@rshono/core 1.0.0-rc.19 → 1.0.0-rc.20

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.
Files changed (151) hide show
  1. package/README.md +93 -2
  2. package/dist/builder/env-shadow-loader.cjs +41 -1
  3. package/dist/builder/page-entry-loader.cjs +28 -1
  4. package/dist/builder/page-files.d.ts +0 -1
  5. package/dist/builder/public-env.d.ts +0 -1
  6. package/dist/builder/react-versions.d.ts +0 -1
  7. package/dist/builder/rspack-config.d.ts +0 -1
  8. package/dist/builder/rspack-config.js +4 -0
  9. package/dist/builder/rspack-config.js.map +1 -1
  10. package/dist/builder/server-only-imports.d.ts +0 -1
  11. package/dist/cli/build.d.ts +0 -1
  12. package/dist/cli/build.js +37 -35
  13. package/dist/cli/build.js.map +1 -1
  14. package/dist/cli/dev.d.ts +0 -1
  15. package/dist/cli/dev.js +15 -1
  16. package/dist/cli/dev.js.map +1 -1
  17. package/dist/cli/exit.d.ts +12 -0
  18. package/dist/cli/exit.js +16 -0
  19. package/dist/cli/exit.js.map +1 -0
  20. package/dist/cli/index.d.ts +0 -1
  21. package/dist/cli/index.js +74 -20
  22. package/dist/cli/index.js.map +1 -1
  23. package/dist/cli/start.d.ts +0 -1
  24. package/dist/cli/start.js +3 -2
  25. package/dist/cli/start.js.map +1 -1
  26. package/dist/config.d.ts +0 -1
  27. package/dist/deploy/aws-lambda/runtime.d.ts +0 -1
  28. package/dist/deploy/build-marker.d.ts +7 -2
  29. package/dist/deploy/build-marker.js +6 -0
  30. package/dist/deploy/build-marker.js.map +1 -1
  31. package/dist/deploy/cloudflare/build.d.ts +0 -1
  32. package/dist/deploy/cloudflare/build.js +6 -0
  33. package/dist/deploy/cloudflare/build.js.map +1 -1
  34. package/dist/deploy/cloudflare/runtime.d.ts +0 -1
  35. package/dist/deploy/cloudflare/runtime.js +24 -2
  36. package/dist/deploy/cloudflare/runtime.js.map +1 -1
  37. package/dist/deploy/contract.d.ts +20 -2
  38. package/dist/deploy/contract.js.map +1 -1
  39. package/dist/deploy/filesystem.d.ts +0 -1
  40. package/dist/deploy/node/runtime.d.ts +0 -1
  41. package/dist/deploy/presets.d.ts +7 -1
  42. package/dist/deploy/presets.js +13 -2
  43. package/dist/deploy/presets.js.map +1 -1
  44. package/dist/deploy/public-paths.d.ts +42 -0
  45. package/dist/deploy/public-paths.js +66 -0
  46. package/dist/deploy/public-paths.js.map +1 -0
  47. package/dist/deploy/vercel/build.d.ts +0 -1
  48. package/dist/deploy/vercel/build.js +6 -0
  49. package/dist/deploy/vercel/build.js.map +1 -1
  50. package/dist/deploy/vercel/runtime.d.ts +0 -1
  51. package/dist/index.d.ts +0 -1
  52. package/dist/router.d.ts +20 -5
  53. package/dist/router.js.map +1 -1
  54. package/dist/runtime/boundaries.d.ts +0 -1
  55. package/dist/runtime/client.d.ts +0 -1
  56. package/dist/runtime/context.d.ts +15 -2
  57. package/dist/runtime/context.js +15 -1
  58. package/dist/runtime/context.js.map +1 -1
  59. package/dist/runtime/control.d.ts +15 -1
  60. package/dist/runtime/control.js +17 -0
  61. package/dist/runtime/control.js.map +1 -1
  62. package/dist/runtime/dev-protocol.d.ts +0 -1
  63. package/dist/runtime/empty-server-app.d.ts +0 -1
  64. package/dist/runtime/entry.client.d.ts +0 -1
  65. package/dist/runtime/entry.client.js +31 -4
  66. package/dist/runtime/entry.client.js.map +1 -1
  67. package/dist/runtime/entry.rsc.d.ts +6 -4
  68. package/dist/runtime/entry.rsc.js +473 -56
  69. package/dist/runtime/entry.rsc.js.map +1 -1
  70. package/dist/runtime/entry.ssr.d.ts +19 -5
  71. package/dist/runtime/entry.ssr.js +22 -47
  72. package/dist/runtime/entry.ssr.js.map +1 -1
  73. package/dist/runtime/failure-document.d.ts +11 -0
  74. package/dist/runtime/failure-document.js +35 -0
  75. package/dist/runtime/failure-document.js.map +1 -0
  76. package/dist/runtime/flight-inject.d.ts +0 -1
  77. package/dist/runtime/flight-inject.js +15 -9
  78. package/dist/runtime/flight-inject.js.map +1 -1
  79. package/dist/runtime/hot-update.d.ts +0 -1
  80. package/dist/runtime/navigation.d.ts +6 -1
  81. package/dist/runtime/navigation.js +6 -0
  82. package/dist/runtime/navigation.js.map +1 -1
  83. package/dist/runtime/request.d.ts +23 -1
  84. package/dist/runtime/request.js +39 -0
  85. package/dist/runtime/request.js.map +1 -1
  86. package/dist/runtime/server.d.ts +0 -1
  87. package/dist/runtime/validate-entries.d.ts +0 -1
  88. package/dist/runtime/validate-entries.js +114 -10
  89. package/dist/runtime/validate-entries.js.map +1 -1
  90. package/dist/server/headers.d.ts +37 -4
  91. package/dist/server/headers.js +49 -13
  92. package/dist/server/headers.js.map +1 -1
  93. package/dist/server/load-config.d.ts +0 -1
  94. package/dist/server/load-env.d.ts +0 -1
  95. package/dist/server/prerendered.d.ts +14 -2
  96. package/dist/server/prerendered.js +42 -4
  97. package/dist/server/prerendered.js.map +1 -1
  98. package/dist/server/server-config.d.ts +0 -1
  99. package/dist/server/shutdown.d.ts +0 -1
  100. package/dist/server/ssg.d.ts +6 -1
  101. package/dist/server/ssg.js +30 -7
  102. package/dist/server/ssg.js.map +1 -1
  103. package/dist/server/static.d.ts +0 -1
  104. package/dist/server/static.js +5 -5
  105. package/dist/server/static.js.map +1 -1
  106. package/package.json +1 -1
  107. package/dist/builder/page-files.d.ts.map +0 -1
  108. package/dist/builder/public-env.d.ts.map +0 -1
  109. package/dist/builder/react-versions.d.ts.map +0 -1
  110. package/dist/builder/rspack-config.d.ts.map +0 -1
  111. package/dist/builder/server-only-imports.d.ts.map +0 -1
  112. package/dist/cli/build.d.ts.map +0 -1
  113. package/dist/cli/dev.d.ts.map +0 -1
  114. package/dist/cli/index.d.ts.map +0 -1
  115. package/dist/cli/start.d.ts.map +0 -1
  116. package/dist/config.d.ts.map +0 -1
  117. package/dist/deploy/aws-lambda/runtime.d.ts.map +0 -1
  118. package/dist/deploy/build-marker.d.ts.map +0 -1
  119. package/dist/deploy/cloudflare/build.d.ts.map +0 -1
  120. package/dist/deploy/cloudflare/runtime.d.ts.map +0 -1
  121. package/dist/deploy/contract.d.ts.map +0 -1
  122. package/dist/deploy/filesystem.d.ts.map +0 -1
  123. package/dist/deploy/node/runtime.d.ts.map +0 -1
  124. package/dist/deploy/presets.d.ts.map +0 -1
  125. package/dist/deploy/vercel/build.d.ts.map +0 -1
  126. package/dist/deploy/vercel/runtime.d.ts.map +0 -1
  127. package/dist/index.d.ts.map +0 -1
  128. package/dist/router.d.ts.map +0 -1
  129. package/dist/runtime/boundaries.d.ts.map +0 -1
  130. package/dist/runtime/client.d.ts.map +0 -1
  131. package/dist/runtime/context.d.ts.map +0 -1
  132. package/dist/runtime/control.d.ts.map +0 -1
  133. package/dist/runtime/dev-protocol.d.ts.map +0 -1
  134. package/dist/runtime/empty-server-app.d.ts.map +0 -1
  135. package/dist/runtime/entry.client.d.ts.map +0 -1
  136. package/dist/runtime/entry.rsc.d.ts.map +0 -1
  137. package/dist/runtime/entry.ssr.d.ts.map +0 -1
  138. package/dist/runtime/flight-inject.d.ts.map +0 -1
  139. package/dist/runtime/hot-update.d.ts.map +0 -1
  140. package/dist/runtime/navigation.d.ts.map +0 -1
  141. package/dist/runtime/request.d.ts.map +0 -1
  142. package/dist/runtime/server.d.ts.map +0 -1
  143. package/dist/runtime/validate-entries.d.ts.map +0 -1
  144. package/dist/server/headers.d.ts.map +0 -1
  145. package/dist/server/load-config.d.ts.map +0 -1
  146. package/dist/server/load-env.d.ts.map +0 -1
  147. package/dist/server/prerendered.d.ts.map +0 -1
  148. package/dist/server/server-config.d.ts.map +0 -1
  149. package/dist/server/shutdown.d.ts.map +0 -1
  150. package/dist/server/ssg.d.ts.map +0 -1
  151. package/dist/server/static.d.ts.map +0 -1
