@rshono/core 1.0.0-rc.17 → 1.0.0-rc.19
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/README.md +34 -4
- package/dist/builder/env-shadow-loader.cjs +58 -2
- package/dist/builder/rspack-config.d.ts.map +1 -1
- package/dist/builder/rspack-config.js +51 -7
- package/dist/builder/rspack-config.js.map +1 -1
- package/dist/builder/server-only-imports.d.ts +33 -0
- package/dist/builder/server-only-imports.d.ts.map +1 -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.map +1 -1
- package/dist/cli/build.js +35 -3
- package/dist/cli/build.js.map +1 -1
- package/dist/cli/dev.d.ts.map +1 -1
- package/dist/cli/dev.js +49 -0
- package/dist/cli/dev.js.map +1 -1
- package/dist/cli/index.js +14 -8
- package/dist/cli/index.js.map +1 -1
- package/dist/config.d.ts +10 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js.map +1 -1
- package/dist/deploy/cloudflare/runtime.d.ts.map +1 -1
- package/dist/deploy/cloudflare/runtime.js +31 -5
- package/dist/deploy/cloudflare/runtime.js.map +1 -1
- package/dist/deploy/contract.d.ts +6 -2
- package/dist/deploy/contract.d.ts.map +1 -1
- package/dist/deploy/contract.js.map +1 -1
- package/dist/deploy/filesystem.d.ts.map +1 -1
- package/dist/deploy/filesystem.js +2 -1
- package/dist/deploy/filesystem.js.map +1 -1
- package/dist/deploy/node/runtime.d.ts.map +1 -1
- package/dist/deploy/node/runtime.js +3 -4
- package/dist/deploy/node/runtime.js.map +1 -1
- package/dist/deploy/presets.d.ts +8 -0
- package/dist/deploy/presets.d.ts.map +1 -1
- package/dist/deploy/presets.js +1 -0
- package/dist/deploy/presets.js.map +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/dist/router.d.ts +93 -25
- package/dist/router.d.ts.map +1 -1
- package/dist/router.js.map +1 -1
- package/dist/runtime/boundaries.d.ts +11 -4
- package/dist/runtime/boundaries.d.ts.map +1 -1
- package/dist/runtime/boundaries.js.map +1 -1
- package/dist/runtime/context.d.ts +60 -9
- package/dist/runtime/context.d.ts.map +1 -1
- package/dist/runtime/context.js +61 -8
- package/dist/runtime/context.js.map +1 -1
- package/dist/runtime/entry.client.js +156 -21
- package/dist/runtime/entry.client.js.map +1 -1
- package/dist/runtime/entry.rsc.d.ts +7 -0
- package/dist/runtime/entry.rsc.d.ts.map +1 -1
- package/dist/runtime/entry.rsc.js +202 -57
- package/dist/runtime/entry.rsc.js.map +1 -1
- package/dist/runtime/flight-inject.d.ts +1 -1
- package/dist/runtime/flight-inject.d.ts.map +1 -1
- package/dist/runtime/flight-inject.js +207 -40
- package/dist/runtime/flight-inject.js.map +1 -1
- package/dist/runtime/request.d.ts.map +1 -1
- package/dist/runtime/request.js +10 -0
- package/dist/runtime/request.js.map +1 -1
- package/dist/runtime/validate-entries.d.ts +78 -0
- package/dist/runtime/validate-entries.d.ts.map +1 -0
- package/dist/runtime/validate-entries.js +301 -0
- package/dist/runtime/validate-entries.js.map +1 -0
- package/dist/server/prerendered.d.ts +30 -11
- package/dist/server/prerendered.d.ts.map +1 -1
- package/dist/server/prerendered.js +72 -14
- package/dist/server/prerendered.js.map +1 -1
- package/dist/server/server-config.d.ts +20 -1
- package/dist/server/server-config.d.ts.map +1 -1
- package/dist/server/server-config.js +27 -1
- package/dist/server/server-config.js.map +1 -1
- package/dist/server/ssg.d.ts.map +1 -1
- package/dist/server/ssg.js +170 -27
- package/dist/server/ssg.js.map +1 -1
- package/dist/server/static.d.ts.map +1 -1
- package/dist/server/static.js +7 -1
- package/dist/server/static.js.map +1 -1
- package/package.json +5 -4
package/README.md
CHANGED
|
@@ -17,10 +17,11 @@ prerendering, and hard env/secret safety.
|
|
|
17
17
|
|
|
18
18
|
> **Release candidate.** The framework itself is covered end to end (see [Testing](#testing)) and its API is
|
|
19
19
|
> settled. What is not settled underneath it: Rspack's RSC support is an experimental API
|
|
20
|
-
> (`rspack.experiments.rsc`) and `react-server-dom-rspack`
|
|
21
|
-
> versions — in the manifests and in workspace overrides —
|
|
22
|
-
>
|
|
23
|
-
> caveat, and it is the reason to read the
|
|
20
|
+
> (`rspack.experiments.rsc`) and `react-server-dom-rspack` has not reached 1.0, so its own minor bumps are
|
|
21
|
+
> breaking by convention. Both are pinned to exact versions — in the manifests and in workspace overrides —
|
|
22
|
+
> and a release of rshono is what moves them, so an upstream change reaches you as a tested release rather
|
|
23
|
+
> than as a broken install. That is the whole of the caveat, and it is the reason to read the
|
|
24
|
+
> [changelog](../../CHANGELOG.md) before upgrading.
|
|
24
25
|
|
|
25
26
|
**Full documentation: [rshono.com/docs](https://www.rshono.com/docs).**
|
|
26
27
|
|
|
@@ -286,6 +287,9 @@ Every target streams, which is the bar a new one has to clear.
|
|
|
286
287
|
- **Node ≥ 22.18** (worker threads, `process.loadEnvFile`, `Promise.withResolvers`, `URL.parse`, and native
|
|
287
288
|
TypeScript stripping, so a `.ts` config needs no loader) and **React ≥ 19.1** (the floor
|
|
288
289
|
`react-server-dom-rspack` requires).
|
|
290
|
+
- **ESM only.** The package declares `import` and `types` conditions and no `require` one, so
|
|
291
|
+
`require('@rshono/core')` is `ERR_PACKAGE_PATH_NOT_EXPORTED` rather than a working call — deliberately,
|
|
292
|
+
since the framework's own graph is ESM throughout. Use `import`, or `await import()` from CommonJS.
|
|
289
293
|
- No response compression, no base path (`siteUrl` is a bare origin), and wildcard, optional and regex
|
|
290
294
|
params cannot be prerendered.
|
|
291
295
|
- **No link prefetching**, by choice: a link is one fetch at click time and nothing before it. Speculative
|
|
@@ -298,6 +302,32 @@ Every target streams, which is the bar a new one has to clear.
|
|
|
298
302
|
January 2026. Where it is missing there is no interception at all and every link is a real browser load,
|
|
299
303
|
which a server-rendered app answers correctly; only the soft part is gone. Scroll restoration, the fragment
|
|
300
304
|
jump and the post-navigation focus reset are all the browser's.
|
|
305
|
+
- **`redirect()` and `notFound()` must be reached before the page shell is sent.** A page streams: the status
|
|
306
|
+
line and the first bytes go out as soon as the shell is ready, and HTTP has no take-backs after that. Called
|
|
307
|
+
from a `<Suspense>` boundary that resolves later, the signal can no longer be a 3xx or a 404 — the response
|
|
308
|
+
is already committed as `200 text/html`. The signal still rides the payload as a digest, so a browser with
|
|
309
|
+
JavaScript acts on it: `redirect()` navigates, and `notFound()` — which has nowhere to navigate to — asks
|
|
310
|
+
for the page once more and then paints a plain "Page not found" panel, since a second identical response is
|
|
311
|
+
proof that reloading will not help. A visitor without JavaScript is left on the fallback under a 200, and a
|
|
312
|
+
crawler indexes that 200 as a soft 404. The fix is app-side: decide in Hono
|
|
313
|
+
middleware, or in the page component body above the boundary. `rshono dev` warns when it happens.
|
|
314
|
+
- **A page route answers `GET`, `POST` and `HEAD`.** Every other method is a 404 rather than a 405: the
|
|
315
|
+
`Allow` header a 405 owes the client means tracking the methods registered per path, which is state on a hot
|
|
316
|
+
path for a distinction nothing acts on differently here. An endpoint route is the way to answer a `PUT`,
|
|
317
|
+
`PATCH`, `DELETE` or `OPTIONS`.
|
|
318
|
+
- **A page's `ctx` prop is non-enumerable**, so `<Child {...props} />` hands a _server_ child
|
|
319
|
+
`ctx: undefined` — silently, since a spread copies enumerables only, and the type still says it is there.
|
|
320
|
+
It cannot be otherwise: an enumerable `ctx` would put `ctx.hono.env`, every binding and secret, into
|
|
321
|
+
React's dev-only serialization of a server component's props. Nested server components are meant to call
|
|
322
|
+
`getRequestContext()` rather than be handed the context.
|
|
323
|
+
- **`rshono build` does not type-check.** swc strips types and `tsc` is never invoked, so the build is as fast
|
|
324
|
+
as it is and no faster because it skipped something. Several of the framework's guarantees are types alone —
|
|
325
|
+
the `handler` an endpoint module owes, and `defineRoutes`' path ↔ props and `staticPaths` ↔ path checks — so
|
|
326
|
+
run `tsc --noEmit` (`npm run typecheck` in a scaffolded app) in CI beside the build. The mistakes that make
|
|
327
|
+
a route unservable are checked at build time whether or not you do.
|
|
328
|
+
- **`.env` and `rshono.config.ts` are read once, at startup**, and what a build needs from them is compiled
|
|
329
|
+
in — so `rshono dev` does not pick up an edit to either, and a rebuild that serves the old value looks like
|
|
330
|
+
nothing happened. Restart it; it watches both and says so when one changes.
|
|
301
331
|
- The dev proxy doesn't forward WebSocket upgrades to a custom sub-app; production is unaffected.
|
|
302
332
|
- Dev source maps embed the original source of `'use server'` modules (dev binds 127.0.0.1 only, and
|
|
303
333
|
production ships no client source maps).
|
|
@@ -20,6 +20,47 @@ const DIRECTIVE = '([\'"])use [a-z -]+\\1\\s*;?';
|
|
|
20
20
|
*/
|
|
21
21
|
const DIRECTIVE_PROLOGUE = new RegExp(`^${TRIVIA}(?:${DIRECTIVE}${TRIVIA})*`);
|
|
22
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
|
+
|
|
23
64
|
/**
|
|
24
65
|
* Shadows `process.env` with the PUBLIC_-only view inside SSR-layer modules, so a secret read from a
|
|
25
66
|
* `'use client'` component renders empty on the server rather than leaking into the HTML stream — and SSR
|
|
@@ -30,8 +71,10 @@ const DIRECTIVE_PROLOGUE = new RegExp(`^${TRIVIA}(?:${DIRECTIVE}${TRIVIA})*`);
|
|
|
30
71
|
* guarantee with nothing to notice.
|
|
31
72
|
*/
|
|
32
73
|
module.exports = function envShadowLoader(source) {
|
|
33
|
-
|
|
34
|
-
|
|
74
|
+
// Two steps rather than the regex alone: this loader sees every module in the bundle, and most of them do
|
|
75
|
+
// not contain the substring at all, which `includes` settles far more cheaply than a scan for word breaks.
|
|
76
|
+
if (!source.includes('process') || !MENTIONS_PROCESS.test(source)) return source;
|
|
77
|
+
const { prelude, layer, appSrcPrefix } = this.getOptions();
|
|
35
78
|
if (!this._module) {
|
|
36
79
|
throw new Error(
|
|
37
80
|
"[rshono] env-shadow-loader could not read the module's layer (Rspack's `this._module` is unavailable). " +
|
|
@@ -40,6 +83,19 @@ module.exports = function envShadowLoader(source) {
|
|
|
40
83
|
);
|
|
41
84
|
}
|
|
42
85
|
if (this._module.layer !== layer) return source;
|
|
86
|
+
|
|
87
|
+
if (appSrcPrefix && this.resourcePath?.startsWith(appSrcPrefix) && GLOBAL_PROCESS.test(source)) {
|
|
88
|
+
// Rspack prints the module path ahead of this, so the message is only the part it does not know.
|
|
89
|
+
this.emitWarning(
|
|
90
|
+
new Error(
|
|
91
|
+
'[rshono] This module reads `process` through the global object. It is rendered on the server as part ' +
|
|
92
|
+
'of a client component, where `process.env` is shadowed with the `PUBLIC_`-only view — and that ' +
|
|
93
|
+
'shadow is a binding, so it cannot cover `globalThis.process`. A variable read that way is the real ' +
|
|
94
|
+
'one, and it ends up in the HTML. Read `process.env` directly instead.',
|
|
95
|
+
),
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
|
|
43
99
|
const prologue = source.match(DIRECTIVE_PROLOGUE)[0];
|
|
44
100
|
return prologue + prelude + source.slice(prologue.length);
|
|
45
101
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rspack-config.d.ts","sourceRoot":"","sources":["../../src/builder/rspack-config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAyB,KAAK,aAAa,EAAoB,MAAM,cAAc,CAAC;AAK3F,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AACjD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"rspack-config.d.ts","sourceRoot":"","sources":["../../src/builder/rspack-config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAyB,KAAK,aAAa,EAAoB,MAAM,cAAc,CAAC;AAK3F,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AACjD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAazD;;;;;;;GAOG;AACH,eAAO,MAAM,aAAa,SAAS,CAAC;AACpC,eAAO,MAAM,WAAW,YAAY,CAAC;AAqErC,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,OAAO,CAAC;IACf,2IAA2I;IAC3I,MAAM,EAAE,YAAY,CAAC;IACrB,4GAA4G;IAC5G,MAAM,EAAE,YAAY,CAAC;IACrB,wBAAwB,CAAC,EAAE,MAAM,IAAI,CAAC;CACvC;AA0BD,wBAAgB,aAAa,CAAC,OAAO,EAAE,mBAAmB,GAAG,CAAC,aAAa,EAAE,aAAa,CAAC,CAyQ1F"}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { rspack } from '@rspack/core';
|
|
2
2
|
import { ReactRefreshRspackPlugin } from '@rspack/plugin-react-refresh';
|
|
3
|
-
import { existsSync } from 'node:fs';
|
|
3
|
+
import { existsSync, readdirSync, readFileSync } from 'node:fs';
|
|
4
4
|
import { createRequire } from 'node:module';
|
|
5
|
-
import { basename, dirname, join, win32 } from 'node:path';
|
|
5
|
+
import { basename, dirname, join, sep, win32 } from 'node:path';
|
|
6
6
|
import { resolveServerConfig } from '../server/server-config.js';
|
|
7
|
+
import { serverOnlyImportPlugin } from './server-only-imports.js';
|
|
7
8
|
import { scanPageFiles } from './page-files.js';
|
|
8
9
|
import { publicEnv } from './public-env.js';
|
|
9
10
|
import { checkReactVersions } from './react-versions.js';
|
|
@@ -85,6 +86,31 @@ const NODE_TARGETS = ['node >= 22'];
|
|
|
85
86
|
function cssRule() {
|
|
86
87
|
return { test: /\.css$/i, type: 'css/auto' };
|
|
87
88
|
}
|
|
89
|
+
/** Source files worth reading for {@link mentions} — the app's own code, whatever dialect it is written in. */
|
|
90
|
+
const SOURCE_FILE = /\.[cm]?[jt]sx?$/;
|
|
91
|
+
/**
|
|
92
|
+
* Whether `identifier` appears anywhere in the app's own source. The whole tree rather than `src/server.ts`
|
|
93
|
+
* alone, so middleware registered from a helper module counts.
|
|
94
|
+
*
|
|
95
|
+
* A textual scan on purpose. What is being answered is "did the author think about this at all", which is a
|
|
96
|
+
* question a build warning is allowed to get wrong: the failure mode is a warning that is unwarranted, never a
|
|
97
|
+
* build that fails or a behaviour that changes. Resolving it properly would mean reading the module graph, and
|
|
98
|
+
* a middleware registered from a package outside `src/` would still slip past.
|
|
99
|
+
*/
|
|
100
|
+
function mentions(srcDir, identifier) {
|
|
101
|
+
try {
|
|
102
|
+
for (const entry of readdirSync(srcDir, { recursive: true, withFileTypes: true })) {
|
|
103
|
+
if (!entry.isFile() || !SOURCE_FILE.test(entry.name))
|
|
104
|
+
continue;
|
|
105
|
+
if (readFileSync(join(entry.parentPath, entry.name), 'utf8').includes(identifier))
|
|
106
|
+
return true;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
catch {
|
|
110
|
+
// An unreadable `src/` is not this function's to report: the build is about to fail on `routes.ts`.
|
|
111
|
+
}
|
|
112
|
+
return false;
|
|
113
|
+
}
|
|
88
114
|
export function createConfigs(options) {
|
|
89
115
|
const { rootDir, isDev, config, preset, onServerComponentChanges } = options;
|
|
90
116
|
const srcDir = join(rootDir, 'src');
|
|
@@ -102,9 +128,19 @@ export function createConfigs(options) {
|
|
|
102
128
|
// Optional, and staying optional — but per-request security is Hono middleware registered there, so an app
|
|
103
129
|
// without one has opted out of all of it, which is worth hearing once rather than discovering. Builds only:
|
|
104
130
|
// `dev` would print it on every rebuild, and it is not news on a developer's machine.
|
|
105
|
-
if (!
|
|
106
|
-
|
|
107
|
-
'
|
|
131
|
+
if (!isDev) {
|
|
132
|
+
if (!serverAppFile) {
|
|
133
|
+
console.warn(' ⚠ No src/server.ts — this build has no CSRF check and no request body cap. Both are Hono middleware\n' +
|
|
134
|
+
' (`csrf()`, `bodyLimit()`); `npx @rshono/create` scaffolds a src/server.ts with them registered.');
|
|
135
|
+
}
|
|
136
|
+
else if (!mentions(srcDir, 'bodyLimit')) {
|
|
137
|
+
// Having a src/server.ts is not the same as having the cap in it, and this half used to be warned about
|
|
138
|
+
// nowhere: every `'use server'` export is a public POST endpoint by design, and the action path buffers
|
|
139
|
+
// the whole body before it can decide anything about it.
|
|
140
|
+
console.warn(" ⚠ No bodyLimit() anywhere in src/ — this build has no request body cap. Every 'use server' export is\n" +
|
|
141
|
+
' a public POST endpoint, and the action path buffers whatever arrives before it can reject it.\n' +
|
|
142
|
+
' Add `server.use(bodyLimit({ maxSize: 1024 * 1024 }))` to src/server.ts (from `hono/body-limit`).');
|
|
143
|
+
}
|
|
108
144
|
}
|
|
109
145
|
const rscEntry = join(FRAMEWORK_DIST, 'runtime', 'entry.rsc.js');
|
|
110
146
|
const ssrEntry = join(FRAMEWORK_DIST, 'runtime', 'entry.ssr.js');
|
|
@@ -178,6 +214,7 @@ export function createConfigs(options) {
|
|
|
178
214
|
},
|
|
179
215
|
plugins: [
|
|
180
216
|
new ClientPlugin(),
|
|
217
|
+
serverOnlyImportPlugin(rootDir),
|
|
181
218
|
new rspack.DefinePlugin({ 'process.env': JSON.stringify(publicEnv(isDev)) }),
|
|
182
219
|
...(isDev ? [new rspack.HotModuleReplacementPlugin(), new ReactRefreshRspackPlugin()] : []),
|
|
183
220
|
],
|
|
@@ -260,7 +297,7 @@ export function createConfigs(options) {
|
|
|
260
297
|
// the same layer, and scoping this to the app's own source left those rendering against the real
|
|
261
298
|
// `process.env` while the browser bundle saw the `PUBLIC_`-only view — a hydration mismatch on
|
|
262
299
|
// anything the host sets, and a leak for anything secret. The loader's own layer check is what
|
|
263
|
-
// decides; every other module gets one `includes('process
|
|
300
|
+
// decides; every other module gets one `includes('process')` scan.
|
|
264
301
|
test: /\.[cm]?[tj]sx?$/,
|
|
265
302
|
enforce: 'pre',
|
|
266
303
|
use: [
|
|
@@ -273,6 +310,11 @@ export function createConfigs(options) {
|
|
|
273
310
|
options: {
|
|
274
311
|
prelude: `const process = Object.assign(Object.create(globalThis.process ?? Object.prototype), { env: ${JSON.stringify(publicEnv(isDev))} }); `,
|
|
275
312
|
layer: Layers.ssr,
|
|
313
|
+
// Only the app's own source is warned about when it reaches `process` through the global
|
|
314
|
+
// object, which no prelude can shadow — a library feature-detecting `globalThis.process` is
|
|
315
|
+
// doing nothing wrong and has no app secret to read. The separator is part of the prefix so
|
|
316
|
+
// a sibling directory named `src-old` is not mistaken for it.
|
|
317
|
+
appSrcPrefix: srcDir + sep,
|
|
276
318
|
},
|
|
277
319
|
},
|
|
278
320
|
],
|
|
@@ -301,7 +343,9 @@ export function createConfigs(options) {
|
|
|
301
343
|
pageScanPlugin,
|
|
302
344
|
new ServerPlugin({ onServerComponentChanges }),
|
|
303
345
|
// Bakes rshono.config.ts into the bundle, read back as `__RSHONO_CONFIG__` at request time.
|
|
304
|
-
new rspack.DefinePlugin({
|
|
346
|
+
new rspack.DefinePlugin({
|
|
347
|
+
__RSHONO_CONFIG__: JSON.stringify(resolveServerConfig(config, { isDev, outDir, envBindings: preset.envBindings })),
|
|
348
|
+
}),
|
|
305
349
|
],
|
|
306
350
|
performance: false,
|
|
307
351
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rspack-config.js","sourceRoot":"","sources":["../../src/builder/rspack-config.ts"],"names":[],"mappings":"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","sourcesContent":["import { rspack, type Compiler, type RspackOptions, type RuleSetRule } from '@rspack/core';\nimport { ReactRefreshRspackPlugin } from '@rspack/plugin-react-refresh';\nimport { existsSync } from 'node:fs';\nimport { createRequire } from 'node:module';\nimport { basename, dirname, join, win32 } from 'node:path';\nimport type { RshonoConfig } from '../config.js';\nimport type { DeployPreset } from '../deploy/presets.js';\nimport { resolveServerConfig } from '../server/server-config.js';\nimport { scanPageFiles } from './page-files.js';\nimport { publicEnv } from './public-env.js';\nimport { checkReactVersions } from './react-versions.js';\n\n// This file lives in `dist/builder/`, so the entries and loaders below are the *built* framework.\nconst FRAMEWORK_DIST = join(import.meta.dirname, '..');\nconst FRAMEWORK_ROOT = join(FRAMEWORK_DIST, '..');\n\nconst BUNDLED_PACKAGES = /^(@rshono\\/core|react|react-dom|react-server-dom-rspack|hono|@hono\\/node-server)(\\/|$)/;\n\n/**\n * The directory a command writes its bundles to, relative to the project root — one per command, never\n * shared: a `rshono build` alongside a running `rshono dev` would `clean` away the route chunks that\n * server imports lazily, and Rspack's watch-mode emit never re-emits a file it believes it wrote.\n *\n * Both names are a single level under the root, which `deploy/filesystem.ts` relies on to derive the\n * project root from where the bundle ended up.\n */\nexport const BUILD_OUT_DIR = 'dist';\nexport const DEV_OUT_DIR = '.rshono';\n\n/**\n * Packages the app's source ends up importing without ever naming them: the RSC transform rewrites a\n * `'use client'` module, a page or an action into imports of the RSC runtime, resolved from the app's `src/`.\n */\nconst INJECTED_PACKAGES = ['react-server-dom-rspack'];\n\n/** The `node_modules` directory a resolved file sits in — `…/node_modules/pkg/index.js` → `…/node_modules`. */\nfunction enclosingNodeModules(file: string): string | undefined {\n let dir = dirname(file);\n while (basename(dir) !== 'node_modules') {\n const parent = dirname(dir);\n if (parent === dir) return undefined;\n dir = parent;\n }\n return dir;\n}\n\n/**\n * Where packages are resolved from: the app's own `node_modules`, then the framework's — which is what makes\n * {@link INJECTED_PACKAGES} resolvable from app source under pnpm, where the framework's dependencies are\n * installed as siblings of `@rshono/core` rather than hoisted into the app's tree.\n *\n * Asking Node where the framework resolves the package finds it in any layout, and a search path rather than\n * an alias keeps the package's own `exports` conditions in play — the RSC layer, the SSR layer and each\n * deploy target need a different build of it.\n */\nfunction resolveModules(): string[] {\n const require = createRequire(import.meta.url);\n const dirs = new Set<string>();\n for (const name of INJECTED_PACKAGES) {\n try {\n const dir = enclosingNodeModules(require.resolve(name));\n if (dir) dirs.add(dir);\n } catch {\n // Left to the resolver to report against the request that actually needed it.\n }\n }\n // A checkout or a vendored copy, where the framework has real dependencies of its own.\n dirs.add(join(FRAMEWORK_ROOT, 'node_modules'));\n return ['node_modules', ...dirs];\n}\n\n/**\n * Whether a request names a file rather than a package, and so belongs in the bundle.\n *\n * Rspack's RSC plugins ask for their proxies by absolute path, which on Windows carries a drive letter —\n * externalizing one emits an `import(\"D:\\\\…\")` Node rejects. `win32.isAbsolute` accepts the POSIX form too,\n * so it runs on every platform and the Windows shapes stay testable off Windows.\n */\nfunction isPathRequest(request: string): boolean {\n return request.startsWith('.') || win32.isAbsolute(request);\n}\n\nconst BROWSER_TARGETS = ['last 2 versions', '> 0.2%', 'not dead', 'Firefox ESR'];\nconst NODE_TARGETS = ['node >= 22'];\n\n/**\n * Rspack's native CSS pipeline, which both compilers get. It parses *finished* CSS, so a stylesheet needing\n * PostCSS — Tailwind, most obviously — adds that loader through the {@link RshonoConfig.rspack} hook, which\n * keeps `postcss` a dependency of the apps that want one.\n *\n * A fresh object per compiler, so an app changing this rule does not silently change the other bundle's.\n */\nfunction cssRule(): RuleSetRule {\n return { test: /\\.css$/i, type: 'css/auto' };\n}\n\nexport interface RspackConfigOptions {\n rootDir: string;\n isDev: boolean;\n /** The project's resolved config — supplies the {@link RshonoConfig.rspack} hook and the runtime settings baked into the server bundle. */\n config: RshonoConfig;\n /** The platform being built for: decides the `@rshono/deploy` runtime and any server-compiler overrides. */\n preset: DeployPreset;\n onServerComponentChanges?: () => void;\n}\n\nexport function createConfigs(options: RspackConfigOptions): [RspackOptions, RspackOptions] {\n const { rootDir, isDev, config, preset, onServerComponentChanges } = options;\n const srcDir = join(rootDir, 'src');\n const mode = isDev ? 'development' : 'production';\n const outDir = isDev ? DEV_OUT_DIR : BUILD_OUT_DIR;\n\n // Before anything is compiled: a react/react-dom split fails inside React at render time, and this is the\n // one place that can say so with the two versions in hand.\n checkReactVersions(rootDir);\n\n const routesFile = ['routes.ts', 'routes.tsx'].map((f) => join(srcDir, f)).find(existsSync);\n if (!routesFile) {\n throw new Error(`[rshono] src/routes.ts not found in ${rootDir} — it is the one required file.`);\n }\n const serverAppFile = ['server.ts', 'server.tsx'].map((f) => join(srcDir, f)).find(existsSync);\n const serverAppAlias = serverAppFile ?? join(FRAMEWORK_DIST, 'runtime', 'empty-server-app.js');\n // Optional, and staying optional — but per-request security is Hono middleware registered there, so an app\n // without one has opted out of all of it, which is worth hearing once rather than discovering. Builds only:\n // `dev` would print it on every rebuild, and it is not news on a developer's machine.\n if (!serverAppFile && !isDev) {\n console.warn(\n ' ⚠ No src/server.ts — this build has no CSRF check and no request body cap. Both are Hono middleware\\n' +\n ' (`csrf()`, `bodyLimit()`); `npx @rshono/create` scaffolds a src/server.ts with them registered.',\n );\n }\n\n const rscEntry = join(FRAMEWORK_DIST, 'runtime', 'entry.rsc.js');\n const ssrEntry = join(FRAMEWORK_DIST, 'runtime', 'entry.ssr.js');\n const clientEntry = join(FRAMEWORK_DIST, 'runtime', 'entry.client.js');\n\n const swcRule = (targets: string[]): RuleSetRule => ({\n test: /\\.[cm]?[jt]sx?$/,\n exclude: /[\\\\/]core-js[\\\\/]/,\n use: {\n loader: 'builtin:swc-loader',\n options: {\n detectSyntax: 'auto',\n jsc: {\n transform: { react: { runtime: 'automatic', development: isDev } },\n experimental: { keepImportAttributes: true },\n },\n env: { targets },\n rspackExperiments: { reactServerComponents: true },\n },\n },\n type: 'javascript/auto',\n });\n\n const resolveBase = {\n extensions: ['.tsx', '.ts', '.jsx', '.js', '.json'],\n extensionAlias: { '.js': ['.ts', '.tsx', '.js'] },\n modules: resolveModules(),\n };\n\n // Ahead of whatever the Rspack target implies ('...'), which is what hands the server bundle the right\n // build of React and the RSC runtime. Unset for Node, whose target already implies the `node` condition.\n const runtimeConditions = preset.resolveConditions ?? [];\n const serverResolveBase = runtimeConditions.length > 0 ? { ...resolveBase, conditionNames: [...runtimeConditions, '...'] } : resolveBase;\n const rscConditionNames = ['react-server', ...runtimeConditions, '...'];\n\n const { ServerPlugin, ClientPlugin } = rspack.experiments.rsc.createPlugins();\n const { Layers } = rspack.experiments.rsc;\n\n const pageFiles = new Set<string>();\n scanPageFiles(routesFile, srcDir, pageFiles);\n const pageScanPlugin = {\n apply(compiler: Compiler) {\n const refresh = () => scanPageFiles(routesFile, srcDir, pageFiles);\n compiler.hooks.beforeRun.tap('rshono/page-scan', refresh);\n compiler.hooks.watchRun.tap('rshono/page-scan', refresh);\n },\n };\n\n const clientConfig: RspackOptions = {\n name: 'client',\n mode,\n target: 'web',\n context: rootDir,\n // Never in a build: a client map is served from `/_static` like everything else beside it, and it would\n // publish the original source of the app's own modules. Dev binds 127.0.0.1 only.\n devtool: isDev ? 'source-map' : false,\n entry: { main: clientEntry },\n output: {\n path: join(rootDir, outDir, 'static'),\n publicPath: '/_static/',\n clean: !isDev,\n filename: isDev ? 'chunks/main.js' : 'chunks/main.[contenthash].js',\n chunkFilename: isDev ? 'chunks/[name].js' : 'chunks/[name].[contenthash].js',\n cssFilename: isDev ? 'chunks/[name].css' : 'chunks/[name].[contenthash].css',\n cssChunkFilename: isDev ? 'chunks/[name].css' : 'chunks/[name].[contenthash].css',\n assetModuleFilename: 'assets/[name].[hash][ext]',\n },\n optimization: {\n moduleIds: isDev ? 'named' : 'hashed',\n },\n resolve: {\n ...resolveBase,\n alias: { '@': srcDir },\n },\n module: {\n rules: [swcRule(BROWSER_TARGETS), cssRule(), { test: /\\.(png|jpe?g|gif|webp|avif|ico|svg|woff2?|ttf|otf)$/i, type: 'asset' }],\n },\n plugins: [\n new ClientPlugin(),\n new rspack.DefinePlugin({ 'process.env': JSON.stringify(publicEnv(isDev)) }),\n ...(isDev ? [new rspack.HotModuleReplacementPlugin(), new ReactRefreshRspackPlugin()] : []),\n ],\n performance: false,\n };\n\n const serverConfig: RspackOptions = {\n name: 'server',\n mode,\n target: 'node',\n context: rootDir,\n // In a build too, unlike the client's: the bundle is minified, and without a map every stack trace that\n // reaches `onServerError` — the error-tracker funnel — is unmappable minified frames. A server map is\n // never served to anyone; `dist/server` is not on a public path, and the runtime enables Node's own\n // mapping so no host has to pass a flag.\n devtool: 'source-map',\n entry: { main: rscEntry },\n output: {\n path: join(rootDir, outDir, 'server'),\n // Off in dev: route chunks are imported lazily, so a superseded one on disk is harmless where deleting\n // one mid-request is not. `rshono dev` empties the directory on startup instead.\n clean: !isDev,\n module: true,\n chunkFormat: 'module',\n chunkLoading: 'import',\n library: { type: 'module' },\n filename: 'main.mjs',\n chunkFilename: 'chunks/[name].mjs',\n publicPath: '/_static/',\n assetModuleFilename: 'assets/[name].[hash][ext]',\n },\n optimization: {\n minimize: isDev ? false : true,\n },\n externalsType: 'module-import',\n externals: [\n ({ request, contextInfo }, callback) => {\n if (\n !request ||\n isPathRequest(request) ||\n request.startsWith('@/') ||\n request.startsWith('@rshono/') ||\n request.startsWith('builtin:') ||\n request.includes('!') ||\n BUNDLED_PACKAGES.test(request) ||\n // Anything reached from the SSR layer, which is where `'use client'` components are rendered on the\n // server. It has to be *compiled* rather than imported by name, because the env-shadow loader is what\n // keeps a secret out of SSR'd HTML and a loader cannot run on a module the bundle only names — an\n // externalized third-party client component is loaded raw at request time and reads the real\n // `process.env`. Widening the loader's rule to cover `node_modules` did not reach this: there was no\n // module for it to run on.\n //\n // Free of the usual bundling trade-off, too. Every module in this layer is in the *browser* bundle as\n // well, so it is already required to be bundleable — a native addon or a `__dirname` file read could\n // not have been a client component in the first place. The RSC layer, where server-only dependencies\n // live, keeps the externals policy.\n contextInfo?.issuerLayer === Layers.ssr\n ) {\n return callback();\n }\n callback(undefined, `module-import ${request}`);\n },\n ],\n resolve: {\n ...serverResolveBase,\n alias: {\n '@rshono/routes$': routesFile,\n '@rshono/server-app$': serverAppAlias,\n // Split on '/' so a preset can declare a POSIX-looking path and still resolve on Windows.\n '@rshono/deploy$': join(FRAMEWORK_DIST, ...preset.runtimeModule.split('/')),\n '@': srcDir,\n },\n },\n module: {\n rules: [\n {\n test: (resource: string) => pageFiles.has(resource),\n enforce: 'pre',\n use: [{ loader: join(FRAMEWORK_DIST, 'builder', 'page-entry-loader.cjs') }],\n },\n {\n // Deliberately not scoped to `srcDir`: a `'use client'` component from `node_modules` is SSR'd in\n // the same layer, and scoping this to the app's own source left those rendering against the real\n // `process.env` while the browser bundle saw the `PUBLIC_`-only view — a hydration mismatch on\n // anything the host sets, and a leak for anything secret. The loader's own layer check is what\n // decides; every other module gets one `includes('process.env')` scan.\n test: /\\.[cm]?[tj]sx?$/,\n enforce: 'pre',\n use: [\n {\n loader: join(FRAMEWORK_DIST, 'builder', 'env-shadow-loader.cjs'),\n // `Object.create` over the real `process`, not a bare `{ env }`: the prelude shadows the whole\n // binding for that module, and `react-dom/server` is in this layer too — anything reading\n // `process.nextTick` or `process.platform` has to still find it. Not a `{ __proto__: … }`\n // literal, whose meaning would change if a minifier ever quoted the key.\n options: {\n prelude: `const process = Object.assign(Object.create(globalThis.process ?? Object.prototype), { env: ${JSON.stringify(publicEnv(isDev))} }); `,\n layer: Layers.ssr,\n },\n },\n ],\n },\n swcRule([...(preset.syntaxTargets ?? NODE_TARGETS)]),\n cssRule(),\n {\n test: /\\.(png|jpe?g|gif|webp|avif|ico|svg|woff2?|ttf|otf)$/i,\n type: 'asset',\n generator: { emit: false },\n },\n { resource: ssrEntry, layer: Layers.ssr },\n {\n resource: rscEntry,\n layer: Layers.rsc,\n resolve: { conditionNames: rscConditionNames },\n },\n {\n issuerLayer: Layers.rsc,\n exclude: ssrEntry,\n resolve: { conditionNames: rscConditionNames },\n },\n ],\n },\n plugins: [\n pageScanPlugin,\n new ServerPlugin({ onServerComponentChanges }),\n // Bakes rshono.config.ts into the bundle, read back as `__RSHONO_CONFIG__` at request time.\n new rspack.DefinePlugin({ __RSHONO_CONFIG__: JSON.stringify(resolveServerConfig(config, { isDev, outDir })) }),\n ],\n performance: false,\n };\n\n // The platform's overrides first, then the user's hook — so an app can adjust whatever a preset decided.\n preset.configureServer?.(serverConfig);\n\n const rspackHook = config.rspack;\n if (rspackHook) {\n return [\n rspackHook(clientConfig, { isServer: false, isDev }) ?? clientConfig,\n rspackHook(serverConfig, { isServer: true, isDev }) ?? serverConfig,\n ];\n }\n return [clientConfig, serverConfig];\n}\n"]}
|
|
1
|
+
{"version":3,"file":"rspack-config.js","sourceRoot":"","sources":["../../src/builder/rspack-config.ts"],"names":[],"mappings":"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","sourcesContent":["import { rspack, type Compiler, type RspackOptions, type RuleSetRule } from '@rspack/core';\nimport { ReactRefreshRspackPlugin } from '@rspack/plugin-react-refresh';\nimport { existsSync, readdirSync, readFileSync } from 'node:fs';\nimport { createRequire } from 'node:module';\nimport { basename, dirname, join, sep, win32 } from 'node:path';\nimport type { RshonoConfig } from '../config.js';\nimport type { DeployPreset } from '../deploy/presets.js';\nimport { resolveServerConfig } from '../server/server-config.js';\nimport { serverOnlyImportPlugin } from './server-only-imports.js';\nimport { scanPageFiles } from './page-files.js';\nimport { publicEnv } from './public-env.js';\nimport { checkReactVersions } from './react-versions.js';\n\n// This file lives in `dist/builder/`, so the entries and loaders below are the *built* framework.\nconst FRAMEWORK_DIST = join(import.meta.dirname, '..');\nconst FRAMEWORK_ROOT = join(FRAMEWORK_DIST, '..');\n\nconst BUNDLED_PACKAGES = /^(@rshono\\/core|react|react-dom|react-server-dom-rspack|hono|@hono\\/node-server)(\\/|$)/;\n\n/**\n * The directory a command writes its bundles to, relative to the project root — one per command, never\n * shared: a `rshono build` alongside a running `rshono dev` would `clean` away the route chunks that\n * server imports lazily, and Rspack's watch-mode emit never re-emits a file it believes it wrote.\n *\n * Both names are a single level under the root, which `deploy/filesystem.ts` relies on to derive the\n * project root from where the bundle ended up.\n */\nexport const BUILD_OUT_DIR = 'dist';\nexport const DEV_OUT_DIR = '.rshono';\n\n/**\n * Packages the app's source ends up importing without ever naming them: the RSC transform rewrites a\n * `'use client'` module, a page or an action into imports of the RSC runtime, resolved from the app's `src/`.\n */\nconst INJECTED_PACKAGES = ['react-server-dom-rspack'];\n\n/** The `node_modules` directory a resolved file sits in — `…/node_modules/pkg/index.js` → `…/node_modules`. */\nfunction enclosingNodeModules(file: string): string | undefined {\n let dir = dirname(file);\n while (basename(dir) !== 'node_modules') {\n const parent = dirname(dir);\n if (parent === dir) return undefined;\n dir = parent;\n }\n return dir;\n}\n\n/**\n * Where packages are resolved from: the app's own `node_modules`, then the framework's — which is what makes\n * {@link INJECTED_PACKAGES} resolvable from app source under pnpm, where the framework's dependencies are\n * installed as siblings of `@rshono/core` rather than hoisted into the app's tree.\n *\n * Asking Node where the framework resolves the package finds it in any layout, and a search path rather than\n * an alias keeps the package's own `exports` conditions in play — the RSC layer, the SSR layer and each\n * deploy target need a different build of it.\n */\nfunction resolveModules(): string[] {\n const require = createRequire(import.meta.url);\n const dirs = new Set<string>();\n for (const name of INJECTED_PACKAGES) {\n try {\n const dir = enclosingNodeModules(require.resolve(name));\n if (dir) dirs.add(dir);\n } catch {\n // Left to the resolver to report against the request that actually needed it.\n }\n }\n // A checkout or a vendored copy, where the framework has real dependencies of its own.\n dirs.add(join(FRAMEWORK_ROOT, 'node_modules'));\n return ['node_modules', ...dirs];\n}\n\n/**\n * Whether a request names a file rather than a package, and so belongs in the bundle.\n *\n * Rspack's RSC plugins ask for their proxies by absolute path, which on Windows carries a drive letter —\n * externalizing one emits an `import(\"D:\\\\…\")` Node rejects. `win32.isAbsolute` accepts the POSIX form too,\n * so it runs on every platform and the Windows shapes stay testable off Windows.\n */\nfunction isPathRequest(request: string): boolean {\n return request.startsWith('.') || win32.isAbsolute(request);\n}\n\nconst BROWSER_TARGETS = ['last 2 versions', '> 0.2%', 'not dead', 'Firefox ESR'];\nconst NODE_TARGETS = ['node >= 22'];\n\n/**\n * Rspack's native CSS pipeline, which both compilers get. It parses *finished* CSS, so a stylesheet needing\n * PostCSS — Tailwind, most obviously — adds that loader through the {@link RshonoConfig.rspack} hook, which\n * keeps `postcss` a dependency of the apps that want one.\n *\n * A fresh object per compiler, so an app changing this rule does not silently change the other bundle's.\n */\nfunction cssRule(): RuleSetRule {\n return { test: /\\.css$/i, type: 'css/auto' };\n}\n\nexport interface RspackConfigOptions {\n rootDir: string;\n isDev: boolean;\n /** The project's resolved config — supplies the {@link RshonoConfig.rspack} hook and the runtime settings baked into the server bundle. */\n config: RshonoConfig;\n /** The platform being built for: decides the `@rshono/deploy` runtime and any server-compiler overrides. */\n preset: DeployPreset;\n onServerComponentChanges?: () => void;\n}\n\n/** Source files worth reading for {@link mentions} — the app's own code, whatever dialect it is written in. */\nconst SOURCE_FILE = /\\.[cm]?[jt]sx?$/;\n\n/**\n * Whether `identifier` appears anywhere in the app's own source. The whole tree rather than `src/server.ts`\n * alone, so middleware registered from a helper module counts.\n *\n * A textual scan on purpose. What is being answered is \"did the author think about this at all\", which is a\n * question a build warning is allowed to get wrong: the failure mode is a warning that is unwarranted, never a\n * build that fails or a behaviour that changes. Resolving it properly would mean reading the module graph, and\n * a middleware registered from a package outside `src/` would still slip past.\n */\nfunction mentions(srcDir: string, identifier: string): boolean {\n try {\n for (const entry of readdirSync(srcDir, { recursive: true, withFileTypes: true })) {\n if (!entry.isFile() || !SOURCE_FILE.test(entry.name)) continue;\n if (readFileSync(join(entry.parentPath, entry.name), 'utf8').includes(identifier)) return true;\n }\n } catch {\n // An unreadable `src/` is not this function's to report: the build is about to fail on `routes.ts`.\n }\n return false;\n}\n\nexport function createConfigs(options: RspackConfigOptions): [RspackOptions, RspackOptions] {\n const { rootDir, isDev, config, preset, onServerComponentChanges } = options;\n const srcDir = join(rootDir, 'src');\n const mode = isDev ? 'development' : 'production';\n const outDir = isDev ? DEV_OUT_DIR : BUILD_OUT_DIR;\n\n // Before anything is compiled: a react/react-dom split fails inside React at render time, and this is the\n // one place that can say so with the two versions in hand.\n checkReactVersions(rootDir);\n\n const routesFile = ['routes.ts', 'routes.tsx'].map((f) => join(srcDir, f)).find(existsSync);\n if (!routesFile) {\n throw new Error(`[rshono] src/routes.ts not found in ${rootDir} — it is the one required file.`);\n }\n const serverAppFile = ['server.ts', 'server.tsx'].map((f) => join(srcDir, f)).find(existsSync);\n const serverAppAlias = serverAppFile ?? join(FRAMEWORK_DIST, 'runtime', 'empty-server-app.js');\n // Optional, and staying optional — but per-request security is Hono middleware registered there, so an app\n // without one has opted out of all of it, which is worth hearing once rather than discovering. Builds only:\n // `dev` would print it on every rebuild, and it is not news on a developer's machine.\n if (!isDev) {\n if (!serverAppFile) {\n console.warn(\n ' ⚠ No src/server.ts — this build has no CSRF check and no request body cap. Both are Hono middleware\\n' +\n ' (`csrf()`, `bodyLimit()`); `npx @rshono/create` scaffolds a src/server.ts with them registered.',\n );\n } else if (!mentions(srcDir, 'bodyLimit')) {\n // Having a src/server.ts is not the same as having the cap in it, and this half used to be warned about\n // nowhere: every `'use server'` export is a public POST endpoint by design, and the action path buffers\n // the whole body before it can decide anything about it.\n console.warn(\n \" ⚠ No bodyLimit() anywhere in src/ — this build has no request body cap. Every 'use server' export is\\n\" +\n ' a public POST endpoint, and the action path buffers whatever arrives before it can reject it.\\n' +\n ' Add `server.use(bodyLimit({ maxSize: 1024 * 1024 }))` to src/server.ts (from `hono/body-limit`).',\n );\n }\n }\n\n const rscEntry = join(FRAMEWORK_DIST, 'runtime', 'entry.rsc.js');\n const ssrEntry = join(FRAMEWORK_DIST, 'runtime', 'entry.ssr.js');\n const clientEntry = join(FRAMEWORK_DIST, 'runtime', 'entry.client.js');\n\n const swcRule = (targets: string[]): RuleSetRule => ({\n test: /\\.[cm]?[jt]sx?$/,\n exclude: /[\\\\/]core-js[\\\\/]/,\n use: {\n loader: 'builtin:swc-loader',\n options: {\n detectSyntax: 'auto',\n jsc: {\n transform: { react: { runtime: 'automatic', development: isDev } },\n experimental: { keepImportAttributes: true },\n },\n env: { targets },\n rspackExperiments: { reactServerComponents: true },\n },\n },\n type: 'javascript/auto',\n });\n\n const resolveBase = {\n extensions: ['.tsx', '.ts', '.jsx', '.js', '.json'],\n extensionAlias: { '.js': ['.ts', '.tsx', '.js'] },\n modules: resolveModules(),\n };\n\n // Ahead of whatever the Rspack target implies ('...'), which is what hands the server bundle the right\n // build of React and the RSC runtime. Unset for Node, whose target already implies the `node` condition.\n const runtimeConditions = preset.resolveConditions ?? [];\n const serverResolveBase = runtimeConditions.length > 0 ? { ...resolveBase, conditionNames: [...runtimeConditions, '...'] } : resolveBase;\n const rscConditionNames = ['react-server', ...runtimeConditions, '...'];\n\n const { ServerPlugin, ClientPlugin } = rspack.experiments.rsc.createPlugins();\n const { Layers } = rspack.experiments.rsc;\n\n const pageFiles = new Set<string>();\n scanPageFiles(routesFile, srcDir, pageFiles);\n const pageScanPlugin = {\n apply(compiler: Compiler) {\n const refresh = () => scanPageFiles(routesFile, srcDir, pageFiles);\n compiler.hooks.beforeRun.tap('rshono/page-scan', refresh);\n compiler.hooks.watchRun.tap('rshono/page-scan', refresh);\n },\n };\n\n const clientConfig: RspackOptions = {\n name: 'client',\n mode,\n target: 'web',\n context: rootDir,\n // Never in a build: a client map is served from `/_static` like everything else beside it, and it would\n // publish the original source of the app's own modules. Dev binds 127.0.0.1 only.\n devtool: isDev ? 'source-map' : false,\n entry: { main: clientEntry },\n output: {\n path: join(rootDir, outDir, 'static'),\n publicPath: '/_static/',\n clean: !isDev,\n filename: isDev ? 'chunks/main.js' : 'chunks/main.[contenthash].js',\n chunkFilename: isDev ? 'chunks/[name].js' : 'chunks/[name].[contenthash].js',\n cssFilename: isDev ? 'chunks/[name].css' : 'chunks/[name].[contenthash].css',\n cssChunkFilename: isDev ? 'chunks/[name].css' : 'chunks/[name].[contenthash].css',\n assetModuleFilename: 'assets/[name].[hash][ext]',\n },\n optimization: {\n moduleIds: isDev ? 'named' : 'hashed',\n },\n resolve: {\n ...resolveBase,\n alias: { '@': srcDir },\n },\n module: {\n rules: [swcRule(BROWSER_TARGETS), cssRule(), { test: /\\.(png|jpe?g|gif|webp|avif|ico|svg|woff2?|ttf|otf)$/i, type: 'asset' }],\n },\n plugins: [\n new ClientPlugin(),\n serverOnlyImportPlugin(rootDir),\n new rspack.DefinePlugin({ 'process.env': JSON.stringify(publicEnv(isDev)) }),\n ...(isDev ? [new rspack.HotModuleReplacementPlugin(), new ReactRefreshRspackPlugin()] : []),\n ],\n performance: false,\n };\n\n const serverConfig: RspackOptions = {\n name: 'server',\n mode,\n target: 'node',\n context: rootDir,\n // In a build too, unlike the client's: the bundle is minified, and without a map every stack trace that\n // reaches `onServerError` — the error-tracker funnel — is unmappable minified frames. A server map is\n // never served to anyone; `dist/server` is not on a public path, and the runtime enables Node's own\n // mapping so no host has to pass a flag.\n devtool: 'source-map',\n entry: { main: rscEntry },\n output: {\n path: join(rootDir, outDir, 'server'),\n // Off in dev: route chunks are imported lazily, so a superseded one on disk is harmless where deleting\n // one mid-request is not. `rshono dev` empties the directory on startup instead.\n clean: !isDev,\n module: true,\n chunkFormat: 'module',\n chunkLoading: 'import',\n library: { type: 'module' },\n filename: 'main.mjs',\n chunkFilename: 'chunks/[name].mjs',\n publicPath: '/_static/',\n assetModuleFilename: 'assets/[name].[hash][ext]',\n },\n optimization: {\n minimize: isDev ? false : true,\n },\n externalsType: 'module-import',\n externals: [\n ({ request, contextInfo }, callback) => {\n if (\n !request ||\n isPathRequest(request) ||\n request.startsWith('@/') ||\n request.startsWith('@rshono/') ||\n request.startsWith('builtin:') ||\n request.includes('!') ||\n BUNDLED_PACKAGES.test(request) ||\n // Anything reached from the SSR layer, which is where `'use client'` components are rendered on the\n // server. It has to be *compiled* rather than imported by name, because the env-shadow loader is what\n // keeps a secret out of SSR'd HTML and a loader cannot run on a module the bundle only names — an\n // externalized third-party client component is loaded raw at request time and reads the real\n // `process.env`. Widening the loader's rule to cover `node_modules` did not reach this: there was no\n // module for it to run on.\n //\n // Free of the usual bundling trade-off, too. Every module in this layer is in the *browser* bundle as\n // well, so it is already required to be bundleable — a native addon or a `__dirname` file read could\n // not have been a client component in the first place. The RSC layer, where server-only dependencies\n // live, keeps the externals policy.\n contextInfo?.issuerLayer === Layers.ssr\n ) {\n return callback();\n }\n callback(undefined, `module-import ${request}`);\n },\n ],\n resolve: {\n ...serverResolveBase,\n alias: {\n '@rshono/routes$': routesFile,\n '@rshono/server-app$': serverAppAlias,\n // Split on '/' so a preset can declare a POSIX-looking path and still resolve on Windows.\n '@rshono/deploy$': join(FRAMEWORK_DIST, ...preset.runtimeModule.split('/')),\n '@': srcDir,\n },\n },\n module: {\n rules: [\n {\n test: (resource: string) => pageFiles.has(resource),\n enforce: 'pre',\n use: [{ loader: join(FRAMEWORK_DIST, 'builder', 'page-entry-loader.cjs') }],\n },\n {\n // Deliberately not scoped to `srcDir`: a `'use client'` component from `node_modules` is SSR'd in\n // the same layer, and scoping this to the app's own source left those rendering against the real\n // `process.env` while the browser bundle saw the `PUBLIC_`-only view — a hydration mismatch on\n // anything the host sets, and a leak for anything secret. The loader's own layer check is what\n // decides; every other module gets one `includes('process')` scan.\n test: /\\.[cm]?[tj]sx?$/,\n enforce: 'pre',\n use: [\n {\n loader: join(FRAMEWORK_DIST, 'builder', 'env-shadow-loader.cjs'),\n // `Object.create` over the real `process`, not a bare `{ env }`: the prelude shadows the whole\n // binding for that module, and `react-dom/server` is in this layer too — anything reading\n // `process.nextTick` or `process.platform` has to still find it. Not a `{ __proto__: … }`\n // literal, whose meaning would change if a minifier ever quoted the key.\n options: {\n prelude: `const process = Object.assign(Object.create(globalThis.process ?? Object.prototype), { env: ${JSON.stringify(publicEnv(isDev))} }); `,\n layer: Layers.ssr,\n // Only the app's own source is warned about when it reaches `process` through the global\n // object, which no prelude can shadow — a library feature-detecting `globalThis.process` is\n // doing nothing wrong and has no app secret to read. The separator is part of the prefix so\n // a sibling directory named `src-old` is not mistaken for it.\n appSrcPrefix: srcDir + sep,\n },\n },\n ],\n },\n swcRule([...(preset.syntaxTargets ?? NODE_TARGETS)]),\n cssRule(),\n {\n test: /\\.(png|jpe?g|gif|webp|avif|ico|svg|woff2?|ttf|otf)$/i,\n type: 'asset',\n generator: { emit: false },\n },\n { resource: ssrEntry, layer: Layers.ssr },\n {\n resource: rscEntry,\n layer: Layers.rsc,\n resolve: { conditionNames: rscConditionNames },\n },\n {\n issuerLayer: Layers.rsc,\n exclude: ssrEntry,\n resolve: { conditionNames: rscConditionNames },\n },\n ],\n },\n plugins: [\n pageScanPlugin,\n new ServerPlugin({ onServerComponentChanges }),\n // Bakes rshono.config.ts into the bundle, read back as `__RSHONO_CONFIG__` at request time.\n new rspack.DefinePlugin({\n __RSHONO_CONFIG__: JSON.stringify(resolveServerConfig(config, { isDev, outDir, envBindings: preset.envBindings })),\n }),\n ],\n performance: false,\n };\n\n // The platform's overrides first, then the user's hook — so an app can adjust whatever a preset decided.\n preset.configureServer?.(serverConfig);\n\n const rspackHook = config.rspack;\n if (rspackHook) {\n return [\n rspackHook(clientConfig, { isServer: false, isDev }) ?? clientConfig,\n rspackHook(serverConfig, { isServer: true, isDev }) ?? serverConfig,\n ];\n }\n return [clientConfig, serverConfig];\n}\n"]}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The one import mistake the client compiler could not explain: `@rshono/core/server` reached from a
|
|
3
|
+
* `'use client'` module.
|
|
4
|
+
*
|
|
5
|
+
* The entry point is server-only by construction — `getRequestContext()` is an `AsyncLocalStorage` lookup —
|
|
6
|
+
* so the browser bundle cannot have it, and the build is right to fail. What it used to fail *with* was the
|
|
7
|
+
* resolver's own report of the first `node:` builtin three modules down:
|
|
8
|
+
*
|
|
9
|
+
* ```
|
|
10
|
+
* ERROR in node:async_hooks
|
|
11
|
+
* × Reading from "node:async_hooks" is not handled by plugins (Unhandled scheme).
|
|
12
|
+
* ```
|
|
13
|
+
*
|
|
14
|
+
* No file path, no issuer, and no mention of rshono or of the import that caused it. Everything else in the
|
|
15
|
+
* framework names the file to open; this was the one common mistake that did not, and the docs warn against it
|
|
16
|
+
* often enough to suggest people make it.
|
|
17
|
+
*
|
|
18
|
+
* So the request is caught before it resolves, where the *issuer* is still known, and reported as itself.
|
|
19
|
+
*/
|
|
20
|
+
import type { Compiler } from '@rspack/core';
|
|
21
|
+
/**
|
|
22
|
+
* Reports a server-only import by naming the module that made it.
|
|
23
|
+
*
|
|
24
|
+
* Two halves, because the resolver and the error list are reachable at different times. `beforeResolve` is
|
|
25
|
+
* where `contextInfo.issuer` is still on the request, and returning `false` there drops the dependency — which
|
|
26
|
+
* is what keeps the raw `node:` scheme error from being the build's only output. The error itself is pushed
|
|
27
|
+
* once the compilation exists to hold it, so it prints as an error against the compilation rather than as a
|
|
28
|
+
* throw out of a resolver hook.
|
|
29
|
+
*/
|
|
30
|
+
export declare function serverOnlyImportPlugin(rootDir: string): {
|
|
31
|
+
apply(compiler: Compiler): void;
|
|
32
|
+
};
|
|
33
|
+
//# sourceMappingURL=server-only-imports.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server-only-imports.d.ts","sourceRoot":"","sources":["../../src/builder/server-only-imports.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AACH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAY7C;;;;;;;;GAQG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,MAAM,GAAG;IAAE,KAAK,CAAC,QAAQ,EAAE,QAAQ,GAAG,IAAI,CAAA;CAAE,CAmC3F"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { relative } from 'node:path';
|
|
2
|
+
/** Requests that exist only on the server, and what a browser module should reach for instead. */
|
|
3
|
+
const SERVER_ONLY = {
|
|
4
|
+
'@rshono/core/server': [
|
|
5
|
+
'It is server-only by construction: `getRequestContext()` reads an AsyncLocalStorage store that exists only while a request is being handled.',
|
|
6
|
+
"In a 'use client' component, read the URL and params with `useNavigation()` from '@rshono/core/client', navigate with the `router` it returns, and take anything else as a prop from the server component that renders this one.",
|
|
7
|
+
"If this module was not meant to be a client component, the 'use client' directive at the top of it — or of a module it imports — is what put it in the browser bundle.",
|
|
8
|
+
].join('\n'),
|
|
9
|
+
};
|
|
10
|
+
/**
|
|
11
|
+
* Reports a server-only import by naming the module that made it.
|
|
12
|
+
*
|
|
13
|
+
* Two halves, because the resolver and the error list are reachable at different times. `beforeResolve` is
|
|
14
|
+
* where `contextInfo.issuer` is still on the request, and returning `false` there drops the dependency — which
|
|
15
|
+
* is what keeps the raw `node:` scheme error from being the build's only output. The error itself is pushed
|
|
16
|
+
* once the compilation exists to hold it, so it prints as an error against the compilation rather than as a
|
|
17
|
+
* throw out of a resolver hook.
|
|
18
|
+
*/
|
|
19
|
+
export function serverOnlyImportPlugin(rootDir) {
|
|
20
|
+
const NAME = 'rshono/server-only-imports';
|
|
21
|
+
return {
|
|
22
|
+
apply(compiler) {
|
|
23
|
+
/** Issuer → the request it made. A Map, so one module importing the same thing twice is reported once. */
|
|
24
|
+
const offenders = new Map();
|
|
25
|
+
compiler.hooks.normalModuleFactory.tap(NAME, (factory) => {
|
|
26
|
+
factory.hooks.beforeResolve.tap(NAME, (data) => {
|
|
27
|
+
// `hasOwn`, not `in`: a module named `toString` would otherwise inherit a match from Object.prototype.
|
|
28
|
+
if (!Object.hasOwn(SERVER_ONLY, data.request))
|
|
29
|
+
return;
|
|
30
|
+
offenders.set(data.contextInfo?.issuer ?? '', data.request);
|
|
31
|
+
// Dropped rather than left to fail: the resolver's report would name a `node:` builtin the author
|
|
32
|
+
// never wrote, and it would arrive beside this one.
|
|
33
|
+
return false;
|
|
34
|
+
});
|
|
35
|
+
});
|
|
36
|
+
// Cleared per compilation, so a watch rebuild that fixed the import stops reporting it.
|
|
37
|
+
compiler.hooks.thisCompilation.tap(NAME, () => offenders.clear());
|
|
38
|
+
compiler.hooks.afterCompile.tap(NAME, (compilation) => {
|
|
39
|
+
for (const [issuer, request] of offenders) {
|
|
40
|
+
const where = issuer ? relative(rootDir, issuer) : 'a module in the browser bundle';
|
|
41
|
+
// `file` is what Rspack puts on its own "ERROR in …" line, so the path lands where a reader looks
|
|
42
|
+
// for one instead of being the first words of the message.
|
|
43
|
+
compilation.errors.push(Object.assign(new Error(`[rshono] ${where} imports '${request}', which the browser bundle cannot have.\n${SERVER_ONLY[request]}`), {
|
|
44
|
+
file: where,
|
|
45
|
+
}));
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
},
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=server-only-imports.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server-only-imports.js","sourceRoot":"","sources":["../../src/builder/server-only-imports.ts"],"names":[],"mappings":"AAoBA,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAErC,kGAAkG;AAClG,MAAM,WAAW,GAA2B;IAC1C,qBAAqB,EAAE;QACrB,8IAA8I;QAC9I,kOAAkO;QAClO,wKAAwK;KACzK,CAAC,IAAI,CAAC,IAAI,CAAC;CACb,CAAC;AAEF;;;;;;;;GAQG;AACH,MAAM,UAAU,sBAAsB,CAAC,OAAe;IACpD,MAAM,IAAI,GAAG,4BAA4B,CAAC;IAC1C,OAAO;QACL,KAAK,CAAC,QAAkB;YACtB,0GAA0G;YAC1G,MAAM,SAAS,GAAG,IAAI,GAAG,EAAkB,CAAC;YAE5C,QAAQ,CAAC,KAAK,CAAC,mBAAmB,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,OAAO,EAAE,EAAE;gBACvD,OAAO,CAAC,KAAK,CAAC,aAAa,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,EAAE;oBAC7C,uGAAuG;oBACvG,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,WAAW,EAAE,IAAI,CAAC,OAAO,CAAC;wBAAE,OAAO;oBACtD,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,WAAW,EAAE,MAAM,IAAI,EAAE,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;oBAC5D,kGAAkG;oBAClG,oDAAoD;oBACpD,OAAO,KAAK,CAAC;gBACf,CAAC,CAAC,CAAC;YACL,CAAC,CAAC,CAAC;YAEH,wFAAwF;YACxF,QAAQ,CAAC,KAAK,CAAC,eAAe,CAAC,GAAG,CAAC,IAAI,EAAE,GAAG,EAAE,CAAC,SAAS,CAAC,KAAK,EAAE,CAAC,CAAC;YAElE,QAAQ,CAAC,KAAK,CAAC,YAAY,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,EAAE;gBACpD,KAAK,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,IAAI,SAAS,EAAE,CAAC;oBAC1C,MAAM,KAAK,GAAG,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,gCAAgC,CAAC;oBACpF,kGAAkG;oBAClG,2DAA2D;oBAC3D,WAAW,CAAC,MAAM,CAAC,IAAI,CACrB,MAAM,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,YAAY,KAAK,aAAa,OAAO,6CAA6C,WAAW,CAAC,OAAO,CAAC,EAAE,CAAC,EAAE;wBACjI,IAAI,EAAE,KAAK;qBACZ,CAAC,CACH,CAAC;gBACJ,CAAC;YACH,CAAC,CAAC,CAAC;QACL,CAAC;KACF,CAAC;AACJ,CAAC","sourcesContent":["/**\n * The one import mistake the client compiler could not explain: `@rshono/core/server` reached from a\n * `'use client'` module.\n *\n * The entry point is server-only by construction — `getRequestContext()` is an `AsyncLocalStorage` lookup —\n * so the browser bundle cannot have it, and the build is right to fail. What it used to fail *with* was the\n * resolver's own report of the first `node:` builtin three modules down:\n *\n * ```\n * ERROR in node:async_hooks\n * × Reading from \"node:async_hooks\" is not handled by plugins (Unhandled scheme).\n * ```\n *\n * No file path, no issuer, and no mention of rshono or of the import that caused it. Everything else in the\n * framework names the file to open; this was the one common mistake that did not, and the docs warn against it\n * often enough to suggest people make it.\n *\n * So the request is caught before it resolves, where the *issuer* is still known, and reported as itself.\n */\nimport type { Compiler } from '@rspack/core';\nimport { relative } from 'node:path';\n\n/** Requests that exist only on the server, and what a browser module should reach for instead. */\nconst SERVER_ONLY: Record<string, string> = {\n '@rshono/core/server': [\n 'It is server-only by construction: `getRequestContext()` reads an AsyncLocalStorage store that exists only while a request is being handled.',\n \"In a 'use client' component, read the URL and params with `useNavigation()` from '@rshono/core/client', navigate with the `router` it returns, and take anything else as a prop from the server component that renders this one.\",\n \"If this module was not meant to be a client component, the 'use client' directive at the top of it — or of a module it imports — is what put it in the browser bundle.\",\n ].join('\\n'),\n};\n\n/**\n * Reports a server-only import by naming the module that made it.\n *\n * Two halves, because the resolver and the error list are reachable at different times. `beforeResolve` is\n * where `contextInfo.issuer` is still on the request, and returning `false` there drops the dependency — which\n * is what keeps the raw `node:` scheme error from being the build's only output. The error itself is pushed\n * once the compilation exists to hold it, so it prints as an error against the compilation rather than as a\n * throw out of a resolver hook.\n */\nexport function serverOnlyImportPlugin(rootDir: string): { apply(compiler: Compiler): void } {\n const NAME = 'rshono/server-only-imports';\n return {\n apply(compiler: Compiler): void {\n /** Issuer → the request it made. A Map, so one module importing the same thing twice is reported once. */\n const offenders = new Map<string, string>();\n\n compiler.hooks.normalModuleFactory.tap(NAME, (factory) => {\n factory.hooks.beforeResolve.tap(NAME, (data) => {\n // `hasOwn`, not `in`: a module named `toString` would otherwise inherit a match from Object.prototype.\n if (!Object.hasOwn(SERVER_ONLY, data.request)) return;\n offenders.set(data.contextInfo?.issuer ?? '', data.request);\n // Dropped rather than left to fail: the resolver's report would name a `node:` builtin the author\n // never wrote, and it would arrive beside this one.\n return false;\n });\n });\n\n // Cleared per compilation, so a watch rebuild that fixed the import stops reporting it.\n compiler.hooks.thisCompilation.tap(NAME, () => offenders.clear());\n\n compiler.hooks.afterCompile.tap(NAME, (compilation) => {\n for (const [issuer, request] of offenders) {\n const where = issuer ? relative(rootDir, issuer) : 'a module in the browser bundle';\n // `file` is what Rspack puts on its own \"ERROR in …\" line, so the path lands where a reader looks\n // for one instead of being the first words of the message.\n compilation.errors.push(\n Object.assign(new Error(`[rshono] ${where} imports '${request}', which the browser bundle cannot have.\\n${SERVER_ONLY[request]}`), {\n file: where,\n }),\n );\n }\n });\n },\n };\n}\n"]}
|
package/dist/cli/build.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"build.d.ts","sourceRoot":"","sources":["../../src/cli/build.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAEjD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAIzD,UAAU,YAAY;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,YAAY,CAAC;IACrB,iGAAiG;IACjG,MAAM,EAAE,YAAY,CAAC;CACtB;
|
|
1
|
+
{"version":3,"file":"build.d.ts","sourceRoot":"","sources":["../../src/cli/build.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAEjD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAIzD,UAAU,YAAY;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,YAAY,CAAC;IACrB,iGAAiG;IACjG,MAAM,EAAE,YAAY,CAAC;CACtB;AAiCD,wBAAsB,YAAY,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CA2EvE"}
|
package/dist/cli/build.js
CHANGED
|
@@ -6,6 +6,29 @@ import { pathToFileURL } from 'node:url';
|
|
|
6
6
|
import { BUILD_OUT_DIR, createConfigs } from '../builder/rspack-config.js';
|
|
7
7
|
import { writeBuildMarker } from '../deploy/build-marker.js';
|
|
8
8
|
import { prerenderStaticRoutes } from '../server/ssg.js';
|
|
9
|
+
/**
|
|
10
|
+
* Runs one phase of the build that the app itself can fail.
|
|
11
|
+
*
|
|
12
|
+
* A `[rshono]` message was written for whoever is running the build, so it is printed as the one line it is;
|
|
13
|
+
* anything else is a bug in the framework and keeps its stack. Everything the app can get wrong arrives this
|
|
14
|
+
* way — the two entry modules, checked in the bundle's module scope; each route's own module; and a page that
|
|
15
|
+
* throws while prerendering.
|
|
16
|
+
*/
|
|
17
|
+
async function phase(run) {
|
|
18
|
+
try {
|
|
19
|
+
return await run();
|
|
20
|
+
}
|
|
21
|
+
catch (error) {
|
|
22
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
23
|
+
if (!message.startsWith('[rshono]'))
|
|
24
|
+
throw error;
|
|
25
|
+
console.error(`\n ✗ ${message}\n`);
|
|
26
|
+
process.exit(1);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
function importServerBundle(distDir) {
|
|
30
|
+
return import(pathToFileURL(join(distDir, 'server', 'main.mjs')).href);
|
|
31
|
+
}
|
|
9
32
|
export async function buildCommand(options) {
|
|
10
33
|
const { rootDir, config, preset } = options;
|
|
11
34
|
const distDir = join(rootDir, BUILD_OUT_DIR);
|
|
@@ -26,6 +49,10 @@ export async function buildCommand(options) {
|
|
|
26
49
|
console.error(stats.toString({ preset: 'errors-warnings', colors: true }));
|
|
27
50
|
process.exit(1);
|
|
28
51
|
}
|
|
52
|
+
// Printed rather than left to the summary below, which counts warnings without saying what they are —
|
|
53
|
+
// and one of them is the env shadow reporting a read it cannot cover. See `env-shadow-loader.cjs`.
|
|
54
|
+
if (stats.hasWarnings())
|
|
55
|
+
console.warn(stats.toString({ preset: 'errors-warnings', colors: true }));
|
|
29
56
|
console.log(stats.toString({ preset: 'summary', colors: true }));
|
|
30
57
|
const publicDir = join(rootDir, 'public');
|
|
31
58
|
let distPublicDir = null;
|
|
@@ -37,13 +64,18 @@ export async function buildCommand(options) {
|
|
|
37
64
|
const ssgDir = join(distDir, 'ssg');
|
|
38
65
|
await rm(ssgDir, { recursive: true, force: true });
|
|
39
66
|
process.env.RSHONO_PRERENDER = '1';
|
|
40
|
-
|
|
41
|
-
const
|
|
67
|
+
// The bundle's module scope is where `src/routes.ts` and `src/server.ts` are validated.
|
|
68
|
+
const bundle = await phase(() => importServerBundle(distDir));
|
|
69
|
+
// Before anything is rendered: the checks here are the ones a route fails on *every* request, so a build
|
|
70
|
+
// that skipped them would spend the prerender pass on a route it is about to refuse anyway. Without this
|
|
71
|
+
// they ran on first request instead, which meant a page that could never work shipped behind a green build.
|
|
72
|
+
await phase(() => bundle.checkRouteModules());
|
|
73
|
+
const { written, skipped } = await phase(() => prerenderStaticRoutes({
|
|
42
74
|
routes: bundle.routes,
|
|
43
75
|
fetch: (request) => bundle.app.fetch(request),
|
|
44
76
|
ssgDir,
|
|
45
77
|
siteUrl: config.siteUrl,
|
|
46
|
-
});
|
|
78
|
+
}));
|
|
47
79
|
if (written.length > 0)
|
|
48
80
|
console.log(` • prerendered ${written.length} static page(s): ${written.join(', ')}`);
|
|
49
81
|
if (skipped.length > 0)
|
package/dist/cli/build.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"build.js","sourceRoot":"","sources":["../../src/cli/build.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAmB,MAAM,cAAc,CAAC;AAEvD,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAC7C,OAAO,EAAE,EAAE,EAAE,MAAM,kBAAkB,CAAC;AACtC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAC;AAE3E,OAAO,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAG7D,OAAO,EAAE,qBAAqB,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"build.js","sourceRoot":"","sources":["../../src/cli/build.ts"],"names":[],"mappings":"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","sourcesContent":["import { rspack, type MultiStats } from '@rspack/core';\nimport type { Hono } from 'hono';\nimport { cpSync, existsSync } from 'node:fs';\nimport { rm } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport { pathToFileURL } from 'node:url';\nimport { BUILD_OUT_DIR, createConfigs } from '../builder/rspack-config.js';\nimport type { RshonoConfig } from '../config.js';\nimport { writeBuildMarker } from '../deploy/build-marker.js';\nimport type { DeployPreset } from '../deploy/presets.js';\nimport type { Route } from '../router.js';\nimport { prerenderStaticRoutes } from '../server/ssg.js';\n\ninterface BuildOptions {\n rootDir: string;\n config: RshonoConfig;\n /** The platform to build for, already resolved from the flag, the environment and the config. */\n preset: DeployPreset;\n}\n\n/** What `rshono build` uses the app's own server bundle for, once it has compiled it. */\ninterface ServerBundle {\n app: Hono;\n routes: readonly Route[];\n /** Resolves every route's own module and checks it — see `assertRouteModules`. */\n checkRouteModules: () => Promise<void>;\n}\n\n/**\n * Runs one phase of the build that the app itself can fail.\n *\n * A `[rshono]` message was written for whoever is running the build, so it is printed as the one line it is;\n * anything else is a bug in the framework and keeps its stack. Everything the app can get wrong arrives this\n * way — the two entry modules, checked in the bundle's module scope; each route's own module; and a page that\n * throws while prerendering.\n */\nasync function phase<T>(run: () => Promise<T>): Promise<T> {\n try {\n return await run();\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n if (!message.startsWith('[rshono]')) throw error;\n console.error(`\\n ✗ ${message}\\n`);\n process.exit(1);\n }\n}\n\nfunction importServerBundle(distDir: string): Promise<ServerBundle> {\n return import(pathToFileURL(join(distDir, 'server', 'main.mjs')).href) as Promise<ServerBundle>;\n}\n\nexport async function buildCommand(options: BuildOptions): Promise<void> {\n const { rootDir, config, preset } = options;\n const distDir = join(rootDir, BUILD_OUT_DIR);\n\n console.log(' • building client + server bundles…');\n const configs = createConfigs({ rootDir, isDev: false, config, preset });\n const compiler = rspack(configs);\n\n const stats = await new Promise<MultiStats>((resolve, reject) => {\n compiler.run((err, result) => {\n compiler.close(() => {\n if (err) reject(err);\n else resolve(result!);\n });\n });\n });\n\n if (stats.hasErrors()) {\n console.error(stats.toString({ preset: 'errors-warnings', colors: true }));\n process.exit(1);\n }\n // Printed rather than left to the summary below, which counts warnings without saying what they are —\n // and one of them is the env shadow reporting a read it cannot cover. See `env-shadow-loader.cjs`.\n if (stats.hasWarnings()) console.warn(stats.toString({ preset: 'errors-warnings', colors: true }));\n console.log(stats.toString({ preset: 'summary', colors: true }));\n\n const publicDir = join(rootDir, 'public');\n let distPublicDir: string | null = null;\n if (existsSync(publicDir)) {\n distPublicDir = join(distDir, 'public');\n cpSync(publicDir, distPublicDir, { recursive: true });\n console.log(' • copied public/ into dist/public (served at /)');\n }\n\n const ssgDir = join(distDir, 'ssg');\n await rm(ssgDir, { recursive: true, force: true });\n process.env.RSHONO_PRERENDER = '1';\n // The bundle's module scope is where `src/routes.ts` and `src/server.ts` are validated.\n const bundle = await phase(() => importServerBundle(distDir));\n\n // Before anything is rendered: the checks here are the ones a route fails on *every* request, so a build\n // that skipped them would spend the prerender pass on a route it is about to refuse anyway. Without this\n // they ran on first request instead, which meant a page that could never work shipped behind a green build.\n await phase(() => bundle.checkRouteModules());\n\n const { written, skipped } = await phase(() =>\n prerenderStaticRoutes({\n routes: bundle.routes,\n fetch: (request) => bundle.app.fetch(request),\n ssgDir,\n siteUrl: config.siteUrl,\n }),\n );\n if (written.length > 0) console.log(` • prerendered ${written.length} static page(s): ${written.join(', ')}`);\n if (skipped.length > 0) console.log(` • skipped ${skipped.length} (will SSR per request)`);\n\n // Before `finalize`, so a preset that copies `dist/` into a platform layout takes it along.\n writeBuildMarker(distDir, preset.name);\n\n // Last, so a preset arranging its output finds every piece of the build on disk.\n await preset.finalize?.({\n rootDir,\n distDir,\n staticDir: join(distDir, 'static'),\n publicDir: distPublicDir,\n ssgDir,\n });\n\n console.log(` ✓ build complete — ${preset.deployHint}`);\n\n // Rspack's worker pool can keep the loop alive after `close()`, so the exit is explicit — but a piped\n // stdout is asynchronous, and exiting drops whatever has not drained. In CI that is exactly the lines\n // saying what was built and where it went. A zero-length write's callback fires behind the real ones.\n await new Promise<void>((resolve) => process.stdout.write('', () => resolve()));\n process.exit(0);\n}\n"]}
|
package/dist/cli/dev.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dev.d.ts","sourceRoot":"","sources":["../../src/cli/dev.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"dev.d.ts","sourceRoot":"","sources":["../../src/cli/dev.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAoEjD,UAAU,UAAU;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,YAAY,CAAC;CACtB;AAED,wBAAsB,UAAU,CAAC,OAAO,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAsPnE"}
|