@blinkk/root 3.0.5 → 3.1.1

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.
Files changed (44) hide show
  1. package/dist/build-worker.js +108 -0
  2. package/dist/build-worker.js.map +7 -0
  3. package/dist/chunk-3PWR4F2R.js +77 -0
  4. package/dist/chunk-3PWR4F2R.js.map +7 -0
  5. package/dist/{chunk-VQPDCDKL.js → chunk-E4F6CMO3.js} +469 -536
  6. package/dist/chunk-E4F6CMO3.js.map +7 -0
  7. package/dist/{chunk-6P3B7ZXL.js → chunk-I3RCAIW7.js} +1 -62
  8. package/dist/chunk-I3RCAIW7.js.map +7 -0
  9. package/dist/chunk-KHO3HMX6.js +291 -0
  10. package/dist/chunk-KHO3HMX6.js.map +7 -0
  11. package/dist/chunk-M3E4RKS7.js +374 -0
  12. package/dist/chunk-M3E4RKS7.js.map +7 -0
  13. package/dist/{chunk-6NBKAR5Y.js → chunk-TM6QRBGS.js} +31 -1
  14. package/dist/chunk-TM6QRBGS.js.map +7 -0
  15. package/dist/chunk-WX62JK5E.js +81 -0
  16. package/dist/chunk-WX62JK5E.js.map +7 -0
  17. package/dist/{chunk-7PSEEE6C.js → chunk-YU22SAIG.js} +31 -317
  18. package/dist/chunk-YU22SAIG.js.map +7 -0
  19. package/dist/cli/build-page.d.ts +57 -0
  20. package/dist/cli/build-worker-pool.d.ts +46 -0
  21. package/dist/cli/build-worker.d.ts +4 -0
  22. package/dist/cli/build.d.ts +6 -0
  23. package/dist/cli.js +8 -4
  24. package/dist/core/config.d.ts +11 -3
  25. package/dist/core/security.d.ts +14 -0
  26. package/dist/core.js.map +2 -2
  27. package/dist/functions.js +8 -4
  28. package/dist/functions.js.map +1 -1
  29. package/dist/jsx/types.d.ts +1 -0
  30. package/dist/middleware/common.d.ts +18 -0
  31. package/dist/middleware/compression.d.ts +12 -0
  32. package/dist/middleware/middleware.d.ts +1 -0
  33. package/dist/middleware.js +6 -1
  34. package/dist/node.js +7 -5
  35. package/dist/render/asset-map/build-asset-map.d.ts +11 -1
  36. package/dist/render/html-transform.d.ts +15 -0
  37. package/dist/render/render.d.ts +15 -9
  38. package/dist/render.js +45 -104
  39. package/dist/render.js.map +2 -2
  40. package/package.json +2 -2
  41. package/dist/chunk-6NBKAR5Y.js.map +0 -7
  42. package/dist/chunk-6P3B7ZXL.js.map +0 -7
  43. package/dist/chunk-7PSEEE6C.js.map +0 -7
  44. package/dist/chunk-VQPDCDKL.js.map +0 -7
package/dist/render.js CHANGED
@@ -7,15 +7,20 @@ import {
7
7
  testPathHasParams
8
8
  } from "./chunk-KW7KTYGC.js";
9
9
  import {
10
- RouteTrie,
11
- htmlMinify,
12
- htmlPretty,
13
- parseTagNames
14
- } from "./chunk-6P3B7ZXL.js";
10
+ RouteTrie
11
+ } from "./chunk-I3RCAIW7.js";
12
+ import {
13
+ parseTagNames,
14
+ transformHtml
15
+ } from "./chunk-3PWR4F2R.js";
15
16
  import {
16
17
  renderJsxToString
17
18
  } from "./chunk-X2C2MEJ2.js";
18
19
  import "./chunk-3Z5TNZEP.js";
20
+ import {
21
+ getSecurityConfig,
22
+ setSecurityHeaders
23
+ } from "./chunk-WX62JK5E.js";
19
24
 
20
25
  // src/render/render.tsx
21
26
  import crypto from "node:crypto";
@@ -13120,6 +13125,14 @@ var Renderer = class {
13120
13125
  getRoute(url) {
13121
13126
  return this.router.get(url);
13122
13127
  }
13128
+ /** Returns all routes that match a given url path. */
13129
+ getRouteMatches(url) {
13130
+ return this.router.matchAll(url);
13131
+ }
13132
+ /** Walks all routes registered with the router. */
13133
+ async walkRoutes(cb) {
13134
+ await this.router.walk(cb);
13135
+ }
13123
13136
  async handle(req, res, next) {
13124
13137
  let url = req.path;
13125
13138
  if (url.includes("%")) {
@@ -13171,6 +13184,7 @@ var Renderer = class {
13171
13184
  scriptDeps: []
13172
13185
  };
13173
13186
  const vdom = /* @__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: /* @__PURE__ */ jsx4(Component, { ...props }) }) }) });
