@rshono/core 1.0.0-rc.18 → 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.
Files changed (53) hide show
  1. package/README.md +13 -3
  2. package/dist/builder/rspack-config.d.ts.map +1 -1
  3. package/dist/builder/rspack-config.js +5 -1
  4. package/dist/builder/rspack-config.js.map +1 -1
  5. package/dist/builder/server-only-imports.d.ts +33 -0
  6. package/dist/builder/server-only-imports.d.ts.map +1 -0
  7. package/dist/builder/server-only-imports.js +51 -0
  8. package/dist/builder/server-only-imports.js.map +1 -0
  9. package/dist/cli/build.d.ts.map +1 -1
  10. package/dist/cli/build.js +18 -9
  11. package/dist/cli/build.js.map +1 -1
  12. package/dist/cli/dev.d.ts.map +1 -1
  13. package/dist/cli/dev.js +45 -0
  14. package/dist/cli/dev.js.map +1 -1
  15. package/dist/deploy/presets.d.ts +8 -0
  16. package/dist/deploy/presets.d.ts.map +1 -1
  17. package/dist/deploy/presets.js +1 -0
  18. package/dist/deploy/presets.js.map +1 -1
  19. package/dist/router.d.ts +33 -18
  20. package/dist/router.d.ts.map +1 -1
  21. package/dist/router.js.map +1 -1
  22. package/dist/runtime/boundaries.d.ts +11 -4
  23. package/dist/runtime/boundaries.d.ts.map +1 -1
  24. package/dist/runtime/boundaries.js.map +1 -1
  25. package/dist/runtime/context.d.ts +9 -2
  26. package/dist/runtime/context.d.ts.map +1 -1
  27. package/dist/runtime/context.js +12 -3
  28. package/dist/runtime/context.js.map +1 -1
  29. package/dist/runtime/entry.client.js +124 -19
  30. package/dist/runtime/entry.client.js.map +1 -1
  31. package/dist/runtime/entry.rsc.d.ts +7 -0
  32. package/dist/runtime/entry.rsc.d.ts.map +1 -1
  33. package/dist/runtime/entry.rsc.js +17 -20
  34. package/dist/runtime/entry.rsc.js.map +1 -1
  35. package/dist/runtime/validate-entries.d.ts +49 -4
  36. package/dist/runtime/validate-entries.d.ts.map +1 -1
  37. package/dist/runtime/validate-entries.js +118 -2
  38. package/dist/runtime/validate-entries.js.map +1 -1
  39. package/dist/server/prerendered.d.ts +0 -5
  40. package/dist/server/prerendered.d.ts.map +1 -1
  41. package/dist/server/prerendered.js +1 -1
  42. package/dist/server/prerendered.js.map +1 -1
  43. package/dist/server/server-config.d.ts +8 -1
  44. package/dist/server/server-config.d.ts.map +1 -1
  45. package/dist/server/server-config.js +2 -1
  46. package/dist/server/server-config.js.map +1 -1
  47. package/dist/server/ssg.d.ts.map +1 -1
  48. package/dist/server/ssg.js +24 -7
  49. package/dist/server/ssg.js.map +1 -1
  50. package/dist/server/static.d.ts.map +1 -1
  51. package/dist/server/static.js +5 -1
  52. package/dist/server/static.js.map +1 -1
  53. package/package.json +2 -2
package/README.md CHANGED
@@ -305,9 +305,11 @@ Every target streams, which is the bar a new one has to clear.
305
305
  - **`redirect()` and `notFound()` must be reached before the page shell is sent.** A page streams: the status
