waku 0.12.0 → 0.13.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 +5 -2
- package/dist/cjs/cli.js +5 -5
- package/dist/cjs/client.js +1 -1
- package/dist/cjs/config.js +25 -3
- package/dist/cjs/lib/builder.js +25 -34
- package/dist/cjs/lib/config.js +16 -9
- package/dist/cjs/lib/middleware/devServer.js +22 -20
- package/dist/cjs/lib/middleware/rsc/utils.js +11 -0
- package/dist/cjs/lib/middleware/rsc/worker-api.js +51 -26
- package/dist/cjs/lib/middleware/rsc/worker-impl.js +54 -55
- package/dist/cjs/lib/middleware/rsc.js +29 -16
- package/dist/cjs/lib/middleware/ssr/utils.js +23 -11
- package/dist/cjs/lib/middleware/ssr.js +3 -3
- package/dist/cjs/lib/vite-plugin/rsc-index-plugin.js +1 -1
- package/dist/cjs/main.js +4 -0
- package/dist/cjs/router/client.js +2 -2
- package/dist/cjs/router/server.js +2 -2
- package/dist/cjs/server.js +22 -0
- package/dist/cli.js +5 -5
- package/dist/client.js +1 -1
- package/dist/config.d.ts +52 -2
- package/dist/config.js +13 -0
- package/dist/lib/builder.js +25 -34
- package/dist/lib/config.d.ts +4 -2
- package/dist/lib/config.js +16 -9
- package/dist/lib/middleware/devServer.js +23 -21
- package/dist/lib/middleware/rsc/utils.d.ts +1 -0
- package/dist/lib/middleware/rsc/utils.js +8 -0
- package/dist/lib/middleware/rsc/worker-api.d.ts +9 -2
- package/dist/lib/middleware/rsc/worker-api.js +51 -26
- package/dist/lib/middleware/rsc/worker-impl.js +55 -56
- package/dist/lib/middleware/rsc.d.ts +4 -2
- package/dist/lib/middleware/rsc.js +29 -16
- package/dist/lib/middleware/ssr/utils.d.ts +1 -1
- package/dist/lib/middleware/ssr/utils.js +24 -12
- package/dist/lib/middleware/ssr.d.ts +1 -1
- package/dist/lib/middleware/ssr.js +3 -3
- package/dist/lib/vite-plugin/rsc-index-plugin.js +1 -1
- package/dist/main.d.ts +1 -0
- package/dist/main.js +1 -0
- package/dist/router/client.js +1 -1
- package/dist/router/server.d.ts +1 -1
- package/dist/router/server.js +2 -2
- package/dist/server.d.ts +7 -2
- package/dist/server.js +17 -0
- package/package.json +25 -21
- package/src/cli.ts +13 -5
- package/src/client.ts +1 -1
- package/src/config.ts +68 -5
- package/src/lib/builder.ts +39 -44
- package/src/lib/config.ts +18 -7
- package/src/lib/middleware/devServer.ts +20 -16
- package/src/lib/middleware/rsc/utils.ts +9 -0
- package/src/lib/middleware/rsc/worker-api.ts +63 -31
- package/src/lib/middleware/rsc/worker-impl.ts +92 -68
- package/src/lib/middleware/rsc.ts +30 -13
- package/src/lib/middleware/ssr/utils.ts +30 -13
- package/src/lib/middleware/ssr.ts +11 -7
- package/src/lib/vite-plugin/rsc-index-plugin.ts +1 -1
- package/src/main.ts +1 -0
- package/src/router/client.ts +1 -1
- package/src/router/server.ts +4 -4
- package/src/server.ts +30 -3
package/README.md
CHANGED
|
@@ -10,6 +10,8 @@ Minimalistic React Framework
|
|
|
10
10
|
|
|
11
11
|
## How to start a new project
|
|
12
12
|
|
|
13
|
+
Minimum requirement: Node.js 18
|
|
14
|
+
|
|
13
15
|
```bash
|
|
14
16
|
npm create waku@latest
|
|
15
17
|
```
|
|
@@ -29,9 +31,8 @@ pnpm create waku # pnpm not working for now
|
|
|
29
31
|
- [x] `waku start` (production server)
|
|
30
32
|
- [x] Exportable build
|
|
31
33
|
- [x] Static site generation
|
|
32
|
-
- [ ] Client rendering fallback
|
|
33
34
|
- [x] Opt-in router (reference implementation)
|
|
34
|
-
- [
|
|
35
|
+
- [x] Opt-in SSR (HTML generation only)
|
|
35
36
|
|
|
36
37
|
## Tweets
|
|
37
38
|
|
|
@@ -72,6 +73,8 @@ pnpm create waku # pnpm not working for now
|
|
|
72
73
|
- https://twitter.com/dai_shi/status/1664286329763684353
|
|
73
74
|
- https://twitter.com/dai_shi/status/1664989534889861123
|
|
74
75
|
- https://twitter.com/dai_shi/status/1667545252654366721
|
|
76
|
+
- https://twitter.com/dai_shi/status/1670650381762961408
|
|
77
|
+
- https://twitter.com/dai_shi/status/1671161795061628930
|
|
75
78
|
|
|
76
79
|
</details>
|
|
77
80
|
|
package/dist/cjs/cli.js
CHANGED
|
@@ -114,12 +114,12 @@ async function runDev(options) {
|
|
|
114
114
|
const { devServer } = await Promise.resolve().then(()=>/*#__PURE__*/ _interop_require_wildcard(require("./lib/middleware/devServer.js")));
|
|
115
115
|
const app = express();
|
|
116
116
|
app.use(rsc({
|
|
117
|
-
|
|
117
|
+
command: "dev"
|
|
118
118
|
}));
|
|
119
119
|
if (options?.ssr) {
|
|
120
120
|
const { ssr } = await Promise.resolve().then(()=>/*#__PURE__*/ _interop_require_wildcard(require("./lib/middleware/ssr.js")));
|
|
121
121
|
app.use(ssr({
|
|
122
|
-
|
|
122
|
+
command: "dev"
|
|
123
123
|
}));
|
|
124
124
|
}
|
|
125
125
|
app.use(devServer());
|
|
@@ -139,15 +139,15 @@ async function runStart(options) {
|
|
|
139
139
|
const { rsc } = await Promise.resolve().then(()=>/*#__PURE__*/ _interop_require_wildcard(require("./lib/middleware/rsc.js")));
|
|
140
140
|
const app = express();
|
|
141
141
|
app.use(rsc({
|
|
142
|
-
|
|
142
|
+
command: "start"
|
|
143
143
|
}));
|
|
144
144
|
if (options?.ssr) {
|
|
145
145
|
const { ssr } = await Promise.resolve().then(()=>/*#__PURE__*/ _interop_require_wildcard(require("./lib/middleware/ssr.js")));
|
|
146
146
|
app.use(ssr({
|
|
147
|
-
|
|
147
|
+
command: "start"
|
|
148
148
|
}));
|
|
149
149
|
}
|
|
150
|
-
app.use(express.static(_nodepath.default.join(config.root, config.framework.
|
|
150
|
+
app.use(express.static(_nodepath.default.join(config.root, config.framework.distDir, config.framework.publicDir)));
|
|
151
151
|
express.static.mime.default_type = "";
|
|
152
152
|
const port = process.env.PORT || 8080;
|
|
153
153
|
app.listen(port, ()=>{
|
package/dist/cjs/client.js
CHANGED
package/dist/cjs/config.js
CHANGED
|
@@ -2,12 +2,34 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", {
|
|
3
3
|
value: true
|
|
4
4
|
});
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
function _export(target, all) {
|
|
6
|
+
for(var name in all)Object.defineProperty(target, name, {
|
|
7
|
+
enumerable: true,
|
|
8
|
+
get: all[name]
|
|
9
|
+
});
|
|
10
|
+
}
|
|
11
|
+
_export(exports, {
|
|
12
|
+
defineConfig: function() {
|
|
8
13
|
return defineConfig;
|
|
14
|
+
},
|
|
15
|
+
unstable_setRootDir: function() {
|
|
16
|
+
return unstable_setRootDir;
|
|
17
|
+
},
|
|
18
|
+
unstable_rootDir: function() {
|
|
19
|
+
return unstable_rootDir;
|
|
9
20
|
}
|
|
10
21
|
});
|
|
11
22
|
function defineConfig(config) {
|
|
12
23
|
return config;
|
|
13
24
|
}
|
|
25
|
+
function unstable_setRootDir(root) {
|
|
26
|
+
// FIXME it would be better to use a module variable or async local storage
|
|
27
|
+
globalThis.WAKU_CONFIG_ROOT_DIR = root;
|
|
28
|
+
}
|
|
29
|
+
function unstable_rootDir() {
|
|
30
|
+
const resolvedRootDir = globalThis.WAKU_CONFIG_ROOT_DIR;
|
|
31
|
+
if (typeof resolvedRootDir !== "string") {
|
|
32
|
+
throw new Error("rootDir() is called before resolved");
|
|
33
|
+
}
|
|
34
|
+
return resolvedRootDir;
|
|
35
|
+
}
|
package/dist/cjs/lib/builder.js
CHANGED
|
@@ -10,7 +10,6 @@ Object.defineProperty(exports, "build", {
|
|
|
10
10
|
});
|
|
11
11
|
const _nodepath = /*#__PURE__*/ _interop_require_default(require("node:path"));
|
|
12
12
|
const _nodefs = /*#__PURE__*/ _interop_require_default(require("node:fs"));
|
|
13
|
-
const _nodemodule = require("node:module");
|
|
14
13
|
const _nodecrypto = require("node:crypto");
|
|
15
14
|
const _vite = require("vite");
|
|
16
15
|
const _pluginreact = /*#__PURE__*/ _interop_require_default(require("@vitejs/plugin-react"));
|
|
@@ -101,8 +100,10 @@ const buildServerBundle = async (config, entriesFile, clientEntryFiles, serverEn
|
|
|
101
100
|
"react-server"
|
|
102
101
|
]
|
|
103
102
|
},
|
|
103
|
+
publicDir: false,
|
|
104
104
|
build: {
|
|
105
105
|
ssr: true,
|
|
106
|
+
outDir: config.framework.distDir,
|
|
106
107
|
rollupOptions: {
|
|
107
108
|
onwarn,
|
|
108
109
|
input: {
|
|
@@ -138,7 +139,7 @@ const buildServerBundle = async (config, entriesFile, clientEntryFiles, serverEn
|
|
|
138
139
|
return serverBuildOutput;
|
|
139
140
|
};
|
|
140
141
|
const buildClientBundle = async (config, clientEntryFiles)=>{
|
|
141
|
-
const indexHtmlFile = _nodepath.default.join(config.root, config.framework.indexHtml);
|
|
142
|
+
const indexHtmlFile = _nodepath.default.join(config.root, config.framework.srcDir, config.framework.indexHtml);
|
|
142
143
|
const clientBuildOutput = await (0, _vite.build)({
|
|
143
144
|
..._config.configFileConfig,
|
|
144
145
|
plugins: [
|
|
@@ -146,8 +147,9 @@ const buildClientBundle = async (config, clientEntryFiles)=>{
|
|
|
146
147
|
(0, _pluginreact.default)(),
|
|
147
148
|
(0, _rscindexplugin.rscIndexPlugin)()
|
|
148
149
|
],
|
|
150
|
+
root: _nodepath.default.join(config.root, config.framework.srcDir),
|
|
149
151
|
build: {
|
|
150
|
-
outDir: _nodepath.default.join(config.
|
|
152
|
+
outDir: _nodepath.default.join(config.root, config.framework.distDir, config.framework.publicDir),
|
|
151
153
|
rollupOptions: {
|
|
152
154
|
onwarn,
|
|
153
155
|
input: {
|
|
@@ -189,22 +191,24 @@ const emitRscFiles = async (config)=>{
|
|
|
189
191
|
return Array.from(idSet || []);
|
|
190
192
|
};
|
|
191
193
|
const rscFileSet = new Set(); // XXX could be implemented better
|
|
192
|
-
await Promise.all(Object.entries(buildConfig).map(async ([, { elements }])=>{
|
|
194
|
+
await Promise.all(Object.entries(buildConfig).map(async ([, { elements, ctx }])=>{
|
|
193
195
|
for (const [rscId, props] of elements || []){
|
|
194
196
|
// FIXME we blindly expect JSON.stringify usage is deterministic
|
|
195
197
|
const serializedProps = JSON.stringify(props);
|
|
196
198
|
const searchParams = new URLSearchParams();
|
|
197
199
|
searchParams.set("props", serializedProps);
|
|
198
|
-
const destFile = _nodepath.default.join(config.root, config.
|
|
200
|
+
const destFile = _nodepath.default.join(config.root, config.framework.distDir, config.framework.publicDir, config.framework.rscPrefix + decodeURIComponent(rscId), decodeURIComponent(`${searchParams}`));
|
|
199
201
|
if (!rscFileSet.has(destFile)) {
|
|
200
202
|
rscFileSet.add(destFile);
|
|
201
203
|
_nodefs.default.mkdirSync(_nodepath.default.dirname(destFile), {
|
|
202
204
|
recursive: true
|
|
203
205
|
});
|
|
204
|
-
const pipeable = (0, _workerapi.renderRSC)({
|
|
206
|
+
const [pipeable] = await (0, _workerapi.renderRSC)({
|
|
205
207
|
rscId,
|
|
206
208
|
props
|
|
207
209
|
}, {
|
|
210
|
+
command: "build",
|
|
211
|
+
ctx,
|
|
208
212
|
moduleIdCallback: (id)=>addClientModule(rscId, serializedProps, id)
|
|
209
213
|
});
|
|
210
214
|
await new Promise((resolve, reject)=>{
|
|
@@ -222,27 +226,30 @@ const emitRscFiles = async (config)=>{
|
|
|
222
226
|
rscFiles: Array.from(rscFileSet)
|
|
223
227
|
};
|
|
224
228
|
};
|
|
225
|
-
const renderHtml = async (config, pathStr, htmlStr)=>{
|
|
226
|
-
const ssrConfig = await (0, _workerapi.getSsrConfigRSC)(pathStr);
|
|
229
|
+
const renderHtml = async (config, pathStr, htmlStr, ctx)=>{
|
|
230
|
+
const ssrConfig = await (0, _workerapi.getSsrConfigRSC)(pathStr, "build");
|
|
227
231
|
if (!ssrConfig) {
|
|
228
232
|
return null;
|
|
229
233
|
}
|
|
230
234
|
const { splitHTML, getFallback } = config.framework.ssr;
|
|
231
235
|
const [rscId, props] = ssrConfig.element;
|
|
232
|
-
const pipeable = (0, _workerapi.renderRSC)({
|
|
236
|
+
const [pipeable] = await (0, _workerapi.renderRSC)({
|
|
233
237
|
rscId,
|
|
234
238
|
props
|
|
239
|
+
}, {
|
|
240
|
+
command: "build",
|
|
241
|
+
ctx
|
|
235
242
|
});
|
|
236
243
|
return (0, _utils1.renderHtmlToReadable)(htmlStr, pipeable, splitHTML, getFallback);
|
|
237
244
|
};
|
|
238
245
|
const emitHtmlFiles = async (config, buildConfig, getClientModules)=>{
|
|
239
246
|
const basePrefix = config.base + config.framework.rscPrefix;
|
|
240
|
-
const publicIndexHtmlFile = _nodepath.default.join(config.root, config.
|
|
247
|
+
const publicIndexHtmlFile = _nodepath.default.join(config.root, config.framework.distDir, config.framework.publicDir, config.framework.indexHtml);
|
|
241
248
|
const publicIndexHtml = _nodefs.default.readFileSync(publicIndexHtmlFile, {
|
|
242
249
|
encoding: "utf8"
|
|
243
250
|
});
|
|
244
|
-
const htmlFiles = await Promise.all(Object.entries(buildConfig).map(async ([pathStr, { elements, customCode, skipSsr }])=>{
|
|
245
|
-
const destFile = _nodepath.default.join(config.root, config.
|
|
251
|
+
const htmlFiles = await Promise.all(Object.entries(buildConfig).map(async ([pathStr, { elements, customCode, ctx, skipSsr }])=>{
|
|
252
|
+
const destFile = _nodepath.default.join(config.root, config.framework.distDir, config.framework.publicDir, pathStr, pathStr.endsWith("/") ? "index.html" : "");
|
|
246
253
|
let data = "";
|
|
247
254
|
if (_nodefs.default.existsSync(destFile)) {
|
|
248
255
|
data = _nodefs.default.readFileSync(destFile, {
|
|
@@ -272,9 +279,9 @@ const emitHtmlFiles = async (config, buildConfig, getClientModules)=>{
|
|
|
272
279
|
const code = (0, _utils.generatePrefetchCode)(basePrefix, elementsForPrefetch, moduleIdsForPrefetch) + (customCode || "");
|
|
273
280
|
if (code) {
|
|
274
281
|
// HACK is this too naive to inject script code?
|
|
275
|
-
data = data.replace(/<\/
|
|
282
|
+
data = data.replace(/<\/head>/, `<script>${code}</script></head>`);
|
|
276
283
|
}
|
|
277
|
-
const htmlReadable = !skipSsr && await renderHtml(config, pathStr, data);
|
|
284
|
+
const htmlReadable = !skipSsr && await renderHtml(config, pathStr, data, ctx);
|
|
278
285
|
if (htmlReadable) {
|
|
279
286
|
await new Promise((resolve, reject)=>{
|
|
280
287
|
const stream = _nodefs.default.createWriteStream(destFile);
|
|
@@ -293,25 +300,10 @@ const emitHtmlFiles = async (config, buildConfig, getClientModules)=>{
|
|
|
293
300
|
htmlFiles
|
|
294
301
|
};
|
|
295
302
|
};
|
|
296
|
-
const emitPackageJson = (config)=>{
|
|
297
|
-
const require1 = (0, _nodemodule.createRequire)(require("url").pathToFileURL(__filename).toString());
|
|
298
|
-
const origPackageJson = require1(_nodepath.default.join(config.root, "package.json"));
|
|
299
|
-
const packageJson = {
|
|
300
|
-
name: origPackageJson.name,
|
|
301
|
-
version: origPackageJson.version,
|
|
302
|
-
private: true,
|
|
303
|
-
type: "module",
|
|
304
|
-
scripts: {
|
|
305
|
-
start: "waku start"
|
|
306
|
-
},
|
|
307
|
-
dependencies: origPackageJson.dependencies
|
|
308
|
-
};
|
|
309
|
-
_nodefs.default.writeFileSync(_nodepath.default.join(config.root, config.build.outDir, "package.json"), JSON.stringify(packageJson, null, 2));
|
|
310
|
-
};
|
|
311
303
|
const emitVercelOutput = (config, clientBuildOutput, rscFiles, htmlFiles)=>{
|
|
312
|
-
const clientFiles = clientBuildOutput.output.map(({ fileName })=>_nodepath.default.join(config.root, config.
|
|
313
|
-
const srcDir = _nodepath.default.join(config.root, config.
|
|
314
|
-
const dstDir = _nodepath.default.join(config.root, config.
|
|
304
|
+
const clientFiles = clientBuildOutput.output.map(({ fileName })=>_nodepath.default.join(config.root, config.framework.distDir, config.framework.publicDir, fileName));
|
|
305
|
+
const srcDir = _nodepath.default.join(config.root, config.framework.distDir, config.framework.publicDir);
|
|
306
|
+
const dstDir = _nodepath.default.join(config.root, config.framework.distDir, ".vercel", "output");
|
|
315
307
|
for (const file of [
|
|
316
308
|
...clientFiles,
|
|
317
309
|
...rscFiles,
|
|
@@ -364,13 +356,12 @@ const resolveFileName = (fname)=>{
|
|
|
364
356
|
};
|
|
365
357
|
async function build() {
|
|
366
358
|
const config = await (0, _config.resolveConfig)("build");
|
|
367
|
-
const entriesFile = resolveFileName(_nodepath.default.join(config.root, config.framework.entriesJs));
|
|
359
|
+
const entriesFile = resolveFileName(_nodepath.default.join(config.root, config.framework.srcDir, config.framework.entriesJs));
|
|
368
360
|
const { clientEntryFiles, serverEntryFiles } = await analyzeEntries(entriesFile);
|
|
369
361
|
await buildServerBundle(config, entriesFile, clientEntryFiles, serverEntryFiles);
|
|
370
362
|
const clientBuildOutput = await buildClientBundle(config, clientEntryFiles);
|
|
371
363
|
const { buildConfig, getClientModules, rscFiles } = await emitRscFiles(config);
|
|
372
364
|
const { htmlFiles } = await emitHtmlFiles(config, buildConfig, getClientModules);
|
|
373
|
-
emitPackageJson(config);
|
|
374
365
|
// https://vercel.com/docs/build-output-api/v3
|
|
375
366
|
// So far, only static sites are supported.
|
|
376
367
|
emitVercelOutput(config, clientBuildOutput, rscFiles, htmlFiles);
|
package/dist/cjs/lib/config.js
CHANGED
|
@@ -18,16 +18,21 @@ _export(exports, {
|
|
|
18
18
|
});
|
|
19
19
|
const _vite = require("vite");
|
|
20
20
|
const splitHTML = (htmlStr)=>{
|
|
21
|
-
const
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
21
|
+
const P1 = [
|
|
22
|
+
"<!--placeholder1-->\\s*<div[^>]*>",
|
|
23
|
+
"</div>\\s*<!--/placeholder1-->"
|
|
24
|
+
];
|
|
25
|
+
const P2 = [
|
|
26
|
+
"<!--placeholder2-->",
|
|
27
|
+
"<!--/placeholder2-->"
|
|
28
|
+
];
|
|
29
|
+
const anyRE = "[\\s\\S]*";
|
|
30
|
+
const match = htmlStr.match(new RegExp(// prettier-ignore
|
|
31
|
+
"^(" + anyRE + P1[0] + ")" + anyRE + "(" + P1[1] + anyRE + P2[0] + ")" + anyRE + "(" + P2[1] + anyRE + ")$"));
|
|
32
|
+
if (match?.length !== 1 + 3) {
|
|
25
33
|
throw new Error("Failed to split HTML");
|
|
26
34
|
}
|
|
27
|
-
return
|
|
28
|
-
splitted[0] + startStr,
|
|
29
|
-
endStr + splitted[1]
|
|
30
|
-
];
|
|
35
|
+
return match.slice(1);
|
|
31
36
|
};
|
|
32
37
|
const getFallback = (id)=>{
|
|
33
38
|
if (id.endsWith("#Waku_SSR_Capable_Link")) {
|
|
@@ -42,9 +47,11 @@ async function resolveConfig(command) {
|
|
|
42
47
|
const origConfig = await (0, _vite.resolveConfig)(configFileConfig, command);
|
|
43
48
|
const origFramework = origConfig.framework;
|
|
44
49
|
const framework = {
|
|
50
|
+
srcDir: "src",
|
|
51
|
+
distDir: "dist",
|
|
52
|
+
publicDir: "public",
|
|
45
53
|
indexHtml: "index.html",
|
|
46
54
|
entriesJs: "entries.js",
|
|
47
|
-
outPublic: "public",
|
|
48
55
|
rscPrefix: "RSC/",
|
|
49
56
|
...origFramework,
|
|
50
57
|
ssr: {
|
|
@@ -20,27 +20,29 @@ function _interop_require_default(obj) {
|
|
|
20
20
|
};
|
|
21
21
|
}
|
|
22
22
|
function devServer() {
|
|
23
|
-
const
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
23
|
+
const configPromise = (0, _config.resolveConfig)("serve");
|
|
24
|
+
const vitePromise = configPromise.then((config)=>(0, _vite.createServer)({
|
|
25
|
+
..._config.configFileConfig,
|
|
26
|
+
root: _nodepath.default.join(config.root, config.framework.srcDir),
|
|
27
|
+
optimizeDeps: {
|
|
28
|
+
include: [
|
|
29
|
+
"react-server-dom-webpack/client"
|
|
30
|
+
],
|
|
31
|
+
// FIXME without this, waku router has dual module hazard,
|
|
32
|
+
// and "Uncaught Error: Missing Router" happens.
|
|
33
|
+
exclude: [
|
|
34
|
+
"waku"
|
|
35
|
+
]
|
|
36
|
+
},
|
|
37
|
+
plugins: [
|
|
38
|
+
// @ts-expect-error This expression is not callable.
|
|
39
|
+
(0, _pluginreact.default)(),
|
|
40
|
+
(0, _rscindexplugin.rscIndexPlugin)()
|
|
28
41
|
],
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
]
|
|
34
|
-
},
|
|
35
|
-
plugins: [
|
|
36
|
-
// @ts-expect-error This expression is not callable.
|
|
37
|
-
(0, _pluginreact.default)(),
|
|
38
|
-
(0, _rscindexplugin.rscIndexPlugin)()
|
|
39
|
-
],
|
|
40
|
-
server: {
|
|
41
|
-
middlewareMode: true
|
|
42
|
-
}
|
|
43
|
-
});
|
|
42
|
+
server: {
|
|
43
|
+
middlewareMode: true
|
|
44
|
+
}
|
|
45
|
+
}));
|
|
44
46
|
vitePromise.then((vite)=>{
|
|
45
47
|
(0, _workerapi.registerReloadCallback)((type)=>vite.ws.send({
|
|
46
48
|
type
|
|
@@ -20,6 +20,9 @@ _export(exports, {
|
|
|
20
20
|
},
|
|
21
21
|
transformRsfId: function() {
|
|
22
22
|
return transformRsfId;
|
|
23
|
+
},
|
|
24
|
+
deepFreeze: function() {
|
|
25
|
+
return deepFreeze;
|
|
23
26
|
}
|
|
24
27
|
});
|
|
25
28
|
const _nodebuffer = require("node:buffer");
|
|
@@ -75,3 +78,11 @@ const transformRsfId = (prefixToRemove)=>new _nodestream.Transform({
|
|
|
75
78
|
callback(null, changed ? _nodebuffer.Buffer.from(lines.join("\n")) : chunk);
|
|
76
79
|
}
|
|
77
80
|
});
|
|
81
|
+
const deepFreeze = (x)=>{
|
|
82
|
+
if (typeof x === "object" && x !== null) {
|
|
83
|
+
Object.freeze(x);
|
|
84
|
+
for (const value of Object.values(x)){
|
|
85
|
+
deepFreeze(value);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
};
|
|
@@ -60,32 +60,56 @@ function shutdown() {
|
|
|
60
60
|
let nextId = 1;
|
|
61
61
|
function renderRSC(input, options) {
|
|
62
62
|
const id = nextId++;
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
63
|
+
let started = false;
|
|
64
|
+
return new Promise((resolve, reject)=>{
|
|
65
|
+
const passthrough = new _nodestream.PassThrough();
|
|
66
|
+
messageCallbacks.set(id, (mesg)=>{
|
|
67
|
+
if (mesg.type === "start") {
|
|
68
|
+
if (!started) {
|
|
69
|
+
started = true;
|
|
70
|
+
resolve([
|
|
71
|
+
passthrough,
|
|
72
|
+
mesg.ctx
|
|
73
|
+
]);
|
|
74
|
+
} else {
|
|
75
|
+
throw new Error("already started");
|
|
76
|
+
}
|
|
77
|
+
} else if (mesg.type === "buf") {
|
|
78
|
+
if (!started) {
|
|
79
|
+
throw new Error("not yet started");
|
|
80
|
+
}
|
|
81
|
+
passthrough.write(Buffer.from(mesg.buf, mesg.offset, mesg.len));
|
|
82
|
+
} else if (mesg.type === "moduleId") {
|
|
83
|
+
options.moduleIdCallback?.(mesg.moduleId);
|
|
84
|
+
} else if (mesg.type === "end") {
|
|
85
|
+
if (!started) {
|
|
86
|
+
throw new Error("not yet started");
|
|
87
|
+
}
|
|
88
|
+
passthrough.end();
|
|
89
|
+
messageCallbacks.delete(id);
|
|
90
|
+
} else if (mesg.type === "err") {
|
|
91
|
+
const err = mesg.err instanceof Error ? mesg.err : new Error(String(mesg.err));
|
|
92
|
+
if (mesg.statusCode) {
|
|
93
|
+
err.statusCode = mesg.statusCode;
|
|
94
|
+
}
|
|
95
|
+
if (!started) {
|
|
96
|
+
reject(err);
|
|
97
|
+
} else {
|
|
98
|
+
passthrough.destroy(err);
|
|
99
|
+
}
|
|
100
|
+
messageCallbacks.delete(id);
|
|
76
101
|
}
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
102
|
+
});
|
|
103
|
+
const mesg = {
|
|
104
|
+
id,
|
|
105
|
+
type: "render",
|
|
106
|
+
input,
|
|
107
|
+
command: options.command,
|
|
108
|
+
ctx: options.ctx ?? null,
|
|
109
|
+
moduleIdCallback: !!options.moduleIdCallback
|
|
110
|
+
};
|
|
111
|
+
worker.postMessage(mesg);
|
|
80
112
|
});
|
|
81
|
-
const mesg = {
|
|
82
|
-
id,
|
|
83
|
-
type: "render",
|
|
84
|
-
input,
|
|
85
|
-
moduleIdCallback: !!options?.moduleIdCallback
|
|
86
|
-
};
|
|
87
|
-
worker.postMessage(mesg);
|
|
88
|
-
return passthrough;
|
|
89
113
|
}
|
|
90
114
|
function getBuildConfigRSC() {
|
|
91
115
|
return new Promise((resolve, reject)=>{
|
|
@@ -106,7 +130,7 @@ function getBuildConfigRSC() {
|
|
|
106
130
|
worker.postMessage(mesg);
|
|
107
131
|
});
|
|
108
132
|
}
|
|
109
|
-
function getSsrConfigRSC(pathStr) {
|
|
133
|
+
function getSsrConfigRSC(pathStr, command) {
|
|
110
134
|
return new Promise((resolve, reject)=>{
|
|
111
135
|
const id = nextId++;
|
|
112
136
|
messageCallbacks.set(id, (mesg)=>{
|
|
@@ -121,7 +145,8 @@ function getSsrConfigRSC(pathStr) {
|
|
|
121
145
|
const mesg = {
|
|
122
146
|
id,
|
|
123
147
|
type: "getSsrConfig",
|
|
124
|
-
pathStr
|
|
148
|
+
pathStr,
|
|
149
|
+
command
|
|
125
150
|
};
|
|
126
151
|
worker.postMessage(mesg);
|
|
127
152
|
});
|
|
@@ -19,9 +19,11 @@ function _interop_require_default(obj) {
|
|
|
19
19
|
}
|
|
20
20
|
const { renderToPipeableStream } = _server.default;
|
|
21
21
|
const handleRender = async (mesg)=>{
|
|
22
|
-
const { id, input, moduleIdCallback } = mesg;
|
|
22
|
+
const { id, input, command, ctx, moduleIdCallback } = mesg;
|
|
23
23
|
try {
|
|
24
|
-
const options = {
|
|
24
|
+
const options = {
|
|
25
|
+
command
|
|
26
|
+
};
|
|
25
27
|
if (moduleIdCallback) {
|
|
26
28
|
options.moduleIdCallback = (moduleId)=>{
|
|
27
29
|
const mesg = {
|
|
@@ -32,7 +34,15 @@ const handleRender = async (mesg)=>{
|
|
|
32
34
|
_nodeworker_threads.parentPort.postMessage(mesg);
|
|
33
35
|
};
|
|
34
36
|
}
|
|
35
|
-
const
|
|
37
|
+
const { runWithContext } = await loadServerFile("waku/server");
|
|
38
|
+
const pipeable = await runWithContext(ctx, ()=>renderRSC(input, options));
|
|
39
|
+
const mesg = {
|
|
40
|
+
id,
|
|
41
|
+
type: "start",
|
|
42
|
+
ctx
|
|
43
|
+
};
|
|
44
|
+
_nodeworker_threads.parentPort.postMessage(mesg);
|
|
45
|
+
(0, _utils.deepFreeze)(ctx);
|
|
36
46
|
const writable = new _nodestream.Writable({
|
|
37
47
|
write (chunk, encoding, callback) {
|
|
38
48
|
if (encoding !== "buffer") {
|
|
@@ -93,9 +103,9 @@ const handleGetBuildConfig = async (mesg)=>{
|
|
|
93
103
|
}
|
|
94
104
|
};
|
|
95
105
|
const handleGetSsrConfig = async (mesg)=>{
|
|
96
|
-
const { id, pathStr } = mesg;
|
|
106
|
+
const { id, pathStr, command } = mesg;
|
|
97
107
|
try {
|
|
98
|
-
const output = await getSsrConfigRSC(pathStr);
|
|
108
|
+
const output = await getSsrConfigRSC(pathStr, command);
|
|
99
109
|
const mesg = {
|
|
100
110
|
id,
|
|
101
111
|
type: "ssrConfig",
|
|
@@ -158,55 +168,39 @@ _nodeworker_threads.parentPort.on("message", (mesg)=>{
|
|
|
158
168
|
handleGetSsrConfig(mesg);
|
|
159
169
|
}
|
|
160
170
|
});
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
const getEntriesFile = async ()=>{
|
|
164
|
-
if (!resolvedConfig) {
|
|
165
|
-
throw new Error("config is not ready");
|
|
166
|
-
}
|
|
167
|
-
const config = resolvedConfig;
|
|
168
|
-
if (config.command === "build") {
|
|
169
|
-
return _nodepath.default.join(config.root, config.build.outDir, config.framework.entriesJs);
|
|
170
|
-
}
|
|
171
|
-
return _nodepath.default.join(config.root, config.framework.entriesJs);
|
|
171
|
+
const getEntriesFile = async (config, command)=>{
|
|
172
|
+
return _nodepath.default.join(config.root, command === "dev" ? config.framework.srcDir : config.framework.distDir, config.framework.entriesJs);
|
|
172
173
|
};
|
|
173
|
-
const
|
|
174
|
-
const
|
|
175
|
-
|
|
176
|
-
const mod = await getEntry(rscId);
|
|
177
|
-
if (typeof mod === "function") {
|
|
178
|
-
return mod;
|
|
179
|
-
}
|
|
180
|
-
if (typeof mod?.default === "function") {
|
|
181
|
-
return mod?.default;
|
|
182
|
-
}
|
|
183
|
-
const err = new Error("No function component found");
|
|
184
|
-
err.statusCode = 404; // HACK our convention for NotFound
|
|
185
|
-
throw err;
|
|
186
|
-
};
|
|
187
|
-
const resolveClientEntry = (filePath)=>{
|
|
188
|
-
if (!resolvedConfig) {
|
|
189
|
-
throw new Error("config is not ready");
|
|
190
|
-
}
|
|
191
|
-
const config = resolvedConfig;
|
|
192
|
-
if (config.command === "build") {
|
|
193
|
-
return config.base + _nodepath.default.relative(_nodepath.default.join(config.root, config.build.outDir), filePath);
|
|
194
|
-
}
|
|
195
|
-
if (config.mode === "development" && !filePath.startsWith(config.root)) {
|
|
174
|
+
const resolveClientEntry = (filePath, config, command)=>{
|
|
175
|
+
const root = _nodepath.default.join(config.root, command === "dev" ? config.framework.srcDir : config.framework.distDir);
|
|
176
|
+
if (command === "dev" && !filePath.startsWith(root)) {
|
|
196
177
|
// HACK this relies on Vite's internal implementation detail.
|
|
197
178
|
return config.base + "@fs" + filePath;
|
|
198
179
|
}
|
|
199
|
-
return config.base + _nodepath.default.relative(
|
|
180
|
+
return config.base + _nodepath.default.relative(root, filePath);
|
|
200
181
|
};
|
|
201
182
|
async function renderRSC(input, options) {
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
const
|
|
183
|
+
const config = await (0, _config.resolveConfig)(options.command === "build" ? "build" : "serve");
|
|
184
|
+
const { unstable_setRootDir } = await loadServerFile("waku/config");
|
|
185
|
+
unstable_setRootDir(config.root);
|
|
186
|
+
const getFunctionComponent = async (rscId)=>{
|
|
187
|
+
const entriesFile = await getEntriesFile(config, options.command);
|
|
188
|
+
const { default: { getEntry } } = await loadServerFile(entriesFile);
|
|
189
|
+
const mod = await getEntry(rscId);
|
|
190
|
+
if (typeof mod === "function") {
|
|
191
|
+
return mod;
|
|
192
|
+
}
|
|
193
|
+
if (typeof mod?.default === "function") {
|
|
194
|
+
return mod?.default;
|
|
195
|
+
}
|
|
196
|
+
const err = new Error("No function component found");
|
|
197
|
+
err.statusCode = 404; // HACK our convention for NotFound
|
|
198
|
+
throw err;
|
|
199
|
+
};
|
|
206
200
|
const bundlerConfig = new Proxy({}, {
|
|
207
201
|
get (_target, encodedId) {
|
|
208
202
|
const [filePath, name] = encodedId.split("#");
|
|
209
|
-
const id = resolveClientEntry(filePath);
|
|
203
|
+
const id = resolveClientEntry(filePath, config, options.command);
|
|
210
204
|
options?.moduleIdCallback?.(id);
|
|
211
205
|
return {
|
|
212
206
|
id,
|
|
@@ -235,22 +229,27 @@ async function renderRSC(input, options) {
|
|
|
235
229
|
throw new Error("Unexpected input");
|
|
236
230
|
}
|
|
237
231
|
async function getBuildConfigRSC() {
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
const
|
|
242
|
-
const
|
|
243
|
-
const { default: { getBuildConfig } } = await loadServerFile(distEntriesFile);
|
|
232
|
+
const config = await (0, _config.resolveConfig)("build");
|
|
233
|
+
const { unstable_setRootDir } = await loadServerFile("waku/config");
|
|
234
|
+
unstable_setRootDir(config.root);
|
|
235
|
+
const entriesFile = await getEntriesFile(config, "build");
|
|
236
|
+
const { default: { getBuildConfig } } = await loadServerFile(entriesFile);
|
|
244
237
|
if (!getBuildConfig) {
|
|
245
238
|
console.warn("getBuildConfig is undefined. It's recommended for optimization and sometimes required.");
|
|
246
239
|
return {};
|
|
247
240
|
}
|
|
248
|
-
const output = await getBuildConfig(
|
|
241
|
+
const output = await getBuildConfig((input, options)=>renderRSC(input, {
|
|
242
|
+
...options,
|
|
243
|
+
command: "build"
|
|
244
|
+
}));
|
|
249
245
|
return output;
|
|
250
246
|
}
|
|
251
|
-
async function getSsrConfigRSC(pathStr) {
|
|
252
|
-
const
|
|
253
|
-
const {
|
|
247
|
+
async function getSsrConfigRSC(pathStr, command) {
|
|
248
|
+
const config = await (0, _config.resolveConfig)(command === "build" ? "build" : "serve");
|
|
249
|
+
const { unstable_setRootDir } = await loadServerFile("waku/config");
|
|
250
|
+
unstable_setRootDir(config.root);
|
|
251
|
+
const entriesFile = await getEntriesFile(config, command);
|
|
252
|
+
const { default: { getSsrConfig } } = await loadServerFile(entriesFile);
|
|
254
253
|
if (!getSsrConfig) {
|
|
255
254
|
return null;
|
|
256
255
|
}
|