package/README.md CHANGED
@@ -35,7 +35,9 @@ rshono build # production build: client + server bundles + prerendered pages
35
35
  rshono start # run the production build
36
36
  ```
37
37
 
38
- `--port` / `PORT` and `HOST` set where it listens; `--config <path>` points `build` at another config file.
38
+ `--port` / `PORT` sets the port for `dev` and `start`. **`HOST` applies to `start` only** `dev` always binds
39
+ `127.0.0.1`, because its source maps embed the original source of your `'use server'` modules, and it warns if
40
+ you set one. `--config <path>` points `build` at another config file.
39
41
 
40
42
  ## Project layout
41
43
 
@@ -132,6 +134,12 @@ carries a fresh page payload, so server-rendered UI updates after a mutation.
132
134
  action. [Server actions docs](https://www.rshono.com/docs/pages#server-actions), and
133
135
  [how to use them](https://www.rshono.com/docs/usage).
134
136
 
137
+ Rspack compiles an app's whole `'use server'` graph into **one** server module, and calling any action loads
138
+ it — so a module that throws while it evaluates takes every action in the app with it, not just its own.
139
+ Nothing on the server imports these modules until an action is called, which used to make that a green build
140
+ followed by a 500 on the first click. `rshono build` now loads them and warns if one will not, and at run
141
+ time a failure to load is reported as an action fault, not answered as a bad request.
142
+
135
143
  ## Full Hono underneath
136
144
 
137
145
  - `{ type: 'endpoint' }` routes export a Hono `handler` from a server module.
@@ -212,7 +220,9 @@ server.use(secureHeaders({ contentSecurityPolicy: { scriptSrc: ["'self'", NONCE]
212
220
 
213
221
  `create-rshono` scaffolds the first two. Under `rshono dev` the framework widens `script-src` with
214
222
  `'unsafe-eval'` for React Refresh — never in a build — so one policy serves both, and a route with a
215
- nonce in play falls back to rendering per request. Everything else Hono ships works the same way:
223
+ nonce in play falls back to rendering its _document_ per request: fixed bytes cannot carry a fresh nonce.
224
+ Its flight payload has no nonce to go stale and is still served from disk, so `rshono build` marks such a
225
+ page `(flight only)` rather than counting it as fully prerendered. Everything else Hono ships works the same way:
216
226
  `cors`, `basicAuth`, `jwt`, `timeout`, `requestId`, `ipRestriction`. A middleware that rejects by
217
227
  throwing an `HTTPException` keeps its own status rather than becoming the 500 page.
218
228
  [Middleware docs](https://www.rshono.com/docs/hono#security-middleware) · [Hono](https://hono.dev/docs).
@@ -265,9 +275,21 @@ Every target streams, which is the bar a new one has to clear.
265
275
  - **Streaming is the fragile part of a serverless target, and it fails silently** — `supportsResponseStreaming`
266
276
  on Vercel, `streamifyResponse` plus a `RESPONSE_STREAM` Function URL on Lambda. Getting those right is what
267
277
  the presets are for.
278
+ - **Most of what `vercel` and `aws-lambda` upload is the source map.** `dist/server/main.mjs.map` is roughly
279
+ three quarters of `dist/server`, and both targets take the whole directory — `vercel` copies it into the
280
+ function, the `aws-lambda` handoff is "zip `dist/`". That is deliberate: the map is what turns the
281
+ `onServerError` funnel from minified frames into real ones, and it is never served to anyone. But it is
282
+ upload weight rather than cold-start weight — nothing parses it unless a stack trace is being mapped — so
283
+ delete it from the package if size matters more to you than readable production traces.
268
284
  - **Prerendered pages are never CDN-served**: one URL answers with a document or a flight payload depending on
269
285
  the `RSC` request header, and a path-keyed CDN cannot choose. `/_static` and `public/` do go straight to the
270
286
  CDN.
287
+ - **`public/` beats a route on `cloudflare` and `vercel`, and loses to it on `node` and `aws-lambda`.** Where
288
+ the app owns the whole surface, `public/` is mounted after every route and answers only what none of them
289
+ claimed. Where a CDN sits in front it is part of the static output, so the platform answers from it before
290
+ the app is invoked — the framework cannot reorder either one. So `public/index.html` beside a page route at
291
+ `/` serves the file on two targets and renders the page on the other two. `rshono build` warns when a
292
+ `public/` file lands on a route's path, on the targets where it matters.
271
293
  - **The serverless targets bundle your dependencies**; `node` bundles only the ones a `'use client'` component
272
294
  pulls in. A function is an uploaded directory with no `node_modules` to resolve against, so `vercel`,
273
295
  `aws-lambda` and `cloudflare` compile everything in. The cost is that a native addon — or a package that
@@ -311,10 +333,45 @@ Every target streams, which is the bar a new one has to clear.
311
333
  proof that reloading will not help. A visitor without JavaScript is left on the fallback under a 200, and a
312
334
  crawler indexes that 200 as a soft 404. The fix is app-side: decide in Hono
313
335
  middleware, or in the page component body above the boundary. `rshono dev` warns when it happens.
336
+
337
+ **On a soft navigation there is no shell to beat, so every `notFound()` degrades.** A flight fetch is
338
+ committed as `200 text/x-component` the moment the render hands its stream back — before anything has been
339
+ awaited — so a `notFound()` from the _first line_ of a page component is already too late to be a 404. It
340
+ rides the payload as a digest, and the client recovers by reloading the page for real (once per URL per
341
+ tab, then a plain "Page not found" panel). Every in-app click that lands on something missing therefore
342
+ costs an extra round trip and a full document parse. **`redirect()` is not affected** — the same digest
343
+ becomes a soft `push()` to the new location, which is what the navigation was going to be anyway. Prefer a
344
+ `redirect()` where either would do, and decide in Hono middleware where the status still has to be a real 404.
345
+
346
+ **A page that _throws_ lands in the same place: the flight response is a `200`.** A document request for it
347
+ is a 500 answered by your `error` page, but the payload's status was committed before the render failed, so
348
+ the error rides it as a row and the client runtime paints its error UI — which is the design working, and
349
+ is what makes a soft navigation onto a broken page recoverable rather than a blank tab. What it costs is
350
+ observability: **an uptime monitor or a CDN log watching for 5xx sees nothing** for a soft navigation, and
351
+ the ratio between the two statuses depends on how much of your traffic is in-app clicks. Count failures
352
+ through `onServerError()`, which fires for both. Buffering the payload to learn whether it failed is the one
353
+ fix that is not available: it would cost every page its streaming.
354
+
314
355
  - **A page route answers `GET`, `POST` and `HEAD`.** Every other method is a 404 rather than a 405: the
315
356
  `Allow` header a 405 owes the client means tracking the methods registered per path, which is state on a hot
316
357
  path for a distinction nothing acts on differently here. An endpoint route is the way to answer a `PUT`,
317
358
  `PATCH`, `DELETE` or `OPTIONS`.
359
+ - **A page route refuses every cross-site form post**, not only one carrying a server action. A form post to
360
+ a page is how a `<form action={serverAction}>` reaches the server, and whether a given one holds an action
361
+ can only be known by reading the body — so the framework refuses on `Sec-Fetch-Site` and the request's
362
+ shape, before parsing, rather than buffering a body for anyone who asks. "Shape" is all three `enctype`
363
+ values a browser form can send — `application/x-www-form-urlencoded`, `multipart/form-data` and
364
+ `text/plain` — since what makes one forgeable from another site is the shape and not what is in it. The
365
+ cases this rules out are real ones: a **SAML ACS callback**, OIDC **`response_mode=form_post`**, and most
366
+ payment-gateway returns all arrive as a cross-site POST in exactly that shape. `csrf()`'s allowlist does
367
+ not widen it — this is the framework declining to run its own action mechanism, ahead of any app policy.
368
+ **Receive them on an `{ type: 'endpoint' }` route**, which calls your Hono handler directly and never
369
+ reaches the page renderer, then redirect to the page.
370
+ - **`/_static` is reserved**, on every deploy target and under `rshono dev`: it is where the hashed client
371
+ bundle is served from, mounted ahead of the route table and answering its whole subtree. A route whose path
372
+ is `/_static` or sits below it is refused by name — it could never have answered a request. A parameterised
373
+ route that happens to overlap the prefix (`/:section/thing`) is left alone; it loses those paths and
374
+ answers the rest.
318
375
  - **A page's `ctx` prop is non-enumerable**, so `<Child {...props} />` hands a _server_ child
319
376
  `ctx: undefined` — silently, since a spread copies enumerables only, and the type still says it is there.
320
377
  It cannot be otherwise: an enumerable `ctx` would put `ctx.hono.env`, every binding and secret, into
@@ -329,6 +386,12 @@ Every target streams, which is the bar a new one has to clear.
329
386
  in — so `rshono dev` does not pick up an edit to either, and a rebuild that serves the old value looks like
330
387
  nothing happened. Restart it; it watches both and says so when one changes.
331
388
  - The dev proxy doesn't forward WebSocket upgrades to a custom sub-app; production is unaffected.
389
+ - **`rshono dev` answers `/_static` itself, so your middleware does not run for an asset** — where a build
390
+ serves assets through the app and they carry HSTS, your CSP and everything else it sets. The dev front-end
391
+ owns the prefix on purpose: every request it proxies waits on the server rebuild, and the client bundle is
392
+ built by a separate compiler, so proxying assets would stall the browser's JS and CSS on a save that only
393
+ touched a server component. The cost is that a policy is developed against files it does not apply to;
394
+ check a header that has to be on an asset against `rshono build` and `rshono start`.
332
395
  - Dev source maps embed the original source of `'use server'` modules (dev binds 127.0.0.1 only, and
333
396
  production ships no client source maps).
334
397
 
@@ -354,6 +417,34 @@ Every target streams, which is the bar a new one has to clear.
354
417
  soft navigation, `useNavigation`, client-initiated actions, boundary fallbacks and the fatal overlay — the
355
418
  client runtime, which no amount of asserting on HTML can reach.
356
419
 
420
+ `test:coverage` gates the build tooling, **not the request hot path** — worth knowing before reading anything
421
+ into the number. Node measures what this process loads, and the modules that answer a request only ever run
422
+ inside the bundled testbed in a child process: `entry.rsc`, `entry.client`, `entry.ssr`, `boundaries`,
423
+ `navigation` and every `deploy/*/runtime` appear nowhere in the report, and `runtime/` in it means `context`,
424
+ `control`, `flight-inject`, `hot-update`, `request` and `validate-entries` alone. Those absent modules are the
425
+ most thoroughly tested code in the package — by the e2e suites above, over HTTP, which is the only way to test
426
+ them at all. So the percentage covers the builder, the CLI, the deploy build steps and the pure runtime
427
+ helpers; treat its floors as a ratchet on those, and never as a statement about the framework as a whole.
428
+
429
+ The other half can be measured on demand, and is worth a look before a release. Every process the suite
430
+ spawns writes its own V8 coverage if you ask for it, and the testbed's bundle carries a source map that
431
+ reaches back into this package's `src/`, so the request path can be remapped onto the files it came from:
432
+
433
+ ```bash
434
+ # from the repository root, so the testbed the coverage points into is under the working directory
435
+ NODE_V8_COVERAGE=.coverage-e2e node --test packages/core/test/prod.test.mjs
436
+ npx c8 report --temp-directory .coverage-e2e --reporter=text --all=false --exclude-after-remap \
437
+ --exclude='**/node_modules/**' --exclude='**/testbed/src/**' --exclude='**/webpack/**' \
438
+ --exclude='**/test/**' --exclude='**/drift-*/**' --exclude='**/*.css'
439
+ ```
440
+
441
+ One suite at a time, because a full run builds the testbed several times over — production, dev, cloudflare,
442
+ vercel — and each bundle is a separate path holding the same sources, which no total can add up. Last taken
443
+ this way, the production e2e suite alone reached **97.4% of `entry.rsc.tsx`** (92.3% of its branches),
444
+ **100% of `entry.ssr.tsx`, `navigation.tsx` and the node runtime**, and 88% of `boundaries.tsx` — the rest of
445
+ which is its client half, and belongs to the browser suite. That is why the gate is left where it is rather
446
+ than tightened: the code it cannot see is not the code that is thin.
447
+
357
448
  ## How it works
358
449
 
359
450
  Two coordinated Rspack compilers, using native RSC support (`rspack.experiments.rsc`):
@@ -61,6 +61,29 @@ const MENTIONS_PROCESS = /\bprocess\b/;
61
61
  */
62
62
  const GLOBAL_PROCESS = /\b(?:globalThis|global|self)\s*(?:\?\.|\.)?\s*(?:process\b|\[\s*(['"`])process\1\s*\])/;