306
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
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`. A browser with JavaScript still follows it (the signal rides the
309
- payload as a digest, and the client runtime navigates), but a visitor without JavaScript is left on the
310
- fallback under a 200, and a crawler indexes that 200 as a soft 404. The fix is app-side: decide in Hono
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
311
313
  middleware, or in the page component body above the boundary. `rshono dev` warns when it happens.
312
314
  - **A page route answers `GET`, `POST` and `HEAD`.** Every other method is a 404 rather than a 405: the
313
315
  `Allow` header a 405 owes the client means tracking the methods registered per path, which is state on a hot
@@ -318,6 +320,14 @@ Every target streams, which is the bar a new one has to clear.
318
320
  It cannot be otherwise: an enumerable `ctx` would put `ctx.hono.env`, every binding and secret, into
319
321
  React's dev-only serialization of a server component's props. Nested server components are meant to call
320
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.
321
331
  - The dev proxy doesn't forward WebSocket upgrades to a custom sub-app; production is unaffected.
322
332
  - Dev source maps embed the original source of `'use server'` modules (dev binds 127.0.0.1 only, and
323
333
  production ships no client source maps).
@@ -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;AAYzD;;;;;;;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,CAsQ1F"}
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"}
@@ -4,6 +4,7 @@ import { existsSync, readdirSync, readFileSync } from 'node:fs';
4
4
  import { createRequire } from 'node:module';
5
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';
@@ -213,6 +214,7 @@ export function createConfigs(options) {
213
214
  },
214
215
  plugins: [
215
216
  new ClientPlugin(),
217
+ serverOnlyImportPlugin(rootDir),
216
218
  new rspack.DefinePlugin({ 'process.env': JSON.stringify(publicEnv(isDev)) }),
217
219
  ...(isDev ? [new rspack.HotModuleReplacementPlugin(), new ReactRefreshRspackPlugin()] : []),
218
220
  ],
@@ -341,7 +343,9 @@ export function createConfigs(options) {
341
343
  pageScanPlugin,
342
344
  new ServerPlugin({ onServerComponentChanges }),
343
345
  // Bakes rshono.config.ts into the bundle, read back as `__RSHONO_CONFIG__` at request time.
344
- new rspack.DefinePlugin({ __RSHONO_CONFIG__: JSON.stringify(resolveServerConfig(config, { isDev, outDir })) }),
346
+ new rspack.DefinePlugin({
347
+ __RSHONO_CONFIG__: JSON.stringify(resolveServerConfig(config, { isDev, outDir, envBindings: preset.envBindings })),
348
+ }),
345
349
  ],
346
350
  performance: false,
347
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, 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 { 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 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({ __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"]}
@@ -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;AAuBD,wBAAsB,YAAY,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAkEvE"}
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
@@ -7,15 +7,16 @@ 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
9
  /**
10
- * Loads the built server bundle, which the prerender pass renders through.
10
+ * Runs one phase of the build that the app itself can fail.
11
11
  *
12
- * Its module scope is where `src/routes.ts` and `src/server.ts` are validated, so a throw from here is
13
- * usually a message written for whoever is running the build printed as one, rather than as a stack
14
- * through the minified frames of a bundle they did not write.
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.
15
16
  */
16
- async function importServerBundle(distDir) {
17
+ async function phase(run) {
17
18
  try {
18
- return (await import(pathToFileURL(join(distDir, 'server', 'main.mjs')).href));
19
+ return await run();
19
20
  }
20
21
  catch (error) {
21
22
  const message = error instanceof Error ? error.message : String(error);
@@ -25,6 +26,9 @@ async function importServerBundle(distDir) {
25
26
  process.exit(1);
26
27
  }
27
28
  }
29
+ function importServerBundle(distDir) {
30
+ return import(pathToFileURL(join(distDir, 'server', 'main.mjs')).href);
31
+ }
28
32
  export async function buildCommand(options) {
29
33
  const { rootDir, config, preset } = options;
30
34
  const distDir = join(rootDir, BUILD_OUT_DIR);
@@ -60,13 +64,18 @@ export async function buildCommand(options) {
60
64
  const ssgDir = join(distDir, 'ssg');
61
65
  await rm(ssgDir, { recursive: true, force: true });
62
66
  process.env.RSHONO_PRERENDER = '1';
63
- const bundle = await importServerBundle(distDir);
64
- const { written, skipped } = await prerenderStaticRoutes({
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({
65
74
  routes: bundle.routes,
66
75
  fetch: (request) => bundle.app.fetch(request),
67
76
  ssgDir,
68
77
  siteUrl: config.siteUrl,
69
- });
78
+ }));
70
79
  if (written.length > 0)
71
80
  console.log(` • prerendered ${written.length} static page(s): ${written.join(', ')}`);
72
81
  if (skipped.length > 0)
@@ -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/**\n * Loads the built server bundle, which the prerender pass renders through.\n *\n * Its module scope is where `src/routes.ts` and `src/server.ts` are validated, so a throw from here is\n * usually a message written for whoever is running the build printed as one, rather than as a stack\n * through the minified frames of a bundle they did not write.\n */\nasync function importServerBundle(distDir: string): Promise<{ app: Hono; routes: readonly Route[] }> {\n try {\n return (await import(pathToFileURL(join(distDir, 'server', 'main.mjs')).href)) as {\n app: Hono;\n routes: readonly Route[];\n };\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\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 const bundle = await importServerBundle(distDir);\n const { written, skipped } = 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) 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":"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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 +1 @@
1
- {"version":3,"file":"dev.d.ts","sourceRoot":"","sources":["../../src/cli/dev.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AA0BjD,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,CAoPnE"}
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"}
package/dist/cli/dev.js CHANGED
@@ -2,6 +2,7 @@ import { serve } from '@hono/node-server';
2
2
  import { rspack } from '@rspack/core';
3
3
  import { Hono } from 'hono';
4
4
  import { proxy } from 'hono/proxy';
5
+ import { watch } from 'node:fs';
5
6
  import { mkdir, rm } from 'node:fs/promises';
6
7
  import { createServer } from 'node:net';
7
8
  import { join } from 'node:path';
@@ -11,6 +12,49 @@ import { NODE_PRESET } from '../deploy/presets.js';
11
12
  import { SERVER_DEFAULTS } from '../server/server-config.js';
12
13
  import { createStaticAssetsApp } from '../server/static.js';
13
14
  const WORKER_READY_TIMEOUT_MS = 15_000;
15
+ /**
16
+ * Files whose contents are *compiled into* a build rather than read per request, so no rebuild picks a change
17
+ * to them up: `.env` because the `PUBLIC_` view of it is baked into the client bundle by DefinePlugin and into
18
+ * the SSR layer by the env-shadow prelude, and `rshono.config.ts` because `ServerConfig` is a DefinePlugin
19
+ * literal too. `.env` is worse than the config, which the docs at least describe as build-time: a page that
20
+ * reads `process.env` rebuilds, serves, and shows the old value.
21
+ */
22
+ const BAKED_IN_FILES = ['.env.local', '.env', 'rshono.config.ts', 'rshono.config.js', 'rshono.config.mjs'];
23
+ /**
24
+ * Watches those files, only to say that a restart is needed.
25
+ *
26
+ * Reloading them for real means re-exec'ing this process: `process.loadEnvFile` does not override a variable
27
+ * that is already set, so the copy this process took at startup would survive an in-place reload — and the
28
+ * worker is spawned with `env: process.env`, so it would inherit the stale value too. Choreographing that
29
+ * against a bound port and a live SSE connection is a great deal of machinery to save one Ctrl-C; a line
30
+ * saying which key to press costs nothing and loses nobody an afternoon.
31
+ *
32
+ * The directory is watched rather than each file, so a file that does not exist yet is still covered — and
33
+ * because an editor's atomic save replaces the file, which a watch on the file itself does not survive. An
34
+ * explicit `--config` outside the project root is not covered: this is a convenience, not a contract.
35
+ */
36
+ function warnOnBakedInFileChanges(rootDir) {
37
+ const warnedAt = new Map();
38
+ try {
39
+ // `persistent: false`: the dev server is kept alive by its listener, and a watcher that outlived it
40
+ // would hold the process open with nothing left to report to.
41
+ watch(rootDir, { persistent: false }, (_event, filename) => {
42
+ if (!filename || !BAKED_IN_FILES.includes(filename))
43
+ return;
44
+ // One save fires several events — a write, a rename, an attribute change — and they are all the same
45
+ // save. The window is short enough that a second real edit still gets its own line.
46
+ const now = Date.now();
47
+ if (now - (warnedAt.get(filename) ?? 0) < 250)
48
+ return;
49
+ warnedAt.set(filename, now);
50
+ console.warn(` ⚠ ${filename} changed — restart \`rshono dev\` to pick it up. It is compiled into the build, not read per request.`);
51
+ });
52
+ }
53
+ catch {
54
+ // No watcher available: a filesystem that cannot, or a platform limit reached. Rebuilding still works,
55
+ // so the dev server goes on without the reminder rather than refusing to start over one.
56
+ }
57
+ }
14
58
  /** An error as the dev server shows it in the browser: the stack where there is one, since this is dev. */
15
59
  function describe(error) {
16
60
  return error instanceof Error ? (error.stack ?? error.message) : String(error);
@@ -248,6 +292,7 @@ export async function devCommand(options) {
248
292
  }
249
293
  return response;
250
294
  });
295
+ warnOnBakedInFileChanges(rootDir);
251
296
  serve({ fetch: front.fetch, port, hostname: '127.0.0.1' }, (info) => {
252
297
  console.log(` ➜ rshono dev server: http://localhost:${info.port}`);
253
298
  });