@rsc-kit/core 0.20.0 → 0.20.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,15 @@
1
+ export interface ImportMetaUse {
2
+ line: number;
3
+ /** `import.meta.env`, or `import.meta` for the bare object. */
4
+ form: string;
5
+ }
6
+ /** The uses of `import.meta` in a module that bytecode cannot express. */
7
+ export declare function unlowerableImportMeta(source: string): ImportMetaUse[];
8
+ /** Every server module the binary would compile, with its first unlowerable use. */
9
+ export declare function bytecodeBlockers(serverDir: string): {
10
+ file: string;
11
+ line: number;
12
+ form: string;
13
+ }[];
14
+ /** The note the build prints, or null when the binary can take --bytecode. */
15
+ export declare function bytecodeNote(serverDir: string): string | null;
@@ -0,0 +1,65 @@
1
+ // What stops `bun build --compile --bytecode` from applying, said by name.
2
+ //
3
+ // Bun compiles bytecode as CommonJS, and in CommonJS `import.meta` does not
4
+ // exist: `import.meta.url`, `.dirname`, `.dir` and `.main` are lowered to
5
+ // values, and every other form - `.env`, `.filename`, `.resolve`, the object
6
+ // itself - fails the bytecode step. Bun then reports "Failed to generate
7
+ // bytecode", names no file, and exits 0, so a CI ships a binary that dies at
8
+ // boot with "import.meta is only valid inside modules" and reads green.
9
+ // The build scans its own server output and names the file and line.
10
+ import { readdirSync, readFileSync } from "node:fs";
11
+ import { join, relative } from "node:path";
12
+ const LOWERED = new Set(["url", "dirname", "dir", "main"]);
13
+ /** `import.meta`, and what follows it, at each use outside a comment. */
14
+ const USE = /\bimport\.meta\b(?:\s*\??\.\s*([A-Za-z_$][\w$]*))?/g;
15
+ /** The uses of `import.meta` in a module that bytecode cannot express. */
16
+ export function unlowerableImportMeta(source) {
17
+ const found = [];
18
+ const lines = source.split("\n");
19
+ for (const [index, raw] of lines.entries()) {
20
+ const line = raw.trimStart();
21
+ if (line.startsWith("//") || line.startsWith("*") || line.startsWith("/*"))
22
+ continue;
23
+ for (const match of raw.matchAll(USE)) {
24
+ const property = match[1];
25
+ if (property && LOWERED.has(property))
26
+ continue;
27
+ found.push({ line: index + 1, form: property ? `import.meta.${property}` : "import.meta" });
28
+ }
29
+ }
30
+ return found;
31
+ }
32
+ /** Every server module the binary would compile, with its first unlowerable use. */
33
+ export function bytecodeBlockers(serverDir) {
34
+ const blockers = [];
35
+ const walk = (dir) => {
36
+ for (const entry of readdirSync(dir, { withFileTypes: true })) {
37
+ const path = join(dir, entry.name);
38
+ if (entry.isDirectory()) {
39
+ if (entry.name !== "node_modules")
40
+ walk(path);
41
+ continue;
42
+ }
43
+ // The stored pages, as one module: their text is data, not code.
44
+ if (!/\.m?js$/.test(entry.name) || entry.name.endsWith("-inline.mjs"))
45
+ continue;
46
+ const [first] = unlowerableImportMeta(readFileSync(path, "utf-8"));
47
+ if (first)
48
+ blockers.push({ file: relative(serverDir, path), ...first });
49
+ }
50
+ };
51
+ walk(serverDir);
52
+ return blockers;
53
+ }
54
+ /** The note the build prints, or null when the binary can take --bytecode. */
55
+ export function bytecodeNote(serverDir) {
56
+ const blockers = bytecodeBlockers(serverDir);
57
+ if (blockers.length === 0)
58
+ return null;
59
+ return ("bun build --compile --bytecode will not apply to this server: " +
60
+ blockers.map((b) => `${b.file}:${b.line} uses ${b.form}`).join(", ") +
61
+ ". Bytecode is CommonJS, where only import.meta.url, .dirname, .dir and .main can be expressed; " +
62
+ "Bun reports the failure, names no file, and exits 0, so the binary dies at boot. " +
63
+ "--bytecode --format=esm applies regardless.");
64
+ }
65
+ //# sourceMappingURL=bytecodeCheck.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bytecodeCheck.js","sourceRoot":"","sources":["../src/bytecodeCheck.ts"],"names":[],"mappings":"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","sourcesContent":["// What stops `bun build --compile --bytecode` from applying, said by name.\n//\n// Bun compiles bytecode as CommonJS, and in CommonJS `import.meta` does not\n// exist: `import.meta.url`, `.dirname`, `.dir` and `.main` are lowered to\n// values, and every other form - `.env`, `.filename`, `.resolve`, the object\n// itself - fails the bytecode step. Bun then reports \"Failed to generate\n// bytecode\", names no file, and exits 0, so a CI ships a binary that dies at\n// boot with \"import.meta is only valid inside modules\" and reads green.\n// The build scans its own server output and names the file and line.\n\nimport { readdirSync, readFileSync } from \"node:fs\";\nimport { join, relative } from \"node:path\";\n\nconst LOWERED = new Set([\"url\", \"dirname\", \"dir\", \"main\"]);\n\n/** `import.meta`, and what follows it, at each use outside a comment. */\nconst USE = /\\bimport\\.meta\\b(?:\\s*\\??\\.\\s*([A-Za-z_$][\\w$]*))?/g;\n\nexport interface ImportMetaUse {\n line: number;\n /** `import.meta.env`, or `import.meta` for the bare object. */\n form: string;\n}\n\n/** The uses of `import.meta` in a module that bytecode cannot express. */\nexport function unlowerableImportMeta(source: string): ImportMetaUse[] {\n const found: ImportMetaUse[] = [];\n const lines = source.split(\"\\n\");\n\n for (const [index, raw] of lines.entries()) {\n const line = raw.trimStart();\n\n if (line.startsWith(\"//\") || line.startsWith(\"*\") || line.startsWith(\"/*\")) continue;\n\n for (const match of raw.matchAll(USE)) {\n const property = match[1];\n\n if (property && LOWERED.has(property)) continue;\n\n found.push({ line: index + 1, form: property ? `import.meta.${property}` : \"import.meta\" });\n }\n }\n\n return found;\n}\n\n/** Every server module the binary would compile, with its first unlowerable use. */\nexport function bytecodeBlockers(serverDir: string): { file: string; line: number; form: string }[] {\n const blockers: { file: string; line: number; form: string }[] = [];\n\n const walk = (dir: string): void => {\n for (const entry of readdirSync(dir, { withFileTypes: true })) {\n const path = join(dir, entry.name);\n\n if (entry.isDirectory()) {\n if (entry.name !== \"node_modules\") walk(path);\n\n continue;\n }\n\n // The stored pages, as one module: their text is data, not code.\n if (!/\\.m?js$/.test(entry.name) || entry.name.endsWith(\"-inline.mjs\")) continue;\n\n const [first] = unlowerableImportMeta(readFileSync(path, \"utf-8\"));\n\n if (first) blockers.push({ file: relative(serverDir, path), ...first });\n }\n };\n\n walk(serverDir);\n\n return blockers;\n}\n\n/** The note the build prints, or null when the binary can take --bytecode. */\nexport function bytecodeNote(serverDir: string): string | null {\n const blockers = bytecodeBlockers(serverDir);\n\n if (blockers.length === 0) return null;\n\n return (\n \"bun build --compile --bytecode will not apply to this server: \" +\n blockers.map((b) => `${b.file}:${b.line} uses ${b.form}`).join(\", \") +\n \". Bytecode is CommonJS, where only import.meta.url, .dirname, .dir and .main can be expressed; \" +\n \"Bun reports the failure, names no file, and exits 0, so the binary dies at boot. \" +\n \"--bytecode --format=esm applies regardless.\"\n );\n}\n"]}
@@ -12,6 +12,31 @@ export declare function hasClientDirective(dir: string): boolean;
12
12
  * first call throws React's refusal at a visitor.
13
13
  */
14
14
  export declare function importsServerRenderer(dir: string): boolean;