13187
+ const renderJsxFn = await this.getJsxRenderFn();
13174
13188
  const preactHook = preactOptions.vnode;
13175
13189
  let mainHtml;
13176
13190
  try {
@@ -13188,11 +13202,9 @@ var Renderer = class {
13188
13202
  preactHook(vnode);
13189
13203
  }
13190
13204
  };
13191
- mainHtml = await this.renderJsx(vdom);
13192
- preactOptions.vnode = preactHook;
13193
- } catch (err) {
13205
+ mainHtml = renderJsxFn(vdom);
13206
+ } finally {
13194
13207
  preactOptions.vnode = preactHook;
13195
- throw err;
13196
13208
  }
13197
13209
  const jsDeps = /* @__PURE__ */ new Set();
13198
13210
  const cssDeps = /* @__PURE__ */ new Set();
@@ -13322,7 +13334,7 @@ var Renderer = class {
13322
13334
  render404();
13323
13335
  return;
13324
13336
  }
13325
- const securityConfig = this.getSecurityConfig();
13337
+ const securityConfig = getSecurityConfig(this.rootConfig);
13326
13338
  const cspEnabled = !!securityConfig.contentSecurityPolicy;
13327
13339
  const currentPath = req.path;
13328
13340
  const locale = options2?.locale || route.locale;
@@ -13336,12 +13348,7 @@ var Renderer = class {
13336
13348
  translations,
13337
13349
  nonce
13338
13350
  });
