@blinkk/root 4.0.1 → 4.1.2
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/render/render.d.ts +13 -0
- package/dist/render.js +98 -63
- package/dist/render.js.map +3 -3
- package/package.json +1 -1
package/dist/render/render.d.ts
CHANGED
|
@@ -19,6 +19,19 @@ export declare class Renderer {
|
|
|
19
19
|
walkRoutes(cb: (urlPathFormat: string, route: Route) => void | Promise<void>): Promise<void>;
|
|
20
20
|
handle(req: Request, res: Response, next: NextFunction): Promise<void>;
|
|
21
21
|
private renderComponent;
|
|
22
|
+
/**
|
|
23
|
+
* Runs a synchronous render function with a hook installed that auto-injects
|
|
24
|
+
* the CSP nonce into `<script>`, `<style>` and `<link rel="stylesheet">`
|
|
25
|
+
* vnodes.
|
|
26
|
+
*
|
|
27
|
+
* NOTE: `preactOptions.vnode` is a global, so the hook must be installed and
|
|
28
|
+
* restored within a single synchronous block. Awaiting inside the swapped
|
|
29
|
+
* window allows concurrent renders to interleave, leaking a stale hook (and
|
|
30
|
+
* its nonce) into other requests.
|
|
31
|
+
*
|
|
32
|
+
* https://preactjs.com/guide/v10/options/
|
|
33
|
+
*/
|
|
34
|
+
private withNonceHooks;
|
|
22
35
|
/** SSG renders a route. */
|
|
23
36
|
renderRoute(route: Route, options: {
|
|
24
37
|
routeParams: Record<string, string>;
|
package/dist/render.js
CHANGED
|
@@ -17,7 +17,9 @@ import {
|
|
|
17
17
|
import {
|
|
18
18
|
renderJsxToString
|
|
19
19
|
} from "./chunk-5OPFFJU7.js";
|
|
20
|
-
import
|
|
20
|
+
import {
|
|
21
|
+
options
|
|
22
|
+
} from "./chunk-3Z5TNZEP.js";
|
|
21
23
|
import {
|
|
22
24
|
getSecurityConfig,
|
|
23
25
|
setSecurityHeaders
|
|
@@ -13071,9 +13073,9 @@ var Router = class {
|
|
|
13071
13073
|
return urlPaths;
|
|
13072
13074
|
}
|
|
13073
13075
|
};
|
|
13074
|
-
function normalizeUrlPath(urlPath,
|
|
13076
|
+
function normalizeUrlPath(urlPath, options2) {
|
|
13075
13077
|
urlPath = urlPath.replace(/\/+/g, "/");
|
|
13076
|
-
if (testPathHasFileExt(urlPath) ||
|
|
13078
|
+
if (testPathHasFileExt(urlPath) || options2?.trailingSlash === false && urlPath !== "/" && urlPath.endsWith("/")) {
|
|
13077
13079
|
urlPath = removeTrailingSlash(urlPath);
|
|
13078
13080
|
}
|
|
13079
13081
|
if (urlPath.endsWith("/index")) {
|
|
@@ -13082,7 +13084,7 @@ function normalizeUrlPath(urlPath, options) {
|
|
|
13082
13084
|
if (!urlPath.startsWith("/")) {
|
|
13083
13085
|
urlPath = `/${urlPath}`;
|
|
13084
13086
|
}
|
|
13085
|
-
if (
|
|
13087
|
+
if (options2?.trailingSlash && !testPathHasFileExt(urlPath) && !urlPath.endsWith("/")) {
|
|
13086
13088
|
urlPath = `${urlPath}/`;
|
|
13087
13089
|
}
|
|
13088
13090
|
return urlPath;
|
|
@@ -13142,10 +13144,10 @@ var Renderer = class {
|
|
|
13142
13144
|
assetMap;
|
|
13143
13145
|
elementGraph;
|
|
13144
13146
|
router;
|
|
13145
|
-
constructor(rootConfig,
|
|
13147
|
+
constructor(rootConfig, options2) {
|
|
13146
13148
|
this.rootConfig = rootConfig;
|
|
13147
|
-
this.assetMap =
|
|
13148
|
-
this.elementGraph =
|
|
13149
|
+
this.assetMap = options2.assetMap;
|
|
13150
|
+
this.elementGraph = options2.elementGraph;
|
|
13149
13151
|
this.router = new Router(rootConfig);
|
|
13150
13152
|
}
|
|
13151
13153
|
/** Returns a route from the router. */
|
|
@@ -13187,12 +13189,12 @@ var Renderer = class {
|
|
|
13187
13189
|
};
|
|
13188
13190
|
await nextRouteHandler();
|
|
13189
13191
|
}
|
|
13190
|
-
async renderComponent(Component, props,
|
|
13191
|
-
const { currentPath, route, routeParams, nonce } =
|
|
13192
|
-
const locale =
|
|
13192
|
+
async renderComponent(Component, props, options2) {
|
|
13193
|
+
const { currentPath, route, routeParams, nonce } = options2;
|
|
13194
|
+
const locale = options2.locale;
|
|
13193
13195
|
const translations = {
|
|
13194
13196
|
...getTranslations(locale),
|
|
13195
|
-
...
|
|
13197
|
+
...options2.translations || {}
|
|
13196
13198
|
};
|
|
13197
13199
|
const ctx = {
|
|
13198
13200
|
currentPath,
|
|
@@ -13211,29 +13213,10 @@ var Renderer = class {
|
|
|
13211
13213
|
scriptDeps: [],
|
|
13212
13214
|
styleDeps: []
|
|
13213
13215
|
};
|
|
13214
|
-
const
|
|
13215
|
-
const
|
|
13216
|
-
const
|
|
13217
|
-
|
|
13218
|
-
try {
|
|
13219
|
-
preactOptions.vnode = (vnode) => {
|
|
13220
|
-
if (vnode && vnode.type === "script") {
|
|
13221
|
-
vnode.props.nonce = nonce;
|
|
13222
|
-
}
|
|
13223
|
-
if (vnode && vnode.type === "style") {
|
|
13224
|
-
vnode.props.nonce = nonce;
|
|
13225
|
-
}
|
|
13226
|
-
if (vnode && vnode.type === "link" && vnode.props.rel === "stylesheet") {
|
|
13227
|
-
vnode.props.nonce = nonce;
|
|
13228
|
-
}
|
|
13229
|
-
if (preactHook) {
|
|
13230
|
-
preactHook(vnode);
|
|
13231
|
-
}
|
|
13232
|
-
};
|
|
13233
|
-
mainHtml = renderJsxFn(vdom);
|
|
13234
|
-
} finally {
|
|
13235
|
-
preactOptions.vnode = preactHook;
|
|
13236
|
-
}
|
|
13216
|
+
const withProviders = (children) => /* @__PURE__ */ jsx4(ASSET_MAP_CONTEXT.Provider, { value: this.assetMap, children: /* @__PURE__ */ jsx4(REQUEST_CONTEXT.Provider, { value: ctx, children: /* @__PURE__ */ jsx4(I18N_CONTEXT.Provider, { value: { locale, translations }, children: /* @__PURE__ */ jsx4(HTML_CONTEXT.Provider, { value: htmlContext, children }) }) }) });
|
|
13217
|
+
const vdom = withProviders(/* @__PURE__ */ jsx4(Component, { ...props }));
|
|
13218
|
+
const renderJsxFn = await this.getJsxRenderFn({ mode: options2.renderMode });
|
|
13219
|
+
const mainHtml = this.withNonceHooks(nonce, () => renderJsxFn(vdom));
|
|
13237
13220
|
const jsDeps = /* @__PURE__ */ new Set();
|
|
13238
13221
|
const cssDeps = /* @__PURE__ */ new Set();
|
|
13239
13222
|
const preloadDeps = this.rootConfig.modulePreload !== false ? /* @__PURE__ */ new Set() : null;
|
|
@@ -13286,19 +13269,69 @@ var Renderer = class {
|
|
|
13286
13269
|
htmlAttrs: htmlContext.htmlAttrs,
|
|
13287
13270
|
headAttrs: htmlContext.headAttrs,
|
|
13288
13271
|
bodyAttrs: htmlContext.bodyAttrs,
|
|
13272
|
+
// Components passed as `<Head>` children are only rendered here, so they
|
|
13273
|
+
// need the same context providers and nonce hooks as the main render.
|
|
13289
13274
|
headComponents: [
|
|
13290
|
-
|
|
13275
|
+
withProviders(htmlContext.headComponents),
|
|
13291
13276
|
...styleTags,
|
|
13292
13277
|
...preloadTags,
|
|
13293
13278
|
...scriptTags
|
|
13294
13279
|
],
|
|
13295
|
-
renderMode:
|
|
13280
|
+
renderMode: options2.renderMode,
|
|
13281
|
+
nonce
|
|
13296
13282
|
});
|
|
13297
13283
|
return { html };
|
|
13298
13284
|
}
|
|
13285
|
+
/**
|
|
13286
|
+
* Runs a synchronous render function with a hook installed that auto-injects
|
|
13287
|
+
* the CSP nonce into `<script>`, `<style>` and `<link rel="stylesheet">`
|
|
13288
|
+
* vnodes.
|
|
13289
|
+
*
|
|
13290
|
+
* NOTE: `preactOptions.vnode` is a global, so the hook must be installed and
|
|
13291
|
+
* restored within a single synchronous block. Awaiting inside the swapped
|
|
13292
|
+
* window allows concurrent renders to interleave, leaking a stale hook (and
|
|
13293
|
+
* its nonce) into other requests.
|
|
13294
|
+
*
|
|
13295
|
+
* https://preactjs.com/guide/v10/options/
|
|
13296
|
+
*/
|
|
13297
|
+
withNonceHooks(nonce, fn) {
|
|
13298
|
+
const injectNonce = (vnode) => {
|
|
13299
|
+
if (!vnode) {
|
|
13300
|
+
return;
|
|
13301
|
+
}
|
|
13302
|
+
if (vnode.type === "script" || vnode.type === "style") {
|
|
13303
|
+
vnode.props.nonce = nonce;
|
|
13304
|
+
}
|
|
13305
|
+
if (vnode.type === "link" && vnode.props.rel === "stylesheet") {
|
|
13306
|
+
vnode.props.nonce = nonce;
|
|
13307
|
+
}
|
|
13308
|
+
};
|
|
13309
|
+
const preactHook = preactOptions.vnode;
|
|
13310
|
+
const rootJsxHook = options.vnode;
|
|
13311
|
+
try {
|
|
13312
|
+
preactOptions.vnode = (vnode) => {
|
|
13313
|
+
injectNonce(vnode);
|
|
13314
|
+
if (preactHook) {
|
|
13315
|
+
preactHook(vnode);
|
|
13316
|
+
}
|
|
13317
|
+
};
|
|
13318
|
+
if (options !== preactOptions) {
|
|
13319
|
+
options.vnode = (vnode) => {
|
|
13320
|
+
injectNonce(vnode);
|
|
13321
|
+
if (rootJsxHook) {
|
|
13322
|
+
rootJsxHook(vnode);
|
|
13323
|
+
}
|
|
13324
|
+
};
|
|
13325
|
+
}
|
|
13326
|
+
return fn();
|
|
13327
|
+
} finally {
|
|
13328
|
+
preactOptions.vnode = preactHook;
|
|
13329
|
+
options.vnode = rootJsxHook;
|
|
13330
|
+
}
|
|
13331
|
+
}
|
|
13299
13332
|
/** SSG renders a route. */
|
|
13300
|
-
async renderRoute(route,
|
|
13301
|
-
const routeParams =
|
|
13333
|
+
async renderRoute(route, options2) {
|
|
13334
|
+
const routeParams = options2.routeParams;
|
|
13302
13335
|
if (route.locale) {
|
|
13303
13336
|
routeParams.$locale = route.locale;
|
|
13304
13337
|
}
|
|
@@ -13343,10 +13376,10 @@ var Renderer = class {
|
|
|
13343
13376
|
});
|
|
13344
13377
|
}
|
|
13345
13378
|
/** Handles the SSR rendering of a route. */
|
|
13346
|
-
async handleRoute(req, res, next, route,
|
|
13347
|
-
const defaultStatusCode =
|
|
13348
|
-
const routeParams =
|
|
13349
|
-
const nextRouteHandler =
|
|
13379
|
+
async handleRoute(req, res, next, route, options2) {
|
|
13380
|
+
const defaultStatusCode = options2?.defaultStatusCode || 200;
|
|
13381
|
+
const routeParams = options2?.routeParams || {};
|
|
13382
|
+
const nextRouteHandler = options2?.nextRouteHandler;
|
|
13350
13383
|
const fallbackLocales = route.isDefaultLocale ? getFallbackLocales(req) : [route.locale];
|
|
13351
13384
|
const getPreferredLocale = (availableLocales) => {
|
|
13352
13385
|
const lowerLocales = availableLocales.map((l) => l.toLowerCase());
|
|
@@ -13357,8 +13390,8 @@ var Renderer = class {
|
|
|
13357
13390
|
}
|
|
13358
13391
|
return req.rootConfig?.i18n?.defaultLocale || "en";
|
|
13359
13392
|
};
|
|
13360
|
-
const render404 = async (
|
|
13361
|
-
if (
|
|
13393
|
+
const render404 = async (options3) => {
|
|
13394
|
+
if (options3?.nextRoute && nextRouteHandler) {
|
|
13362
13395
|
await nextRouteHandler();
|
|
13363
13396
|
return;
|
|
13364
13397
|
}
|
|
@@ -13367,7 +13400,7 @@ var Renderer = class {
|
|
|
13367
13400
|
}
|
|
13368
13401
|
next();
|
|
13369
13402
|
};
|
|
13370
|
-
const render = async (props2,
|
|
13403
|
+
const render = async (props2, options3) => {
|
|
13371
13404
|
if (!route.module.default) {
|
|
13372
13405
|
console.error(`no default component exported in route: ${route.src}`);
|
|
13373
13406
|
render404();
|
|
@@ -13376,8 +13409,8 @@ var Renderer = class {
|
|
|
13376
13409
|
const securityConfig = getSecurityConfig(this.rootConfig);
|
|
13377
13410
|
const cspEnabled = !!securityConfig.contentSecurityPolicy;
|
|
13378
13411
|
const currentPath = req.path;
|
|
13379
|
-
const locale =
|
|
13380
|
-
const translations =
|
|
13412
|
+
const locale = options3?.locale || route.locale;
|
|
13413
|
+
const translations = options3?.translations;
|
|
13381
13414
|
const nonce = cspEnabled ? this.generateNonce() : void 0;
|
|
13382
13415
|
const output = await this.renderComponent(route.module.default, props2, {
|
|
13383
13416
|
currentPath,
|
|
@@ -13386,7 +13419,7 @@ var Renderer = class {
|
|
|
13386
13419
|
locale,
|
|
13387
13420
|
translations,
|
|
13388
13421
|
nonce,
|
|
13389
|
-
renderMode:
|
|
13422
|
+
renderMode: options3?.renderMode
|
|
13390
13423
|
});
|
|
13391
13424
|
let html = await transformHtml(output.html, this.rootConfig);
|
|
13392
13425
|
if (req.viteServer) {
|
|
@@ -13398,7 +13431,7 @@ var Renderer = class {
|
|
|
13398
13431
|
);
|
|
13399
13432
|
}
|
|
13400
13433
|
}
|
|
13401
|
-
let statusCode =
|
|
13434
|
+
let statusCode = options3?.statusCode || defaultStatusCode;
|
|
13402
13435
|
if (route.src === "routes/404.tsx") {
|
|
13403
13436
|
statusCode = 404;
|
|
13404
13437
|
} else if (route.src === "routes/401.tsx") {
|
|
@@ -13561,8 +13594,8 @@ var Renderer = class {
|
|
|
13561
13594
|
async getJsxRenderFn(renderOptions) {
|
|
13562
13595
|
const mode = renderOptions?.mode ?? this.rootConfig.jsxRenderer?.mode;
|
|
13563
13596
|
if (mode) {
|
|
13564
|
-
const
|
|
13565
|
-
return (vnode) => renderJsxToString(vnode,
|
|
13597
|
+
const options2 = { ...this.getJsxRenderOptions(), mode };
|
|
13598
|
+
return (vnode) => renderJsxToString(vnode, options2);
|
|
13566
13599
|
}
|
|
13567
13600
|
const { renderToString } = await import("preact-render-to-string");
|
|
13568
13601
|
if (!renderToString) {
|
|
@@ -13577,8 +13610,8 @@ var Renderer = class {
|
|
|
13577
13610
|
* `preact-render-to-string` depending if the `jsxRenderer` config is set up
|
|
13578
13611
|
* in `root.config.ts`.
|
|
13579
13612
|
*/
|
|
13580
|
-
async renderJsx(vnode,
|
|
13581
|
-
const render = await this.getJsxRenderFn(
|
|
13613
|
+
async renderJsx(vnode, options2) {
|
|
13614
|
+
const render = await this.getJsxRenderFn(options2);
|
|
13582
13615
|
return render(vnode);
|
|
13583
13616
|
}
|
|
13584
13617
|
getConfiguredStyleEntries() {
|
|
@@ -13586,14 +13619,14 @@ var Renderer = class {
|
|
|
13586
13619
|
const basePath = this.rootConfig.base || "/";
|
|
13587
13620
|
return styleEntries.map((entry) => entry.trim()).filter((entry) => entry).map((entry) => normalizeStyleEntry(entry, basePath));
|
|
13588
13621
|
}
|
|
13589
|
-
async renderHtml(html,
|
|
13590
|
-
const htmlAttrs =
|
|
13591
|
-
const headAttrs =
|
|
13592
|
-
const bodyAttrs =
|
|
13622
|
+
async renderHtml(html, options2) {
|
|
13623
|
+
const htmlAttrs = options2?.htmlAttrs || {};
|
|
13624
|
+
const headAttrs = options2?.headAttrs || {};
|
|
13625
|
+
const bodyAttrs = options2?.bodyAttrs || {};
|
|
13593
13626
|
const page = /* @__PURE__ */ jsxs4("html", { ...htmlAttrs, children: [
|
|
13594
13627
|
/* @__PURE__ */ jsxs4("head", { ...headAttrs, children: [
|
|
13595
13628
|
/* @__PURE__ */ jsx4("meta", { charSet: "utf-8" }),
|
|
13596
|
-
|
|
13629
|
+
options2?.headComponents
|
|
13597
13630
|
] }),
|
|
13598
13631
|
/* @__PURE__ */ jsx4(
|
|
13599
13632
|
"body",
|
|
@@ -13603,7 +13636,9 @@ var Renderer = class {
|
|
|
13603
13636
|
}
|
|
13604
13637
|
)
|
|
13605
13638
|
] });
|
|
13606
|
-
const
|
|
13639
|
+
const renderJsxFn = await this.getJsxRenderFn({ mode: options2?.renderMode });
|
|
13640
|
+
const nonce = options2?.nonce;
|
|
13641
|
+
const content = nonce ? this.withNonceHooks(nonce, () => renderJsxFn(page)) : renderJsxFn(page);
|
|
13607
13642
|
return `<!doctype html>
|
|
13608
13643
|
${content}`;
|
|
13609
13644
|
}
|
|
@@ -13613,10 +13648,10 @@ ${content}`;
|
|
|
13613
13648
|
}
|
|
13614
13649
|
return str;
|
|
13615
13650
|
}
|
|
13616
|
-
async render404(
|
|
13651
|
+
async render404(options2) {
|
|
13617
13652
|
const basePath = this.rootConfig.base || "/";
|
|
13618
13653
|
const notFoundPath = normalizeUrlPath(`${basePath}/404`);
|
|
13619
|
-
const currentPath =
|
|
13654
|
+
const currentPath = options2?.currentPath || notFoundPath;
|
|
13620
13655
|
const [route, routeParams] = this.router.get(notFoundPath);
|
|
13621
13656
|
if (route && route.src === "routes/404.tsx" && route.module.default) {
|
|
13622
13657
|
const Component = route.module.default;
|
|
@@ -13651,8 +13686,8 @@ ${content}`;
|
|
|
13651
13686
|
});
|
|
13652
13687
|
return { html };
|
|
13653
13688
|
}
|
|
13654
|
-
async renderError(err,
|
|
13655
|
-
const currentPath =
|
|
13689
|
+
async renderError(err, options2) {
|
|
13690
|
+
const currentPath = options2?.currentPath || "/500";
|
|
13656
13691
|
const [route, routeParams] = this.router.get("/500");
|
|
13657
13692
|
if (route && route.src === "routes/500.tsx" && route.module.default) {
|
|
13658
13693
|
const Component = route.module.default;
|