Local MD: implement dev server

This commit is contained in:
Fuma Nama
2026-04-17 22:50:22 +08:00
parent a2bf1de5c2
commit 8a7bd2d608
16 changed files with 640 additions and 87 deletions
+1
View File
@@ -23,6 +23,7 @@
"packages/mdx-remote/test/fixtures/**/*.js",
"packages/mdx-remote/test/fixtures/**/*.json",
"packages/local-md/test/fixtures/**/*.json",
"packages/core/test/fixtures/page-trees/**/*.json",
"routeTree.gen.ts",
@@ -15,3 +15,9 @@ console.log('Hello World');
<Card title="Learn more about Next.js" href="https://nextjs.org/docs" />
<Card title="Learn more about Fumadocs" href="https://fumadocs.dev" />
</Cards>
safdafsd
fsdafsdaafsdsf
sffsadafsd
fsad
fsad
+4
View File
@@ -7,6 +7,10 @@ export const docs = localMd({
dir: 'content/docs',
});
if (process.env.NODE_ENV === 'development') {
void docs.devServer();
}
// See https://fumadocs.dev/docs/headless/source-api for more info
export const source = loader({
baseUrl: docsRoute,
+1 -1
View File
@@ -4,7 +4,7 @@
"private": true,
"scripts": {
"build": "next build",
"dev": "next dev",
"dev": "local-md dev -- pnpm next dev",
"start": "next start",
"types:check": "fumadocs-mdx && next typegen && tsc --noEmit"
},
+11 -2
View File
@@ -10,13 +10,17 @@
"license": "MIT",
"author": "Fuma Nama",
"repository": "github:fuma-nama/fumadocs",
"bin": {
"local-md": "./dist/bin.js"
},
"files": [
"dist"
],
"type": "module",
"exports": {
".": "./dist/index.js",
"./dev": "./dist/dev/index.js",
"./dev/client": "./dist/dev/client.js",
"./dev/server": "./dist/dev/server.js",
"./js/executor": "./dist/js/executor.js",
"./js/executor-native": "./dist/js/executor-native.js",
"./js/executor-virtual": "./dist/js/executor-virtual.js",
@@ -36,15 +40,19 @@
"@mdx-js/mdx": "^3.1.1",
"@standard-schema/spec": "^1.1.0",
"chokidar": "^5.0.0",
"commander": "^14.0.3",
"cross-spawn": "^7.0.6",
"estree-util-to-js": "^2.0.0",
"hast-util-to-jsx-runtime": "^2.3.6",
"remark": "^15.0.1",
"remark-rehype": "^11.1.2",
"tinyglobby": "^0.2.16",
"unified": "^11.0.5",
"vfile": "^6.0.3"
"vfile": "^6.0.3",
"ws": "^8.20.0"
},
"devDependencies": {
"@types/cross-spawn": "^6.0.6",
"@types/estree": "^1.0.8",
"@types/estree-jsx": "^1.0.5",
"@types/hast": "^3.0.4",
@@ -52,6 +60,7 @@
"@types/react": "^19.2.14",
"@types/react-dom": "^19.2.3",
"@types/unist": "^3.0.3",
"@types/ws": "^8.18.1",
"fumadocs-core": "workspace:*",
"ignore": "^7.0.5",
"react": "^19.2.5",
+89
View File
@@ -0,0 +1,89 @@
#!/usr/bin/env node
import { Command } from 'commander';
import spawn from 'cross-spawn';
import packageJson from '../package.json';
import { startDevServer } from './dev/server';
import { LOCAL_MD_DEV_PORT_ENV } from './dev/shared';
const program = new Command()
.name('local-md')
.description('CLI for local Markdown development utilities')
.version(packageJson.version)
.enablePositionalOptions();
program
.command('dev')
.description('start the local-md dev server and run a command')
.argument('<command...>', 'command to run')
.option('-p, --port <number>', 'port for the dev server', parsePort, 8000)
.allowUnknownOption()
.passThroughOptions()
.action(async (command: string[], options: { port: number }) => {
const handle = await startDevServer({
port: options.port,
});
const child = spawn(command[0], command.slice(1), {
stdio: 'inherit',
shell: true,
env: {
...process.env,
[LOCAL_MD_DEV_PORT_ENV]: String(handle.port),
},
});
const forwardSignal = (signal: NodeJS.Signals) => {
if (child.exitCode === null && child.signalCode === null) {
child.kill(signal);
}
};
process.on('SIGINT', forwardSignal);
process.on('SIGTERM', forwardSignal);
let exitCode = 1;
try {
exitCode = await new Promise<number>((resolve, reject) => {
child.once('error', reject);
child.once('exit', (code, signal) => {
if (signal) {
resolve(signalToExitCode(signal));
return;
}
resolve(code ?? 0);
});
});
} finally {
process.off('SIGINT', forwardSignal);
process.off('SIGTERM', forwardSignal);
await handle.close().catch(() => undefined);
}
process.exit(exitCode);
});
program.parseAsync(process.argv).catch((error: unknown) => {
console.error(error);
process.exit(1);
});
function parsePort(value: string): number {
const port = Number.parseInt(value, 10);
if (!Number.isFinite(port) || port < 0) {
throw new Error(`Invalid port: ${value}`);
}
return port;
}
function signalToExitCode(signal: NodeJS.Signals): number {
switch (signal) {
case 'SIGINT':
return 130;
case 'SIGTERM':
return 143;
default:
return 1;
}
}
+146
View File
@@ -0,0 +1,146 @@
import {
decodeDevEvent,
encodeDevClientEvent,
getDevServerPort,
getDevServerUrl,
type DevServerEvent,
} from './shared';
import { WebSocket, type RawData } from 'ws';
export interface DevServerConnection {
readonly port: number;
readonly url: string;
readonly connected: boolean;
readonly lastEvent?: DevServerEvent;
subscribe: (listener: (event: DevServerEvent) => void) => () => void;
watchDir: (dir: string) => void;
close: () => void;
}
const STORE_KEY = Symbol.for('__fumadocs_local_md_dev_connections__');
export function connectDevServer(port = getDevServerPort()): DevServerConnection {
if (!port) {
throw new Error('Missing local-md dev server port');
}
const url = getDevServerUrl(port);
const store = getStore();
let connection = store.get(url);
if (!connection) {
connection = new SharedDevServerConnection(port, url);
store.set(url, connection);
}
return connection;
}
class SharedDevServerConnection implements DevServerConnection {
readonly listeners = new Set<(event: DevServerEvent) => void>();
readonly watchedDirs = new Set<string>();
readonly port: number;
readonly url: string;
connected = false;
lastEvent?: DevServerEvent;
private socket?: WebSocket;
private retryTimer?: ReturnType<typeof setTimeout>;
constructor(port: number, url: string) {
this.port = port;
this.url = url;
this.connect();
}
subscribe(listener: (event: DevServerEvent) => void) {
this.listeners.add(listener);
if (this.lastEvent) {
listener(this.lastEvent);
}
return () => {
this.listeners.delete(listener);
};
}
close() {
this.connected = false;
clearTimeout(this.retryTimer);
this.socket?.close();
this.listeners.clear();
getStore().delete(this.url);
}
watchDir(dir: string) {
this.watchedDirs.add(dir);
this.send({
type: 'watch',
absolutePath: dir,
});
}
private connect() {
if (this.socket) return;
const socket = new WebSocket(this.url);
this.socket = socket;
socket.on('open', () => {
this.connected = true;
console.log(`[@fumadocs/local-md] connected to dev server at ${this.url}`);
for (const dir of this.watchedDirs) {
this.send({
type: 'watch',
absolutePath: dir,
});
}
});
socket.on('message', (data) => {
const decoded = decodeDevEvent(rawDataToString(data));
if (decoded) this.emit(decoded);
});
socket.on('close', () => {
this.connected = false;
this.socket = undefined;
console.log(`[@fumadocs/local-md] disconnected from dev server at ${this.url}`);
});
socket.on('error', (e) => {
console.error(e);
});
}
private emit(event: DevServerEvent) {
this.lastEvent = event;
for (const listener of this.listeners) {
listener(event);
}
}
private send(message: { type: 'watch'; absolutePath: string }) {
if (this.socket?.readyState !== WebSocket.OPEN) return;
this.socket.send(encodeDevClientEvent(message));
}
}
function rawDataToString(data: RawData): string {
if (typeof data === 'string') return data;
if (data instanceof ArrayBuffer) return Buffer.from(data).toString('utf8');
if (Array.isArray(data)) return Buffer.concat(data).toString('utf8');
return data.toString('utf8');
}
function getStore() {
const globalStore = globalThis as typeof globalThis & {
[STORE_KEY]?: Map<string, SharedDevServerConnection>;
};
globalStore[STORE_KEY] ??= new Map<string, SharedDevServerConnection>();
return globalStore[STORE_KEY];
}
-1
View File
@@ -1 +0,0 @@
export * from './watcher';
+224
View File
@@ -0,0 +1,224 @@
import { type ChokidarOptions, type FSWatcher, watch } from 'chokidar';
import ignore, { type Ignore } from 'ignore';
import fs from 'node:fs/promises';
import path from 'node:path';
import {
decodeDevClientEvent,
encodeDevEvent,
getDevServerUrl,
LOCAL_MD_DEV_PATH,
type DevServerEvent,
type DevWatchEvent,
} from './shared';
import { WebSocket, WebSocketServer, type RawData } from 'ws';
const WATCH_EVENTS = new Set<DevWatchEvent>(['add', 'addDir', 'change', 'unlink', 'unlinkDir']);
type IgnoredFn = (value: string) => boolean;
export interface DevServerOptions {
port: number;
host?: string;
/**
* customise chokidar, by default, file watcher will watch all files under the `dir` directory.
*/
watchOptions?: (options: ChokidarOptions) => ChokidarOptions;
}
export interface DevServerHandle {
port: number;
host: string;
watcher: FSWatcher;
watchDir: (dir: string) => Promise<void>;
close: () => Promise<void>;
}
export async function startDevServer(options: DevServerOptions): Promise<DevServerHandle> {
const host = options.host ?? '127.0.0.1';
const port = options.port;
const rootIgnored = await fromGitIgnore(process.cwd(), 'node_modules\ndist\nbuild');
let watchOptions: ChokidarOptions = {
ignoreInitial: true,
followSymlinks: false,
ignored: (value) => {
if (rootIgnored?.(value)) return true;
return false;
},
};
if (options.watchOptions) {
watchOptions = options.watchOptions(watchOptions);
}
const watcher = watch([], watchOptions);
const clients = new Map<WebSocket, Set<string>>();
const watchedDirs = new Map<string, number>();
const wsServer = new WebSocketServer({
path: LOCAL_MD_DEV_PATH,
port,
host,
});
function broadcast(event: DevServerEvent) {
const encoded = encodeDevEvent(event);
for (const client of clients.keys()) {
if (client.readyState !== WebSocket.OPEN) {
clients.delete(client);
continue;
}
client.send(encoded);
}
}
wsServer.on('connection', (client) => {
clients.set(client, new Set());
client.send(
encodeDevEvent({
type: 'connected',
timestamp: Date.now(),
}),
);
client.on('message', async (data) => {
const decoded = decodeDevClientEvent(rawDataToString(data));
if (!decoded) return;
if (decoded.type === 'watch') {
try {
await addClientDir(client, decoded.absolutePath);
} catch (error) {
client.send(
encodeDevEvent({
type: 'error',
message: error instanceof Error ? error.message : String(error),
timestamp: Date.now(),
}),
);
}
}
});
client.on('close', () => {
void removeClientDirs(client);
clients.delete(client);
});
});
watcher.on('all', (event, filePath) => {
if (!WATCH_EVENTS.has(event as DevWatchEvent)) return;
broadcast({
type: 'change',
event: event as DevWatchEvent,
absolutePath: filePath,
timestamp: Date.now(),
});
});
watcher.on('error', (error) => {
broadcast({
type: 'error',
message: error instanceof Error ? error.message : String(error),
timestamp: Date.now(),
});
});
await new Promise<void>((resolve, reject) => {
wsServer.once('error', reject);
wsServer.on('listening', () => {
wsServer.off('error', reject);
console.log(`[@fumadocs/local-md] dev server is ready at ${getDevServerUrl(port, host)}`);
resolve();
});
});
return {
port,
host,
watcher,
watchDir: addWatchDir,
async close() {
for (const client of clients.keys()) {
client.close();
}
clients.clear();
wsServer.close();
await watcher.close();
await new Promise<void>((resolve, reject) => {
wsServer.close((error) => {
if (error) reject(error);
else resolve();
});
});
},
};
async function addClientDir(client: WebSocket, dir: string) {
const absolutePath = path.resolve(dir);
const dirs = clients.get(client);
if (!dirs || dirs.has(absolutePath)) return;
dirs.add(absolutePath);
await addWatchDir(absolutePath);
}
async function addWatchDir(dir: string) {
const absolutePath = path.resolve(dir);
const count = watchedDirs.get(absolutePath) ?? 0;
if (count === 0) watcher.add(absolutePath);
watchedDirs.set(absolutePath, count + 1);
}
async function removeClientDirs(client: WebSocket) {
const dirs = clients.get(client);
if (!dirs) return;
for (const dir of dirs) removeWatchDir(dir);
dirs.clear();
}
function removeWatchDir(dir: string) {
const absolutePath = path.resolve(dir);
const count = watchedDirs.get(absolutePath);
if (!count) return;
if (count === 1) {
watchedDirs.delete(absolutePath);
watcher.unwatch(absolutePath);
return;
}
watchedDirs.set(absolutePath, count - 1);
}
}
async function fromGitIgnore(dir: string, defaultValue?: string): Promise<IgnoredFn | undefined> {
const gitignore = await fs
.readFile(path.join(dir, '.gitignore'), 'utf-8')
.catch(() => defaultValue);
if (gitignore) {
const ig = ignore();
ig.add(gitignore);
return toMatcher(dir, ig);
}
}
function toMatcher(dir: string, ig: Ignore): IgnoredFn {
return (v) => {
const relativePath = path.relative(dir, v);
// for invalid path, don't ignore
return ignore.isPathValid(relativePath) && ig.checkIgnore(relativePath).ignored;
};
}
function rawDataToString(data: RawData): string {
if (typeof data === 'string') return data;
if (data instanceof ArrayBuffer) return Buffer.from(data).toString('utf8');
if (Array.isArray(data)) return Buffer.concat(data).toString('utf8');
return data.toString('utf8');
}
+63
View File
@@ -0,0 +1,63 @@
export const LOCAL_MD_DEV_PATH = '/_fumadocs_local_md';
export const LOCAL_MD_DEV_PORT_ENV = 'FUMADOCS_LOCAL_MD_DEV_SERVER_PORT';
export type DevWatchEvent = 'add' | 'addDir' | 'change' | 'unlink' | 'unlinkDir';
export interface DevClientEvent {
type: 'watch';
absolutePath: string;
}
export type DevServerEvent =
| {
type: 'connected';
timestamp: number;
}
| {
type: 'change';
event: DevWatchEvent;
absolutePath: string;
timestamp: number;
}
| {
type: 'error';
message: string;
timestamp: number;
};
export function encodeDevClientEvent(event: DevClientEvent): string {
return JSON.stringify(event);
}
export function decodeDevClientEvent(value: string): DevClientEvent | undefined {
try {
return JSON.parse(value) as DevClientEvent;
} catch {
return undefined;
}
}
export function encodeDevEvent(event: DevServerEvent): string {
return JSON.stringify(event);
}
export function decodeDevEvent(value: string): DevServerEvent | undefined {
try {
return JSON.parse(value) as DevServerEvent;
} catch {
return undefined;
}
}
export function getDevServerPort(port = process.env[LOCAL_MD_DEV_PORT_ENV]): number | undefined {
if (!port) return undefined;
const value = Number.parseInt(port, 10);
if (!Number.isFinite(value) || value <= 0) return undefined;
return value;
}
export function getDevServerUrl(port: number, host = '127.0.0.1'): string {
return `ws://${host}:${port}${LOCAL_MD_DEV_PATH}`;
}
-51
View File
@@ -1,51 +0,0 @@
import { type ChokidarOptions, type Matcher, watch } from 'chokidar';
import ignore, { type Ignore } from 'ignore';
import fs from 'node:fs/promises';
import path from 'node:path';
import type { LocalMarkdown } from '..';
import type { StandardSchemaV1 } from '@standard-schema/spec';
export async function startWatcher(
instance: LocalMarkdown<StandardSchemaV1, StandardSchemaV1>,
/**
* customise chokidar, by default, file watcher will watch all files under the `dir` directory.
*/
watchOptions?: (options: ChokidarOptions) => ChokidarOptions,
) {
const dir = instance.config.dir;
// init .gitignore
const ignored = await Promise.all([
fromGitIgnore(process.cwd(), 'node_modules\ndist\nbuild'),
fromGitIgnore(dir),
]);
let options: ChokidarOptions = {
ignoreInitial: true,
followSymlinks: false,
ignored: ignored.filter((v) => v !== undefined),
};
if (watchOptions) options = watchOptions(options);
return watch(dir, options);
}
async function fromGitIgnore(dir: string, defaultValue?: string) {
const gitignore = await fs
.readFile(path.join(dir, '.gitignore'), 'utf-8')
.catch(() => defaultValue);
if (gitignore) {
const ig = ignore();
ig.add(gitignore);
return toMatcher(dir, ig);
}
}
function toMatcher(dir: string, ig: Ignore): Matcher {
return (v) => {
const relativePath = path.relative(dir, v);
// for invalid path, don't ignore
return ignore.isPathValid(relativePath) && ig.checkIgnore(relativePath).ignored;
};
}
+18
View File
@@ -1,9 +1,11 @@
import type { MetaData, Source, VirtualFile } from 'fumadocs-core/source';
import type { StandardSchemaV1 } from '@standard-schema/spec';
import path from 'node:path';
import { createStorage } from './storage';
import type * as defaultSchemas from 'fumadocs-core/source/schema';
import { createMarkdownRenderer, MarkdownRendererOptions, PageRenderer } from './md/renderer';
import { createMarkdownCompiler, MarkdownCompilerOptions } from './md/compiler';
import { getDevServerPort } from './dev/shared';
export interface LocalMarkdownConfig<
FrontmatterSchema extends StandardSchemaV1,
@@ -29,6 +31,8 @@ export interface LocalMarkdown<
MetaSchema extends StandardSchemaV1,
> {
config: LocalMarkdownConfig<FrontmatterSchema, MetaSchema>;
/** connect to dev server, required for hot reload */
devServer: (port?: number) => Promise<void>;
toSource: () => Promise<
Source<{
pageData: LocalMarkdownPage<StandardSchemaV1.InferOutput<FrontmatterSchema>>;
@@ -58,6 +62,20 @@ export function localMd<
return {
config,
async devServer(port) {
const { connectDevServer } = await import('@/dev/client');
const resolvedPort = port ?? getDevServerPort();
if (!resolvedPort) return;
const conn = connectDevServer(resolvedPort);
conn.watchDir(path.resolve(config.dir));
conn.subscribe((event) => {
if (event.type === 'change') {
storage.invalidateCache(event.absolutePath);
}
});
},
async toSource() {
const { metas, pages } = await storage.getPages();
const files: VirtualFile<{
+3
View File
@@ -110,6 +110,9 @@ export function createStorage<
}
return {
invalidateCache(absolutePath: string) {
filesCache.delete(absolutePath);
},
async getPages() {
const files = await glob(include, {
cwd: dir,
+1 -1
View File
@@ -1,6 +1,6 @@
# JSX expressions
export const ignored = <span>Ignored export</span>
export const ignored = <span>Ignored export</span>;
{<div id="native-jsx">Native JSX child</div>}
+7 -2
View File
@@ -4,9 +4,14 @@ export default defineConfig({
dts: true,
fixedExtension: false,
target: 'es2023',
entry: ['./src/index.ts', './src/js/*', './src/dev/index.ts'],
entry: ['./src/index.ts', './src/bin.ts', './src/js/*', './src/dev/{server,client}.ts'],
format: 'esm',
exports: true,
exports: {
bin: {
'local-md': './src/bin.ts',
},
exclude: ['bin'],
},
deps: {
onlyBundle: ['ignore', 'stable-hash'],
neverBundle: ['mdx/types'],
+66 -29
View File
@@ -1798,7 +1798,7 @@ importers:
version: link:../tsconfig
tsdown:
specifier: 0.21.8
version: 0.21.8(@tsdown/[email protected])([email protected])([email protected])
version: 0.21.8(@tsdown/[email protected])([email protected](@emnapi/[email protected])(@emnapi/[email protected]))([email protected])
unified:
specifier: ^11.0.5
version: 11.0.5
@@ -1832,7 +1832,7 @@ importers:
version: link:../tsconfig
tsdown:
specifier: 0.21.8
version: 0.21.8(@tsdown/[email protected])([email protected])([email protected])
version: 0.21.8(@tsdown/[email protected])([email protected](@emnapi/[email protected])(@emnapi/[email protected]))([email protected])
packages/content:
dependencies:
@@ -1857,7 +1857,7 @@ importers:
version: link:../tsconfig
tsdown:
specifier: 0.21.8
version: 0.21.8(@tsdown/[email protected])([email protected])([email protected])
version: 0.21.8(@tsdown/[email protected])([email protected](@emnapi/[email protected])(@emnapi/[email protected]))([email protected])
packages/content-collections:
dependencies:
@@ -1885,7 +1885,7 @@ importers:
version: link:../tsconfig
tsdown:
specifier: 0.21.8
version: 0.21.8(@tsdown/[email protected])([email protected])([email protected])
version: 0.21.8(@tsdown/[email protected])([email protected](@emnapi/[email protected])(@emnapi/[email protected]))([email protected])
packages/core:
dependencies:
@@ -2033,7 +2033,7 @@ importers:
version: link:../tsconfig
tsdown:
specifier: 0.21.8
version: 0.21.8(@tsdown/[email protected])([email protected])([email protected])
version: 0.21.8(@tsdown/[email protected])([email protected](@emnapi/[email protected])(@emnapi/[email protected]))([email protected])
typescript:
specifier: ^6.0.2
version: 6.0.2
@@ -2085,7 +2085,7 @@ importers:
version: link:../tsconfig
tsdown:
specifier: 0.21.8
version: 0.21.8(@tsdown/[email protected])([email protected])([email protected])
version: 0.21.8(@tsdown/[email protected])([email protected](@emnapi/[email protected])(@emnapi/[email protected]))([email protected])
typescript:
specifier: ^6.0.2
version: 6.0.2
@@ -2176,7 +2176,7 @@ importers:
version: link:../tsconfig
tsdown:
specifier: 0.21.8
version: 0.21.8(@tsdown/[email protected])([email protected])([email protected])
version: 0.21.8(@tsdown/[email protected])([email protected](@emnapi/[email protected])(@emnapi/[email protected]))([email protected])
typescript:
specifier: ^6.0.2
version: 6.0.2
@@ -2222,7 +2222,7 @@ importers:
version: link:../tsconfig
tsdown:
specifier: 0.21.8
version: 0.21.8(@tsdown/[email protected])([email protected])([email protected])
version: 0.21.8(@tsdown/[email protected])([email protected](@emnapi/[email protected])(@emnapi/[email protected]))([email protected])
typescript:
specifier: ^6.0.2
version: 6.0.2
@@ -2241,6 +2241,12 @@ importers:
chokidar:
specifier: ^5.0.0
version: 5.0.0
commander:
specifier: ^14.0.3
version: 14.0.3
cross-spawn:
specifier: ^7.0.6
version: 7.0.6
estree-util-to-js:
specifier: ^2.0.0
version: 2.0.0
@@ -2262,7 +2268,13 @@ importers:
vfile:
specifier: ^6.0.3
version: 6.0.3
ws:
specifier: ^8.20.0
version: 8.20.0
devDependencies:
'@types/cross-spawn':
specifier: ^6.0.6
version: 6.0.6
'@types/estree':
specifier: ^1.0.8
version: 1.0.8
@@ -2284,6 +2296,9 @@ importers:
'@types/unist':
specifier: ^3.0.3
version: 3.0.3
'@types/ws':
specifier: ^8.18.1
version: 8.18.1
fumadocs-core:
specifier: workspace:*
version: link:../core
@@ -2304,7 +2319,7 @@ importers:
version: link:../tsconfig
tsdown:
specifier: 0.21.8
version: 0.21.8(@tsdown/[email protected])([email protected])([email protected])
version: 0.21.8(@tsdown/[email protected])([email protected](@emnapi/[email protected])(@emnapi/[email protected]))([email protected])
packages/mdx:
dependencies:
@@ -2410,7 +2425,7 @@ importers:
version: link:../tsconfig
tsdown:
specifier: 0.21.8
version: 0.21.8(@tsdown/[email protected])([email protected])([email protected])
version: 0.21.8(@tsdown/[email protected])([email protected](@emnapi/[email protected])(@emnapi/[email protected]))([email protected])
vite:
specifier: ^8.0.8
version: 8.0.8(@types/[email protected])([email protected])([email protected])([email protected])([email protected])([email protected])
@@ -2459,7 +2474,7 @@ importers:
version: link:../tsconfig
tsdown:
specifier: 0.21.8
version: 0.21.8(@tsdown/[email protected])([email protected])([email protected])
version: 0.21.8(@tsdown/[email protected])([email protected](@emnapi/[email protected])(@emnapi/[email protected]))([email protected])
packages/obsidian:
dependencies:
@@ -2535,7 +2550,7 @@ importers:
version: link:../tsconfig
tsdown:
specifier: 0.21.8
version: 0.21.8(@tsdown/[email protected])([email protected])([email protected])
version: 0.21.8(@tsdown/[email protected])([email protected](@emnapi/[email protected])(@emnapi/[email protected]))([email protected])
typescript:
specifier: ^6.0.2
version: 6.0.2
@@ -2647,7 +2662,7 @@ importers:
version: link:../tsconfig
tsdown:
specifier: 0.21.8
version: 0.21.8(@tsdown/[email protected])([email protected])([email protected])
version: 0.21.8(@tsdown/[email protected])([email protected](@emnapi/[email protected])(@emnapi/[email protected]))([email protected])
packages/press:
dependencies:
@@ -2786,7 +2801,7 @@ importers:
version: 4.2.2
tsdown:
specifier: 0.21.8
version: 0.21.8(@tsdown/[email protected])([email protected])([email protected])
version: 0.21.8(@tsdown/[email protected])([email protected](@emnapi/[email protected])(@emnapi/[email protected]))([email protected])
typescript:
specifier: ^6.0.2
version: 6.0.2
@@ -2838,7 +2853,7 @@ importers:
version: link:../tsconfig
tsdown:
specifier: 0.21.8
version: 0.21.8(@tsdown/[email protected])([email protected])([email protected])
version: 0.21.8(@tsdown/[email protected])([email protected](@emnapi/[email protected])(@emnapi/[email protected]))([email protected])
packages/radix-ui:
dependencies:
@@ -2959,7 +2974,7 @@ importers:
version: link:../tsconfig
tsdown:
specifier: 0.21.8
version: 0.21.8(@tsdown/[email protected])([email protected])([email protected])
version: 0.21.8(@tsdown/[email protected])([email protected](@emnapi/[email protected])(@emnapi/[email protected]))([email protected])
unified:
specifier: ^11.0.5
version: 11.0.5
@@ -3003,7 +3018,7 @@ importers:
version: link:../tsconfig
tsdown:
specifier: 0.21.8
version: 0.21.8(@tsdown/[email protected])([email protected])([email protected])
version: 0.21.8(@tsdown/[email protected])([email protected](@emnapi/[email protected])(@emnapi/[email protected]))([email protected])
packages/story:
dependencies:
@@ -3061,7 +3076,7 @@ importers:
version: link:../tsconfig
tsdown:
specifier: 0.21.8
version: 0.21.8(@tsdown/[email protected])([email protected])([email protected])
version: 0.21.8(@tsdown/[email protected])([email protected](@emnapi/[email protected])(@emnapi/[email protected]))([email protected])
packages/tailwind:
devDependencies:
@@ -3085,7 +3100,7 @@ importers:
version: link:../tsconfig
tsdown:
specifier: 0.21.8
version: 0.21.8(@tsdown/[email protected])([email protected])([email protected])
version: 0.21.8(@tsdown/[email protected])([email protected](@emnapi/[email protected])(@emnapi/[email protected]))([email protected])
packages/tsconfig: {}
@@ -3145,7 +3160,7 @@ importers:
version: link:../tsconfig
tsdown:
specifier: 0.21.8
version: 0.21.8(@tsdown/[email protected])([email protected])([email protected])
version: 0.21.8(@tsdown/[email protected])([email protected](@emnapi/[email protected])(@emnapi/[email protected]))([email protected])
typescript:
specifier: ^6.0.2
version: 6.0.2
@@ -3221,7 +3236,7 @@ importers:
version: link:../tsconfig
tsdown:
specifier: 0.21.8
version: 0.21.8(@tsdown/[email protected])([email protected])([email protected])
version: 0.21.8(@tsdown/[email protected])([email protected](@emnapi/[email protected])(@emnapi/[email protected]))([email protected])
typescript:
specifier: ^6.0.2
version: 6.0.2
@@ -7333,6 +7348,9 @@ packages:
'@types/[email protected]':
resolution: {integrity: sha512-h8fgEd/DpoS9CBrjEQXR+dIDraopAEfu4wYVNY2tEPwk60stPWhvZMf4Foo5FakuQ7HFZoa8WceaWFervK2Ovg==}
'@types/[email protected]':
resolution: {integrity: sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==}
'@typescript/[email protected]':
resolution: {integrity: sha512-PJFXFS4ZJKiJ9Qiuix6Dz/OwEIqHD7Dme1UwZhTK11vR+5dqW2ACbdndWQexBzCx+CPuMe5WBYQWCsFyGlQLlQ==}
peerDependencies:
@@ -7832,6 +7850,7 @@ packages:
[email protected]:
resolution: {integrity: sha512-KLwDUqs5WIny/94F4xZ4QfaAE6YWyjR+s79pt/ItQhk2CG+PJQ5xL6VuOWhiyN2eP3fryZK95vog9CTLCaYV2Q==}
cpu: [arm64, x64]
os: [darwin, linux, win32]
hasBin: true
@@ -12419,6 +12438,18 @@ packages:
utf-8-validate:
optional: true
[email protected]:
resolution: {integrity: sha512-sAt8BhgNbzCtgGbt2OxmpuryO63ZoDk/sqaB/znQm94T4fCEsy/yV+7CdC1kJhOU9lboAEU7R3kquuycDoibVA==}
engines: {node: '>=10.0.0'}
peerDependencies:
bufferutil: ^4.0.1
utf-8-validate: '>=5.0.2'
peerDependenciesMeta:
bufferutil:
optional: true
utf-8-validate:
optional: true
[email protected]:
resolution: {integrity: sha512-g/eziiSUNBSsdDJtCLB8bdYEUMj4jR7AGeUo96p/3dTafgjHhpF4RiCFPiRILwjQoDXx5MqkBr4fwWtR3Ky4Wg==}
engines: {node: '>=20'}
@@ -16196,7 +16227,7 @@ snapshots:
react: 19.2.5
react-dom: 19.2.5([email protected])
optionalDependencies:
'@vitejs/plugin-rsc': 0.5.24([email protected]([email protected]))([email protected]([email protected]([email protected]))([email protected])([email protected]([email protected])))([email protected])([email protected](@types/[email protected])([email protected])([email protected])([email protected])([email protected])([email protected]))
'@vitejs/plugin-rsc': 0.5.24([email protected]([email protected]))([email protected]([email protected]([email protected]))([email protected]))([email protected])([email protected](@types/[email protected])([email protected])([email protected])([email protected])([email protected])([email protected]))
transitivePeerDependencies:
- '@rsbuild/core'
- crossws
@@ -16232,7 +16263,7 @@ snapshots:
react-dom: 19.2.5([email protected])
vite: 8.0.8(@types/[email protected])([email protected])([email protected])([email protected])([email protected])([email protected])
optionalDependencies:
'@vitejs/plugin-rsc': 0.5.24([email protected]([email protected]))([email protected]([email protected]([email protected]))([email protected])([email protected]([email protected])))([email protected])([email protected](@types/[email protected])([email protected])([email protected])([email protected])([email protected])([email protected]))
'@vitejs/plugin-rsc': 0.5.24([email protected]([email protected]))([email protected]([email protected]([email protected]))([email protected]))([email protected])([email protected](@types/[email protected])([email protected])([email protected])([email protected])([email protected])([email protected]))
transitivePeerDependencies:
- '@rsbuild/core'
- crossws
@@ -16403,7 +16434,7 @@ snapshots:
lightningcss: 1.32.0
postcss-load-config: 6.0.1([email protected])([email protected])([email protected])([email protected])
rolldown: 1.0.0-rc.15
tsdown: 0.21.8(@tsdown/[email protected])([email protected])([email protected])
tsdown: 0.21.8(@tsdown/[email protected])([email protected](@emnapi/[email protected])(@emnapi/[email protected]))([email protected])
optionalDependencies:
postcss: 8.5.9
transitivePeerDependencies:
@@ -16703,6 +16734,10 @@ snapshots:
'@types/[email protected]': {}
'@types/[email protected]':
dependencies:
'@types/node': 25.6.0
'@typescript/[email protected]([email protected])':
dependencies:
debug: 4.4.3
@@ -18069,7 +18104,7 @@ snapshots:
[email protected]: {}
[email protected]([email protected]):
[email protected]([email protected](@emnapi/[email protected])(@emnapi/[email protected])):
optionalDependencies:
oxc-resolver: 11.19.1(@emnapi/[email protected])(@emnapi/[email protected])
@@ -21447,7 +21482,7 @@ snapshots:
[email protected]: {}
[email protected]([email protected])([email protected])([email protected]):
[email protected]([email protected](@emnapi/[email protected])(@emnapi/[email protected]))([email protected])([email protected]):
dependencies:
'@babel/generator': 8.0.0-rc.3
'@babel/helper-validator-identifier': 8.0.0-rc.3
@@ -21455,7 +21490,7 @@ snapshots:
'@babel/types': 8.0.0-rc.3
ast-kit: 3.0.0-beta.1
birpc: 4.0.0
dts-resolver: 2.1.3([email protected])
dts-resolver: 2.1.3([email protected](@emnapi/[email protected])(@emnapi/[email protected]))
get-tsconfig: 4.14.0
obug: 2.1.1
picomatch: 4.0.4
@@ -22094,7 +22129,7 @@ snapshots:
minimist: 1.2.8
strip-bom: 3.0.0
[email protected](@tsdown/[email protected])([email protected])([email protected]):
[email protected](@tsdown/[email protected])([email protected](@emnapi/[email protected])(@emnapi/[email protected]))([email protected]):
dependencies:
ansis: 4.2.0
cac: 7.0.0
@@ -22105,7 +22140,7 @@ snapshots:
obug: 2.1.1
picomatch: 4.0.4
rolldown: 1.0.0-rc.15
rolldown-plugin-dts: 0.23.2([email protected])([email protected])([email protected])
rolldown-plugin-dts: 0.23.2([email protected](@emnapi/[email protected])(@emnapi/[email protected]))([email protected])([email protected])
semver: 7.7.4
tinyexec: 1.1.1
tinyglobby: 0.2.16
@@ -22670,6 +22705,8 @@ snapshots:
[email protected]: {}
[email protected]: {}
[email protected]:
dependencies:
is-wsl: 3.1.1