@webtypen/webframez-react 0.0.3 → 0.0.5
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 +25 -2
- package/bin/webframez-react.mjs +21 -0
- package/dist/http.cjs +171 -55
- package/dist/http.js +172 -56
- package/dist/index.cjs +171 -55
- package/dist/index.js +172 -56
- package/dist/webframez-core.cjs +171 -55
- package/dist/webframez-core.js +172 -56
- package/package.json +4 -2
- package/register.cjs +1 -0
package/README.md
CHANGED
|
@@ -228,10 +228,10 @@ Recommended scripts:
|
|
|
228
228
|
"build:server": "webframez-react build:server",
|
|
229
229
|
"build:client": "webframez-react build:client",
|
|
230
230
|
"build": "npm run build:server && npm run build:client",
|
|
231
|
-
"start": "
|
|
231
|
+
"start": "NODE_OPTIONS='--conditions react-server -r @webtypen/webframez-react/register' node start-server.cjs",
|
|
232
232
|
"watch:server": "webframez-react watch:server",
|
|
233
233
|
"watch:client": "webframez-react watch:client",
|
|
234
|
-
"serve:watch": "
|
|
234
|
+
"serve:watch": "NODE_OPTIONS='--conditions react-server -r @webtypen/webframez-react/register' node --watch start-server.cjs",
|
|
235
235
|
"watch": "sh -c 'npm run watch:server & npm run watch:client & npm run serve:watch & wait'",
|
|
236
236
|
"dev": "sh -c 'npm run watch:server & npm run watch:client & npm run serve:watch & wait'"
|
|
237
237
|
}
|
|
@@ -242,6 +242,29 @@ Notes:
|
|
|
242
242
|
- `build` compiles the server output (`pages`, `server.ts`) and the browser client bundle (`client.tsx` + RSC manifests).
|
|
243
243
|
- `start` runs the built app in React Server mode.
|
|
244
244
|
- `watch` / `dev` keep TypeScript and webpack in watch mode and restart Node automatically when server output changes.
|
|
245
|
+
- `@webtypen/webframez-react/register` activates the React Server module register, so `"use client"` modules are treated correctly in Node and in the package's SSR worker.
|
|
246
|
+
|
|
247
|
+
If you run an existing `webframez-core` app directly with `ts-node` or `node`, use the same preload:
|
|
248
|
+
|
|
249
|
+
```json
|
|
250
|
+
{
|
|
251
|
+
"scripts": {
|
|
252
|
+
"start": "NODE_OPTIONS='--conditions react-server -r @webtypen/webframez-react/register' ts-node ./app.ts",
|
|
253
|
+
"watch:app": "nodemon --exec \"NODE_OPTIONS='--conditions react-server -r @webtypen/webframez-react/register' ts-node ./app.ts\""
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
Or via the shipped wrapper command:
|
|
259
|
+
|
|
260
|
+
```json
|
|
261
|
+
{
|
|
262
|
+
"scripts": {
|
|
263
|
+
"start": "TS_NODE_FILES=true webframez-react exec -- ts-node ./app.ts",
|
|
264
|
+
"watch:app": "nodemon --exec \"TS_NODE_FILES=true webframez-react exec -- ts-node ./app.ts\""
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
```
|
|
245
268
|
|
|
246
269
|
## CLI Config and Custom Entry Paths
|
|
247
270
|
|
package/bin/webframez-react.mjs
CHANGED
|
@@ -27,6 +27,7 @@ function printHelp() {
|
|
|
27
27
|
" webframez-react watch:client",
|
|
28
28
|
" webframez-react build:server:webpack",
|
|
29
29
|
" webframez-react watch:server:webpack",
|
|
30
|
+
" webframez-react exec -- <command> [args...]",
|
|
30
31
|
"",
|
|
31
32
|
"Config fallback order:",
|
|
32
33
|
" 1) project root override file",
|
|
@@ -115,6 +116,11 @@ function resolveBinary(name) {
|
|
|
115
116
|
return name;
|
|
116
117
|
}
|
|
117
118
|
|
|
119
|
+
function buildReactServerNodeOptions() {
|
|
120
|
+
const existing = process.env.NODE_OPTIONS ? `${process.env.NODE_OPTIONS} ` : "";
|
|
121
|
+
return `${existing}--conditions react-server -r @webtypen/webframez-react/register`.trim();
|
|
122
|
+
}
|
|
123
|
+
|
|
118
124
|
async function loadProjectConfig() {
|
|
119
125
|
const configFiles = [
|
|
120
126
|
"webframez-react.config.mjs",
|
|
@@ -259,6 +265,21 @@ async function main() {
|
|
|
259
265
|
return;
|
|
260
266
|
}
|
|
261
267
|
|
|
268
|
+
if (command === "exec") {
|
|
269
|
+
if (passthroughArgsClean.length === 0) {
|
|
270
|
+
console.error("[webframez-react] Missing command for exec.");
|
|
271
|
+
printHelp();
|
|
272
|
+
process.exit(1);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
const [binaryName, ...binaryArgs] = passthroughArgsClean;
|
|
276
|
+
const code = await run(binaryName, binaryArgs, {
|
|
277
|
+
NODE_OPTIONS: buildReactServerNodeOptions(),
|
|
278
|
+
});
|
|
279
|
+
process.exit(code);
|
|
280
|
+
return;
|
|
281
|
+
}
|
|
282
|
+
|
|
262
283
|
if (command === "build:client" || command === "watch:client") {
|
|
263
284
|
const config = resolveConfig("webpack.client.cjs", "webpack.client.cjs");
|
|
264
285
|
console.log(`[webframez-react] webpack config (${config.source}): ${config.path}`);
|
package/dist/http.cjs
CHANGED
|
@@ -478,6 +478,68 @@ function parseSearchParams(query) {
|
|
|
478
478
|
}
|
|
479
479
|
|
|
480
480
|
// src/http.ts
|
|
481
|
+
var INITIAL_HTML_WORKER_SCRIPT = `
|
|
482
|
+
const path = require("node:path");
|
|
483
|
+
const Module = require("node:module");
|
|
484
|
+
|
|
485
|
+
const pagesDir = process.env.WEBFRAMEZ_REACT_PAGES_DIR || "";
|
|
486
|
+
if (!pagesDir) {
|
|
487
|
+
throw new Error("Missing WEBFRAMEZ_REACT_PAGES_DIR");
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
const appRequire = Module.createRequire(path.join(pagesDir, "__webframez_react_worker__.js"));
|
|
491
|
+
const originalResolveFilename = Module._resolveFilename;
|
|
492
|
+
const forcedResolutions = new Map([
|
|
493
|
+
["react", appRequire.resolve("react")],
|
|
494
|
+
["react/jsx-runtime", appRequire.resolve("react/jsx-runtime")],
|
|
495
|
+
["react/jsx-dev-runtime", appRequire.resolve("react/jsx-dev-runtime")],
|
|
496
|
+
["react-dom/client", appRequire.resolve("react-dom/client")]
|
|
497
|
+
]);
|
|
498
|
+
|
|
499
|
+
Module._resolveFilename = function(request, parent, isMain, options) {
|
|
500
|
+
if (forcedResolutions.has(request)) {
|
|
501
|
+
return forcedResolutions.get(request);
|
|
502
|
+
}
|
|
503
|
+
return originalResolveFilename.call(this, request, parent, isMain, options);
|
|
504
|
+
};
|
|
505
|
+
|
|
506
|
+
const { createFileRouter } = require("@webtypen/webframez-react/router");
|
|
507
|
+
const reactDomPkg = require.resolve("react-dom/package.json", {
|
|
508
|
+
paths: [process.cwd(), pagesDir]
|
|
509
|
+
});
|
|
510
|
+
const reactDomServer = require(path.join(path.dirname(reactDomPkg), "server.node.js"));
|
|
511
|
+
const router = createFileRouter({ pagesDir });
|
|
512
|
+
|
|
513
|
+
process.on("message", async (message) => {
|
|
514
|
+
if (!message || message.type !== "render") {
|
|
515
|
+
return;
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
const previousBasename = globalThis.__RSC_BASENAME;
|
|
519
|
+
globalThis.__RSC_BASENAME = message.payload.basename || "";
|
|
520
|
+
|
|
521
|
+
try {
|
|
522
|
+
const resolved = await router.resolve({
|
|
523
|
+
pathname: message.payload.pathname,
|
|
524
|
+
searchParams: message.payload.searchParams || {},
|
|
525
|
+
cookies: message.payload.cookies || {},
|
|
526
|
+
});
|
|
527
|
+
const html = reactDomServer.renderToString(resolved.model);
|
|
528
|
+
if (typeof process.send === "function") {
|
|
529
|
+
process.send({ id: message.id, ok: true, html });
|
|
530
|
+
}
|
|
531
|
+
} catch (error) {
|
|
532
|
+
const formatted = error && (error.stack || String(error))
|
|
533
|
+
? (error.stack || String(error))
|
|
534
|
+
: "Unknown SSR worker error";
|
|
535
|
+
if (typeof process.send === "function") {
|
|
536
|
+
process.send({ id: message.id, ok: false, error: formatted });
|
|
537
|
+
}
|
|
538
|
+
} finally {
|
|
539
|
+
globalThis.__RSC_BASENAME = previousBasename;
|
|
540
|
+
}
|
|
541
|
+
});
|
|
542
|
+
`;
|
|
481
543
|
function normalizeBasePath(basePath) {
|
|
482
544
|
if (!basePath || basePath === "/") {
|
|
483
545
|
return "";
|
|
@@ -497,62 +559,110 @@ function stripBasePath(pathname, basePath) {
|
|
|
497
559
|
}
|
|
498
560
|
return pathname;
|
|
499
561
|
}
|
|
500
|
-
function
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
562
|
+
function createInitialHtmlWorker(pagesDir) {
|
|
563
|
+
let child = null;
|
|
564
|
+
let nextRequestId = 1;
|
|
565
|
+
let stderrBuffer = "";
|
|
566
|
+
const pending = /* @__PURE__ */ new Map();
|
|
567
|
+
const rejectPending = (error) => {
|
|
568
|
+
for (const entry of pending.values()) {
|
|
569
|
+
clearTimeout(entry.timeout);
|
|
570
|
+
entry.reject(error);
|
|
571
|
+
}
|
|
572
|
+
pending.clear();
|
|
573
|
+
};
|
|
574
|
+
const stopWorker = () => {
|
|
575
|
+
if (!child) {
|
|
576
|
+
return;
|
|
577
|
+
}
|
|
578
|
+
child.removeAllListeners();
|
|
579
|
+
if (!child.killed) {
|
|
580
|
+
child.kill();
|
|
581
|
+
}
|
|
582
|
+
child = null;
|
|
583
|
+
};
|
|
584
|
+
const startWorker = () => {
|
|
585
|
+
if (child && child.connected && !child.killed) {
|
|
586
|
+
return child;
|
|
587
|
+
}
|
|
588
|
+
stderrBuffer = "";
|
|
589
|
+
child = (0, import_node_child_process.spawn)(process.execPath, ["-e", INITIAL_HTML_WORKER_SCRIPT], {
|
|
590
|
+
cwd: process.cwd(),
|
|
591
|
+
env: {
|
|
592
|
+
...process.env,
|
|
593
|
+
WEBFRAMEZ_REACT_PAGES_DIR: pagesDir
|
|
594
|
+
},
|
|
595
|
+
stdio: ["ignore", "ignore", "pipe", "ipc"]
|
|
596
|
+
});
|
|
597
|
+
child.on("message", (message) => {
|
|
598
|
+
if (!message || typeof message.id !== "number") {
|
|
599
|
+
return;
|
|
600
|
+
}
|
|
601
|
+
const entry = pending.get(message.id);
|
|
602
|
+
if (!entry) {
|
|
506
603
|
return;
|
|
507
604
|
}
|
|
508
|
-
|
|
605
|
+
pending.delete(message.id);
|
|
606
|
+
clearTimeout(entry.timeout);
|
|
607
|
+
if (message.ok) {
|
|
608
|
+
entry.resolve(message.html);
|
|
609
|
+
return;
|
|
610
|
+
}
|
|
611
|
+
entry.reject(new Error(message.error));
|
|
509
612
|
});
|
|
510
|
-
|
|
511
|
-
}
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
const
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
}
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
613
|
+
child.stderr?.on("data", (chunk) => {
|
|
614
|
+
stderrBuffer = `${stderrBuffer}${chunk.toString("utf8")}`.slice(-8192);
|
|
615
|
+
});
|
|
616
|
+
child.on("exit", (code, signal) => {
|
|
617
|
+
const suffix = stderrBuffer.trim() !== "" ? `
|
|
618
|
+
${stderrBuffer.trim()}` : "";
|
|
619
|
+
rejectPending(
|
|
620
|
+
new Error(
|
|
621
|
+
`[webframez-react] Initial HTML worker exited (${signal ?? code ?? "unknown"})${suffix}`
|
|
622
|
+
)
|
|
623
|
+
);
|
|
624
|
+
child = null;
|
|
625
|
+
});
|
|
626
|
+
child.on("error", (error) => {
|
|
627
|
+
rejectPending(error instanceof Error ? error : new Error(String(error)));
|
|
628
|
+
child = null;
|
|
629
|
+
});
|
|
630
|
+
return child;
|
|
631
|
+
};
|
|
632
|
+
return {
|
|
633
|
+
render(payload) {
|
|
634
|
+
const activeChild = startWorker();
|
|
635
|
+
const requestId = nextRequestId++;
|
|
636
|
+
return new Promise((resolve, reject) => {
|
|
637
|
+
const timeout = setTimeout(() => {
|
|
638
|
+
rejectPending(new Error("[webframez-react] Initial HTML worker timed out"));
|
|
639
|
+
stopWorker();
|
|
640
|
+
}, 1e4);
|
|
641
|
+
pending.set(requestId, { resolve, reject, timeout });
|
|
642
|
+
const request = {
|
|
643
|
+
id: requestId,
|
|
644
|
+
type: "render",
|
|
645
|
+
payload
|
|
646
|
+
};
|
|
647
|
+
activeChild.send(request, (error) => {
|
|
648
|
+
if (!error) {
|
|
649
|
+
return;
|
|
650
|
+
}
|
|
651
|
+
const entry = pending.get(requestId);
|
|
652
|
+
if (!entry) {
|
|
653
|
+
return;
|
|
654
|
+
}
|
|
655
|
+
pending.delete(requestId);
|
|
656
|
+
clearTimeout(entry.timeout);
|
|
657
|
+
entry.reject(error instanceof Error ? error : new Error(String(error)));
|
|
658
|
+
});
|
|
659
|
+
});
|
|
660
|
+
},
|
|
661
|
+
dispose() {
|
|
662
|
+
rejectPending(new Error("[webframez-react] Initial HTML worker disposed"));
|
|
663
|
+
stopWorker();
|
|
664
|
+
}
|
|
665
|
+
};
|
|
556
666
|
}
|
|
557
667
|
function withRequestBasename(basename, fn) {
|
|
558
668
|
const target = globalThis;
|
|
@@ -612,6 +722,13 @@ function createNodeRequestHandler(options) {
|
|
|
612
722
|
const liveReloadClients = /* @__PURE__ */ new Set();
|
|
613
723
|
const router = createFileRouter({ pagesDir });
|
|
614
724
|
const moduleMap = JSON.parse(import_node_fs2.default.readFileSync(manifestPath, "utf-8"));
|
|
725
|
+
const initialHtmlWorker = createInitialHtmlWorker(pagesDir);
|
|
726
|
+
const disposeInitialHtmlWorker = () => {
|
|
727
|
+
initialHtmlWorker.dispose();
|
|
728
|
+
};
|
|
729
|
+
process.once("exit", disposeInitialHtmlWorker);
|
|
730
|
+
process.once("SIGINT", disposeInitialHtmlWorker);
|
|
731
|
+
process.once("SIGTERM", disposeInitialHtmlWorker);
|
|
615
732
|
return async function handleRequest(req, res) {
|
|
616
733
|
if (!req.url) {
|
|
617
734
|
res.statusCode = 400;
|
|
@@ -711,8 +828,7 @@ function createNodeRequestHandler(options) {
|
|
|
711
828
|
);
|
|
712
829
|
let rootHtml = "";
|
|
713
830
|
try {
|
|
714
|
-
rootHtml = await
|
|
715
|
-
pagesDir,
|
|
831
|
+
rootHtml = await initialHtmlWorker.render({
|
|
716
832
|
pathname: stripBasePath(url.pathname, basePath),
|
|
717
833
|
searchParams: parseSearchParams(url.searchParams),
|
|
718
834
|
cookies: requestCookies,
|
package/dist/http.js
CHANGED
|
@@ -9,7 +9,7 @@ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require
|
|
|
9
9
|
// src/http.ts
|
|
10
10
|
import fs2 from "node:fs";
|
|
11
11
|
import path2 from "node:path";
|
|
12
|
-
import {
|
|
12
|
+
import { spawn } from "node:child_process";
|
|
13
13
|
|
|
14
14
|
// src/server.ts
|
|
15
15
|
import { renderToPipeableStream } from "react-server-dom-webpack/server";
|
|
@@ -453,6 +453,68 @@ function parseSearchParams(query) {
|
|
|
453
453
|
}
|
|
454
454
|
|
|
455
455
|
// src/http.ts
|
|
456
|
+
var INITIAL_HTML_WORKER_SCRIPT = `
|
|
457
|
+
const path = require("node:path");
|
|
458
|
+
const Module = require("node:module");
|
|
459
|
+
|
|
460
|
+
const pagesDir = process.env.WEBFRAMEZ_REACT_PAGES_DIR || "";
|
|
461
|
+
if (!pagesDir) {
|
|
462
|
+
throw new Error("Missing WEBFRAMEZ_REACT_PAGES_DIR");
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
const appRequire = Module.createRequire(path.join(pagesDir, "__webframez_react_worker__.js"));
|
|
466
|
+
const originalResolveFilename = Module._resolveFilename;
|
|
467
|
+
const forcedResolutions = new Map([
|
|
468
|
+
["react", appRequire.resolve("react")],
|
|
469
|
+
["react/jsx-runtime", appRequire.resolve("react/jsx-runtime")],
|
|
470
|
+
["react/jsx-dev-runtime", appRequire.resolve("react/jsx-dev-runtime")],
|
|
471
|
+
["react-dom/client", appRequire.resolve("react-dom/client")]
|
|
472
|
+
]);
|
|
473
|
+
|
|
474
|
+
Module._resolveFilename = function(request, parent, isMain, options) {
|
|
475
|
+
if (forcedResolutions.has(request)) {
|
|
476
|
+
return forcedResolutions.get(request);
|
|
477
|
+
}
|
|
478
|
+
return originalResolveFilename.call(this, request, parent, isMain, options);
|
|
479
|
+
};
|
|
480
|
+
|
|
481
|
+
const { createFileRouter } = require("@webtypen/webframez-react/router");
|
|
482
|
+
const reactDomPkg = require.resolve("react-dom/package.json", {
|
|
483
|
+
paths: [process.cwd(), pagesDir]
|
|
484
|
+
});
|
|
485
|
+
const reactDomServer = require(path.join(path.dirname(reactDomPkg), "server.node.js"));
|
|
486
|
+
const router = createFileRouter({ pagesDir });
|
|
487
|
+
|
|
488
|
+
process.on("message", async (message) => {
|
|
489
|
+
if (!message || message.type !== "render") {
|
|
490
|
+
return;
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
const previousBasename = globalThis.__RSC_BASENAME;
|
|
494
|
+
globalThis.__RSC_BASENAME = message.payload.basename || "";
|
|
495
|
+
|
|
496
|
+
try {
|
|
497
|
+
const resolved = await router.resolve({
|
|
498
|
+
pathname: message.payload.pathname,
|
|
499
|
+
searchParams: message.payload.searchParams || {},
|
|
500
|
+
cookies: message.payload.cookies || {},
|
|
501
|
+
});
|
|
502
|
+
const html = reactDomServer.renderToString(resolved.model);
|
|
503
|
+
if (typeof process.send === "function") {
|
|
504
|
+
process.send({ id: message.id, ok: true, html });
|
|
505
|
+
}
|
|
506
|
+
} catch (error) {
|
|
507
|
+
const formatted = error && (error.stack || String(error))
|
|
508
|
+
? (error.stack || String(error))
|
|
509
|
+
: "Unknown SSR worker error";
|
|
510
|
+
if (typeof process.send === "function") {
|
|
511
|
+
process.send({ id: message.id, ok: false, error: formatted });
|
|
512
|
+
}
|
|
513
|
+
} finally {
|
|
514
|
+
globalThis.__RSC_BASENAME = previousBasename;
|
|
515
|
+
}
|
|
516
|
+
});
|
|
517
|
+
`;
|
|
456
518
|
function normalizeBasePath(basePath) {
|
|
457
519
|
if (!basePath || basePath === "/") {
|
|
458
520
|
return "";
|
|
@@ -472,62 +534,110 @@ function stripBasePath(pathname, basePath) {
|
|
|
472
534
|
}
|
|
473
535
|
return pathname;
|
|
474
536
|
}
|
|
475
|
-
function
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
537
|
+
function createInitialHtmlWorker(pagesDir) {
|
|
538
|
+
let child = null;
|
|
539
|
+
let nextRequestId = 1;
|
|
540
|
+
let stderrBuffer = "";
|
|
541
|
+
const pending = /* @__PURE__ */ new Map();
|
|
542
|
+
const rejectPending = (error) => {
|
|
543
|
+
for (const entry of pending.values()) {
|
|
544
|
+
clearTimeout(entry.timeout);
|
|
545
|
+
entry.reject(error);
|
|
546
|
+
}
|
|
547
|
+
pending.clear();
|
|
548
|
+
};
|
|
549
|
+
const stopWorker = () => {
|
|
550
|
+
if (!child) {
|
|
551
|
+
return;
|
|
552
|
+
}
|
|
553
|
+
child.removeAllListeners();
|
|
554
|
+
if (!child.killed) {
|
|
555
|
+
child.kill();
|
|
556
|
+
}
|
|
557
|
+
child = null;
|
|
558
|
+
};
|
|
559
|
+
const startWorker = () => {
|
|
560
|
+
if (child && child.connected && !child.killed) {
|
|
561
|
+
return child;
|
|
562
|
+
}
|
|
563
|
+
stderrBuffer = "";
|
|
564
|
+
child = spawn(process.execPath, ["-e", INITIAL_HTML_WORKER_SCRIPT], {
|
|
565
|
+
cwd: process.cwd(),
|
|
566
|
+
env: {
|
|
567
|
+
...process.env,
|
|
568
|
+
WEBFRAMEZ_REACT_PAGES_DIR: pagesDir
|
|
569
|
+
},
|
|
570
|
+
stdio: ["ignore", "ignore", "pipe", "ipc"]
|
|
571
|
+
});
|
|
572
|
+
child.on("message", (message) => {
|
|
573
|
+
if (!message || typeof message.id !== "number") {
|
|
574
|
+
return;
|
|
575
|
+
}
|
|
576
|
+
const entry = pending.get(message.id);
|
|
577
|
+
if (!entry) {
|
|
481
578
|
return;
|
|
482
579
|
}
|
|
483
|
-
|
|
580
|
+
pending.delete(message.id);
|
|
581
|
+
clearTimeout(entry.timeout);
|
|
582
|
+
if (message.ok) {
|
|
583
|
+
entry.resolve(message.html);
|
|
584
|
+
return;
|
|
585
|
+
}
|
|
586
|
+
entry.reject(new Error(message.error));
|
|
484
587
|
});
|
|
485
|
-
|
|
486
|
-
}
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
const
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
}
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
588
|
+
child.stderr?.on("data", (chunk) => {
|
|
589
|
+
stderrBuffer = `${stderrBuffer}${chunk.toString("utf8")}`.slice(-8192);
|
|
590
|
+
});
|
|
591
|
+
child.on("exit", (code, signal) => {
|
|
592
|
+
const suffix = stderrBuffer.trim() !== "" ? `
|
|
593
|
+
${stderrBuffer.trim()}` : "";
|
|
594
|
+
rejectPending(
|
|
595
|
+
new Error(
|
|
596
|
+
`[webframez-react] Initial HTML worker exited (${signal ?? code ?? "unknown"})${suffix}`
|
|
597
|
+
)
|
|
598
|
+
);
|
|
599
|
+
child = null;
|
|
600
|
+
});
|
|
601
|
+
child.on("error", (error) => {
|
|
602
|
+
rejectPending(error instanceof Error ? error : new Error(String(error)));
|
|
603
|
+
child = null;
|
|
604
|
+
});
|
|
605
|
+
return child;
|
|
606
|
+
};
|
|
607
|
+
return {
|
|
608
|
+
render(payload) {
|
|
609
|
+
const activeChild = startWorker();
|
|
610
|
+
const requestId = nextRequestId++;
|
|
611
|
+
return new Promise((resolve, reject) => {
|
|
612
|
+
const timeout = setTimeout(() => {
|
|
613
|
+
rejectPending(new Error("[webframez-react] Initial HTML worker timed out"));
|
|
614
|
+
stopWorker();
|
|
615
|
+
}, 1e4);
|
|
616
|
+
pending.set(requestId, { resolve, reject, timeout });
|
|
617
|
+
const request = {
|
|
618
|
+
id: requestId,
|
|
619
|
+
type: "render",
|
|
620
|
+
payload
|
|
621
|
+
};
|
|
622
|
+
activeChild.send(request, (error) => {
|
|
623
|
+
if (!error) {
|
|
624
|
+
return;
|
|
625
|
+
}
|
|
626
|
+
const entry = pending.get(requestId);
|
|
627
|
+
if (!entry) {
|
|
628
|
+
return;
|
|
629
|
+
}
|
|
630
|
+
pending.delete(requestId);
|
|
631
|
+
clearTimeout(entry.timeout);
|
|
632
|
+
entry.reject(error instanceof Error ? error : new Error(String(error)));
|
|
633
|
+
});
|
|
634
|
+
});
|
|
635
|
+
},
|
|
636
|
+
dispose() {
|
|
637
|
+
rejectPending(new Error("[webframez-react] Initial HTML worker disposed"));
|
|
638
|
+
stopWorker();
|
|
639
|
+
}
|
|
640
|
+
};
|
|
531
641
|
}
|
|
532
642
|
function withRequestBasename(basename, fn) {
|
|
533
643
|
const target = globalThis;
|
|
@@ -587,6 +697,13 @@ function createNodeRequestHandler(options) {
|
|
|
587
697
|
const liveReloadClients = /* @__PURE__ */ new Set();
|
|
588
698
|
const router = createFileRouter({ pagesDir });
|
|
589
699
|
const moduleMap = JSON.parse(fs2.readFileSync(manifestPath, "utf-8"));
|
|
700
|
+
const initialHtmlWorker = createInitialHtmlWorker(pagesDir);
|
|
701
|
+
const disposeInitialHtmlWorker = () => {
|
|
702
|
+
initialHtmlWorker.dispose();
|
|
703
|
+
};
|
|
704
|
+
process.once("exit", disposeInitialHtmlWorker);
|
|
705
|
+
process.once("SIGINT", disposeInitialHtmlWorker);
|
|
706
|
+
process.once("SIGTERM", disposeInitialHtmlWorker);
|
|
590
707
|
return async function handleRequest(req, res) {
|
|
591
708
|
if (!req.url) {
|
|
592
709
|
res.statusCode = 400;
|
|
@@ -686,8 +803,7 @@ function createNodeRequestHandler(options) {
|
|
|
686
803
|
);
|
|
687
804
|
let rootHtml = "";
|
|
688
805
|
try {
|
|
689
|
-
rootHtml = await
|
|
690
|
-
pagesDir,
|
|
806
|
+
rootHtml = await initialHtmlWorker.render({
|
|
691
807
|
pathname: stripBasePath(url.pathname, basePath),
|
|
692
808
|
searchParams: parseSearchParams(url.searchParams),
|
|
693
809
|
cookies: requestCookies,
|