@rsc-kit/core 0.20.1 → 0.20.3
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/host.d.ts +7 -4
- package/dist/host.js +23 -1
- package/dist/host.js.map +1 -1
- package/dist/js/DefaultRouteError.js +15 -5
- package/dist/js/DefaultRouteError.js.map +1 -1
- package/dist/js/Link.d.ts +1 -1
- package/dist/js/Link.js +28 -2
- package/dist/js/Link.js.map +1 -1
- package/dist/js/createViteRscApp.js +9 -4
- package/dist/js/createViteRscApp.js.map +1 -1
- package/dist/js/navigate.js +39 -6
- package/dist/js/navigate.js.map +1 -1
- package/dist/js/staleAssets.d.ts +10 -0
- package/dist/js/staleAssets.js +27 -6
- package/dist/js/staleAssets.js.map +1 -1
- package/dist/js/viewportPrefetch.d.ts +21 -0
- package/dist/js/viewportPrefetch.js +67 -0
- package/dist/js/viewportPrefetch.js.map +1 -0
- 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 +173 -11
- 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"]}
|
package/dist/host.d.ts
CHANGED
|
@@ -18,6 +18,8 @@ import type { RouteManifest } from "./manifest.js";
|
|
|
18
18
|
export interface RscEngine {
|
|
19
19
|
/** The route table this bundle was built from. */
|
|
20
20
|
manifest?(): RouteManifest;
|
|
21
|
+
/** A short id of this build's client, for the version the host answers with when given none. */
|
|
22
|
+
buildId?(): Promise<string>;
|
|
21
23
|
installHostFn(fn: (name: string, ...args: unknown[]) => unknown): void;
|
|
22
24
|
handleRscStream(component: string, props?: Record<string, unknown>, layouts?: {
|
|
23
25
|
component: string;
|
|
@@ -143,10 +145,11 @@ export interface RscHostOptions {
|
|
|
143
145
|
/** Serve a built browser asset. Return null for anything not found. */
|
|
144
146
|
assets?: (pathname: string, request: Request) => Promise<Response | null> | Response | null;
|
|
145
147
|
/**
|
|
146
|
-
* Identifies this build to the client, which
|
|
147
|
-
* navigation and
|
|
148
|
-
*
|
|
149
|
-
*
|
|
148
|
+
* Identifies this build to the client, which says it back on every
|
|
149
|
+
* navigation and is sent to load the document when it differs. For an
|
|
150
|
+
* engine with no `buildId` of its own; the generated one has, and every
|
|
151
|
+
* document it renders says that id, so a version named here would
|
|
152
|
+
* disagree with what the client was told and refuse every navigation.
|
|
150
153
|
*/
|
|
151
154
|
version?: string;
|
|
152
155
|
/**
|
package/dist/host.js
CHANGED
|
@@ -286,7 +286,13 @@ function matchPage(routes, url) {
|
|
|
286
286
|
return matchRoute(routes, url.pathname, hostOf.get(url) ?? null);
|
|
287
287
|
}
|
|
288
288
|
export function createRscHandler(options) {
|
|
289
|
-
const { engine, assets
|
|
289
|
+
const { engine, assets } = options;
|
|
290
|
+
// The build's own id, which is also what every document says it is - the
|
|
291
|
+
// two must agree, or a client's honest claim is a 409 on every request.
|
|
292
|
+
// A version named by the app applies only to an engine with no id of its
|
|
293
|
+
// own. Resolved on the first request: the engine reads it from a build
|
|
294
|
+
// product it only has at runtime.
|
|
295
|
+
let version = engine.buildId ? undefined : options.version;
|
|
290
296
|
const maxActionBody = options.maxActionBody ?? DEFAULT_MAX_ACTION_BODY;
|
|
291
297
|
const compress = options.compress ?? true;
|
|
292
298
|
// Annotated rather than inferred: the narrowing below is lost inside the
|
|
@@ -421,6 +427,8 @@ export function createRscHandler(options) {
|
|
|
421
427
|
return [version ?? "", url.pathname + url.search, ...varies].join("\n");
|
|
422
428
|
}
|
|
423
429
|
return async function handle(request) {
|
|
430
|
+
if (version === undefined && engine.buildId)
|
|
431
|
+
version = await engine.buildId();
|
|
424
432
|
return await withRequest(request, () => withCache(() =>
|
|
425
433
|
// Open for the whole request and sealed the moment an answer exists,
|
|
426
434
|
// so middleware — which runs before any rendering — can put headers on
|
|
@@ -583,6 +591,20 @@ export function createRscHandler(options) {
|
|
|
583
591
|
const formPost = await formPostOf(request, url);
|
|
584
592
|
if (!formPost && request.method !== "GET" && request.method !== "HEAD")
|
|
585
593
|
return null;
|
|
594
|
+
// A client saying which build it runs, and it is not this one: its
|
|
595
|
+
// manifest cannot load what this build's payload names - a client
|
|
596
|
+
// component added since is "client reference not found" and the route's
|
|
597
|
+
// error boundary, on a page that worked a click ago. Under a service
|
|
598
|
+
// worker that serves the last build's document first, that is every
|
|
599
|
+
// returning visitor's first navigation after a deploy, not an open tab.
|
|
600
|
+
// A 409 sends the client to load the document instead, from this build.
|
|
601
|
+
const claimed = request.headers.get(HEADER.version);
|
|
602
|
+
if (claimed !== null && claimed !== "" && version && claimed !== version && request.headers.get(HEADER.rsc) !== null) {
|
|
603
|
+
return new Response(null, {
|
|
604
|
+
status: 409,
|
|
605
|
+
headers: withVersion({ "X-RSC-Location": url.pathname + url.search, "Cache-Control": "no-store" }),
|
|
606
|
+
});
|
|
607
|
+
}
|
|
586
608
|
// One named region of this page, asked for without mutating anything to
|
|
587
609
|
// earn it. What an action invalidated does not come through here — that
|
|
588
610
|
// travels back inside the action's own answer, which is the whole point of
|