63
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;
86
+
64
87
  /**
65
88
  * Shadows `process.env` with the PUBLIC_-only view inside SSR-layer modules, so a secret read from a
66
89
  * `'use client'` component renders empty on the server rather than leaking into the HTML stream — and SSR
@@ -74,7 +97,7 @@ module.exports = function envShadowLoader(source) {
74
97
  // Two steps rather than the regex alone: this loader sees every module in the bundle, and most of them do
75
98
  // not contain the substring at all, which `includes` settles far more cheaply than a scan for word breaks.
76
99
  if (!source.includes('process') || !MENTIONS_PROCESS.test(source)) return source;
77
- const { prelude, layer, appSrcPrefix } = this.getOptions();
100
+ const { prelude, layer, appSrcPrefix, nodeEnv } = this.getOptions();
78
101
  if (!this._module) {
79
102
  throw new Error(
80
103
  "[rshono] env-shadow-loader could not read the module's layer (Rspack's `this._module` is unavailable). " +
@@ -96,6 +119,23 @@ module.exports = function envShadowLoader(source) {
96
119
  );
97
120
  }
98
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
+
99
139
  const prologue = source.match(DIRECTIVE_PROLOGUE)[0];
100
140
  return prologue + prelude + source.slice(prologue.length);
101
141
  };
@@ -1,5 +1,32 @@
1
1
  'use strict';
2
- const OPENS_WITH_DIRECTIVE = /^(?:\s|\/\/[^\n]*(?:\n|$)|\/\*[\s\S]*?\*\/)*(['"])use (?:client|server|server-entry)\1\s*(?:;|\n|$)/;
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,2 +1 @@
1
1
  export declare function scanPageFiles(routesFile: string, srcDir: string, into: Set<string>): void;
2
- //# sourceMappingURL=page-files.d.ts.map
@@ -6,4 +6,3 @@
6
6
  * hydrates.
7
7
  */
