@webtypen/webframez-react 0.0.1 → 0.0.3
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/README.md +276 -52
- package/bin/webframez-react.mjs +309 -0
- package/defaults/tsconfig.server.example.json +46 -0
- package/defaults/tsconfig.server.json +64 -0
- package/defaults/webpack.client.cjs +64 -0
- package/defaults/webpack.server.cjs +40 -0
- package/dist/client.cjs +1 -1
- package/dist/client.js +1 -1
- package/dist/http.cjs +50 -11
- package/dist/http.d.ts +18 -7
- package/dist/http.js +50 -11
- package/dist/index.cjs +50 -11
- package/dist/index.d.ts +2 -5
- package/dist/index.js +50 -11
- package/dist/router.cjs +30 -10
- package/dist/router.js +30 -10
- package/dist/types.d.ts +25 -8
- package/dist/webframez-core.cjs +50 -11
- package/dist/webframez-core.d.ts +32 -8
- package/dist/webframez-core.js +50 -11
- package/package.json +13 -3
package/dist/http.cjs
CHANGED
|
@@ -223,6 +223,10 @@ function readSearchParams(urlSearchParams) {
|
|
|
223
223
|
function escapeHtml(value) {
|
|
224
224
|
return value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/\"/g, """).replace(/'/g, "'");
|
|
225
225
|
}
|
|
226
|
+
var MANAGED_HEAD_ATTR = "data-webframez-head";
|
|
227
|
+
function createManagedAttributes() {
|
|
228
|
+
return `${MANAGED_HEAD_ATTR}="true"`;
|
|
229
|
+
}
|
|
226
230
|
function toRouteEntry(pagesDir, filePath) {
|
|
227
231
|
const normalized = filePath.replace(/\\/g, "/");
|
|
228
232
|
if (!normalized.endsWith("/index.js")) {
|
|
@@ -291,19 +295,21 @@ function renderHeadToString(head) {
|
|
|
291
295
|
const tags = [];
|
|
292
296
|
if (head.description) {
|
|
293
297
|
tags.push(
|
|
294
|
-
`<meta name="description" content="${escapeHtml(head.description)}" />`
|
|
298
|
+
`<meta ${createManagedAttributes()} name="description" content="${escapeHtml(head.description)}" />`
|
|
295
299
|
);
|
|
296
300
|
}
|
|
297
301
|
if (head.favicon) {
|
|
298
|
-
tags.push(
|
|
302
|
+
tags.push(
|
|
303
|
+
`<link ${createManagedAttributes()} rel="icon" href="${escapeHtml(head.favicon)}" />`
|
|
304
|
+
);
|
|
299
305
|
}
|
|
300
306
|
for (const meta of head.meta ?? []) {
|
|
301
307
|
const attrs = Object.entries(meta).filter(([, value]) => Boolean(value)).map(([key, value]) => `${key}="${escapeHtml(String(value))}"`).join(" ");
|
|
302
|
-
tags.push(`<meta ${attrs} />`);
|
|
308
|
+
tags.push(`<meta ${createManagedAttributes()} ${attrs} />`);
|
|
303
309
|
}
|
|
304
310
|
for (const link of head.links ?? []) {
|
|
305
311
|
const attrs = Object.entries(link).filter(([, value]) => Boolean(value)).map(([key, value]) => `${key}="${escapeHtml(String(value))}"`).join(" ");
|
|
306
|
-
tags.push(`<link ${attrs} />`);
|
|
312
|
+
tags.push(`<link ${createManagedAttributes()} ${attrs} />`);
|
|
307
313
|
}
|
|
308
314
|
return tags.join("\n");
|
|
309
315
|
}
|
|
@@ -317,6 +323,12 @@ async function resolveHead(candidate, context) {
|
|
|
317
323
|
}
|
|
318
324
|
return candidate.Head(context);
|
|
319
325
|
}
|
|
326
|
+
async function resolvePageData(candidate, context) {
|
|
327
|
+
if (!candidate.Data) {
|
|
328
|
+
return void 0;
|
|
329
|
+
}
|
|
330
|
+
return candidate.Data(context);
|
|
331
|
+
}
|
|
320
332
|
function findBestMatch(entries, pathname) {
|
|
321
333
|
const matches = [];
|
|
322
334
|
for (const entry of entries) {
|
|
@@ -396,10 +408,11 @@ function createFileRouter(options) {
|
|
|
396
408
|
};
|
|
397
409
|
}
|
|
398
410
|
const errorModule = resolveModule(errorPath);
|
|
399
|
-
const errorNode = errorModule.default(errorProps);
|
|
411
|
+
const errorNode = await errorModule.default(errorProps);
|
|
400
412
|
const layoutHead = layoutModule ? await resolveHead(layoutModule, context) : void 0;
|
|
401
413
|
const errorHead = await resolveHead(errorModule, errorProps);
|
|
402
|
-
const
|
|
414
|
+
const layoutNode = layoutModule ? await layoutModule.default(context) : null;
|
|
415
|
+
const model = layoutNode ? injectRouteChildren(layoutNode, errorNode) : errorNode;
|
|
403
416
|
return {
|
|
404
417
|
statusCode,
|
|
405
418
|
model,
|
|
@@ -431,10 +444,17 @@ function createFileRouter(options) {
|
|
|
431
444
|
activeContext = context;
|
|
432
445
|
const pageModule = resolveModule(match.entry.filePath);
|
|
433
446
|
const layoutModule = import_node_fs.default.existsSync(layoutPath) ? resolveModule(layoutPath) : null;
|
|
434
|
-
const
|
|
435
|
-
const
|
|
436
|
-
|
|
437
|
-
|
|
447
|
+
const pageData = await resolvePageData(pageModule, context);
|
|
448
|
+
const pageContext = {
|
|
449
|
+
...context,
|
|
450
|
+
data: pageData
|
|
451
|
+
};
|
|
452
|
+
activeContext = pageContext;
|
|
453
|
+
const pageNode = await pageModule.default(pageContext);
|
|
454
|
+
const layoutHead = layoutModule ? await resolveHead(layoutModule, pageContext) : void 0;
|
|
455
|
+
const pageHead = await resolveHead(pageModule, pageContext);
|
|
456
|
+
const layoutNode = layoutModule ? await layoutModule.default(pageContext) : null;
|
|
457
|
+
const model = layoutNode ? injectRouteChildren(layoutNode, pageNode) : pageNode;
|
|
438
458
|
return {
|
|
439
459
|
statusCode: 200,
|
|
440
460
|
model,
|
|
@@ -493,8 +513,23 @@ async function renderInitialHtmlInWorker(options) {
|
|
|
493
513
|
const payload = Buffer.from(JSON.stringify(options), "utf8").toString("base64url");
|
|
494
514
|
const script = `
|
|
495
515
|
const path = require("node:path");
|
|
516
|
+
const Module = require("node:module");
|
|
496
517
|
const input = JSON.parse(Buffer.from(process.argv[1], "base64url").toString("utf8"));
|
|
497
518
|
globalThis.__RSC_BASENAME = input.basename || "";
|
|
519
|
+
const appRequire = Module.createRequire(path.join(input.pagesDir, "__webframez_react_worker__.js"));
|
|
520
|
+
const originalResolveFilename = Module._resolveFilename;
|
|
521
|
+
const forcedResolutions = new Map([
|
|
522
|
+
["react", appRequire.resolve("react")],
|
|
523
|
+
["react/jsx-runtime", appRequire.resolve("react/jsx-runtime")],
|
|
524
|
+
["react/jsx-dev-runtime", appRequire.resolve("react/jsx-dev-runtime")],
|
|
525
|
+
["react-dom/client", appRequire.resolve("react-dom/client")]
|
|
526
|
+
]);
|
|
527
|
+
Module._resolveFilename = function(request, parent, isMain, options) {
|
|
528
|
+
if (forcedResolutions.has(request)) {
|
|
529
|
+
return forcedResolutions.get(request);
|
|
530
|
+
}
|
|
531
|
+
return originalResolveFilename.call(this, request, parent, isMain, options);
|
|
532
|
+
};
|
|
498
533
|
const { createFileRouter } = require("webframez-react/router");
|
|
499
534
|
const reactDomPkg = require.resolve("react-dom/package.json", {
|
|
500
535
|
paths: [process.cwd(), input.pagesDir]
|
|
@@ -634,7 +669,11 @@ function createNodeRequestHandler(options) {
|
|
|
634
669
|
cookies: requestCookies
|
|
635
670
|
})
|
|
636
671
|
);
|
|
637
|
-
|
|
672
|
+
const payload = {
|
|
673
|
+
model: resolved2.model,
|
|
674
|
+
head: resolved2.head
|
|
675
|
+
};
|
|
676
|
+
sendRSC(res, payload, {
|
|
638
677
|
moduleMap,
|
|
639
678
|
statusCode: resolved2.statusCode
|
|
640
679
|
});
|
package/dist/http.d.ts
CHANGED
|
@@ -1,15 +1,26 @@
|
|
|
1
1
|
import type { IncomingMessage, ServerResponse } from "node:http";
|
|
2
2
|
|
|
3
|
-
export type
|
|
3
|
+
export type WebframezReactRoutePath = `/${string}` | "/";
|
|
4
|
+
export type WebframezReactAssetsPrefix = `${WebframezReactRoutePath}/` | "/";
|
|
5
|
+
|
|
6
|
+
export interface CreateNodeHandlerPathsOptions {
|
|
4
7
|
distRootDir: string;
|
|
5
8
|
pagesDir?: string;
|
|
6
9
|
manifestPath?: string;
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface CreateNodeHandlerRoutingOptions {
|
|
13
|
+
assetsPrefix?: WebframezReactAssetsPrefix;
|
|
14
|
+
rscPath?: WebframezReactRoutePath;
|
|
15
|
+
clientScriptUrl?: WebframezReactRoutePath;
|
|
16
|
+
basePath?: WebframezReactRoutePath;
|
|
17
|
+
liveReloadPath?: WebframezReactRoutePath | false;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface CreateNodeHandlerOptions
|
|
21
|
+
extends CreateNodeHandlerPathsOptions,
|
|
22
|
+
CreateNodeHandlerRoutingOptions {
|
|
23
|
+
}
|
|
13
24
|
|
|
14
25
|
export function createNodeRequestHandler(
|
|
15
26
|
options: CreateNodeHandlerOptions
|
package/dist/http.js
CHANGED
|
@@ -198,6 +198,10 @@ function readSearchParams(urlSearchParams) {
|
|
|
198
198
|
function escapeHtml(value) {
|
|
199
199
|
return value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/\"/g, """).replace(/'/g, "'");
|
|
200
200
|
}
|
|
201
|
+
var MANAGED_HEAD_ATTR = "data-webframez-head";
|
|
202
|
+
function createManagedAttributes() {
|
|
203
|
+
return `${MANAGED_HEAD_ATTR}="true"`;
|
|
204
|
+
}
|
|
201
205
|
function toRouteEntry(pagesDir, filePath) {
|
|
202
206
|
const normalized = filePath.replace(/\\/g, "/");
|
|
203
207
|
if (!normalized.endsWith("/index.js")) {
|
|
@@ -266,19 +270,21 @@ function renderHeadToString(head) {
|
|
|
266
270
|
const tags = [];
|
|
267
271
|
if (head.description) {
|
|
268
272
|
tags.push(
|
|
269
|
-
`<meta name="description" content="${escapeHtml(head.description)}" />`
|
|
273
|
+
`<meta ${createManagedAttributes()} name="description" content="${escapeHtml(head.description)}" />`
|
|
270
274
|
);
|
|
271
275
|
}
|
|
272
276
|
if (head.favicon) {
|
|
273
|
-
tags.push(
|
|
277
|
+
tags.push(
|
|
278
|
+
`<link ${createManagedAttributes()} rel="icon" href="${escapeHtml(head.favicon)}" />`
|
|
279
|
+
);
|
|
274
280
|
}
|
|
275
281
|
for (const meta of head.meta ?? []) {
|
|
276
282
|
const attrs = Object.entries(meta).filter(([, value]) => Boolean(value)).map(([key, value]) => `${key}="${escapeHtml(String(value))}"`).join(" ");
|
|
277
|
-
tags.push(`<meta ${attrs} />`);
|
|
283
|
+
tags.push(`<meta ${createManagedAttributes()} ${attrs} />`);
|
|
278
284
|
}
|
|
279
285
|
for (const link of head.links ?? []) {
|
|
280
286
|
const attrs = Object.entries(link).filter(([, value]) => Boolean(value)).map(([key, value]) => `${key}="${escapeHtml(String(value))}"`).join(" ");
|
|
281
|
-
tags.push(`<link ${attrs} />`);
|
|
287
|
+
tags.push(`<link ${createManagedAttributes()} ${attrs} />`);
|
|
282
288
|
}
|
|
283
289
|
return tags.join("\n");
|
|
284
290
|
}
|
|
@@ -292,6 +298,12 @@ async function resolveHead(candidate, context) {
|
|
|
292
298
|
}
|
|
293
299
|
return candidate.Head(context);
|
|
294
300
|
}
|
|
301
|
+
async function resolvePageData(candidate, context) {
|
|
302
|
+
if (!candidate.Data) {
|
|
303
|
+
return void 0;
|
|
304
|
+
}
|
|
305
|
+
return candidate.Data(context);
|
|
306
|
+
}
|
|
295
307
|
function findBestMatch(entries, pathname) {
|
|
296
308
|
const matches = [];
|
|
297
309
|
for (const entry of entries) {
|
|
@@ -371,10 +383,11 @@ function createFileRouter(options) {
|
|
|
371
383
|
};
|
|
372
384
|
}
|
|
373
385
|
const errorModule = resolveModule(errorPath);
|
|
374
|
-
const errorNode = errorModule.default(errorProps);
|
|
386
|
+
const errorNode = await errorModule.default(errorProps);
|
|
375
387
|
const layoutHead = layoutModule ? await resolveHead(layoutModule, context) : void 0;
|
|
376
388
|
const errorHead = await resolveHead(errorModule, errorProps);
|
|
377
|
-
const
|
|
389
|
+
const layoutNode = layoutModule ? await layoutModule.default(context) : null;
|
|
390
|
+
const model = layoutNode ? injectRouteChildren(layoutNode, errorNode) : errorNode;
|
|
378
391
|
return {
|
|
379
392
|
statusCode,
|
|
380
393
|
model,
|
|
@@ -406,10 +419,17 @@ function createFileRouter(options) {
|
|
|
406
419
|
activeContext = context;
|
|
407
420
|
const pageModule = resolveModule(match.entry.filePath);
|
|
408
421
|
const layoutModule = fs.existsSync(layoutPath) ? resolveModule(layoutPath) : null;
|
|
409
|
-
const
|
|
410
|
-
const
|
|
411
|
-
|
|
412
|
-
|
|
422
|
+
const pageData = await resolvePageData(pageModule, context);
|
|
423
|
+
const pageContext = {
|
|
424
|
+
...context,
|
|
425
|
+
data: pageData
|
|
426
|
+
};
|
|
427
|
+
activeContext = pageContext;
|
|
428
|
+
const pageNode = await pageModule.default(pageContext);
|
|
429
|
+
const layoutHead = layoutModule ? await resolveHead(layoutModule, pageContext) : void 0;
|
|
430
|
+
const pageHead = await resolveHead(pageModule, pageContext);
|
|
431
|
+
const layoutNode = layoutModule ? await layoutModule.default(pageContext) : null;
|
|
432
|
+
const model = layoutNode ? injectRouteChildren(layoutNode, pageNode) : pageNode;
|
|
413
433
|
return {
|
|
414
434
|
statusCode: 200,
|
|
415
435
|
model,
|
|
@@ -468,8 +488,23 @@ async function renderInitialHtmlInWorker(options) {
|
|
|
468
488
|
const payload = Buffer.from(JSON.stringify(options), "utf8").toString("base64url");
|
|
469
489
|
const script = `
|
|
470
490
|
const path = require("node:path");
|
|
491
|
+
const Module = require("node:module");
|
|
471
492
|
const input = JSON.parse(Buffer.from(process.argv[1], "base64url").toString("utf8"));
|
|
472
493
|
globalThis.__RSC_BASENAME = input.basename || "";
|
|
494
|
+
const appRequire = Module.createRequire(path.join(input.pagesDir, "__webframez_react_worker__.js"));
|
|
495
|
+
const originalResolveFilename = Module._resolveFilename;
|
|
496
|
+
const forcedResolutions = new Map([
|
|
497
|
+
["react", appRequire.resolve("react")],
|
|
498
|
+
["react/jsx-runtime", appRequire.resolve("react/jsx-runtime")],
|
|
499
|
+
["react/jsx-dev-runtime", appRequire.resolve("react/jsx-dev-runtime")],
|
|
500
|
+
["react-dom/client", appRequire.resolve("react-dom/client")]
|
|
501
|
+
]);
|
|
502
|
+
Module._resolveFilename = function(request, parent, isMain, options) {
|
|
503
|
+
if (forcedResolutions.has(request)) {
|
|
504
|
+
return forcedResolutions.get(request);
|
|
505
|
+
}
|
|
506
|
+
return originalResolveFilename.call(this, request, parent, isMain, options);
|
|
507
|
+
};
|
|
473
508
|
const { createFileRouter } = require("webframez-react/router");
|
|
474
509
|
const reactDomPkg = require.resolve("react-dom/package.json", {
|
|
475
510
|
paths: [process.cwd(), input.pagesDir]
|
|
@@ -609,7 +644,11 @@ function createNodeRequestHandler(options) {
|
|
|
609
644
|
cookies: requestCookies
|
|
610
645
|
})
|
|
611
646
|
);
|
|
612
|
-
|
|
647
|
+
const payload = {
|
|
648
|
+
model: resolved2.model,
|
|
649
|
+
head: resolved2.head
|
|
650
|
+
};
|
|
651
|
+
sendRSC(res, payload, {
|
|
613
652
|
moduleMap,
|
|
614
653
|
statusCode: resolved2.statusCode
|
|
615
654
|
});
|
package/dist/index.cjs
CHANGED
|
@@ -257,6 +257,10 @@ function readSearchParams(urlSearchParams) {
|
|
|
257
257
|
function escapeHtml(value) {
|
|
258
258
|
return value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/\"/g, """).replace(/'/g, "'");
|
|
259
259
|
}
|
|
260
|
+
var MANAGED_HEAD_ATTR = "data-webframez-head";
|
|
261
|
+
function createManagedAttributes() {
|
|
262
|
+
return `${MANAGED_HEAD_ATTR}="true"`;
|
|
263
|
+
}
|
|
260
264
|
function toRouteEntry(pagesDir, filePath) {
|
|
261
265
|
const normalized = filePath.replace(/\\/g, "/");
|
|
262
266
|
if (!normalized.endsWith("/index.js")) {
|
|
@@ -325,19 +329,21 @@ function renderHeadToString(head) {
|
|
|
325
329
|
const tags = [];
|
|
326
330
|
if (head.description) {
|
|
327
331
|
tags.push(
|
|
328
|
-
`<meta name="description" content="${escapeHtml(head.description)}" />`
|
|
332
|
+
`<meta ${createManagedAttributes()} name="description" content="${escapeHtml(head.description)}" />`
|
|
329
333
|
);
|
|
330
334
|
}
|
|
331
335
|
if (head.favicon) {
|
|
332
|
-
tags.push(
|
|
336
|
+
tags.push(
|
|
337
|
+
`<link ${createManagedAttributes()} rel="icon" href="${escapeHtml(head.favicon)}" />`
|
|
338
|
+
);
|
|
333
339
|
}
|
|
334
340
|
for (const meta of head.meta ?? []) {
|
|
335
341
|
const attrs = Object.entries(meta).filter(([, value]) => Boolean(value)).map(([key, value]) => `${key}="${escapeHtml(String(value))}"`).join(" ");
|
|
336
|
-
tags.push(`<meta ${attrs} />`);
|
|
342
|
+
tags.push(`<meta ${createManagedAttributes()} ${attrs} />`);
|
|
337
343
|
}
|
|
338
344
|
for (const link of head.links ?? []) {
|
|
339
345
|
const attrs = Object.entries(link).filter(([, value]) => Boolean(value)).map(([key, value]) => `${key}="${escapeHtml(String(value))}"`).join(" ");
|
|
340
|
-
tags.push(`<link ${attrs} />`);
|
|
346
|
+
tags.push(`<link ${createManagedAttributes()} ${attrs} />`);
|
|
341
347
|
}
|
|
342
348
|
return tags.join("\n");
|
|
343
349
|
}
|
|
@@ -351,6 +357,12 @@ async function resolveHead(candidate, context) {
|
|
|
351
357
|
}
|
|
352
358
|
return candidate.Head(context);
|
|
353
359
|
}
|
|
360
|
+
async function resolvePageData(candidate, context) {
|
|
361
|
+
if (!candidate.Data) {
|
|
362
|
+
return void 0;
|
|
363
|
+
}
|
|
364
|
+
return candidate.Data(context);
|
|
365
|
+
}
|
|
354
366
|
function findBestMatch(entries, pathname) {
|
|
355
367
|
const matches = [];
|
|
356
368
|
for (const entry of entries) {
|
|
@@ -430,10 +442,11 @@ function createFileRouter(options) {
|
|
|
430
442
|
};
|
|
431
443
|
}
|
|
432
444
|
const errorModule = resolveModule(errorPath);
|
|
433
|
-
const errorNode = errorModule.default(errorProps);
|
|
445
|
+
const errorNode = await errorModule.default(errorProps);
|
|
434
446
|
const layoutHead = layoutModule ? await resolveHead(layoutModule, context) : void 0;
|
|
435
447
|
const errorHead = await resolveHead(errorModule, errorProps);
|
|
436
|
-
const
|
|
448
|
+
const layoutNode = layoutModule ? await layoutModule.default(context) : null;
|
|
449
|
+
const model = layoutNode ? injectRouteChildren(layoutNode, errorNode) : errorNode;
|
|
437
450
|
return {
|
|
438
451
|
statusCode,
|
|
439
452
|
model,
|
|
@@ -465,10 +478,17 @@ function createFileRouter(options) {
|
|
|
465
478
|
activeContext = context;
|
|
466
479
|
const pageModule = resolveModule(match.entry.filePath);
|
|
467
480
|
const layoutModule = import_node_fs.default.existsSync(layoutPath) ? resolveModule(layoutPath) : null;
|
|
468
|
-
const
|
|
469
|
-
const
|
|
470
|
-
|
|
471
|
-
|
|
481
|
+
const pageData = await resolvePageData(pageModule, context);
|
|
482
|
+
const pageContext = {
|
|
483
|
+
...context,
|
|
484
|
+
data: pageData
|
|
485
|
+
};
|
|
486
|
+
activeContext = pageContext;
|
|
487
|
+
const pageNode = await pageModule.default(pageContext);
|
|
488
|
+
const layoutHead = layoutModule ? await resolveHead(layoutModule, pageContext) : void 0;
|
|
489
|
+
const pageHead = await resolveHead(pageModule, pageContext);
|
|
490
|
+
const layoutNode = layoutModule ? await layoutModule.default(pageContext) : null;
|
|
491
|
+
const model = layoutNode ? injectRouteChildren(layoutNode, pageNode) : pageNode;
|
|
472
492
|
return {
|
|
473
493
|
statusCode: 200,
|
|
474
494
|
model,
|
|
@@ -530,8 +550,23 @@ async function renderInitialHtmlInWorker(options) {
|
|
|
530
550
|
const payload = Buffer.from(JSON.stringify(options), "utf8").toString("base64url");
|
|
531
551
|
const script = `
|
|
532
552
|
const path = require("node:path");
|
|
553
|
+
const Module = require("node:module");
|
|
533
554
|
const input = JSON.parse(Buffer.from(process.argv[1], "base64url").toString("utf8"));
|
|
534
555
|
globalThis.__RSC_BASENAME = input.basename || "";
|
|
556
|
+
const appRequire = Module.createRequire(path.join(input.pagesDir, "__webframez_react_worker__.js"));
|
|
557
|
+
const originalResolveFilename = Module._resolveFilename;
|
|
558
|
+
const forcedResolutions = new Map([
|
|
559
|
+
["react", appRequire.resolve("react")],
|
|
560
|
+
["react/jsx-runtime", appRequire.resolve("react/jsx-runtime")],
|
|
561
|
+
["react/jsx-dev-runtime", appRequire.resolve("react/jsx-dev-runtime")],
|
|
562
|
+
["react-dom/client", appRequire.resolve("react-dom/client")]
|
|
563
|
+
]);
|
|
564
|
+
Module._resolveFilename = function(request, parent, isMain, options) {
|
|
565
|
+
if (forcedResolutions.has(request)) {
|
|
566
|
+
return forcedResolutions.get(request);
|
|
567
|
+
}
|
|
568
|
+
return originalResolveFilename.call(this, request, parent, isMain, options);
|
|
569
|
+
};
|
|
535
570
|
const { createFileRouter } = require("webframez-react/router");
|
|
536
571
|
const reactDomPkg = require.resolve("react-dom/package.json", {
|
|
537
572
|
paths: [process.cwd(), input.pagesDir]
|
|
@@ -671,7 +706,11 @@ function createNodeRequestHandler(options) {
|
|
|
671
706
|
cookies: requestCookies
|
|
672
707
|
})
|
|
673
708
|
);
|
|
674
|
-
|
|
709
|
+
const payload = {
|
|
710
|
+
model: resolved2.model,
|
|
711
|
+
head: resolved2.head
|
|
712
|
+
};
|
|
713
|
+
sendRSC(res, payload, {
|
|
675
714
|
moduleMap,
|
|
676
715
|
statusCode: resolved2.statusCode
|
|
677
716
|
});
|
package/dist/index.d.ts
CHANGED
|
@@ -1,15 +1,12 @@
|
|
|
1
1
|
import type { IncomingMessage, ServerResponse } from "node:http";
|
|
2
2
|
import type { CreateHtmlShellOptions, SendRSCOptions } from "./types";
|
|
3
3
|
import type { CreateNodeHandlerOptions } from "./http";
|
|
4
|
+
import "./webframez-core";
|
|
4
5
|
|
|
5
6
|
export * from "./types";
|
|
6
7
|
export { createFileRouter, parseSearchParams, renderHeadToString, RouteChildren } from "./router";
|
|
7
8
|
export type { ResolvedRoute } from "./router";
|
|
8
|
-
export
|
|
9
|
-
export type {
|
|
10
|
-
WebframezCoreRouteMethod,
|
|
11
|
-
WebframezCoreReactRenderRouteOptions,
|
|
12
|
-
} from "./webframez-core";
|
|
9
|
+
export * from "./webframez-core";
|
|
13
10
|
|
|
14
11
|
export function createHTMLShell(options?: CreateHtmlShellOptions): string;
|
|
15
12
|
export function sendRSC(
|
package/dist/index.js
CHANGED
|
@@ -221,6 +221,10 @@ function readSearchParams(urlSearchParams) {
|
|
|
221
221
|
function escapeHtml(value) {
|
|
222
222
|
return value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/\"/g, """).replace(/'/g, "'");
|
|
223
223
|
}
|
|
224
|
+
var MANAGED_HEAD_ATTR = "data-webframez-head";
|
|
225
|
+
function createManagedAttributes() {
|
|
226
|
+
return `${MANAGED_HEAD_ATTR}="true"`;
|
|
227
|
+
}
|
|
224
228
|
function toRouteEntry(pagesDir, filePath) {
|
|
225
229
|
const normalized = filePath.replace(/\\/g, "/");
|
|
226
230
|
if (!normalized.endsWith("/index.js")) {
|
|
@@ -289,19 +293,21 @@ function renderHeadToString(head) {
|
|
|
289
293
|
const tags = [];
|
|
290
294
|
if (head.description) {
|
|
291
295
|
tags.push(
|
|
292
|
-
`<meta name="description" content="${escapeHtml(head.description)}" />`
|
|
296
|
+
`<meta ${createManagedAttributes()} name="description" content="${escapeHtml(head.description)}" />`
|
|
293
297
|
);
|
|
294
298
|
}
|
|
295
299
|
if (head.favicon) {
|
|
296
|
-
tags.push(
|
|
300
|
+
tags.push(
|
|
301
|
+
`<link ${createManagedAttributes()} rel="icon" href="${escapeHtml(head.favicon)}" />`
|
|
302
|
+
);
|
|
297
303
|
}
|
|
298
304
|
for (const meta of head.meta ?? []) {
|
|
299
305
|
const attrs = Object.entries(meta).filter(([, value]) => Boolean(value)).map(([key, value]) => `${key}="${escapeHtml(String(value))}"`).join(" ");
|
|
300
|
-
tags.push(`<meta ${attrs} />`);
|
|
306
|
+
tags.push(`<meta ${createManagedAttributes()} ${attrs} />`);
|
|
301
307
|
}
|
|
302
308
|
for (const link of head.links ?? []) {
|
|
303
309
|
const attrs = Object.entries(link).filter(([, value]) => Boolean(value)).map(([key, value]) => `${key}="${escapeHtml(String(value))}"`).join(" ");
|
|
304
|
-
tags.push(`<link ${attrs} />`);
|
|
310
|
+
tags.push(`<link ${createManagedAttributes()} ${attrs} />`);
|
|
305
311
|
}
|
|
306
312
|
return tags.join("\n");
|
|
307
313
|
}
|
|
@@ -315,6 +321,12 @@ async function resolveHead(candidate, context) {
|
|
|
315
321
|
}
|
|
316
322
|
return candidate.Head(context);
|
|
317
323
|
}
|
|
324
|
+
async function resolvePageData(candidate, context) {
|
|
325
|
+
if (!candidate.Data) {
|
|
326
|
+
return void 0;
|
|
327
|
+
}
|
|
328
|
+
return candidate.Data(context);
|
|
329
|
+
}
|
|
318
330
|
function findBestMatch(entries, pathname) {
|
|
319
331
|
const matches = [];
|
|
320
332
|
for (const entry of entries) {
|
|
@@ -394,10 +406,11 @@ function createFileRouter(options) {
|
|
|
394
406
|
};
|
|
395
407
|
}
|
|
396
408
|
const errorModule = resolveModule(errorPath);
|
|
397
|
-
const errorNode = errorModule.default(errorProps);
|
|
409
|
+
const errorNode = await errorModule.default(errorProps);
|
|
398
410
|
const layoutHead = layoutModule ? await resolveHead(layoutModule, context) : void 0;
|
|
399
411
|
const errorHead = await resolveHead(errorModule, errorProps);
|
|
400
|
-
const
|
|
412
|
+
const layoutNode = layoutModule ? await layoutModule.default(context) : null;
|
|
413
|
+
const model = layoutNode ? injectRouteChildren(layoutNode, errorNode) : errorNode;
|
|
401
414
|
return {
|
|
402
415
|
statusCode,
|
|
403
416
|
model,
|
|
@@ -429,10 +442,17 @@ function createFileRouter(options) {
|
|
|
429
442
|
activeContext = context;
|
|
430
443
|
const pageModule = resolveModule(match.entry.filePath);
|
|
431
444
|
const layoutModule = fs.existsSync(layoutPath) ? resolveModule(layoutPath) : null;
|
|
432
|
-
const
|
|
433
|
-
const
|
|
434
|
-
|
|
435
|
-
|
|
445
|
+
const pageData = await resolvePageData(pageModule, context);
|
|
446
|
+
const pageContext = {
|
|
447
|
+
...context,
|
|
448
|
+
data: pageData
|
|
449
|
+
};
|
|
450
|
+
activeContext = pageContext;
|
|
451
|
+
const pageNode = await pageModule.default(pageContext);
|
|
452
|
+
const layoutHead = layoutModule ? await resolveHead(layoutModule, pageContext) : void 0;
|
|
453
|
+
const pageHead = await resolveHead(pageModule, pageContext);
|
|
454
|
+
const layoutNode = layoutModule ? await layoutModule.default(pageContext) : null;
|
|
455
|
+
const model = layoutNode ? injectRouteChildren(layoutNode, pageNode) : pageNode;
|
|
436
456
|
return {
|
|
437
457
|
statusCode: 200,
|
|
438
458
|
model,
|
|
@@ -494,8 +514,23 @@ async function renderInitialHtmlInWorker(options) {
|
|
|
494
514
|
const payload = Buffer.from(JSON.stringify(options), "utf8").toString("base64url");
|
|
495
515
|
const script = `
|
|
496
516
|
const path = require("node:path");
|
|
517
|
+
const Module = require("node:module");
|
|
497
518
|
const input = JSON.parse(Buffer.from(process.argv[1], "base64url").toString("utf8"));
|
|
498
519
|
globalThis.__RSC_BASENAME = input.basename || "";
|
|
520
|
+
const appRequire = Module.createRequire(path.join(input.pagesDir, "__webframez_react_worker__.js"));
|
|
521
|
+
const originalResolveFilename = Module._resolveFilename;
|
|
522
|
+
const forcedResolutions = new Map([
|
|
523
|
+
["react", appRequire.resolve("react")],
|
|
524
|
+
["react/jsx-runtime", appRequire.resolve("react/jsx-runtime")],
|
|
525
|
+
["react/jsx-dev-runtime", appRequire.resolve("react/jsx-dev-runtime")],
|
|
526
|
+
["react-dom/client", appRequire.resolve("react-dom/client")]
|
|
527
|
+
]);
|
|
528
|
+
Module._resolveFilename = function(request, parent, isMain, options) {
|
|
529
|
+
if (forcedResolutions.has(request)) {
|
|
530
|
+
return forcedResolutions.get(request);
|
|
531
|
+
}
|
|
532
|
+
return originalResolveFilename.call(this, request, parent, isMain, options);
|
|
533
|
+
};
|
|
499
534
|
const { createFileRouter } = require("webframez-react/router");
|
|
500
535
|
const reactDomPkg = require.resolve("react-dom/package.json", {
|
|
501
536
|
paths: [process.cwd(), input.pagesDir]
|
|
@@ -635,7 +670,11 @@ function createNodeRequestHandler(options) {
|
|
|
635
670
|
cookies: requestCookies
|
|
636
671
|
})
|
|
637
672
|
);
|
|
638
|
-
|
|
673
|
+
const payload = {
|
|
674
|
+
model: resolved2.model,
|
|
675
|
+
head: resolved2.head
|
|
676
|
+
};
|
|
677
|
+
sendRSC(res, payload, {
|
|
639
678
|
moduleMap,
|
|
640
679
|
statusCode: resolved2.statusCode
|
|
641
680
|
});
|
package/dist/router.cjs
CHANGED
|
@@ -144,6 +144,10 @@ function readSearchParams(urlSearchParams) {
|
|
|
144
144
|
function escapeHtml(value) {
|
|
145
145
|
return value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/\"/g, """).replace(/'/g, "'");
|
|
146
146
|
}
|
|
147
|
+
var MANAGED_HEAD_ATTR = "data-webframez-head";
|
|
148
|
+
function createManagedAttributes() {
|
|
149
|
+
return `${MANAGED_HEAD_ATTR}="true"`;
|
|
150
|
+
}
|
|
147
151
|
function toRouteEntry(pagesDir, filePath) {
|
|
148
152
|
const normalized = filePath.replace(/\\/g, "/");
|
|
149
153
|
if (!normalized.endsWith("/index.js")) {
|
|
@@ -212,19 +216,21 @@ function renderHeadToString(head) {
|
|
|
212
216
|
const tags = [];
|
|
213
217
|
if (head.description) {
|
|
214
218
|
tags.push(
|
|
215
|
-
`<meta name="description" content="${escapeHtml(head.description)}" />`
|
|
219
|
+
`<meta ${createManagedAttributes()} name="description" content="${escapeHtml(head.description)}" />`
|
|
216
220
|
);
|
|
217
221
|
}
|
|
218
222
|
if (head.favicon) {
|
|
219
|
-
tags.push(
|
|
223
|
+
tags.push(
|
|
224
|
+
`<link ${createManagedAttributes()} rel="icon" href="${escapeHtml(head.favicon)}" />`
|
|
225
|
+
);
|
|
220
226
|
}
|
|
221
227
|
for (const meta of head.meta ?? []) {
|
|
222
228
|
const attrs = Object.entries(meta).filter(([, value]) => Boolean(value)).map(([key, value]) => `${key}="${escapeHtml(String(value))}"`).join(" ");
|
|
223
|
-
tags.push(`<meta ${attrs} />`);
|
|
229
|
+
tags.push(`<meta ${createManagedAttributes()} ${attrs} />`);
|
|
224
230
|
}
|
|
225
231
|
for (const link of head.links ?? []) {
|
|
226
232
|
const attrs = Object.entries(link).filter(([, value]) => Boolean(value)).map(([key, value]) => `${key}="${escapeHtml(String(value))}"`).join(" ");
|
|
227
|
-
tags.push(`<link ${attrs} />`);
|
|
233
|
+
tags.push(`<link ${createManagedAttributes()} ${attrs} />`);
|
|
228
234
|
}
|
|
229
235
|
return tags.join("\n");
|
|
230
236
|
}
|
|
@@ -238,6 +244,12 @@ async function resolveHead(candidate, context) {
|
|
|
238
244
|
}
|
|
239
245
|
return candidate.Head(context);
|
|
240
246
|
}
|
|
247
|
+
async function resolvePageData(candidate, context) {
|
|
248
|
+
if (!candidate.Data) {
|
|
249
|
+
return void 0;
|
|
250
|
+
}
|
|
251
|
+
return candidate.Data(context);
|
|
252
|
+
}
|
|
241
253
|
function findBestMatch(entries, pathname) {
|
|
242
254
|
const matches = [];
|
|
243
255
|
for (const entry of entries) {
|
|
@@ -317,10 +329,11 @@ function createFileRouter(options) {
|
|
|
317
329
|
};
|
|
318
330
|
}
|
|
319
331
|
const errorModule = resolveModule(errorPath);
|
|
320
|
-
const errorNode = errorModule.default(errorProps);
|
|
332
|
+
const errorNode = await errorModule.default(errorProps);
|
|
321
333
|
const layoutHead = layoutModule ? await resolveHead(layoutModule, context) : void 0;
|
|
322
334
|
const errorHead = await resolveHead(errorModule, errorProps);
|
|
323
|
-
const
|
|
335
|
+
const layoutNode = layoutModule ? await layoutModule.default(context) : null;
|
|
336
|
+
const model = layoutNode ? injectRouteChildren(layoutNode, errorNode) : errorNode;
|
|
324
337
|
return {
|
|
325
338
|
statusCode,
|
|
326
339
|
model,
|
|
@@ -352,10 +365,17 @@ function createFileRouter(options) {
|
|
|
352
365
|
activeContext = context;
|
|
353
366
|
const pageModule = resolveModule(match.entry.filePath);
|
|
354
367
|
const layoutModule = import_node_fs.default.existsSync(layoutPath) ? resolveModule(layoutPath) : null;
|
|
355
|
-
const
|
|
356
|
-
const
|
|
357
|
-
|
|
358
|
-
|
|
368
|
+
const pageData = await resolvePageData(pageModule, context);
|
|
369
|
+
const pageContext = {
|
|
370
|
+
...context,
|
|
371
|
+
data: pageData
|
|
372
|
+
};
|
|
373
|
+
activeContext = pageContext;
|
|
374
|
+
const pageNode = await pageModule.default(pageContext);
|
|
375
|
+
const layoutHead = layoutModule ? await resolveHead(layoutModule, pageContext) : void 0;
|
|
376
|
+
const pageHead = await resolveHead(pageModule, pageContext);
|
|
377
|
+
const layoutNode = layoutModule ? await layoutModule.default(pageContext) : null;
|
|
378
|
+
const model = layoutNode ? injectRouteChildren(layoutNode, pageNode) : pageNode;
|
|
359
379
|
return {
|
|
360
380
|
statusCode: 200,
|
|
361
381
|
model,
|