@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 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": "node --conditions react-server start-server.cjs",
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": "node --watch --conditions react-server start-server.cjs",
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
 
@@ -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 runNodeCommand(args) {
501
- return new Promise((resolve, reject) => {
502
- (0, import_node_child_process.execFile)(process.execPath, args, { timeout: 1e4, maxBuffer: 1024 * 1024 * 5 }, (error, stdout, stderr) => {
503
- if (error) {
504
- const out = stderr && stderr.trim() !== "" ? stderr : stdout;
505
- reject(new Error(out || error.message));
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
- resolve({ stdout, stderr });
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
- async function renderInitialHtmlInWorker(options) {
513
- const payload = Buffer.from(JSON.stringify(options), "utf8").toString("base64url");
514
- const script = `
515
- const path = require("node:path");
516
- const Module = require("node:module");
517
- const input = JSON.parse(Buffer.from(process.argv[1], "base64url").toString("utf8"));
518
- globalThis.__RSC_BASENAME = input.basename || "";
519
- const appRequire = Module.createRequire(path.join(input.pagesDir, "__webframez_react_worker__.js"));
520
- const originalResolveFilename = Module._resolveFilename;
521
- const forcedResolutions = new Map([
522
- ["react", appRequire.resolve("react")],
523
- ["react/jsx-runtime", appRequire.resolve("react/jsx-runtime")],
524
- ["react/jsx-dev-runtime", appRequire.resolve("react/jsx-dev-runtime")],
525
- ["react-dom/client", appRequire.resolve("react-dom/client")]
526
- ]);
527
- Module._resolveFilename = function(request, parent, isMain, options) {
528
- if (forcedResolutions.has(request)) {
529
- return forcedResolutions.get(request);
530
- }
531
- return originalResolveFilename.call(this, request, parent, isMain, options);
532
- };
533
- const { createFileRouter } = require("webframez-react/router");
534
- const reactDomPkg = require.resolve("react-dom/package.json", {
535
- paths: [process.cwd(), input.pagesDir]
536
- });
537
- const reactDomServer = require(path.join(path.dirname(reactDomPkg), "server.node.js"));
538
-
539
- (async () => {
540
- const router = createFileRouter({ pagesDir: input.pagesDir });
541
- const resolved = await router.resolve({
542
- pathname: input.pathname,
543
- searchParams: input.searchParams || {},
544
- cookies: input.cookies || {},
545
- });
546
- const html = reactDomServer.renderToString(resolved.model);
547
- process.stdout.write(JSON.stringify({ html }));
548
- })().catch((error) => {
549
- process.stderr.write(error && (error.stack || String(error)) ? (error.stack || String(error)) : "Unknown SSR worker error");
550
- process.exit(1);
551
- });
552
- `;
553
- const { stdout } = await runNodeCommand(["-e", script, payload]);
554
- const parsed = JSON.parse(stdout || "{}");
555
- return parsed.html || "";
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 renderInitialHtmlInWorker({
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 { execFile } from "node:child_process";
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 runNodeCommand(args) {
476
- return new Promise((resolve, reject) => {
477
- execFile(process.execPath, args, { timeout: 1e4, maxBuffer: 1024 * 1024 * 5 }, (error, stdout, stderr) => {
478
- if (error) {
479
- const out = stderr && stderr.trim() !== "" ? stderr : stdout;
480
- reject(new Error(out || error.message));
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
- resolve({ stdout, stderr });
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
- async function renderInitialHtmlInWorker(options) {
488
- const payload = Buffer.from(JSON.stringify(options), "utf8").toString("base64url");
489
- const script = `
490
- const path = require("node:path");
491
- const Module = require("node:module");
492
- const input = JSON.parse(Buffer.from(process.argv[1], "base64url").toString("utf8"));
493
- globalThis.__RSC_BASENAME = input.basename || "";
494
- const appRequire = Module.createRequire(path.join(input.pagesDir, "__webframez_react_worker__.js"));
495
- const originalResolveFilename = Module._resolveFilename;
496
- const forcedResolutions = new Map([
497
- ["react", appRequire.resolve("react")],
498
- ["react/jsx-runtime", appRequire.resolve("react/jsx-runtime")],
499
- ["react/jsx-dev-runtime", appRequire.resolve("react/jsx-dev-runtime")],
500
- ["react-dom/client", appRequire.resolve("react-dom/client")]
501
- ]);
502
- Module._resolveFilename = function(request, parent, isMain, options) {
503
- if (forcedResolutions.has(request)) {
504
- return forcedResolutions.get(request);
505
- }
506
- return originalResolveFilename.call(this, request, parent, isMain, options);
507
- };
508
- const { createFileRouter } = require("webframez-react/router");
509
- const reactDomPkg = require.resolve("react-dom/package.json", {
510
- paths: [process.cwd(), input.pagesDir]
511
- });
512
- const reactDomServer = require(path.join(path.dirname(reactDomPkg), "server.node.js"));
513
-
514
- (async () => {
515
- const router = createFileRouter({ pagesDir: input.pagesDir });
516
- const resolved = await router.resolve({
517
- pathname: input.pathname,
518
- searchParams: input.searchParams || {},
519
- cookies: input.cookies || {},
520
- });
521
- const html = reactDomServer.renderToString(resolved.model);
522
- process.stdout.write(JSON.stringify({ html }));
523
- })().catch((error) => {
524
- process.stderr.write(error && (error.stack || String(error)) ? (error.stack || String(error)) : "Unknown SSR worker error");
525
- process.exit(1);
526
- });
527
- `;
528
- const { stdout } = await runNodeCommand(["-e", script, payload]);
529
- const parsed = JSON.parse(stdout || "{}");
530
- return parsed.html || "";
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 renderInitialHtmlInWorker({
690
- pagesDir,
806
+ rootHtml = await initialHtmlWorker.render({
691
807
  pathname: stripBasePath(url.pathname, basePath),
692
808
  searchParams: parseSearchParams(url.searchParams),
693
809
  cookies: requestCookies,