8
8
  export declare function publicEnv(isDev: boolean): Record<string, string>;
9
- //# sourceMappingURL=public-env.d.ts.map
@@ -10,4 +10,3 @@
10
10
  * framework's.
11
11
  */
12
12
  export declare function checkReactVersions(rootDir: string): void;
13
- //# sourceMappingURL=react-versions.d.ts.map
@@ -21,4 +21,3 @@ export interface RspackConfigOptions {
21
21
  onServerComponentChanges?: () => void;
22
22
  }
23
23
  export declare function createConfigs(options: RspackConfigOptions): [RspackOptions, RspackOptions];
24
- //# sourceMappingURL=rspack-config.d.ts.map
@@ -310,6 +310,10 @@ export function createConfigs(options) {
310
310
  options: {
311
311
  prelude: `const process = Object.assign(Object.create(globalThis.process ?? Object.prototype), { env: ${JSON.stringify(publicEnv(isDev))} }); `,
312
312
  layer: Layers.ssr,
313
+ // The prelude's binding hides `process.env.NODE_ENV` from DefinePlugin, which is what erases
314
+ // React's `if (NODE_ENV === 'production')` wrappers. The loader substitutes it back so the
315
+ // dead half still goes; without it the SSR layer ships both builds of every React package.
316
+ nodeEnv: mode,
313
317
  // Only the app's own source is warned about when it reaches `process` through the global
314
318
  // object, which no prelude can shadow — a library feature-detecting `globalThis.process` is
315
319
  // doing nothing wrong and has no app secret to read. The separator is part of the prefix so
@@ -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, 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"]}
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 // The prelude's binding hides `process.env.NODE_ENV` from DefinePlugin, which is what erases\n // React's `if (NODE_ENV === 'production')` wrappers. The loader substitutes it back so the\n // dead half still goes; without it the SSR layer ships both builds of every React package.\n nodeEnv: mode,\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"]}
@@ -30,4 +30,3 @@ import type { Compiler } from '@rspack/core';
30
30
  export declare function serverOnlyImportPlugin(rootDir: string): {
31
31
  apply(compiler: Compiler): void;
32
32
  };
33
- //# sourceMappingURL=server-only-imports.d.ts.map
@@ -8,4 +8,3 @@ interface BuildOptions {
8
8
  }
9
9
  export declare function buildCommand(options: BuildOptions): Promise<void>;
10
10
  export {};
11
- //# sourceMappingURL=build.d.ts.map
package/dist/cli/build.js CHANGED
@@ -6,26 +6,7 @@ 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
- }
9
+ import { exit } from './exit.js';
29
10
  function importServerBundle(distDir) {
30
11
  return import(pathToFileURL(join(distDir, 'server', 'main.mjs')).href);
31
12
  }
@@ -47,7 +28,7 @@ export async function buildCommand(options) {
47
28
  });