15
+ /**
16
+ * Of the packages a build would trace by name, the ones the tracer can find.
17
+ *
18
+ * Nitro's tracer resolves a name the way Node does: up the directory tree
19
+ * from the project, `node_modules` by `node_modules`. A package that is in
20
+ * the graph only through a dependency - under an isolated store, never
21
+ * hoisted - is not resolvable that way, and naming it earns "could not
22
+ * resolve `traceInclude` entry" once per build, for a package the tracer
23
+ * reaches through its dependant anyway. So: present by name, or not named.
24
+ */
25
+ export declare function traceableByName(names: readonly string[], root: string): string[];
26
+ /**
27
+ * The polyfills to keep outside the bundle, as patterns.
28
+ *
29
+ * A polyfill is evaluated first by the server's entry (see the Nitro module)
30
+ * and imported again by the dependency that checks for it. Bundled, the
31
+ * bundler merges the one module into the chunk of that dependency, and the
32
+ * chunk's own imports - tsyringe, under @peculiar/x509 - evaluate before the
33
+ * chunk's body installs the polyfill: the server did not boot. Left
34
+ * external, the entry's import of it is the first import there is, and the
35
+ * tracer copies the file by the path it was resolved to. A pattern rather
36
+ * than a name so the tracer does not also try to resolve the name from the
37
+ * project root, which under an isolated store it cannot.
38
+ */
39
+ export declare function polyfillsToTrace(names: readonly string[], root: string): RegExp[];
15
40
  /**
16
41
  * Where a package is, anywhere in the project's dependency graph.
17
42
  *
@@ -169,6 +169,37 @@ function reactDependenciesOf(dir, into, seen) {
169
169
  reactDependenciesOf(depDir, into, seen);
170
170
  }
171
171
  }
172
+ /**
173
+ * Of the packages a build would trace by name, the ones the tracer can find.
174
+ *
175
+ * Nitro's tracer resolves a name the way Node does: up the directory tree
176
+ * from the project, `node_modules` by `node_modules`. A package that is in
177
+ * the graph only through a dependency - under an isolated store, never
178
+ * hoisted - is not resolvable that way, and naming it earns "could not
179
+ * resolve `traceInclude` entry" once per build, for a package the tracer
180
+ * reaches through its dependant anyway. So: present by name, or not named.
181
+ */
182
+ export function traceableByName(names, root) {
183
+ return names.filter((name) => packageDir(name, root) !== null);
184
+ }
185
+ /**
186
+ * The polyfills to keep outside the bundle, as patterns.
187
+ *
188
+ * A polyfill is evaluated first by the server's entry (see the Nitro module)
189
+ * and imported again by the dependency that checks for it. Bundled, the
190
+ * bundler merges the one module into the chunk of that dependency, and the
191
+ * chunk's own imports - tsyringe, under @peculiar/x509 - evaluate before the
192
+ * chunk's body installs the polyfill: the server did not boot. Left
193
+ * external, the entry's import of it is the first import there is, and the
194
+ * tracer copies the file by the path it was resolved to. A pattern rather
195
+ * than a name so the tracer does not also try to resolve the name from the
196
+ * project root, which under an isolated store it cannot.
197
+ */
198
+ export function polyfillsToTrace(names, root) {
199
+ return names
200
+ .filter((name) => packageEntryInGraph(root, name) !== null)
201
+ .map((name) => new RegExp(name.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")));
202
+ }
172
203
  /**
173
204
  * Where a package is, anywhere in the project's dependency graph.
174
205
  *
@@ -1 +1 @@
1
- {"version":3,"file":"clientPackages.js","sourceRoot":"","sources":["../src/clientPackages.ts"],"names":[],"mappings":"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","sourcesContent":["// Dependencies with \"use client\" files that plugin-rsc would leave external.\n//\n// plugin-rsc bundles a dependency into the server graphs - and so sees its\n// directives - only when the dependency declares react as a peer. That is\n// the right signal for a library written for React. It misses the package\n// that imports react and never says so: a component wrapper generated by a\n// tool, a workspace package with react under dependencies, a published\n// package whose author forgot. Left external, its \"use client\" is a string\n// nobody reads, the rsc environment loads it under the react-server\n// condition, and the first useState inside it fails at render with an error\n// that names React and not the package.\n//\n// So the direct dependencies are read once at config time: any that plugin-rsc\n// would not bundle, and that carries a \"use client\" directive in one of its\n// files, is bundled here. Direct dependencies only - a directive two levels\n// down is the concern of the package between, which declared it.\n\nimport { existsSync, readdirSync, readFileSync, realpathSync, statSync, openSync, readSync, closeSync } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\n\n/** A directive at the top of a file: comments may precede it, code may not. */\nconst DIRECTIVE = /^(?:\\s|\\/\\/[^\\n]*\\n|\\/\\*[\\s\\S]*?\\*\\/)*(?:'use client'|\"use client\")\\s*;?/;\n\nconst SOURCE = /\\.(?:m?js|cjs|jsx)$/;\n\n/** The most files read per package before giving up: a bound, not a budget. */\nconst FILE_CAP = 3000;\n\n/** Enough of a file to hold a licence header and the directive after it. */\nconst HEAD_BYTES = 2048;\n\n/** The packages plugin-rsc handles itself, and this one. */\nconst OWN = new Set([\"react\", \"react-dom\", \"react-server-dom-webpack\", \"@vitejs/plugin-rsc\"]);\n\ninterface PackageJson {\n name?: string;\n dependencies?: Record<string, string>;\n peerDependencies?: Record<string, string>;\n}\n\nfunction readJson(path: string): PackageJson | null {\n try {\n return JSON.parse(readFileSync(path, \"utf8\")) as PackageJson;\n } catch {\n return null;\n }\n}\n\n/** Where `name` is installed, walking up from `from` the way resolution does. */\nexport function packageDir(name: string, from: string): string | null {\n let dir = from;\n\n for (;;) {\n const candidate = join(dir, \"node_modules\", name);\n\n if (existsSync(join(candidate, \"package.json\"))) return candidate;\n\n const parent = dirname(dir);\n\n if (parent === dir) return null;\n\n dir = parent;\n }\n}\n\nfunction realpathOf(path: string): string {\n try {\n return realpathSync(path);\n } catch {\n return path;\n }\n}\n\nfunction headOf(path: string): string {\n const fd = openSync(path, \"r\");\n\n try {\n const buffer = Buffer.alloc(HEAD_BYTES);\n const read = readSync(fd, buffer, 0, HEAD_BYTES, 0);\n\n return buffer.toString(\"utf8\", 0, read);\n } finally {\n closeSync(fd);\n }\n}\n\n/** Whether any source file under `dir` opens with \"use client\". */\nexport function hasClientDirective(dir: string): boolean {\n return someSourceFile(dir, (path) => DIRECTIVE.test(headOf(path)));\n}\n\n/** An import of react-dom's server renderer, static or dynamic, ESM or CJS. */\nconst SERVER_RENDERER_IMPORT = /[\"']react-dom\\/server(?:\\.[a-z]+)?[\"']/;\n\n/** The largest file worth reading whole for an import: a bundled dist, not a data table. */\nconst WHOLE_FILE_CAP = 512 * 1024;\n\n/**\n * Whether any source file under `dir` imports react-dom/server.\n *\n * For a dependency left external where server components render: its\n * imports are never resolved by the build, so the renderer stub that names\n * the app file for a direct import never sees this one. @react-email/render\n * is the usual case - an action imports it, the build says nothing, and the\n * first call throws React's refusal at a visitor.\n */\nexport function importsServerRenderer(dir: string): boolean {\n return someSourceFile(dir, (path) => {\n try {\n if (statSync(path).size > WHOLE_FILE_CAP) return false;\n\n return SERVER_RENDERER_IMPORT.test(readFileSync(path, \"utf8\"));\n } catch {\n return false;\n }\n });\n}\n\n/** Whether `test` holds for some source file under `dir`, within the cap. */\nfunction someSourceFile(dir: string, test: (path: string) => boolean): boolean {\n const pending = [dir];\n let seen = 0;\n\n while (pending.length) {\n const current = pending.pop()!;\n let entries: string[];\n\n try {\n entries = readdirSync(current);\n } catch {\n continue;\n }\n\n for (const entry of entries) {\n if (entry === \"node_modules\" || entry.startsWith(\".\")) continue;\n\n const path = join(current, entry);\n let stat;\n\n try {\n stat = statSync(path);\n } catch {\n continue;\n }\n\n if (stat.isDirectory()) {\n pending.push(path);\n continue;\n }\n\n if (!SOURCE.test(entry)) continue;\n if (++seen > FILE_CAP) return false;\n\n try {\n if (test(path)) return true;\n } catch {\n // Unreadable is neither.\n }\n }\n }\n\n return false;\n}\n\n/** Whether a package says it uses React, as a peer or as a dependency. */\nfunction usesReact(manifest: PackageJson | null): boolean {\n return !!manifest && (\"react\" in (manifest.peerDependencies ?? {}) || \"react\" in (manifest.dependencies ?? {}));\n}\n\n/**\n * The dependencies of a bundled package that use React, and theirs.\n *\n * Bundling the package that carries the directive is not enough: the\n * runtime it calls into - @stencil/react-output-target under a generated\n * wrapper - is its own package, with react as a peer, and plugin-rsc's\n * crawl never reached it because it only descends through packages that\n * declared React. Left external, that runtime loads React through Node\n * while everything else gets it through Vite: two copies, and the hooks\n * dispatcher is null in one of them. Every React-using dependency under\n * a bundled package is bundled with it.\n */\nfunction reactDependenciesOf(dir: string, into: Set<string>, seen: Set<string>): void {\n const manifest = readJson(join(dir, \"package.json\"));\n\n for (const name of Object.keys(manifest?.dependencies ?? {})) {\n if (OWN.has(name) || seen.has(name)) continue;\n\n seen.add(name);\n\n // From where the package really is: under Bun's store a symlink in the\n // app's node_modules points into .bun/<pkg>@<v>/node_modules/<pkg>, and\n // the dependency sits beside it there, not beside the symlink.\n const depDir = packageDir(name, realpathOf(dir));\n\n if (!depDir || !usesReact(readJson(join(depDir, \"package.json\")))) continue;\n\n into.add(name);\n reactDependenciesOf(depDir, into, seen);\n }\n}\n\n/**\n * Where a package is, anywhere in the project's dependency graph.\n *\n * For a polyfill a dependency of a dependency needs: not hoisted to the\n * project under a strict store, so not resolvable from the project by\n * name, and still what has to be evaluated first. A breadth-first walk of\n * the manifests, from each package's real path, bounded in depth. The\n * entry file is what `main` names, or index.js.\n */\nexport function packageEntryInGraph(root: string, name: string, depth = 5): string | null {\n const seen = new Set<string>();\n let frontier = [root];\n\n for (let level = 0; level <= depth && frontier.length; level++) {\n const next: string[] = [];\n\n for (const dir of frontier) {\n const found = packageDir(name, realpathOf(dir));\n\n if (found) {\n const manifest = readJson(join(found, \"package.json\")) as (PackageJson & { main?: string }) | null;\n const entry = join(found, manifest?.main ?? \"index.js\");\n\n return existsSync(entry) ? entry : existsSync(entry + \".js\") ? entry + \".js\" : null;\n }\n\n const manifest = readJson(join(dir, \"package.json\"));\n\n for (const dep of Object.keys(manifest?.dependencies ?? {})) {\n if (seen.has(dep)) continue;\n\n seen.add(dep);\n\n const depDir = packageDir(dep, realpathOf(dir));\n\n if (depDir) next.push(depDir);\n }\n }\n\n frontier = next;\n }\n\n return null;\n}\n\n/**\n * The direct dependencies of the project at `root` that plugin-rsc would\n * leave external and that carry a \"use client\" file - and the React-using\n * dependencies under each, which have to be bundled with it.\n */\nexport function clientPackages(root: string): string[] {\n const own = readJson(join(root, \"package.json\"));\n\n if (!own?.dependencies) return [];\n\n const found = new Set<string>();\n\n for (const name of Object.keys(own.dependencies)) {\n if (OWN.has(name) || name.startsWith(\"@rsc-kit/\")) continue;\n\n const dir = packageDir(name, root);\n\n if (!dir) continue;\n\n const manifest = readJson(join(dir, \"package.json\"));\n\n // plugin-rsc's own rule: react as a peer, and the package is bundled already.\n if (manifest?.peerDependencies && \"react\" in manifest.peerDependencies) continue;\n\n if (!hasClientDirective(dir)) continue;\n\n found.add(name);\n reactDependenciesOf(dir, found, new Set([name]));\n }\n\n return [...found].sort();\n}\n"]}
1
+ {"version":3,"file":"clientPackages.js","sourceRoot":"","sources":["../src/clientPackages.ts"],"names":[],"mappings":"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","sourcesContent":["// Dependencies with \"use client\" files that plugin-rsc would leave external.\n//\n// plugin-rsc bundles a dependency into the server graphs - and so sees its\n// directives - only when the dependency declares react as a peer. That is\n// the right signal for a library written for React. It misses the package\n// that imports react and never says so: a component wrapper generated by a\n// tool, a workspace package with react under dependencies, a published\n// package whose author forgot. Left external, its \"use client\" is a string\n// nobody reads, the rsc environment loads it under the react-server\n// condition, and the first useState inside it fails at render with an error\n// that names React and not the package.\n//\n// So the direct dependencies are read once at config time: any that plugin-rsc\n// would not bundle, and that carries a \"use client\" directive in one of its\n// files, is bundled here. Direct dependencies only - a directive two levels\n// down is the concern of the package between, which declared it.\n\nimport { existsSync, readdirSync, readFileSync, realpathSync, statSync, openSync, readSync, closeSync } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\n\n/** A directive at the top of a file: comments may precede it, code may not. */\nconst DIRECTIVE = /^(?:\\s|\\/\\/[^\\n]*\\n|\\/\\*[\\s\\S]*?\\*\\/)*(?:'use client'|\"use client\")\\s*;?/;\n\nconst SOURCE = /\\.(?:m?js|cjs|jsx)$/;\n\n/** The most files read per package before giving up: a bound, not a budget. */\nconst FILE_CAP = 3000;\n\n/** Enough of a file to hold a licence header and the directive after it. */\nconst HEAD_BYTES = 2048;\n\n/** The packages plugin-rsc handles itself, and this one. */\nconst OWN = new Set([\"react\", \"react-dom\", \"react-server-dom-webpack\", \"@vitejs/plugin-rsc\"]);\n\ninterface PackageJson {\n name?: string;\n dependencies?: Record<string, string>;\n peerDependencies?: Record<string, string>;\n}\n\nfunction readJson(path: string): PackageJson | null {\n try {\n return JSON.parse(readFileSync(path, \"utf8\")) as PackageJson;\n } catch {\n return null;\n }\n}\n\n/** Where `name` is installed, walking up from `from` the way resolution does. */\nexport function packageDir(name: string, from: string): string | null {\n let dir = from;\n\n for (;;) {\n const candidate = join(dir, \"node_modules\", name);\n\n if (existsSync(join(candidate, \"package.json\"))) return candidate;\n\n const parent = dirname(dir);\n\n if (parent === dir) return null;\n\n dir = parent;\n }\n}\n\nfunction realpathOf(path: string): string {\n try {\n return realpathSync(path);\n } catch {\n return path;\n }\n}\n\nfunction headOf(path: string): string {\n const fd = openSync(path, \"r\");\n\n try {\n const buffer = Buffer.alloc(HEAD_BYTES);\n const read = readSync(fd, buffer, 0, HEAD_BYTES, 0);\n\n return buffer.toString(\"utf8\", 0, read);\n } finally {\n closeSync(fd);\n }\n}\n\n/** Whether any source file under `dir` opens with \"use client\". */\nexport function hasClientDirective(dir: string): boolean {\n return someSourceFile(dir, (path) => DIRECTIVE.test(headOf(path)));\n}\n\n/** An import of react-dom's server renderer, static or dynamic, ESM or CJS. */\nconst SERVER_RENDERER_IMPORT = /[\"']react-dom\\/server(?:\\.[a-z]+)?[\"']/;\n\n/** The largest file worth reading whole for an import: a bundled dist, not a data table. */\nconst WHOLE_FILE_CAP = 512 * 1024;\n\n/**\n * Whether any source file under `dir` imports react-dom/server.\n *\n * For a dependency left external where server components render: its\n * imports are never resolved by the build, so the renderer stub that names\n * the app file for a direct import never sees this one. @react-email/render\n * is the usual case - an action imports it, the build says nothing, and the\n * first call throws React's refusal at a visitor.\n */\nexport function importsServerRenderer(dir: string): boolean {\n return someSourceFile(dir, (path) => {\n try {\n if (statSync(path).size > WHOLE_FILE_CAP) return false;\n\n return SERVER_RENDERER_IMPORT.test(readFileSync(path, \"utf8\"));\n } catch {\n return false;\n }\n });\n}\n\n/** Whether `test` holds for some source file under `dir`, within the cap. */\nfunction someSourceFile(dir: string, test: (path: string) => boolean): boolean {\n const pending = [dir];\n let seen = 0;\n\n while (pending.length) {\n const current = pending.pop()!;\n let entries: string[];\n\n try {\n entries = readdirSync(current);\n } catch {\n continue;\n }\n\n for (const entry of entries) {\n if (entry === \"node_modules\" || entry.startsWith(\".\")) continue;\n\n const path = join(current, entry);\n let stat;\n\n try {\n stat = statSync(path);\n } catch {\n continue;\n }\n\n if (stat.isDirectory()) {\n pending.push(path);\n continue;\n }\n\n if (!SOURCE.test(entry)) continue;\n if (++seen > FILE_CAP) return false;\n\n try {\n if (test(path)) return true;\n } catch {\n // Unreadable is neither.\n }\n }\n }\n\n return false;\n}\n\n/** Whether a package says it uses React, as a peer or as a dependency. */\nfunction usesReact(manifest: PackageJson | null): boolean {\n return !!manifest && (\"react\" in (manifest.peerDependencies ?? {}) || \"react\" in (manifest.dependencies ?? {}));\n}\n\n/**\n * The dependencies of a bundled package that use React, and theirs.\n *\n * Bundling the package that carries the directive is not enough: the\n * runtime it calls into - @stencil/react-output-target under a generated\n * wrapper - is its own package, with react as a peer, and plugin-rsc's\n * crawl never reached it because it only descends through packages that\n * declared React. Left external, that runtime loads React through Node\n * while everything else gets it through Vite: two copies, and the hooks\n * dispatcher is null in one of them. Every React-using dependency under\n * a bundled package is bundled with it.\n */\nfunction reactDependenciesOf(dir: string, into: Set<string>, seen: Set<string>): void {\n const manifest = readJson(join(dir, \"package.json\"));\n\n for (const name of Object.keys(manifest?.dependencies ?? {})) {\n if (OWN.has(name) || seen.has(name)) continue;\n\n seen.add(name);\n\n // From where the package really is: under Bun's store a symlink in the\n // app's node_modules points into .bun/<pkg>@<v>/node_modules/<pkg>, and\n // the dependency sits beside it there, not beside the symlink.\n const depDir = packageDir(name, realpathOf(dir));\n\n if (!depDir || !usesReact(readJson(join(depDir, \"package.json\")))) continue;\n\n into.add(name);\n reactDependenciesOf(depDir, into, seen);\n }\n}\n\n/**\n * Of the packages a build would trace by name, the ones the tracer can find.\n *\n * Nitro's tracer resolves a name the way Node does: up the directory tree\n * from the project, `node_modules` by `node_modules`. A package that is in\n * the graph only through a dependency - under an isolated store, never\n * hoisted - is not resolvable that way, and naming it earns \"could not\n * resolve `traceInclude` entry\" once per build, for a package the tracer\n * reaches through its dependant anyway. So: present by name, or not named.\n */\nexport function traceableByName(names: readonly string[], root: string): string[] {\n return names.filter((name) => packageDir(name, root) !== null);\n}\n\n/**\n * The polyfills to keep outside the bundle, as patterns.\n *\n * A polyfill is evaluated first by the server's entry (see the Nitro module)\n * and imported again by the dependency that checks for it. Bundled, the\n * bundler merges the one module into the chunk of that dependency, and the\n * chunk's own imports - tsyringe, under @peculiar/x509 - evaluate before the\n * chunk's body installs the polyfill: the server did not boot. Left\n * external, the entry's import of it is the first import there is, and the\n * tracer copies the file by the path it was resolved to. A pattern rather\n * than a name so the tracer does not also try to resolve the name from the\n * project root, which under an isolated store it cannot.\n */\nexport function polyfillsToTrace(names: readonly string[], root: string): RegExp[] {\n return names\n .filter((name) => packageEntryInGraph(root, name) !== null)\n .map((name) => new RegExp(name.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\")));\n}\n\n/**\n * Where a package is, anywhere in the project's dependency graph.\n *\n * For a polyfill a dependency of a dependency needs: not hoisted to the\n * project under a strict store, so not resolvable from the project by\n * name, and still what has to be evaluated first. A breadth-first walk of\n * the manifests, from each package's real path, bounded in depth. The\n * entry file is what `main` names, or index.js.\n */\nexport function packageEntryInGraph(root: string, name: string, depth = 5): string | null {\n const seen = new Set<string>();\n let frontier = [root];\n\n for (let level = 0; level <= depth && frontier.length; level++) {\n const next: string[] = [];\n\n for (const dir of frontier) {\n const found = packageDir(name, realpathOf(dir));\n\n if (found) {\n const manifest = readJson(join(found, \"package.json\")) as (PackageJson & { main?: string }) | null;\n const entry = join(found, manifest?.main ?? \"index.js\");\n\n return existsSync(entry) ? entry : existsSync(entry + \".js\") ? entry + \".js\" : null;\n }\n\n const manifest = readJson(join(dir, \"package.json\"));\n\n for (const dep of Object.keys(manifest?.dependencies ?? {})) {\n if (seen.has(dep)) continue;\n\n seen.add(dep);\n\n const depDir = packageDir(dep, realpathOf(dir));\n\n if (depDir) next.push(depDir);\n }\n }\n\n frontier = next;\n }\n\n return null;\n}\n\n/**\n * The direct dependencies of the project at `root` that plugin-rsc would\n * leave external and that carry a \"use client\" file - and the React-using\n * dependencies under each, which have to be bundled with it.\n */\nexport function clientPackages(root: string): string[] {\n const own = readJson(join(root, \"package.json\"));\n\n if (!own?.dependencies) return [];\n\n const found = new Set<string>();\n\n for (const name of Object.keys(own.dependencies)) {\n if (OWN.has(name) || name.startsWith(\"@rsc-kit/\")) continue;\n\n const dir = packageDir(name, root);\n\n if (!dir) continue;\n\n const manifest = readJson(join(dir, \"package.json\"));\n\n // plugin-rsc's own rule: react as a peer, and the package is bundled already.\n if (manifest?.peerDependencies && \"react\" in manifest.peerDependencies) continue;\n\n if (!hasClientDirective(dir)) continue;\n\n found.add(name);\n reactDependenciesOf(dir, found, new Set([name]));\n }\n\n return [...found].sort();\n}\n"]}
package/dist/files.d.ts CHANGED
@@ -40,9 +40,15 @@ export declare function prerenderedBeside(moduleUrl: string, dirName: string, le
40
40
  /**
41
41
  * The entry a binary is compiled from, written beside the server.
42
42
  *
43
- * Two imports, in this order: the inline module first, so its pages are
44
- * handed over before the server's first request; the server second. Bun
45
- * embeds both. `bun build --compile .output/server/compile.mjs`.
43
+ * Two static imports, in this order: the inline module first - evaluating
44
+ * it hands the pages over - and the server second. A module's imports are
45
+ * evaluated in order, before its own body, so the pages are in place before
46
+ * the server's first line runs. Bun embeds both.
47
+ *
48
+ * Static rather than `await import()`: a top-level await is what made Bun
49
+ * skip `--bytecode` for this entry, silently, byte for byte - and a port
50
+ * whose other Nitro app compiled with it asked why this one could not.
51
+ * `bun build --compile --bytecode .output/server/compile.mjs` works now.
46
52
  */
47
53
  export declare function compileEntrySource(dirName: string): string;
48
54
  /** The name the build writes the inline module under, beside the stored pages' directory. */
package/dist/files.js CHANGED
@@ -192,9 +192,15 @@ function safeUrl(relative, base) {
192
192
  /**
193
193
  * The entry a binary is compiled from, written beside the server.
194
194
  *
195
- * Two imports, in this order: the inline module first, so its pages are
196
- * handed over before the server's first request; the server second. Bun
197
- * embeds both. `bun build --compile .output/server/compile.mjs`.
195
+ * Two static imports, in this order: the inline module first - evaluating
196
+ * it hands the pages over - and the server second. A module's imports are
197
+ * evaluated in order, before its own body, so the pages are in place before
198
+ * the server's first line runs. Bun embeds both.
199
+ *
200
+ * Static rather than `await import()`: a top-level await is what made Bun
201
+ * skip `--bytecode` for this entry, silently, byte for byte - and a port
202
+ * whose other Nitro app compiled with it asked why this one could not.
203
+ * `bun build --compile --bytecode .output/server/compile.mjs` works now.
198
204
  */
199
205
  export function compileEntrySource(dirName) {
200
206
  return ('// The entry to compile into one binary: bun build --compile .output/server/compile.mjs\n' +
@@ -202,11 +208,10 @@ export function compileEntrySource(dirName) {
202
208
  '// A binary carries what is imported by name. The server reads its frozen pages\n' +
203
209
  '// through a computed import, which a compile cannot see - imported here, they\n' +
204
210
  '// travel inside the binary and are handed to the server before it starts.\n' +
205
- `import pages from "./${inlineModuleName(dirName)}";\n` +
206
- '\n' +
207
- `globalThis[Symbol.for("rsc-kit.embedded-pages")] = pages;\n` +
208
- '\n' +
209
- 'await import("./index.mjs");\n');
211
+ '// Two static imports in this order, and no top-level await: imports evaluate\n' +
212
+ '// in order, and an await here is what stopped --bytecode from applying.\n' +
213
+ `import "./${inlineModuleName(dirName)}";\n` +
214
+ 'import "./index.mjs";\n');
210
215
  }
211
216
  /** The name the build writes the inline module under, beside the stored pages' directory. */
212
217
  export function inlineModuleName(dirName) {
@@ -229,10 +234,15 @@ export async function inlineModuleSource(dir) {
229
234
  }
230
235
  };
231
236
  await walk('');
237
+ // Exported for the Worker, which imports it when the directory is not
238
+ // there, and handed over as a global for the binary, whose compile entry
239
+ // imports it first for exactly this side effect. The same data either way.
232
240
  return ('// @generated by the RSC build: the stored pages, for a runtime with no filesystem.\n' +
233
- 'export default ' +
241
+ 'const pages = ' +
234
242
  JSON.stringify(entries) +
235
- '\n');
243
+ ';\n' +
244
+ 'globalThis[Symbol.for("rsc-kit.embedded-pages")] = pages;\n' +
245
+ 'export default pages;\n');
236
246
  }
237
247
  function contentTypeOf(pathname) {
238
248
  if (pathname.endsWith('.js'))
package/dist/files.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"files.js","sourceRoot":"","sources":["../src/files.ts"],"names":[],"mappings":"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","sourcesContent":["// Reading built output off a disk.\n//\n// Kept apart from the host on purpose: it is the only part of serving an RSC\n// app that assumes a filesystem, and plenty of places to run one do not have\n// one. Nothing in `@rsc-kit/core/host` imports this module, so a bundle for a\n// host without a filesystem carries no reference to `node:fs` at all.\n//\n// Assets are not here any more — Nitro publishes and serves those. What is\n// left is the prerendered output: written by the build, read by the server it\n// generates, and copied by a static export.\n\nimport { cp, mkdir, readdir, readFile, writeFile } from 'node:fs/promises'\nimport { dirname, join } from 'node:path'\n\n/**\n * Read what the prerenderer wrote, from a directory.\n *\n * Missing is an answer, not an error: a partial prerender is a valid state,\n * and anything not found here is rendered on demand instead.\n */\nexport function prerenderedFrom(dir: string) {\n /**\n * What is on disk, listed once.\n *\n * A miss used to be a thrown ENOENT, and misses are the common case: the\n * host asks for `{url}.html`, then `{url}.ppr.html`, then the route's\n * pattern — so every request to a page served by a pattern shell threw and\n * caught two exceptions before finding anything. Measured at 3,000 req/s\n * against 28,000 for the same page read from memory.\n *\n * Contents are cached for the life of the process. A build's output does\n * not change under a running server - a deploy restarts it, and a server\n * that outlived a rebuild would be serving a stale engine against new\n * pages anyway - so the first read is the only one. Under load the same\n * page used to be opened, read and closed thousands of times a second,\n * which a profile showed as the one cost on a stored page that was ours\n * rather than React's. Capped by bytes, so a site with more pages than\n * memory holds the hot ones and re-reads the rest.\n */\n let present: Promise<Set<string>> | null = null\n\n const CONTENT_CAP_BYTES = 64 * 1024 * 1024\n const contents = new Map<string, string>()\n let held = 0\n\n const remember = (name: string, text: string): void => {\n const size = text.length * 2\n\n if (size > CONTENT_CAP_BYTES / 4) return\n\n while (held + size > CONTENT_CAP_BYTES && contents.size > 0) {\n const oldest = contents.keys().next().value as string\n held -= contents.get(oldest)!.length * 2\n contents.delete(oldest)\n }\n\n contents.set(name, text)\n held += size\n }\n\n const listing = async (): Promise<Set<string>> => {\n present ??= readdir(dir, { recursive: true })\n .then((names) => new Set(names.map((n) => String(n).replace(/\\\\/g, '/'))))\n // An absent directory is an empty one: a partial prerender is a valid\n // state, and everything falls through to being rendered.\n .catch(() => new Set<string>())\n\n return await present\n }\n\n return async (name: string): Promise<string | null> => {\n if (name.includes('..')) return null\n\n if (!(await listing()).has(name)) return null\n\n const cached = contents.get(name)\n\n if (cached !== undefined) return cached\n\n try {\n const text = await readFile(join(dir, name), 'utf-8')\n\n remember(name, text)\n\n return text\n } catch {\n return null\n }\n }\n}\n\n/**\n * The same reader, for a directory whose absolute path is not known until the\n * server runs.\n *\n * Nitro bundles the generated entry to a different depth depending on the\n * preset — `.output/server/index.mjs` for one, `.output/server/_ssr/rsc.mjs`\n * for another — so neither a fixed relative path nor a path baked in at build\n * time survives: the first is wrong for half the presets, and the second stops\n * being true the moment `.output/` is copied to the machine that serves it.\n *\n * So the caller passes its own `import.meta.url` and the directory is found by\n * walking up from it. A few levels, bounded, and resolved once.\n *\n * prerendered: prerenderedBeside(import.meta.url, 'rsc-static')\n *\n * Not finding it is a valid state and not an error: nothing was frozen, or\n * this host has no filesystem to have frozen it on. Every page renders live,\n * which is what happens today everywhere.\n */\n/**\n * Where a compiled binary puts the frozen pages.\n *\n * `bun build --compile` embeds what is imported statically and nothing a\n * computed import names, so the walk below finds no directory and no\n * module inside a binary. The build writes `compile.mjs` beside the\n * server: it imports the inline module by name - which embeds it - and\n * hands the pages over here before importing the server. Shared as a\n * global under a registered symbol, because the wrapper and this module\n * are bundled apart.\n */\nexport const EMBEDDED_PAGES = Symbol.for('rsc-kit.embedded-pages')\n\nfunction embeddedPages(): Record<string, string> | null {\n const pages = (globalThis as Record<symbol, unknown>)[EMBEDDED_PAGES]\n\n return pages && typeof pages === 'object' ? (pages as Record<string, string>) : null\n}\n\nexport function prerenderedBeside(moduleUrl: string, dirName: string, levels = 4) {\n let reader: Promise<(name: string) => Promise<string | null>> | null = null\n\n const resolveDir = async (): Promise<(name: string) => Promise<string | null>> => {\n const embedded = embeddedPages()\n\n if (embedded) return async (name) => embedded[name] ?? null\n\n let dir: string | null = null\n\n try {\n const { fileURLToPath } = await import('node:url')\n\n dir = dirname(fileURLToPath(moduleUrl))\n } catch {\n // No filesystem, or no file: url to speak of. The inline module below\n // is the only place the pages can be.\n }\n\n if (dir !== null) {\n for (let i = 0; i <= levels; i++) {\n const candidate = join(dir, ...Array(i).fill('..'), dirName)\n\n try {\n await readdir(candidate)\n\n return prerenderedFrom(candidate)\n } catch {\n // Not at this level. Keep walking.\n }\n }\n }\n\n const inline = await inlinePrerendered(moduleUrl, `${dirName}-inline.mjs`, levels)\n\n return inline ? async (name) => inline[name] ?? null : async () => null\n }\n\n return async (name: string): Promise<string | null> => {\n reader ??= resolveDir()\n\n return await (await reader)(name)\n }\n}\n\n/**\n * The frozen pages as one module, for a runtime with no filesystem.\n *\n * A Worker cannot readdir. Its modules are what wrangler uploaded, and a\n * sibling .mjs beside the bundle is one of them - so the build writes every\n * stored file into `rsc-static-inline.mjs` as strings, and this walks up from\n * the caller the way the disk reader does, importing rather than reading.\n * The specifier is computed, and marked for the bundler to leave alone: at\n * bundle time the file does not exist yet, because it is written by the\n * prerender that runs the bundle.\n *\n * Two forms of the specifier, because runtimes disagree. workerd resolves a\n * relative string against the importing module's name and refuses a url;\n * Node and Bun want the url, since a relative string would resolve against\n * this file rather than the caller's.\n */\nasync function inlinePrerendered(\n moduleUrl: string,\n fileName: string,\n levels: number,\n): Promise<Record<string, string> | null> {\n for (let i = 0; i <= levels; i++) {\n const relative = './' + '../'.repeat(i) + fileName\n\n for (const specifier of [relative, safeUrl(relative, moduleUrl)]) {\n if (!specifier) continue\n\n try {\n const loaded = (await import(/* @vite-ignore */ specifier)) as { default?: Record<string, string> }\n\n if (loaded.default && typeof loaded.default === 'object') return loaded.default\n } catch {\n // Not there, or not at this level.\n }\n }\n }\n\n return null\n}\n\nfunction safeUrl(relative: string, base: string): string | null {\n try {\n return new URL(relative, base).href\n } catch {\n return null\n }\n}\n\n/**\n * The entry a binary is compiled from, written beside the server.\n *\n * Two imports, in this order: the inline module first, so its pages are\n * handed over before the server's first request; the server second. Bun\n * embeds both. `bun build --compile .output/server/compile.mjs`.\n */\nexport function compileEntrySource(dirName: string): string {\n return (\n '// The entry to compile into one binary: bun build --compile .output/server/compile.mjs\\n' +\n '//\\n' +\n '// A binary carries what is imported by name. The server reads its frozen pages\\n' +\n '// through a computed import, which a compile cannot see - imported here, they\\n' +\n '// travel inside the binary and are handed to the server before it starts.\\n' +\n `import pages from \"./${inlineModuleName(dirName)}\";\\n` +\n '\\n' +\n `globalThis[Symbol.for(\"rsc-kit.embedded-pages\")] = pages;\\n` +\n '\\n' +\n 'await import(\"./index.mjs\");\\n'\n )\n}\n\n/** The name the build writes the inline module under, beside the stored pages' directory. */\nexport function inlineModuleName(dirName: string): string {\n return `${dirName}-inline.mjs`\n}\n\n/**\n * Every stored file under `dir`, as the source of the inline module.\n * Strings only: a frozen page, its flight payload, its meta, an api answer.\n */\nexport async function inlineModuleSource(dir: string): Promise<string> {\n const { readdir: list, readFile: read } = await import('node:fs/promises')\n const entries: Record<string, string> = {}\n\n const walk = async (sub: string): Promise<void> => {\n for (const entry of await list(join(dir, sub), { withFileTypes: true })) {\n const rel = sub ? `${sub}/${entry.name}` : entry.name\n\n if (entry.isDirectory()) await walk(rel)\n else entries[rel] = await read(join(dir, rel), 'utf-8')\n }\n }\n\n await walk('')\n\n return (\n '// @generated by the RSC build: the stored pages, for a runtime with no filesystem.\\n' +\n 'export default ' +\n JSON.stringify(entries) +\n '\\n'\n )\n}\n\nfunction contentTypeOf(pathname: string): string {\n if (pathname.endsWith('.js')) return 'text/javascript; charset=utf-8'\n if (pathname.endsWith('.css')) return 'text/css; charset=utf-8'\n if (pathname.endsWith('.map')) return 'application/json; charset=utf-8'\n if (pathname.endsWith('.svg')) return 'image/svg+xml'\n if (pathname.endsWith('.woff2')) return 'font/woff2'\n\n return 'application/octet-stream'\n}\n\n/**\n * Write build output into a directory.\n *\n * The sink `prerender` and `exportSite` take. Names are relative and may\n * contain directories — `docs/index.html` — so each one's parent is created\n * as it goes.\n */\nexport function writeTo(dir: string): ((name: string, contents: string) => Promise<void>) & { dir: string } {\n const write = async (name: string, contents: string): Promise<void> => {\n const path = join(dir, name)\n\n await mkdir(dirname(path), { recursive: true })\n await writeFile(path, contents)\n }\n\n // Where it wrote, for a caller that has to read it back. The embeddable\n // module has to list these files, and being told the directory a second time\n // is a second place for it to be wrong.\n return Object.assign(write, { dir })\n}\n\n/**\n * Copy the built browser bundle into an exported site.\n *\n * Separate from the export itself because copying a tree of files is a\n * filesystem operation by nature — a deploy that uploads them to a bucket\n * has its own way to do that, and passes its own callback instead.\n */\nexport function copyAssets(from: string, to: string, url = '/assets/') {\n return async (): Promise<void> => {\n const at = url.replace(/^\\/+|\\/+$/g, '')\n\n if (at === '') return\n\n const target = join(to, at)\n\n await mkdir(dirname(target), { recursive: true })\n await cp(from, target, { recursive: true })\n }\n}\n"]}
1
+ {"version":3,"file":"files.js","sourceRoot":"","sources":["../src/files.ts"],"names":[],"mappings":"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","sourcesContent":["// Reading built output off a disk.\n//\n// Kept apart from the host on purpose: it is the only part of serving an RSC\n// app that assumes a filesystem, and plenty of places to run one do not have\n// one. Nothing in `@rsc-kit/core/host` imports this module, so a bundle for a\n// host without a filesystem carries no reference to `node:fs` at all.\n//\n// Assets are not here any more — Nitro publishes and serves those. What is\n// left is the prerendered output: written by the build, read by the server it\n// generates, and copied by a static export.\n\nimport { cp, mkdir, readdir, readFile, writeFile } from 'node:fs/promises'\nimport { dirname, join } from 'node:path'\n\n/**\n * Read what the prerenderer wrote, from a directory.\n *\n * Missing is an answer, not an error: a partial prerender is a valid state,\n * and anything not found here is rendered on demand instead.\n */\nexport function prerenderedFrom(dir: string) {\n /**\n * What is on disk, listed once.\n *\n * A miss used to be a thrown ENOENT, and misses are the common case: the\n * host asks for `{url}.html`, then `{url}.ppr.html`, then the route's\n * pattern — so every request to a page served by a pattern shell threw and\n * caught two exceptions before finding anything. Measured at 3,000 req/s\n * against 28,000 for the same page read from memory.\n *\n * Contents are cached for the life of the process. A build's output does\n * not change under a running server - a deploy restarts it, and a server\n * that outlived a rebuild would be serving a stale engine against new\n * pages anyway - so the first read is the only one. Under load the same\n * page used to be opened, read and closed thousands of times a second,\n * which a profile showed as the one cost on a stored page that was ours\n * rather than React's. Capped by bytes, so a site with more pages than\n * memory holds the hot ones and re-reads the rest.\n */\n let present: Promise<Set<string>> | null = null\n\n const CONTENT_CAP_BYTES = 64 * 1024 * 1024\n const contents = new Map<string, string>()\n let held = 0\n\n const remember = (name: string, text: string): void => {\n const size = text.length * 2\n\n if (size > CONTENT_CAP_BYTES / 4) return\n\n while (held + size > CONTENT_CAP_BYTES && contents.size > 0) {\n const oldest = contents.keys().next().value as string\n held -= contents.get(oldest)!.length * 2\n contents.delete(oldest)\n }\n\n contents.set(name, text)\n held += size\n }\n\n const listing = async (): Promise<Set<string>> => {\n present ??= readdir(dir, { recursive: true })\n .then((names) => new Set(names.map((n) => String(n).replace(/\\\\/g, '/'))))\n // An absent directory is an empty one: a partial prerender is a valid\n // state, and everything falls through to being rendered.\n .catch(() => new Set<string>())\n\n return await present\n }\n\n return async (name: string): Promise<string | null> => {\n if (name.includes('..')) return null\n\n if (!(await listing()).has(name)) return null\n\n const cached = contents.get(name)\n\n if (cached !== undefined) return cached\n\n try {\n const text = await readFile(join(dir, name), 'utf-8')\n\n remember(name, text)\n\n return text\n } catch {\n return null\n }\n }\n}\n\n/**\n * The same reader, for a directory whose absolute path is not known until the\n * server runs.\n *\n * Nitro bundles the generated entry to a different depth depending on the\n * preset — `.output/server/index.mjs` for one, `.output/server/_ssr/rsc.mjs`\n * for another — so neither a fixed relative path nor a path baked in at build\n * time survives: the first is wrong for half the presets, and the second stops\n * being true the moment `.output/` is copied to the machine that serves it.\n *\n * So the caller passes its own `import.meta.url` and the directory is found by\n * walking up from it. A few levels, bounded, and resolved once.\n *\n * prerendered: prerenderedBeside(import.meta.url, 'rsc-static')\n *\n * Not finding it is a valid state and not an error: nothing was frozen, or\n * this host has no filesystem to have frozen it on. Every page renders live,\n * which is what happens today everywhere.\n */\n/**\n * Where a compiled binary puts the frozen pages.\n *\n * `bun build --compile` embeds what is imported statically and nothing a\n * computed import names, so the walk below finds no directory and no\n * module inside a binary. The build writes `compile.mjs` beside the\n * server: it imports the inline module by name - which embeds it - and\n * hands the pages over here before importing the server. Shared as a\n * global under a registered symbol, because the wrapper and this module\n * are bundled apart.\n */\nexport const EMBEDDED_PAGES = Symbol.for('rsc-kit.embedded-pages')\n\nfunction embeddedPages(): Record<string, string> | null {\n const pages = (globalThis as Record<symbol, unknown>)[EMBEDDED_PAGES]\n\n return pages && typeof pages === 'object' ? (pages as Record<string, string>) : null\n}\n\nexport function prerenderedBeside(moduleUrl: string, dirName: string, levels = 4) {\n let reader: Promise<(name: string) => Promise<string | null>> | null = null\n\n const resolveDir = async (): Promise<(name: string) => Promise<string | null>> => {\n const embedded = embeddedPages()\n\n if (embedded) return async (name) => embedded[name] ?? null\n\n let dir: string | null = null\n\n try {\n const { fileURLToPath } = await import('node:url')\n\n dir = dirname(fileURLToPath(moduleUrl))\n } catch {\n // No filesystem, or no file: url to speak of. The inline module below\n // is the only place the pages can be.\n }\n\n if (dir !== null) {\n for (let i = 0; i <= levels; i++) {\n const candidate = join(dir, ...Array(i).fill('..'), dirName)\n\n try {\n await readdir(candidate)\n\n return prerenderedFrom(candidate)\n } catch {\n // Not at this level. Keep walking.\n }\n }\n }\n\n const inline = await inlinePrerendered(moduleUrl, `${dirName}-inline.mjs`, levels)\n\n return inline ? async (name) => inline[name] ?? null : async () => null\n }\n\n return async (name: string): Promise<string | null> => {\n reader ??= resolveDir()\n\n return await (await reader)(name)\n }\n}\n\n/**\n * The frozen pages as one module, for a runtime with no filesystem.\n *\n * A Worker cannot readdir. Its modules are what wrangler uploaded, and a\n * sibling .mjs beside the bundle is one of them - so the build writes every\n * stored file into `rsc-static-inline.mjs` as strings, and this walks up from\n * the caller the way the disk reader does, importing rather than reading.\n * The specifier is computed, and marked for the bundler to leave alone: at\n * bundle time the file does not exist yet, because it is written by the\n * prerender that runs the bundle.\n *\n * Two forms of the specifier, because runtimes disagree. workerd resolves a\n * relative string against the importing module's name and refuses a url;\n * Node and Bun want the url, since a relative string would resolve against\n * this file rather than the caller's.\n */\nasync function inlinePrerendered(\n moduleUrl: string,\n fileName: string,\n levels: number,\n): Promise<Record<string, string> | null> {\n for (let i = 0; i <= levels; i++) {\n const relative = './' + '../'.repeat(i) + fileName\n\n for (const specifier of [relative, safeUrl(relative, moduleUrl)]) {\n if (!specifier) continue\n\n try {\n const loaded = (await import(/* @vite-ignore */ specifier)) as { default?: Record<string, string> }\n\n if (loaded.default && typeof loaded.default === 'object') return loaded.default\n } catch {\n // Not there, or not at this level.\n }\n }\n }\n\n return null\n}\n\nfunction safeUrl(relative: string, base: string): string | null {\n try {\n return new URL(relative, base).href\n } catch {\n return null\n }\n}\n\n/**\n * The entry a binary is compiled from, written beside the server.\n *\n * Two static imports, in this order: the inline module first - evaluating\n * it hands the pages over - and the server second. A module's imports are\n * evaluated in order, before its own body, so the pages are in place before\n * the server's first line runs. Bun embeds both.\n *\n * Static rather than `await import()`: a top-level await is what made Bun\n * skip `--bytecode` for this entry, silently, byte for byte - and a port\n * whose other Nitro app compiled with it asked why this one could not.\n * `bun build --compile --bytecode .output/server/compile.mjs` works now.\n */\nexport function compileEntrySource(dirName: string): string {\n return (\n '// The entry to compile into one binary: bun build --compile .output/server/compile.mjs\\n' +\n '//\\n' +\n '// A binary carries what is imported by name. The server reads its frozen pages\\n' +\n '// through a computed import, which a compile cannot see - imported here, they\\n' +\n '// travel inside the binary and are handed to the server before it starts.\\n' +\n '// Two static imports in this order, and no top-level await: imports evaluate\\n' +\n '// in order, and an await here is what stopped --bytecode from applying.\\n' +\n `import \"./${inlineModuleName(dirName)}\";\\n` +\n 'import \"./index.mjs\";\\n'\n )\n}\n\n/** The name the build writes the inline module under, beside the stored pages' directory. */\nexport function inlineModuleName(dirName: string): string {\n return `${dirName}-inline.mjs`\n}\n\n/**\n * Every stored file under `dir`, as the source of the inline module.\n * Strings only: a frozen page, its flight payload, its meta, an api answer.\n */\nexport async function inlineModuleSource(dir: string): Promise<string> {\n const { readdir: list, readFile: read } = await import('node:fs/promises')\n const entries: Record<string, string> = {}\n\n const walk = async (sub: string): Promise<void> => {\n for (const entry of await list(join(dir, sub), { withFileTypes: true })) {\n const rel = sub ? `${sub}/${entry.name}` : entry.name\n\n if (entry.isDirectory()) await walk(rel)\n else entries[rel] = await read(join(dir, rel), 'utf-8')\n }\n }\n\n await walk('')\n\n // Exported for the Worker, which imports it when the directory is not\n // there, and handed over as a global for the binary, whose compile entry\n // imports it first for exactly this side effect. The same data either way.\n return (\n '// @generated by the RSC build: the stored pages, for a runtime with no filesystem.\\n' +\n 'const pages = ' +\n JSON.stringify(entries) +\n ';\\n' +\n 'globalThis[Symbol.for(\"rsc-kit.embedded-pages\")] = pages;\\n' +\n 'export default pages;\\n'\n )\n}\n\nfunction contentTypeOf(pathname: string): string {\n if (pathname.endsWith('.js')) return 'text/javascript; charset=utf-8'\n if (pathname.endsWith('.css')) return 'text/css; charset=utf-8'\n if (pathname.endsWith('.map')) return 'application/json; charset=utf-8'\n if (pathname.endsWith('.svg')) return 'image/svg+xml'\n if (pathname.endsWith('.woff2')) return 'font/woff2'\n\n return 'application/octet-stream'\n}\n\n/**\n * Write build output into a directory.\n *\n * The sink `prerender` and `exportSite` take. Names are relative and may\n * contain directories — `docs/index.html` — so each one's parent is created\n * as it goes.\n */\nexport function writeTo(dir: string): ((name: string, contents: string) => Promise<void>) & { dir: string } {\n const write = async (name: string, contents: string): Promise<void> => {\n const path = join(dir, name)\n\n await mkdir(dirname(path), { recursive: true })\n await writeFile(path, contents)\n }\n\n // Where it wrote, for a caller that has to read it back. The embeddable\n // module has to list these files, and being told the directory a second time\n // is a second place for it to be wrong.\n return Object.assign(write, { dir })\n}\n\n/**\n * Copy the built browser bundle into an exported site.\n *\n * Separate from the export itself because copying a tree of files is a\n * filesystem operation by nature — a deploy that uploads them to a bucket\n * has its own way to do that, and passes its own callback instead.\n */\nexport function copyAssets(from: string, to: string, url = '/assets/') {\n return async (): Promise<void> => {\n const at = url.replace(/^\\/+|\\/+$/g, '')\n\n if (at === '') return\n\n const target = join(to, at)\n\n await mkdir(dirname(target), { recursive: true })\n await cp(from, target, { recursive: true })\n }\n}\n"]}
@@ -12,8 +12,13 @@
12
12
  // digest, and the digest is what this shows: the line to search the server
13
13
  // log for.
14
14
  import { createElement } from "react";
15
- const DEV = typeof import.meta !== "undefined" &&
16
- import.meta.env?.DEV === true;
15
+ // `import.meta.env.DEV` as Vite spells it, so the build replaces the whole
16
+ // expression with a literal. A `typeof import.meta` guard in front of it
17
+ // survived the build as a bare `import.meta` in the ssr chunk - the one
18
+ // thing Bun cannot lower when it compiles a binary's bytecode, so
19
+ // `--bytecode` failed on this file - and guarded nothing: where import.meta
20
+ // does not exist, a typeof of it does not parse either.
21
+ const DEV = Boolean(import.meta.env.DEV);
17
22
  const mono = "ui-monospace,SFMono-Regular,Menlo,monospace";
18
23
  const box = {
19
24
  maxWidth: "60rem",
@@ -1 +1 @@
1
- {"version":3,"file":"DefaultRouteError.js","sourceRoot":"","sources":["../../src/js/DefaultRouteError.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAEb,6DAA6D;AAC7D,EAAE;AACF,2EAA2E;AAC3E,6EAA6E;AAC7E,yEAAyE;AACzE,wEAAwE;AACxE,4DAA4D;AAC5D,EAAE;AACF,yEAAyE;AACzE,uEAAuE;AACvE,2EAA2E;AAC3E,WAAW;AAEX,OAAO,EAAE,aAAa,EAAkB,MAAM,OAAO,CAAC;AAGtD,MAAM,GAAG,GACP,OAAO,OAAO,IAAI,KAAK,WAAW;IACjC,OAAO,IAAoC,CAAC,GAAG,EAAE,GAAG,KAAK,IAAI,CAAC;AAEjE,MAAM,IAAI,GAAG,6CAA6C,CAAC;AAC3D,MAAM,GAAG,GAAG;IACV,QAAQ,EAAE,OAAO;IACjB,MAAM,EAAE,WAAW;IACnB,OAAO,EAAE,QAAQ;IACjB,IAAI,EAAE,WAAW,GAAG,IAAI;IACxB,KAAK,EAAE,SAAS;IAChB,UAAU,EAAE,SAAS;IACrB,MAAM,EAAE,mBAAmB;IAC3B,YAAY,EAAE,CAAC;CAChB,CAAC;AACF,MAAM,OAAO,GAAG;IACd,MAAM,EAAE,YAAY;IACpB,QAAQ,EAAE,EAAE;IACZ,UAAU,EAAE,GAAG;IACf,KAAK,EAAE,MAAM;CACd,CAAC;AACF,MAAM,GAAG,GAAG;IACV,UAAU,EAAE,UAAmB;IAC/B,SAAS,EAAE,YAAqB;IAChC,MAAM,EAAE,YAAY;IACpB,KAAK,EAAE,SAAS;CACjB,CAAC;AACF,MAAM,IAAI,GAAG,EAAE,MAAM,EAAE,YAAY,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC;AACxD,MAAM,MAAM,GAAG;IACb,SAAS,EAAE,MAAM;IACjB,OAAO,EAAE,aAAa;IACtB,MAAM,EAAE,mBAAmB;IAC3B,YAAY,EAAE,CAAC;IACf,UAAU,EAAE,aAAa;IACzB,KAAK,EAAE,MAAM;IACb,MAAM,EAAE,SAAS;IACjB,IAAI,EAAE,SAAS;CAChB,CAAC;AAEF,MAAM,UAAU,iBAAiB,CAAC,EAChC,KAAK,EACL,KAAK,GACW;IAChB,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,IAAI,KAAK,CAAC,IAAI,KAAK,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,GAAG,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;IAE3E,OAAO,aAAa,CAClB,MAAM,EACN,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE,oBAAoB,EAAE,EAAE,EAAE,EACvD,aAAa,CACX,IAAI,EACJ,EAAE,KAAK,EAAE,OAAO,EAAE,EAClB,GAAG,CAAC,CAAC,CAAC,iCAAiC,CAAC,CAAC,CAAC,sBAAsB,CACjE,EACD,GAAG;QACD,CAAC,CAAC,aAAa,CACX,KAAK,EACL,EAAE,KAAK,EAAE,GAAG,EAAE,EACd,KAAK,CAAC,KAAK,IAAI,IAAI,GAAG,KAAK,CAAC,OAAO,CACpC;QACH,CAAC,CAAC,aAAa,CACX,GAAG,EACH,EAAE,KAAK,EAAE,EAAE,MAAM,EAAE,CAAC,EAAE,EAAE,EACxB,oCAAoC;YAClC,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC;YAC3C,GAAG,CACN,EACL,GAAG;QACD,CAAC,CAAC,aAAa,CACX,GAAG,EACH,EAAE,KAAK,EAAE,IAAI,EAAE,EACf,sEAAsE,CACvE;QACH,CAAC,CAAC,IAAI,EACR,aAAa,CACX,QAAQ,EACR,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,EACjD,WAAW,CACZ,CACF,CAAC;AACJ,CAAC","sourcesContent":["\"use client\";\n\n// What a page that throws shows when no error.tsx covers it.\n//\n// Without this, nothing did: the Flight row failed, the browser rethrew on\n// hydration with no boundary to catch it, and React unmounted the document -\n// a black page with the cause nowhere near it. Next shows its overlay in\n// development and a plain \"something went wrong\" in production; this is\n// that, as a page, and the layouts above it stay on screen.\n//\n// In development the message and stack, because the developer is the one\n// looking. In production React has already replaced the message with a\n// digest, and the digest is what this shows: the line to search the server\n// log for.\n\nimport { createElement, type ReactNode } from \"react\";\nimport type { RouteErrorProps } from \"./RouteErrorBoundary\";\n\nconst DEV =\n typeof import.meta !== \"undefined\" &&\n (import.meta as { env?: { DEV?: boolean } }).env?.DEV === true;\n\nconst mono = \"ui-monospace,SFMono-Regular,Menlo,monospace\";\nconst box = {\n maxWidth: \"60rem\",\n margin: \"3rem auto\",\n padding: \"1.5rem\",\n font: \"14px/1.5 \" + mono,\n color: \"#fecaca\",\n background: \"#1f0b0b\",\n border: \"1px solid #7f1d1d\",\n borderRadius: 8,\n};\nconst heading = {\n margin: \"0 0 .75rem\",\n fontSize: 16,\n fontWeight: 600,\n color: \"#fff\",\n};\nconst pre = {\n whiteSpace: \"pre-wrap\" as const,\n wordBreak: \"break-word\" as const,\n margin: \".75rem 0 0\",\n color: \"#fecaca\",\n};\nconst hint = { margin: \".75rem 0 0\", color: \"#fca5a5\" };\nconst button = {\n marginTop: \"1rem\",\n padding: \".4rem .8rem\",\n border: \"1px solid #fca5a5\",\n borderRadius: 6,\n background: \"transparent\",\n color: \"#fff\",\n cursor: \"pointer\",\n font: \"inherit\",\n};\n\nexport function DefaultRouteError({\n error,\n reset,\n}: RouteErrorProps): ReactNode {\n const name = error.name && error.name !== \"Error\" ? error.name + \": \" : \"\";\n\n return createElement(\n \"main\",\n { role: \"alert\", style: box, \"data-rsc-kit-error\": \"\" },\n createElement(\n \"h1\",\n { style: heading },\n DEV ? \"This page threw while rendering\" : \"Something went wrong\",\n ),\n DEV\n ? createElement(\n \"pre\",\n { style: pre },\n error.stack || name + error.message,\n )\n : createElement(\n \"p\",\n { style: { margin: 0 } },\n \"The error was logged on the server\" +\n (error.digest ? \" as \" + error.digest : \"\") +\n \".\",\n ),\n DEV\n ? createElement(\n \"p\",\n { style: hint },\n \"Add an error.tsx beside the page to show something of your own here.\",\n )\n : null,\n createElement(\n \"button\",\n { type: \"button\", style: button, onClick: reset },\n \"Try again\",\n ),\n );\n}\n"]}
1
+ {"version":3,"file":"DefaultRouteError.js","sourceRoot":"","sources":["../../src/js/DefaultRouteError.tsx"],"names":[],"mappings":"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","sourcesContent":["\"use client\";\n\n// What a page that throws shows when no error.tsx covers it.\n//\n// Without this, nothing did: the Flight row failed, the browser rethrew on\n// hydration with no boundary to catch it, and React unmounted the document -\n// a black page with the cause nowhere near it. Next shows its overlay in\n// development and a plain \"something went wrong\" in production; this is\n// that, as a page, and the layouts above it stay on screen.\n//\n// In development the message and stack, because the developer is the one\n// looking. In production React has already replaced the message with a\n// digest, and the digest is what this shows: the line to search the server\n// log for.\n\nimport { createElement, type ReactNode } from \"react\";\nimport type { RouteErrorProps } from \"./RouteErrorBoundary\";\n\n// `import.meta.env.DEV` as Vite spells it, so the build replaces the whole\n// expression with a literal. A `typeof import.meta` guard in front of it\n// survived the build as a bare `import.meta` in the ssr chunk - the one\n// thing Bun cannot lower when it compiles a binary's bytecode, so\n// `--bytecode` failed on this file - and guarded nothing: where import.meta\n// does not exist, a typeof of it does not parse either.\nconst DEV = Boolean(import.meta.env.DEV);\n\nconst mono = \"ui-monospace,SFMono-Regular,Menlo,monospace\";\nconst box = {\n maxWidth: \"60rem\",\n margin: \"3rem auto\",\n padding: \"1.5rem\",\n font: \"14px/1.5 \" + mono,\n color: \"#fecaca\",\n background: \"#1f0b0b\",\n border: \"1px solid #7f1d1d\",\n borderRadius: 8,\n};\nconst heading = {\n margin: \"0 0 .75rem\",\n fontSize: 16,\n fontWeight: 600,\n color: \"#fff\",\n};\nconst pre = {\n whiteSpace: \"pre-wrap\" as const,\n wordBreak: \"break-word\" as const,\n margin: \".75rem 0 0\",\n color: \"#fecaca\",\n};\nconst hint = { margin: \".75rem 0 0\", color: \"#fca5a5\" };\nconst button = {\n marginTop: \"1rem\",\n padding: \".4rem .8rem\",\n border: \"1px solid #fca5a5\",\n borderRadius: 6,\n background: \"transparent\",\n color: \"#fff\",\n cursor: \"pointer\",\n font: \"inherit\",\n};\n\nexport function DefaultRouteError({\n error,\n reset,\n}: RouteErrorProps): ReactNode {\n const name = error.name && error.name !== \"Error\" ? error.name + \": \" : \"\";\n\n return createElement(\n \"main\",\n { role: \"alert\", style: box, \"data-rsc-kit-error\": \"\" },\n createElement(\n \"h1\",\n { style: heading },\n DEV ? \"This page threw while rendering\" : \"Something went wrong\",\n ),\n DEV\n ? createElement(\n \"pre\",\n { style: pre },\n error.stack || name + error.message,\n )\n : createElement(\n \"p\",\n { style: { margin: 0 } },\n \"The error was logged on the server\" +\n (error.digest ? \" as \" + error.digest : \"\") +\n \".\",\n ),\n DEV\n ? createElement(\n \"p\",\n { style: hint },\n \"Add an error.tsx beside the page to show something of your own here.\",\n )\n : null,\n createElement(\n \"button\",\n { type: \"button\", style: button, onClick: reset },\n \"Try again\",\n ),\n );\n}\n"]}
package/dist/js/Link.js CHANGED
@@ -66,6 +66,14 @@ export default function Link({ href: path, search, prefetch: prefetchProp = "hov
66
66
  return;
67
67
  e.preventDefault();
68
68
  setPending(true);
69
+ // A hover that had not yet prefetched: the click is the navigation, and
70
+ // a prefetch of the page being left for, firing after it, is a request
71
+ // for nothing - seen as a stray 204 for a guarded link after landing
72
+ // on the login page.
73
+ if (hoverTimer.current !== null) {
74
+ clearTimeout(hoverTimer.current);
75
+ hoverTimer.current = null;
76
+ }
69
77
  // navigate() returns a Promise — clear pending when it resolves or rejects
70
78
  const nav = window.__rsc_navigate;
71
79
  const promise = nav?.(href, { replace, preserveScroll });
@@ -1 +1 @@
1
- {"version":3,"file":"Link.js","sourceRoot":"","sources":["../../src/js/Link.tsx"],"names":[],"mappings":"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","sourcesContent":["\"use client\";\n\nimport { LinkStatusContext } from \"./useLinkStatus\";\nimport { type Route, type SearchProp, withSearch } from \"../routes.js\";\nimport {\n type AnchorHTMLAttributes,\n type MouseEvent,\n type Ref,\n useCallback,\n useEffect,\n useRef,\n useState,\n} from \"react\";\n\ntype PrefetchStrategy = \"hover\" | \"mount\" | \"click\" | \"none\" | boolean;\n\ninterface LinkBaseProps<H extends Route> extends Omit<\n AnchorHTMLAttributes<HTMLAnchorElement>,\n \"href\"\n> {\n /**\n * Where this goes. Typed to the routes the build found, so a link to a page\n * that does not exist stops compiling; `path as Route` when it is computed.\n */\n href: H;\n /**\n * The underlying anchor.\n *\n * Spread onto it like any other prop — React 19 passes ref through a\n * function component without forwardRef — but declared here because it is\n * not part of AnchorHTMLAttributes. A link that cannot be focused is a link\n * a dialog cannot move focus to when it opens.\n */\n ref?: Ref<HTMLAnchorElement>;\n prefetch?: PrefetchStrategy;\n cacheFor?: number;\n replace?: boolean;\n preserveScroll?: boolean;\n}\n\n/**\n * `search` is typed to the page's own `searchParams` schema when it exports\n * one — the same schema the page parses with, so a key it never reads or a\n * number written as text does not compile, and a key it requires is required\n * here. With no schema, any scalars. See SearchFor in routes.ts.\n */\ntype LinkProps<H extends Route> = LinkBaseProps<H> & SearchProp<H>;\n\nfunction isExternalUrl(url: string): boolean {\n try {\n return (\n new URL(url, window.location.origin).origin !== window.location.origin\n );\n } catch {\n return false;\n }\n}\n\nfunction shouldInterceptClick(e: MouseEvent<HTMLAnchorElement>): boolean {\n return (\n !e.defaultPrevented &&\n e.button === 0 &&\n !e.metaKey &&\n !e.ctrlKey &&\n !e.shiftKey &&\n !e.altKey\n );\n}\n\n/**\n * How long a pointer has to settle on a link before it prefetches.\n *\n * A pointer crossing a nav bar enters every link on the way, and each one used\n * to fire a request immediately — enough to fill the browser's per-origin\n * connection limit with pages the user never meant to visit. Waiting is close\n * to free: the prefetch only has to beat the click, and a click that follows a\n * hover this short would not have had its payload back anyway.\n */\nconst HOVER_PREFETCH_DELAY_MS = 100;\n\nexport default function Link<H extends Route>({\n href: path,\n search,\n prefetch: prefetchProp = \"hover\",\n cacheFor,\n replace = false,\n preserveScroll = false,\n children,\n onClick,\n onMouseEnter,\n onMouseLeave,\n ...rest\n}: LinkProps<H>) {\n // The string the anchor and the router both use: the path, with the typed\n // search params serialised onto it.\n const href = (search ? withSearch(path, search as object) : path) as Route;\n const [pending, setPending] = useState(false);\n const hoverTimer = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n const prefetchStrategy =\n prefetchProp === true\n ? \"hover\"\n : prefetchProp === false\n ? \"none\"\n : prefetchProp;\n\n const doPrefetch = useCallback(() => {\n if (isExternalUrl(href)) return;\n const fn = (window as any).__rsc_prefetch;\n fn?.(href, cacheFor);\n }, [href, cacheFor]);\n\n // Only useEffect needed: prefetch on mount strategy\n useEffect(() => {\n if (prefetchStrategy === \"mount\") {\n doPrefetch();\n }\n }, [prefetchStrategy, doPrefetch]);\n\n const handleClick = useCallback(\n (e: MouseEvent<HTMLAnchorElement>) => {\n onClick?.(e);\n\n if (e.defaultPrevented) return;\n\n const target = (e.currentTarget as HTMLAnchorElement).target;\n if (target && target !== \"_self\") return;\n if (!shouldInterceptClick(e) || isExternalUrl(href)) return;\n\n // Hash-only links (#section) — let the browser scroll natively\n if (href.startsWith(\"#\")) return;\n\n e.preventDefault();\n setPending(true);\n\n // navigate() returns a Promise — clear pending when it resolves or rejects\n const nav = (window as any).__rsc_navigate;\n const promise = nav?.(href, { replace, preserveScroll });\n promise?.then(\n () => setPending(false),\n () => setPending(false),\n );\n },\n [href, replace, preserveScroll, onClick],\n );\n\n const handleMouseEnter = useCallback(\n (e: MouseEvent<HTMLAnchorElement>) => {\n onMouseEnter?.(e);\n\n if (prefetchStrategy !== \"hover\" && prefetchStrategy !== \"click\") return;\n\n if (hoverTimer.current !== null) clearTimeout(hoverTimer.current);\n\n hoverTimer.current = setTimeout(() => {\n hoverTimer.current = null;\n doPrefetch();\n }, HOVER_PREFETCH_DELAY_MS);\n },\n [prefetchStrategy, doPrefetch, onMouseEnter],\n );\n\n const handleMouseLeave = useCallback(\n (e: MouseEvent<HTMLAnchorElement>) => {\n onMouseLeave?.(e);\n\n // Never fired: the pointer passed over on its way somewhere else.\n if (hoverTimer.current !== null) {\n clearTimeout(hoverTimer.current);\n hoverTimer.current = null;\n }\n\n // Already in flight: give the connection back to whatever the pointer is\n // heading for. A prefetch that has landed is kept — see cancelPrefetch.\n if (isExternalUrl(href)) return;\n (window as any).__rsc_cancel_prefetch?.(href);\n },\n [href, onMouseLeave],\n );\n\n // Touch gets no delay. There is no hovering to disambiguate — a touch is\n // already the start of a tap — and touchstart leads the click by little\n // enough that spending any of it waiting would waste the head start.\n const handleTouchStart = useCallback(() => {\n if (prefetchStrategy === \"hover\" || prefetchStrategy === \"click\") {\n doPrefetch();\n }\n }, [prefetchStrategy, doPrefetch]);\n\n // A link unmounted mid-hover (navigating away) must not prefetch afterwards.\n useEffect(\n () => () => {\n if (hoverTimer.current !== null) clearTimeout(hoverTimer.current);\n },\n [],\n );\n\n return (\n <LinkStatusContext.Provider value={{ pending }}>\n <a\n href={href}\n onClick={handleClick}\n onMouseEnter={handleMouseEnter}\n onMouseLeave={handleMouseLeave}\n onTouchStart={handleTouchStart}\n data-pending={pending ? \"\" : undefined}\n {...rest}\n >\n {children}\n </a>\n </LinkStatusContext.Provider>\n );\n}\n"]}
1
+ {"version":3,"file":"Link.js","sourceRoot":"","sources":["../../src/js/Link.tsx"],"names":[],"mappings":"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","sourcesContent":["\"use client\";\n\nimport { LinkStatusContext } from \"./useLinkStatus\";\nimport { type Route, type SearchProp, withSearch } from \"../routes.js\";\nimport {\n type AnchorHTMLAttributes,\n type MouseEvent,\n type Ref,\n useCallback,\n useEffect,\n useRef,\n useState,\n} from \"react\";\n\ntype PrefetchStrategy = \"hover\" | \"mount\" | \"click\" | \"none\" | boolean;\n\ninterface LinkBaseProps<H extends Route> extends Omit<\n AnchorHTMLAttributes<HTMLAnchorElement>,\n \"href\"\n> {\n /**\n * Where this goes. Typed to the routes the build found, so a link to a page\n * that does not exist stops compiling; `path as Route` when it is computed.\n */\n href: H;\n /**\n * The underlying anchor.\n *\n * Spread onto it like any other prop — React 19 passes ref through a\n * function component without forwardRef — but declared here because it is\n * not part of AnchorHTMLAttributes. A link that cannot be focused is a link\n * a dialog cannot move focus to when it opens.\n */\n ref?: Ref<HTMLAnchorElement>;\n prefetch?: PrefetchStrategy;\n cacheFor?: number;\n replace?: boolean;\n preserveScroll?: boolean;\n}\n\n/**\n * `search` is typed to the page's own `searchParams` schema when it exports\n * one — the same schema the page parses with, so a key it never reads or a\n * number written as text does not compile, and a key it requires is required\n * here. With no schema, any scalars. See SearchFor in routes.ts.\n */\ntype LinkProps<H extends Route> = LinkBaseProps<H> & SearchProp<H>;\n\nfunction isExternalUrl(url: string): boolean {\n try {\n return (\n new URL(url, window.location.origin).origin !== window.location.origin\n );\n } catch {\n return false;\n }\n}\n\nfunction shouldInterceptClick(e: MouseEvent<HTMLAnchorElement>): boolean {\n return (\n !e.defaultPrevented &&\n e.button === 0 &&\n !e.metaKey &&\n !e.ctrlKey &&\n !e.shiftKey &&\n !e.altKey\n );\n}\n\n/**\n * How long a pointer has to settle on a link before it prefetches.\n *\n * A pointer crossing a nav bar enters every link on the way, and each one used\n * to fire a request immediately — enough to fill the browser's per-origin\n * connection limit with pages the user never meant to visit. Waiting is close\n * to free: the prefetch only has to beat the click, and a click that follows a\n * hover this short would not have had its payload back anyway.\n */\nconst HOVER_PREFETCH_DELAY_MS = 100;\n\nexport default function Link<H extends Route>({\n href: path,\n search,\n prefetch: prefetchProp = \"hover\",\n cacheFor,\n replace = false,\n preserveScroll = false,\n children,\n onClick,\n onMouseEnter,\n onMouseLeave,\n ...rest\n}: LinkProps<H>) {\n // The string the anchor and the router both use: the path, with the typed\n // search params serialised onto it.\n const href = (search ? withSearch(path, search as object) : path) as Route;\n const [pending, setPending] = useState(false);\n const hoverTimer = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n const prefetchStrategy =\n prefetchProp === true\n ? \"hover\"\n : prefetchProp === false\n ? \"none\"\n : prefetchProp;\n\n const doPrefetch = useCallback(() => {\n if (isExternalUrl(href)) return;\n const fn = (window as any).__rsc_prefetch;\n fn?.(href, cacheFor);\n }, [href, cacheFor]);\n\n // Only useEffect needed: prefetch on mount strategy\n useEffect(() => {\n if (prefetchStrategy === \"mount\") {\n doPrefetch();\n }\n }, [prefetchStrategy, doPrefetch]);\n\n const handleClick = useCallback(\n (e: MouseEvent<HTMLAnchorElement>) => {\n onClick?.(e);\n\n if (e.defaultPrevented) return;\n\n const target = (e.currentTarget as HTMLAnchorElement).target;\n if (target && target !== \"_self\") return;\n if (!shouldInterceptClick(e) || isExternalUrl(href)) return;\n\n // Hash-only links (#section) — let the browser scroll natively\n if (href.startsWith(\"#\")) return;\n\n e.preventDefault();\n setPending(true);\n\n // A hover that had not yet prefetched: the click is the navigation, and\n // a prefetch of the page being left for, firing after it, is a request\n // for nothing - seen as a stray 204 for a guarded link after landing\n // on the login page.\n if (hoverTimer.current !== null) {\n clearTimeout(hoverTimer.current);\n hoverTimer.current = null;\n }\n\n // navigate() returns a Promise — clear pending when it resolves or rejects\n const nav = (window as any).__rsc_navigate;\n const promise = nav?.(href, { replace, preserveScroll });\n promise?.then(\n () => setPending(false),\n () => setPending(false),\n );\n },\n [href, replace, preserveScroll, onClick],\n );\n\n const handleMouseEnter = useCallback(\n (e: MouseEvent<HTMLAnchorElement>) => {\n onMouseEnter?.(e);\n\n if (prefetchStrategy !== \"hover\" && prefetchStrategy !== \"click\") return;\n\n if (hoverTimer.current !== null) clearTimeout(hoverTimer.current);\n\n hoverTimer.current = setTimeout(() => {\n hoverTimer.current = null;\n doPrefetch();\n }, HOVER_PREFETCH_DELAY_MS);\n },\n [prefetchStrategy, doPrefetch, onMouseEnter],\n );\n\n const handleMouseLeave = useCallback(\n (e: MouseEvent<HTMLAnchorElement>) => {\n onMouseLeave?.(e);\n\n // Never fired: the pointer passed over on its way somewhere else.\n if (hoverTimer.current !== null) {\n clearTimeout(hoverTimer.current);\n hoverTimer.current = null;\n }\n\n // Already in flight: give the connection back to whatever the pointer is\n // heading for. A prefetch that has landed is kept — see cancelPrefetch.\n if (isExternalUrl(href)) return;\n (window as any).__rsc_cancel_prefetch?.(href);\n },\n [href, onMouseLeave],\n );\n\n // Touch gets no delay. There is no hovering to disambiguate — a touch is\n // already the start of a tap — and touchstart leads the click by little\n // enough that spending any of it waiting would waste the head start.\n const handleTouchStart = useCallback(() => {\n if (prefetchStrategy === \"hover\" || prefetchStrategy === \"click\") {\n doPrefetch();\n }\n }, [prefetchStrategy, doPrefetch]);\n\n // A link unmounted mid-hover (navigating away) must not prefetch afterwards.\n useEffect(\n () => () => {\n if (hoverTimer.current !== null) clearTimeout(hoverTimer.current);\n },\n [],\n );\n\n return (\n <LinkStatusContext.Provider value={{ pending }}>\n <a\n href={href}\n onClick={handleClick}\n onMouseEnter={handleMouseEnter}\n onMouseLeave={handleMouseLeave}\n onTouchStart={handleTouchStart}\n data-pending={pending ? \"\" : undefined}\n {...rest}\n >\n {children}\n </a>\n </LinkStatusContext.Provider>\n );\n}\n"]}
@@ -4,13 +4,23 @@ interface Props {
4
4
  children: ReactNode;
5
5
  /** Shown while the navigation is in flight. Null keeps the space empty. */
6
6
  fallback?: ReactNode;
7
+ /**
8
+ * Whether what this boundary wraps is the page on screen. A segment keeps
9
+ * pages alive behind the active one, each under its own boundary, and a
10
+ * redirect caught on a page that is not showing is not performed — it was
11
+ * already performed when it was. Shown again, the boundary renders the
12
+ * page afresh, and a refusal that still stands redirects again.
13
+ */
14
+ active?: boolean;
7
15
  }
8
16
  interface State {
9
17
  redirecting: boolean;
18
+ wasActive: boolean;
10
19
  }
11
20
  export declare class RedirectBoundary extends Component<Props, State> {
12
21
  state: State;
13
- static getDerivedStateFromError(error: unknown): State | null;
22
+ static getDerivedStateFromError(error: unknown): Partial<State> | null;
23
+ static getDerivedStateFromProps(props: Props, state: State): Partial<State> | null;
14
24
  componentDidCatch(error: unknown, _info: ErrorInfo): void;
15
25
  render(): ReactNode;
16
26
  }
@@ -3,7 +3,7 @@ import { Component } from "react";
3
3
  import { parseRedirectDigest } from "../redirectDigest.js";
4
4
  import { visit } from "./router";
5
5
  export class RedirectBoundary extends Component {
6
- state = { redirecting: false };
6
+ state = { redirecting: false, wasActive: this.props.active !== false };
7
7
  static getDerivedStateFromError(error) {
8
8
  // Reading `digest` rather than the message: React replaces a server
9
9
  // error's message in production and transmits the digest either way.
@@ -11,10 +11,19 @@ export class RedirectBoundary extends Component {
11
11
  ? { redirecting: true }
12
12
  : null;
13
13
  }
14
+ static getDerivedStateFromProps(props, state) {
15
+ const active = props.active !== false;
16
+ if (active === state.wasActive)
17
+ return null;
18
+ // Revealed again: render the page, not the space left by its redirect.
19
+ return { wasActive: active, redirecting: active ? false : state.redirecting };
20
+ }
14
21
  componentDidCatch(error, _info) {
15
22
  const target = parseRedirectDigest(error?.digest);
16
23
  if (!target)
17
24
  throw error;
25
+ if (this.props.active === false)
26
+ return;
18
27
  // An SPA navigation, not a location assignment: the layouts above this
19
28
  // boundary are already mounted and correct, so replacing the document
20
29
  // would throw away state the user can see — including whatever they had