@webtypen/webframez-react 0.0.35 → 0.0.36
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/bin/webframez-react.mjs +358 -1
- package/defaults/default-client.js +3 -0
- package/defaults/webpack.client.cjs +177 -10
- package/dist/build-plugin.cjs +111 -0
- package/dist/build-plugin.d.ts +14 -0
- package/dist/build-plugin.js +77 -0
- package/dist/client.cjs +1 -1
- package/dist/client.js +1 -1
- package/dist/http.cjs +197 -35
- package/dist/http.js +202 -35
- package/dist/index.cjs +325 -51
- package/dist/index.js +327 -51
- package/dist/types.d.ts +0 -1
- package/dist/webframez-core.cjs +325 -51
- package/dist/webframez-core.d.ts +27 -3
- package/dist/webframez-core.js +329 -51
- package/package.json +10 -1
package/dist/http.js
CHANGED
|
@@ -11,6 +11,12 @@ import fs2 from "node:fs";
|
|
|
11
11
|
import path3 from "node:path";
|
|
12
12
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
13
13
|
import { spawn } from "node:child_process";
|
|
14
|
+
import {
|
|
15
|
+
brotliCompressSync,
|
|
16
|
+
createBrotliCompress,
|
|
17
|
+
createGzip,
|
|
18
|
+
gzipSync
|
|
19
|
+
} from "node:zlib";
|
|
14
20
|
|
|
15
21
|
// src/server.ts
|
|
16
22
|
import path from "node:path";
|
|
@@ -767,7 +773,77 @@ function parseSearchParams(query) {
|
|
|
767
773
|
function createInitialHtmlErrorMarkup(message) {
|
|
768
774
|
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>`;
|
|
769
775
|
}
|
|
776
|
+
function createClientAssetVersion(distRootDir) {
|
|
777
|
+
try {
|
|
778
|
+
const stat = fs2.statSync(path3.join(distRootDir, "client.js"));
|
|
779
|
+
return `${Math.floor(stat.mtimeMs)}-${stat.size}`;
|
|
780
|
+
} catch {
|
|
781
|
+
return "";
|
|
782
|
+
}
|
|
783
|
+
}
|
|
784
|
+
function appendAssetVersion(url, version) {
|
|
785
|
+
if (!version) {
|
|
786
|
+
return url;
|
|
787
|
+
}
|
|
788
|
+
const separator = url.includes("?") ? "&" : "?";
|
|
789
|
+
return `${url}${separator}v=${encodeURIComponent(version)}`;
|
|
790
|
+
}
|
|
791
|
+
function isWithinDirectory(filePath, directory) {
|
|
792
|
+
return filePath === directory || filePath.startsWith(`${directory}${path3.sep}`);
|
|
793
|
+
}
|
|
794
|
+
function isCompressibleAsset(ext) {
|
|
795
|
+
return [".js", ".mjs", ".json", ".css", ".svg", ".txt", ".html"].includes(ext);
|
|
796
|
+
}
|
|
797
|
+
function isHashedAssetPath(filePath) {
|
|
798
|
+
return /-[a-f0-9]{12,}\.[cm]?js$/i.test(path3.basename(filePath));
|
|
799
|
+
}
|
|
800
|
+
function setAssetContentType(res, ext) {
|
|
801
|
+
if (ext === ".js" || ext === ".mjs") {
|
|
802
|
+
res.setHeader("Content-Type", "text/javascript; charset=utf-8");
|
|
803
|
+
} else if (ext === ".json") {
|
|
804
|
+
res.setHeader("Content-Type", "application/json; charset=utf-8");
|
|
805
|
+
} else if (ext === ".css") {
|
|
806
|
+
res.setHeader("Content-Type", "text/css; charset=utf-8");
|
|
807
|
+
} else if (ext === ".svg") {
|
|
808
|
+
res.setHeader("Content-Type", "image/svg+xml; charset=utf-8");
|
|
809
|
+
}
|
|
810
|
+
}
|
|
811
|
+
function getPreferredContentEncoding(req, ext, fileSize) {
|
|
812
|
+
if (!isCompressibleAsset(ext) || fileSize < 1024) {
|
|
813
|
+
return "";
|
|
814
|
+
}
|
|
815
|
+
const acceptEncoding = String(req.headers["accept-encoding"] || "");
|
|
816
|
+
if (/\bbr\b/.test(acceptEncoding)) {
|
|
817
|
+
return "br";
|
|
818
|
+
}
|
|
819
|
+
if (/\bgzip\b/.test(acceptEncoding)) {
|
|
820
|
+
return "gzip";
|
|
821
|
+
}
|
|
822
|
+
return "";
|
|
823
|
+
}
|
|
824
|
+
function sendTextResponse(req, res, body, options) {
|
|
825
|
+
const bodyBuffer = Buffer.from(body);
|
|
826
|
+
const contentEncoding = options.compress === false ? "" : getPreferredContentEncoding(req, ".html", bodyBuffer.length);
|
|
827
|
+
res.statusCode = options.statusCode ?? 200;
|
|
828
|
+
res.setHeader("Content-Type", options.contentType);
|
|
829
|
+
if (options.cacheControl) {
|
|
830
|
+
res.setHeader("Cache-Control", options.cacheControl);
|
|
831
|
+
}
|
|
832
|
+
res.setHeader("Vary", "Accept-Encoding");
|
|
833
|
+
if (contentEncoding === "br") {
|
|
834
|
+
res.setHeader("Content-Encoding", "br");
|
|
835
|
+
res.end(brotliCompressSync(bodyBuffer));
|
|
836
|
+
return;
|
|
837
|
+
}
|
|
838
|
+
if (contentEncoding === "gzip") {
|
|
839
|
+
res.setHeader("Content-Encoding", "gzip");
|
|
840
|
+
res.end(gzipSync(bodyBuffer));
|
|
841
|
+
return;
|
|
842
|
+
}
|
|
843
|
+
res.end(bodyBuffer);
|
|
844
|
+
}
|
|
770
845
|
var INITIAL_HTML_WORKER_SCRIPT = `
|
|
846
|
+
const fs = require("node:fs");
|
|
771
847
|
const path = require("node:path");
|
|
772
848
|
const Module = require("node:module");
|
|
773
849
|
const { Readable, Writable } = require("node:stream");
|
|
@@ -817,11 +893,42 @@ const reactDomServer = require(path.join(path.dirname(reactDomPkg), "server.node
|
|
|
817
893
|
globalThis.__webpack_chunk_load__ = function __webframezNoopChunkLoad() {
|
|
818
894
|
return Promise.resolve();
|
|
819
895
|
};
|
|
896
|
+
function normalizeWebframezRequireCandidate(candidate) {
|
|
897
|
+
const stagingMarker = path.join(".webframez-build", "");
|
|
898
|
+
const appMarker = path.join("app", "");
|
|
899
|
+
const stagingIndex = candidate.indexOf(stagingMarker);
|
|
900
|
+
const appIndex = candidate.indexOf(appMarker, stagingIndex >= 0 ? stagingIndex : 0);
|
|
901
|
+
if (stagingIndex >= 0 && appIndex >= 0) {
|
|
902
|
+
const runtimeCandidate = path.resolve(process.cwd(), candidate.slice(appIndex));
|
|
903
|
+
if (fs.existsSync(runtimeCandidate)) {
|
|
904
|
+
return runtimeCandidate;
|
|
905
|
+
}
|
|
906
|
+
}
|
|
907
|
+
|
|
908
|
+
const frameworkDistDir = path.join("node_modules", "@webtypen", "webframez-react", "dist");
|
|
909
|
+
const shouldUseCjs =
|
|
910
|
+
candidate.includes(frameworkDistDir) &&
|
|
911
|
+
(candidate.endsWith(path.join("dist", "navigation.js")) ||
|
|
912
|
+
candidate.endsWith(path.join("dist", "route-slot.js")));
|
|
913
|
+
|
|
914
|
+
if (!shouldUseCjs) {
|
|
915
|
+
return candidate;
|
|
916
|
+
}
|
|
917
|
+
|
|
918
|
+
const cjsCandidate = candidate.slice(0, -3) + ".cjs";
|
|
919
|
+
return fs.existsSync(cjsCandidate) ? cjsCandidate : candidate;
|
|
920
|
+
}
|
|
921
|
+
|
|
820
922
|
globalThis.__webpack_require__ = function __webframezNodeRequire(id) {
|
|
821
923
|
if (typeof id !== "string") {
|
|
822
924
|
return require(id);
|
|
823
925
|
}
|
|
824
926
|
|
|
927
|
+
const directRequireTarget = normalizeWebframezRequireCandidate(id);
|
|
928
|
+
if (directRequireTarget !== id) {
|
|
929
|
+
return require(directRequireTarget);
|
|
930
|
+
}
|
|
931
|
+
|
|
825
932
|
if (id.startsWith("./")) {
|
|
826
933
|
const relativeId = id.slice(2);
|
|
827
934
|
const candidates = [
|
|
@@ -829,10 +936,16 @@ globalThis.__webpack_require__ = function __webframezNodeRequire(id) {
|
|
|
829
936
|
path.resolve(process.cwd(), "..", relativeId)
|
|
830
937
|
];
|
|
831
938
|
for (const candidate of candidates) {
|
|
939
|
+
const requireTarget = normalizeWebframezRequireCandidate(candidate);
|
|
832
940
|
try {
|
|
833
|
-
return require(
|
|
941
|
+
return require(requireTarget);
|
|
834
942
|
} catch (error) {
|
|
835
|
-
const missingCandidate =
|
|
943
|
+
const missingCandidate =
|
|
944
|
+
error &&
|
|
945
|
+
error.code === "MODULE_NOT_FOUND" &&
|
|
946
|
+
typeof error.message === "string" &&
|
|
947
|
+
(error.message.includes("'" + candidate + "'") ||
|
|
948
|
+
error.message.includes("'" + requireTarget + "'"));
|
|
836
949
|
if (!missingCandidate) {
|
|
837
950
|
throw error;
|
|
838
951
|
}
|
|
@@ -1054,6 +1167,14 @@ function joinRuntimeBasePath(basePath, pathname) {
|
|
|
1054
1167
|
}
|
|
1055
1168
|
return normalizedPath === "/" ? basePath : `${basePath}${normalizedPath}`;
|
|
1056
1169
|
}
|
|
1170
|
+
function joinRuntimeAssetPath(basePath, pathname) {
|
|
1171
|
+
const normalizedBasePath = normalizeRuntimeBasePath(basePath) ?? "";
|
|
1172
|
+
const normalizedPath = !pathname || pathname === "/" ? "/" : pathname.startsWith("/") ? pathname : `/${pathname}`;
|
|
1173
|
+
if (normalizedBasePath && (normalizedPath === normalizedBasePath || normalizedPath.startsWith(`${normalizedBasePath}/`))) {
|
|
1174
|
+
return normalizedPath;
|
|
1175
|
+
}
|
|
1176
|
+
return joinRuntimeBasePath(normalizedBasePath, normalizedPath);
|
|
1177
|
+
}
|
|
1057
1178
|
function sanitizeInitialHtmlWorkerNodeOptions(rawNodeOptions) {
|
|
1058
1179
|
if (!rawNodeOptions || rawNodeOptions.trim() === "") {
|
|
1059
1180
|
return "";
|
|
@@ -1248,6 +1369,18 @@ function createServerConsumerManifest(manifest) {
|
|
|
1248
1369
|
const consumerManifest = {};
|
|
1249
1370
|
const normalizeWorkerModuleId = (requestKey, rawModuleId) => {
|
|
1250
1371
|
if (typeof rawModuleId === "string" && rawModuleId.trim() !== "") {
|
|
1372
|
+
const isStagingModuleId = rawModuleId.startsWith("./.webframez-build/") || rawModuleId.includes(`${path3.sep}.webframez-build${path3.sep}`);
|
|
1373
|
+
const isRuntimeBuildKey = requestKey.startsWith("file://") || requestKey.startsWith("/") && requestKey.includes(`${path3.sep}build${path3.sep}app${path3.sep}`);
|
|
1374
|
+
if (isStagingModuleId && isRuntimeBuildKey) {
|
|
1375
|
+
if (requestKey.startsWith("file://")) {
|
|
1376
|
+
try {
|
|
1377
|
+
return fileURLToPath(requestKey);
|
|
1378
|
+
} catch {
|
|
1379
|
+
return rawModuleId;
|
|
1380
|
+
}
|
|
1381
|
+
}
|
|
1382
|
+
return requestKey;
|
|
1383
|
+
}
|
|
1251
1384
|
return rawModuleId;
|
|
1252
1385
|
}
|
|
1253
1386
|
if (typeof rawModuleId !== "number" || !Number.isFinite(rawModuleId)) {
|
|
@@ -1329,6 +1462,14 @@ function createServerConsumerManifest(manifest) {
|
|
|
1329
1462
|
name: exportName
|
|
1330
1463
|
});
|
|
1331
1464
|
}
|
|
1465
|
+
} else if (typeof entry.id === "string" && entry.id.trim() !== "") {
|
|
1466
|
+
addReference(entry.id, "*", reference);
|
|
1467
|
+
for (const exportName of exportNames) {
|
|
1468
|
+
addReference(entry.id, exportName, {
|
|
1469
|
+
...reference,
|
|
1470
|
+
name: exportName
|
|
1471
|
+
});
|
|
1472
|
+
}
|
|
1332
1473
|
}
|
|
1333
1474
|
}
|
|
1334
1475
|
return consumerManifest;
|
|
@@ -1443,24 +1584,49 @@ function createNodeRequestHandler(options) {
|
|
|
1443
1584
|
if (url.pathname.startsWith(assetsPrefix)) {
|
|
1444
1585
|
const relative = url.pathname.slice(assetsPrefix.length);
|
|
1445
1586
|
const filePath = path3.resolve(distRootDir, relative);
|
|
1446
|
-
if (!filePath
|
|
1587
|
+
if (!isWithinDirectory(filePath, distRootDir)) {
|
|
1447
1588
|
res.statusCode = 400;
|
|
1448
1589
|
res.end("Invalid path");
|
|
1449
1590
|
return;
|
|
1450
1591
|
}
|
|
1451
1592
|
const ext = path3.extname(filePath);
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1593
|
+
let stat;
|
|
1594
|
+
try {
|
|
1595
|
+
stat = fs2.statSync(filePath);
|
|
1596
|
+
if (!stat.isFile()) {
|
|
1597
|
+
throw new Error("Asset path is not a file");
|
|
1598
|
+
}
|
|
1599
|
+
} catch {
|
|
1600
|
+
res.statusCode = 404;
|
|
1601
|
+
res.end("Not found");
|
|
1602
|
+
return;
|
|
1456
1603
|
}
|
|
1457
|
-
res
|
|
1604
|
+
setAssetContentType(res, ext);
|
|
1605
|
+
const isDevelopmentAsset = nodeEnv !== "production";
|
|
1606
|
+
const isVersionedClientAsset = path3.basename(filePath) === "client.js" && url.searchParams.has("v");
|
|
1607
|
+
const canCacheLongTerm = !isDevelopmentAsset && (isHashedAssetPath(filePath) || isVersionedClientAsset);
|
|
1608
|
+
res.setHeader(
|
|
1609
|
+
"Cache-Control",
|
|
1610
|
+
canCacheLongTerm ? "public, max-age=31536000, immutable" : "no-store, no-cache, must-revalidate, proxy-revalidate"
|
|
1611
|
+
);
|
|
1612
|
+
res.setHeader("Vary", "Accept-Encoding");
|
|
1613
|
+
const contentEncoding = !isDevelopmentAsset ? getPreferredContentEncoding(req, ext, stat.size) : "";
|
|
1458
1614
|
const stream = fs2.createReadStream(filePath);
|
|
1459
1615
|
stream.on("error", () => {
|
|
1460
|
-
res.
|
|
1616
|
+
if (!res.headersSent) {
|
|
1617
|
+
res.statusCode = 404;
|
|
1618
|
+
}
|
|
1461
1619
|
res.end("Not found");
|
|
1462
1620
|
});
|
|
1463
|
-
|
|
1621
|
+
if (contentEncoding === "br") {
|
|
1622
|
+
res.setHeader("Content-Encoding", "br");
|
|
1623
|
+
stream.pipe(createBrotliCompress()).pipe(res);
|
|
1624
|
+
} else if (contentEncoding === "gzip") {
|
|
1625
|
+
res.setHeader("Content-Encoding", "gzip");
|
|
1626
|
+
stream.pipe(createGzip()).pipe(res);
|
|
1627
|
+
} else {
|
|
1628
|
+
stream.pipe(res);
|
|
1629
|
+
}
|
|
1464
1630
|
return;
|
|
1465
1631
|
}
|
|
1466
1632
|
const resolved = await withRequestBasename(
|
|
@@ -1478,22 +1644,23 @@ function createNodeRequestHandler(options) {
|
|
|
1478
1644
|
);
|
|
1479
1645
|
const initialPayload = {
|
|
1480
1646
|
model: resolved.model,
|
|
1481
|
-
contextModel: resolved.contextModel,
|
|
1482
|
-
pageModel: resolved.pageModel,
|
|
1483
1647
|
head: resolved.head
|
|
1484
1648
|
};
|
|
1485
1649
|
const initialFlightData = await renderRSCToString(initialPayload, {
|
|
1486
1650
|
moduleMap
|
|
1487
1651
|
});
|
|
1488
1652
|
const transportBasePath = normalizeRuntimeBasePath(resolved.head.transportBasePath) ?? normalizeRuntimeBasePath(basePath) ?? "";
|
|
1489
|
-
const shellClientScriptUrl =
|
|
1490
|
-
|
|
1491
|
-
|
|
1653
|
+
const shellClientScriptUrl = appendAssetVersion(
|
|
1654
|
+
joinRuntimeAssetPath(
|
|
1655
|
+
transportBasePath,
|
|
1656
|
+
clientScriptUrl
|
|
1657
|
+
),
|
|
1658
|
+
createClientAssetVersion(distRootDir)
|
|
1492
1659
|
);
|
|
1493
1660
|
const shellRscEndpoint = joinRuntimeBasePath(transportBasePath, "/rsc");
|
|
1494
1661
|
const shellBasename = normalizeRuntimeBasePath(resolved.head.basename) ?? normalizeRuntimeBasePath(basePath) ?? "";
|
|
1495
1662
|
const shellRouteBasePath = normalizeRuntimeBasePath(resolved.head.routeBasePath) ?? "";
|
|
1496
|
-
let rootHtml
|
|
1663
|
+
let rootHtml;
|
|
1497
1664
|
try {
|
|
1498
1665
|
rootHtml = await initialHtmlWorker.renderFromFlightData({
|
|
1499
1666
|
flightData: initialFlightData,
|
|
@@ -1517,29 +1684,23 @@ function createNodeRequestHandler(options) {
|
|
|
1517
1684
|
});
|
|
1518
1685
|
} catch (flightRenderError) {
|
|
1519
1686
|
console.error("[webframez-react] Flight-to-HTML render failed", flightRenderError);
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
initialFlightData,
|
|
1530
|
-
basename: shellBasename,
|
|
1531
|
-
routeBasePath: shellRouteBasePath,
|
|
1532
|
-
liveReloadPath: liveReloadPath || void 0,
|
|
1533
|
-
liveReloadServerId: liveReloadPath ? devServerId : void 0
|
|
1534
|
-
})
|
|
1687
|
+
sendTextResponse(
|
|
1688
|
+
req,
|
|
1689
|
+
res,
|
|
1690
|
+
createInitialHtmlErrorMarkup("Failed to render initial React HTML."),
|
|
1691
|
+
{
|
|
1692
|
+
statusCode: 500,
|
|
1693
|
+
contentType: "text/html; charset=utf-8",
|
|
1694
|
+
compress: nodeEnv === "production"
|
|
1695
|
+
}
|
|
1535
1696
|
);
|
|
1536
1697
|
return;
|
|
1537
1698
|
}
|
|
1538
1699
|
}
|
|
1539
1700
|
}
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1701
|
+
sendTextResponse(
|
|
1702
|
+
req,
|
|
1703
|
+
res,
|
|
1543
1704
|
createHTMLShell({
|
|
1544
1705
|
title: resolved.head.title || "Webframez React",
|
|
1545
1706
|
headTags: renderHeadToString(resolved.head),
|
|
@@ -1551,7 +1712,13 @@ function createNodeRequestHandler(options) {
|
|
|
1551
1712
|
routeBasePath: shellRouteBasePath,
|
|
1552
1713
|
liveReloadPath: liveReloadPath || void 0,
|
|
1553
1714
|
liveReloadServerId: liveReloadPath ? devServerId : void 0
|
|
1554
|
-
})
|
|
1715
|
+
}),
|
|
1716
|
+
{
|
|
1717
|
+
statusCode: resolved.statusCode,
|
|
1718
|
+
contentType: "text/html; charset=utf-8",
|
|
1719
|
+
cacheControl: "no-store, no-cache, must-revalidate, proxy-revalidate",
|
|
1720
|
+
compress: nodeEnv === "production"
|
|
1721
|
+
}
|
|
1555
1722
|
);
|
|
1556
1723
|
};
|
|
1557
1724
|
}
|