@rsc-kit/core 0.20.1 → 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.
- package/dist/bytecodeCheck.d.ts +15 -0
- package/dist/bytecodeCheck.js +65 -0
- package/dist/bytecodeCheck.js.map +1 -0
- package/dist/clientPackages.d.ts +25 -0
- package/dist/clientPackages.js +31 -0
- package/dist/clientPackages.js.map +1 -1
- package/dist/files.d.ts +9 -3
- package/dist/files.js +20 -10
- package/dist/files.js.map +1 -1
- package/dist/js/DefaultRouteError.js +7 -2
- package/dist/js/DefaultRouteError.js.map +1 -1
- package/dist/js/Link.js +8 -0
- package/dist/js/Link.js.map +1 -1
- package/dist/js/navigate.js +22 -1
- package/dist/js/navigate.js.map +1 -1
- package/dist/metadata.d.ts +24 -0
- package/dist/metadata.js.map +1 -1
- package/dist/request.js +36 -8
- package/dist/request.js.map +1 -1
- package/dist/useSsr.js +10 -3
- package/dist/useSsr.js.map +1 -1
- package/dist/vite.js +119 -10
- package/dist/vite.js.map +1 -1
- package/package.json +1 -1
|
@@ -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"]}
|
package/dist/clientPackages.d.ts
CHANGED
|
@@ -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
|
*
|
package/dist/clientPackages.js
CHANGED
|
@@ -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
|
|
44
|
-
*
|
|
45
|
-
*
|
|
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
|
|
196
|
-
*
|
|
197
|
-
*
|
|
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
|
-
|
|
206
|
-
'
|
|
207
|
-
`
|
|
208
|
-
'
|
|
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
|
-
'
|
|
241
|
+
'const pages = ' +
|
|
234
242
|
JSON.stringify(entries) +
|
|
235
|
-
'
|
|
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
|
-
|
|
16
|
-
|
|
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,
|
|
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 });
|
package/dist/js/Link.js.map
CHANGED
|
@@ -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"]}
|
package/dist/js/navigate.js
CHANGED
|
@@ -614,6 +614,22 @@ export async function navigate(url, opts) {
|
|
|
614
614
|
throw error;
|
|
615
615
|
}
|
|
616
616
|
if (reused) {
|
|
617
|
+
if (controller.signal.aborted)
|
|
618
|
+
return;
|
|
619
|
+
// The prefetch this navigation took did not come back as a page. A
|
|
620
|
+
// redirect is followed as one on this request would be; a failure is
|
|
621
|
+
// asked again, from a cache that no longer holds the entry.
|
|
622
|
+
if (reused.redirectTo) {
|
|
623
|
+
await navigate(reused.redirectTo, {
|
|
624
|
+
replace: true,
|
|
625
|
+
redirectsFollowed: redirectsFollowed + 1,
|
|
626
|
+
});
|
|
627
|
+
return;
|
|
628
|
+
}
|
|
629
|
+
if (reused.failed) {
|
|
630
|
+
await navigate(url, opts);
|
|
631
|
+
return;
|
|
632
|
+
}
|
|
617
633
|
segmentDepth = reused.segmentDepth;
|
|
618
634
|
nextLayouts = reused.layouts;
|
|
619
635
|
slotPayload = reused.slot;
|
|
@@ -852,6 +868,8 @@ function prefetchUrl(cacheKey, url, ttl, interceptSlot, refererUrl) {
|
|
|
852
868
|
layouts: null,
|
|
853
869
|
slot: null,
|
|
854
870
|
heldWhenFetched: chain.join(","),
|
|
871
|
+
redirectTo: null,
|
|
872
|
+
failed: false,
|
|
855
873
|
};
|
|
856
874
|
// Low priority: the browser then lets a real navigation overtake a queue of
|
|
857
875
|
// speculative requests instead of serving them in the order they were made.
|
|
@@ -864,7 +882,9 @@ function prefetchUrl(cacheKey, url, ttl, interceptSlot, refererUrl) {
|
|
|
864
882
|
// A prefetch that lands on a redirect is not cached. Following it would
|
|
865
883
|
// navigate on hover, and storing it would hand the click a 204 with no
|
|
866
884
|
// body to deserialize. The click re-requests and redirects properly.
|
|
867
|
-
|
|
885
|
+
const redirectTo = response.headers.get("X-RSC-Redirect");
|
|
886
|
+
if (redirectTo) {
|
|
887
|
+
entry.redirectTo = redirectTo;
|
|
868
888
|
cache.delete(cacheKey);
|
|
869
889
|
return null;
|
|
870
890
|
}
|
|
@@ -882,6 +902,7 @@ function prefetchUrl(cacheKey, url, ttl, interceptSlot, refererUrl) {
|
|
|
882
902
|
return deserializeResponse(response);
|
|
883
903
|
})
|
|
884
904
|
.catch(() => {
|
|
905
|
+
entry.failed = true;
|
|
885
906
|
cache.delete(cacheKey);
|
|
886
907
|
return null;
|
|
887
908
|
})
|