48
29
  if (stats.hasErrors()) {
49
30
  console.error(stats.toString({ preset: 'errors-warnings', colors: true }));
50
- process.exit(1);
31
+ return exit(1);
51
32
  }
52
33
  // Printed rather than left to the summary below, which counts warnings without saying what they are —
53
34
  // and one of them is the env shadow reporting a read it cannot cover. See `env-shadow-loader.cjs`.
@@ -55,9 +36,17 @@ export async function buildCommand(options) {
55
36
  console.warn(stats.toString({ preset: 'errors-warnings', colors: true }));
56
37
  console.log(stats.toString({ preset: 'summary', colors: true }));
57
38
  const publicDir = join(rootDir, 'public');
58
- let distPublicDir = null;
39
+ // From scratch, and unconditionally, like every other directory this build assembles. `cpSync` only
40
+ // adds, so a file deleted from `public/` used to survive in `dist/public` and go on being served by
41
+ // `rshono start` for good — and deleting `public/` outright left the whole of the old tree there, since
42
+ // the copy below is the only thing that touches it. The two CDN presets clear their own output but read
43
+ // *this* directory, so a stale file propagated into their assets as well, past a comment promising it
44
+ // could not.
45
+ const distPublicDir = join(distDir, 'public');
46
+ await rm(distPublicDir, { recursive: true, force: true });
47
+ let servedPublicDir = null;
59
48
  if (existsSync(publicDir)) {
60
- distPublicDir = join(distDir, 'public');
49
+ servedPublicDir = distPublicDir;
61
50
  cpSync(publicDir, distPublicDir, { recursive: true });
62
51
  console.log(' • copied public/ into dist/public (served at /)');
63
52
  }
@@ -65,19 +54,33 @@ export async function buildCommand(options) {
65
54
  await rm(ssgDir, { recursive: true, force: true });
66
55
  process.env.RSHONO_PRERENDER = '1';
67
56
  // The bundle's module scope is where `src/routes.ts` and `src/server.ts` are validated.
68
- const bundle = await phase(() => importServerBundle(distDir));
57
+ //
58
+ // This and the two stages below are what the *app* can fail — the two entry modules here, each route's own
59
+ // module next, and a page that throws while prerendering after that. Each raises a `[rshono]` message
60
+ // written for whoever is running the build, and `main().catch` in `cli/index.ts` is what prints it as the
61
+ // one line it is. These used to be wrapped in a `phase()` helper that did the same thing three times and
62
+ // nowhere else, which left `createConfigs` above — where a missing `src/routes.ts` is found — printing a
63
+ // raw `Error` object with framework frames in it.
64
+ const bundle = await importServerBundle(distDir);
69
65
  // Before anything is rendered: the checks here are the ones a route fails on *every* request, so a build
70
66
  // that skipped them would spend the prerender pass on a route it is about to refuse anyway. Without this
71
67
  // 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({
68
+ await bundle.checkAppModules();
69
+ const { written, skipped, flightOnly } = await prerenderStaticRoutes({
74
70
  routes: bundle.routes,
75
71
  fetch: (request) => bundle.app.fetch(request),
76
72
  ssgDir,
77
73
  siteUrl: config.siteUrl,
78
- }));
79
- if (written.length > 0)
80
- console.log(` • prerendered ${written.length} static page(s): ${written.join(', ')}`);
74
+ });
75
+ if (written.length > 0) {
76
+ // Marked per page rather than counted separately, so the line answers the question a reader has about
77
+ // each name in it: is this page served from disk? A page under a CSP nonce is half of one — its flight
78
+ // payload is, its document is re-rendered per request — and saying "prerendered" alone would claim more
79
+ // than the build did. The warning above says why, once.
80
+ const nonced = new Set(flightOnly);
81
+ const label = (path) => (nonced.has(path) ? `${path} (flight only)` : path);
82
+ console.log(` • prerendered ${written.length} static page(s): ${written.map(label).join(', ')}`);
83
+ }
81
84
  if (skipped.length > 0)
