@webtypen/webframez-react 0.0.5 → 0.0.7
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/client.cjs +1 -1
- package/dist/client.js +1 -1
- package/dist/http.cjs +70 -0
- package/dist/http.js +70 -0
- package/dist/index.cjs +70 -0
- package/dist/index.js +70 -0
- package/dist/webframez-core.cjs +70 -0
- package/dist/webframez-core.js +70 -0
- package/package.json +1 -1
package/dist/client.cjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
var
|
|
1
|
+
var P=Object.create;var f=Object.defineProperty;var O=Object.getOwnPropertyDescriptor;var T=Object.getOwnPropertyNames;var N=Object.getPrototypeOf,U=Object.prototype.hasOwnProperty;var $=(e,t)=>{for(var n in t)f(e,n,{get:t[n],enumerable:!0})},_=(e,t,n,o)=>{if(t&&typeof t=="object"||typeof t=="function")for(let i of T(t))!U.call(e,i)&&i!==n&&f(e,i,{get:()=>t[i],enumerable:!(o=O(t,i))||o.enumerable});return e};var M=(e,t,n)=>(n=e!=null?P(N(e)):{},_(t||!e||!e.__esModule?f(n,"default",{value:e,enumerable:!0}):n,e)),H=e=>_(f({},"__esModule",{value:!0}),e);var X={};$(X,{mountWebframezClient:()=>j,useCookie:()=>z,useRouter:()=>G});module.exports=H(X);var r=M(require("react"),1),S=require("react-dom/client"),u=require("react-server-dom-webpack/client"),a=require("react/jsx-runtime"),W={push:()=>{},replace:()=>{},refresh:()=>{}},p=typeof r.default.createContext=="function"?r.default.createContext(null):null,k="__WEBFRAMEZ_ROUTER__",D="[data-webframez-head='true']";function F(){return typeof window>"u"?null:window[k]??null}function B(e){typeof window>"u"||(window[k]=e)}function v(){let e={},t=typeof document>"u"?"":document.cookie;if(!t||t.trim()==="")return e;for(let n of t.split(";")){let o=n.trim();if(!o)continue;let i=o.indexOf("="),d=i>=0?o.slice(0,i).trim():o,c=i>=0?o.slice(i+1):"";d&&(e[d]=decodeURIComponent(c))}return e}function A(e,t,n={}){let o=[`${e}=${encodeURIComponent(t)}`];return n.path&&o.push(`Path=${n.path}`),n.domain&&o.push(`Domain=${n.domain}`),typeof n.maxAge=="number"&&o.push(`Max-Age=${Math.floor(n.maxAge)}`),n.expires&&o.push(`Expires=${n.expires.toUTCString()}`),n.sameSite&&o.push(`SameSite=${n.sameSite}`),n.secure&&o.push("Secure"),o.join("; ")}function z(){return r.default.useMemo(()=>({all:()=>v(),get:e=>v()[e],set:(e,t,n)=>{typeof document>"u"||(document.cookie=A(e,t,n))},remove:(e,t)=>{typeof document>"u"||(document.cookie=A(e,"",{...t??{},maxAge:0}))}}),[])}function G(){let e=p?r.default.useContext(p):null;if(!e){let t=F();if(t)return t;if(typeof window>"u")return W;throw new Error("useRouter must be used inside mountWebframezClient()")}return e}function x({active:e}){return(0,a.jsx)("div",{style:{position:"fixed",top:0,left:0,width:"100%",height:3,zIndex:9999,transformOrigin:"left",transform:e?"scaleX(1)":"scaleX(0)",transition:"transform 180ms ease",background:"linear-gradient(90deg, #0ea5e9, #22c55e)"}})}function E(e){let t=document.createElement("meta");t.setAttribute("data-webframez-head","true");let n=Object.entries(e).filter(([,o])=>!!o);for(let[o,i]of n)t.setAttribute(o,String(i));document.head.appendChild(t)}function b(e){let t=document.createElement("link");t.setAttribute("data-webframez-head","true");let n=Object.entries(e).filter(([,o])=>!!o);for(let[o,i]of n)t.setAttribute(o,String(i));document.head.appendChild(t)}function I(e){if(!(typeof document>"u")){document.title=e.title||"Webframez React";for(let t of document.head.querySelectorAll(D))t.remove();e.description&&E({name:"description",content:e.description}),e.favicon&&b({rel:"icon",href:e.favicon});for(let t of e.meta??[])E(t);for(let t of e.links??[])b(t)}}function Y(e){let t=new TextEncoder;return new ReadableStream({start(n){n.enqueue(t.encode(e)),n.close()}})}function q(){if(typeof window>"u")return null;let e=window.__RSC_INITIAL_PAYLOAD;return typeof e!="string"||e===""?null:(delete window.__RSC_INITIAL_PAYLOAD,(0,u.createFromReadableStream)(Y(e)))}function Z(e){return function(){let[n]=(0,r.useState)(()=>q()??(0,u.createFromFetch)(fetch(`${e}?path=${encodeURIComponent(window.location.pathname)}&search=${encodeURIComponent(window.location.search)}`,{headers:{Accept:"text/x-component"}}))),o=r.default.use(n),[i,d]=(0,r.useState)(o.model),[c,L]=(0,r.useState)(o.head),[g,w]=(0,r.useState)(!1);async function l(s,h="push"){w(!0);try{let m=await(0,u.createFromFetch)(fetch(`${e}?path=${encodeURIComponent(s.pathname)}&search=${encodeURIComponent(s.search)}`,{headers:{Accept:"text/x-component"}}));I(m.head),L(m.head),d(m.model);let y=`${s.pathname}${s.search}`;h==="replace"?history.replaceState(null,"",y):h==="push"&&history.pushState(null,"",y)}catch(C){console.error("[webframez-react] Failed to render route",C),d((0,a.jsx)("p",{children:"Failed to load route."}))}finally{w(!1)}}(0,r.useEffect)(()=>{I(c)},[c]),(0,r.useEffect)(()=>{let s=()=>{l(new URL(window.location.href),"none")};return window.addEventListener("popstate",s),()=>{window.removeEventListener("popstate",s)}},[]);let R=r.default.useMemo(()=>({push:s=>{l(new URL(s,window.location.origin),"push")},replace:s=>{l(new URL(s,window.location.origin),"replace")},refresh:()=>{l(new URL(window.location.href),"none")}}),[]);return B(R),p?(0,a.jsxs)(p.Provider,{value:R,children:[(0,a.jsx)(x,{active:g}),i??(0,a.jsx)("p",{style:{padding:24},children:"Loading..."})]}):(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)(x,{active:g}),i??(0,a.jsx)("p",{style:{padding:24},children:"Loading..."})]})}}function j(e={}){let t=e.rootId??"root",n=document.getElementById(t);if(!n)throw new Error(`Missing #${t} element`);let o=typeof window<"u"&&window.__RSC_ENDPOINT,i=e.rscEndpoint??o??"/rsc",d=Z(i);return(0,S.hydrateRoot)(n,(0,a.jsx)(d,{}))}
|
package/dist/client.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import
|
|
1
|
+
import d,{useEffect as y,useState as l}from"react";import{hydrateRoot as P}from"react-dom/client";import{createFromFetch as _,createFromReadableStream as O}from"react-server-dom-webpack/client";import{Fragment as D,jsx as s,jsxs as S}from"react/jsx-runtime";var T={push:()=>{},replace:()=>{},refresh:()=>{}},f=typeof d.createContext=="function"?d.createContext(null):null,k="__WEBFRAMEZ_ROUTER__",N="[data-webframez-head='true']";function U(){return typeof window>"u"?null:window[k]??null}function $(e){typeof window>"u"||(window[k]=e)}function v(){let e={},t=typeof document>"u"?"":document.cookie;if(!t||t.trim()==="")return e;for(let n of t.split(";")){let o=n.trim();if(!o)continue;let i=o.indexOf("="),a=i>=0?o.slice(0,i).trim():o,c=i>=0?o.slice(i+1):"";a&&(e[a]=decodeURIComponent(c))}return e}function A(e,t,n={}){let o=[`${e}=${encodeURIComponent(t)}`];return n.path&&o.push(`Path=${n.path}`),n.domain&&o.push(`Domain=${n.domain}`),typeof n.maxAge=="number"&&o.push(`Max-Age=${Math.floor(n.maxAge)}`),n.expires&&o.push(`Expires=${n.expires.toUTCString()}`),n.sameSite&&o.push(`SameSite=${n.sameSite}`),n.secure&&o.push("Secure"),o.join("; ")}function G(){return d.useMemo(()=>({all:()=>v(),get:e=>v()[e],set:(e,t,n)=>{typeof document>"u"||(document.cookie=A(e,t,n))},remove:(e,t)=>{typeof document>"u"||(document.cookie=A(e,"",{...t??{},maxAge:0}))}}),[])}function Y(){let e=f?d.useContext(f):null;if(!e){let t=U();if(t)return t;if(typeof window>"u")return T;throw new Error("useRouter must be used inside mountWebframezClient()")}return e}function x({active:e}){return s("div",{style:{position:"fixed",top:0,left:0,width:"100%",height:3,zIndex:9999,transformOrigin:"left",transform:e?"scaleX(1)":"scaleX(0)",transition:"transform 180ms ease",background:"linear-gradient(90deg, #0ea5e9, #22c55e)"}})}function E(e){let t=document.createElement("meta");t.setAttribute("data-webframez-head","true");let n=Object.entries(e).filter(([,o])=>!!o);for(let[o,i]of n)t.setAttribute(o,String(i));document.head.appendChild(t)}function b(e){let t=document.createElement("link");t.setAttribute("data-webframez-head","true");let n=Object.entries(e).filter(([,o])=>!!o);for(let[o,i]of n)t.setAttribute(o,String(i));document.head.appendChild(t)}function I(e){if(!(typeof document>"u")){document.title=e.title||"Webframez React";for(let t of document.head.querySelectorAll(N))t.remove();e.description&&E({name:"description",content:e.description}),e.favicon&&b({rel:"icon",href:e.favicon});for(let t of e.meta??[])E(t);for(let t of e.links??[])b(t)}}function M(e){let t=new TextEncoder;return new ReadableStream({start(n){n.enqueue(t.encode(e)),n.close()}})}function H(){if(typeof window>"u")return null;let e=window.__RSC_INITIAL_PAYLOAD;return typeof e!="string"||e===""?null:(delete window.__RSC_INITIAL_PAYLOAD,O(M(e)))}function W(e){return function(){let[n]=l(()=>H()??_(fetch(`${e}?path=${encodeURIComponent(window.location.pathname)}&search=${encodeURIComponent(window.location.search)}`,{headers:{Accept:"text/x-component"}}))),o=d.use(n),[i,a]=l(o.model),[c,L]=l(o.head),[m,g]=l(!1);async function u(r,R="push"){g(!0);try{let p=await _(fetch(`${e}?path=${encodeURIComponent(r.pathname)}&search=${encodeURIComponent(r.search)}`,{headers:{Accept:"text/x-component"}}));I(p.head),L(p.head),a(p.model);let C=`${r.pathname}${r.search}`;R==="replace"?history.replaceState(null,"",C):R==="push"&&history.pushState(null,"",C)}catch(h){console.error("[webframez-react] Failed to render route",h),a(s("p",{children:"Failed to load route."}))}finally{g(!1)}}y(()=>{I(c)},[c]),y(()=>{let r=()=>{u(new URL(window.location.href),"none")};return window.addEventListener("popstate",r),()=>{window.removeEventListener("popstate",r)}},[]);let w=d.useMemo(()=>({push:r=>{u(new URL(r,window.location.origin),"push")},replace:r=>{u(new URL(r,window.location.origin),"replace")},refresh:()=>{u(new URL(window.location.href),"none")}}),[]);return $(w),f?S(f.Provider,{value:w,children:[s(x,{active:m}),i??s("p",{style:{padding:24},children:"Loading..."})]}):S(D,{children:[s(x,{active:m}),i??s("p",{style:{padding:24},children:"Loading..."})]})}}function q(e={}){let t=e.rootId??"root",n=document.getElementById(t);if(!n)throw new Error(`Missing #${t} element`);let o=typeof window<"u"&&window.__RSC_ENDPOINT,i=e.rscEndpoint??o??"/rsc",a=W(i);return P(n,s(a,{}))}export{q as mountWebframezClient,G as useCookie,Y as useRouter};
|
package/dist/http.cjs
CHANGED
|
@@ -37,6 +37,7 @@ var import_node_path2 = __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_stream = require("node:stream");
|
|
40
41
|
var import_server = require("react-server-dom-webpack/server");
|
|
41
42
|
function defaultOnError(err) {
|
|
42
43
|
console.error("[webframez-react] RSC render error", err);
|
|
@@ -48,10 +49,12 @@ function createHTMLShell(options = {}) {
|
|
|
48
49
|
clientScriptUrl = "/client.js",
|
|
49
50
|
headTags = "",
|
|
50
51
|
rootHtml = "",
|
|
52
|
+
initialFlightData = "",
|
|
51
53
|
basename = "",
|
|
52
54
|
liveReloadPath,
|
|
53
55
|
liveReloadServerId
|
|
54
56
|
} = options;
|
|
57
|
+
const escapedInitialFlightData = initialFlightData ? JSON.stringify(initialFlightData).replace(/</g, "\\u003c").replace(/\u2028/g, "\\u2028").replace(/\u2029/g, "\\u2029") : '""';
|
|
55
58
|
const liveReloadScript = liveReloadPath && liveReloadServerId ? `<script>
|
|
56
59
|
(() => {
|
|
57
60
|
const endpoint = ${JSON.stringify(liveReloadPath)};
|
|
@@ -96,11 +99,34 @@ function createHTMLShell(options = {}) {
|
|
|
96
99
|
<div id="root">${rootHtml}</div>
|
|
97
100
|
<script>window.__RSC_ENDPOINT = "${rscEndpoint}";</script>
|
|
98
101
|
<script>window.__RSC_BASENAME = "${basename}";</script>
|
|
102
|
+
<script>window.__RSC_INITIAL_PAYLOAD = ${escapedInitialFlightData};</script>
|
|
99
103
|
<script type="module" src="${clientScriptUrl}"></script>
|
|
100
104
|
${liveReloadScript}
|
|
101
105
|
</body>
|
|
102
106
|
</html>`;
|
|
103
107
|
}
|
|
108
|
+
function renderRSCToString(payload, options = {}) {
|
|
109
|
+
const {
|
|
110
|
+
moduleMap = {},
|
|
111
|
+
onError = defaultOnError
|
|
112
|
+
} = options;
|
|
113
|
+
return new Promise((resolve, reject) => {
|
|
114
|
+
const chunks = [];
|
|
115
|
+
const writable = new import_node_stream.Writable({
|
|
116
|
+
write(chunk, _encoding, callback) {
|
|
117
|
+
const normalized = typeof chunk === "string" ? Buffer.from(chunk) : Buffer.from(chunk);
|
|
118
|
+
chunks.push(normalized);
|
|
119
|
+
callback();
|
|
120
|
+
}
|
|
121
|
+
});
|
|
122
|
+
writable.on("finish", () => {
|
|
123
|
+
resolve(Buffer.concat(chunks).toString("utf8"));
|
|
124
|
+
});
|
|
125
|
+
writable.on("error", reject);
|
|
126
|
+
const stream = (0, import_server.renderToPipeableStream)(payload, moduleMap, { onError });
|
|
127
|
+
stream.pipe(writable);
|
|
128
|
+
});
|
|
129
|
+
}
|
|
104
130
|
function sendRSC(res, model, options = {}) {
|
|
105
131
|
const {
|
|
106
132
|
moduleMap = {},
|
|
@@ -478,6 +504,9 @@ function parseSearchParams(query) {
|
|
|
478
504
|
}
|
|
479
505
|
|
|
480
506
|
// src/http.ts
|
|
507
|
+
function createInitialHtmlErrorMarkup(message) {
|
|
508
|
+
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>`;
|
|
509
|
+
}
|
|
481
510
|
var INITIAL_HTML_WORKER_SCRIPT = `
|
|
482
511
|
const path = require("node:path");
|
|
483
512
|
const Module = require("node:module");
|
|
@@ -559,6 +588,12 @@ function stripBasePath(pathname, basePath) {
|
|
|
559
588
|
}
|
|
560
589
|
return pathname;
|
|
561
590
|
}
|
|
591
|
+
function sanitizeInitialHtmlWorkerNodeOptions(rawNodeOptions) {
|
|
592
|
+
if (!rawNodeOptions || rawNodeOptions.trim() === "") {
|
|
593
|
+
return "";
|
|
594
|
+
}
|
|
595
|
+
return rawNodeOptions.replace(/(^|\s)--conditions\s+react-server(?=\s|$)/g, " ").replace(/(^|\s)-r\s+(\S*webframez-react\/register)(?=\s|$)/g, " ").replace(/\s+/g, " ").trim();
|
|
596
|
+
}
|
|
562
597
|
function createInitialHtmlWorker(pagesDir) {
|
|
563
598
|
let child = null;
|
|
564
599
|
let nextRequestId = 1;
|
|
@@ -590,6 +625,7 @@ function createInitialHtmlWorker(pagesDir) {
|
|
|
590
625
|
cwd: process.cwd(),
|
|
591
626
|
env: {
|
|
592
627
|
...process.env,
|
|
628
|
+
NODE_OPTIONS: sanitizeInitialHtmlWorkerNodeOptions(process.env.NODE_OPTIONS),
|
|
593
629
|
WEBFRAMEZ_REACT_PAGES_DIR: pagesDir
|
|
594
630
|
},
|
|
595
631
|
stdio: ["ignore", "ignore", "pipe", "ipc"]
|
|
@@ -826,6 +862,10 @@ function createNodeRequestHandler(options) {
|
|
|
826
862
|
cookies: requestCookies
|
|
827
863
|
})
|
|
828
864
|
);
|
|
865
|
+
const initialPayload = {
|
|
866
|
+
model: resolved.model,
|
|
867
|
+
head: resolved.head
|
|
868
|
+
};
|
|
829
869
|
let rootHtml = "";
|
|
830
870
|
try {
|
|
831
871
|
rootHtml = await initialHtmlWorker.render({
|
|
@@ -836,7 +876,36 @@ function createNodeRequestHandler(options) {
|
|
|
836
876
|
});
|
|
837
877
|
} catch (error) {
|
|
838
878
|
console.error("[webframez-react] Failed to render initial HTML", error);
|
|
879
|
+
try {
|
|
880
|
+
initialHtmlWorker.dispose();
|
|
881
|
+
rootHtml = await initialHtmlWorker.render({
|
|
882
|
+
pathname: stripBasePath(url.pathname, basePath),
|
|
883
|
+
searchParams: parseSearchParams(url.searchParams),
|
|
884
|
+
cookies: requestCookies,
|
|
885
|
+
basename: basePath
|
|
886
|
+
});
|
|
887
|
+
} catch (retryError) {
|
|
888
|
+
console.error("[webframez-react] Retry for initial HTML failed", retryError);
|
|
889
|
+
res.statusCode = 500;
|
|
890
|
+
res.setHeader("Content-Type", "text/html");
|
|
891
|
+
res.end(
|
|
892
|
+
createHTMLShell({
|
|
893
|
+
title: "500 - Initial HTML render failed",
|
|
894
|
+
headTags: "",
|
|
895
|
+
clientScriptUrl,
|
|
896
|
+
rscEndpoint: rscPath,
|
|
897
|
+
rootHtml: createInitialHtmlErrorMarkup("Initial HTML render failed."),
|
|
898
|
+
basename: basePath,
|
|
899
|
+
liveReloadPath: liveReloadPath || void 0,
|
|
900
|
+
liveReloadServerId: liveReloadPath ? devServerId : void 0
|
|
901
|
+
})
|
|
902
|
+
);
|
|
903
|
+
return;
|
|
904
|
+
}
|
|
839
905
|
}
|
|
906
|
+
const initialFlightData = await renderRSCToString(initialPayload, {
|
|
907
|
+
moduleMap
|
|
908
|
+
});
|
|
840
909
|
res.statusCode = resolved.statusCode;
|
|
841
910
|
res.setHeader("Content-Type", "text/html");
|
|
842
911
|
res.end(
|
|
@@ -846,6 +915,7 @@ function createNodeRequestHandler(options) {
|
|
|
846
915
|
clientScriptUrl,
|
|
847
916
|
rscEndpoint: rscPath,
|
|
848
917
|
rootHtml,
|
|
918
|
+
initialFlightData,
|
|
849
919
|
basename: basePath,
|
|
850
920
|
liveReloadPath: liveReloadPath || void 0,
|
|
851
921
|
liveReloadServerId: liveReloadPath ? devServerId : void 0
|
package/dist/http.js
CHANGED
|
@@ -12,6 +12,7 @@ import path2 from "node:path";
|
|
|
12
12
|
import { spawn } from "node:child_process";
|
|
13
13
|
|
|
14
14
|
// src/server.ts
|
|
15
|
+
import { Writable } from "node:stream";
|
|
15
16
|
import { renderToPipeableStream } from "react-server-dom-webpack/server";
|
|
16
17
|
function defaultOnError(err) {
|
|
17
18
|
console.error("[webframez-react] RSC render error", err);
|
|
@@ -23,10 +24,12 @@ function createHTMLShell(options = {}) {
|
|
|
23
24
|
clientScriptUrl = "/client.js",
|
|
24
25
|
headTags = "",
|
|
25
26
|
rootHtml = "",
|
|
27
|
+
initialFlightData = "",
|
|
26
28
|
basename = "",
|
|
27
29
|
liveReloadPath,
|
|
28
30
|
liveReloadServerId
|
|
29
31
|
} = options;
|
|
32
|
+
const escapedInitialFlightData = initialFlightData ? JSON.stringify(initialFlightData).replace(/</g, "\\u003c").replace(/\u2028/g, "\\u2028").replace(/\u2029/g, "\\u2029") : '""';
|
|
30
33
|
const liveReloadScript = liveReloadPath && liveReloadServerId ? `<script>
|
|
31
34
|
(() => {
|
|
32
35
|
const endpoint = ${JSON.stringify(liveReloadPath)};
|
|
@@ -71,11 +74,34 @@ function createHTMLShell(options = {}) {
|
|
|
71
74
|
<div id="root">${rootHtml}</div>
|
|
72
75
|
<script>window.__RSC_ENDPOINT = "${rscEndpoint}";</script>
|
|
73
76
|
<script>window.__RSC_BASENAME = "${basename}";</script>
|
|
77
|
+
<script>window.__RSC_INITIAL_PAYLOAD = ${escapedInitialFlightData};</script>
|
|
74
78
|
<script type="module" src="${clientScriptUrl}"></script>
|
|
75
79
|
${liveReloadScript}
|
|
76
80
|
</body>
|
|
77
81
|
</html>`;
|
|
78
82
|
}
|
|
83
|
+
function renderRSCToString(payload, options = {}) {
|
|
84
|
+
const {
|
|
85
|
+
moduleMap = {},
|
|
86
|
+
onError = defaultOnError
|
|
87
|
+
} = options;
|
|
88
|
+
return new Promise((resolve, reject) => {
|
|
89
|
+
const chunks = [];
|
|
90
|
+
const writable = new Writable({
|
|
91
|
+
write(chunk, _encoding, callback) {
|
|
92
|
+
const normalized = typeof chunk === "string" ? Buffer.from(chunk) : Buffer.from(chunk);
|
|
93
|
+
chunks.push(normalized);
|
|
94
|
+
callback();
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
writable.on("finish", () => {
|
|
98
|
+
resolve(Buffer.concat(chunks).toString("utf8"));
|
|
99
|
+
});
|
|
100
|
+
writable.on("error", reject);
|
|
101
|
+
const stream = renderToPipeableStream(payload, moduleMap, { onError });
|
|
102
|
+
stream.pipe(writable);
|
|
103
|
+
});
|
|
104
|
+
}
|
|
79
105
|
function sendRSC(res, model, options = {}) {
|
|
80
106
|
const {
|
|
81
107
|
moduleMap = {},
|
|
@@ -453,6 +479,9 @@ function parseSearchParams(query) {
|
|
|
453
479
|
}
|
|
454
480
|
|
|
455
481
|
// src/http.ts
|
|
482
|
+
function createInitialHtmlErrorMarkup(message) {
|
|
483
|
+
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>`;
|
|
484
|
+
}
|
|
456
485
|
var INITIAL_HTML_WORKER_SCRIPT = `
|
|
457
486
|
const path = require("node:path");
|
|
458
487
|
const Module = require("node:module");
|
|
@@ -534,6 +563,12 @@ function stripBasePath(pathname, basePath) {
|
|
|
534
563
|
}
|
|
535
564
|
return pathname;
|
|
536
565
|
}
|
|
566
|
+
function sanitizeInitialHtmlWorkerNodeOptions(rawNodeOptions) {
|
|
567
|
+
if (!rawNodeOptions || rawNodeOptions.trim() === "") {
|
|
568
|
+
return "";
|
|
569
|
+
}
|
|
570
|
+
return rawNodeOptions.replace(/(^|\s)--conditions\s+react-server(?=\s|$)/g, " ").replace(/(^|\s)-r\s+(\S*webframez-react\/register)(?=\s|$)/g, " ").replace(/\s+/g, " ").trim();
|
|
571
|
+
}
|
|
537
572
|
function createInitialHtmlWorker(pagesDir) {
|
|
538
573
|
let child = null;
|
|
539
574
|
let nextRequestId = 1;
|
|
@@ -565,6 +600,7 @@ function createInitialHtmlWorker(pagesDir) {
|
|
|
565
600
|
cwd: process.cwd(),
|
|
566
601
|
env: {
|
|
567
602
|
...process.env,
|
|
603
|
+
NODE_OPTIONS: sanitizeInitialHtmlWorkerNodeOptions(process.env.NODE_OPTIONS),
|
|
568
604
|
WEBFRAMEZ_REACT_PAGES_DIR: pagesDir
|
|
569
605
|
},
|
|
570
606
|
stdio: ["ignore", "ignore", "pipe", "ipc"]
|
|
@@ -801,6 +837,10 @@ function createNodeRequestHandler(options) {
|
|
|
801
837
|
cookies: requestCookies
|
|
802
838
|
})
|
|
803
839
|
);
|
|
840
|
+
const initialPayload = {
|
|
841
|
+
model: resolved.model,
|
|
842
|
+
head: resolved.head
|
|
843
|
+
};
|
|
804
844
|
let rootHtml = "";
|
|
805
845
|
try {
|
|
806
846
|
rootHtml = await initialHtmlWorker.render({
|
|
@@ -811,7 +851,36 @@ function createNodeRequestHandler(options) {
|
|
|
811
851
|
});
|
|
812
852
|
} catch (error) {
|
|
813
853
|
console.error("[webframez-react] Failed to render initial HTML", error);
|
|
854
|
+
try {
|
|
855
|
+
initialHtmlWorker.dispose();
|
|
856
|
+
rootHtml = await initialHtmlWorker.render({
|
|
857
|
+
pathname: stripBasePath(url.pathname, basePath),
|
|
858
|
+
searchParams: parseSearchParams(url.searchParams),
|
|
859
|
+
cookies: requestCookies,
|
|
860
|
+
basename: basePath
|
|
861
|
+
});
|
|
862
|
+
} catch (retryError) {
|
|
863
|
+
console.error("[webframez-react] Retry for initial HTML failed", retryError);
|
|
864
|
+
res.statusCode = 500;
|
|
865
|
+
res.setHeader("Content-Type", "text/html");
|
|
866
|
+
res.end(
|
|
867
|
+
createHTMLShell({
|
|
868
|
+
title: "500 - Initial HTML render failed",
|
|
869
|
+
headTags: "",
|
|
870
|
+
clientScriptUrl,
|
|
871
|
+
rscEndpoint: rscPath,
|
|
872
|
+
rootHtml: createInitialHtmlErrorMarkup("Initial HTML render failed."),
|
|
873
|
+
basename: basePath,
|
|
874
|
+
liveReloadPath: liveReloadPath || void 0,
|
|
875
|
+
liveReloadServerId: liveReloadPath ? devServerId : void 0
|
|
876
|
+
})
|
|
877
|
+
);
|
|
878
|
+
return;
|
|
879
|
+
}
|
|
814
880
|
}
|
|
881
|
+
const initialFlightData = await renderRSCToString(initialPayload, {
|
|
882
|
+
moduleMap
|
|
883
|
+
});
|
|
815
884
|
res.statusCode = resolved.statusCode;
|
|
816
885
|
res.setHeader("Content-Type", "text/html");
|
|
817
886
|
res.end(
|
|
@@ -821,6 +890,7 @@ function createNodeRequestHandler(options) {
|
|
|
821
890
|
clientScriptUrl,
|
|
822
891
|
rscEndpoint: rscPath,
|
|
823
892
|
rootHtml,
|
|
893
|
+
initialFlightData,
|
|
824
894
|
basename: basePath,
|
|
825
895
|
liveReloadPath: liveReloadPath || void 0,
|
|
826
896
|
liveReloadServerId: liveReloadPath ? devServerId : void 0
|
package/dist/index.cjs
CHANGED
|
@@ -43,6 +43,7 @@ __export(src_exports, {
|
|
|
43
43
|
module.exports = __toCommonJS(src_exports);
|
|
44
44
|
|
|
45
45
|
// src/server.ts
|
|
46
|
+
var import_node_stream = require("node:stream");
|
|
46
47
|
var import_server = require("react-server-dom-webpack/server");
|
|
47
48
|
function defaultOnError(err) {
|
|
48
49
|
console.error("[webframez-react] RSC render error", err);
|
|
@@ -54,10 +55,12 @@ function createHTMLShell(options = {}) {
|
|
|
54
55
|
clientScriptUrl = "/client.js",
|
|
55
56
|
headTags = "",
|
|
56
57
|
rootHtml = "",
|
|
58
|
+
initialFlightData = "",
|
|
57
59
|
basename = "",
|
|
58
60
|
liveReloadPath,
|
|
59
61
|
liveReloadServerId
|
|
60
62
|
} = options;
|
|
63
|
+
const escapedInitialFlightData = initialFlightData ? JSON.stringify(initialFlightData).replace(/</g, "\\u003c").replace(/\u2028/g, "\\u2028").replace(/\u2029/g, "\\u2029") : '""';
|
|
61
64
|
const liveReloadScript = liveReloadPath && liveReloadServerId ? `<script>
|
|
62
65
|
(() => {
|
|
63
66
|
const endpoint = ${JSON.stringify(liveReloadPath)};
|
|
@@ -102,11 +105,34 @@ function createHTMLShell(options = {}) {
|
|
|
102
105
|
<div id="root">${rootHtml}</div>
|
|
103
106
|
<script>window.__RSC_ENDPOINT = "${rscEndpoint}";</script>
|
|
104
107
|
<script>window.__RSC_BASENAME = "${basename}";</script>
|
|
108
|
+
<script>window.__RSC_INITIAL_PAYLOAD = ${escapedInitialFlightData};</script>
|
|
105
109
|
<script type="module" src="${clientScriptUrl}"></script>
|
|
106
110
|
${liveReloadScript}
|
|
107
111
|
</body>
|
|
108
112
|
</html>`;
|
|
109
113
|
}
|
|
114
|
+
function renderRSCToString(payload, options = {}) {
|
|
115
|
+
const {
|
|
116
|
+
moduleMap = {},
|
|
117
|
+
onError = defaultOnError
|
|
118
|
+
} = options;
|
|
119
|
+
return new Promise((resolve, reject) => {
|
|
120
|
+
const chunks = [];
|
|
121
|
+
const writable = new import_node_stream.Writable({
|
|
122
|
+
write(chunk, _encoding, callback) {
|
|
123
|
+
const normalized = typeof chunk === "string" ? Buffer.from(chunk) : Buffer.from(chunk);
|
|
124
|
+
chunks.push(normalized);
|
|
125
|
+
callback();
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
writable.on("finish", () => {
|
|
129
|
+
resolve(Buffer.concat(chunks).toString("utf8"));
|
|
130
|
+
});
|
|
131
|
+
writable.on("error", reject);
|
|
132
|
+
const stream = (0, import_server.renderToPipeableStream)(payload, moduleMap, { onError });
|
|
133
|
+
stream.pipe(writable);
|
|
134
|
+
});
|
|
135
|
+
}
|
|
110
136
|
function sendRSC(res, model, options = {}) {
|
|
111
137
|
const {
|
|
112
138
|
moduleMap = {},
|
|
@@ -515,6 +541,9 @@ function parseSearchParams(query) {
|
|
|
515
541
|
var import_node_fs2 = __toESM(require("node:fs"), 1);
|
|
516
542
|
var import_node_path2 = __toESM(require("node:path"), 1);
|
|
517
543
|
var import_node_child_process = require("node:child_process");
|
|
544
|
+
function createInitialHtmlErrorMarkup(message) {
|
|
545
|
+
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>`;
|
|
546
|
+
}
|
|
518
547
|
var INITIAL_HTML_WORKER_SCRIPT = `
|
|
519
548
|
const path = require("node:path");
|
|
520
549
|
const Module = require("node:module");
|
|
@@ -596,6 +625,12 @@ function stripBasePath(pathname, basePath) {
|
|
|
596
625
|
}
|
|
597
626
|
return pathname;
|
|
598
627
|
}
|
|
628
|
+
function sanitizeInitialHtmlWorkerNodeOptions(rawNodeOptions) {
|
|
629
|
+
if (!rawNodeOptions || rawNodeOptions.trim() === "") {
|
|
630
|
+
return "";
|
|
631
|
+
}
|
|
632
|
+
return rawNodeOptions.replace(/(^|\s)--conditions\s+react-server(?=\s|$)/g, " ").replace(/(^|\s)-r\s+(\S*webframez-react\/register)(?=\s|$)/g, " ").replace(/\s+/g, " ").trim();
|
|
633
|
+
}
|
|
599
634
|
function createInitialHtmlWorker(pagesDir) {
|
|
600
635
|
let child = null;
|
|
601
636
|
let nextRequestId = 1;
|
|
@@ -627,6 +662,7 @@ function createInitialHtmlWorker(pagesDir) {
|
|
|
627
662
|
cwd: process.cwd(),
|
|
628
663
|
env: {
|
|
629
664
|
...process.env,
|
|
665
|
+
NODE_OPTIONS: sanitizeInitialHtmlWorkerNodeOptions(process.env.NODE_OPTIONS),
|
|
630
666
|
WEBFRAMEZ_REACT_PAGES_DIR: pagesDir
|
|
631
667
|
},
|
|
632
668
|
stdio: ["ignore", "ignore", "pipe", "ipc"]
|
|
@@ -863,6 +899,10 @@ function createNodeRequestHandler(options) {
|
|
|
863
899
|
cookies: requestCookies
|
|
864
900
|
})
|
|
865
901
|
);
|
|
902
|
+
const initialPayload = {
|
|
903
|
+
model: resolved.model,
|
|
904
|
+
head: resolved.head
|
|
905
|
+
};
|
|
866
906
|
let rootHtml = "";
|
|
867
907
|
try {
|
|
868
908
|
rootHtml = await initialHtmlWorker.render({
|
|
@@ -873,7 +913,36 @@ function createNodeRequestHandler(options) {
|
|
|
873
913
|
});
|
|
874
914
|
} catch (error) {
|
|
875
915
|
console.error("[webframez-react] Failed to render initial HTML", error);
|
|
916
|
+
try {
|
|
917
|
+
initialHtmlWorker.dispose();
|
|
918
|
+
rootHtml = await initialHtmlWorker.render({
|
|
919
|
+
pathname: stripBasePath(url.pathname, basePath),
|
|
920
|
+
searchParams: parseSearchParams(url.searchParams),
|
|
921
|
+
cookies: requestCookies,
|
|
922
|
+
basename: basePath
|
|
923
|
+
});
|
|
924
|
+
} catch (retryError) {
|
|
925
|
+
console.error("[webframez-react] Retry for initial HTML failed", retryError);
|
|
926
|
+
res.statusCode = 500;
|
|
927
|
+
res.setHeader("Content-Type", "text/html");
|
|
928
|
+
res.end(
|
|
929
|
+
createHTMLShell({
|
|
930
|
+
title: "500 - Initial HTML render failed",
|
|
931
|
+
headTags: "",
|
|
932
|
+
clientScriptUrl,
|
|
933
|
+
rscEndpoint: rscPath,
|
|
934
|
+
rootHtml: createInitialHtmlErrorMarkup("Initial HTML render failed."),
|
|
935
|
+
basename: basePath,
|
|
936
|
+
liveReloadPath: liveReloadPath || void 0,
|
|
937
|
+
liveReloadServerId: liveReloadPath ? devServerId : void 0
|
|
938
|
+
})
|
|
939
|
+
);
|
|
940
|
+
return;
|
|
941
|
+
}
|
|
876
942
|
}
|
|
943
|
+
const initialFlightData = await renderRSCToString(initialPayload, {
|
|
944
|
+
moduleMap
|
|
945
|
+
});
|
|
877
946
|
res.statusCode = resolved.statusCode;
|
|
878
947
|
res.setHeader("Content-Type", "text/html");
|
|
879
948
|
res.end(
|
|
@@ -883,6 +952,7 @@ function createNodeRequestHandler(options) {
|
|
|
883
952
|
clientScriptUrl,
|
|
884
953
|
rscEndpoint: rscPath,
|
|
885
954
|
rootHtml,
|
|
955
|
+
initialFlightData,
|
|
886
956
|
basename: basePath,
|
|
887
957
|
liveReloadPath: liveReloadPath || void 0,
|
|
888
958
|
liveReloadServerId: liveReloadPath ? devServerId : void 0
|
package/dist/index.js
CHANGED
|
@@ -7,6 +7,7 @@ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require
|
|
|
7
7
|
});
|
|
8
8
|
|
|
9
9
|
// src/server.ts
|
|
10
|
+
import { Writable } from "node:stream";
|
|
10
11
|
import { renderToPipeableStream } from "react-server-dom-webpack/server";
|
|
11
12
|
function defaultOnError(err) {
|
|
12
13
|
console.error("[webframez-react] RSC render error", err);
|
|
@@ -18,10 +19,12 @@ function createHTMLShell(options = {}) {
|
|
|
18
19
|
clientScriptUrl = "/client.js",
|
|
19
20
|
headTags = "",
|
|
20
21
|
rootHtml = "",
|
|
22
|
+
initialFlightData = "",
|
|
21
23
|
basename = "",
|
|
22
24
|
liveReloadPath,
|
|
23
25
|
liveReloadServerId
|
|
24
26
|
} = options;
|
|
27
|
+
const escapedInitialFlightData = initialFlightData ? JSON.stringify(initialFlightData).replace(/</g, "\\u003c").replace(/\u2028/g, "\\u2028").replace(/\u2029/g, "\\u2029") : '""';
|
|
25
28
|
const liveReloadScript = liveReloadPath && liveReloadServerId ? `<script>
|
|
26
29
|
(() => {
|
|
27
30
|
const endpoint = ${JSON.stringify(liveReloadPath)};
|
|
@@ -66,11 +69,34 @@ function createHTMLShell(options = {}) {
|
|
|
66
69
|
<div id="root">${rootHtml}</div>
|
|
67
70
|
<script>window.__RSC_ENDPOINT = "${rscEndpoint}";</script>
|
|
68
71
|
<script>window.__RSC_BASENAME = "${basename}";</script>
|
|
72
|
+
<script>window.__RSC_INITIAL_PAYLOAD = ${escapedInitialFlightData};</script>
|
|
69
73
|
<script type="module" src="${clientScriptUrl}"></script>
|
|
70
74
|
${liveReloadScript}
|
|
71
75
|
</body>
|
|
72
76
|
</html>`;
|
|
73
77
|
}
|
|
78
|
+
function renderRSCToString(payload, options = {}) {
|
|
79
|
+
const {
|
|
80
|
+
moduleMap = {},
|
|
81
|
+
onError = defaultOnError
|
|
82
|
+
} = options;
|
|
83
|
+
return new Promise((resolve, reject) => {
|
|
84
|
+
const chunks = [];
|
|
85
|
+
const writable = new Writable({
|
|
86
|
+
write(chunk, _encoding, callback) {
|
|
87
|
+
const normalized = typeof chunk === "string" ? Buffer.from(chunk) : Buffer.from(chunk);
|
|
88
|
+
chunks.push(normalized);
|
|
89
|
+
callback();
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
writable.on("finish", () => {
|
|
93
|
+
resolve(Buffer.concat(chunks).toString("utf8"));
|
|
94
|
+
});
|
|
95
|
+
writable.on("error", reject);
|
|
96
|
+
const stream = renderToPipeableStream(payload, moduleMap, { onError });
|
|
97
|
+
stream.pipe(writable);
|
|
98
|
+
});
|
|
99
|
+
}
|
|
74
100
|
function sendRSC(res, model, options = {}) {
|
|
75
101
|
const {
|
|
76
102
|
moduleMap = {},
|
|
@@ -479,6 +505,9 @@ function parseSearchParams(query) {
|
|
|
479
505
|
import fs2 from "node:fs";
|
|
480
506
|
import path2 from "node:path";
|
|
481
507
|
import { spawn } from "node:child_process";
|
|
508
|
+
function createInitialHtmlErrorMarkup(message) {
|
|
509
|
+
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>`;
|
|
510
|
+
}
|
|
482
511
|
var INITIAL_HTML_WORKER_SCRIPT = `
|
|
483
512
|
const path = require("node:path");
|
|
484
513
|
const Module = require("node:module");
|
|
@@ -560,6 +589,12 @@ function stripBasePath(pathname, basePath) {
|
|
|
560
589
|
}
|
|
561
590
|
return pathname;
|
|
562
591
|
}
|
|
592
|
+
function sanitizeInitialHtmlWorkerNodeOptions(rawNodeOptions) {
|
|
593
|
+
if (!rawNodeOptions || rawNodeOptions.trim() === "") {
|
|
594
|
+
return "";
|
|
595
|
+
}
|
|
596
|
+
return rawNodeOptions.replace(/(^|\s)--conditions\s+react-server(?=\s|$)/g, " ").replace(/(^|\s)-r\s+(\S*webframez-react\/register)(?=\s|$)/g, " ").replace(/\s+/g, " ").trim();
|
|
597
|
+
}
|
|
563
598
|
function createInitialHtmlWorker(pagesDir) {
|
|
564
599
|
let child = null;
|
|
565
600
|
let nextRequestId = 1;
|
|
@@ -591,6 +626,7 @@ function createInitialHtmlWorker(pagesDir) {
|
|
|
591
626
|
cwd: process.cwd(),
|
|
592
627
|
env: {
|
|
593
628
|
...process.env,
|
|
629
|
+
NODE_OPTIONS: sanitizeInitialHtmlWorkerNodeOptions(process.env.NODE_OPTIONS),
|
|
594
630
|
WEBFRAMEZ_REACT_PAGES_DIR: pagesDir
|
|
595
631
|
},
|
|
596
632
|
stdio: ["ignore", "ignore", "pipe", "ipc"]
|
|
@@ -827,6 +863,10 @@ function createNodeRequestHandler(options) {
|
|
|
827
863
|
cookies: requestCookies
|
|
828
864
|
})
|
|
829
865
|
);
|
|
866
|
+
const initialPayload = {
|
|
867
|
+
model: resolved.model,
|
|
868
|
+
head: resolved.head
|
|
869
|
+
};
|
|
830
870
|
let rootHtml = "";
|
|
831
871
|
try {
|
|
832
872
|
rootHtml = await initialHtmlWorker.render({
|
|
@@ -837,7 +877,36 @@ function createNodeRequestHandler(options) {
|
|
|
837
877
|
});
|
|
838
878
|
} catch (error) {
|
|
839
879
|
console.error("[webframez-react] Failed to render initial HTML", error);
|
|
880
|
+
try {
|
|
881
|
+
initialHtmlWorker.dispose();
|
|
882
|
+
rootHtml = await initialHtmlWorker.render({
|
|
883
|
+
pathname: stripBasePath(url.pathname, basePath),
|
|
884
|
+
searchParams: parseSearchParams(url.searchParams),
|
|
885
|
+
cookies: requestCookies,
|
|
886
|
+
basename: basePath
|
|
887
|
+
});
|
|
888
|
+
} catch (retryError) {
|
|
889
|
+
console.error("[webframez-react] Retry for initial HTML failed", retryError);
|
|
890
|
+
res.statusCode = 500;
|
|
891
|
+
res.setHeader("Content-Type", "text/html");
|
|
892
|
+
res.end(
|
|
893
|
+
createHTMLShell({
|
|
894
|
+
title: "500 - Initial HTML render failed",
|
|
895
|
+
headTags: "",
|
|
896
|
+
clientScriptUrl,
|
|
897
|
+
rscEndpoint: rscPath,
|
|
898
|
+
rootHtml: createInitialHtmlErrorMarkup("Initial HTML render failed."),
|
|
899
|
+
basename: basePath,
|
|
900
|
+
liveReloadPath: liveReloadPath || void 0,
|
|
901
|
+
liveReloadServerId: liveReloadPath ? devServerId : void 0
|
|
902
|
+
})
|
|
903
|
+
);
|
|
904
|
+
return;
|
|
905
|
+
}
|
|
840
906
|
}
|
|
907
|
+
const initialFlightData = await renderRSCToString(initialPayload, {
|
|
908
|
+
moduleMap
|
|
909
|
+
});
|
|
841
910
|
res.statusCode = resolved.statusCode;
|
|
842
911
|
res.setHeader("Content-Type", "text/html");
|
|
843
912
|
res.end(
|
|
@@ -847,6 +916,7 @@ function createNodeRequestHandler(options) {
|
|
|
847
916
|
clientScriptUrl,
|
|
848
917
|
rscEndpoint: rscPath,
|
|
849
918
|
rootHtml,
|
|
919
|
+
initialFlightData,
|
|
850
920
|
basename: basePath,
|
|
851
921
|
liveReloadPath: liveReloadPath || void 0,
|
|
852
922
|
liveReloadServerId: liveReloadPath ? devServerId : void 0
|
package/dist/webframez-core.cjs
CHANGED
|
@@ -40,6 +40,7 @@ var import_node_path2 = __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_stream = require("node:stream");
|
|
43
44
|
var import_server = require("react-server-dom-webpack/server");
|
|
44
45
|
function defaultOnError(err) {
|
|
45
46
|
console.error("[webframez-react] RSC render error", err);
|
|
@@ -51,10 +52,12 @@ function createHTMLShell(options = {}) {
|
|
|
51
52
|
clientScriptUrl = "/client.js",
|
|
52
53
|
headTags = "",
|
|
53
54
|
rootHtml = "",
|
|
55
|
+
initialFlightData = "",
|
|
54
56
|
basename = "",
|
|
55
57
|
liveReloadPath,
|
|
56
58
|
liveReloadServerId
|
|
57
59
|
} = options;
|
|
60
|
+
const escapedInitialFlightData = initialFlightData ? JSON.stringify(initialFlightData).replace(/</g, "\\u003c").replace(/\u2028/g, "\\u2028").replace(/\u2029/g, "\\u2029") : '""';
|
|
58
61
|
const liveReloadScript = liveReloadPath && liveReloadServerId ? `<script>
|
|
59
62
|
(() => {
|
|
60
63
|
const endpoint = ${JSON.stringify(liveReloadPath)};
|
|
@@ -99,11 +102,34 @@ function createHTMLShell(options = {}) {
|
|
|
99
102
|
<div id="root">${rootHtml}</div>
|
|
100
103
|
<script>window.__RSC_ENDPOINT = "${rscEndpoint}";</script>
|
|
101
104
|
<script>window.__RSC_BASENAME = "${basename}";</script>
|
|
105
|
+
<script>window.__RSC_INITIAL_PAYLOAD = ${escapedInitialFlightData};</script>
|
|
102
106
|
<script type="module" src="${clientScriptUrl}"></script>
|
|
103
107
|
${liveReloadScript}
|
|
104
108
|
</body>
|
|
105
109
|
</html>`;
|
|
106
110
|
}
|
|
111
|
+
function renderRSCToString(payload, options = {}) {
|
|
112
|
+
const {
|
|
113
|
+
moduleMap = {},
|
|
114
|
+
onError = defaultOnError
|
|
115
|
+
} = options;
|
|
116
|
+
return new Promise((resolve, reject) => {
|
|
117
|
+
const chunks = [];
|
|
118
|
+
const writable = new import_node_stream.Writable({
|
|
119
|
+
write(chunk, _encoding, callback) {
|
|
120
|
+
const normalized = typeof chunk === "string" ? Buffer.from(chunk) : Buffer.from(chunk);
|
|
121
|
+
chunks.push(normalized);
|
|
122
|
+
callback();
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
writable.on("finish", () => {
|
|
126
|
+
resolve(Buffer.concat(chunks).toString("utf8"));
|
|
127
|
+
});
|
|
128
|
+
writable.on("error", reject);
|
|
129
|
+
const stream = (0, import_server.renderToPipeableStream)(payload, moduleMap, { onError });
|
|
130
|
+
stream.pipe(writable);
|
|
131
|
+
});
|
|
132
|
+
}
|
|
107
133
|
function sendRSC(res, model, options = {}) {
|
|
108
134
|
const {
|
|
109
135
|
moduleMap = {},
|
|
@@ -481,6 +507,9 @@ function parseSearchParams(query) {
|
|
|
481
507
|
}
|
|
482
508
|
|
|
483
509
|
// src/http.ts
|
|
510
|
+
function createInitialHtmlErrorMarkup(message) {
|
|
511
|
+
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>`;
|
|
512
|
+
}
|
|
484
513
|
var INITIAL_HTML_WORKER_SCRIPT = `
|
|
485
514
|
const path = require("node:path");
|
|
486
515
|
const Module = require("node:module");
|
|
@@ -562,6 +591,12 @@ function stripBasePath(pathname, basePath) {
|
|
|
562
591
|
}
|
|
563
592
|
return pathname;
|
|
564
593
|
}
|
|
594
|
+
function sanitizeInitialHtmlWorkerNodeOptions(rawNodeOptions) {
|
|
595
|
+
if (!rawNodeOptions || rawNodeOptions.trim() === "") {
|
|
596
|
+
return "";
|
|
597
|
+
}
|
|
598
|
+
return rawNodeOptions.replace(/(^|\s)--conditions\s+react-server(?=\s|$)/g, " ").replace(/(^|\s)-r\s+(\S*webframez-react\/register)(?=\s|$)/g, " ").replace(/\s+/g, " ").trim();
|
|
599
|
+
}
|
|
565
600
|
function createInitialHtmlWorker(pagesDir) {
|
|
566
601
|
let child = null;
|
|
567
602
|
let nextRequestId = 1;
|
|
@@ -593,6 +628,7 @@ function createInitialHtmlWorker(pagesDir) {
|
|
|
593
628
|
cwd: process.cwd(),
|
|
594
629
|
env: {
|
|
595
630
|
...process.env,
|
|
631
|
+
NODE_OPTIONS: sanitizeInitialHtmlWorkerNodeOptions(process.env.NODE_OPTIONS),
|
|
596
632
|
WEBFRAMEZ_REACT_PAGES_DIR: pagesDir
|
|
597
633
|
},
|
|
598
634
|
stdio: ["ignore", "ignore", "pipe", "ipc"]
|
|
@@ -829,6 +865,10 @@ function createNodeRequestHandler(options) {
|
|
|
829
865
|
cookies: requestCookies
|
|
830
866
|
})
|
|
831
867
|
);
|
|
868
|
+
const initialPayload = {
|
|
869
|
+
model: resolved.model,
|
|
870
|
+
head: resolved.head
|
|
871
|
+
};
|
|
832
872
|
let rootHtml = "";
|
|
833
873
|
try {
|
|
834
874
|
rootHtml = await initialHtmlWorker.render({
|
|
@@ -839,7 +879,36 @@ function createNodeRequestHandler(options) {
|
|
|
839
879
|
});
|
|
840
880
|
} catch (error) {
|
|
841
881
|
console.error("[webframez-react] Failed to render initial HTML", error);
|
|
882
|
+
try {
|
|
883
|
+
initialHtmlWorker.dispose();
|
|
884
|
+
rootHtml = await initialHtmlWorker.render({
|
|
885
|
+
pathname: stripBasePath(url.pathname, basePath),
|
|
886
|
+
searchParams: parseSearchParams(url.searchParams),
|
|
887
|
+
cookies: requestCookies,
|
|
888
|
+
basename: basePath
|
|
889
|
+
});
|
|
890
|
+
} catch (retryError) {
|
|
891
|
+
console.error("[webframez-react] Retry for initial HTML failed", retryError);
|
|
892
|
+
res.statusCode = 500;
|
|
893
|
+
res.setHeader("Content-Type", "text/html");
|
|
894
|
+
res.end(
|
|
895
|
+
createHTMLShell({
|
|
896
|
+
title: "500 - Initial HTML render failed",
|
|
897
|
+
headTags: "",
|
|
898
|
+
clientScriptUrl,
|
|
899
|
+
rscEndpoint: rscPath,
|
|
900
|
+
rootHtml: createInitialHtmlErrorMarkup("Initial HTML render failed."),
|
|
901
|
+
basename: basePath,
|
|
902
|
+
liveReloadPath: liveReloadPath || void 0,
|
|
903
|
+
liveReloadServerId: liveReloadPath ? devServerId : void 0
|
|
904
|
+
})
|
|
905
|
+
);
|
|
906
|
+
return;
|
|
907
|
+
}
|
|
842
908
|
}
|
|
909
|
+
const initialFlightData = await renderRSCToString(initialPayload, {
|
|
910
|
+
moduleMap
|
|
911
|
+
});
|
|
843
912
|
res.statusCode = resolved.statusCode;
|
|
844
913
|
res.setHeader("Content-Type", "text/html");
|
|
845
914
|
res.end(
|
|
@@ -849,6 +918,7 @@ function createNodeRequestHandler(options) {
|
|
|
849
918
|
clientScriptUrl,
|
|
850
919
|
rscEndpoint: rscPath,
|
|
851
920
|
rootHtml,
|
|
921
|
+
initialFlightData,
|
|
852
922
|
basename: basePath,
|
|
853
923
|
liveReloadPath: liveReloadPath || void 0,
|
|
854
924
|
liveReloadServerId: liveReloadPath ? devServerId : void 0
|
package/dist/webframez-core.js
CHANGED
|
@@ -12,6 +12,7 @@ import path2 from "node:path";
|
|
|
12
12
|
import { spawn } from "node:child_process";
|
|
13
13
|
|
|
14
14
|
// src/server.ts
|
|
15
|
+
import { Writable } from "node:stream";
|
|
15
16
|
import { renderToPipeableStream } from "react-server-dom-webpack/server";
|
|
16
17
|
function defaultOnError(err) {
|
|
17
18
|
console.error("[webframez-react] RSC render error", err);
|
|
@@ -23,10 +24,12 @@ function createHTMLShell(options = {}) {
|
|
|
23
24
|
clientScriptUrl = "/client.js",
|
|
24
25
|
headTags = "",
|
|
25
26
|
rootHtml = "",
|
|
27
|
+
initialFlightData = "",
|
|
26
28
|
basename = "",
|
|
27
29
|
liveReloadPath,
|
|
28
30
|
liveReloadServerId
|
|
29
31
|
} = options;
|
|
32
|
+
const escapedInitialFlightData = initialFlightData ? JSON.stringify(initialFlightData).replace(/</g, "\\u003c").replace(/\u2028/g, "\\u2028").replace(/\u2029/g, "\\u2029") : '""';
|
|
30
33
|
const liveReloadScript = liveReloadPath && liveReloadServerId ? `<script>
|
|
31
34
|
(() => {
|
|
32
35
|
const endpoint = ${JSON.stringify(liveReloadPath)};
|
|
@@ -71,11 +74,34 @@ function createHTMLShell(options = {}) {
|
|
|
71
74
|
<div id="root">${rootHtml}</div>
|
|
72
75
|
<script>window.__RSC_ENDPOINT = "${rscEndpoint}";</script>
|
|
73
76
|
<script>window.__RSC_BASENAME = "${basename}";</script>
|
|
77
|
+
<script>window.__RSC_INITIAL_PAYLOAD = ${escapedInitialFlightData};</script>
|
|
74
78
|
<script type="module" src="${clientScriptUrl}"></script>
|
|
75
79
|
${liveReloadScript}
|
|
76
80
|
</body>
|
|
77
81
|
</html>`;
|
|
78
82
|
}
|
|
83
|
+
function renderRSCToString(payload, options = {}) {
|
|
84
|
+
const {
|
|
85
|
+
moduleMap = {},
|
|
86
|
+
onError = defaultOnError
|
|
87
|
+
} = options;
|
|
88
|
+
return new Promise((resolve, reject) => {
|
|
89
|
+
const chunks = [];
|
|
90
|
+
const writable = new Writable({
|
|
91
|
+
write(chunk, _encoding, callback) {
|
|
92
|
+
const normalized = typeof chunk === "string" ? Buffer.from(chunk) : Buffer.from(chunk);
|
|
93
|
+
chunks.push(normalized);
|
|
94
|
+
callback();
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
writable.on("finish", () => {
|
|
98
|
+
resolve(Buffer.concat(chunks).toString("utf8"));
|
|
99
|
+
});
|
|
100
|
+
writable.on("error", reject);
|
|
101
|
+
const stream = renderToPipeableStream(payload, moduleMap, { onError });
|
|
102
|
+
stream.pipe(writable);
|
|
103
|
+
});
|
|
104
|
+
}
|
|
79
105
|
function sendRSC(res, model, options = {}) {
|
|
80
106
|
const {
|
|
81
107
|
moduleMap = {},
|
|
@@ -453,6 +479,9 @@ function parseSearchParams(query) {
|
|
|
453
479
|
}
|
|
454
480
|
|
|
455
481
|
// src/http.ts
|
|
482
|
+
function createInitialHtmlErrorMarkup(message) {
|
|
483
|
+
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>`;
|
|
484
|
+
}
|
|
456
485
|
var INITIAL_HTML_WORKER_SCRIPT = `
|
|
457
486
|
const path = require("node:path");
|
|
458
487
|
const Module = require("node:module");
|
|
@@ -534,6 +563,12 @@ function stripBasePath(pathname, basePath) {
|
|
|
534
563
|
}
|
|
535
564
|
return pathname;
|
|
536
565
|
}
|
|
566
|
+
function sanitizeInitialHtmlWorkerNodeOptions(rawNodeOptions) {
|
|
567
|
+
if (!rawNodeOptions || rawNodeOptions.trim() === "") {
|
|
568
|
+
return "";
|
|
569
|
+
}
|
|
570
|
+
return rawNodeOptions.replace(/(^|\s)--conditions\s+react-server(?=\s|$)/g, " ").replace(/(^|\s)-r\s+(\S*webframez-react\/register)(?=\s|$)/g, " ").replace(/\s+/g, " ").trim();
|
|
571
|
+
}
|
|
537
572
|
function createInitialHtmlWorker(pagesDir) {
|
|
538
573
|
let child = null;
|
|
539
574
|
let nextRequestId = 1;
|
|
@@ -565,6 +600,7 @@ function createInitialHtmlWorker(pagesDir) {
|
|
|
565
600
|
cwd: process.cwd(),
|
|
566
601
|
env: {
|
|
567
602
|
...process.env,
|
|
603
|
+
NODE_OPTIONS: sanitizeInitialHtmlWorkerNodeOptions(process.env.NODE_OPTIONS),
|
|
568
604
|
WEBFRAMEZ_REACT_PAGES_DIR: pagesDir
|
|
569
605
|
},
|
|
570
606
|
stdio: ["ignore", "ignore", "pipe", "ipc"]
|
|
@@ -801,6 +837,10 @@ function createNodeRequestHandler(options) {
|
|
|
801
837
|
cookies: requestCookies
|
|
802
838
|
})
|
|
803
839
|
);
|
|
840
|
+
const initialPayload = {
|
|
841
|
+
model: resolved.model,
|
|
842
|
+
head: resolved.head
|
|
843
|
+
};
|
|
804
844
|
let rootHtml = "";
|
|
805
845
|
try {
|
|
806
846
|
rootHtml = await initialHtmlWorker.render({
|
|
@@ -811,7 +851,36 @@ function createNodeRequestHandler(options) {
|
|
|
811
851
|
});
|
|
812
852
|
} catch (error) {
|
|
813
853
|
console.error("[webframez-react] Failed to render initial HTML", error);
|
|
854
|
+
try {
|
|
855
|
+
initialHtmlWorker.dispose();
|
|
856
|
+
rootHtml = await initialHtmlWorker.render({
|
|
857
|
+
pathname: stripBasePath(url.pathname, basePath),
|
|
858
|
+
searchParams: parseSearchParams(url.searchParams),
|
|
859
|
+
cookies: requestCookies,
|
|
860
|
+
basename: basePath
|
|
861
|
+
});
|
|
862
|
+
} catch (retryError) {
|
|
863
|
+
console.error("[webframez-react] Retry for initial HTML failed", retryError);
|
|
864
|
+
res.statusCode = 500;
|
|
865
|
+
res.setHeader("Content-Type", "text/html");
|
|
866
|
+
res.end(
|
|
867
|
+
createHTMLShell({
|
|
868
|
+
title: "500 - Initial HTML render failed",
|
|
869
|
+
headTags: "",
|
|
870
|
+
clientScriptUrl,
|
|
871
|
+
rscEndpoint: rscPath,
|
|
872
|
+
rootHtml: createInitialHtmlErrorMarkup("Initial HTML render failed."),
|
|
873
|
+
basename: basePath,
|
|
874
|
+
liveReloadPath: liveReloadPath || void 0,
|
|
875
|
+
liveReloadServerId: liveReloadPath ? devServerId : void 0
|
|
876
|
+
})
|
|
877
|
+
);
|
|
878
|
+
return;
|
|
879
|
+
}
|
|
814
880
|
}
|
|
881
|
+
const initialFlightData = await renderRSCToString(initialPayload, {
|
|
882
|
+
moduleMap
|
|
883
|
+
});
|
|
815
884
|
res.statusCode = resolved.statusCode;
|
|
816
885
|
res.setHeader("Content-Type", "text/html");
|
|
817
886
|
res.end(
|
|
@@ -821,6 +890,7 @@ function createNodeRequestHandler(options) {
|
|
|
821
890
|
clientScriptUrl,
|
|
822
891
|
rscEndpoint: rscPath,
|
|
823
892
|
rootHtml,
|
|
893
|
+
initialFlightData,
|
|
824
894
|
basename: basePath,
|
|
825
895
|
liveReloadPath: liveReloadPath || void 0,
|
|
826
896
|
liveReloadServerId: liveReloadPath ? devServerId : void 0
|