@webtypen/webframez-react 0.0.10 → 0.0.12

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/dist/http.cjs CHANGED
@@ -33,14 +33,15 @@ __export(http_exports, {
33
33
  });
34
34
  module.exports = __toCommonJS(http_exports);
35
35
  var import_node_fs2 = __toESM(require("node:fs"), 1);
36
- var import_node_path2 = __toESM(require("node:path"), 1);
36
+ var import_node_path3 = __toESM(require("node:path"), 1);
37
37
  var import_node_child_process = require("node:child_process");
38
38
 
39
39
  // src/server.ts
40
+ var import_node_path = __toESM(require("node:path"), 1);
40
41
  var import_node_stream = require("node:stream");
42
+ var import_node_module = require("node:module");
41
43
  var import_server = require("react-server-dom-webpack/server");
42
44
  var import_client = require("react-server-dom-webpack/client.node");
43
- var import_server2 = require("react-dom/server");
44
45
  function defaultOnError(err) {
45
46
  console.error("[webframez-react] RSC render error", err);
46
47
  }
@@ -139,6 +140,9 @@ function createReadableStreamFromString(value) {
139
140
  });
140
141
  }
141
142
  async function renderHtmlFromFlightData(flightData, options) {
143
+ const appRequire = (0, import_node_module.createRequire)(import_node_path.default.join(process.cwd(), "__webframez_react_server__.js"));
144
+ const reactDomPkg = appRequire.resolve("react-dom/package.json");
145
+ const { renderToString } = appRequire(import_node_path.default.join(import_node_path.default.dirname(reactDomPkg), "server.node.js"));
142
146
  const model = await (0, import_client.createFromReadableStream)(createReadableStreamFromString(flightData), {
143
147
  serverConsumerManifest: {
144
148
  moduleMap: options.moduleMap ?? {},
@@ -146,7 +150,7 @@ async function renderHtmlFromFlightData(flightData, options) {
146
150
  moduleLoading: null
147
151
  }
148
152
  });
149
- return (0, import_server2.renderToString)(model);
153
+ return renderToString(model);
150
154
  }
151
155
  function sendRSC(res, model, options = {}) {
152
156
  const {
@@ -164,7 +168,7 @@ function sendRSC(res, model, options = {}) {
164
168
 
165
169
  // src/file-router.tsx
166
170
  var import_node_fs = __toESM(require("node:fs"), 1);
167
- var import_node_path = __toESM(require("node:path"), 1);
171
+ var import_node_path2 = __toESM(require("node:path"), 1);
168
172
 
169
173
  // src/router-runtime.tsx
170
174
  var import_react = __toESM(require("react"), 1);
@@ -249,7 +253,7 @@ function walkFiles(dir) {
249
253
  continue;
250
254
  }
251
255
  for (const entry of import_node_fs.default.readdirSync(current, { withFileTypes: true })) {
252
- const fullPath = import_node_path.default.join(current, entry.name);
256
+ const fullPath = import_node_path2.default.join(current, entry.name);
253
257
  if (entry.isDirectory()) {
254
258
  stack.push(fullPath);
255
259
  } else if (entry.isFile()) {
@@ -279,7 +283,7 @@ function toRouteEntry(pagesDir, filePath) {
279
283
  if (!normalized.endsWith("/index.js")) {
280
284
  return null;
281
285
  }
282
- const relativeDir = import_node_path.default.dirname(import_node_path.default.relative(pagesDir, filePath)).replace(/\\/g, "/");
286
+ const relativeDir = import_node_path2.default.dirname(import_node_path2.default.relative(pagesDir, filePath)).replace(/\\/g, "/");
283
287
  const segments = relativeDir === "." ? [] : relativeDir.split("/").filter(Boolean);
284
288
  const staticCount = segments.filter((segment) => !segment.startsWith("[")).length;
285
289
  return {
@@ -422,8 +426,8 @@ function isRouteAbort(value) {
422
426
  }
423
427
  function createFileRouter(options) {
424
428
  const pagesDir = options.pagesDir;
425
- const layoutPath = import_node_path.default.join(pagesDir, "layout.js");
426
- const errorPath = import_node_path.default.join(pagesDir, "errors.js");
429
+ const layoutPath = import_node_path2.default.join(pagesDir, "layout.js");
430
+ const errorPath = import_node_path2.default.join(pagesDir, "errors.js");
427
431
  function buildRouteEntries() {
428
432
  const files = walkFiles(pagesDir);
429
433
  return files.map((filePath) => toRouteEntry(pagesDir, filePath)).filter((entry) => entry !== null).sort((a, b) => {
@@ -531,6 +535,7 @@ function createInitialHtmlErrorMarkup(message) {
531
535
  var INITIAL_HTML_WORKER_SCRIPT = `
532
536
  const path = require("node:path");
533
537
  const Module = require("node:module");
538
+ const { Writable } = require("node:stream");
534
539
 
535
540
  const pagesDir = process.env.WEBFRAMEZ_REACT_PAGES_DIR || "";
536
541
  if (!pagesDir) {
@@ -560,6 +565,41 @@ const reactDomPkg = require.resolve("react-dom/package.json", {
560
565
  const reactDomServer = require(path.join(path.dirname(reactDomPkg), "server.node.js"));
561
566
  const router = createFileRouter({ pagesDir });
562
567
 
568
+ function renderHtml(model) {
569
+ return new Promise((resolve, reject) => {
570
+ let didError = false;
571
+ const chunks = [];
572
+ const writable = new Writable({
573
+ write(chunk, _encoding, callback) {
574
+ const normalized =
575
+ typeof chunk === "string" ? Buffer.from(chunk) : Buffer.from(chunk);
576
+ chunks.push(normalized);
577
+ callback();
578
+ }
579
+ });
580
+
581
+ writable.on("finish", () => {
582
+ if (didError) {
583
+ reject(new Error("Initial HTML stream finished after a render error."));
584
+ return;
585
+ }
586
+
587
+ resolve(Buffer.concat(chunks).toString("utf8"));
588
+ });
589
+ writable.on("error", reject);
590
+
591
+ const stream = reactDomServer.renderToPipeableStream(model, {
592
+ onAllReady() {
593
+ stream.pipe(writable);
594
+ },
595
+ onError(error) {
596
+ didError = true;
597
+ reject(error);
598
+ }
599
+ });
600
+ });
601
+ }
602
+
563
603
  process.on("message", async (message) => {
564
604
  if (!message || message.type !== "render") {
565
605
  return;
@@ -574,7 +614,7 @@ process.on("message", async (message) => {
574
614
  searchParams: message.payload.searchParams || {},
575
615
  cookies: message.payload.cookies || {},
576
616
  });
577
- const html = reactDomServer.renderToString(resolved.model);
617
+ const html = await renderHtml(resolved.model);
578
618
  if (typeof process.send === "function") {
579
619
  process.send({ id: message.id, ok: true, html });
580
620
  }
@@ -763,10 +803,10 @@ function parseCookies(rawCookieHeader) {
763
803
  }
764
804
  function createNodeRequestHandler(options) {
765
805
  const devServerId = `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
766
- const distRootDir = import_node_path2.default.resolve(options.distRootDir);
767
- const pagesDir = import_node_path2.default.resolve(options.pagesDir ?? import_node_path2.default.join(distRootDir, "pages"));
768
- const manifestPath = import_node_path2.default.resolve(
769
- options.manifestPath ?? import_node_path2.default.join(distRootDir, "react-client-manifest.json")
806
+ const distRootDir = import_node_path3.default.resolve(options.distRootDir);
807
+ const pagesDir = import_node_path3.default.resolve(options.pagesDir ?? import_node_path3.default.join(distRootDir, "pages"));
808
+ const manifestPath = import_node_path3.default.resolve(
809
+ options.manifestPath ?? import_node_path3.default.join(distRootDir, "react-client-manifest.json")
770
810
  );
771
811
  const assetsPrefix = options.assetsPrefix ?? "/assets/";
772
812
  const rscPath = options.rscPath ?? "/rsc";
@@ -855,13 +895,13 @@ function createNodeRequestHandler(options) {
855
895
  }
856
896
  if (url.pathname.startsWith(assetsPrefix)) {
857
897
  const relative = url.pathname.slice(assetsPrefix.length);
858
- const filePath = import_node_path2.default.resolve(distRootDir, relative);
898
+ const filePath = import_node_path3.default.resolve(distRootDir, relative);
859
899
  if (!filePath.startsWith(distRootDir)) {
860
900
  res.statusCode = 400;
861
901
  res.end("Invalid path");
862
902
  return;
863
903
  }
864
- const ext = import_node_path2.default.extname(filePath);
904
+ const ext = import_node_path3.default.extname(filePath);
865
905
  if (ext === ".js" || ext === ".mjs") {
866
906
  res.setHeader("Content-Type", "text/javascript; charset=utf-8");
867
907
  } else if (ext === ".json") {
package/dist/http.js CHANGED
@@ -8,14 +8,15 @@ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require
8
8
 
9
9
  // src/http.ts
10
10
  import fs2 from "node:fs";
11
- import path2 from "node:path";
11
+ import path3 from "node:path";
12
12
  import { spawn } from "node:child_process";
13
13
 
14
14
  // src/server.ts
15
+ import path from "node:path";
15
16
  import { Writable } from "node:stream";
17
+ import { createRequire } from "node:module";
16
18
  import { renderToPipeableStream } from "react-server-dom-webpack/server";
17
19
  import { createFromReadableStream } from "react-server-dom-webpack/client.node";
18
- import { renderToString } from "react-dom/server";
19
20
  function defaultOnError(err) {
20
21
  console.error("[webframez-react] RSC render error", err);
21
22
  }
@@ -114,6 +115,9 @@ function createReadableStreamFromString(value) {
114
115
  });
115
116
  }
116
117
  async function renderHtmlFromFlightData(flightData, options) {
118
+ const appRequire = createRequire(path.join(process.cwd(), "__webframez_react_server__.js"));
119
+ const reactDomPkg = appRequire.resolve("react-dom/package.json");
120
+ const { renderToString } = appRequire(path.join(path.dirname(reactDomPkg), "server.node.js"));
117
121
  const model = await createFromReadableStream(createReadableStreamFromString(flightData), {
118
122
  serverConsumerManifest: {
119
123
  moduleMap: options.moduleMap ?? {},
@@ -139,7 +143,7 @@ function sendRSC(res, model, options = {}) {
139
143
 
140
144
  // src/file-router.tsx
141
145
  import fs from "node:fs";
142
- import path from "node:path";
146
+ import path2 from "node:path";
143
147
 
144
148
  // src/router-runtime.tsx
145
149
  import React from "react";
@@ -224,7 +228,7 @@ function walkFiles(dir) {
224
228
  continue;
225
229
  }
226
230
  for (const entry of fs.readdirSync(current, { withFileTypes: true })) {
227
- const fullPath = path.join(current, entry.name);
231
+ const fullPath = path2.join(current, entry.name);
228
232
  if (entry.isDirectory()) {
229
233
  stack.push(fullPath);
230
234
  } else if (entry.isFile()) {
@@ -254,7 +258,7 @@ function toRouteEntry(pagesDir, filePath) {
254
258
  if (!normalized.endsWith("/index.js")) {
255
259
  return null;
256
260
  }
257
- const relativeDir = path.dirname(path.relative(pagesDir, filePath)).replace(/\\/g, "/");
261
+ const relativeDir = path2.dirname(path2.relative(pagesDir, filePath)).replace(/\\/g, "/");
258
262
  const segments = relativeDir === "." ? [] : relativeDir.split("/").filter(Boolean);
259
263
  const staticCount = segments.filter((segment) => !segment.startsWith("[")).length;
260
264
  return {
@@ -397,8 +401,8 @@ function isRouteAbort(value) {
397
401
  }
398
402
  function createFileRouter(options) {
399
403
  const pagesDir = options.pagesDir;
400
- const layoutPath = path.join(pagesDir, "layout.js");
401
- const errorPath = path.join(pagesDir, "errors.js");
404
+ const layoutPath = path2.join(pagesDir, "layout.js");
405
+ const errorPath = path2.join(pagesDir, "errors.js");
402
406
  function buildRouteEntries() {
403
407
  const files = walkFiles(pagesDir);
404
408
  return files.map((filePath) => toRouteEntry(pagesDir, filePath)).filter((entry) => entry !== null).sort((a, b) => {
@@ -506,6 +510,7 @@ function createInitialHtmlErrorMarkup(message) {
506
510
  var INITIAL_HTML_WORKER_SCRIPT = `
507
511
  const path = require("node:path");
508
512
  const Module = require("node:module");
513
+ const { Writable } = require("node:stream");
509
514
 
510
515
  const pagesDir = process.env.WEBFRAMEZ_REACT_PAGES_DIR || "";
511
516
  if (!pagesDir) {
@@ -535,6 +540,41 @@ const reactDomPkg = require.resolve("react-dom/package.json", {
535
540
  const reactDomServer = require(path.join(path.dirname(reactDomPkg), "server.node.js"));
536
541
  const router = createFileRouter({ pagesDir });
537
542
 
543
+ function renderHtml(model) {
544
+ return new Promise((resolve, reject) => {
545
+ let didError = false;
546
+ const chunks = [];
547
+ const writable = new Writable({
548
+ write(chunk, _encoding, callback) {
549
+ const normalized =
550
+ typeof chunk === "string" ? Buffer.from(chunk) : Buffer.from(chunk);
551
+ chunks.push(normalized);
552
+ callback();
553
+ }
554
+ });
555
+
556
+ writable.on("finish", () => {
557
+ if (didError) {
558
+ reject(new Error("Initial HTML stream finished after a render error."));
559
+ return;
560
+ }
561
+
562
+ resolve(Buffer.concat(chunks).toString("utf8"));
563
+ });
564
+ writable.on("error", reject);
565
+
566
+ const stream = reactDomServer.renderToPipeableStream(model, {
567
+ onAllReady() {
568
+ stream.pipe(writable);
569
+ },
570
+ onError(error) {
571
+ didError = true;
572
+ reject(error);
573
+ }
574
+ });
575
+ });
576
+ }
577
+
538
578
  process.on("message", async (message) => {
539
579
  if (!message || message.type !== "render") {
540
580
  return;
@@ -549,7 +589,7 @@ process.on("message", async (message) => {
549
589
  searchParams: message.payload.searchParams || {},
550
590
  cookies: message.payload.cookies || {},
551
591
  });
552
- const html = reactDomServer.renderToString(resolved.model);
592
+ const html = await renderHtml(resolved.model);
553
593
  if (typeof process.send === "function") {
554
594
  process.send({ id: message.id, ok: true, html });
555
595
  }
@@ -738,10 +778,10 @@ function parseCookies(rawCookieHeader) {
738
778
  }
739
779
  function createNodeRequestHandler(options) {
740
780
  const devServerId = `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
741
- const distRootDir = path2.resolve(options.distRootDir);
742
- const pagesDir = path2.resolve(options.pagesDir ?? path2.join(distRootDir, "pages"));
743
- const manifestPath = path2.resolve(
744
- options.manifestPath ?? path2.join(distRootDir, "react-client-manifest.json")
781
+ const distRootDir = path3.resolve(options.distRootDir);
782
+ const pagesDir = path3.resolve(options.pagesDir ?? path3.join(distRootDir, "pages"));
783
+ const manifestPath = path3.resolve(
784
+ options.manifestPath ?? path3.join(distRootDir, "react-client-manifest.json")
745
785
  );
746
786
  const assetsPrefix = options.assetsPrefix ?? "/assets/";
747
787
  const rscPath = options.rscPath ?? "/rsc";
@@ -830,13 +870,13 @@ function createNodeRequestHandler(options) {
830
870
  }
831
871
  if (url.pathname.startsWith(assetsPrefix)) {
832
872
  const relative = url.pathname.slice(assetsPrefix.length);
833
- const filePath = path2.resolve(distRootDir, relative);
873
+ const filePath = path3.resolve(distRootDir, relative);
834
874
  if (!filePath.startsWith(distRootDir)) {
835
875
  res.statusCode = 400;
836
876
  res.end("Invalid path");
837
877
  return;
838
878
  }
839
- const ext = path2.extname(filePath);
879
+ const ext = path3.extname(filePath);
840
880
  if (ext === ".js" || ext === ".mjs") {
841
881
  res.setHeader("Content-Type", "text/javascript; charset=utf-8");
842
882
  } else if (ext === ".json") {
package/dist/index.cjs CHANGED
@@ -43,10 +43,11 @@ __export(src_exports, {
43
43
  module.exports = __toCommonJS(src_exports);
44
44
 
45
45
  // src/server.ts
46
+ var import_node_path = __toESM(require("node:path"), 1);
46
47
  var import_node_stream = require("node:stream");
48
+ var import_node_module = require("node:module");
47
49
  var import_server = require("react-server-dom-webpack/server");
48
50
  var import_client = require("react-server-dom-webpack/client.node");
49
- var import_server2 = require("react-dom/server");
50
51
  function defaultOnError(err) {
51
52
  console.error("[webframez-react] RSC render error", err);
52
53
  }
@@ -145,6 +146,9 @@ function createReadableStreamFromString(value) {
145
146
  });
146
147
  }
147
148
  async function renderHtmlFromFlightData(flightData, options) {
149
+ const appRequire = (0, import_node_module.createRequire)(import_node_path.default.join(process.cwd(), "__webframez_react_server__.js"));
150
+ const reactDomPkg = appRequire.resolve("react-dom/package.json");
151
+ const { renderToString } = appRequire(import_node_path.default.join(import_node_path.default.dirname(reactDomPkg), "server.node.js"));
148
152
  const model = await (0, import_client.createFromReadableStream)(createReadableStreamFromString(flightData), {
149
153
  serverConsumerManifest: {
150
154
  moduleMap: options.moduleMap ?? {},
@@ -152,7 +156,7 @@ async function renderHtmlFromFlightData(flightData, options) {
152
156
  moduleLoading: null
153
157
  }
154
158
  });
155
- return (0, import_server2.renderToString)(model);
159
+ return renderToString(model);
156
160
  }
157
161
  function sendRSC(res, model, options = {}) {
158
162
  const {
@@ -198,7 +202,7 @@ function createRSCHandler(options) {
198
202
 
199
203
  // src/file-router.tsx
200
204
  var import_node_fs = __toESM(require("node:fs"), 1);
201
- var import_node_path = __toESM(require("node:path"), 1);
205
+ var import_node_path2 = __toESM(require("node:path"), 1);
202
206
 
203
207
  // src/router-runtime.tsx
204
208
  var import_react = __toESM(require("react"), 1);
@@ -283,7 +287,7 @@ function walkFiles(dir) {
283
287
  continue;
284
288
  }
285
289
  for (const entry of import_node_fs.default.readdirSync(current, { withFileTypes: true })) {
286
- const fullPath = import_node_path.default.join(current, entry.name);
290
+ const fullPath = import_node_path2.default.join(current, entry.name);
287
291
  if (entry.isDirectory()) {
288
292
  stack.push(fullPath);
289
293
  } else if (entry.isFile()) {
@@ -313,7 +317,7 @@ function toRouteEntry(pagesDir, filePath) {
313
317
  if (!normalized.endsWith("/index.js")) {
314
318
  return null;
315
319
  }
316
- const relativeDir = import_node_path.default.dirname(import_node_path.default.relative(pagesDir, filePath)).replace(/\\/g, "/");
320
+ const relativeDir = import_node_path2.default.dirname(import_node_path2.default.relative(pagesDir, filePath)).replace(/\\/g, "/");
317
321
  const segments = relativeDir === "." ? [] : relativeDir.split("/").filter(Boolean);
318
322
  const staticCount = segments.filter((segment) => !segment.startsWith("[")).length;
319
323
  return {
@@ -456,8 +460,8 @@ function isRouteAbort(value) {
456
460
  }
457
461
  function createFileRouter(options) {
458
462
  const pagesDir = options.pagesDir;
459
- const layoutPath = import_node_path.default.join(pagesDir, "layout.js");
460
- const errorPath = import_node_path.default.join(pagesDir, "errors.js");
463
+ const layoutPath = import_node_path2.default.join(pagesDir, "layout.js");
464
+ const errorPath = import_node_path2.default.join(pagesDir, "errors.js");
461
465
  function buildRouteEntries() {
462
466
  const files = walkFiles(pagesDir);
463
467
  return files.map((filePath) => toRouteEntry(pagesDir, filePath)).filter((entry) => entry !== null).sort((a, b) => {
@@ -560,7 +564,7 @@ function parseSearchParams(query) {
560
564
 
561
565
  // src/http.ts
562
566
  var import_node_fs2 = __toESM(require("node:fs"), 1);
563
- var import_node_path2 = __toESM(require("node:path"), 1);
567
+ var import_node_path3 = __toESM(require("node:path"), 1);
564
568
  var import_node_child_process = require("node:child_process");
565
569
  function createInitialHtmlErrorMarkup(message) {
566
570
  return `<main style="font-family:system-ui,sans-serif;padding:24px"><h1 style="margin:0 0 12px">500</h1><p style="margin:0">${message}</p></main>`;
@@ -568,6 +572,7 @@ function createInitialHtmlErrorMarkup(message) {
568
572
  var INITIAL_HTML_WORKER_SCRIPT = `
569
573
  const path = require("node:path");
570
574
  const Module = require("node:module");
575
+ const { Writable } = require("node:stream");
571
576
 
572
577
  const pagesDir = process.env.WEBFRAMEZ_REACT_PAGES_DIR || "";
573
578
  if (!pagesDir) {
@@ -597,6 +602,41 @@ const reactDomPkg = require.resolve("react-dom/package.json", {
597
602
  const reactDomServer = require(path.join(path.dirname(reactDomPkg), "server.node.js"));
598
603
  const router = createFileRouter({ pagesDir });
599
604
 
605
+ function renderHtml(model) {
606
+ return new Promise((resolve, reject) => {
607
+ let didError = false;
608
+ const chunks = [];
609
+ const writable = new Writable({
610
+ write(chunk, _encoding, callback) {
611
+ const normalized =
612
+ typeof chunk === "string" ? Buffer.from(chunk) : Buffer.from(chunk);
613
+ chunks.push(normalized);
614
+ callback();
615
+ }
616
+ });
617
+
618
+ writable.on("finish", () => {
619
+ if (didError) {
620
+ reject(new Error("Initial HTML stream finished after a render error."));
621
+ return;
622
+ }
623
+
624
+ resolve(Buffer.concat(chunks).toString("utf8"));
625
+ });
626
+ writable.on("error", reject);
627
+
628
+ const stream = reactDomServer.renderToPipeableStream(model, {
629
+ onAllReady() {
630
+ stream.pipe(writable);
631
+ },
632
+ onError(error) {
633
+ didError = true;
634
+ reject(error);
635
+ }
636
+ });
637
+ });
638
+ }
639
+
600
640
  process.on("message", async (message) => {
601
641
  if (!message || message.type !== "render") {
602
642
  return;
@@ -611,7 +651,7 @@ process.on("message", async (message) => {
611
651
  searchParams: message.payload.searchParams || {},
612
652
  cookies: message.payload.cookies || {},
613
653
  });
614
- const html = reactDomServer.renderToString(resolved.model);
654
+ const html = await renderHtml(resolved.model);
615
655
  if (typeof process.send === "function") {
616
656
  process.send({ id: message.id, ok: true, html });
617
657
  }
@@ -800,10 +840,10 @@ function parseCookies(rawCookieHeader) {
800
840
  }
801
841
  function createNodeRequestHandler(options) {
802
842
  const devServerId = `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
803
- const distRootDir = import_node_path2.default.resolve(options.distRootDir);
804
- const pagesDir = import_node_path2.default.resolve(options.pagesDir ?? import_node_path2.default.join(distRootDir, "pages"));
805
- const manifestPath = import_node_path2.default.resolve(
806
- options.manifestPath ?? import_node_path2.default.join(distRootDir, "react-client-manifest.json")
843
+ const distRootDir = import_node_path3.default.resolve(options.distRootDir);
844
+ const pagesDir = import_node_path3.default.resolve(options.pagesDir ?? import_node_path3.default.join(distRootDir, "pages"));
845
+ const manifestPath = import_node_path3.default.resolve(
846
+ options.manifestPath ?? import_node_path3.default.join(distRootDir, "react-client-manifest.json")
807
847
  );
808
848
  const assetsPrefix = options.assetsPrefix ?? "/assets/";
809
849
  const rscPath = options.rscPath ?? "/rsc";
@@ -892,13 +932,13 @@ function createNodeRequestHandler(options) {
892
932
  }
893
933
  if (url.pathname.startsWith(assetsPrefix)) {
894
934
  const relative = url.pathname.slice(assetsPrefix.length);
895
- const filePath = import_node_path2.default.resolve(distRootDir, relative);
935
+ const filePath = import_node_path3.default.resolve(distRootDir, relative);
896
936
  if (!filePath.startsWith(distRootDir)) {
897
937
  res.statusCode = 400;
898
938
  res.end("Invalid path");
899
939
  return;
900
940
  }
901
- const ext = import_node_path2.default.extname(filePath);
941
+ const ext = import_node_path3.default.extname(filePath);
902
942
  if (ext === ".js" || ext === ".mjs") {
903
943
  res.setHeader("Content-Type", "text/javascript; charset=utf-8");
904
944
  } else if (ext === ".json") {
@@ -989,8 +1029,8 @@ function createNodeRequestHandler(options) {
989
1029
  }
990
1030
 
991
1031
  // src/webframez-core.ts
992
- function normalizeMountPath(path3) {
993
- const trimmed = (path3 || "").trim();
1032
+ function normalizeMountPath(path4) {
1033
+ const trimmed = (path4 || "").trim();
994
1034
  let normalized = trimmed.startsWith("/") ? trimmed : `/${trimmed}`;
995
1035
  if (normalized.endsWith("/**")) {
996
1036
  normalized = normalized.slice(0, -3);
@@ -1002,8 +1042,8 @@ function normalizeMountPath(path3) {
1002
1042
  }
1003
1043
  return normalized || "/";
1004
1044
  }
1005
- function buildRenderDefaults(path3) {
1006
- const mountPath = normalizeMountPath(path3);
1045
+ function buildRenderDefaults(path4) {
1046
+ const mountPath = normalizeMountPath(path4);
1007
1047
  const routePath = mountPath === "/" ? "/*" : `${mountPath}/*`;
1008
1048
  if (mountPath === "/") {
1009
1049
  return {
@@ -1055,30 +1095,30 @@ function normalizeMethods(method) {
1055
1095
  }
1056
1096
  return Array.isArray(method) ? method : [method];
1057
1097
  }
1058
- function registerByMethod(route, method, path3, component, routeOptions) {
1098
+ function registerByMethod(route, method, path4, component, routeOptions) {
1059
1099
  if (method === "GET") {
1060
- route.get(path3, component, routeOptions);
1100
+ route.get(path4, component, routeOptions);
1061
1101
  return;
1062
1102
  }
1063
1103
  if (method === "POST") {
1064
- route.post(path3, component, routeOptions);
1104
+ route.post(path4, component, routeOptions);
1065
1105
  return;
1066
1106
  }
1067
1107
  if (method === "PUT") {
1068
- route.put(path3, component, routeOptions);
1108
+ route.put(path4, component, routeOptions);
1069
1109
  return;
1070
1110
  }
1071
- route.delete(path3, component, routeOptions);
1111
+ route.delete(path4, component, routeOptions);
1072
1112
  }
1073
1113
  function registerRouteRenderer(route, methodName) {
1074
1114
  route.extend(methodName, () => {
1075
- return (path3, options) => {
1115
+ return (path4, options) => {
1076
1116
  if (!options || !options.distRootDir) {
1077
1117
  throw new Error(
1078
1118
  `Route.${methodName} requires at least { distRootDir }`
1079
1119
  );
1080
1120
  }
1081
- const defaults = buildRenderDefaults(path3);
1121
+ const defaults = buildRenderDefaults(path4);
1082
1122
  const {
1083
1123
  method,
1084
1124
  routeOptions,
package/dist/index.js CHANGED
@@ -7,10 +7,11 @@ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require
7
7
  });
8
8
 
9
9
  // src/server.ts
10
+ import path from "node:path";
10
11
  import { Writable } from "node:stream";
12
+ import { createRequire } from "node:module";
11
13
  import { renderToPipeableStream } from "react-server-dom-webpack/server";
12
14
  import { createFromReadableStream } from "react-server-dom-webpack/client.node";
13
- import { renderToString } from "react-dom/server";
14
15
  function defaultOnError(err) {
15
16
  console.error("[webframez-react] RSC render error", err);
16
17
  }
@@ -109,6 +110,9 @@ function createReadableStreamFromString(value) {
109
110
  });
110
111
  }
111
112
  async function renderHtmlFromFlightData(flightData, options) {
113
+ const appRequire = createRequire(path.join(process.cwd(), "__webframez_react_server__.js"));
114
+ const reactDomPkg = appRequire.resolve("react-dom/package.json");
115
+ const { renderToString } = appRequire(path.join(path.dirname(reactDomPkg), "server.node.js"));
112
116
  const model = await createFromReadableStream(createReadableStreamFromString(flightData), {
113
117
  serverConsumerManifest: {
114
118
  moduleMap: options.moduleMap ?? {},
@@ -162,7 +166,7 @@ function createRSCHandler(options) {
162
166
 
163
167
  // src/file-router.tsx
164
168
  import fs from "node:fs";
165
- import path from "node:path";
169
+ import path2 from "node:path";
166
170
 
167
171
  // src/router-runtime.tsx
168
172
  import React from "react";
@@ -247,7 +251,7 @@ function walkFiles(dir) {
247
251
  continue;
248
252
  }
249
253
  for (const entry of fs.readdirSync(current, { withFileTypes: true })) {
250
- const fullPath = path.join(current, entry.name);
254
+ const fullPath = path2.join(current, entry.name);
251
255
  if (entry.isDirectory()) {
252
256
  stack.push(fullPath);
253
257
  } else if (entry.isFile()) {
@@ -277,7 +281,7 @@ function toRouteEntry(pagesDir, filePath) {
277
281
  if (!normalized.endsWith("/index.js")) {
278
282
  return null;
279
283
  }
280
- const relativeDir = path.dirname(path.relative(pagesDir, filePath)).replace(/\\/g, "/");
284
+ const relativeDir = path2.dirname(path2.relative(pagesDir, filePath)).replace(/\\/g, "/");
281
285
  const segments = relativeDir === "." ? [] : relativeDir.split("/").filter(Boolean);
282
286
  const staticCount = segments.filter((segment) => !segment.startsWith("[")).length;
283
287
  return {
@@ -420,8 +424,8 @@ function isRouteAbort(value) {
420
424
  }
421
425
  function createFileRouter(options) {
422
426
  const pagesDir = options.pagesDir;
423
- const layoutPath = path.join(pagesDir, "layout.js");
424
- const errorPath = path.join(pagesDir, "errors.js");
427
+ const layoutPath = path2.join(pagesDir, "layout.js");
428
+ const errorPath = path2.join(pagesDir, "errors.js");
425
429
  function buildRouteEntries() {
426
430
  const files = walkFiles(pagesDir);
427
431
  return files.map((filePath) => toRouteEntry(pagesDir, filePath)).filter((entry) => entry !== null).sort((a, b) => {
@@ -524,7 +528,7 @@ function parseSearchParams(query) {
524
528
 
525
529
  // src/http.ts
526
530
  import fs2 from "node:fs";
527
- import path2 from "node:path";
531
+ import path3 from "node:path";
528
532
  import { spawn } from "node:child_process";
529
533
  function createInitialHtmlErrorMarkup(message) {
530
534
  return `<main style="font-family:system-ui,sans-serif;padding:24px"><h1 style="margin:0 0 12px">500</h1><p style="margin:0">${message}</p></main>`;
@@ -532,6 +536,7 @@ function createInitialHtmlErrorMarkup(message) {
532
536
  var INITIAL_HTML_WORKER_SCRIPT = `
533
537
  const path = require("node:path");
534
538
  const Module = require("node:module");
539
+ const { Writable } = require("node:stream");
535
540
 
536
541
  const pagesDir = process.env.WEBFRAMEZ_REACT_PAGES_DIR || "";
537
542
  if (!pagesDir) {
@@ -561,6 +566,41 @@ const reactDomPkg = require.resolve("react-dom/package.json", {
561
566
  const reactDomServer = require(path.join(path.dirname(reactDomPkg), "server.node.js"));
562
567
  const router = createFileRouter({ pagesDir });
563
568
 
569
+ function renderHtml(model) {
570
+ return new Promise((resolve, reject) => {
571
+ let didError = false;
572
+ const chunks = [];
573
+ const writable = new Writable({
574
+ write(chunk, _encoding, callback) {
575
+ const normalized =
576
+ typeof chunk === "string" ? Buffer.from(chunk) : Buffer.from(chunk);
577
+ chunks.push(normalized);
578
+ callback();
579
+ }
580
+ });
581
+
582
+ writable.on("finish", () => {
583
+ if (didError) {
584
+ reject(new Error("Initial HTML stream finished after a render error."));
585
+ return;
586
+ }
587
+
588
+ resolve(Buffer.concat(chunks).toString("utf8"));
589
+ });
590
+ writable.on("error", reject);
591
+
592
+ const stream = reactDomServer.renderToPipeableStream(model, {
593
+ onAllReady() {
594
+ stream.pipe(writable);
595
+ },
596
+ onError(error) {
597
+ didError = true;
598
+ reject(error);
599
+ }
600
+ });
601
+ });
602
+ }
603
+
564
604
  process.on("message", async (message) => {
565
605
  if (!message || message.type !== "render") {
566
606
  return;
@@ -575,7 +615,7 @@ process.on("message", async (message) => {
575
615
  searchParams: message.payload.searchParams || {},
576
616
  cookies: message.payload.cookies || {},
577
617
  });
578
- const html = reactDomServer.renderToString(resolved.model);
618
+ const html = await renderHtml(resolved.model);
579
619
  if (typeof process.send === "function") {
580
620
  process.send({ id: message.id, ok: true, html });
581
621
  }
@@ -764,10 +804,10 @@ function parseCookies(rawCookieHeader) {
764
804
  }
765
805
  function createNodeRequestHandler(options) {
766
806
  const devServerId = `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
767
- const distRootDir = path2.resolve(options.distRootDir);
768
- const pagesDir = path2.resolve(options.pagesDir ?? path2.join(distRootDir, "pages"));
769
- const manifestPath = path2.resolve(
770
- options.manifestPath ?? path2.join(distRootDir, "react-client-manifest.json")
807
+ const distRootDir = path3.resolve(options.distRootDir);
808
+ const pagesDir = path3.resolve(options.pagesDir ?? path3.join(distRootDir, "pages"));
809
+ const manifestPath = path3.resolve(
810
+ options.manifestPath ?? path3.join(distRootDir, "react-client-manifest.json")
771
811
  );
772
812
  const assetsPrefix = options.assetsPrefix ?? "/assets/";
773
813
  const rscPath = options.rscPath ?? "/rsc";
@@ -856,13 +896,13 @@ function createNodeRequestHandler(options) {
856
896
  }
857
897
  if (url.pathname.startsWith(assetsPrefix)) {
858
898
  const relative = url.pathname.slice(assetsPrefix.length);
859
- const filePath = path2.resolve(distRootDir, relative);
899
+ const filePath = path3.resolve(distRootDir, relative);
860
900
  if (!filePath.startsWith(distRootDir)) {
861
901
  res.statusCode = 400;
862
902
  res.end("Invalid path");
863
903
  return;
864
904
  }
865
- const ext = path2.extname(filePath);
905
+ const ext = path3.extname(filePath);
866
906
  if (ext === ".js" || ext === ".mjs") {
867
907
  res.setHeader("Content-Type", "text/javascript; charset=utf-8");
868
908
  } else if (ext === ".json") {
@@ -953,8 +993,8 @@ function createNodeRequestHandler(options) {
953
993
  }
954
994
 
955
995
  // src/webframez-core.ts
956
- function normalizeMountPath(path3) {
957
- const trimmed = (path3 || "").trim();
996
+ function normalizeMountPath(path4) {
997
+ const trimmed = (path4 || "").trim();
958
998
  let normalized = trimmed.startsWith("/") ? trimmed : `/${trimmed}`;
959
999
  if (normalized.endsWith("/**")) {
960
1000
  normalized = normalized.slice(0, -3);
@@ -966,8 +1006,8 @@ function normalizeMountPath(path3) {
966
1006
  }
967
1007
  return normalized || "/";
968
1008
  }
969
- function buildRenderDefaults(path3) {
970
- const mountPath = normalizeMountPath(path3);
1009
+ function buildRenderDefaults(path4) {
1010
+ const mountPath = normalizeMountPath(path4);
971
1011
  const routePath = mountPath === "/" ? "/*" : `${mountPath}/*`;
972
1012
  if (mountPath === "/") {
973
1013
  return {
@@ -1019,30 +1059,30 @@ function normalizeMethods(method) {
1019
1059
  }
1020
1060
  return Array.isArray(method) ? method : [method];
1021
1061
  }
1022
- function registerByMethod(route, method, path3, component, routeOptions) {
1062
+ function registerByMethod(route, method, path4, component, routeOptions) {
1023
1063
  if (method === "GET") {
1024
- route.get(path3, component, routeOptions);
1064
+ route.get(path4, component, routeOptions);
1025
1065
  return;
1026
1066
  }
1027
1067
  if (method === "POST") {
1028
- route.post(path3, component, routeOptions);
1068
+ route.post(path4, component, routeOptions);
1029
1069
  return;
1030
1070
  }
1031
1071
  if (method === "PUT") {
1032
- route.put(path3, component, routeOptions);
1072
+ route.put(path4, component, routeOptions);
1033
1073
  return;
1034
1074
  }
1035
- route.delete(path3, component, routeOptions);
1075
+ route.delete(path4, component, routeOptions);
1036
1076
  }
1037
1077
  function registerRouteRenderer(route, methodName) {
1038
1078
  route.extend(methodName, () => {
1039
- return (path3, options) => {
1079
+ return (path4, options) => {
1040
1080
  if (!options || !options.distRootDir) {
1041
1081
  throw new Error(
1042
1082
  `Route.${methodName} requires at least { distRootDir }`
1043
1083
  );
1044
1084
  }
1045
- const defaults = buildRenderDefaults(path3);
1085
+ const defaults = buildRenderDefaults(path4);
1046
1086
  const {
1047
1087
  method,
1048
1088
  routeOptions,
@@ -36,14 +36,15 @@ module.exports = __toCommonJS(webframez_core_exports);
36
36
 
37
37
  // src/http.ts
38
38
  var import_node_fs2 = __toESM(require("node:fs"), 1);
39
- var import_node_path2 = __toESM(require("node:path"), 1);
39
+ var import_node_path3 = __toESM(require("node:path"), 1);
40
40
  var import_node_child_process = require("node:child_process");
41
41
 
42
42
  // src/server.ts
43
+ var import_node_path = __toESM(require("node:path"), 1);
43
44
  var import_node_stream = require("node:stream");
45
+ var import_node_module = require("node:module");
44
46
  var import_server = require("react-server-dom-webpack/server");
45
47
  var import_client = require("react-server-dom-webpack/client.node");
46
- var import_server2 = require("react-dom/server");
47
48
  function defaultOnError(err) {
48
49
  console.error("[webframez-react] RSC render error", err);
49
50
  }
@@ -142,6 +143,9 @@ function createReadableStreamFromString(value) {
142
143
  });
143
144
  }
144
145
  async function renderHtmlFromFlightData(flightData, options) {
146
+ const appRequire = (0, import_node_module.createRequire)(import_node_path.default.join(process.cwd(), "__webframez_react_server__.js"));
147
+ const reactDomPkg = appRequire.resolve("react-dom/package.json");
148
+ const { renderToString } = appRequire(import_node_path.default.join(import_node_path.default.dirname(reactDomPkg), "server.node.js"));
145
149
  const model = await (0, import_client.createFromReadableStream)(createReadableStreamFromString(flightData), {
146
150
  serverConsumerManifest: {
147
151
  moduleMap: options.moduleMap ?? {},
@@ -149,7 +153,7 @@ async function renderHtmlFromFlightData(flightData, options) {
149
153
  moduleLoading: null
150
154
  }
151
155
  });
152
- return (0, import_server2.renderToString)(model);
156
+ return renderToString(model);
153
157
  }
154
158
  function sendRSC(res, model, options = {}) {
155
159
  const {
@@ -167,7 +171,7 @@ function sendRSC(res, model, options = {}) {
167
171
 
168
172
  // src/file-router.tsx
169
173
  var import_node_fs = __toESM(require("node:fs"), 1);
170
- var import_node_path = __toESM(require("node:path"), 1);
174
+ var import_node_path2 = __toESM(require("node:path"), 1);
171
175
 
172
176
  // src/router-runtime.tsx
173
177
  var import_react = __toESM(require("react"), 1);
@@ -252,7 +256,7 @@ function walkFiles(dir) {
252
256
  continue;
253
257
  }
254
258
  for (const entry of import_node_fs.default.readdirSync(current, { withFileTypes: true })) {
255
- const fullPath = import_node_path.default.join(current, entry.name);
259
+ const fullPath = import_node_path2.default.join(current, entry.name);
256
260
  if (entry.isDirectory()) {
257
261
  stack.push(fullPath);
258
262
  } else if (entry.isFile()) {
@@ -282,7 +286,7 @@ function toRouteEntry(pagesDir, filePath) {
282
286
  if (!normalized.endsWith("/index.js")) {
283
287
  return null;
284
288
  }
285
- const relativeDir = import_node_path.default.dirname(import_node_path.default.relative(pagesDir, filePath)).replace(/\\/g, "/");
289
+ const relativeDir = import_node_path2.default.dirname(import_node_path2.default.relative(pagesDir, filePath)).replace(/\\/g, "/");
286
290
  const segments = relativeDir === "." ? [] : relativeDir.split("/").filter(Boolean);
287
291
  const staticCount = segments.filter((segment) => !segment.startsWith("[")).length;
288
292
  return {
@@ -425,8 +429,8 @@ function isRouteAbort(value) {
425
429
  }
426
430
  function createFileRouter(options) {
427
431
  const pagesDir = options.pagesDir;
428
- const layoutPath = import_node_path.default.join(pagesDir, "layout.js");
429
- const errorPath = import_node_path.default.join(pagesDir, "errors.js");
432
+ const layoutPath = import_node_path2.default.join(pagesDir, "layout.js");
433
+ const errorPath = import_node_path2.default.join(pagesDir, "errors.js");
430
434
  function buildRouteEntries() {
431
435
  const files = walkFiles(pagesDir);
432
436
  return files.map((filePath) => toRouteEntry(pagesDir, filePath)).filter((entry) => entry !== null).sort((a, b) => {
@@ -534,6 +538,7 @@ function createInitialHtmlErrorMarkup(message) {
534
538
  var INITIAL_HTML_WORKER_SCRIPT = `
535
539
  const path = require("node:path");
536
540
  const Module = require("node:module");
541
+ const { Writable } = require("node:stream");
537
542
 
538
543
  const pagesDir = process.env.WEBFRAMEZ_REACT_PAGES_DIR || "";
539
544
  if (!pagesDir) {
@@ -563,6 +568,41 @@ const reactDomPkg = require.resolve("react-dom/package.json", {
563
568
  const reactDomServer = require(path.join(path.dirname(reactDomPkg), "server.node.js"));
564
569
  const router = createFileRouter({ pagesDir });
565
570
 
571
+ function renderHtml(model) {
572
+ return new Promise((resolve, reject) => {
573
+ let didError = false;
574
+ const chunks = [];
575
+ const writable = new Writable({
576
+ write(chunk, _encoding, callback) {
577
+ const normalized =
578
+ typeof chunk === "string" ? Buffer.from(chunk) : Buffer.from(chunk);
579
+ chunks.push(normalized);
580
+ callback();
581
+ }
582
+ });
583
+
584
+ writable.on("finish", () => {
585
+ if (didError) {
586
+ reject(new Error("Initial HTML stream finished after a render error."));
587
+ return;
588
+ }
589
+
590
+ resolve(Buffer.concat(chunks).toString("utf8"));
591
+ });
592
+ writable.on("error", reject);
593
+
594
+ const stream = reactDomServer.renderToPipeableStream(model, {
595
+ onAllReady() {
596
+ stream.pipe(writable);
597
+ },
598
+ onError(error) {
599
+ didError = true;
600
+ reject(error);
601
+ }
602
+ });
603
+ });
604
+ }
605
+
566
606
  process.on("message", async (message) => {
567
607
  if (!message || message.type !== "render") {
568
608
  return;
@@ -577,7 +617,7 @@ process.on("message", async (message) => {
577
617
  searchParams: message.payload.searchParams || {},
578
618
  cookies: message.payload.cookies || {},
579
619
  });
580
- const html = reactDomServer.renderToString(resolved.model);
620
+ const html = await renderHtml(resolved.model);
581
621
  if (typeof process.send === "function") {
582
622
  process.send({ id: message.id, ok: true, html });
583
623
  }
@@ -766,10 +806,10 @@ function parseCookies(rawCookieHeader) {
766
806
  }
767
807
  function createNodeRequestHandler(options) {
768
808
  const devServerId = `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
769
- const distRootDir = import_node_path2.default.resolve(options.distRootDir);
770
- const pagesDir = import_node_path2.default.resolve(options.pagesDir ?? import_node_path2.default.join(distRootDir, "pages"));
771
- const manifestPath = import_node_path2.default.resolve(
772
- options.manifestPath ?? import_node_path2.default.join(distRootDir, "react-client-manifest.json")
809
+ const distRootDir = import_node_path3.default.resolve(options.distRootDir);
810
+ const pagesDir = import_node_path3.default.resolve(options.pagesDir ?? import_node_path3.default.join(distRootDir, "pages"));
811
+ const manifestPath = import_node_path3.default.resolve(
812
+ options.manifestPath ?? import_node_path3.default.join(distRootDir, "react-client-manifest.json")
773
813
  );
774
814
  const assetsPrefix = options.assetsPrefix ?? "/assets/";
775
815
  const rscPath = options.rscPath ?? "/rsc";
@@ -858,13 +898,13 @@ function createNodeRequestHandler(options) {
858
898
  }
859
899
  if (url.pathname.startsWith(assetsPrefix)) {
860
900
  const relative = url.pathname.slice(assetsPrefix.length);
861
- const filePath = import_node_path2.default.resolve(distRootDir, relative);
901
+ const filePath = import_node_path3.default.resolve(distRootDir, relative);
862
902
  if (!filePath.startsWith(distRootDir)) {
863
903
  res.statusCode = 400;
864
904
  res.end("Invalid path");
865
905
  return;
866
906
  }
867
- const ext = import_node_path2.default.extname(filePath);
907
+ const ext = import_node_path3.default.extname(filePath);
868
908
  if (ext === ".js" || ext === ".mjs") {
869
909
  res.setHeader("Content-Type", "text/javascript; charset=utf-8");
870
910
  } else if (ext === ".json") {
@@ -955,8 +995,8 @@ function createNodeRequestHandler(options) {
955
995
  }
956
996
 
957
997
  // src/webframez-core.ts
958
- function normalizeMountPath(path3) {
959
- const trimmed = (path3 || "").trim();
998
+ function normalizeMountPath(path4) {
999
+ const trimmed = (path4 || "").trim();
960
1000
  let normalized = trimmed.startsWith("/") ? trimmed : `/${trimmed}`;
961
1001
  if (normalized.endsWith("/**")) {
962
1002
  normalized = normalized.slice(0, -3);
@@ -968,8 +1008,8 @@ function normalizeMountPath(path3) {
968
1008
  }
969
1009
  return normalized || "/";
970
1010
  }
971
- function buildRenderDefaults(path3) {
972
- const mountPath = normalizeMountPath(path3);
1011
+ function buildRenderDefaults(path4) {
1012
+ const mountPath = normalizeMountPath(path4);
973
1013
  const routePath = mountPath === "/" ? "/*" : `${mountPath}/*`;
974
1014
  if (mountPath === "/") {
975
1015
  return {
@@ -1021,30 +1061,30 @@ function normalizeMethods(method) {
1021
1061
  }
1022
1062
  return Array.isArray(method) ? method : [method];
1023
1063
  }
1024
- function registerByMethod(route, method, path3, component, routeOptions) {
1064
+ function registerByMethod(route, method, path4, component, routeOptions) {
1025
1065
  if (method === "GET") {
1026
- route.get(path3, component, routeOptions);
1066
+ route.get(path4, component, routeOptions);
1027
1067
  return;
1028
1068
  }
1029
1069
  if (method === "POST") {
1030
- route.post(path3, component, routeOptions);
1070
+ route.post(path4, component, routeOptions);
1031
1071
  return;
1032
1072
  }
1033
1073
  if (method === "PUT") {
1034
- route.put(path3, component, routeOptions);
1074
+ route.put(path4, component, routeOptions);
1035
1075
  return;
1036
1076
  }
1037
- route.delete(path3, component, routeOptions);
1077
+ route.delete(path4, component, routeOptions);
1038
1078
  }
1039
1079
  function registerRouteRenderer(route, methodName) {
1040
1080
  route.extend(methodName, () => {
1041
- return (path3, options) => {
1081
+ return (path4, options) => {
1042
1082
  if (!options || !options.distRootDir) {
1043
1083
  throw new Error(
1044
1084
  `Route.${methodName} requires at least { distRootDir }`
1045
1085
  );
1046
1086
  }
1047
- const defaults = buildRenderDefaults(path3);
1087
+ const defaults = buildRenderDefaults(path4);
1048
1088
  const {
1049
1089
  method,
1050
1090
  routeOptions,
@@ -8,14 +8,15 @@ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require
8
8
 
9
9
  // src/http.ts
10
10
  import fs2 from "node:fs";
11
- import path2 from "node:path";
11
+ import path3 from "node:path";
12
12
  import { spawn } from "node:child_process";
13
13
 
14
14
  // src/server.ts
15
+ import path from "node:path";
15
16
  import { Writable } from "node:stream";
17
+ import { createRequire } from "node:module";
16
18
  import { renderToPipeableStream } from "react-server-dom-webpack/server";
17
19
  import { createFromReadableStream } from "react-server-dom-webpack/client.node";
18
- import { renderToString } from "react-dom/server";
19
20
  function defaultOnError(err) {
20
21
  console.error("[webframez-react] RSC render error", err);
21
22
  }
@@ -114,6 +115,9 @@ function createReadableStreamFromString(value) {
114
115
  });
115
116
  }
116
117
  async function renderHtmlFromFlightData(flightData, options) {
118
+ const appRequire = createRequire(path.join(process.cwd(), "__webframez_react_server__.js"));
119
+ const reactDomPkg = appRequire.resolve("react-dom/package.json");
120
+ const { renderToString } = appRequire(path.join(path.dirname(reactDomPkg), "server.node.js"));
117
121
  const model = await createFromReadableStream(createReadableStreamFromString(flightData), {
118
122
  serverConsumerManifest: {
119
123
  moduleMap: options.moduleMap ?? {},
@@ -139,7 +143,7 @@ function sendRSC(res, model, options = {}) {
139
143
 
140
144
  // src/file-router.tsx
141
145
  import fs from "node:fs";
142
- import path from "node:path";
146
+ import path2 from "node:path";
143
147
 
144
148
  // src/router-runtime.tsx
145
149
  import React from "react";
@@ -224,7 +228,7 @@ function walkFiles(dir) {
224
228
  continue;
225
229
  }
226
230
  for (const entry of fs.readdirSync(current, { withFileTypes: true })) {
227
- const fullPath = path.join(current, entry.name);
231
+ const fullPath = path2.join(current, entry.name);
228
232
  if (entry.isDirectory()) {
229
233
  stack.push(fullPath);
230
234
  } else if (entry.isFile()) {
@@ -254,7 +258,7 @@ function toRouteEntry(pagesDir, filePath) {
254
258
  if (!normalized.endsWith("/index.js")) {
255
259
  return null;
256
260
  }
257
- const relativeDir = path.dirname(path.relative(pagesDir, filePath)).replace(/\\/g, "/");
261
+ const relativeDir = path2.dirname(path2.relative(pagesDir, filePath)).replace(/\\/g, "/");
258
262
  const segments = relativeDir === "." ? [] : relativeDir.split("/").filter(Boolean);
259
263
  const staticCount = segments.filter((segment) => !segment.startsWith("[")).length;
260
264
  return {
@@ -397,8 +401,8 @@ function isRouteAbort(value) {
397
401
  }
398
402
  function createFileRouter(options) {
399
403
  const pagesDir = options.pagesDir;
400
- const layoutPath = path.join(pagesDir, "layout.js");
401
- const errorPath = path.join(pagesDir, "errors.js");
404
+ const layoutPath = path2.join(pagesDir, "layout.js");
405
+ const errorPath = path2.join(pagesDir, "errors.js");
402
406
  function buildRouteEntries() {
403
407
  const files = walkFiles(pagesDir);
404
408
  return files.map((filePath) => toRouteEntry(pagesDir, filePath)).filter((entry) => entry !== null).sort((a, b) => {
@@ -506,6 +510,7 @@ function createInitialHtmlErrorMarkup(message) {
506
510
  var INITIAL_HTML_WORKER_SCRIPT = `
507
511
  const path = require("node:path");
508
512
  const Module = require("node:module");
513
+ const { Writable } = require("node:stream");
509
514
 
510
515
  const pagesDir = process.env.WEBFRAMEZ_REACT_PAGES_DIR || "";
511
516
  if (!pagesDir) {
@@ -535,6 +540,41 @@ const reactDomPkg = require.resolve("react-dom/package.json", {
535
540
  const reactDomServer = require(path.join(path.dirname(reactDomPkg), "server.node.js"));
536
541
  const router = createFileRouter({ pagesDir });
537
542
 
543
+ function renderHtml(model) {
544
+ return new Promise((resolve, reject) => {
545
+ let didError = false;
546
+ const chunks = [];
547
+ const writable = new Writable({
548
+ write(chunk, _encoding, callback) {
549
+ const normalized =
550
+ typeof chunk === "string" ? Buffer.from(chunk) : Buffer.from(chunk);
551
+ chunks.push(normalized);
552
+ callback();
553
+ }
554
+ });
555
+
556
+ writable.on("finish", () => {
557
+ if (didError) {
558
+ reject(new Error("Initial HTML stream finished after a render error."));
559
+ return;
560
+ }
561
+
562
+ resolve(Buffer.concat(chunks).toString("utf8"));
563
+ });
564
+ writable.on("error", reject);
565
+
566
+ const stream = reactDomServer.renderToPipeableStream(model, {
567
+ onAllReady() {
568
+ stream.pipe(writable);
569
+ },
570
+ onError(error) {
571
+ didError = true;
572
+ reject(error);
573
+ }
574
+ });
575
+ });
576
+ }
577
+
538
578
  process.on("message", async (message) => {
539
579
  if (!message || message.type !== "render") {
540
580
  return;
@@ -549,7 +589,7 @@ process.on("message", async (message) => {
549
589
  searchParams: message.payload.searchParams || {},
550
590
  cookies: message.payload.cookies || {},
551
591
  });
552
- const html = reactDomServer.renderToString(resolved.model);
592
+ const html = await renderHtml(resolved.model);
553
593
  if (typeof process.send === "function") {
554
594
  process.send({ id: message.id, ok: true, html });
555
595
  }
@@ -738,10 +778,10 @@ function parseCookies(rawCookieHeader) {
738
778
  }
739
779
  function createNodeRequestHandler(options) {
740
780
  const devServerId = `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
741
- const distRootDir = path2.resolve(options.distRootDir);
742
- const pagesDir = path2.resolve(options.pagesDir ?? path2.join(distRootDir, "pages"));
743
- const manifestPath = path2.resolve(
744
- options.manifestPath ?? path2.join(distRootDir, "react-client-manifest.json")
781
+ const distRootDir = path3.resolve(options.distRootDir);
782
+ const pagesDir = path3.resolve(options.pagesDir ?? path3.join(distRootDir, "pages"));
783
+ const manifestPath = path3.resolve(
784
+ options.manifestPath ?? path3.join(distRootDir, "react-client-manifest.json")
745
785
  );
746
786
  const assetsPrefix = options.assetsPrefix ?? "/assets/";
747
787
  const rscPath = options.rscPath ?? "/rsc";
@@ -830,13 +870,13 @@ function createNodeRequestHandler(options) {
830
870
  }
831
871
  if (url.pathname.startsWith(assetsPrefix)) {
832
872
  const relative = url.pathname.slice(assetsPrefix.length);
833
- const filePath = path2.resolve(distRootDir, relative);
873
+ const filePath = path3.resolve(distRootDir, relative);
834
874
  if (!filePath.startsWith(distRootDir)) {
835
875
  res.statusCode = 400;
836
876
  res.end("Invalid path");
837
877
  return;
838
878
  }
839
- const ext = path2.extname(filePath);
879
+ const ext = path3.extname(filePath);
840
880
  if (ext === ".js" || ext === ".mjs") {
841
881
  res.setHeader("Content-Type", "text/javascript; charset=utf-8");
842
882
  } else if (ext === ".json") {
@@ -927,8 +967,8 @@ function createNodeRequestHandler(options) {
927
967
  }
928
968
 
929
969
  // src/webframez-core.ts
930
- function normalizeMountPath(path3) {
931
- const trimmed = (path3 || "").trim();
970
+ function normalizeMountPath(path4) {
971
+ const trimmed = (path4 || "").trim();
932
972
  let normalized = trimmed.startsWith("/") ? trimmed : `/${trimmed}`;
933
973
  if (normalized.endsWith("/**")) {
934
974
  normalized = normalized.slice(0, -3);
@@ -940,8 +980,8 @@ function normalizeMountPath(path3) {
940
980
  }
941
981
  return normalized || "/";
942
982
  }
943
- function buildRenderDefaults(path3) {
944
- const mountPath = normalizeMountPath(path3);
983
+ function buildRenderDefaults(path4) {
984
+ const mountPath = normalizeMountPath(path4);
945
985
  const routePath = mountPath === "/" ? "/*" : `${mountPath}/*`;
946
986
  if (mountPath === "/") {
947
987
  return {
@@ -993,30 +1033,30 @@ function normalizeMethods(method) {
993
1033
  }
994
1034
  return Array.isArray(method) ? method : [method];
995
1035
  }
996
- function registerByMethod(route, method, path3, component, routeOptions) {
1036
+ function registerByMethod(route, method, path4, component, routeOptions) {
997
1037
  if (method === "GET") {
998
- route.get(path3, component, routeOptions);
1038
+ route.get(path4, component, routeOptions);
999
1039
  return;
1000
1040
  }
1001
1041
  if (method === "POST") {
1002
- route.post(path3, component, routeOptions);
1042
+ route.post(path4, component, routeOptions);
1003
1043
  return;
1004
1044
  }
1005
1045
  if (method === "PUT") {
1006
- route.put(path3, component, routeOptions);
1046
+ route.put(path4, component, routeOptions);
1007
1047
  return;
1008
1048
  }
1009
- route.delete(path3, component, routeOptions);
1049
+ route.delete(path4, component, routeOptions);
1010
1050
  }
1011
1051
  function registerRouteRenderer(route, methodName) {
1012
1052
  route.extend(methodName, () => {
1013
- return (path3, options) => {
1053
+ return (path4, options) => {
1014
1054
  if (!options || !options.distRootDir) {
1015
1055
  throw new Error(
1016
1056
  `Route.${methodName} requires at least { distRootDir }`
1017
1057
  );
1018
1058
  }
1019
- const defaults = buildRenderDefaults(path3);
1059
+ const defaults = buildRenderDefaults(path4);
1020
1060
  const {
1021
1061
  method,
1022
1062
  routeOptions,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@webtypen/webframez-react",
3
- "version": "0.0.10",
3
+ "version": "0.0.12",
4
4
  "description": "TypeScript React RSC addition for @webtypen/webframez-core",
5
5
  "homepage": "https://webtypen.de/",
6
6
  "author": {