@rshono/core 1.0.0-rc.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +273 -0
- package/bin/rshono.mjs +15 -0
- package/dist/builder/env-shadow-loader.cjs +26 -0
- package/dist/builder/page-entry-loader.cjs +7 -0
- package/dist/builder/page-files.d.ts +2 -0
- package/dist/builder/page-files.d.ts.map +1 -0
- package/dist/builder/page-files.js +45 -0
- package/dist/builder/page-files.js.map +1 -0
- package/dist/builder/public-env.d.ts +2 -0
- package/dist/builder/public-env.d.ts.map +1 -0
- package/dist/builder/public-env.js +5 -0
- package/dist/builder/public-env.js.map +1 -0
- package/dist/builder/rspack-config.d.ts +14 -0
- package/dist/builder/rspack-config.d.ts.map +1 -0
- package/dist/builder/rspack-config.js +235 -0
- package/dist/builder/rspack-config.js.map +1 -0
- package/dist/cli/build.d.ts +11 -0
- package/dist/cli/build.d.ts.map +1 -0
- package/dist/cli/build.js +64 -0
- package/dist/cli/build.js.map +1 -0
- package/dist/cli/dev.d.ts +9 -0
- package/dist/cli/dev.d.ts.map +1 -0
- package/dist/cli/dev.js +210 -0
- package/dist/cli/dev.js.map +1 -0
- package/dist/cli/index.d.ts +2 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +77 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/start.d.ts +8 -0
- package/dist/cli/start.d.ts.map +1 -0
- package/dist/cli/start.js +39 -0
- package/dist/cli/start.js.map +1 -0
- package/dist/config.d.ts +121 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +5 -0
- package/dist/config.js.map +1 -0
- package/dist/deploy/aws-lambda/runtime.d.ts +14 -0
- package/dist/deploy/aws-lambda/runtime.d.ts.map +1 -0
- package/dist/deploy/aws-lambda/runtime.js +24 -0
- package/dist/deploy/aws-lambda/runtime.js.map +1 -0
- package/dist/deploy/build-marker.d.ts +15 -0
- package/dist/deploy/build-marker.d.ts.map +1 -0
- package/dist/deploy/build-marker.js +26 -0
- package/dist/deploy/build-marker.js.map +1 -0
- package/dist/deploy/bun/runtime.d.ts +11 -0
- package/dist/deploy/bun/runtime.d.ts.map +1 -0
- package/dist/deploy/bun/runtime.js +22 -0
- package/dist/deploy/bun/runtime.js.map +1 -0
- package/dist/deploy/cloudflare/build.d.ts +7 -0
- package/dist/deploy/cloudflare/build.d.ts.map +1 -0
- package/dist/deploy/cloudflare/build.js +74 -0
- package/dist/deploy/cloudflare/build.js.map +1 -0
- package/dist/deploy/cloudflare/runtime.d.ts +10 -0
- package/dist/deploy/cloudflare/runtime.d.ts.map +1 -0
- package/dist/deploy/cloudflare/runtime.js +129 -0
- package/dist/deploy/cloudflare/runtime.js.map +1 -0
- package/dist/deploy/contract.d.ts +65 -0
- package/dist/deploy/contract.d.ts.map +1 -0
- package/dist/deploy/contract.js +2 -0
- package/dist/deploy/contract.js.map +1 -0
- package/dist/deploy/deno/runtime.d.ts +11 -0
- package/dist/deploy/deno/runtime.d.ts.map +1 -0
- package/dist/deploy/deno/runtime.js +16 -0
- package/dist/deploy/deno/runtime.js.map +1 -0
- package/dist/deploy/filesystem.d.ts +10 -0
- package/dist/deploy/filesystem.d.ts.map +1 -0
- package/dist/deploy/filesystem.js +47 -0
- package/dist/deploy/filesystem.js.map +1 -0
- package/dist/deploy/listen.d.ts +20 -0
- package/dist/deploy/listen.d.ts.map +1 -0
- package/dist/deploy/listen.js +24 -0
- package/dist/deploy/listen.js.map +1 -0
- package/dist/deploy/netlify/build.d.ts +8 -0
- package/dist/deploy/netlify/build.d.ts.map +1 -0
- package/dist/deploy/netlify/build.js +52 -0
- package/dist/deploy/netlify/build.js.map +1 -0
- package/dist/deploy/netlify/runtime.d.ts +13 -0
- package/dist/deploy/netlify/runtime.d.ts.map +1 -0
- package/dist/deploy/netlify/runtime.js +24 -0
- package/dist/deploy/netlify/runtime.js.map +1 -0
- package/dist/deploy/node/runtime.d.ts +7 -0
- package/dist/deploy/node/runtime.d.ts.map +1 -0
- package/dist/deploy/node/runtime.js +36 -0
- package/dist/deploy/node/runtime.js.map +1 -0
- package/dist/deploy/presets.d.ts +88 -0
- package/dist/deploy/presets.d.ts.map +1 -0
- package/dist/deploy/presets.js +110 -0
- package/dist/deploy/presets.js.map +1 -0
- package/dist/deploy/vercel/build.d.ts +3 -0
- package/dist/deploy/vercel/build.d.ts.map +1 -0
- package/dist/deploy/vercel/build.js +56 -0
- package/dist/deploy/vercel/build.js.map +1 -0
- package/dist/deploy/vercel/runtime.d.ts +14 -0
- package/dist/deploy/vercel/runtime.d.ts.map +1 -0
- package/dist/deploy/vercel/runtime.js +26 -0
- package/dist/deploy/vercel/runtime.js.map +1 -0
- package/dist/index.d.ts +23 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +17 -0
- package/dist/index.js.map +1 -0
- package/dist/router.d.ts +337 -0
- package/dist/router.d.ts.map +1 -0
- package/dist/router.js +18 -0
- package/dist/router.js.map +1 -0
- package/dist/runtime/boundaries.d.ts +102 -0
- package/dist/runtime/boundaries.d.ts.map +1 -0
- package/dist/runtime/boundaries.js +101 -0
- package/dist/runtime/boundaries.js.map +1 -0
- package/dist/runtime/client.d.ts +16 -0
- package/dist/runtime/client.d.ts.map +1 -0
- package/dist/runtime/client.js +16 -0
- package/dist/runtime/client.js.map +1 -0
- package/dist/runtime/context.d.ts +271 -0
- package/dist/runtime/context.d.ts.map +1 -0
- package/dist/runtime/context.js +360 -0
- package/dist/runtime/context.js.map +1 -0
- package/dist/runtime/control.d.ts +18 -0
- package/dist/runtime/control.d.ts.map +1 -0
- package/dist/runtime/control.js +39 -0
- package/dist/runtime/control.js.map +1 -0
- package/dist/runtime/dev-protocol.d.ts +22 -0
- package/dist/runtime/dev-protocol.d.ts.map +1 -0
- package/dist/runtime/dev-protocol.js +2 -0
- package/dist/runtime/dev-protocol.js.map +1 -0
- package/dist/runtime/empty-server-app.d.ts +3 -0
- package/dist/runtime/empty-server-app.d.ts.map +1 -0
- package/dist/runtime/empty-server-app.js +2 -0
- package/dist/runtime/empty-server-app.js.map +1 -0
- package/dist/runtime/entry.client.d.ts +2 -0
- package/dist/runtime/entry.client.d.ts.map +1 -0
- package/dist/runtime/entry.client.js +453 -0
- package/dist/runtime/entry.client.js.map +1 -0
- package/dist/runtime/entry.rsc.d.ts +32 -0
- package/dist/runtime/entry.rsc.d.ts.map +1 -0
- package/dist/runtime/entry.rsc.js +466 -0
- package/dist/runtime/entry.rsc.js.map +1 -0
- package/dist/runtime/entry.ssr.d.ts +25 -0
- package/dist/runtime/entry.ssr.d.ts.map +1 -0
- package/dist/runtime/entry.ssr.js +86 -0
- package/dist/runtime/entry.ssr.js.map +1 -0
- package/dist/runtime/navigation.d.ts +114 -0
- package/dist/runtime/navigation.d.ts.map +1 -0
- package/dist/runtime/navigation.js +112 -0
- package/dist/runtime/navigation.js.map +1 -0
- package/dist/runtime/request.d.ts +35 -0
- package/dist/runtime/request.d.ts.map +1 -0
- package/dist/runtime/request.js +39 -0
- package/dist/runtime/request.js.map +1 -0
- package/dist/runtime/server.d.ts +24 -0
- package/dist/runtime/server.d.ts.map +1 -0
- package/dist/runtime/server.js +24 -0
- package/dist/runtime/server.js.map +1 -0
- package/dist/server/compress.d.ts +15 -0
- package/dist/server/compress.d.ts.map +1 -0
- package/dist/server/compress.js +76 -0
- package/dist/server/compress.js.map +1 -0
- package/dist/server/headers.d.ts +24 -0
- package/dist/server/headers.d.ts.map +1 -0
- package/dist/server/headers.js +44 -0
- package/dist/server/headers.js.map +1 -0
- package/dist/server/load-config.d.ts +7 -0
- package/dist/server/load-config.d.ts.map +1 -0
- package/dist/server/load-config.js +41 -0
- package/dist/server/load-config.js.map +1 -0
- package/dist/server/load-env.d.ts +2 -0
- package/dist/server/load-env.d.ts.map +1 -0
- package/dist/server/load-env.js +10 -0
- package/dist/server/load-env.js.map +1 -0
- package/dist/server/prerendered.d.ts +67 -0
- package/dist/server/prerendered.d.ts.map +1 -0
- package/dist/server/prerendered.js +39 -0
- package/dist/server/prerendered.js.map +1 -0
- package/dist/server/server-config.d.ts +72 -0
- package/dist/server/server-config.d.ts.map +1 -0
- package/dist/server/server-config.js +94 -0
- package/dist/server/server-config.js.map +1 -0
- package/dist/server/shutdown.d.ts +7 -0
- package/dist/server/shutdown.d.ts.map +1 -0
- package/dist/server/shutdown.js +11 -0
- package/dist/server/shutdown.js.map +1 -0
- package/dist/server/ssg.d.ts +25 -0
- package/dist/server/ssg.d.ts.map +1 -0
- package/dist/server/ssg.js +160 -0
- package/dist/server/ssg.js.map +1 -0
- package/dist/server/static.d.ts +14 -0
- package/dist/server/static.d.ts.map +1 -0
- package/dist/server/static.js +30 -0
- package/dist/server/static.js.map +1 -0
- package/package.json +86 -0
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
import { rspack } from '@rspack/core';
|
|
2
|
+
import { ReactRefreshRspackPlugin } from '@rspack/plugin-react-refresh';
|
|
3
|
+
import { existsSync } from 'node:fs';
|
|
4
|
+
import { join, win32 } from 'node:path';
|
|
5
|
+
import { resolveServerConfig } from '../server/server-config.js';
|
|
6
|
+
import { scanPageFiles } from './page-files.js';
|
|
7
|
+
import { publicEnv } from './public-env.js';
|
|
8
|
+
// The framework's own compiled output (this file lives in `dist/builder/`), which is what the two
|
|
9
|
+
// compilers consume: the entries and loaders below are the *built* framework, not its TypeScript.
|
|
10
|
+
const FRAMEWORK_DIST = join(import.meta.dirname, '..');
|
|
11
|
+
const FRAMEWORK_ROOT = join(FRAMEWORK_DIST, '..');
|
|
12
|
+
const BUNDLED_PACKAGES = /^(@rshono\/core|react|react-dom|react-server-dom-rspack|rsc-html-stream|hono|@hono\/node-server)(\/|$)/;
|
|
13
|
+
/**
|
|
14
|
+
* Whether a request names a file rather than a package, and so belongs in the bundle.
|
|
15
|
+
*
|
|
16
|
+
* Rspack's RSC plugins ask for their client and server-entry proxies by *absolute path*, which on
|
|
17
|
+
* Windows means a drive letter (`D:\app\src\home.tsx`) instead of a leading slash. Externalizing one
|
|
18
|
+
* of those emits `import("D:\\app\\src\\home.tsx")`, and Node rejects that with
|
|
19
|
+
* `ERR_UNSUPPORTED_ESM_URL_SCHEME` ("Received protocol 'd:'") — a build that works everywhere else
|
|
20
|
+
* fails on Windows only. `win32.isAbsolute` also accepts the POSIX form, so it is used on every
|
|
21
|
+
* platform: one code path, and the Windows shapes stay testable off Windows.
|
|
22
|
+
*/
|
|
23
|
+
function isPathRequest(request) {
|
|
24
|
+
return request.startsWith('.') || win32.isAbsolute(request);
|
|
25
|
+
}
|
|
26
|
+
const BROWSER_TARGETS = ['last 2 versions', '> 0.2%', 'not dead', 'Firefox ESR'];
|
|
27
|
+
const NODE_TARGETS = ['node >= 22'];
|
|
28
|
+
/**
|
|
29
|
+
* Rspack's native CSS pipeline, which both compilers get.
|
|
30
|
+
*
|
|
31
|
+
* It parses *finished* CSS, so a stylesheet needing a PostCSS plugin — Tailwind, most obviously — adds
|
|
32
|
+
* the loader through the {@link RSHonoConfig.rspack} hook, along with the two packages a PostCSS pass
|
|
33
|
+
* takes. The framework stays out of it: `postcss` is a dependency an app that wants one can have, rather
|
|
34
|
+
* than one every app pays for. The Styling section of the README has the four lines involved.
|
|
35
|
+
*
|
|
36
|
+
* A fresh object per compiler, not one shared between them — the hook is handed each config in turn, and
|
|
37
|
+
* an app that reaches in to change this rule should not find it has changed the other bundle's too.
|
|
38
|
+
*/
|
|
39
|
+
function cssRule() {
|
|
40
|
+
return { test: /\.css$/i, type: 'css/auto' };
|
|
41
|
+
}
|
|
42
|
+
export function createConfigs(options) {
|
|
43
|
+
const { rootDir, isDev, config, preset, onServerComponentChanges } = options;
|
|
44
|
+
const srcDir = join(rootDir, 'src');
|
|
45
|
+
const mode = isDev ? 'development' : 'production';
|
|
46
|
+
const routesFile = ['routes.ts', 'routes.tsx'].map((f) => join(srcDir, f)).find(existsSync);
|
|
47
|
+
if (!routesFile) {
|
|
48
|
+
throw new Error(`[rshono] src/routes.ts not found in ${rootDir} — it is the one required file.`);
|
|
49
|
+
}
|
|
50
|
+
const serverAppFile = ['server.ts', 'server.tsx'].map((f) => join(srcDir, f)).find(existsSync);
|
|
51
|
+
const serverAppAlias = serverAppFile ?? join(FRAMEWORK_DIST, 'runtime', 'empty-server-app.js');
|
|
52
|
+
const rscEntry = join(FRAMEWORK_DIST, 'runtime', 'entry.rsc.js');
|
|
53
|
+
const ssrEntry = join(FRAMEWORK_DIST, 'runtime', 'entry.ssr.js');
|
|
54
|
+
const clientEntry = join(FRAMEWORK_DIST, 'runtime', 'entry.client.js');
|
|
55
|
+
const swcRule = (targets) => ({
|
|
56
|
+
test: /\.[cm]?[jt]sx?$/,
|
|
57
|
+
exclude: /[\\/]core-js[\\/]/,
|
|
58
|
+
use: {
|
|
59
|
+
loader: 'builtin:swc-loader',
|
|
60
|
+
options: {
|
|
61
|
+
detectSyntax: 'auto',
|
|
62
|
+
jsc: {
|
|
63
|
+
transform: { react: { runtime: 'automatic', development: isDev } },
|
|
64
|
+
experimental: { keepImportAttributes: true },
|
|
65
|
+
},
|
|
66
|
+
env: { targets },
|
|
67
|
+
rspackExperiments: { reactServerComponents: true },
|
|
68
|
+
},
|
|
69
|
+
},
|
|
70
|
+
type: 'javascript/auto',
|
|
71
|
+
});
|
|
72
|
+
const resolveBase = {
|
|
73
|
+
extensions: ['.tsx', '.ts', '.jsx', '.js', '.json'],
|
|
74
|
+
extensionAlias: { '.js': ['.ts', '.tsx', '.js'] },
|
|
75
|
+
modules: ['node_modules', join(FRAMEWORK_ROOT, 'node_modules')],
|
|
76
|
+
};
|
|
77
|
+
// The platform's own resolve conditions, ahead of whatever the Rspack target implies ('...'). This
|
|
78
|
+
// is what hands the server bundle the right build of React and the RSC runtime, both of which ship
|
|
79
|
+
// one per runtime. Left unset for Node, where the target already implies the `node` condition.
|
|
80
|
+
const runtimeConditions = preset.resolveConditions ?? [];
|
|
81
|
+
const serverResolveBase = runtimeConditions.length > 0 ? { ...resolveBase, conditionNames: [...runtimeConditions, '...'] } : resolveBase;
|
|
82
|
+
const rscConditionNames = ['react-server', ...runtimeConditions, '...'];
|
|
83
|
+
const { ServerPlugin, ClientPlugin } = rspack.experiments.rsc.createPlugins();
|
|
84
|
+
const { Layers } = rspack.experiments.rsc;
|
|
85
|
+
const pageFiles = new Set();
|
|
86
|
+
scanPageFiles(routesFile, srcDir, pageFiles);
|
|
87
|
+
const pageScanPlugin = {
|
|
88
|
+
apply(compiler) {
|
|
89
|
+
const refresh = () => scanPageFiles(routesFile, srcDir, pageFiles);
|
|
90
|
+
compiler.hooks.beforeRun.tap('rshono/page-scan', refresh);
|
|
91
|
+
compiler.hooks.watchRun.tap('rshono/page-scan', refresh);
|
|
92
|
+
},
|
|
93
|
+
};
|
|
94
|
+
const clientConfig = {
|
|
95
|
+
name: 'client',
|
|
96
|
+
mode,
|
|
97
|
+
target: 'web',
|
|
98
|
+
context: rootDir,
|
|
99
|
+
devtool: isDev ? 'source-map' : false,
|
|
100
|
+
entry: { main: clientEntry },
|
|
101
|
+
output: {
|
|
102
|
+
path: join(rootDir, 'dist', 'static'),
|
|
103
|
+
publicPath: '/_static/',
|
|
104
|
+
clean: !isDev,
|
|
105
|
+
filename: isDev ? 'chunks/main.js' : 'chunks/main.[contenthash].js',
|
|
106
|
+
chunkFilename: isDev ? 'chunks/[name].js' : 'chunks/[name].[contenthash].js',
|
|
107
|
+
cssFilename: isDev ? 'chunks/[name].css' : 'chunks/[name].[contenthash].css',
|
|
108
|
+
cssChunkFilename: isDev ? 'chunks/[name].css' : 'chunks/[name].[contenthash].css',
|
|
109
|
+
assetModuleFilename: 'assets/[name].[hash][ext]',
|
|
110
|
+
},
|
|
111
|
+
optimization: {
|
|
112
|
+
moduleIds: isDev ? 'named' : 'hashed',
|
|
113
|
+
},
|
|
114
|
+
resolve: {
|
|
115
|
+
...resolveBase,
|
|
116
|
+
alias: { '@': srcDir },
|
|
117
|
+
},
|
|
118
|
+
module: {
|
|
119
|
+
rules: [swcRule(BROWSER_TARGETS), cssRule(), { test: /\.(png|jpe?g|gif|webp|avif|ico|svg|woff2?|ttf|otf)$/i, type: 'asset' }],
|
|
120
|
+
},
|
|
121
|
+
plugins: [
|
|
122
|
+
new ClientPlugin(),
|
|
123
|
+
new rspack.DefinePlugin({ 'process.env': JSON.stringify(publicEnv(isDev)) }),
|
|
124
|
+
...(isDev ? [new rspack.HotModuleReplacementPlugin(), new ReactRefreshRspackPlugin()] : []),
|
|
125
|
+
],
|
|
126
|
+
performance: false,
|
|
127
|
+
};
|
|
128
|
+
const serverConfig = {
|
|
129
|
+
name: 'server',
|
|
130
|
+
mode,
|
|
131
|
+
target: 'node',
|
|
132
|
+
context: rootDir,
|
|
133
|
+
devtool: isDev ? 'source-map' : false,
|
|
134
|
+
entry: { main: rscEntry },
|
|
135
|
+
output: {
|
|
136
|
+
path: join(rootDir, 'dist', 'server'),
|
|
137
|
+
clean: true,
|
|
138
|
+
module: true,
|
|
139
|
+
chunkFormat: 'module',
|
|
140
|
+
chunkLoading: 'import',
|
|
141
|
+
library: { type: 'module' },
|
|
142
|
+
filename: 'main.mjs',
|
|
143
|
+
chunkFilename: 'chunks/[name].mjs',
|
|
144
|
+
publicPath: '/_static/',
|
|
145
|
+
assetModuleFilename: 'assets/[name].[hash][ext]',
|
|
146
|
+
},
|
|
147
|
+
optimization: {
|
|
148
|
+
minimize: false,
|
|
149
|
+
},
|
|
150
|
+
externalsType: 'module-import',
|
|
151
|
+
externals: [
|
|
152
|
+
({ request }, callback) => {
|
|
153
|
+
if (!request ||
|
|
154
|
+
isPathRequest(request) ||
|
|
155
|
+
request.startsWith('@/') ||
|
|
156
|
+
request.startsWith('@rshono/') ||
|
|
157
|
+
request.startsWith('builtin:') ||
|
|
158
|
+
request.includes('!') ||
|
|
159
|
+
BUNDLED_PACKAGES.test(request)) {
|
|
160
|
+
return callback();
|
|
161
|
+
}
|
|
162
|
+
callback(undefined, `module-import ${request}`);
|
|
163
|
+
},
|
|
164
|
+
],
|
|
165
|
+
resolve: {
|
|
166
|
+
...serverResolveBase,
|
|
167
|
+
alias: {
|
|
168
|
+
'@rshono/routes$': routesFile,
|
|
169
|
+
'@rshono/server-app$': serverAppAlias,
|
|
170
|
+
// The one import that decides which platform the server bundle is for. Split on '/' so the
|
|
171
|
+
// preset can declare a POSIX-looking path and still resolve on Windows.
|
|
172
|
+
'@rshono/deploy$': join(FRAMEWORK_DIST, ...preset.runtimeModule.split('/')),
|
|
173
|
+
'@': srcDir,
|
|
174
|
+
},
|
|
175
|
+
},
|
|
176
|
+
module: {
|
|
177
|
+
rules: [
|
|
178
|
+
{
|
|
179
|
+
test: (resource) => pageFiles.has(resource),
|
|
180
|
+
enforce: 'pre',
|
|
181
|
+
use: [{ loader: join(FRAMEWORK_DIST, 'builder', 'page-entry-loader.cjs') }],
|
|
182
|
+
},
|
|
183
|
+
{
|
|
184
|
+
test: /\.[cm]?[tj]sx?$/,
|
|
185
|
+
include: srcDir,
|
|
186
|
+
enforce: 'pre',
|
|
187
|
+
use: [
|
|
188
|
+
{
|
|
189
|
+
loader: join(FRAMEWORK_DIST, 'builder', 'env-shadow-loader.cjs'),
|
|
190
|
+
options: { prelude: `const process = { env: ${JSON.stringify(publicEnv(isDev))} }; `, layer: Layers.ssr },
|
|
191
|
+
},
|
|
192
|
+
],
|
|
193
|
+
},
|
|
194
|
+
swcRule([...(preset.syntaxTargets ?? NODE_TARGETS)]),
|
|
195
|
+
cssRule(),
|
|
196
|
+
{
|
|
197
|
+
test: /\.(png|jpe?g|gif|webp|avif|ico|svg|woff2?|ttf|otf)$/i,
|
|
198
|
+
type: 'asset',
|
|
199
|
+
generator: { emit: false },
|
|
200
|
+
},
|
|
201
|
+
{ resource: ssrEntry, layer: Layers.ssr },
|
|
202
|
+
{
|
|
203
|
+
resource: rscEntry,
|
|
204
|
+
layer: Layers.rsc,
|
|
205
|
+
resolve: { conditionNames: rscConditionNames },
|
|
206
|
+
},
|
|
207
|
+
{
|
|
208
|
+
issuerLayer: Layers.rsc,
|
|
209
|
+
exclude: ssrEntry,
|
|
210
|
+
resolve: { conditionNames: rscConditionNames },
|
|
211
|
+
},
|
|
212
|
+
],
|
|
213
|
+
},
|
|
214
|
+
plugins: [
|
|
215
|
+
pageScanPlugin,
|
|
216
|
+
new ServerPlugin({ onServerComponentChanges }),
|
|
217
|
+
// Bake the framework settings from rshono.config.ts into the bundle (read as __RSHONO_CONFIG__
|
|
218
|
+
// in entry.rsc.tsx) — the server-side counterpart to the client's publicEnv injection.
|
|
219
|
+
new rspack.DefinePlugin({ __RSHONO_CONFIG__: JSON.stringify(resolveServerConfig(config, { isDev })) }),
|
|
220
|
+
],
|
|
221
|
+
performance: false,
|
|
222
|
+
};
|
|
223
|
+
// The platform's own overrides, then the user's hook — so an app can still adjust whatever a
|
|
224
|
+
// preset decided.
|
|
225
|
+
preset.configureServer?.(serverConfig);
|
|
226
|
+
const rspackHook = config.rspack;
|
|
227
|
+
if (rspackHook) {
|
|
228
|
+
return [
|
|
229
|
+
rspackHook(clientConfig, { isServer: false, isDev }) ?? clientConfig,
|
|
230
|
+
rspackHook(serverConfig, { isServer: true, isDev }) ?? serverConfig,
|
|
231
|
+
];
|
|
232
|
+
}
|
|
233
|
+
return [clientConfig, serverConfig];
|
|
234
|
+
}
|
|
235
|
+
//# sourceMappingURL=rspack-config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rspack-config.js","sourceRoot":"","sources":["../../src/builder/rspack-config.ts"],"names":[],"mappings":"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","sourcesContent":["import { rspack, type Compiler, type RspackOptions, type RuleSetRule } from '@rspack/core';\nimport { ReactRefreshRspackPlugin } from '@rspack/plugin-react-refresh';\nimport { existsSync } from 'node:fs';\nimport { join, win32 } from 'node:path';\nimport type { RSHonoConfig } from '../config.js';\nimport type { DeployPreset } from '../deploy/presets.js';\nimport { resolveServerConfig } from '../server/server-config.js';\nimport { scanPageFiles } from './page-files.js';\nimport { publicEnv } from './public-env.js';\n\n// The framework's own compiled output (this file lives in `dist/builder/`), which is what the two\n// compilers consume: the entries and loaders below are the *built* framework, not its TypeScript.\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|rsc-html-stream|hono|@hono\\/node-server)(\\/|$)/;\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 client and server-entry proxies by *absolute path*, which on\n * Windows means a drive letter (`D:\\app\\src\\home.tsx`) instead of a leading slash. Externalizing one\n * of those emits `import(\"D:\\\\app\\\\src\\\\home.tsx\")`, and Node rejects that with\n * `ERR_UNSUPPORTED_ESM_URL_SCHEME` (\"Received protocol 'd:'\") — a build that works everywhere else\n * fails on Windows only. `win32.isAbsolute` also accepts the POSIX form, so it is used on every\n * platform: one code path, 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.\n *\n * It parses *finished* CSS, so a stylesheet needing a PostCSS plugin — Tailwind, most obviously — adds\n * the loader through the {@link RSHonoConfig.rspack} hook, along with the two packages a PostCSS pass\n * takes. The framework stays out of it: `postcss` is a dependency an app that wants one can have, rather\n * than one every app pays for. The Styling section of the README has the four lines involved.\n *\n * A fresh object per compiler, not one shared between them — the hook is handed each config in turn, and\n * an app that reaches in to change this rule should not find it has changed the other bundle's too.\n */\nfunction cssRule(): RuleSetRule {\n return { test: /\\.css$/i, type: 'css/auto' };\n}\n\nexport interface RspackConfigOptions {\n rootDir: string;\n isDev: boolean;\n /** The project's resolved config — supplies the {@link RSHonoConfig.rspack} hook and the runtime settings baked into the server bundle. */\n config: RSHonoConfig;\n /** The platform being built for: decides the `@rshono/deploy` runtime and any server-compiler overrides. */\n preset: DeployPreset;\n onServerComponentChanges?: () => void;\n}\n\nexport function createConfigs(options: RspackConfigOptions): [RspackOptions, RspackOptions] {\n const { rootDir, isDev, config, preset, onServerComponentChanges } = options;\n const srcDir = join(rootDir, 'src');\n const mode = isDev ? 'development' : 'production';\n\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\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: ['node_modules', join(FRAMEWORK_ROOT, 'node_modules')],\n };\n\n // The platform's own resolve conditions, ahead of whatever the Rspack target implies ('...'). This\n // is what hands the server bundle the right build of React and the RSC runtime, both of which ship\n // one per runtime. Left unset for Node, where the 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 devtool: isDev ? 'source-map' : false,\n entry: { main: clientEntry },\n output: {\n path: join(rootDir, 'dist', '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 devtool: isDev ? 'source-map' : false,\n entry: { main: rscEntry },\n output: {\n path: join(rootDir, 'dist', 'server'),\n clean: true,\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: false,\n },\n externalsType: 'module-import',\n externals: [\n ({ request }, 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 ) {\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 // The one import that decides which platform the server bundle is for. Split on '/' so the\n // 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 test: /\\.[cm]?[tj]sx?$/,\n include: srcDir,\n enforce: 'pre',\n use: [\n {\n loader: join(FRAMEWORK_DIST, 'builder', 'env-shadow-loader.cjs'),\n options: { prelude: `const process = { env: ${JSON.stringify(publicEnv(isDev))} }; `, layer: Layers.ssr },\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 // Bake the framework settings from rshono.config.ts into the bundle (read as __RSHONO_CONFIG__\n // in entry.rsc.tsx) — the server-side counterpart to the client's publicEnv injection.\n new rspack.DefinePlugin({ __RSHONO_CONFIG__: JSON.stringify(resolveServerConfig(config, { isDev })) }),\n ],\n performance: false,\n };\n\n // The platform's own overrides, then the user's hook — so an app can still adjust whatever a\n // 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,11 @@
|
|
|
1
|
+
import type { RSHonoConfig } from '../config.js';
|
|
2
|
+
import type { DeployPreset } from '../deploy/presets.js';
|
|
3
|
+
interface BuildOptions {
|
|
4
|
+
rootDir: string;
|
|
5
|
+
config: RSHonoConfig;
|
|
6
|
+
/** The platform to build for, already resolved from the flag, the environment and the config. */
|
|
7
|
+
preset: DeployPreset;
|
|
8
|
+
}
|
|
9
|
+
export declare function buildCommand(options: BuildOptions): Promise<void>;
|
|
10
|
+
export {};
|
|
11
|
+
//# sourceMappingURL=build.d.ts.map
|
|
@@ -0,0 +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;AAED,wBAAsB,YAAY,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CA6DvE"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { rspack } from '@rspack/core';
|
|
2
|
+
import { cpSync, existsSync } from 'node:fs';
|
|
3
|
+
import { rm } from 'node:fs/promises';
|
|
4
|
+
import { join } from 'node:path';
|
|
5
|
+
import { pathToFileURL } from 'node:url';
|
|
6
|
+
import { createConfigs } from '../builder/rspack-config.js';
|
|
7
|
+
import { writeBuildMarker } from '../deploy/build-marker.js';
|
|
8
|
+
import { prerenderStaticRoutes } from '../server/ssg.js';
|
|
9
|
+
export async function buildCommand(options) {
|
|
10
|
+
const { rootDir, config, preset } = options;
|
|
11
|
+
const distDir = join(rootDir, 'dist');
|
|
12
|
+
console.log(' • building client + server bundles…');
|
|
13
|
+
const configs = createConfigs({ rootDir, isDev: false, config, preset });
|
|
14
|
+
const compiler = rspack(configs);
|
|
15
|
+
const stats = await new Promise((resolve, reject) => {
|
|
16
|
+
compiler.run((err, result) => {
|
|
17
|
+
compiler.close(() => {
|
|
18
|
+
if (err)
|
|
19
|
+
reject(err);
|
|
20
|
+
else
|
|
21
|
+
resolve(result);
|
|
22
|
+
});
|
|
23
|
+
});
|
|
24
|
+
});
|
|
25
|
+
if (stats.hasErrors()) {
|
|
26
|
+
console.error(stats.toString({ preset: 'errors-warnings', colors: true }));
|
|
27
|
+
process.exit(1);
|
|
28
|
+
}
|
|
29
|
+
console.log(stats.toString({ preset: 'summary', colors: true }));
|
|
30
|
+
const publicDir = join(rootDir, 'public');
|
|
31
|
+
let distPublicDir = null;
|
|
32
|
+
if (existsSync(publicDir)) {
|
|
33
|
+
distPublicDir = join(distDir, 'public');
|
|
34
|
+
cpSync(publicDir, distPublicDir, { recursive: true });
|
|
35
|
+
console.log(' • copied public/ into dist/public (served at /)');
|
|
36
|
+
}
|
|
37
|
+
const ssgDir = join(distDir, 'ssg');
|
|
38
|
+
await rm(ssgDir, { recursive: true, force: true });
|
|
39
|
+
process.env.RSHONO_PRERENDER = '1';
|
|
40
|
+
const bundle = (await import(pathToFileURL(join(distDir, 'server', 'main.mjs')).href));
|
|
41
|
+
const { written, skipped } = await prerenderStaticRoutes({
|
|
42
|
+
routes: bundle.routes,
|
|
43
|
+
fetch: (request) => bundle.app.fetch(request),
|
|
44
|
+
ssgDir,
|
|
45
|
+
siteUrl: config.siteUrl,
|
|
46
|
+
});
|
|
47
|
+
if (written.length > 0)
|
|
48
|
+
console.log(` • prerendered ${written.length} static page(s): ${written.join(', ')}`);
|
|
49
|
+
if (skipped.length > 0)
|
|
50
|
+
console.log(` • skipped ${skipped.length} (will SSR per request)`);
|
|
51
|
+
// Written before `finalize`, so a preset that copies `dist/` into a platform layout takes it along.
|
|
52
|
+
writeBuildMarker(distDir, preset.name);
|
|
53
|
+
// Last, so a preset arranging its output finds every piece of the build already written.
|
|
54
|
+
await preset.finalize?.({
|
|
55
|
+
rootDir,
|
|
56
|
+
distDir,
|
|
57
|
+
staticDir: join(distDir, 'static'),
|
|
58
|
+
publicDir: distPublicDir,
|
|
59
|
+
ssgDir,
|
|
60
|
+
});
|
|
61
|
+
console.log(` ✓ build complete — ${preset.deployHint}`);
|
|
62
|
+
process.exit(0);
|
|
63
|
+
}
|
|
64
|
+
//# sourceMappingURL=build.js.map
|
|
@@ -0,0 +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 { 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\nexport async function buildCommand(options: BuildOptions): Promise<void> {\n const { rootDir, config, preset } = options;\n const distDir = join(rootDir, 'dist');\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 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 import(pathToFileURL(join(distDir, 'server', 'main.mjs')).href)) as {\n app: Hono;\n routes: readonly Route[];\n };\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 // Written 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 already written.\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 process.exit(0);\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dev.d.ts","sourceRoot":"","sources":["../../src/cli/dev.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAQjD,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,CAuNnE"}
|
package/dist/cli/dev.js
ADDED
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
import { serve } from '@hono/node-server';
|
|
2
|
+
import { rspack } from '@rspack/core';
|
|
3
|
+
import { Hono } from 'hono';
|
|
4
|
+
import { mkdir, rm } from 'node:fs/promises';
|
|
5
|
+
import { join } from 'node:path';
|
|
6
|
+
import { Worker } from 'node:worker_threads';
|
|
7
|
+
import { createConfigs } from '../builder/rspack-config.js';
|
|
8
|
+
import { NODE_PRESET } from '../deploy/presets.js';
|
|
9
|
+
import { SERVER_DEFAULTS } from '../server/server-config.js';
|
|
10
|
+
import { createStaticAssetsApp } from '../server/static.js';
|
|
11
|
+
const WORKER_READY_TIMEOUT_MS = 15_000;
|
|
12
|
+
export async function devCommand(options) {
|
|
13
|
+
const { rootDir, config } = options;
|
|
14
|
+
const port = options.port ?? SERVER_DEFAULTS.port;
|
|
15
|
+
const distDir = join(rootDir, 'dist');
|
|
16
|
+
await rm(distDir, { recursive: true, force: true });
|
|
17
|
+
await mkdir(join(distDir, 'static'), { recursive: true });
|
|
18
|
+
const encoder = new TextEncoder();
|
|
19
|
+
const sseClients = new Set();
|
|
20
|
+
let clientHash;
|
|
21
|
+
const sseChunk = (data) => encoder.encode(`data: ${JSON.stringify(data)}\n\n`);
|
|
22
|
+
function broadcast(message) {
|
|
23
|
+
const chunk = sseChunk(message);
|
|
24
|
+
for (const controller of sseClients) {
|
|
25
|
+
try {
|
|
26
|
+
controller.enqueue(chunk);
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
sseClients.delete(controller);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
let serverComponentsChanged = false;
|
|
34
|
+
const [clientConfig, serverConfig] = createConfigs({
|
|
35
|
+
rootDir,
|
|
36
|
+
isDev: true,
|
|
37
|
+
config,
|
|
38
|
+
// Always Node, whatever `deploy` says: the dev server runs the bundle in a worker thread of this
|
|
39
|
+
// process and fronts it on one port. `deploy` is a property of `build` output, not of developing.
|
|
40
|
+
preset: NODE_PRESET,
|
|
41
|
+
onServerComponentChanges: () => {
|
|
42
|
+
serverComponentsChanged = true;
|
|
43
|
+
},
|
|
44
|
+
});
|
|
45
|
+
const compiler = rspack([clientConfig, serverConfig]);
|
|
46
|
+
const [clientCompiler, serverCompiler] = compiler.compilers;
|
|
47
|
+
let currentWorker = null;
|
|
48
|
+
let workerPort = null;
|
|
49
|
+
let workerGate = createGate();
|
|
50
|
+
let restartChain = Promise.resolve();
|
|
51
|
+
function createGate() {
|
|
52
|
+
const { promise, resolve } = Promise.withResolvers();
|
|
53
|
+
return { promise, open: resolve };
|
|
54
|
+
}
|
|
55
|
+
function spawnWorker() {
|
|
56
|
+
return new Promise((resolve, reject) => {
|
|
57
|
+
const worker = new Worker(join(distDir, 'server', 'main.mjs'), {
|
|
58
|
+
workerData: { port: 0, hostname: '127.0.0.1' },
|
|
59
|
+
execArgv: ['--enable-source-maps'],
|
|
60
|
+
env: process.env,
|
|
61
|
+
});
|
|
62
|
+
const timeout = setTimeout(() => {
|
|
63
|
+
worker.terminate();
|
|
64
|
+
reject(new Error(`server worker did not become ready within ${WORKER_READY_TIMEOUT_MS / 1000}s`));
|
|
65
|
+
}, WORKER_READY_TIMEOUT_MS);
|
|
66
|
+
worker.once('message', (message) => {
|
|
67
|
+
if (message?.type === 'ready' && typeof message.port === 'number') {
|
|
68
|
+
clearTimeout(timeout);
|
|
69
|
+
resolve({ worker, port: message.port });
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
worker.once('error', (error) => {
|
|
73
|
+
clearTimeout(timeout);
|
|
74
|
+
reject(error);
|
|
75
|
+
});
|
|
76
|
+
worker.on('exit', (code) => {
|
|
77
|
+
if (worker === currentWorker && code !== 0) {
|
|
78
|
+
console.error(` ✗ server worker exited with code ${code} — waiting for the next rebuild`);
|
|
79
|
+
currentWorker = null;
|
|
80
|
+
workerGate = createGate();
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
serverCompiler.hooks.invalid.tap('rshono/gate', () => {
|
|
86
|
+
workerGate = createGate();
|
|
87
|
+
});
|
|
88
|
+
serverCompiler.hooks.done.tapPromise('rshono/worker', async (stats) => {
|
|
89
|
+
const gate = workerGate;
|
|
90
|
+
restartChain = restartChain.then(async () => {
|
|
91
|
+
if (stats.hasErrors()) {
|
|
92
|
+
console.error(stats.toString({ preset: 'errors-warnings', colors: true }));
|
|
93
|
+
gate.open({ error: stats.toString({ preset: 'errors-only', colors: false }) });
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
try {
|
|
97
|
+
if (currentWorker) {
|
|
98
|
+
const old = currentWorker;
|
|
99
|
+
currentWorker = null;
|
|
100
|
+
await old.terminate();
|
|
101
|
+
}
|
|
102
|
+
const { worker, port: newPort } = await spawnWorker();
|
|
103
|
+
currentWorker = worker;
|
|
104
|
+
workerPort = newPort;
|
|
105
|
+
gate.open({});
|
|
106
|
+
if (serverComponentsChanged) {
|
|
107
|
+
serverComponentsChanged = false;
|
|
108
|
+
broadcast({ type: 'rsc-update' });
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
catch (error) {
|
|
112
|
+
console.error(' ✗ failed to start server worker:', error);
|
|
113
|
+
gate.open({ error: error instanceof Error ? (error.stack ?? error.message) : String(error) });
|
|
114
|
+
}
|
|
115
|
+
});
|
|
116
|
+
await restartChain;
|
|
117
|
+
});
|
|
118
|
+
clientCompiler.hooks.done.tap('rshono/hmr', (stats) => {
|
|
119
|
+
if (stats.hasErrors()) {
|
|
120
|
+
console.error(stats.toString({ preset: 'errors-warnings', colors: true }));
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
clientHash = stats.hash ?? undefined;
|
|
124
|
+
if (clientHash)
|
|
125
|
+
broadcast({ type: 'client-built', hash: clientHash });
|
|
126
|
+
});
|
|
127
|
+
let firstBuild = true;
|
|
128
|
+
compiler.watch([{}, {}], (err, multiStats) => {
|
|
129
|
+
if (err) {
|
|
130
|
+
console.error(' ✗ build failed:', err);
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
if (multiStats && !multiStats.hasErrors()) {
|
|
134
|
+
const seconds = Math.max(...multiStats.stats.map((s) => (s.endTime ?? 0) - (s.startTime ?? 0))) / 1000;
|
|
135
|
+
console.log(` ${firstBuild ? '✓ built' : '✓ rebuilt'} in ${seconds.toFixed(1)}s`);
|
|
136
|
+
firstBuild = false;
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
const front = new Hono();
|
|
140
|
+
front.route('/_static', createStaticAssetsApp({ root: join(distDir, 'static'), isDev: true }));
|
|
141
|
+
front.get('/_rshono/hmr', (c) => {
|
|
142
|
+
let ctrl;
|
|
143
|
+
const stream = new ReadableStream({
|
|
144
|
+
start(controller) {
|
|
145
|
+
ctrl = controller;
|
|
146
|
+
sseClients.add(controller);
|
|
147
|
+
controller.enqueue(encoder.encode('retry: 500\n\n'));
|
|
148
|
+
controller.enqueue(sseChunk({ type: 'hello', hash: clientHash }));
|
|
149
|
+
},
|
|
150
|
+
cancel() {
|
|
151
|
+
sseClients.delete(ctrl);
|
|
152
|
+
},
|
|
153
|
+
});
|
|
154
|
+
return new Response(stream, {
|
|
155
|
+
headers: {
|
|
156
|
+
'content-type': 'text/event-stream',
|
|
157
|
+
'cache-control': 'no-cache',
|
|
158
|
+
connection: 'keep-alive',
|
|
159
|
+
},
|
|
160
|
+
});
|
|
161
|
+
});
|
|
162
|
+
setInterval(() => {
|
|
163
|
+
for (const controller of sseClients) {
|
|
164
|
+
try {
|
|
165
|
+
controller.enqueue(encoder.encode(': ping\n\n'));
|
|
166
|
+
}
|
|
167
|
+
catch {
|
|
168
|
+
sseClients.delete(controller);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}, 15_000).unref();
|
|
172
|
+
front.all('*', async (c) => {
|
|
173
|
+
const { error } = await workerGate.promise;
|
|
174
|
+
if (error || workerPort === null) {
|
|
175
|
+
return c.text(`Build failed:\n\n${error ?? 'server not running'}`, 500);
|
|
176
|
+
}
|
|
177
|
+
const incoming = new URL(c.req.url);
|
|
178
|
+
const target = `http://127.0.0.1:${workerPort}${incoming.pathname}${incoming.search}`;
|
|
179
|
+
const headers = new Headers(c.req.raw.headers);
|
|
180
|
+
headers.delete('accept-encoding');
|
|
181
|
+
headers.set('x-forwarded-host', incoming.host);
|
|
182
|
+
headers.set('x-forwarded-proto', incoming.protocol.replace(':', ''));
|
|
183
|
+
const hasBody = c.req.method !== 'GET' && c.req.method !== 'HEAD';
|
|
184
|
+
const response = await fetch(target, {
|
|
185
|
+
method: c.req.method,
|
|
186
|
+
headers,
|
|
187
|
+
body: hasBody ? c.req.raw.body : undefined,
|
|
188
|
+
redirect: 'manual',
|
|
189
|
+
// @ts-expect-error — Node fetch requires duplex for streamed bodies
|
|
190
|
+
duplex: hasBody ? 'half' : undefined,
|
|
191
|
+
});
|
|
192
|
+
const responseHeaders = new Headers(response.headers);
|
|
193
|
+
responseHeaders.delete('content-encoding');
|
|
194
|
+
responseHeaders.delete('content-length');
|
|
195
|
+
const location = responseHeaders.get('location');
|
|
196
|
+
const loc = location ? URL.parse(location, target) : null;
|
|
197
|
+
if (loc && loc.host === `127.0.0.1:${workerPort}`) {
|
|
198
|
+
responseHeaders.set('location', `${loc.pathname}${loc.search}${loc.hash}`);
|
|
199
|
+
}
|
|
200
|
+
return new Response(response.body, {
|
|
201
|
+
status: response.status,
|
|
202
|
+
statusText: response.statusText,
|
|
203
|
+
headers: responseHeaders,
|
|
204
|
+
});
|
|
205
|
+
});
|
|
206
|
+
serve({ fetch: front.fetch, port, hostname: '127.0.0.1' }, (info) => {
|
|
207
|
+
console.log(` ➜ rshono dev server: http://localhost:${info.port}`);
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
//# sourceMappingURL=dev.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dev.js","sourceRoot":"","sources":["../../src/cli/dev.ts"],"names":[],"mappings":"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","sourcesContent":["import { serve } from '@hono/node-server';\nimport { rspack, type Stats } from '@rspack/core';\nimport { Hono } from 'hono';\nimport { mkdir, rm } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport { Worker } from 'node:worker_threads';\nimport { createConfigs } from '../builder/rspack-config.js';\nimport type { RSHonoConfig } from '../config.js';\nimport { NODE_PRESET } from '../deploy/presets.js';\nimport type { DevMessage } from '../runtime/dev-protocol.js';\nimport { SERVER_DEFAULTS } from '../server/server-config.js';\nimport { createStaticAssetsApp } from '../server/static.js';\n\nconst WORKER_READY_TIMEOUT_MS = 15_000;\n\ninterface DevOptions {\n rootDir: string;\n port?: number;\n config: RSHonoConfig;\n}\n\nexport async function devCommand(options: DevOptions): Promise<void> {\n const { rootDir, config } = options;\n const port = options.port ?? SERVER_DEFAULTS.port;\n const distDir = join(rootDir, 'dist');\n\n await rm(distDir, { recursive: true, force: true });\n await mkdir(join(distDir, 'static'), { recursive: true });\n\n const encoder = new TextEncoder();\n const sseClients = new Set<ReadableStreamDefaultController<Uint8Array>>();\n let clientHash: string | undefined;\n\n const sseChunk = (data: unknown) => encoder.encode(`data: ${JSON.stringify(data)}\\n\\n`);\n function broadcast(message: DevMessage): void {\n const chunk = sseChunk(message);\n for (const controller of sseClients) {\n try {\n controller.enqueue(chunk);\n } catch {\n sseClients.delete(controller);\n }\n }\n }\n\n let serverComponentsChanged = false;\n const [clientConfig, serverConfig] = createConfigs({\n rootDir,\n isDev: true,\n config,\n // Always Node, whatever `deploy` says: the dev server runs the bundle in a worker thread of this\n // process and fronts it on one port. `deploy` is a property of `build` output, not of developing.\n preset: NODE_PRESET,\n onServerComponentChanges: () => {\n serverComponentsChanged = true;\n },\n });\n const compiler = rspack([clientConfig, serverConfig]);\n const [clientCompiler, serverCompiler] = compiler.compilers;\n\n let currentWorker: Worker | null = null;\n let workerPort: number | null = null;\n let workerGate = createGate();\n let restartChain: Promise<void> = Promise.resolve();\n\n function createGate() {\n const { promise, resolve } = Promise.withResolvers<{ error?: string }>();\n return { promise, open: resolve };\n }\n\n function spawnWorker(): Promise<{ worker: Worker; port: number }> {\n return new Promise((resolve, reject) => {\n const worker = new Worker(join(distDir, 'server', 'main.mjs'), {\n workerData: { port: 0, hostname: '127.0.0.1' },\n execArgv: ['--enable-source-maps'],\n env: process.env as Record<string, string>,\n });\n const timeout = setTimeout(() => {\n worker.terminate();\n reject(new Error(`server worker did not become ready within ${WORKER_READY_TIMEOUT_MS / 1000}s`));\n }, WORKER_READY_TIMEOUT_MS);\n\n worker.once('message', (message: { type?: string; port?: number }) => {\n if (message?.type === 'ready' && typeof message.port === 'number') {\n clearTimeout(timeout);\n resolve({ worker, port: message.port });\n }\n });\n worker.once('error', (error) => {\n clearTimeout(timeout);\n reject(error);\n });\n worker.on('exit', (code) => {\n if (worker === currentWorker && code !== 0) {\n console.error(` ✗ server worker exited with code ${code} — waiting for the next rebuild`);\n currentWorker = null;\n workerGate = createGate();\n }\n });\n });\n }\n\n serverCompiler.hooks.invalid.tap('rshono/gate', () => {\n workerGate = createGate();\n });\n\n serverCompiler.hooks.done.tapPromise('rshono/worker', async (stats: Stats) => {\n const gate = workerGate;\n restartChain = restartChain.then(async () => {\n if (stats.hasErrors()) {\n console.error(stats.toString({ preset: 'errors-warnings', colors: true }));\n gate.open({ error: stats.toString({ preset: 'errors-only', colors: false }) });\n return;\n }\n try {\n if (currentWorker) {\n const old = currentWorker;\n currentWorker = null;\n await old.terminate();\n }\n const { worker, port: newPort } = await spawnWorker();\n currentWorker = worker;\n workerPort = newPort;\n gate.open({});\n if (serverComponentsChanged) {\n serverComponentsChanged = false;\n broadcast({ type: 'rsc-update' });\n }\n } catch (error) {\n console.error(' ✗ failed to start server worker:', error);\n gate.open({ error: error instanceof Error ? (error.stack ?? error.message) : String(error) });\n }\n });\n await restartChain;\n });\n\n clientCompiler.hooks.done.tap('rshono/hmr', (stats: Stats) => {\n if (stats.hasErrors()) {\n console.error(stats.toString({ preset: 'errors-warnings', colors: true }));\n return;\n }\n clientHash = stats.hash ?? undefined;\n if (clientHash) broadcast({ type: 'client-built', hash: clientHash });\n });\n\n let firstBuild = true;\n compiler.watch([{}, {}] as never, (err, multiStats) => {\n if (err) {\n console.error(' ✗ build failed:', err);\n return;\n }\n if (multiStats && !multiStats.hasErrors()) {\n const seconds = Math.max(...multiStats.stats.map((s) => (s.endTime ?? 0) - (s.startTime ?? 0))) / 1000;\n console.log(` ${firstBuild ? '✓ built' : '✓ rebuilt'} in ${seconds.toFixed(1)}s`);\n firstBuild = false;\n }\n });\n\n const front = new Hono();\n\n front.route('/_static', createStaticAssetsApp({ root: join(distDir, 'static'), isDev: true }));\n\n front.get('/_rshono/hmr', (c) => {\n let ctrl: ReadableStreamDefaultController<Uint8Array>;\n const stream = new ReadableStream<Uint8Array>({\n start(controller) {\n ctrl = controller;\n sseClients.add(controller);\n controller.enqueue(encoder.encode('retry: 500\\n\\n'));\n controller.enqueue(sseChunk({ type: 'hello', hash: clientHash } satisfies DevMessage));\n },\n cancel() {\n sseClients.delete(ctrl);\n },\n });\n return new Response(stream, {\n headers: {\n 'content-type': 'text/event-stream',\n 'cache-control': 'no-cache',\n connection: 'keep-alive',\n },\n });\n });\n\n setInterval(() => {\n for (const controller of sseClients) {\n try {\n controller.enqueue(encoder.encode(': ping\\n\\n'));\n } catch {\n sseClients.delete(controller);\n }\n }\n }, 15_000).unref();\n\n front.all('*', async (c) => {\n const { error } = await workerGate.promise;\n if (error || workerPort === null) {\n return c.text(`Build failed:\\n\\n${error ?? 'server not running'}`, 500);\n }\n\n const incoming = new URL(c.req.url);\n const target = `http://127.0.0.1:${workerPort}${incoming.pathname}${incoming.search}`;\n\n const headers = new Headers(c.req.raw.headers);\n headers.delete('accept-encoding');\n headers.set('x-forwarded-host', incoming.host);\n headers.set('x-forwarded-proto', incoming.protocol.replace(':', ''));\n\n const hasBody = c.req.method !== 'GET' && c.req.method !== 'HEAD';\n const response = await fetch(target, {\n method: c.req.method,\n headers,\n body: hasBody ? c.req.raw.body : undefined,\n redirect: 'manual',\n // @ts-expect-error — Node fetch requires duplex for streamed bodies\n duplex: hasBody ? 'half' : undefined,\n });\n\n const responseHeaders = new Headers(response.headers);\n responseHeaders.delete('content-encoding');\n responseHeaders.delete('content-length');\n const location = responseHeaders.get('location');\n const loc = location ? URL.parse(location, target) : null;\n if (loc && loc.host === `127.0.0.1:${workerPort}`) {\n responseHeaders.set('location', `${loc.pathname}${loc.search}${loc.hash}`);\n }\n return new Response(response.body, {\n status: response.status,\n statusText: response.statusText,\n headers: responseHeaders,\n });\n });\n\n serve({ fetch: front.fetch, port, hostname: '127.0.0.1' }, (info) => {\n console.log(` ➜ rshono dev server: http://localhost:${info.port}`);\n });\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":""}
|