@webtypen/webframez-react 0.0.48 → 0.0.50

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.js CHANGED
@@ -7,6 +7,7 @@ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require
7
7
  });
8
8
 
9
9
  // src/http.ts
10
+ import { AsyncLocalStorage } from "node:async_hooks";
10
11
  import fs2 from "node:fs";
11
12
  import path3 from "node:path";
12
13
  import { fileURLToPath, pathToFileURL } from "node:url";
@@ -35,6 +36,8 @@ function createHTMLShell(options = {}) {
35
36
  buildId = "",
36
37
  headTags = "",
37
38
  bodyClassName = "",
39
+ bodyStartHtml = "",
40
+ bodyEndHtml = "",
38
41
  rootHtml = "",
39
42
  initialFlightData = "",
40
43
  basename = "",
@@ -84,7 +87,9 @@ function createHTMLShell(options = {}) {
84
87
  ${headTags}
85
88
  </head>
86
89
  <body${bodyClassName ? ` class="${bodyClassName.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;").replace(/>/g, "&gt;")}"` : ""}>
90
+ ${bodyStartHtml}
87
91
  <div id="root">${rootHtml}</div>
92
+ ${bodyEndHtml}
88
93
  <script>window.__RSC_ENDPOINT = "${rscEndpoint}";</script>
89
94
  <script>window.__RSC_BASENAME = "${basename}";</script>
90
95
  <script>window.__RSC_ROUTE_BASE_PATH = "${routeBasePath}";</script>
@@ -199,6 +204,12 @@ function normalizeHeadConfig(head, inheritedBasename) {
199
204
  href: resolveHeadAssetUrl(link.href, effectiveBasename)
200
205
  }));
201
206
  }
207
+ if (normalizedHead.scripts) {
208
+ normalizedHead.scripts = normalizedHead.scripts.map((script) => ({
209
+ ...script,
210
+ ...script.src ? { src: resolveHeadAssetUrl(script.src, effectiveBasename) } : {}
211
+ }));
212
+ }
202
213
  return normalizedHead;
203
214
  }
204
215
 
@@ -311,8 +322,7 @@ var FORCED_PACKAGE_REQUESTS = [
311
322
  "react-server-dom-webpack",
312
323
  "react-server-dom-webpack/server",
313
324
  "react-server-dom-webpack/client",
314
- "react-server-dom-webpack/client.node",
315
- "scheduler"
325
+ "react-server-dom-webpack/client.node"
316
326
  ];
317
327
  var forcedPackageResolutionInstalled = false;
