@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 +55 -15
- package/dist/http.js +54 -14
- package/dist/index.cjs +66 -26
- package/dist/index.js +65 -25
- package/dist/webframez-core.cjs +66 -26
- package/dist/webframez-core.js +65 -25
- package/package.json +1 -1
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
|
|
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
|
|
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
|
|
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 =
|
|
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 =
|
|
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 =
|
|
426
|
-
const errorPath =
|
|
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 =
|
|
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 =
|
|
767
|
-
const pagesDir =
|
|
768
|
-
const manifestPath =
|
|
769
|
-
options.manifestPath ??
|
|
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 =
|
|
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 =
|
|
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
|
|
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
|
|
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 =
|
|
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 =
|
|
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 =
|
|
401
|
-
const errorPath =
|
|
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 =
|
|
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 =
|
|
742
|
-
const pagesDir =
|
|
743
|
-
const manifestPath =
|
|
744
|
-
options.manifestPath ??
|
|
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 =
|
|
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 =
|
|
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
|
|
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
|
|
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 =
|
|
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 =
|
|
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 =
|
|
460
|
-
const errorPath =
|
|
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
|
|
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 =
|
|
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 =
|
|
804
|
-
const pagesDir =
|
|
805
|
-
const manifestPath =
|
|
806
|
-
options.manifestPath ??
|
|
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 =
|
|
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 =
|
|
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(
|
|
993
|
-
const trimmed = (
|
|
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(
|
|
1006
|
-
const mountPath = normalizeMountPath(
|
|
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,
|
|
1098
|
+
function registerByMethod(route, method, path4, component, routeOptions) {
|
|
1059
1099
|
if (method === "GET") {
|
|
1060
|
-
route.get(
|
|
1100
|
+
route.get(path4, component, routeOptions);
|
|
1061
1101
|
return;
|
|
1062
1102
|
}
|
|
1063
1103
|
if (method === "POST") {
|
|
1064
|
-
route.post(
|
|
1104
|
+
route.post(path4, component, routeOptions);
|
|
1065
1105
|
return;
|
|
1066
1106
|
}
|
|
1067
1107
|
if (method === "PUT") {
|
|
1068
|
-
route.put(
|
|
1108
|
+
route.put(path4, component, routeOptions);
|
|
1069
1109
|
return;
|
|
1070
1110
|
}
|
|
1071
|
-
route.delete(
|
|
1111
|
+
route.delete(path4, component, routeOptions);
|
|
1072
1112
|
}
|
|
1073
1113
|
function registerRouteRenderer(route, methodName) {
|
|
1074
1114
|
route.extend(methodName, () => {
|
|
1075
|
-
return (
|
|
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(
|
|
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
|
|
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 =
|
|
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 =
|
|
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 =
|
|
424
|
-
const errorPath =
|
|
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
|
|
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 =
|
|
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 =
|
|
768
|
-
const pagesDir =
|
|
769
|
-
const manifestPath =
|
|
770
|
-
options.manifestPath ??
|
|
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 =
|
|
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 =
|
|
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(
|
|
957
|
-
const trimmed = (
|
|
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(
|
|
970
|
-
const mountPath = normalizeMountPath(
|
|
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,
|
|
1062
|
+
function registerByMethod(route, method, path4, component, routeOptions) {
|
|
1023
1063
|
if (method === "GET") {
|
|
1024
|
-
route.get(
|
|
1064
|
+
route.get(path4, component, routeOptions);
|
|
1025
1065
|
return;
|
|
1026
1066
|
}
|
|
1027
1067
|
if (method === "POST") {
|
|
1028
|
-
route.post(
|
|
1068
|
+
route.post(path4, component, routeOptions);
|
|
1029
1069
|
return;
|
|
1030
1070
|
}
|
|
1031
1071
|
if (method === "PUT") {
|
|
1032
|
-
route.put(
|
|
1072
|
+
route.put(path4, component, routeOptions);
|
|
1033
1073
|
return;
|
|
1034
1074
|
}
|
|
1035
|
-
route.delete(
|
|
1075
|
+
route.delete(path4, component, routeOptions);
|
|
1036
1076
|
}
|
|
1037
1077
|
function registerRouteRenderer(route, methodName) {
|
|
1038
1078
|
route.extend(methodName, () => {
|
|
1039
|
-
return (
|
|
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(
|
|
1085
|
+
const defaults = buildRenderDefaults(path4);
|
|
1046
1086
|
const {
|
|
1047
1087
|
method,
|
|
1048
1088
|
routeOptions,
|
package/dist/webframez-core.cjs
CHANGED
|
@@ -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
|
|
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
|
|
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
|
|
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 =
|
|
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 =
|
|
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 =
|
|
429
|
-
const errorPath =
|
|
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 =
|
|
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 =
|
|
770
|
-
const pagesDir =
|
|
771
|
-
const manifestPath =
|
|
772
|
-
options.manifestPath ??
|
|
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 =
|
|
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 =
|
|
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(
|
|
959
|
-
const trimmed = (
|
|
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(
|
|
972
|
-
const mountPath = normalizeMountPath(
|
|
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,
|
|
1064
|
+
function registerByMethod(route, method, path4, component, routeOptions) {
|
|
1025
1065
|
if (method === "GET") {
|
|
1026
|
-
route.get(
|
|
1066
|
+
route.get(path4, component, routeOptions);
|
|
1027
1067
|
return;
|
|
1028
1068
|
}
|
|
1029
1069
|
if (method === "POST") {
|
|
1030
|
-
route.post(
|
|
1070
|
+
route.post(path4, component, routeOptions);
|
|
1031
1071
|
return;
|
|
1032
1072
|
}
|
|
1033
1073
|
if (method === "PUT") {
|
|
1034
|
-
route.put(
|
|
1074
|
+
route.put(path4, component, routeOptions);
|
|
1035
1075
|
return;
|
|
1036
1076
|
}
|
|
1037
|
-
route.delete(
|
|
1077
|
+
route.delete(path4, component, routeOptions);
|
|
1038
1078
|
}
|
|
1039
1079
|
function registerRouteRenderer(route, methodName) {
|
|
1040
1080
|
route.extend(methodName, () => {
|
|
1041
|
-
return (
|
|
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(
|
|
1087
|
+
const defaults = buildRenderDefaults(path4);
|
|
1048
1088
|
const {
|
|
1049
1089
|
method,
|
|
1050
1090
|
routeOptions,
|
package/dist/webframez-core.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
|
|
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
|
|
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 =
|
|
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 =
|
|
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 =
|
|
401
|
-
const errorPath =
|
|
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 =
|
|
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 =
|
|
742
|
-
const pagesDir =
|
|
743
|
-
const manifestPath =
|
|
744
|
-
options.manifestPath ??
|
|
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 =
|
|
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 =
|
|
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(
|
|
931
|
-
const trimmed = (
|
|
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(
|
|
944
|
-
const mountPath = normalizeMountPath(
|
|
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,
|
|
1036
|
+
function registerByMethod(route, method, path4, component, routeOptions) {
|
|
997
1037
|
if (method === "GET") {
|
|
998
|
-
route.get(
|
|
1038
|
+
route.get(path4, component, routeOptions);
|
|
999
1039
|
return;
|
|
1000
1040
|
}
|
|
1001
1041
|
if (method === "POST") {
|
|
1002
|
-
route.post(
|
|
1042
|
+
route.post(path4, component, routeOptions);
|
|
1003
1043
|
return;
|
|
1004
1044
|
}
|
|
1005
1045
|
if (method === "PUT") {
|
|
1006
|
-
route.put(
|
|
1046
|
+
route.put(path4, component, routeOptions);
|
|
1007
1047
|
return;
|
|
1008
1048
|
}
|
|
1009
|
-
route.delete(
|
|
1049
|
+
route.delete(path4, component, routeOptions);
|
|
1010
1050
|
}
|
|
1011
1051
|
function registerRouteRenderer(route, methodName) {
|
|
1012
1052
|
route.extend(methodName, () => {
|
|
1013
|
-
return (
|
|
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(
|
|
1059
|
+
const defaults = buildRenderDefaults(path4);
|
|
1020
1060
|
const {
|
|
1021
1061
|
method,
|
|
1022
1062
|
routeOptions,
|