13339
- let html = output.html;
13340
- if (this.rootConfig.prettyHtml) {
13341
- html = await htmlPretty(html, this.rootConfig.prettyHtmlOptions);
13342
- } else if (this.rootConfig.minifyHtml !== false) {
13343
- html = await htmlMinify(html, this.rootConfig.minifyHtmlOptions);
13344
- }
13351
+ let html = await transformHtml(output.html, this.rootConfig);
13345
13352
  if (req.viteServer) {
13346
13353
  html = await req.viteServer.transformIndexHtml(currentPath, html);
13347
13354
  if (nonce) {
@@ -13371,7 +13378,7 @@ var Renderer = class {
13371
13378
  }
13372
13379
  res.status(statusCode);
13373
13380
  res.set({ "Content-Type": "text/html" });
13374
- this.setSecurityHeaders(res, {
13381
+ setSecurityHeaders(res, {
13375
13382
  securityConfig,
13376
13383
  nonce
13377
13384
  });
@@ -13499,13 +13506,19 @@ var Renderer = class {
13499
13506
  return this.rootConfig.jsxRenderer || {};
13500
13507
  }
13501
13508
  /**
13502
- * Renders JSX via either the `@blinkk/root/jsx` package or
13503
- * `preact-render-to-string` depending if the `jsxRenderer` config is set up
13504
- * in `root.config.ts`.
13509
+ * Resolves a synchronous JSX render function, using either the
13510
+ * `@blinkk/root/jsx` package or `preact-render-to-string` depending if the
13511
+ * `jsxRenderer` config is set up in `root.config.ts`.
13512
+ *
13513
+ * Resolving the renderer up front (rather than awaiting inside the render
13514
+ * call) allows callers that temporarily swap global state (e.g. the
13515
+ * `preact.options.vnode` nonce hook) to render synchronously without
13516
+ * yielding to the event loop.
13505
13517
  */
13506
- async renderJsx(vnode) {
13518
+ async getJsxRenderFn() {
13507
13519
  if (this.rootConfig.jsxRenderer?.mode) {
13508
- return renderJsxToString(vnode, this.getJsxRenderOptions());
13520
+ const options = this.getJsxRenderOptions();
13521
+ return (vnode) => renderJsxToString(vnode, options);
13509
13522
  }
13510
13523
  const { renderToString } = await import("preact-render-to-string");
13511
13524
  if (!renderToString) {
@@ -13513,7 +13526,16 @@ var Renderer = class {
13513
13526
  'failed to render jsx. either install preact-render-to-string or add the "jsxRenderer" config to root.config.ts'
13514
13527
  );
13515
13528
  }
13516
- return renderToString(vnode);
13529
+ return (vnode) => renderToString(vnode);
13530
+ }
13531
+ /**
13532
+ * Renders JSX via either the `@blinkk/root/jsx` package or
13533
+ * `preact-render-to-string` depending if the `jsxRenderer` config is set up
13534
+ * in `root.config.ts`.
13535
+ */
13536
+ async renderJsx(vnode) {
13537
+ const render = await this.getJsxRenderFn();
13538
+ return render(vnode);
13517
13539
  }
13518
13540
  getConfiguredStyleEntries() {
13519
13541
  const styleEntries = this.rootConfig.styles?.entries || [];
@@ -13685,94 +13707,13 @@ ${content}`;
13685
13707
  );
13686
13708
  return { jsDeps, cssDeps };
13687
13709
  }
13688
- /**
13689
- * Returns the `security` config value with default values inserted wherever
13690
- * a user config value is blank or set to `true`.
13691
- */
13692
- getSecurityConfig() {
13693
- const userConfig = this.rootConfig.server?.security || {};
13694
- const securityConfig = {};
13695
- if (isTrueOrUndefined(userConfig.contentSecurityPolicy)) {
13696
- securityConfig.contentSecurityPolicy = {
13697
- directives: {
13698
- "base-uri": ["'none'"],
13699
- "object-src": ["'none'"],
13700
- // NOTE: nonce is automatically added to this list.
13701
- "script-src": [
13702
- "'unsafe-inline'",
13703
- "'unsafe-eval'",
13704
- "'strict-dynamic' https: http:"
13705
- ]
13706
- },
13707
- reportOnly: true
13708
- };
13709
- } else {
13710
- securityConfig.contentSecurityPolicy = userConfig.contentSecurityPolicy;
13711
- }
13712
- if (isTrueOrUndefined(userConfig.xFrameOptions)) {
13713
- securityConfig.xFrameOptions = "SAMEORIGIN";
13714
- } else {
13715
- securityConfig.xFrameOptions = userConfig.xFrameOptions;
13716
- }
13717
- securityConfig.strictTransportSecurity = userConfig.strictTransportSecurity ?? true;
13718
- securityConfig.xContentTypeOptions = userConfig.xContentTypeOptions ?? true;
13719
- securityConfig.xXssProtection = userConfig.xXssProtection ?? true;
13720
- return securityConfig;
13721
- }
13722
13710
  /**
13723
13711
  * Generates a random string that can be used as the "nonce" value for CSP.
13724
13712
  */
13725
13713
  generateNonce() {
13726
13714
  return crypto.randomBytes(16).toString("base64");
13727
13715
  }
13728
- /**
13729
- * Sets security-related HTTP headers.
13730
- */
13731
- setSecurityHeaders(res, options) {
13732
- const securityConfig = options.securityConfig;
13733
- const contentSecurityPolicy = securityConfig.contentSecurityPolicy;
13734
- if (typeof contentSecurityPolicy === "object") {
13735
- const directives = { ...contentSecurityPolicy.directives };
13736
- if (options.nonce) {
13737
- const scriptSrc = directives["script-src"] || [
13738
- "'unsafe-inline'",
13739
- "'unsafe-eval'",
13740
- "'strict-dynamic' https: http:"
13741
- ];
13742
- const scriptSrcWithNonce = [...scriptSrc, `'nonce-${options.nonce}'`];
13743
- directives["script-src"] = scriptSrcWithNonce;
13744
- }
13745
- const headerSegments = [];
13746
- Object.entries(directives).forEach(([key, values]) => {
13747
- headerSegments.push([key, ...values].join(" "));
13748
- });
13749
- const csp = headerSegments.join("; ");
13750
- if (contentSecurityPolicy.reportOnly === false) {
13751
- res.setHeader("content-security-policy", csp);
13752
- } else {
13753
- res.setHeader("content-security-policy-report-only", csp);
13754
- }
13755
- }
13756
- if (typeof securityConfig.xFrameOptions === "string") {
13757
- res.setHeader("x-frame-options", securityConfig.xFrameOptions);
13758
- }
13759
- if (securityConfig.strictTransportSecurity) {
13760
- res.setHeader(
13761
- "strict-transport-security",
13762
- "max-age=63072000; includeSubdomains; preload"
13763
- );
13764
- }
13765
- if (securityConfig.xContentTypeOptions) {
13766
- res.setHeader("x-content-type-options", "nosniff");
13767
- }
13768
- if (securityConfig.xXssProtection) {
13769
- res.setHeader("x-xss-protection", "1; mode=block");
13770
- }
13771
- }
13772
13716
  };
13773
- function isTrueOrUndefined(value) {
13774
- return value === true || value === void 0;
13775
- }
13776
13717
  function sortLocales(a, b) {
13777
13718
  if (a === "x-default") {
13778
13719
  return -1;