318
328
  function normalizePathname(pathname) {
@@ -451,7 +461,9 @@ function matchRoute(entry, pathname) {
451
461
  function mergeHead(...configs) {
452
462
  const merged = {
453
463
  meta: [],
454
- links: []
464
+ links: [],
465
+ scripts: [],
466
+ html: []
455
467
  };
456
468
  for (const candidate of configs) {
457
469
  const config = normalizeHeadConfig(candidate, merged.basename);
@@ -486,9 +498,28 @@ function mergeHead(...configs) {
486
498
  if (config.links) {
487
499
  merged.links?.push(...config.links);
488
500
  }
501
+ if (config.scripts) {
502
+ merged.scripts?.push(...config.scripts);
503
+ }
504
+ if (config.html) {
505
+ merged.html?.push(...Array.isArray(config.html) ? config.html : [config.html]);
506
+ }
507
+ if (config.bodyStartHtml) {
508
+ merged.bodyStartHtml = [merged.bodyStartHtml, config.bodyStartHtml].filter(Boolean).join("\n");
509
+ }
510
+ if (config.bodyEndHtml) {
511
+ merged.bodyEndHtml = [merged.bodyEndHtml, config.bodyEndHtml].filter(Boolean).join("\n");
512
+ }
489
513
  }
490
514
  return merged;
491
515
  }
516
+ function renderGenericAttributes(entry, excluded = []) {
517
+ return Object.entries(entry).filter(
518
+ ([key, value]) => !excluded.includes(key) && value !== void 0 && value !== null && value !== false
519
+ ).map(
520
+ ([key, value]) => value === true ? key : `${key}="${escapeHtml(String(value))}"`
521
+ ).join(" ");
522
+ }
492
523
  function renderHeadToString(head) {
493
524
  const normalizedHead = normalizeHeadConfig(head) ?? head;
494
525
  const tags = [];
@@ -510,6 +541,23 @@ function renderHeadToString(head) {
510
541
  const attrs = Object.entries(link).filter(([, value]) => Boolean(value)).map(([key, value]) => `${key}="${escapeHtml(String(value))}"`).join(" ");
511
542
  tags.push(`<link ${createManagedAttributes()} ${attrs} />`);
512
543
  }
544
+ for (const script of normalizedHead.scripts ?? []) {
545
+ if (script.html) {
546
+ tags.push(script.html);
547
+ continue;
548
+ }
549
+ const attrs = renderGenericAttributes(script, [
550
+ "content",
551
+ "html"
552
+ ]);
553
+ const content = script.content ? String(script.content) : "";
554
+ tags.push(`<script ${createManagedAttributes()} ${attrs}>${content}</script>`);
555
+ }
556
+ for (const html of normalizedHead.html ?? []) {
557
+ if (typeof html === "string" && html.trim() !== "") {
558
+ tags.push(html);
559
+ }
560
+ }
513
561
  return tags.join("\n");
514
562
  }
515
563
  function clearModuleCache(modulePath, rootDir, visited = /* @__PURE__ */ new Set()) {
@@ -595,6 +643,7 @@ function isRouteAbort(value) {
595
643
  function createFileRouter(options) {
596
644
  installForcedPackageResolution();
597
645
  const pagesDir = options.pagesDir;
646
+ const onData = options.onData;
598
647
  const layoutPath = path2.join(pagesDir, "layout.js");
599
648
  const errorPath = path2.join(pagesDir, "errors.js");
600
649
  const middlewaresPath = path2.join(pagesDir, "middlewares.js");
@@ -759,10 +808,16 @@ function createFileRouter(options) {
759
808
  data: mergeRouteData(middlewareContext.data, pageData)
760
809
  };
761
810
  activeContext = pageContext;
762
- const pageNode = await pageModule.default(pageContext);
763
- const layoutHead = layoutModule ? await resolveHead(layoutModule, pageContext) : void 0;
764
- const pageHead = await resolveHead(pageModule, pageContext);
765
- const layoutNode = layoutModule ? await layoutModule.default(pageContext) : null;
811
+ const onDataResult = typeof onData === "function" ? await onData(pageContext) : void 0;
812
+ const eventContext = {
813
+ ...pageContext,
814
+ data: mergeRouteData(pageContext.data, onDataResult)
815
+ };
816
+ activeContext = eventContext;
817
+ const pageNode = await pageModule.default(eventContext);
818
+ const layoutHead = layoutModule ? await resolveHead(layoutModule, eventContext) : void 0;
819
+ const pageHead = await resolveHead(pageModule, eventContext);
820
+ const layoutNode = layoutModule ? await layoutModule.default(eventContext) : null;
766
821
  const model = layoutNode ? injectRouteChildren(layoutNode, pageNode) : pageNode;
767
822
  const contextModel = layoutNode ? injectRouteChildren(layoutNode, /* @__PURE__ */ jsx(RouteChildren, {})) : void 0;
768
823
  return {
@@ -771,7 +826,7 @@ function createFileRouter(options) {
771
826
  contextModel,
772
827
  pageModel: pageNode,
773
828
  head: mergeHead(layoutHead, pageHead),
774
- context: pageContext
829
+ context: eventContext
775
830
  };
776
831
  } catch (error) {
777
832
  if (isRouteAbort(error)) {
@@ -918,6 +973,7 @@ const rootParent = {
918
973
  path: process.cwd(),
919
974
  paths: Module._nodeModulePaths(process.cwd()),
920
975
  };
976
+ // Transitive dependencies (e.g. scheduler) must resolve from their importer.
921
977
  const forcedPackageRequests = [
922
978
  "@webtypen/webframez-core",
923
979
  "@webtypen/webframez-react",
@@ -932,7 +988,6 @@ const forcedPackageRequests = [
932
988
  "react-server-dom-webpack/server",
933
989
  "react-server-dom-webpack/client",
934
990
  "react-server-dom-webpack/client.node",
935
- "scheduler"
936
991
  ];
937
992
 
938
993
  function shouldForcePackageResolution(request) {
@@ -1124,13 +1179,17 @@ async function renderHtmlFromFlightData(flightData, moduleMap) {
1124
1179
  ? payload.head.basename
1125
1180
  : "";
1126
1181
 
1127
- const previousBasename = globalThis.__RSC_BASENAME;
1128
- globalThis.__RSC_BASENAME = basename;
1129
- try {
1130
- return await renderHtml(model);
1131
- } finally {
1132
- globalThis.__RSC_BASENAME = previousBasename;
1133
- }
1182
+ const routingContext = globalThis.__WEBFRAMEZ_ROUTING_CONTEXT__ ??=
1183
+ new (require("node:async_hooks").AsyncLocalStorage)();
1184
+ return routingContext.run(basename, async () => {
1185
+ const previousBasename = globalThis.__RSC_BASENAME;
1186
+ globalThis.__RSC_BASENAME = basename;
1187
+ try {
1188
+ return await renderHtml(model);
1189
+ } finally {
1190
+ globalThis.__RSC_BASENAME = previousBasename;
1191
+ }
1192
+ });
1134
1193
  }
1135
1194
 
1136
1195
  process.on("message", async (message) => {
@@ -1366,24 +1425,10 @@ ${stderrBuffer.trim()}` : "";
1366
1425
  }
1367
1426
  };
1368
1427
  }
1428
+ var routingRuntime = globalThis;
1429
+ var basenameContext = routingRuntime.__WEBFRAMEZ_ROUTING_CONTEXT__ ??= new AsyncLocalStorage();
1369
1430
  function withRequestBasename(basename, fn) {
1370
- const target = globalThis;
1371
- const previous = target.__RSC_BASENAME;
1372
- target.__RSC_BASENAME = basename;
1373
- const finish = () => {
1374
- target.__RSC_BASENAME = previous;
1375
- };
1376
- try {
1377
- const result = fn();
1378
- if (result && typeof result.then === "function") {
1379
- return result.finally(finish);
1380
- }
1381
- finish();
1382
- return result;
1383
- } catch (error) {
1384
- finish();
1385
- throw error;
1386
- }
1431
+ return basenameContext.run(basename, fn);
1387
1432
  }
1388
1433
  function parseCookies(rawCookieHeader) {
1389
1434
  const raw = Array.isArray(rawCookieHeader) ? rawCookieHeader.join("; ") : rawCookieHeader ?? "";
@@ -1418,12 +1463,27 @@ function normalizeClientManifest(manifest, options) {
1418
1463
  }
1419
1464
  };
1420
1465
  for (const [key, value] of Object.entries(manifest)) {
1466
+ const hashIndex = key.indexOf("#");
1467
+ const moduleKey = hashIndex < 0 ? key : key.slice(0, hashIndex);
1468
+ const exportSuffix = hashIndex < 0 ? "" : key.slice(hashIndex);
1469
+ if (!path3.isAbsolute(moduleKey) && !moduleKey.includes(":") && !moduleKey.startsWith("file://")) {
1470
+ const runtimePath = path3.resolve(options.cwd, moduleKey);
1471
+ const allowedRoots = [options.distRootDir, ...candidateNodeModulesDirs];
1472
+ const insideArtifact = allowedRoots.some((root) => {
1473
+ const relative = path3.relative(path3.resolve(root), runtimePath);
1474
+ return relative !== "" && relative !== ".." && !relative.startsWith(`..${path3.sep}`) && !path3.isAbsolute(relative);
1475
+ });
1476
+ if (insideArtifact) {
1477
+ addAlias(`${runtimePath}${exportSuffix}`, value);
1478
+ addAlias(`${pathToFileURL(runtimePath).href}${exportSuffix}`, value);
1479
+ }
1480
+ }
1421
1481
  if (!key.startsWith("file://")) {
1422
1482
  continue;
1423
1483
  }
1424
1484
  let absolutePath = "";
1425
1485
  try {
1426
- absolutePath = fileURLToPath(key);
1486
+ absolutePath = fileURLToPath(moduleKey);
1427
1487
  } catch {
1428
1488
  continue;
1429
1489
  }
@@ -1434,13 +1494,13 @@ function normalizeClientManifest(manifest, options) {
1434
1494
  }
1435
1495
  const relativeModulePath = absolutePath.slice(markerIndex + marker.length);
1436
1496
  const relativeModulePathPosix = relativeModulePath.split(path3.sep).join("/");
1437
- addAlias(`./node_modules/${relativeModulePathPosix}`, value);
1438
- addAlias(`node_modules/${relativeModulePathPosix}`, value);
1439
- addAlias(absolutePath, value);
1497
+ addAlias(`./node_modules/${relativeModulePathPosix}${exportSuffix}`, value);
1498
+ addAlias(`node_modules/${relativeModulePathPosix}${exportSuffix}`, value);
1499
+ addAlias(`${absolutePath}${exportSuffix}`, value);
1440
1500
  for (const nodeModulesDir of candidateNodeModulesDirs) {
1441
1501
  const aliasPath = path3.join(nodeModulesDir, relativeModulePath);
1442
- addAlias(aliasPath, value);
1443
- addAlias(pathToFileURL(aliasPath).href, value);
1502
+ addAlias(`${aliasPath}${exportSuffix}`, value);
1503
+ addAlias(`${pathToFileURL(aliasPath).href}${exportSuffix}`, value);
1444
1504
  }
1445
1505
  }
1446
1506
  return normalized;
@@ -1585,16 +1645,16 @@ function createNodeRequestHandler(options) {
1585
1645
  const manifestPath = path3.resolve(
1586
1646
  options.manifestPath ?? path3.join(distRootDir, "react-client-manifest.json")
1587
1647
  );
1588
- const assetsPrefix = options.assetsPrefix ?? "/assets/";
1589
- const rscPath = options.rscPath ?? "/rsc";
1590
- const clientScriptUrl = options.clientScriptUrl ?? "/assets/client.js";
1591
1648
  const basePath = normalizeBasePath(options.basePath);
1649
+ const assetsPrefix = options.assetsPrefix ?? `${basePath}/assets/`;
1650
+ const rscPath = options.rscPath ?? `${basePath}/rsc`;
1651
+ const clientScriptUrl = options.clientScriptUrl ?? `${basePath}/assets/client.js`;
1592
1652
  const nodeEnv = process.env.NODE_ENV || "";
1593
1653
  const runningInWatchMode = Array.isArray(process.execArgv) && process.execArgv.includes("--watch");
1594
1654
  const liveReloadEnabled = options.liveReloadPath !== false && (nodeEnv === "development" || runningInWatchMode);
1595
1655
  const liveReloadPath = !liveReloadEnabled ? "" : options.liveReloadPath ?? `${basePath || ""}/__webframez_live_reload`;
1596
1656
  const liveReloadClients = /* @__PURE__ */ new Set();
1597
- const router = createFileRouter({ pagesDir });
1657
+ const router = createFileRouter({ pagesDir, onData: options.onData });
1598
1658
  const getManifestState = createManifestLoader({
1599
1659
  distRootDir,
1600
1660
  manifestPath,
@@ -1607,7 +1667,7 @@ function createNodeRequestHandler(options) {
1607
1667
  process.once("exit", disposeInitialHtmlWorker);
1608
1668
  process.once("SIGINT", disposeInitialHtmlWorker);
1609
1669
  process.once("SIGTERM", disposeInitialHtmlWorker);
1610
- return async function handleRequest(req, res) {
1670
+ const handleRequest = async (req, res) => {
1611
1671
  if (!req.url) {
1612
1672
  res.statusCode = 400;
1613
1673
  res.end("Bad request");
@@ -1671,6 +1731,7 @@ function createNodeRequestHandler(options) {
1671
1731
  request: requestContext
1672
1732
  })
1673
1733
  );
1734
+ resolved2.head = { ...resolved2.head, basename: resolved2.head.basename ?? basePath };
1674
1735
  attachResolvedContextToCoreRequest(req, resolved2.context);
1675
1736
  const payload = {
1676
1737
  model: resolved2.model,
@@ -1746,6 +1807,7 @@ function createNodeRequestHandler(options) {
1746
1807
  )
1747
1808
  })
1748
1809
  );
1810
+ resolved.head = { ...resolved.head, basename: resolved.head.basename ?? basePath };
1749
1811
  attachResolvedContextToCoreRequest(req, resolved.context);
1750
1812
  const initialPayload = {
1751
1813
  model: resolved.model,
@@ -1818,6 +1880,8 @@ function createNodeRequestHandler(options) {
1818
1880
  initialFlightData,
1819
1881
  basename: shellBasename,
1820
1882
  routeBasePath: shellRouteBasePath,
1883
+ bodyStartHtml: resolved.head.bodyStartHtml || "",
1884
+ bodyEndHtml: resolved.head.bodyEndHtml || "",
1821
1885
  liveReloadPath: liveReloadPath || void 0,
1822
1886
  liveReloadServerId: liveReloadPath ? devServerId : void 0
1823
1887
  }),
@@ -1829,7 +1893,29 @@ function createNodeRequestHandler(options) {
1829
1893
  }
1830
1894
  );
1831
1895
  };
1896
+ return (req, res) => withRequestBasename(basePath, () => handleRequest(req, res));
1897
+ }
1898
+ var standaloneHtmlWorker;
1899
+ async function renderReactToHtml(element) {
1900
+ if (!standaloneHtmlWorker) {
1901
+ standaloneHtmlWorker = createInitialHtmlWorker(process.cwd());
1902
+ process.once("exit", disposeReactHtmlRenderer);
1903
+ }
1904
+ const flightData = await renderRSCToString({ model: element }, {
1905
+ moduleMap: {},
1906
+ onError: (error) => {
1907
+ console.error("[webframez-react] HTML render failed", error);
1908
+ }
1909
+ });
1910
+ return standaloneHtmlWorker.renderFromFlightData({ flightData, moduleMap: {} });
1911
+ }
1912
+ function disposeReactHtmlRenderer() {
1913
+ process.removeListener("exit", disposeReactHtmlRenderer);
1914
+ standaloneHtmlWorker?.dispose();
1915
+ standaloneHtmlWorker = void 0;
1832
1916
  }
1833
1917
  export {
1834
- createNodeRequestHandler
1918
+ createNodeRequestHandler,
1919
+ disposeReactHtmlRenderer,
1920
+ renderReactToHtml
1835
1921
  };