82
85
  console.log(` • skipped ${skipped.length} (will SSR per request)`);
83
86
  // Before `finalize`, so a preset that copies `dist/` into a platform layout takes it along.
@@ -87,14 +90,13 @@ export async function buildCommand(options) {
87
90
  rootDir,
88
91
  distDir,
89
92
  staticDir: join(distDir, 'static'),
90
- publicDir: distPublicDir,
93
+ publicDir: servedPublicDir,
91
94
  ssgDir,
95
+ routes: bundle.routes,
92
96
  });
93
97
  console.log(` ✓ build complete — ${preset.deployHint}`);
94
- // Rspack's worker pool can keep the loop alive after `close()`, so the exit is explicit but a piped
95
- // stdout is asynchronous, and exiting drops whatever has not drained. In CI that is exactly the lines
96
- // saying what was built and where it went. A zero-length write's callback fires behind the real ones.
97
- await new Promise((resolve) => process.stdout.write('', () => resolve()));
98
- process.exit(0);
98
+ // Rspack's worker pool can keep the loop alive after `close()`, so the exit is explicit. {@link exit}
99
+ // drains first: in CI the tail is the lines saying what was built and where it went.
100
+ await exit(0);
99
101
  }
100
102
  //# sourceMappingURL=build.js.map
@@ -1 +1 @@
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"]}
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;AACzD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAiBjC,SAAS,kBAAkB,CAAC,OAAe;IACzC,OAAO,MAAM,CAAC,aAAa,CAAC,IAAI,CAAC,OAAO,EAAE,QAAQ,EAAE,UAAU,CAAC,CAAC,CAAC,IAAI,CAA0B,CAAC;AAClG,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,OAAqB;IACtD,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC;IAC5C,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,EAAE,aAAa,CAAC,CAAC;IAE7C,OAAO,CAAC,GAAG,CAAC,uCAAuC,CAAC,CAAC;IACrD,MAAM,OAAO,GAAG,aAAa,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;IACzE,MAAM,QAAQ,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC;IAEjC,MAAM,KAAK,GAAG,MAAM,IAAI,OAAO,CAAa,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QAC9D,QAAQ,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,MAAM,EAAE,EAAE;YAC3B,QAAQ,CAAC,KAAK,CAAC,GAAG,EAAE;gBAClB,IAAI,GAAG;oBAAE,MAAM,CAAC,GAAG,CAAC,CAAC;;oBAChB,OAAO,CAAC,MAAO,CAAC,CAAC;YACxB,CAAC,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,IAAI,KAAK,CAAC,SAAS,EAAE,EAAE,CAAC;QACtB,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,QAAQ,CAAC,EAAE,MAAM,EAAE,iBAAiB,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;QAC3E,OAAO,IAAI,CAAC,CAAC,CAAC,CAAC;IACjB,CAAC;IACD,sGAAsG;IACtG,mGAAmG;IACnG,IAAI,KAAK,CAAC,WAAW,EAAE;QAAE,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,EAAE,MAAM,EAAE,iBAAiB,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;IACnG,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,QAAQ,CAAC,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;IAEjE,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;IAC1C,oGAAoG;IACpG,oGAAoG;IACpG,wGAAwG;IACxG,wGAAwG;IACxG,sGAAsG;IACtG,aAAa;IACb,MAAM,aAAa,GAAG,IAAI,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;IAC9C,MAAM,EAAE,CAAC,aAAa,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IAC1D,IAAI,eAAe,GAAkB,IAAI,CAAC;IAC1C,IAAI,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;QAC1B,eAAe,GAAG,aAAa,CAAC;QAChC,MAAM,CAAC,SAAS,EAAE,aAAa,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACtD,OAAO,CAAC,GAAG,CAAC,mDAAmD,CAAC,CAAC;IACnE,CAAC;IAED,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;IACpC,MAAM,EAAE,CAAC,MAAM,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IACnD,OAAO,CAAC,GAAG,CAAC,gBAAgB,GAAG,GAAG,CAAC;IACnC,wFAAwF;IACxF,EAAE;IACF,2GAA2G;IAC3G,sGAAsG;IACtG,0GAA0G;IAC1G,yGAAyG;IACzG,yGAAyG;IACzG,kDAAkD;IAClD,MAAM,MAAM,GAAG,MAAM,kBAAkB,CAAC,OAAO,CAAC,CAAC;IAEjD,yGAAyG;IACzG,yGAAyG;IACzG,4GAA4G;IAC5G,MAAM,MAAM,CAAC,eAAe,EAAE,CAAC;IAE/B,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,GAAG,MAAM,qBAAqB,CAAC;QACnE,MAAM,EAAE,MAAM,CAAC,MAAM;QACrB,KAAK,EAAE,CAAC,OAAO,EAAE,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC;QAC7C,MAAM;QACN,OAAO,EAAE,MAAM,CAAC,OAAO;KACxB,CAAC,CAAC;IACH,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACvB,sGAAsG;QACtG,uGAAuG;QACvG,wGAAwG;QACxG,wDAAwD;QACxD,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,UAAU,CAAC,CAAC;QACnC,MAAM,KAAK,GAAG,CAAC,IAAY,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,gBAAgB,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QACpF,OAAO,CAAC,GAAG,CAAC,mBAAmB,OAAO,CAAC,MAAM,oBAAoB,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACpG,CAAC;IACD,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,CAAC,GAAG,CAAC,eAAe,OAAO,CAAC,MAAM,yBAAyB,CAAC,CAAC;IAE5F,4FAA4F;IAC5F,gBAAgB,CAAC,OAAO,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC;IAEvC,iFAAiF;IACjF,MAAM,MAAM,CAAC,QAAQ,EAAE,CAAC;QACtB,OAAO;QACP,OAAO;QACP,SAAS,EAAE,IAAI,CAAC,OAAO,EAAE,QAAQ,CAAC;QAClC,SAAS,EAAE,eAAe;QAC1B,MAAM;QACN,MAAM,EAAE,MAAM,CAAC,MAAM;KACtB,CAAC,CAAC;IAEH,OAAO,CAAC,GAAG,CAAC,wBAAwB,MAAM,CAAC,UAAU,EAAE,CAAC,CAAC;IAEzD,sGAAsG;IACtG,qFAAqF;IACrF,MAAM,IAAI,CAAC,CAAC,CAAC,CAAC;AAChB,CAAC","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';\nimport { exit } from './exit.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 the app's server actions, and checks them — see `checkAppModules`. */\n checkAppModules: () => Promise<void>;\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 return 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 // From scratch, and unconditionally, like every other directory this build assembles. `cpSync` only\n // adds, so a file deleted from `public/` used to survive in `dist/public` and go on being served by\n // `rshono start` for good — and deleting `public/` outright left the whole of the old tree there, since\n // the copy below is the only thing that touches it. The two CDN presets clear their own output but read\n // *this* directory, so a stale file propagated into their assets as well, past a comment promising it\n // could not.\n const distPublicDir = join(distDir, 'public');\n await rm(distPublicDir, { recursive: true, force: true });\n let servedPublicDir: string | null = null;\n if (existsSync(publicDir)) {\n servedPublicDir = distPublicDir;\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 //\n // This and the two stages below are what the *app* can fail — the two entry modules here, each route's own\n // module next, and a page that throws while prerendering after that. Each raises a `[rshono]` message\n // written for whoever is running the build, and `main().catch` in `cli/index.ts` is what prints it as the\n // one line it is. These used to be wrapped in a `phase()` helper that did the same thing three times and\n // nowhere else, which left `createConfigs` above — where a missing `src/routes.ts` is found — printing a\n // raw `Error` object with framework frames in it.\n const bundle = await 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 bundle.checkAppModules();\n\n const { written, skipped, flightOnly } = await prerenderStaticRoutes({\n routes: bundle.routes,\n fetch: (request) => bundle.app.fetch(request),\n ssgDir,\n siteUrl: config.siteUrl,\n });\n if (written.length > 0) {\n // Marked per page rather than counted separately, so the line answers the question a reader has about\n // each name in it: is this page served from disk? A page under a CSP nonce is half of one — its flight\n // payload is, its document is re-rendered per request — and saying \"prerendered\" alone would claim more\n // than the build did. The warning above says why, once.\n const nonced = new Set(flightOnly);\n const label = (path: string) => (nonced.has(path) ? `${path} (flight only)` : path);\n console.log(` • prerendered ${written.length} static page(s): ${written.map(label).join(', ')}`);\n }\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: servedPublicDir,\n ssgDir,\n routes: bundle.routes,\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. {@link exit}\n // drains first: in CI the tail is the lines saying what was built and where it went.\n await exit(0);\n}\n"]}