@rshono/core 1.0.0-rc.2 → 1.0.0-rc.21
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/LICENSE +21 -0
- package/README.md +348 -158
- package/bin/rshono.mjs +3 -4
- package/dist/builder/env-shadow-loader.cjs +123 -8
- package/dist/builder/page-entry-loader.cjs +28 -1
- package/dist/builder/page-files.d.ts +0 -1
- package/dist/builder/page-files.js +7 -3
- package/dist/builder/page-files.js.map +1 -1
- package/dist/builder/public-env.d.ts +7 -1
- package/dist/builder/public-env.js +7 -0
- package/dist/builder/public-env.js.map +1 -1
- package/dist/builder/react-versions.d.ts +12 -0
- package/dist/builder/react-versions.js +74 -0
- package/dist/builder/react-versions.js.map +1 -0
- package/dist/builder/rspack-config.d.ts +13 -4
- package/dist/builder/rspack-config.js +136 -53
- package/dist/builder/rspack-config.js.map +1 -1
- package/dist/builder/server-only-imports.d.ts +32 -0
- package/dist/builder/server-only-imports.js +51 -0
- package/dist/builder/server-only-imports.js.map +1 -0
- package/dist/cli/build.d.ts +2 -3
- package/dist/cli/build.js +51 -13
- package/dist/cli/build.js.map +1 -1
- package/dist/cli/dev.d.ts +2 -3
- package/dist/cli/dev.js +149 -45
- package/dist/cli/dev.js.map +1 -1
- package/dist/cli/exit.d.ts +12 -0
- package/dist/cli/exit.js +16 -0
- package/dist/cli/exit.js.map +1 -0
- package/dist/cli/index.d.ts +0 -1
- package/dist/cli/index.js +91 -30
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/start.d.ts +0 -1
- package/dist/cli/start.js +15 -18
- package/dist/cli/start.js.map +1 -1
- package/dist/config.d.ts +72 -78
- package/dist/config.js +17 -1
- package/dist/config.js.map +1 -1
- package/dist/deploy/aws-lambda/runtime.d.ts +4 -7
- package/dist/deploy/aws-lambda/runtime.js +5 -8
- package/dist/deploy/aws-lambda/runtime.js.map +1 -1
- package/dist/deploy/build-marker.d.ts +9 -6
- package/dist/deploy/build-marker.js +8 -4
- package/dist/deploy/build-marker.js.map +1 -1
- package/dist/deploy/cloudflare/build.d.ts +0 -1
- package/dist/deploy/cloudflare/build.js +17 -13
- package/dist/deploy/cloudflare/build.js.map +1 -1
- package/dist/deploy/cloudflare/runtime.d.ts +2 -6
- package/dist/deploy/cloudflare/runtime.js +73 -68
- package/dist/deploy/cloudflare/runtime.js.map +1 -1
- package/dist/deploy/contract.d.ts +57 -41
- package/dist/deploy/contract.js.map +1 -1
- package/dist/deploy/filesystem.d.ts +3 -6
- package/dist/deploy/filesystem.js +23 -20
- package/dist/deploy/filesystem.js.map +1 -1
- package/dist/deploy/node/runtime.d.ts +5 -3
- package/dist/deploy/node/runtime.js +23 -9
- package/dist/deploy/node/runtime.js.map +1 -1
- package/dist/deploy/presets.d.ts +34 -30
- package/dist/deploy/presets.js +56 -44
- package/dist/deploy/presets.js.map +1 -1
- package/dist/deploy/public-paths.d.ts +42 -0
- package/dist/deploy/public-paths.js +66 -0
- package/dist/deploy/public-paths.js.map +1 -0
- package/dist/deploy/vercel/build.d.ts +0 -1
- package/dist/deploy/vercel/build.js +25 -16
- package/dist/deploy/vercel/build.js.map +1 -1
- package/dist/deploy/vercel/runtime.d.ts +10 -7
- package/dist/deploy/vercel/runtime.js +48 -11
- package/dist/deploy/vercel/runtime.js.map +1 -1
- package/dist/index.d.ts +19 -17
- package/dist/index.js +20 -10
- package/dist/index.js.map +1 -1
- package/dist/router.d.ts +212 -120
- package/dist/router.js +3 -8
- package/dist/router.js.map +1 -1
- package/dist/runtime/boundaries.d.ts +62 -48
- package/dist/runtime/boundaries.js +32 -34
- package/dist/runtime/boundaries.js.map +1 -1
- package/dist/runtime/client.d.ts +20 -11
- package/dist/runtime/client.js +20 -10
- package/dist/runtime/client.js.map +1 -1
- package/dist/runtime/context.d.ts +304 -123
- package/dist/runtime/context.js +377 -149
- package/dist/runtime/context.js.map +1 -1
- package/dist/runtime/control.d.ts +15 -1
- package/dist/runtime/control.js +24 -0
- package/dist/runtime/control.js.map +1 -1
- package/dist/runtime/dev-protocol.d.ts +4 -9
- package/dist/runtime/dev-protocol.js.map +1 -1
- package/dist/runtime/empty-server-app.d.ts +0 -1
- package/dist/runtime/entry.client.d.ts +4 -1
- package/dist/runtime/entry.client.js +455 -313
- package/dist/runtime/entry.client.js.map +1 -1
- package/dist/runtime/entry.rsc.d.ts +15 -7
- package/dist/runtime/entry.rsc.js +811 -252
- package/dist/runtime/entry.rsc.js.map +1 -1
- package/dist/runtime/entry.ssr.d.ts +27 -10
- package/dist/runtime/entry.ssr.js +30 -48
- package/dist/runtime/entry.ssr.js.map +1 -1
- package/dist/runtime/failure-document.d.ts +11 -0
- package/dist/runtime/failure-document.js +35 -0
- package/dist/runtime/failure-document.js.map +1 -0
- package/dist/runtime/flight-inject.d.ts +23 -0
- package/dist/runtime/flight-inject.js +403 -0
- package/dist/runtime/flight-inject.js.map +1 -0
- package/dist/runtime/hot-update.d.ts +44 -0
- package/dist/runtime/hot-update.js +44 -0
- package/dist/runtime/hot-update.js.map +1 -0
- package/dist/runtime/navigation.d.ts +46 -54
- package/dist/runtime/navigation.js +22 -63
- package/dist/runtime/navigation.js.map +1 -1
- package/dist/runtime/request.d.ts +42 -8
- package/dist/runtime/request.js +84 -7
- package/dist/runtime/request.js.map +1 -1
- package/dist/runtime/server.d.ts +20 -18
- package/dist/runtime/server.js +22 -17
- package/dist/runtime/server.js.map +1 -1
- package/dist/runtime/validate-entries.d.ts +77 -0
- package/dist/runtime/validate-entries.js +405 -0
- package/dist/runtime/validate-entries.js.map +1 -0
- package/dist/server/headers.d.ts +41 -15
- package/dist/server/headers.js +53 -24
- package/dist/server/headers.js.map +1 -1
- package/dist/server/load-config.d.ts +4 -5
- package/dist/server/load-config.js +22 -14
- package/dist/server/load-config.js.map +1 -1
- package/dist/server/load-env.d.ts +0 -1
- package/dist/server/prerendered.d.ts +88 -34
- package/dist/server/prerendered.js +184 -24
- package/dist/server/prerendered.js.map +1 -1
- package/dist/server/server-config.d.ts +35 -50
- package/dist/server/server-config.js +27 -74
- package/dist/server/server-config.js.map +1 -1
- package/dist/server/shutdown.d.ts +5 -4
- package/dist/server/shutdown.js +5 -3
- package/dist/server/shutdown.js.map +1 -1
- package/dist/server/ssg.d.ts +9 -7
- package/dist/server/ssg.js +209 -65
- package/dist/server/ssg.js.map +1 -1
- package/dist/server/static.d.ts +0 -1
- package/dist/server/static.js +7 -1
- package/dist/server/static.js.map +1 -1
- package/package.json +21 -23
- package/dist/builder/page-files.d.ts.map +0 -1
- package/dist/builder/public-env.d.ts.map +0 -1
- package/dist/builder/rspack-config.d.ts.map +0 -1
- package/dist/cli/build.d.ts.map +0 -1
- package/dist/cli/dev.d.ts.map +0 -1
- package/dist/cli/index.d.ts.map +0 -1
- package/dist/cli/start.d.ts.map +0 -1
- package/dist/config.d.ts.map +0 -1
- package/dist/deploy/aws-lambda/runtime.d.ts.map +0 -1
- package/dist/deploy/build-marker.d.ts.map +0 -1
- package/dist/deploy/bun/runtime.d.ts +0 -11
- package/dist/deploy/bun/runtime.d.ts.map +0 -1
- package/dist/deploy/bun/runtime.js +0 -22
- package/dist/deploy/bun/runtime.js.map +0 -1
- package/dist/deploy/cloudflare/build.d.ts.map +0 -1
- package/dist/deploy/cloudflare/runtime.d.ts.map +0 -1
- package/dist/deploy/contract.d.ts.map +0 -1
- package/dist/deploy/deno/runtime.d.ts +0 -11
- package/dist/deploy/deno/runtime.d.ts.map +0 -1
- package/dist/deploy/deno/runtime.js +0 -16
- package/dist/deploy/deno/runtime.js.map +0 -1
- package/dist/deploy/filesystem.d.ts.map +0 -1
- package/dist/deploy/listen.d.ts +0 -20
- package/dist/deploy/listen.d.ts.map +0 -1
- package/dist/deploy/listen.js +0 -24
- package/dist/deploy/listen.js.map +0 -1
- package/dist/deploy/netlify/build.d.ts +0 -8
- package/dist/deploy/netlify/build.d.ts.map +0 -1
- package/dist/deploy/netlify/build.js +0 -52
- package/dist/deploy/netlify/build.js.map +0 -1
- package/dist/deploy/netlify/runtime.d.ts +0 -13
- package/dist/deploy/netlify/runtime.d.ts.map +0 -1
- package/dist/deploy/netlify/runtime.js +0 -24
- package/dist/deploy/netlify/runtime.js.map +0 -1
- package/dist/deploy/node/runtime.d.ts.map +0 -1
- package/dist/deploy/presets.d.ts.map +0 -1
- package/dist/deploy/vercel/build.d.ts.map +0 -1
- package/dist/deploy/vercel/runtime.d.ts.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/router.d.ts.map +0 -1
- package/dist/runtime/boundaries.d.ts.map +0 -1
- package/dist/runtime/client.d.ts.map +0 -1
- package/dist/runtime/context.d.ts.map +0 -1
- package/dist/runtime/control.d.ts.map +0 -1
- package/dist/runtime/dev-protocol.d.ts.map +0 -1
- package/dist/runtime/empty-server-app.d.ts.map +0 -1
- package/dist/runtime/entry.client.d.ts.map +0 -1
- package/dist/runtime/entry.rsc.d.ts.map +0 -1
- package/dist/runtime/entry.ssr.d.ts.map +0 -1
- package/dist/runtime/navigation.d.ts.map +0 -1
- package/dist/runtime/request.d.ts.map +0 -1
- package/dist/runtime/server.d.ts.map +0 -1
- package/dist/server/compress.d.ts +0 -15
- package/dist/server/compress.d.ts.map +0 -1
- package/dist/server/compress.js +0 -76
- package/dist/server/compress.js.map +0 -1
- package/dist/server/headers.d.ts.map +0 -1
- package/dist/server/load-config.d.ts.map +0 -1
- package/dist/server/load-env.d.ts.map +0 -1
- package/dist/server/prerendered.d.ts.map +0 -1
- package/dist/server/server-config.d.ts.map +0 -1
- package/dist/server/shutdown.d.ts.map +0 -1
- package/dist/server/ssg.d.ts.map +0 -1
- package/dist/server/static.d.ts.map +0 -1
package/bin/rshono.mjs
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
// Thin launcher for the compiled CLI. Everything
|
|
3
|
-
//
|
|
2
|
+
// Thin launcher for the compiled CLI. Everything here has to happen before the CLI module graph loads, which is
|
|
3
|
+
// why the import at the bottom is dynamic.
|
|
4
4
|
const args = process.argv.slice(2);
|
|
5
5
|
|
|
6
|
-
//
|
|
7
|
-
// value is read during config resolution and baked into the bundle, so it has to be set this early.
|
|
6
|
+
// The production commands default NODE_ENV, which is read during config resolution and baked into the bundle.
|
|
8
7
|
if ((args[0] === 'build' || args[0] === 'start') && !process.env.NODE_ENV) {
|
|
9
8
|
process.env.NODE_ENV = 'production';
|
|
10
9
|
}
|
|
@@ -1,18 +1,103 @@
|
|
|
1
1
|
'use strict';
|
|
2
|
-
|
|
2
|
+
|
|
3
|
+
/** Whitespace and comments — what may sit before or between directives without ending the prologue. */
|
|
4
|
+
const TRIVIA = '(?:\\s|//[^\\n]*(?:\\n|$)|/\\*[\\s\\S]*?\\*/)*';
|
|
5
|
+
/** One directive: `'use strict'`, `"use client"`, `'use server'`. The backreference keeps the quotes matched. */
|
|
6
|
+
const DIRECTIVE = '([\'"])use [a-z -]+\\1\\s*;?';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* The whole directive prologue, so the prelude below is inserted *after* every directive rather than after the
|
|
10
|
+
* first one.
|
|
11
|
+
*
|
|
12
|
+
* The repetition is load-bearing. A module with two directives — `"use strict"` beside `'use client'`, which is
|
|
13
|
+
* ordinary output from a published component library, in either order — would otherwise take the prelude
|
|
14
|
+
* *between* them, and a directive preceded by a statement is just an expression: whichever came second would
|
|
15
|
+
* silently stop being a directive. That was survivable while this loader only saw the app's own hand-written
|
|
16
|
+
* source, where `'use client'` comes first and alone; it stopped being survivable when the rule widened to
|
|
17
|
+
* cover `node_modules` (see the SSR-layer rule in `rspack-config.ts`).
|
|
18
|
+
*
|
|
19
|
+
* Each repetition has to consume a whole directive, so the nested quantifiers cannot loop on an empty match.
|
|
20
|
+
*/
|
|
21
|
+
const DIRECTIVE_PROLOGUE = new RegExp(`^${TRIVIA}(?:${DIRECTIVE}${TRIVIA})*`);
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* The gate: a standalone `process` identifier anywhere in the module, not the literal text `process.env`.
|
|
25
|
+
*
|
|
26
|
+
* The prelude replaces the whole `process` *binding*, so every way of reaching the env through that binding is
|
|
27
|
+
* covered once it is emitted — but a gate on `process.env` only ever saw one of them:
|
|
28
|
+
*
|
|
29
|
+
* | source shape | `process.env` | this |
|
|
30
|
+
* | --------------------------------------- | ------------- | ---- |
|
|
31
|
+
* | `process.env.DATABASE_URL` | yes | yes |
|
|
32
|
+
* | `const { DATABASE_URL } = process.env` | yes | yes |
|
|
33
|
+
* | `process?.env.DATABASE_URL` | **no** | yes |
|
|
34
|
+
* | `process['env'].DATABASE_URL` | **no** | yes |
|
|
35
|
+
* | `const { env } = process` | **no** | yes |
|
|
36
|
+
* | `const p = process; p.env.DATABASE_URL` | **no** | yes |
|
|
37
|
+
*
|
|
38
|
+
* `process?.env` is not a contrived shape — it is how env access is written in code meant to run in a browser
|
|
39
|
+
* and on a server, which is exactly what a `'use client'` component is. Every "no" above rendered a secret into
|
|
40
|
+
* the SSR'd HTML while the browser bundle saw the `PUBLIC_`-only view.
|
|
41
|
+
*
|
|
42
|
+
* `\b` on both sides, so `preprocess`, `processEnv` and `child_process` do not drag the prelude in. The cost of
|
|
43
|
+
* being wrong either way is only bytes: a module that mentions `process` in a comment gets a prelude it does
|
|
44
|
+
* not need.
|
|
45
|
+
*
|
|
46
|
+
* The one shape this cannot cover is a module that declares its own module-scope `process` binding — the
|
|
47
|
+
* prelude would be a redeclaration, and the build fails on it rather than shipping. Naming a module-scope
|
|
48
|
+
* binding after a well-known global is what has to give.
|
|
49
|
+
*/
|
|
50
|
+
const MENTIONS_PROCESS = /\bprocess\b/;
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* A read of `process` through the global object rather than through the binding — `globalThis.process.env`,
|
|
54
|
+
* `globalThis?.process`, `global['process']`, and the `self` spelling Workers use.
|
|
55
|
+
*
|
|
56
|
+
* A prelude declares a module-scoped binding, so it cannot reach any of these: `globalThis.process` is the real
|
|
57
|
+
* `process` however the module names it. `typeof globalThis.process !== 'undefined' && …` is a real isomorphic
|
|
58
|
+
* idiom, so this is worth saying out loud rather than leaving to be discovered in a rendered page — but only
|
|
59
|
+
* for the app's own source, since a library feature-detecting `globalThis.process?.env?.NODE_ENV` is doing
|
|
60
|
+
* nothing wrong and has no app secret to read.
|
|
61
|
+
*/
|
|
62
|
+
const GLOBAL_PROCESS = /\b(?:globalThis|global|self)\s*(?:\?\.|\.)?\s*(?:process\b|\[\s*(['"`])process\1\s*\])/;
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* A free `process.env.NODE_ENV` read — the one expression the prelude has to hand back to the optimiser —
|
|
66
|
+
* and the three places that same text can sit without being one.
|
|
67
|
+
*
|
|
68
|
+
* The last alternative is the target. The lookbehind rejects a member read off something else
|
|
69
|
+
* (`this.process.env.NODE_ENV`, `foo.process…`) and a longer identifier (`myprocess`); the trailing `\b`
|
|
70
|
+
* rejects `NODE_ENVIRONMENT`. That is the shape DefinePlugin substitutes, minus the ones it also declines —
|
|
71
|
+
* an optional chain and a computed key are left alone here exactly as they are left alone there.
|
|
72
|
+
*
|
|
73
|
+
* The alternatives ahead of it are what keeps this a substitution of *code*. DefinePlugin works on the parsed
|
|
74
|
+
* module and so cannot rewrite a string; this loader is text, and without them
|
|
75
|
+
* `throw new Error('set process.env.NODE_ENV')` would come out as `throw new Error('set "production"')`.
|
|
76
|
+
* Comments come first so an apostrophe in one (`// don't`) cannot open a string that swallows the code after
|
|
77
|
+
* it, then strings, then templates.
|
|
78
|
+
*
|
|
79
|
+
* It is a scanner, not a parser, and two things are outside it: a regex literal spelled with unescaped dots
|
|
80
|
+
* (`/process.env.NODE_ENV/`), and an interpolation inside a template, which is consumed with the template
|
|
81
|
+
* around it. The first is the residual risk and a strange thing to write; the second only declines a
|
|
82
|
+
* substitution that would have been valid, which costs bytes and changes nothing. Any position it gets wrong
|
|
83
|
+
* leaves the text exactly as a bare `.replace()` would have, so this can only improve on one.
|
|
84
|
+
*/
|
|
85
|
+
const NODE_ENV_SCAN = /\/\/[^\n]*|\/\*[\s\S]*?\*\/|(['"])(?:\\[\s\S]|(?!\1)[^\\\n])*\1|`(?:\\[\s\S]|[^\\`])*`|(?<![.$\w])process\.env\.NODE_ENV\b/g;
|
|
3
86
|
|
|
4
87
|
/**
|
|
5
88
|
* Shadows `process.env` with the PUBLIC_-only view inside SSR-layer modules, so a secret read from a
|
|
6
|
-
* `'use client'` component renders empty on the server
|
|
7
|
-
*
|
|
89
|
+
* `'use client'` component renders empty on the server rather than leaking into the HTML stream — and SSR
|
|
90
|
+
* keeps agreeing with hydration, which sees the same view via DefinePlugin.
|
|
8
91
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
92
|
+
* A security control, so it fails the build rather than passing source through when it cannot confirm a
|
|
93
|
+
* module's layer: `_module` is a private Rspack field, and a silent no-op if it were renamed would drop the
|
|
94
|
+
* guarantee with nothing to notice.
|
|
12
95
|
*/
|
|
13
96
|
module.exports = function envShadowLoader(source) {
|
|
14
|
-
|
|
15
|
-
|
|
97
|
+
// Two steps rather than the regex alone: this loader sees every module in the bundle, and most of them do
|
|
98
|
+
// not contain the substring at all, which `includes` settles far more cheaply than a scan for word breaks.
|
|
99
|
+
if (!source.includes('process') || !MENTIONS_PROCESS.test(source)) return source;
|
|
100
|
+
const { prelude, layer, appSrcPrefix, nodeEnv } = this.getOptions();
|
|
16
101
|
if (!this._module) {
|
|
17
102
|
throw new Error(
|
|
18
103
|
"[rshono] env-shadow-loader could not read the module's layer (Rspack's `this._module` is unavailable). " +
|
|
@@ -21,6 +106,36 @@ module.exports = function envShadowLoader(source) {
|
|
|
21
106
|
);
|
|
22
107
|
}
|
|
23
108
|
if (this._module.layer !== layer) return source;
|
|
109
|
+
|
|
110
|
+
if (appSrcPrefix && this.resourcePath?.startsWith(appSrcPrefix) && GLOBAL_PROCESS.test(source)) {
|
|
111
|
+
// Rspack prints the module path ahead of this, so the message is only the part it does not know.
|
|
112
|
+
this.emitWarning(
|
|
113
|
+
new Error(
|
|
114
|
+
'[rshono] This module reads `process` through the global object. It is rendered on the server as part ' +
|
|
115
|
+
'of a client component, where `process.env` is shadowed with the `PUBLIC_`-only view — and that ' +
|
|
116
|
+
'shadow is a binding, so it cannot cover `globalThis.process`. A variable read that way is the real ' +
|
|
117
|
+
'one, and it ends up in the HTML. Read `process.env` directly instead.',
|
|
118
|
+
),
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Hand `process.env.NODE_ENV` back to the optimiser before the prelude takes it away. DefinePlugin — the
|
|
123
|
+
// pass that erases `if (process.env.NODE_ENV === 'production')` and everything on the dead side of it —
|
|
124
|
+
// respects lexical scope, and the prelude below declares a module-scope `process`. So without this, every
|
|
125
|
+
// React entry wrapper in this layer stays a runtime branch and *both* halves are bundled: six development
|
|
126
|
+
// builds of react / react-dom / react-server-dom-rspack, about half of `dist/server/main.mjs`.
|
|
127
|
+
//
|
|
128
|
+
// Substituting is not a behaviour change. `NODE_ENV` is the one key of the prelude's own `env` literal that
|
|
129
|
+
// is not read from the host environment — it is this build's mode — so the value written here is the value
|
|
130
|
+
// the shadow would have returned anyway, and the shadow's security property is untouched.
|
|
131
|
+
//
|
|
132
|
+
// Textual, like the prelude insertion beside it, but not blindly so: {@link NODE_ENV_SCAN} walks the
|
|
133
|
+
// comments, strings and templates as well, and hands back everything that is not code untouched.
|
|
134
|
+
if (nodeEnv) {
|
|
135
|
+
const literal = JSON.stringify(nodeEnv);
|
|
136
|
+
source = source.replace(NODE_ENV_SCAN, (match) => (match.startsWith('process') ? literal : match));
|
|
137
|
+
}
|
|
138
|
+
|
|
24
139
|
const prologue = source.match(DIRECTIVE_PROLOGUE)[0];
|
|
25
140
|
return prologue + prelude + source.slice(prologue.length);
|
|
26
141
|
};
|
|
@@ -1,5 +1,32 @@
|
|
|
1
1
|
'use strict';
|
|
2
|
-
|
|
2
|
+
|
|
3
|
+
/** Whitespace and comments — what may sit before or between directives without ending the prologue. */
|
|
4
|
+
const TRIVIA = '(?:\\s|//[^\\n]*(?:\\n|$)|/\\*[\\s\\S]*?\\*/)*';
|
|
5
|
+
/**
|
|
6
|
+
* A directive that is *not* one of the three the RSC compilers act on. `'use strict'` above all, which is
|
|
7
|
+
* what a transpiler and many a published component library put at the top of a module; `'use cache'` and
|
|
8
|
+
* whatever else React adds later fall here too, which is the point of writing this as "not those three"
|
|
9
|
+
* rather than as a list.
|
|
10
|
+
*/
|
|
11
|
+
const OTHER_DIRECTIVE = '([\'"])use (?!(?:client|server|server-entry)\\1)[a-z -]+\\1\\s*;?';
|
|
12
|
+
/** One of the three. `\\2`, because {@link OTHER_DIRECTIVE} ahead of it owns group 1. */
|
|
13
|
+
const RSC_DIRECTIVE = '([\'"])use (?:client|server|server-entry)\\2\\s*;?';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Whether the module already declares one of the three RSC directives — anywhere in its directive prologue,
|
|
17
|
+
* which is the fix: this used to match only a directive that came *first*.
|
|
18
|
+
*
|
|
19
|
+
* A page opening `'use strict';` ahead of its `'use client';` therefore had `'use server-entry';` prepended,
|
|
20
|
+
* and the injected directive is the one the compiler then acts on. Measured against a real build: a page
|
|
21
|
+
* whose first line is `'use client'` fails with `assertPageModule`'s message, and the same page with
|
|
22
|
+
* `'use strict'` above it exits 0 and ships — so the one directive the framework relies on to refuse a client
|
|
23
|
+
* page was silently neutered, and the page failed inside React at request time instead.
|
|
24
|
+
*
|
|
25
|
+
* Structural rather than a substring test, so a comment that *mentions* `'use client'` — plausible at the top
|
|
26
|
+
* of a page that used to be one — is consumed as trivia rather than read as a directive. And the same shape
|
|
27
|
+
* as `env-shadow-loader.cjs`'s prologue match, which was widened for this exact class of module.
|
|
28
|
+
*/
|
|
29
|
+
const OPENS_WITH_DIRECTIVE = new RegExp(`^${TRIVIA}(?:${OTHER_DIRECTIVE}${TRIVIA})*${RSC_DIRECTIVE}`);
|
|
3
30
|
|
|
4
31
|
module.exports = function pageEntryLoader(source) {
|
|
5
32
|
if (OPENS_WITH_DIRECTIVE.test(source)) return source;
|
|
@@ -1,10 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Finds the page modules named by inline `component: () => import('…')` thunks in `routes.ts`, so the server
|
|
3
|
+
* compiler can put `page-entry-loader.cjs` in front of exactly those files. A page wired up any other way
|
|
4
|
+
* declares its `'use server-entry'` directive itself.
|
|
5
|
+
*/
|
|
1
6
|
import { readFileSync, statSync } from 'node:fs';
|
|
2
7
|
import { dirname, join, resolve } from 'node:path';
|
|
3
8
|
const COMPONENT_THUNK = /component:\s*(?:async\s*)?\(\s*\)\s*=>\s*import\(\s*(['"])([^'"]+)\1\s*\)/g;
|
|
4
9
|
const EXTENSIONS = ['.tsx', '.ts', '.jsx', '.js'];
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
// while storing a path no rspack resource will ever equal.
|
|
10
|
+
// `join` rather than `base + '/index…'`: these are compared against Rspack's own resource paths, which on
|
|
11
|
+
// Windows carry backslashes.
|
|
8
12
|
function resolveCandidates(base) {
|
|
9
13
|
return [...EXTENSIONS.map((ext) => base + ext), ...EXTENSIONS.map((ext) => join(base, `index${ext}`)), base];
|
|
10
14
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"page-files.js","sourceRoot":"","sources":["../../src/builder/page-files.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACjD,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEnD,MAAM,eAAe,GAAG,4EAA4E,CAAC;AAErG,MAAM,UAAU,GAAG,CAAC,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC,CAAC;AAElD,
|
|
1
|
+
{"version":3,"file":"page-files.js","sourceRoot":"","sources":["../../src/builder/page-files.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACjD,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEnD,MAAM,eAAe,GAAG,4EAA4E,CAAC;AAErG,MAAM,UAAU,GAAG,CAAC,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC,CAAC;AAElD,0GAA0G;AAC1G,6BAA6B;AAC7B,SAAS,iBAAiB,CAAC,IAAY;IACrC,OAAO,CAAC,GAAG,UAAU,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,IAAI,GAAG,GAAG,CAAC,EAAE,GAAG,UAAU,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,QAAQ,GAAG,EAAE,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC;AAC/G,CAAC;AAED,SAAS,MAAM,CAAC,IAAY;IAC1B,IAAI,CAAC;QACH,OAAO,QAAQ,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,CAAC;IACjC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,UAAkB,EAAE,MAAc,EAAE,IAAiB;IACjF,IAAI,CAAC,KAAK,EAAE,CAAC;IACb,IAAI,MAAc,CAAC;IACnB,IAAI,CAAC;QACH,MAAM,GAAG,YAAY,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;IAC5C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO;IACT,CAAC;IACD,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,QAAQ,CAAC,eAAe,CAAC,EAAE,CAAC;QACrD,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;QACtB,IAAI,IAAwB,CAAC;QAC7B,IAAI,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC;YAAE,IAAI,GAAG,OAAO,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE,IAAI,CAAC,CAAC;aAC/D,IAAI,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC;YAAE,IAAI,GAAG,OAAO,CAAC,MAAM,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;QACtE,IAAI,CAAC,IAAI;YAAE,SAAS;QACpB,KAAK,MAAM,SAAS,IAAI,iBAAiB,CAAC,IAAI,CAAC,EAAE,CAAC;YAChD,IAAI,MAAM,CAAC,SAAS,CAAC,EAAE,CAAC;gBACtB,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;gBACpB,MAAM;YACR,CAAC;QACH,CAAC;IACH,CAAC;AACH,CAAC","sourcesContent":["/**\n * Finds the page modules named by inline `component: () => import('…')` thunks in `routes.ts`, so the server\n * compiler can put `page-entry-loader.cjs` in front of exactly those files. A page wired up any other way\n * declares its `'use server-entry'` directive itself.\n */\nimport { readFileSync, statSync } from 'node:fs';\nimport { dirname, join, resolve } from 'node:path';\n\nconst COMPONENT_THUNK = /component:\\s*(?:async\\s*)?\\(\\s*\\)\\s*=>\\s*import\\(\\s*(['\"])([^'\"]+)\\1\\s*\\)/g;\n\nconst EXTENSIONS = ['.tsx', '.ts', '.jsx', '.js'];\n\n// `join` rather than `base + '/index…'`: these are compared against Rspack's own resource paths, which on\n// Windows carry backslashes.\nfunction resolveCandidates(base: string): string[] {\n return [...EXTENSIONS.map((ext) => base + ext), ...EXTENSIONS.map((ext) => join(base, `index${ext}`)), base];\n}\n\nfunction isFile(path: string): boolean {\n try {\n return statSync(path).isFile();\n } catch {\n return false;\n }\n}\n\nexport function scanPageFiles(routesFile: string, srcDir: string, into: Set<string>): void {\n into.clear();\n let source: string;\n try {\n source = readFileSync(routesFile, 'utf8');\n } catch {\n return;\n }\n for (const match of source.matchAll(COMPONENT_THUNK)) {\n const spec = match[2];\n let base: string | undefined;\n if (spec.startsWith('.')) base = resolve(dirname(routesFile), spec);\n else if (spec.startsWith('@/')) base = resolve(srcDir, spec.slice(2));\n if (!base) continue;\n for (const candidate of resolveCandidates(base)) {\n if (isFile(candidate)) {\n into.add(candidate);\n break;\n }\n }\n }\n}\n"]}
|
|
@@ -1,2 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `process.env` view compiled into the browser bundle: `NODE_ENV` plus the `PUBLIC_`-prefixed variables.
|
|
3
|
+
* A stray read of anything else in client code becomes `undefined` rather than shipping the value.
|
|
4
|
+
*
|
|
5
|
+
* Also what `env-shadow-loader.cjs` shadows SSR-layer modules with, so the server renders what the browser
|
|
6
|
+
* hydrates.
|
|
7
|
+
*/
|
|
1
8
|
export declare function publicEnv(isDev: boolean): Record<string, string>;
|
|
2
|
-
//# sourceMappingURL=public-env.d.ts.map
|
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `process.env` view compiled into the browser bundle: `NODE_ENV` plus the `PUBLIC_`-prefixed variables.
|
|
3
|
+
* A stray read of anything else in client code becomes `undefined` rather than shipping the value.
|
|
4
|
+
*
|
|
5
|
+
* Also what `env-shadow-loader.cjs` shadows SSR-layer modules with, so the server renders what the browser
|
|
6
|
+
* hydrates.
|
|
7
|
+
*/
|
|
1
8
|
export function publicEnv(isDev) {
|
|
2
9
|
const entries = Object.entries(process.env).filter((entry) => entry[0].startsWith('PUBLIC_') && entry[1] !== undefined);
|
|
3
10
|
return { NODE_ENV: isDev ? 'development' : 'production', ...Object.fromEntries(entries) };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"public-env.js","sourceRoot":"","sources":["../../src/builder/public-env.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,SAAS,CAAC,KAAc;IACtC,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,KAAK,EAA6B,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,SAAS,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,KAAK,SAAS,CAAC,CAAC;IACnJ,OAAO,EAAE,QAAQ,EAAE,KAAK,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,YAAY,EAAE,GAAG,MAAM,CAAC,WAAW,CAAC,OAAO,CAAC,EAAE,CAAC;AAC5F,CAAC","sourcesContent":["
|
|
1
|
+
{"version":3,"file":"public-env.js","sourceRoot":"","sources":["../../src/builder/public-env.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,MAAM,UAAU,SAAS,CAAC,KAAc;IACtC,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,KAAK,EAA6B,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,SAAS,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,KAAK,SAAS,CAAC,CAAC;IACnJ,OAAO,EAAE,QAAQ,EAAE,KAAK,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,YAAY,EAAE,GAAG,MAAM,CAAC,WAAW,CAAC,OAAO,CAAC,EAAE,CAAC;AAC5F,CAAC","sourcesContent":["/**\n * The `process.env` view compiled into the browser bundle: `NODE_ENV` plus the `PUBLIC_`-prefixed variables.\n * A stray read of anything else in client code becomes `undefined` rather than shipping the value.\n *\n * Also what `env-shadow-loader.cjs` shadows SSR-layer modules with, so the server renders what the browser\n * hydrates.\n */\nexport function publicEnv(isDev: boolean): Record<string, string> {\n const entries = Object.entries(process.env).filter((entry): entry is [string, string] => entry[0].startsWith('PUBLIC_') && entry[1] !== undefined);\n return { NODE_ENV: isDev ? 'development' : 'production', ...Object.fromEntries(entries) };\n}\n"]}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fails the build when `react` and `react-dom` resolve to different versions, and warns when they are not the
|
|
3
|
+
* version this release was tested with.
|
|
4
|
+
*
|
|
5
|
+
* The mismatch is an error because it never works. A different minor is only a warning: upstream declares
|
|
6
|
+
* `^19.1.0` and may well be right, and refusing to build would make rshono the reason an app cannot take a
|
|
7
|
+
* React release — a worse failure than the one it would prevent.
|
|
8
|
+
*
|
|
9
|
+
* @param rootDir - The project root, so resolution follows the *app's* `node_modules` rather than the
|
|
10
|
+
* framework's.
|
|
11
|
+
*/
|
|
12
|
+
export declare function checkReactVersions(rootDir: string): void;
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The one dependency check the framework makes on an app: that `react` and `react-dom` are the same build.
|
|
3
|
+
*
|
|
4
|
+
* React Server Components couple the two across bundles — `react-server-dom-rspack` reaches into
|
|
5
|
+
* `ReactSharedInternals` and emits a flight payload the *matching* `react-dom` has to parse — so a split
|
|
6
|
+
* resolution is not version skew to be tolerated. It is a crash inside minified React at render time, with
|
|
7
|
+
* nothing in the message pointing at the cause.
|
|
8
|
+
*
|
|
9
|
+
* The monorepo prevents it with `overrides` in `pnpm-workspace.yaml` and `packages/create` pins both exactly
|
|
10
|
+
* for the same reason, because a generated app has no overrides to rely on. Neither helps an app that grew
|
|
11
|
+
* past its scaffold, which is why the check lives here — on the one path every `dev` and `build` takes.
|
|
12
|
+
*/
|
|
13
|
+
import { createRequire } from 'node:module';
|
|
14
|
+
import { join } from 'node:path';
|
|
15
|
+
/**
|
|
16
|
+
* The React this release was tested against, read off the framework's own manifest rather than restated as a
|
|
17
|
+
* constant — the pin and the check cannot drift apart that way. `undefined` if it cannot be read, which only
|
|
18
|
+
* turns the advisory warning off.
|
|
19
|
+
*/
|
|
20
|
+
function testedReactVersion() {
|
|
21
|
+
try {
|
|
22
|
+
const manifest = createRequire(import.meta.url)('../../package.json');
|
|
23
|
+
return manifest.devDependencies?.react;
|
|
24
|
+
}
|
|
25
|
+
catch {
|
|
26
|
+
return undefined;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
/** The version of an installed package as the *app* resolves it, or `undefined` if it has none. */
|
|
30
|
+
function installedVersion(require, name) {
|
|
31
|
+
try {
|
|
32
|
+
return require(`${name}/package.json`).version;
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
return undefined;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
/** `19.2.8` → `19.2`, for comparing minors without pulling in a semver parser. */
|
|
39
|
+
function minor(version) {
|
|
40
|
+
return version.split('.', 2).join('.');
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Fails the build when `react` and `react-dom` resolve to different versions, and warns when they are not the
|
|
44
|
+
* version this release was tested with.
|
|
45
|
+
*
|
|
46
|
+
* The mismatch is an error because it never works. A different minor is only a warning: upstream declares
|
|
47
|
+
* `^19.1.0` and may well be right, and refusing to build would make rshono the reason an app cannot take a
|
|
48
|
+
* React release — a worse failure than the one it would prevent.
|
|
49
|
+
*
|
|
50
|
+
* @param rootDir - The project root, so resolution follows the *app's* `node_modules` rather than the
|
|
51
|
+
* framework's.
|
|
52
|
+
*/
|
|
53
|
+
export function checkReactVersions(rootDir) {
|
|
54
|
+
// Resolved from the project's manifest, so a pnpm layout answers with what the app actually gets.
|
|
55
|
+
const require = createRequire(join(rootDir, 'package.json'));
|
|
56
|
+
const react = installedVersion(require, 'react');
|
|
57
|
+
const reactDom = installedVersion(require, 'react-dom');
|
|
58
|
+
// Missing altogether is the resolver's to report, against the import that needed it and with the path.
|
|
59
|
+
if (!react || !reactDom)
|
|
60
|
+
return;
|
|
61
|
+
if (react !== reactDom) {
|
|
62
|
+
throw new Error(`[rshono] react ${react} and react-dom ${reactDom} resolve to different versions.\n` +
|
|
63
|
+
' React Server Components couple them: the RSC runtime emits a payload the matching react-dom parses, ' +
|
|
64
|
+
'so a split resolution fails inside React while rendering rather than here.\n' +
|
|
65
|
+
' Install both at the same version. If a transitive dependency is what split them, pin them — ' +
|
|
66
|
+
'`overrides` in npm, `pnpm.overrides` in pnpm, `resolutions` in yarn.');
|
|
67
|
+
}
|
|
68
|
+
const tested = testedReactVersion();
|
|
69
|
+
if (tested && minor(tested) !== minor(react)) {
|
|
70
|
+
console.warn(` ⚠ react ${react} is not the ${minor(tested)}.x this rshono was tested against. The RSC runtime is pinned\n` +
|
|
71
|
+
' to an exact version and reaches into React internals, so a different minor can fail at render time.');
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
//# sourceMappingURL=react-versions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"react-versions.js","sourceRoot":"","sources":["../../src/builder/react-versions.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC;;;;GAIG;AACH,SAAS,kBAAkB;IACzB,IAAI,CAAC;QACH,MAAM,QAAQ,GAAG,aAAa,CAAC,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,oBAAoB,CAEnE,CAAC;QACF,OAAO,QAAQ,CAAC,eAAe,EAAE,KAAK,CAAC;IACzC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;AACH,CAAC;AAED,mGAAmG;AACnG,SAAS,gBAAgB,CAAC,OAAuB,EAAE,IAAY;IAC7D,IAAI,CAAC;QACH,OAAQ,OAAO,CAAC,GAAG,IAAI,eAAe,CAA0B,CAAC,OAAO,CAAC;IAC3E,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;AACH,CAAC;AAED,kFAAkF;AAClF,SAAS,KAAK,CAAC,OAAe;IAC5B,OAAO,OAAO,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AACzC,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,kBAAkB,CAAC,OAAe;IAChD,kGAAkG;IAClG,MAAM,OAAO,GAAG,aAAa,CAAC,IAAI,CAAC,OAAO,EAAE,cAAc,CAAC,CAAC,CAAC;IAC7D,MAAM,KAAK,GAAG,gBAAgB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;IACjD,MAAM,QAAQ,GAAG,gBAAgB,CAAC,OAAO,EAAE,WAAW,CAAC,CAAC;IACxD,uGAAuG;IACvG,IAAI,CAAC,KAAK,IAAI,CAAC,QAAQ;QAAE,OAAO;IAEhC,IAAI,KAAK,KAAK,QAAQ,EAAE,CAAC;QACvB,MAAM,IAAI,KAAK,CACb,kBAAkB,KAAK,kBAAkB,QAAQ,mCAAmC;YAClF,wGAAwG;YACxG,8EAA8E;YAC9E,gGAAgG;YAChG,sEAAsE,CACzE,CAAC;IACJ,CAAC;IAED,MAAM,MAAM,GAAG,kBAAkB,EAAE,CAAC;IACpC,IAAI,MAAM,IAAI,KAAK,CAAC,MAAM,CAAC,KAAK,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC;QAC7C,OAAO,CAAC,IAAI,CACV,aAAa,KAAK,eAAe,KAAK,CAAC,MAAM,CAAC,gEAAgE;YAC5G,yGAAyG,CAC5G,CAAC;IACJ,CAAC;AACH,CAAC","sourcesContent":["/**\n * The one dependency check the framework makes on an app: that `react` and `react-dom` are the same build.\n *\n * React Server Components couple the two across bundles — `react-server-dom-rspack` reaches into\n * `ReactSharedInternals` and emits a flight payload the *matching* `react-dom` has to parse — so a split\n * resolution is not version skew to be tolerated. It is a crash inside minified React at render time, with\n * nothing in the message pointing at the cause.\n *\n * The monorepo prevents it with `overrides` in `pnpm-workspace.yaml` and `packages/create` pins both exactly\n * for the same reason, because a generated app has no overrides to rely on. Neither helps an app that grew\n * past its scaffold, which is why the check lives here — on the one path every `dev` and `build` takes.\n */\nimport { createRequire } from 'node:module';\nimport { join } from 'node:path';\n\n/**\n * The React this release was tested against, read off the framework's own manifest rather than restated as a\n * constant — the pin and the check cannot drift apart that way. `undefined` if it cannot be read, which only\n * turns the advisory warning off.\n */\nfunction testedReactVersion(): string | undefined {\n try {\n const manifest = createRequire(import.meta.url)('../../package.json') as {\n devDependencies?: Record<string, string>;\n };\n return manifest.devDependencies?.react;\n } catch {\n return undefined;\n }\n}\n\n/** The version of an installed package as the *app* resolves it, or `undefined` if it has none. */\nfunction installedVersion(require: NodeJS.Require, name: string): string | undefined {\n try {\n return (require(`${name}/package.json`) as { version?: string }).version;\n } catch {\n return undefined;\n }\n}\n\n/** `19.2.8` → `19.2`, for comparing minors without pulling in a semver parser. */\nfunction minor(version: string): string {\n return version.split('.', 2).join('.');\n}\n\n/**\n * Fails the build when `react` and `react-dom` resolve to different versions, and warns when they are not the\n * version this release was tested with.\n *\n * The mismatch is an error because it never works. A different minor is only a warning: upstream declares\n * `^19.1.0` and may well be right, and refusing to build would make rshono the reason an app cannot take a\n * React release — a worse failure than the one it would prevent.\n *\n * @param rootDir - The project root, so resolution follows the *app's* `node_modules` rather than the\n * framework's.\n */\nexport function checkReactVersions(rootDir: string): void {\n // Resolved from the project's manifest, so a pnpm layout answers with what the app actually gets.\n const require = createRequire(join(rootDir, 'package.json'));\n const react = installedVersion(require, 'react');\n const reactDom = installedVersion(require, 'react-dom');\n // Missing altogether is the resolver's to report, against the import that needed it and with the path.\n if (!react || !reactDom) return;\n\n if (react !== reactDom) {\n throw new Error(\n `[rshono] react ${react} and react-dom ${reactDom} resolve to different versions.\\n` +\n ' React Server Components couple them: the RSC runtime emits a payload the matching react-dom parses, ' +\n 'so a split resolution fails inside React while rendering rather than here.\\n' +\n ' Install both at the same version. If a transitive dependency is what split them, pin them — ' +\n '`overrides` in npm, `pnpm.overrides` in pnpm, `resolutions` in yarn.',\n );\n }\n\n const tested = testedReactVersion();\n if (tested && minor(tested) !== minor(react)) {\n console.warn(\n ` ⚠ react ${react} is not the ${minor(tested)}.x this rshono was tested against. The RSC runtime is pinned\\n` +\n ' to an exact version and reaches into React internals, so a different minor can fail at render time.',\n );\n }\n}\n"]}
|
|
@@ -1,14 +1,23 @@
|
|
|
1
1
|
import { type RspackOptions } from '@rspack/core';
|
|
2
|
-
import type {
|
|
2
|
+
import type { RshonoConfig } from '../config.js';
|
|
3
3
|
import type { DeployPreset } from '../deploy/presets.js';
|
|
4
|
+
/**
|
|
5
|
+
* The directory a command writes its bundles to, relative to the project root — one per command, never
|
|
6
|
+
* shared: a `rshono build` alongside a running `rshono dev` would `clean` away the route chunks that
|
|
7
|
+
* server imports lazily, and Rspack's watch-mode emit never re-emits a file it believes it wrote.
|
|
8
|
+
*
|
|
9
|
+
* Both names are a single level under the root, which `deploy/filesystem.ts` relies on to derive the
|
|
10
|
+
* project root from where the bundle ended up.
|
|
11
|
+
*/
|
|
12
|
+
export declare const BUILD_OUT_DIR = "dist";
|
|
13
|
+
export declare const DEV_OUT_DIR = ".rshono";
|
|
4
14
|
export interface RspackConfigOptions {
|
|
5
15
|
rootDir: string;
|
|
6
16
|
isDev: boolean;
|
|
7
|
-
/** The project's resolved config — supplies the {@link
|
|
8
|
-
config:
|
|
17
|
+
/** The project's resolved config — supplies the {@link RshonoConfig.rspack} hook and the runtime settings baked into the server bundle. */
|
|
18
|
+
config: RshonoConfig;
|
|
9
19
|
/** The platform being built for: decides the `@rshono/deploy` runtime and any server-compiler overrides. */
|
|
10
20
|
preset: DeployPreset;
|
|
11
21
|
onServerComponentChanges?: () => void;
|
|
12
22
|
}
|
|
13
23
|
export declare function createConfigs(options: RspackConfigOptions): [RspackOptions, RspackOptions];
|
|
14
|
-
//# sourceMappingURL=rspack-config.d.ts.map
|