@timber-js/app 0.2.0-alpha.4 → 0.2.0-alpha.40
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/LICENSE +8 -0
- package/dist/_chunks/{als-registry-B7DbZ2hS.js → als-registry-Ba7URUIn.js} +1 -1
- package/dist/_chunks/als-registry-Ba7URUIn.js.map +1 -0
- package/dist/_chunks/chunk-DYhsFzuS.js +33 -0
- package/dist/_chunks/debug-ECi_61pb.js +108 -0
- package/dist/_chunks/debug-ECi_61pb.js.map +1 -0
- package/dist/_chunks/define-cookie-BmKbSyp0.js +93 -0
- package/dist/_chunks/define-cookie-BmKbSyp0.js.map +1 -0
- package/dist/_chunks/error-boundary-BAN3751q.js +211 -0
- package/dist/_chunks/error-boundary-BAN3751q.js.map +1 -0
- package/dist/_chunks/{format-CwdaB0_2.js → format-cX7wzEp2.js} +2 -2
- package/dist/_chunks/{format-CwdaB0_2.js.map → format-cX7wzEp2.js.map} +1 -1
- package/dist/_chunks/{interception-BOoWmLUA.js → interception-D2djYaIm.js} +112 -77
- package/dist/_chunks/interception-D2djYaIm.js.map +1 -0
- package/dist/_chunks/{metadata-routes-Cjmvi3rQ.js → metadata-routes-BU684ls2.js} +1 -1
- package/dist/_chunks/{metadata-routes-Cjmvi3rQ.js.map → metadata-routes-BU684ls2.js.map} +1 -1
- package/dist/_chunks/{request-context-CZJi4CuK.js → request-context-BxYIJM24.js} +93 -69
- package/dist/_chunks/request-context-BxYIJM24.js.map +1 -0
- package/dist/_chunks/segment-context-C6byCyZU.js +69 -0
- package/dist/_chunks/segment-context-C6byCyZU.js.map +1 -0
- package/dist/_chunks/stale-reload-C0ValzG7.js +47 -0
- package/dist/_chunks/stale-reload-C0ValzG7.js.map +1 -0
- package/dist/_chunks/{tracing-Cwn7697K.js → tracing-CuXiCP5p.js} +17 -3
- package/dist/_chunks/{tracing-Cwn7697K.js.map → tracing-CuXiCP5p.js.map} +1 -1
- package/dist/_chunks/{use-query-states-D5KaffOK.js → use-query-states-BvW0TKDn.js} +1 -1
- package/dist/_chunks/{use-query-states-D5KaffOK.js.map → use-query-states-BvW0TKDn.js.map} +1 -1
- package/dist/_chunks/wrappers-C6J0nNji.js +331 -0
- package/dist/_chunks/wrappers-C6J0nNji.js.map +1 -0
- package/dist/adapters/compress-module.d.ts.map +1 -1
- package/dist/adapters/nitro.d.ts +17 -1
- package/dist/adapters/nitro.d.ts.map +1 -1
- package/dist/adapters/nitro.js +56 -13
- package/dist/adapters/nitro.js.map +1 -1
- package/dist/cache/fast-hash.d.ts +22 -0
- package/dist/cache/fast-hash.d.ts.map +1 -0
- package/dist/cache/index.d.ts +5 -2
- package/dist/cache/index.d.ts.map +1 -1
- package/dist/cache/index.js +88 -18
- package/dist/cache/index.js.map +1 -1
- package/dist/cache/register-cached-function.d.ts.map +1 -1
- package/dist/cache/singleflight.d.ts +18 -1
- package/dist/cache/singleflight.d.ts.map +1 -1
- package/dist/cache/timber-cache.d.ts.map +1 -1
- package/dist/client/error-boundary.d.ts +10 -1
- package/dist/client/error-boundary.d.ts.map +1 -1
- package/dist/client/error-boundary.js +1 -125
- package/dist/client/index.d.ts +3 -2
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +213 -93
- package/dist/client/index.js.map +1 -1
- package/dist/client/link.d.ts +22 -8
- package/dist/client/link.d.ts.map +1 -1
- package/dist/client/navigation-context.d.ts +2 -2
- package/dist/client/router.d.ts +25 -3
- package/dist/client/router.d.ts.map +1 -1
- package/dist/client/rsc-fetch.d.ts +23 -2
- package/dist/client/rsc-fetch.d.ts.map +1 -1
- package/dist/client/segment-cache.d.ts +1 -1
- package/dist/client/segment-cache.d.ts.map +1 -1
- package/dist/client/segment-context.d.ts +1 -1
- package/dist/client/segment-context.d.ts.map +1 -1
- package/dist/client/segment-merger.d.ts.map +1 -1
- package/dist/client/stale-reload.d.ts +15 -0
- package/dist/client/stale-reload.d.ts.map +1 -1
- package/dist/client/top-loader.d.ts +1 -1
- package/dist/client/top-loader.d.ts.map +1 -1
- package/dist/client/transition-root.d.ts +1 -1
- package/dist/client/transition-root.d.ts.map +1 -1
- package/dist/client/use-params.d.ts +2 -2
- package/dist/client/use-params.d.ts.map +1 -1
- package/dist/client/use-query-states.d.ts +1 -1
- package/dist/codec.d.ts +21 -0
- package/dist/codec.d.ts.map +1 -0
- package/dist/cookies/define-cookie.d.ts +33 -12
- package/dist/cookies/define-cookie.d.ts.map +1 -1
- package/dist/cookies/index.js +1 -83
- package/dist/fonts/css.d.ts +1 -0
- package/dist/fonts/css.d.ts.map +1 -1
- package/dist/fonts/local.d.ts +4 -2
- package/dist/fonts/local.d.ts.map +1 -1
- package/dist/index.d.ts +112 -35
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +635 -233
- package/dist/index.js.map +1 -1
- package/dist/params/define.d.ts +76 -0
- package/dist/params/define.d.ts.map +1 -0
- package/dist/params/index.d.ts +8 -0
- package/dist/params/index.d.ts.map +1 -0
- package/dist/params/index.js +104 -0
- package/dist/params/index.js.map +1 -0
- package/dist/plugins/adapter-build.d.ts.map +1 -1
- package/dist/plugins/build-manifest.d.ts.map +1 -1
- package/dist/plugins/client-chunks.d.ts +32 -0
- package/dist/plugins/client-chunks.d.ts.map +1 -0
- package/dist/plugins/dev-error-overlay.d.ts +26 -1
- package/dist/plugins/dev-error-overlay.d.ts.map +1 -1
- package/dist/plugins/entries.d.ts +7 -0
- package/dist/plugins/entries.d.ts.map +1 -1
- package/dist/plugins/fonts.d.ts +9 -1
- package/dist/plugins/fonts.d.ts.map +1 -1
- package/dist/plugins/mdx.d.ts +6 -0
- package/dist/plugins/mdx.d.ts.map +1 -1
- package/dist/plugins/routing.d.ts.map +1 -1
- package/dist/plugins/server-bundle.d.ts.map +1 -1
- package/dist/plugins/static-build.d.ts.map +1 -1
- package/dist/routing/codegen.d.ts +2 -2
- package/dist/routing/codegen.d.ts.map +1 -1
- package/dist/routing/index.js +1 -1
- package/dist/routing/scanner.d.ts.map +1 -1
- package/dist/routing/status-file-lint.d.ts +2 -1
- package/dist/routing/status-file-lint.d.ts.map +1 -1
- package/dist/routing/types.d.ts +6 -4
- package/dist/routing/types.d.ts.map +1 -1
- package/dist/rsc-runtime/rsc.d.ts +1 -1
- package/dist/rsc-runtime/rsc.d.ts.map +1 -1
- package/dist/rsc-runtime/ssr.d.ts +12 -0
- package/dist/rsc-runtime/ssr.d.ts.map +1 -1
- package/dist/search-params/codecs.d.ts +1 -1
- package/dist/search-params/define.d.ts +153 -0
- package/dist/search-params/define.d.ts.map +1 -0
- package/dist/search-params/index.d.ts +4 -5
- package/dist/search-params/index.d.ts.map +1 -1
- package/dist/search-params/index.js +3 -474
- package/dist/search-params/registry.d.ts +1 -1
- package/dist/search-params/wrappers.d.ts +53 -0
- package/dist/search-params/wrappers.d.ts.map +1 -0
- package/dist/server/access-gate.d.ts +4 -0
- package/dist/server/access-gate.d.ts.map +1 -1
- package/dist/server/action-client.d.ts.map +1 -1
- package/dist/server/action-encryption.d.ts +76 -0
- package/dist/server/action-encryption.d.ts.map +1 -0
- package/dist/server/action-handler.d.ts.map +1 -1
- package/dist/server/als-registry.d.ts +18 -4
- package/dist/server/als-registry.d.ts.map +1 -1
- package/dist/server/build-manifest.d.ts +2 -2
- package/dist/server/debug.d.ts +46 -15
- package/dist/server/debug.d.ts.map +1 -1
- package/dist/server/default-logger.d.ts +22 -0
- package/dist/server/default-logger.d.ts.map +1 -0
- package/dist/server/deny-renderer.d.ts.map +1 -1
- package/dist/server/early-hints.d.ts +13 -5
- package/dist/server/early-hints.d.ts.map +1 -1
- package/dist/server/error-boundary-wrapper.d.ts +4 -0
- package/dist/server/error-boundary-wrapper.d.ts.map +1 -1
- package/dist/server/flight-injection-state.d.ts +78 -0
- package/dist/server/flight-injection-state.d.ts.map +1 -0
- package/dist/server/flight-scripts.d.ts +39 -0
- package/dist/server/flight-scripts.d.ts.map +1 -0
- package/dist/server/flush.d.ts.map +1 -1
- package/dist/server/form-data.d.ts +29 -0
- package/dist/server/form-data.d.ts.map +1 -1
- package/dist/server/html-injectors.d.ts +5 -11
- package/dist/server/html-injectors.d.ts.map +1 -1
- package/dist/server/index.d.ts +4 -2
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +1975 -1649
- package/dist/server/index.js.map +1 -1
- package/dist/server/logger.d.ts +24 -7
- package/dist/server/logger.d.ts.map +1 -1
- package/dist/server/node-stream-transforms.d.ts +77 -0
- package/dist/server/node-stream-transforms.d.ts.map +1 -0
- package/dist/server/pipeline.d.ts +7 -4
- package/dist/server/pipeline.d.ts.map +1 -1
- package/dist/server/primitives.d.ts +30 -3
- package/dist/server/primitives.d.ts.map +1 -1
- package/dist/server/render-timeout.d.ts +51 -0
- package/dist/server/render-timeout.d.ts.map +1 -0
- package/dist/server/request-context.d.ts +65 -38
- package/dist/server/request-context.d.ts.map +1 -1
- package/dist/server/route-element-builder.d.ts +7 -0
- package/dist/server/route-element-builder.d.ts.map +1 -1
- package/dist/server/route-handler.d.ts.map +1 -1
- package/dist/server/route-matcher.d.ts +2 -2
- package/dist/server/route-matcher.d.ts.map +1 -1
- package/dist/server/rsc-entry/error-renderer.d.ts.map +1 -1
- package/dist/server/rsc-entry/helpers.d.ts +46 -3
- package/dist/server/rsc-entry/helpers.d.ts.map +1 -1
- package/dist/server/rsc-entry/index.d.ts +6 -1
- package/dist/server/rsc-entry/index.d.ts.map +1 -1
- package/dist/server/rsc-entry/rsc-payload.d.ts.map +1 -1
- package/dist/server/rsc-entry/rsc-stream.d.ts +9 -0
- package/dist/server/rsc-entry/rsc-stream.d.ts.map +1 -1
- package/dist/server/rsc-entry/ssr-renderer.d.ts.map +1 -1
- package/dist/server/slot-resolver.d.ts +1 -1
- package/dist/server/slot-resolver.d.ts.map +1 -1
- package/dist/server/ssr-entry.d.ts +22 -0
- package/dist/server/ssr-entry.d.ts.map +1 -1
- package/dist/server/ssr-render.d.ts +39 -21
- package/dist/server/ssr-render.d.ts.map +1 -1
- package/dist/server/tracing.d.ts +10 -0
- package/dist/server/tracing.d.ts.map +1 -1
- package/dist/server/tree-builder.d.ts +19 -12
- package/dist/server/tree-builder.d.ts.map +1 -1
- package/dist/server/types.d.ts +1 -3
- package/dist/server/types.d.ts.map +1 -1
- package/dist/server/version-skew.d.ts +61 -0
- package/dist/server/version-skew.d.ts.map +1 -0
- package/dist/server/waituntil-bridge.d.ts.map +1 -1
- package/dist/shared/merge-search-params.d.ts +22 -0
- package/dist/shared/merge-search-params.d.ts.map +1 -0
- package/dist/shims/navigation-client.d.ts +1 -1
- package/dist/shims/navigation-client.d.ts.map +1 -1
- package/dist/shims/navigation.d.ts +1 -1
- package/dist/shims/navigation.d.ts.map +1 -1
- package/dist/utils/state-machine.d.ts +80 -0
- package/dist/utils/state-machine.d.ts.map +1 -0
- package/package.json +17 -14
- package/src/adapters/compress-module.ts +24 -4
- package/src/adapters/nitro.ts +58 -9
- package/src/cache/fast-hash.ts +34 -0
- package/src/cache/index.ts +5 -2
- package/src/cache/register-cached-function.ts +7 -3
- package/src/cache/singleflight.ts +62 -4
- package/src/cache/timber-cache.ts +34 -26
- package/src/cli.ts +0 -0
- package/src/client/browser-entry.ts +94 -90
- package/src/client/error-boundary.tsx +18 -1
- package/src/client/index.ts +10 -1
- package/src/client/link.tsx +78 -19
- package/src/client/navigation-context.ts +2 -2
- package/src/client/router.ts +105 -60
- package/src/client/rsc-fetch.ts +63 -2
- package/src/client/segment-cache.ts +1 -1
- package/src/client/segment-context.ts +6 -1
- package/src/client/segment-merger.ts +2 -8
- package/src/client/stale-reload.ts +32 -6
- package/src/client/top-loader.tsx +10 -9
- package/src/client/transition-root.tsx +7 -1
- package/src/client/use-params.ts +3 -3
- package/src/client/use-query-states.ts +1 -1
- package/src/codec.ts +21 -0
- package/src/cookies/define-cookie.ts +69 -18
- package/src/fonts/css.ts +2 -1
- package/src/fonts/local.ts +7 -3
- package/src/index.ts +280 -85
- package/src/params/define.ts +260 -0
- package/src/params/index.ts +28 -0
- package/src/plugins/adapter-build.ts +6 -0
- package/src/plugins/build-manifest.ts +11 -0
- package/src/plugins/client-chunks.ts +65 -0
- package/src/plugins/dev-error-overlay.ts +70 -1
- package/src/plugins/dev-server.ts +38 -4
- package/src/plugins/entries.ts +12 -11
- package/src/plugins/fonts.ts +171 -19
- package/src/plugins/mdx.ts +9 -5
- package/src/plugins/routing.ts +40 -14
- package/src/plugins/server-bundle.ts +32 -1
- package/src/plugins/shims.ts +1 -1
- package/src/plugins/static-build.ts +8 -4
- package/src/routing/codegen.ts +109 -88
- package/src/routing/scanner.ts +55 -6
- package/src/routing/status-file-lint.ts +2 -1
- package/src/routing/types.ts +7 -4
- package/src/rsc-runtime/rsc.ts +2 -0
- package/src/rsc-runtime/ssr.ts +50 -0
- package/src/rsc-runtime/vendor-types.d.ts +7 -0
- package/src/search-params/codecs.ts +1 -1
- package/src/search-params/define.ts +504 -0
- package/src/search-params/index.ts +12 -18
- package/src/search-params/registry.ts +1 -1
- package/src/search-params/wrappers.ts +85 -0
- package/src/server/access-gate.tsx +40 -9
- package/src/server/action-client.ts +14 -5
- package/src/server/action-encryption.ts +144 -0
- package/src/server/action-handler.ts +19 -2
- package/src/server/als-registry.ts +18 -4
- package/src/server/build-manifest.ts +4 -4
- package/src/server/compress.ts +25 -7
- package/src/server/debug.ts +55 -17
- package/src/server/default-logger.ts +98 -0
- package/src/server/deny-renderer.ts +2 -1
- package/src/server/early-hints.ts +36 -15
- package/src/server/error-boundary-wrapper.ts +57 -14
- package/src/server/flight-injection-state.ts +152 -0
- package/src/server/flight-scripts.ts +59 -0
- package/src/server/flush.ts +2 -1
- package/src/server/form-data.ts +76 -0
- package/src/server/html-injectors.ts +103 -66
- package/src/server/index.ts +9 -4
- package/src/server/logger.ts +38 -35
- package/src/server/node-stream-transforms.ts +381 -0
- package/src/server/pipeline.ts +131 -39
- package/src/server/primitives.ts +47 -5
- package/src/server/render-timeout.ts +108 -0
- package/src/server/request-context.ts +112 -119
- package/src/server/route-element-builder.ts +106 -114
- package/src/server/route-handler.ts +2 -1
- package/src/server/route-matcher.ts +2 -2
- package/src/server/rsc-entry/error-renderer.ts +5 -3
- package/src/server/rsc-entry/helpers.ts +122 -3
- package/src/server/rsc-entry/index.ts +125 -49
- package/src/server/rsc-entry/rsc-payload.ts +52 -12
- package/src/server/rsc-entry/rsc-stream.ts +33 -8
- package/src/server/rsc-entry/ssr-renderer.ts +40 -13
- package/src/server/slot-resolver.ts +199 -210
- package/src/server/ssr-entry.ts +169 -17
- package/src/server/ssr-render.ts +266 -67
- package/src/server/tracing.ts +23 -0
- package/src/server/tree-builder.ts +91 -57
- package/src/server/types.ts +1 -3
- package/src/server/version-skew.ts +104 -0
- package/src/server/waituntil-bridge.ts +4 -1
- package/src/shared/merge-search-params.ts +48 -0
- package/src/shims/navigation-client.ts +1 -1
- package/src/shims/navigation.ts +1 -1
- package/src/utils/state-machine.ts +111 -0
- package/dist/_chunks/als-registry-B7DbZ2hS.js.map +0 -1
- package/dist/_chunks/debug-B4WUeqJ-.js +0 -75
- package/dist/_chunks/debug-B4WUeqJ-.js.map +0 -1
- package/dist/_chunks/interception-BOoWmLUA.js.map +0 -1
- package/dist/_chunks/request-context-CZJi4CuK.js.map +0 -1
- package/dist/_chunks/ssr-data-MjmprTmO.js +0 -88
- package/dist/_chunks/ssr-data-MjmprTmO.js.map +0 -1
- package/dist/_chunks/use-cookie-DX-l1_5E.js +0 -91
- package/dist/_chunks/use-cookie-DX-l1_5E.js.map +0 -1
- package/dist/client/error-boundary.js.map +0 -1
- package/dist/cookies/index.js.map +0 -1
- package/dist/plugins/dynamic-transform.d.ts +0 -72
- package/dist/plugins/dynamic-transform.d.ts.map +0 -1
- package/dist/search-params/analyze.d.ts +0 -54
- package/dist/search-params/analyze.d.ts.map +0 -1
- package/dist/search-params/builtin-codecs.d.ts +0 -105
- package/dist/search-params/builtin-codecs.d.ts.map +0 -1
- package/dist/search-params/create.d.ts +0 -106
- package/dist/search-params/create.d.ts.map +0 -1
- package/dist/search-params/index.js.map +0 -1
- package/dist/server/prerender.d.ts +0 -77
- package/dist/server/prerender.d.ts.map +0 -1
- package/dist/server/response-cache.d.ts +0 -53
- package/dist/server/response-cache.d.ts.map +0 -1
- package/src/plugins/dynamic-transform.ts +0 -161
- package/src/search-params/analyze.ts +0 -192
- package/src/search-params/builtin-codecs.ts +0 -228
- package/src/search-params/create.ts +0 -321
- package/src/server/prerender.ts +0 -139
- package/src/server/response-cache.ts +0 -277
package/dist/index.js
CHANGED
|
@@ -1,37 +1,16 @@
|
|
|
1
|
-
import { r as
|
|
2
|
-
import {
|
|
1
|
+
import { r as __toESM, t as __commonJSMin } from "./_chunks/chunk-DYhsFzuS.js";
|
|
2
|
+
import { r as setViteServer, t as formatSize } from "./_chunks/format-cX7wzEp2.js";
|
|
3
|
+
import { i as scanRoutes, n as generateRouteMap, t as collectInterceptionRewrites } from "./_chunks/interception-D2djYaIm.js";
|
|
3
4
|
import { existsSync, readFileSync } from "node:fs";
|
|
4
|
-
import { dirname, extname, join, resolve } from "node:path";
|
|
5
|
-
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
5
|
+
import { dirname, extname, join, normalize, resolve } from "node:path";
|
|
6
6
|
import { createRequire } from "node:module";
|
|
7
|
-
import react from "@vitejs/plugin-react";
|
|
7
|
+
import react, { reactCompilerPreset } from "@vitejs/plugin-react";
|
|
8
|
+
import { constants, createGzip, gzipSync } from "node:zlib";
|
|
9
|
+
import { Readable } from "node:stream";
|
|
10
|
+
import { fileURLToPath } from "node:url";
|
|
8
11
|
import { mkdir, readFile, stat, writeFile } from "node:fs/promises";
|
|
9
|
-
import { createHash } from "node:crypto";
|
|
10
|
-
import { gzipSync } from "node:zlib";
|
|
12
|
+
import { createHash, randomUUID } from "node:crypto";
|
|
11
13
|
import { performance as performance$1 } from "node:perf_hooks";
|
|
12
|
-
//#region \0rolldown/runtime.js
|
|
13
|
-
var __create = Object.create;
|
|
14
|
-
var __defProp = Object.defineProperty;
|
|
15
|
-
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
16
|
-
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
17
|
-
var __getProtoOf = Object.getPrototypeOf;
|
|
18
|
-
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
19
|
-
var __commonJSMin = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
|
|
20
|
-
var __copyProps = (to, from, except, desc) => {
|
|
21
|
-
if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
|
|
22
|
-
key = keys[i];
|
|
23
|
-
if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
|
|
24
|
-
get: ((k) => from[k]).bind(null, key),
|
|
25
|
-
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
26
|
-
});
|
|
27
|
-
}
|
|
28
|
-
return to;
|
|
29
|
-
};
|
|
30
|
-
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
|
|
31
|
-
value: mod,
|
|
32
|
-
enumerable: true
|
|
33
|
-
}) : target, mod));
|
|
34
|
-
//#endregion
|
|
35
14
|
//#region ../../node_modules/.pnpm/acorn@8.16.0/node_modules/acorn/dist/acorn.mjs
|
|
36
15
|
var astralIdentifierCodes = [
|
|
37
16
|
509,
|
|
@@ -11486,14 +11465,51 @@ function formatTerminalError(error, phase, projectRoot) {
|
|
|
11486
11465
|
return lines.join("\n");
|
|
11487
11466
|
}
|
|
11488
11467
|
/**
|
|
11468
|
+
* Format RSC debug component info into a readable string for the overlay.
|
|
11469
|
+
*
|
|
11470
|
+
* Renders the server component tree that was active when an error occurred,
|
|
11471
|
+
* including component names and source locations from stack frames. This
|
|
11472
|
+
* gives developers visibility into which server components were rendering
|
|
11473
|
+
* without exposing source code.
|
|
11474
|
+
*
|
|
11475
|
+
* Returns an empty string if no components are provided.
|
|
11476
|
+
*/
|
|
11477
|
+
function formatRscDebugContext(components) {
|
|
11478
|
+
if (!components || components.length === 0) return "";
|
|
11479
|
+
const seen = /* @__PURE__ */ new Set();
|
|
11480
|
+
const unique = [];
|
|
11481
|
+
for (const c of components) if (!seen.has(c.name)) {
|
|
11482
|
+
seen.add(c.name);
|
|
11483
|
+
unique.push(c);
|
|
11484
|
+
}
|
|
11485
|
+
const lines = ["Server Component Tree:"];
|
|
11486
|
+
for (let i = 0; i < unique.length; i++) {
|
|
11487
|
+
const c = unique[i];
|
|
11488
|
+
const indent = " ".repeat(i + 1);
|
|
11489
|
+
const envLabel = c.env ? ` [${c.env}]` : "";
|
|
11490
|
+
let locStr = "";
|
|
11491
|
+
if (c.stack && c.stack.length > 0) {
|
|
11492
|
+
const frame = c.stack[0];
|
|
11493
|
+
if (Array.isArray(frame) && frame.length >= 3) locStr = ` (${frame[1]}:${frame[2]})`;
|
|
11494
|
+
}
|
|
11495
|
+
lines.push(`${indent}${c.name}${envLabel}${locStr}`);
|
|
11496
|
+
}
|
|
11497
|
+
return lines.join("\n");
|
|
11498
|
+
}
|
|
11499
|
+
/**
|
|
11489
11500
|
* Send an error to Vite's browser overlay and log it to stderr.
|
|
11490
11501
|
*
|
|
11491
11502
|
* Uses `server.ssrFixStacktrace()` to map stack traces back to source,
|
|
11492
11503
|
* then sends the error via `server.hot.send()` for the browser overlay.
|
|
11493
11504
|
*
|
|
11505
|
+
* When `rscDebugComponents` is provided (dev mode only), the server
|
|
11506
|
+
* component tree context is appended to the error message. This helps
|
|
11507
|
+
* developers identify which server component caused the error without
|
|
11508
|
+
* exposing source code.
|
|
11509
|
+
*
|
|
11494
11510
|
* The dev server remains running — errors are handled, not fatal.
|
|
11495
11511
|
*/
|
|
11496
|
-
function sendErrorToOverlay(server, error, phase, projectRoot) {
|
|
11512
|
+
function sendErrorToOverlay(server, error, phase, projectRoot, rscDebugComponents) {
|
|
11497
11513
|
server.ssrFixStacktrace(error);
|
|
11498
11514
|
const formatted = formatTerminalError(error, phase, projectRoot);
|
|
11499
11515
|
process.stderr.write(`${formatted}\n`);
|
|
@@ -11501,6 +11517,8 @@ function sendErrorToOverlay(server, error, phase, projectRoot) {
|
|
|
11501
11517
|
const componentStack = extractComponentStack(error);
|
|
11502
11518
|
let message = error.message;
|
|
11503
11519
|
if (componentStack) message = `${error.message}\n\nComponent Stack:\n${componentStack.trim()}`;
|
|
11520
|
+
const debugContext = formatRscDebugContext(rscDebugComponents ?? []);
|
|
11521
|
+
if (debugContext) message = `${message}\n\n${debugContext}`;
|
|
11504
11522
|
try {
|
|
11505
11523
|
server.hot.send({
|
|
11506
11524
|
type: "error",
|
|
@@ -11624,12 +11642,16 @@ function compressResponse(request, response) {
|
|
|
11624
11642
|
});
|
|
11625
11643
|
}
|
|
11626
11644
|
/**
|
|
11627
|
-
* Compress a ReadableStream with gzip
|
|
11628
|
-
*
|
|
11645
|
+
* Compress a ReadableStream with gzip, flushing each chunk immediately.
|
|
11646
|
+
*
|
|
11647
|
+
* Uses node:zlib's createGzip with Z_SYNC_FLUSH to ensure each HTML chunk
|
|
11648
|
+
* (shell, Suspense resolution, RSC payload) is delivered to the browser
|
|
11649
|
+
* as soon as it's available — preserving streaming semantics.
|
|
11629
11650
|
*/
|
|
11630
11651
|
function compressWithGzip(body) {
|
|
11631
|
-
const
|
|
11632
|
-
|
|
11652
|
+
const gzip = createGzip({ flush: constants.Z_SYNC_FLUSH });
|
|
11653
|
+
Readable.fromWeb(body).pipe(gzip);
|
|
11654
|
+
return Readable.toWeb(gzip);
|
|
11633
11655
|
}
|
|
11634
11656
|
//#endregion
|
|
11635
11657
|
//#region src/plugins/dev-server.ts
|
|
@@ -11714,8 +11736,8 @@ function createTimberMiddleware(server, projectRoot) {
|
|
|
11714
11736
|
const rscModule = await rscEnv.runner.import(RSC_ENTRY_ID);
|
|
11715
11737
|
handler = rscModule.default;
|
|
11716
11738
|
const setHandler = rscModule.setDevPipelineErrorHandler;
|
|
11717
|
-
if (typeof setHandler === "function") setHandler((error) => {
|
|
11718
|
-
sendErrorToOverlay(server, error, classifyErrorPhase(error, projectRoot), projectRoot);
|
|
11739
|
+
if (typeof setHandler === "function") setHandler((error, _phase, debugComponents) => {
|
|
11740
|
+
sendErrorToOverlay(server, error, classifyErrorPhase(error, projectRoot), projectRoot, debugComponents);
|
|
11719
11741
|
});
|
|
11720
11742
|
} catch (error) {
|
|
11721
11743
|
if (error instanceof Error) sendErrorToOverlay(server, error, "module-transform", projectRoot);
|
|
@@ -11804,15 +11826,24 @@ async function sendWebResponse(nodeRes, webResponse) {
|
|
|
11804
11826
|
}
|
|
11805
11827
|
nodeRes.flushHeaders();
|
|
11806
11828
|
const reader = webResponse.body.getReader();
|
|
11829
|
+
let clientDisconnected = false;
|
|
11830
|
+
const onClose = () => {
|
|
11831
|
+
clientDisconnected = true;
|
|
11832
|
+
reader.cancel("Client disconnected").catch(() => {});
|
|
11833
|
+
};
|
|
11834
|
+
nodeRes.on("close", onClose);
|
|
11807
11835
|
try {
|
|
11808
11836
|
while (true) {
|
|
11809
11837
|
const { done, value } = await reader.read();
|
|
11810
11838
|
if (done) break;
|
|
11811
11839
|
nodeRes.write(value);
|
|
11812
11840
|
}
|
|
11841
|
+
} catch (err) {
|
|
11842
|
+
if (!clientDisconnected) throw err;
|
|
11813
11843
|
} finally {
|
|
11844
|
+
nodeRes.off("close", onClose);
|
|
11814
11845
|
reader.releaseLock();
|
|
11815
|
-
nodeRes.end();
|
|
11846
|
+
if (!nodeRes.writableEnded) nodeRes.end();
|
|
11816
11847
|
}
|
|
11817
11848
|
}
|
|
11818
11849
|
/**
|
|
@@ -11923,7 +11954,6 @@ function stripRootPrefix(id, root) {
|
|
|
11923
11954
|
* Serializes output mode and feature flags for runtime consumption.
|
|
11924
11955
|
*/
|
|
11925
11956
|
function generateConfigModule(ctx) {
|
|
11926
|
-
const cookieSecrets = ctx.config.cookies?.secrets ?? (ctx.config.cookies?.secret ? [ctx.config.cookies.secret] : void 0);
|
|
11927
11957
|
const runtimeConfig = {
|
|
11928
11958
|
output: ctx.config.output ?? "server",
|
|
11929
11959
|
csrf: ctx.config.csrf ?? true,
|
|
@@ -11932,10 +11962,11 @@ function generateConfigModule(ctx) {
|
|
|
11932
11962
|
dev: ctx.dev ?? false,
|
|
11933
11963
|
slowPhaseMs: ctx.config.dev?.slowPhaseMs ?? 200,
|
|
11934
11964
|
slowRequestMs: ctx.config.slowRequestMs ?? 3e3,
|
|
11935
|
-
cookieSecrets,
|
|
11936
11965
|
topLoader: ctx.config.topLoader,
|
|
11937
|
-
|
|
11938
|
-
|
|
11966
|
+
debug: ctx.config.debug ?? false,
|
|
11967
|
+
serverTiming: ctx.config.serverTiming,
|
|
11968
|
+
renderTimeoutMs: ctx.config.renderTimeoutMs ?? 3e4,
|
|
11969
|
+
deploymentId: ctx.deploymentId ?? null
|
|
11939
11970
|
};
|
|
11940
11971
|
return [
|
|
11941
11972
|
"// Auto-generated runtime config — do not edit.",
|
|
@@ -11953,12 +11984,14 @@ function generateConfigModule(ctx) {
|
|
|
11953
11984
|
* extensions as timber.config.ts detection.
|
|
11954
11985
|
*/
|
|
11955
11986
|
function detectInstrumentationFile(root) {
|
|
11956
|
-
|
|
11987
|
+
const extensions = [
|
|
11957
11988
|
".ts",
|
|
11958
11989
|
".js",
|
|
11959
11990
|
".mjs"
|
|
11960
|
-
]
|
|
11961
|
-
|
|
11991
|
+
];
|
|
11992
|
+
const dirs = [root, resolve(root, "src")];
|
|
11993
|
+
for (const dir of dirs) for (const ext of extensions) {
|
|
11994
|
+
const candidate = resolve(dir, `instrumentation${ext}`);
|
|
11962
11995
|
if (existsSync(candidate)) return candidate;
|
|
11963
11996
|
}
|
|
11964
11997
|
return null;
|
|
@@ -12018,17 +12051,20 @@ function timberEntries(ctx) {
|
|
|
12018
12051
|
//#region src/plugins/mdx.ts
|
|
12019
12052
|
var MDX_EXTENSIONS = ["mdx", "md"];
|
|
12020
12053
|
/**
|
|
12021
|
-
* Check if mdx-components.tsx (or .ts, .jsx, .js) exists at the project root
|
|
12054
|
+
* Check if mdx-components.tsx (or .ts, .jsx, .js) exists at the project root
|
|
12055
|
+
* or in src/. Root takes precedence, matching Next.js behavior.
|
|
12022
12056
|
* Returns the absolute path if found, otherwise undefined.
|
|
12023
12057
|
*/
|
|
12024
12058
|
function findMdxComponents(root) {
|
|
12025
|
-
|
|
12059
|
+
const candidates = [
|
|
12026
12060
|
"mdx-components.tsx",
|
|
12027
12061
|
"mdx-components.ts",
|
|
12028
12062
|
"mdx-components.jsx",
|
|
12029
12063
|
"mdx-components.js"
|
|
12030
|
-
]
|
|
12031
|
-
|
|
12064
|
+
];
|
|
12065
|
+
const dirs = [root, join(root, "src")];
|
|
12066
|
+
for (const dir of dirs) for (const name of candidates) {
|
|
12067
|
+
const p = join(dir, name);
|
|
12032
12068
|
if (existsSync(p)) return p;
|
|
12033
12069
|
}
|
|
12034
12070
|
}
|
|
@@ -12153,7 +12189,8 @@ var CLIENT_REQUIRED_EXTENSIONS = new Set([
|
|
|
12153
12189
|
* that are missing it.
|
|
12154
12190
|
*
|
|
12155
12191
|
* MDX and JSON status files are excluded — MDX files are server components
|
|
12156
|
-
* by design
|
|
12192
|
+
* by design (pre-rendered as elements via fallbackElement, see TIM-503),
|
|
12193
|
+
* and JSON files are data, not components.
|
|
12157
12194
|
*/
|
|
12158
12195
|
function lintStatusFileDirectives(tree) {
|
|
12159
12196
|
const warnings = [];
|
|
@@ -12198,7 +12235,7 @@ var RESOLVED_VIRTUAL_ID$1 = `\0${VIRTUAL_MODULE_ID$1}`;
|
|
|
12198
12235
|
/**
|
|
12199
12236
|
* File convention names we track for changes that require manifest regeneration.
|
|
12200
12237
|
*/
|
|
12201
|
-
var ROUTE_FILE_PATTERNS = /\/(page|layout|middleware|access|route|error|default|denied
|
|
12238
|
+
var ROUTE_FILE_PATTERNS = /\/(page|layout|middleware|access|route|error|default|denied|\d{3}|[45]xx|not-found|forbidden|unauthorized|sitemap|robots|manifest|favicon|icon|opengraph-image|twitter-image|apple-icon)\./;
|
|
12202
12239
|
/**
|
|
12203
12240
|
* Create the timber-routing Vite plugin.
|
|
12204
12241
|
*
|
|
@@ -12273,14 +12310,40 @@ function timberRouting(ctx) {
|
|
|
12273
12310
|
configureServer(devServer) {
|
|
12274
12311
|
rescan();
|
|
12275
12312
|
devServer.watcher.add(ctx.appDir);
|
|
12276
|
-
|
|
12313
|
+
/** Snapshot of the last generated manifest, used to detect structural changes. */
|
|
12314
|
+
let lastManifest = ctx.routeTree ? generateManifestModule(ctx.routeTree) : "";
|
|
12315
|
+
/**
|
|
12316
|
+
* Handle a route-significant file being added or removed.
|
|
12317
|
+
* Always triggers a full-reload since the route tree structure changed.
|
|
12318
|
+
*/
|
|
12319
|
+
const handleStructuralChange = (filePath) => {
|
|
12277
12320
|
if (!filePath.startsWith(ctx.appDir)) return;
|
|
12278
12321
|
if (!ROUTE_FILE_PATTERNS.test(filePath)) return;
|
|
12279
12322
|
rescan();
|
|
12323
|
+
lastManifest = ctx.routeTree ? generateManifestModule(ctx.routeTree) : "";
|
|
12280
12324
|
invalidateManifest(devServer);
|
|
12281
12325
|
};
|
|
12282
|
-
|
|
12283
|
-
|
|
12326
|
+
/**
|
|
12327
|
+
* Handle a route file's content changing.
|
|
12328
|
+
*
|
|
12329
|
+
* Most content edits (JSX changes, fixing typos) don't affect route
|
|
12330
|
+
* metadata — Vite's React Fast Refresh handles those via normal HMR.
|
|
12331
|
+
* Only rescan and full-reload when route metadata actually changed
|
|
12332
|
+
* (e.g., searchParams export added/removed, metadata export changed).
|
|
12333
|
+
*/
|
|
12334
|
+
const handleContentChange = (filePath) => {
|
|
12335
|
+
if (!filePath.startsWith(ctx.appDir)) return;
|
|
12336
|
+
if (!ROUTE_FILE_PATTERNS.test(filePath)) return;
|
|
12337
|
+
rescan();
|
|
12338
|
+
const newManifest = ctx.routeTree ? generateManifestModule(ctx.routeTree) : "";
|
|
12339
|
+
if (newManifest !== lastManifest) {
|
|
12340
|
+
lastManifest = newManifest;
|
|
12341
|
+
invalidateManifest(devServer);
|
|
12342
|
+
}
|
|
12343
|
+
};
|
|
12344
|
+
devServer.watcher.on("add", handleStructuralChange);
|
|
12345
|
+
devServer.watcher.on("unlink", handleStructuralChange);
|
|
12346
|
+
devServer.watcher.on("change", handleContentChange);
|
|
12284
12347
|
}
|
|
12285
12348
|
};
|
|
12286
12349
|
}
|
|
@@ -12361,10 +12424,6 @@ function generateManifestModule(tree) {
|
|
|
12361
12424
|
const v = addImport(node.denied);
|
|
12362
12425
|
parts.push(`${nextIndent}denied: { load: ${v}, filePath: ${JSON.stringify(node.denied.filePath)} },`);
|
|
12363
12426
|
}
|
|
12364
|
-
if (node.searchParams) {
|
|
12365
|
-
const v = addImport(node.searchParams);
|
|
12366
|
-
parts.push(`${nextIndent}searchParams: { load: ${v}, filePath: ${JSON.stringify(node.searchParams.filePath)} },`);
|
|
12367
|
-
}
|
|
12368
12427
|
if (node.statusFiles && node.statusFiles.size > 0) {
|
|
12369
12428
|
const statusEntries = [];
|
|
12370
12429
|
for (const [code, file] of node.statusFiles) {
|
|
@@ -12542,6 +12601,59 @@ export const coerce = stub;
|
|
|
12542
12601
|
};
|
|
12543
12602
|
}
|
|
12544
12603
|
//#endregion
|
|
12604
|
+
//#region src/fonts/css.ts
|
|
12605
|
+
/**
|
|
12606
|
+
* Generate a single `@font-face` CSS rule from a descriptor.
|
|
12607
|
+
*/
|
|
12608
|
+
function generateFontFace(desc) {
|
|
12609
|
+
const lines = [];
|
|
12610
|
+
lines.push("@font-face {");
|
|
12611
|
+
lines.push(` font-family: '${desc.family}';`);
|
|
12612
|
+
lines.push(` src: ${desc.src};`);
|
|
12613
|
+
if (desc.weight) lines.push(` font-weight: ${desc.weight};`);
|
|
12614
|
+
if (desc.style) lines.push(` font-style: ${desc.style};`);
|
|
12615
|
+
if (desc.display) lines.push(` font-display: ${desc.display};`);
|
|
12616
|
+
if (desc.unicodeRange) lines.push(` unicode-range: ${desc.unicodeRange};`);
|
|
12617
|
+
lines.push("}");
|
|
12618
|
+
return lines.join("\n");
|
|
12619
|
+
}
|
|
12620
|
+
/**
|
|
12621
|
+
* Generate multiple `@font-face` rules from an array of descriptors.
|
|
12622
|
+
*/
|
|
12623
|
+
function generateFontFaces(descriptors) {
|
|
12624
|
+
return descriptors.map(generateFontFace).join("\n\n");
|
|
12625
|
+
}
|
|
12626
|
+
/**
|
|
12627
|
+
* Generate a scoped CSS class that sets a CSS custom property for the font.
|
|
12628
|
+
*
|
|
12629
|
+
* Example output:
|
|
12630
|
+
* ```css
|
|
12631
|
+
* .timber-font-inter {
|
|
12632
|
+
* --font-sans: 'Inter', 'Inter Fallback', system-ui, sans-serif;
|
|
12633
|
+
* font-family: 'Inter', 'Inter Fallback', system-ui, sans-serif;
|
|
12634
|
+
* }
|
|
12635
|
+
* ```
|
|
12636
|
+
*/
|
|
12637
|
+
function generateVariableClass(className, variable, fontFamily) {
|
|
12638
|
+
return `.${className} {\n ${variable}: ${fontFamily};\n font-family: ${fontFamily};\n}`;
|
|
12639
|
+
}
|
|
12640
|
+
/**
|
|
12641
|
+
* Generate a scoped CSS class that applies font-family directly.
|
|
12642
|
+
*
|
|
12643
|
+
* Used when no `variable` is specified — the className applies
|
|
12644
|
+
* the font-family inline instead of through a CSS custom property.
|
|
12645
|
+
*
|
|
12646
|
+
* Example output:
|
|
12647
|
+
* ```css
|
|
12648
|
+
* .timber-font-inter {
|
|
12649
|
+
* font-family: 'Inter', 'Inter Fallback', system-ui, sans-serif;
|
|
12650
|
+
* }
|
|
12651
|
+
* ```
|
|
12652
|
+
*/
|
|
12653
|
+
function generateFontFamilyClass(className, fontFamily) {
|
|
12654
|
+
return `.${className} {\n font-family: ${fontFamily};\n}`;
|
|
12655
|
+
}
|
|
12656
|
+
//#endregion
|
|
12545
12657
|
//#region src/fonts/fallbacks.ts
|
|
12546
12658
|
/**
|
|
12547
12659
|
* Lookup table for commonly used Google Fonts.
|
|
@@ -12680,6 +12792,26 @@ function getGenericFamily(family) {
|
|
|
12680
12792
|
return "sans-serif";
|
|
12681
12793
|
}
|
|
12682
12794
|
/**
|
|
12795
|
+
* Generate the full CSS for a size-adjusted fallback font.
|
|
12796
|
+
*
|
|
12797
|
+
* This produces a complete @font-face block with override descriptors
|
|
12798
|
+
* that FontFaceDescriptor doesn't natively support.
|
|
12799
|
+
*/
|
|
12800
|
+
function generateFallbackCss(family) {
|
|
12801
|
+
const metrics = FALLBACK_METRICS[family.toLowerCase()];
|
|
12802
|
+
if (!metrics) return null;
|
|
12803
|
+
return [
|
|
12804
|
+
"@font-face {",
|
|
12805
|
+
` font-family: '${`${family} Fallback`}';`,
|
|
12806
|
+
` src: local('${metrics.fallbackFont}');`,
|
|
12807
|
+
` size-adjust: ${metrics.sizeAdjust}%;`,
|
|
12808
|
+
` ascent-override: ${metrics.ascentOverride}%;`,
|
|
12809
|
+
` descent-override: ${metrics.descentOverride}%;`,
|
|
12810
|
+
` line-gap-override: ${metrics.lineGapOverride}%;`,
|
|
12811
|
+
"}"
|
|
12812
|
+
].join("\n");
|
|
12813
|
+
}
|
|
12814
|
+
/**
|
|
12683
12815
|
* Check whether fallback metrics are available for a font family.
|
|
12684
12816
|
*/
|
|
12685
12817
|
function hasFallbackMetrics(family) {
|
|
@@ -13028,6 +13160,26 @@ function generateFamilyName(sources) {
|
|
|
13028
13160
|
return stem.replace(/[-_]?(Regular|Bold|Italic|Light|Medium|SemiBold|ExtraBold|Thin|Black|Heavy)$/i, "") || stem;
|
|
13029
13161
|
}
|
|
13030
13162
|
/**
|
|
13163
|
+
* Generate @font-face descriptors for local font sources.
|
|
13164
|
+
*
|
|
13165
|
+
* Each source entry produces one @font-face rule. The `src` descriptor
|
|
13166
|
+
* uses a `url()` pointing to the served path under `/_timber/fonts/`.
|
|
13167
|
+
* The `urlPrefix` defaults to `/_timber/fonts` — the path used by both
|
|
13168
|
+
* the dev server middleware and the production build output.
|
|
13169
|
+
*/
|
|
13170
|
+
function generateLocalFontFaces(family, sources, display, urlPrefix = "/_timber/fonts") {
|
|
13171
|
+
return sources.map((entry) => {
|
|
13172
|
+
const format = inferFontFormat(entry.path);
|
|
13173
|
+
return {
|
|
13174
|
+
family,
|
|
13175
|
+
src: `url('${urlPrefix}/${entry.path.split("/").pop() ?? entry.path}') format('${format}')`,
|
|
13176
|
+
weight: entry.weight,
|
|
13177
|
+
style: entry.style,
|
|
13178
|
+
display
|
|
13179
|
+
};
|
|
13180
|
+
});
|
|
13181
|
+
}
|
|
13182
|
+
/**
|
|
13031
13183
|
* Build the className for a local font, following the same convention
|
|
13032
13184
|
* as Google fonts: `timber-font-<lowercase-hyphenated-family>`.
|
|
13033
13185
|
*/
|
|
@@ -13263,6 +13415,22 @@ var VIRTUAL_LOCAL = "@timber/fonts/local";
|
|
|
13263
13415
|
var RESOLVED_GOOGLE = "\0@timber/fonts/google";
|
|
13264
13416
|
var RESOLVED_LOCAL = "\0@timber/fonts/local";
|
|
13265
13417
|
/**
|
|
13418
|
+
* Virtual side-effect module that registers font CSS on globalThis.
|
|
13419
|
+
*
|
|
13420
|
+
* When a file calls localFont() or a Google font function, the transform
|
|
13421
|
+
* hook injects `import 'virtual:timber-font-css-register'` into that file.
|
|
13422
|
+
* This virtual module sets `globalThis.__timber_font_css` with the combined
|
|
13423
|
+
* @font-face CSS. The RSC entry reads it at render time to inline a <style> tag.
|
|
13424
|
+
*
|
|
13425
|
+
* This approach avoids timing issues because:
|
|
13426
|
+
* 1. The font file is in the RSC module graph (imported by layout.tsx)
|
|
13427
|
+
* 2. The side-effect import is added to the font file during transform
|
|
13428
|
+
* 3. When layout.tsx is loaded, fonts.ts runs → side-effect module runs → globalThis is set
|
|
13429
|
+
* 4. RSC entry renders → reads globalThis → inlines <style>
|
|
13430
|
+
*/
|
|
13431
|
+
var VIRTUAL_FONT_CSS_REGISTER = "virtual:timber-font-css-register";
|
|
13432
|
+
var RESOLVED_FONT_CSS_REGISTER = "\0virtual:timber-font-css-register";
|
|
13433
|
+
/**
|
|
13266
13434
|
* Convert a font family name to a PascalCase export name.
|
|
13267
13435
|
* e.g. "JetBrains Mono" → "JetBrains_Mono"
|
|
13268
13436
|
*/
|
|
@@ -13409,6 +13577,35 @@ function generateLocalVirtualModule() {
|
|
|
13409
13577
|
].join("\n");
|
|
13410
13578
|
}
|
|
13411
13579
|
/**
|
|
13580
|
+
* Generate CSS for a single extracted font.
|
|
13581
|
+
*
|
|
13582
|
+
* Includes @font-face rules (for local fonts), fallback @font-face,
|
|
13583
|
+
* and the scoped class rule.
|
|
13584
|
+
*/
|
|
13585
|
+
function generateFontCss(font) {
|
|
13586
|
+
const cssParts = [];
|
|
13587
|
+
if (font.provider === "local" && font.localSources) {
|
|
13588
|
+
const faceCss = generateFontFaces(generateLocalFontFaces(font.family, font.localSources, font.display));
|
|
13589
|
+
if (faceCss) cssParts.push(faceCss);
|
|
13590
|
+
}
|
|
13591
|
+
const fallbackCss = generateFallbackCss(font.family);
|
|
13592
|
+
if (fallbackCss) cssParts.push(fallbackCss);
|
|
13593
|
+
if (font.variable) cssParts.push(generateVariableClass(font.className, font.variable, font.fontFamily));
|
|
13594
|
+
else cssParts.push(generateFontFamilyClass(font.className, font.fontFamily));
|
|
13595
|
+
return cssParts.join("\n\n");
|
|
13596
|
+
}
|
|
13597
|
+
/**
|
|
13598
|
+
* Generate the CSS output for all extracted fonts.
|
|
13599
|
+
*
|
|
13600
|
+
* Includes @font-face rules for local fonts, fallback @font-face rules,
|
|
13601
|
+
* and scoped classes.
|
|
13602
|
+
*/
|
|
13603
|
+
function generateAllFontCss(registry) {
|
|
13604
|
+
const cssParts = [];
|
|
13605
|
+
for (const font of registry.values()) cssParts.push(generateFontCss(font));
|
|
13606
|
+
return cssParts.join("\n\n");
|
|
13607
|
+
}
|
|
13608
|
+
/**
|
|
13412
13609
|
* Parse the local name used for the default import of `@timber/fonts/local`.
|
|
13413
13610
|
*
|
|
13414
13611
|
* Handles:
|
|
@@ -13459,15 +13656,63 @@ function timberFonts(ctx) {
|
|
|
13459
13656
|
return {
|
|
13460
13657
|
name: "timber-fonts",
|
|
13461
13658
|
resolveId(id) {
|
|
13462
|
-
|
|
13463
|
-
if (
|
|
13659
|
+
let cleanId = id.startsWith("\0") ? id.slice(1) : id;
|
|
13660
|
+
if (cleanId.startsWith(ctx.root)) {
|
|
13661
|
+
const stripped = cleanId.slice(ctx.root.length);
|
|
13662
|
+
if (stripped.startsWith("/") || stripped.startsWith("\\")) cleanId = stripped.slice(1);
|
|
13663
|
+
else cleanId = stripped;
|
|
13664
|
+
}
|
|
13665
|
+
if (cleanId === VIRTUAL_GOOGLE) return RESOLVED_GOOGLE;
|
|
13666
|
+
if (cleanId === VIRTUAL_LOCAL) return RESOLVED_LOCAL;
|
|
13667
|
+
if (cleanId === VIRTUAL_FONT_CSS_REGISTER) return RESOLVED_FONT_CSS_REGISTER;
|
|
13464
13668
|
return null;
|
|
13465
13669
|
},
|
|
13466
13670
|
load(id) {
|
|
13467
13671
|
if (id === RESOLVED_GOOGLE) return generateGoogleVirtualModule(registry);
|
|
13468
13672
|
if (id === RESOLVED_LOCAL) return generateLocalVirtualModule();
|
|
13673
|
+
if (id === RESOLVED_FONT_CSS_REGISTER) {
|
|
13674
|
+
const css = generateAllFontCss(registry);
|
|
13675
|
+
return `globalThis.__timber_font_css = ${JSON.stringify(css)};`;
|
|
13676
|
+
}
|
|
13469
13677
|
return null;
|
|
13470
13678
|
},
|
|
13679
|
+
configureServer(server) {
|
|
13680
|
+
server.middlewares.use((req, res, next) => {
|
|
13681
|
+
const url = req.url;
|
|
13682
|
+
if (!url || !url.startsWith("/_timber/fonts/")) return next();
|
|
13683
|
+
const requestedFilename = url.slice(15);
|
|
13684
|
+
if (requestedFilename.includes("..") || requestedFilename.includes("/")) {
|
|
13685
|
+
res.statusCode = 400;
|
|
13686
|
+
res.end("Bad request");
|
|
13687
|
+
return;
|
|
13688
|
+
}
|
|
13689
|
+
for (const font of registry.values()) {
|
|
13690
|
+
if (font.provider !== "local" || !font.localSources) continue;
|
|
13691
|
+
for (const src of font.localSources) if ((src.path.split("/").pop() ?? "") === requestedFilename) {
|
|
13692
|
+
const absolutePath = normalize(resolve(src.path));
|
|
13693
|
+
if (!existsSync(absolutePath)) {
|
|
13694
|
+
res.statusCode = 404;
|
|
13695
|
+
res.end("Not found");
|
|
13696
|
+
return;
|
|
13697
|
+
}
|
|
13698
|
+
const data = readFileSync(absolutePath);
|
|
13699
|
+
const ext = absolutePath.split(".").pop()?.toLowerCase();
|
|
13700
|
+
res.setHeader("Content-Type", {
|
|
13701
|
+
woff2: "font/woff2",
|
|
13702
|
+
woff: "font/woff",
|
|
13703
|
+
ttf: "font/ttf",
|
|
13704
|
+
otf: "font/otf",
|
|
13705
|
+
eot: "application/vnd.ms-fontopen"
|
|
13706
|
+
}[ext ?? ""] ?? "application/octet-stream");
|
|
13707
|
+
res.setHeader("Cache-Control", "public, max-age=31536000, immutable");
|
|
13708
|
+
res.setHeader("Access-Control-Allow-Origin", "*");
|
|
13709
|
+
res.end(data);
|
|
13710
|
+
return;
|
|
13711
|
+
}
|
|
13712
|
+
}
|
|
13713
|
+
next();
|
|
13714
|
+
});
|
|
13715
|
+
},
|
|
13471
13716
|
async buildStart() {
|
|
13472
13717
|
if (ctx.dev) return;
|
|
13473
13718
|
const googleFonts = [...registry.values()].filter((f) => f.provider === "google");
|
|
@@ -13523,10 +13768,13 @@ function timberFonts(ctx) {
|
|
|
13523
13768
|
}
|
|
13524
13769
|
}
|
|
13525
13770
|
if (hasLocalImport) transformedCode = transformLocalFonts(transformedCode, code, id, registry, this.error.bind(this));
|
|
13526
|
-
if (transformedCode !== code)
|
|
13527
|
-
|
|
13528
|
-
|
|
13529
|
-
|
|
13771
|
+
if (transformedCode !== code) {
|
|
13772
|
+
if (registry.size > 0) transformedCode = `import '${VIRTUAL_FONT_CSS_REGISTER}';\n` + transformedCode;
|
|
13773
|
+
return {
|
|
13774
|
+
code: transformedCode,
|
|
13775
|
+
map: null
|
|
13776
|
+
};
|
|
13777
|
+
}
|
|
13530
13778
|
return null;
|
|
13531
13779
|
},
|
|
13532
13780
|
generateBundle() {
|
|
@@ -13535,6 +13783,23 @@ function timberFonts(ctx) {
|
|
|
13535
13783
|
fileName: `_timber/fonts/${cf.hashedFilename}`,
|
|
13536
13784
|
source: cf.data
|
|
13537
13785
|
});
|
|
13786
|
+
for (const font of registry.values()) {
|
|
13787
|
+
if (font.provider !== "local" || !font.localSources) continue;
|
|
13788
|
+
for (const src of font.localSources) {
|
|
13789
|
+
const absolutePath = normalize(resolve(src.path));
|
|
13790
|
+
if (!existsSync(absolutePath)) {
|
|
13791
|
+
this.warn(`Local font file not found: ${absolutePath}`);
|
|
13792
|
+
continue;
|
|
13793
|
+
}
|
|
13794
|
+
const basename = src.path.split("/").pop() ?? src.path;
|
|
13795
|
+
const data = readFileSync(absolutePath);
|
|
13796
|
+
this.emitFile({
|
|
13797
|
+
type: "asset",
|
|
13798
|
+
fileName: `_timber/fonts/${basename}`,
|
|
13799
|
+
source: data
|
|
13800
|
+
});
|
|
13801
|
+
}
|
|
13802
|
+
}
|
|
13538
13803
|
if (!ctx.buildManifest) return;
|
|
13539
13804
|
const cachedByFamily = /* @__PURE__ */ new Map();
|
|
13540
13805
|
for (const cf of cachedFonts) {
|
|
@@ -13661,16 +13926,14 @@ function validateStaticMode(code, fileId, options) {
|
|
|
13661
13926
|
* - transform: Validates source files for static mode violations
|
|
13662
13927
|
*/
|
|
13663
13928
|
function timberStaticBuild(ctx) {
|
|
13664
|
-
const isStatic = ctx.config.output === "static";
|
|
13665
|
-
const clientJavascriptDisabled = ctx.clientJavascript.disabled;
|
|
13666
13929
|
return {
|
|
13667
13930
|
name: "timber-static-build",
|
|
13668
13931
|
transform(code, id) {
|
|
13669
|
-
if (!
|
|
13932
|
+
if (!(ctx.config.output === "static")) return null;
|
|
13670
13933
|
if (id.includes("node_modules")) return null;
|
|
13671
13934
|
if (!id.includes("/app/") && !id.startsWith("app/")) return null;
|
|
13672
13935
|
if (!/\.[jt]sx?$/.test(id)) return null;
|
|
13673
|
-
const errors = validateStaticMode(code, id, { clientJavascriptDisabled });
|
|
13936
|
+
const errors = validateStaticMode(code, id, { clientJavascriptDisabled: ctx.clientJavascript.disabled });
|
|
13674
13937
|
if (errors.length > 0) {
|
|
13675
13938
|
const messages = errors.map((e) => `[timber] Static mode error in ${e.file}${e.line ? `:${e.line}` : ""}: ${e.message}`);
|
|
13676
13939
|
this.error(messages.join("\n\n"));
|
|
@@ -13680,95 +13943,6 @@ function timberStaticBuild(ctx) {
|
|
|
13680
13943
|
};
|
|
13681
13944
|
}
|
|
13682
13945
|
//#endregion
|
|
13683
|
-
//#region src/plugins/dynamic-transform.ts
|
|
13684
|
-
/**
|
|
13685
|
-
* Quick check: does this source file contain 'use dynamic' anywhere?
|
|
13686
|
-
* Used as a fast bail-out before doing expensive AST parsing.
|
|
13687
|
-
*/
|
|
13688
|
-
function containsUseDynamic(code) {
|
|
13689
|
-
return containsDirective(code, "use dynamic");
|
|
13690
|
-
}
|
|
13691
|
-
/**
|
|
13692
|
-
* Find function declarations/expressions containing 'use dynamic' and
|
|
13693
|
-
* transform them into markDynamic() calls.
|
|
13694
|
-
*
|
|
13695
|
-
* Input:
|
|
13696
|
-
* ```tsx
|
|
13697
|
-
* export default async function AddToCartButton({ productId }) {
|
|
13698
|
-
* 'use dynamic'
|
|
13699
|
-
* const user = await getUser()
|
|
13700
|
-
* return <button>Add to cart</button>
|
|
13701
|
-
* }
|
|
13702
|
-
* ```
|
|
13703
|
-
*
|
|
13704
|
-
* Output:
|
|
13705
|
-
* ```tsx
|
|
13706
|
-
* import { markDynamic as __markDynamic } from '@timber-js/app/runtime';
|
|
13707
|
-
* export default async function AddToCartButton({ productId }) {
|
|
13708
|
-
* __markDynamic();
|
|
13709
|
-
* const user = await getUser()
|
|
13710
|
-
* return <button>Add to cart</button>
|
|
13711
|
-
* }
|
|
13712
|
-
* ```
|
|
13713
|
-
*
|
|
13714
|
-
* The markDynamic() call registers the component boundary as dynamic
|
|
13715
|
-
* at render time. The pre-render pass uses this to know which subtrees
|
|
13716
|
-
* to skip and leave as holes for per-request rendering.
|
|
13717
|
-
*/
|
|
13718
|
-
function transformUseDynamic(code) {
|
|
13719
|
-
if (!containsUseDynamic(code)) return null;
|
|
13720
|
-
const functions = findFunctionsWithDirective(code, "use dynamic");
|
|
13721
|
-
if (functions.length === 0) return null;
|
|
13722
|
-
let result = code;
|
|
13723
|
-
for (const fn of functions) {
|
|
13724
|
-
const cleanBody = fn.bodyContent.replace(/['"]use dynamic['"];?/, "__markDynamic();");
|
|
13725
|
-
result = result.slice(0, fn.bodyStart) + cleanBody + result.slice(fn.bodyEnd);
|
|
13726
|
-
}
|
|
13727
|
-
result = `import { markDynamic as __markDynamic } from '@timber-js/app/runtime';\n` + result;
|
|
13728
|
-
return {
|
|
13729
|
-
code: result,
|
|
13730
|
-
map: null
|
|
13731
|
-
};
|
|
13732
|
-
}
|
|
13733
|
-
/**
|
|
13734
|
-
* In `output: 'static'` mode, `'use dynamic'` is a build error.
|
|
13735
|
-
* Static mode renders everything at build time — there is no per-request
|
|
13736
|
-
* rendering to opt into.
|
|
13737
|
-
*/
|
|
13738
|
-
function validateNoDynamicInStaticMode(code) {
|
|
13739
|
-
if (!containsUseDynamic(code)) return null;
|
|
13740
|
-
const functions = findFunctionsWithDirective(code, "use dynamic");
|
|
13741
|
-
if (functions.length === 0) return null;
|
|
13742
|
-
return {
|
|
13743
|
-
message: "'use dynamic' cannot be used in static mode (output: 'static'). Static mode renders all content at build time — there is no per-request rendering. Remove the directive or switch to output: 'server'.",
|
|
13744
|
-
line: functions[functions.length - 1].directiveLine
|
|
13745
|
-
};
|
|
13746
|
-
}
|
|
13747
|
-
/**
|
|
13748
|
-
* Create the timber-dynamic-transform Vite plugin.
|
|
13749
|
-
*
|
|
13750
|
-
* In server mode: transforms 'use dynamic' into markDynamic() calls.
|
|
13751
|
-
* In static mode: rejects 'use dynamic' as a build error.
|
|
13752
|
-
*/
|
|
13753
|
-
function timberDynamicTransform(ctx) {
|
|
13754
|
-
const isStatic = ctx.config.output === "static";
|
|
13755
|
-
return {
|
|
13756
|
-
name: "timber-dynamic-transform",
|
|
13757
|
-
transform(code, id) {
|
|
13758
|
-
if (id.includes("node_modules")) return null;
|
|
13759
|
-
if (!id.includes("/app/") && !id.startsWith("app/")) return null;
|
|
13760
|
-
if (!/\.[jt]sx?$/.test(id)) return null;
|
|
13761
|
-
if (!containsUseDynamic(code)) return null;
|
|
13762
|
-
if (isStatic) {
|
|
13763
|
-
const error = validateNoDynamicInStaticMode(code);
|
|
13764
|
-
if (error) this.error(`[timber] Static mode error in ${id}${error.line ? `:${error.line}` : ""}: ${error.message}`);
|
|
13765
|
-
return null;
|
|
13766
|
-
}
|
|
13767
|
-
return transformUseDynamic(code);
|
|
13768
|
-
}
|
|
13769
|
-
};
|
|
13770
|
-
}
|
|
13771
|
-
//#endregion
|
|
13772
13946
|
//#region src/plugins/server-action-exports.ts
|
|
13773
13947
|
var jsxParser = Parser.extend((0, import_acorn_jsx.default)());
|
|
13774
13948
|
/**
|
|
@@ -13908,6 +14082,23 @@ function rewriteServerActionExportsFallback(code) {
|
|
|
13908
14082
|
}
|
|
13909
14083
|
//#endregion
|
|
13910
14084
|
//#region src/plugins/build-manifest.ts
|
|
14085
|
+
/**
|
|
14086
|
+
* timber-build-manifest — Vite sub-plugin for build asset manifest generation.
|
|
14087
|
+
*
|
|
14088
|
+
* Provides `virtual:timber-build-manifest` which exports a BuildManifest
|
|
14089
|
+
* mapping route segment file paths to their CSS, JS, and modulepreload
|
|
14090
|
+
* output chunks.
|
|
14091
|
+
*
|
|
14092
|
+
* - Dev mode: exports an empty manifest (Vite HMR handles CSS/JS).
|
|
14093
|
+
* - Build mode: virtual module reads from globalThis.__TIMBER_BUILD_MANIFEST__
|
|
14094
|
+
* at runtime. The actual manifest data is injected by the adapter via a
|
|
14095
|
+
* _timber-manifest-init.js module that runs before the RSC handler.
|
|
14096
|
+
*
|
|
14097
|
+
* The generateBundle hook (client env only) extracts CSS/JS/modulepreload
|
|
14098
|
+
* data from the Rollup bundle and populates ctx.buildManifest.
|
|
14099
|
+
*
|
|
14100
|
+
* Design docs: 18-build-system.md §"Build Manifest", 02-rendering-pipeline.md §"Early Hints"
|
|
14101
|
+
*/
|
|
13911
14102
|
var VIRTUAL_MODULE_ID = "virtual:timber-build-manifest";
|
|
13912
14103
|
var RESOLVED_VIRTUAL_ID = `\0${VIRTUAL_MODULE_ID}`;
|
|
13913
14104
|
/**
|
|
@@ -13983,6 +14174,7 @@ function timberBuildManifest(ctx) {
|
|
|
13983
14174
|
configResolved(config) {
|
|
13984
14175
|
resolvedBase = config.base;
|
|
13985
14176
|
isDev = config.command === "serve";
|
|
14177
|
+
if (!isDev && !ctx.deploymentId) ctx.deploymentId = randomUUID().replace(/-/g, "").slice(0, 16);
|
|
13986
14178
|
},
|
|
13987
14179
|
resolveId(id) {
|
|
13988
14180
|
const cleanId = id.startsWith("\0") ? id.slice(1) : id;
|
|
@@ -14253,55 +14445,115 @@ function timberChunks() {
|
|
|
14253
14445
|
return { name: "timber-chunks" };
|
|
14254
14446
|
}
|
|
14255
14447
|
//#endregion
|
|
14448
|
+
//#region src/plugins/client-chunks.ts
|
|
14449
|
+
/**
|
|
14450
|
+
* Client chunk grouping strategy for @vitejs/plugin-rsc.
|
|
14451
|
+
*
|
|
14452
|
+
* Groups client reference modules by layout boundary to balance route-scoped
|
|
14453
|
+
* code splitting with HTTP request count. A constant group name would collapse
|
|
14454
|
+
* all routes into one chunk (every page downloads every client component).
|
|
14455
|
+
* Per-serverChunk grouping creates many sub-500B files. Layout-boundary
|
|
14456
|
+
* grouping is the middle ground.
|
|
14457
|
+
*
|
|
14458
|
+
* See design/27-chunking-strategy.md, TIM-440, TIM-499.
|
|
14459
|
+
*/
|
|
14460
|
+
/**
|
|
14461
|
+
* Derive a chunk group name for a client reference module.
|
|
14462
|
+
*
|
|
14463
|
+
* Groups by the first non-group route segment under appDir so that all
|
|
14464
|
+
* client components belonging to the same layout boundary land in one
|
|
14465
|
+
* chunk. For example:
|
|
14466
|
+
* - `facade:app/dashboard/settings/page.tsx` → `"client-dashboard"`
|
|
14467
|
+
* - `facade:app/(group-a)/group-page-a/page.tsx` → `"client-group-page-a"`
|
|
14468
|
+
* - `facade:app/layout.tsx` (root layout) → `"client-shared"`
|
|
14469
|
+
* - `shared:...` (shared across chunks) → `"client-shared"`
|
|
14470
|
+
*
|
|
14471
|
+
* This balances route-scoped code splitting with HTTP request count:
|
|
14472
|
+
* fewer chunks than per-serverChunk, but still avoids downloading every
|
|
14473
|
+
* client component on every page.
|
|
14474
|
+
*/
|
|
14475
|
+
function clientChunkGroup(meta, appDir) {
|
|
14476
|
+
const { normalizedId, serverChunk } = meta;
|
|
14477
|
+
if (serverChunk.startsWith("shared:")) return "client-shared";
|
|
14478
|
+
const relPath = normalizedId.replace(/\\/g, "/");
|
|
14479
|
+
const appDirName = appDir.replace(/\\/g, "/").split("/").pop() || "app";
|
|
14480
|
+
const appIdx = relPath.indexOf(appDirName + "/");
|
|
14481
|
+
if (appIdx === -1) return "client-shared";
|
|
14482
|
+
const segments = relPath.slice(appIdx + appDirName.length + 1).split("/");
|
|
14483
|
+
for (const seg of segments) {
|
|
14484
|
+
if (seg.includes(".")) break;
|
|
14485
|
+
if (seg.startsWith("(") && seg.endsWith(")")) continue;
|
|
14486
|
+
return `client-${seg}`;
|
|
14487
|
+
}
|
|
14488
|
+
return "client-shared";
|
|
14489
|
+
}
|
|
14490
|
+
//#endregion
|
|
14256
14491
|
//#region src/plugins/server-bundle.ts
|
|
14257
14492
|
function timberServerBundle() {
|
|
14258
|
-
return [
|
|
14259
|
-
|
|
14260
|
-
|
|
14261
|
-
|
|
14262
|
-
|
|
14263
|
-
|
|
14264
|
-
|
|
14265
|
-
|
|
14266
|
-
|
|
14267
|
-
|
|
14268
|
-
|
|
14269
|
-
|
|
14270
|
-
|
|
14271
|
-
|
|
14272
|
-
|
|
14273
|
-
|
|
14274
|
-
|
|
14275
|
-
|
|
14276
|
-
|
|
14277
|
-
|
|
14278
|
-
|
|
14279
|
-
|
|
14493
|
+
return [
|
|
14494
|
+
{
|
|
14495
|
+
name: "timber-server-bundle",
|
|
14496
|
+
config(_cfg, { command }) {
|
|
14497
|
+
if (command === "serve") return { environments: {
|
|
14498
|
+
rsc: { resolve: { noExternal: ["server-only", "client-only"] } },
|
|
14499
|
+
ssr: { resolve: { noExternal: [
|
|
14500
|
+
"server-only",
|
|
14501
|
+
"client-only",
|
|
14502
|
+
"nuqs"
|
|
14503
|
+
] } }
|
|
14504
|
+
} };
|
|
14505
|
+
const serverDefine = { "process.env.NODE_ENV": JSON.stringify("production") };
|
|
14506
|
+
return {
|
|
14507
|
+
ssr: { target: "webworker" },
|
|
14508
|
+
environments: {
|
|
14509
|
+
rsc: {
|
|
14510
|
+
resolve: { noExternal: true },
|
|
14511
|
+
define: serverDefine
|
|
14512
|
+
},
|
|
14513
|
+
ssr: {
|
|
14514
|
+
resolve: { noExternal: true },
|
|
14515
|
+
define: serverDefine
|
|
14516
|
+
}
|
|
14280
14517
|
}
|
|
14281
|
-
}
|
|
14282
|
-
}
|
|
14283
|
-
}
|
|
14284
|
-
}, {
|
|
14285
|
-
name: "timber-esm-init-fix",
|
|
14286
|
-
applyToEnvironment(environment) {
|
|
14287
|
-
return environment.name === "rsc" || environment.name === "ssr";
|
|
14518
|
+
};
|
|
14519
|
+
}
|
|
14288
14520
|
},
|
|
14289
|
-
|
|
14290
|
-
|
|
14291
|
-
|
|
14292
|
-
|
|
14293
|
-
|
|
14294
|
-
|
|
14295
|
-
"
|
|
14296
|
-
|
|
14297
|
-
|
|
14298
|
-
|
|
14299
|
-
|
|
14300
|
-
|
|
14301
|
-
|
|
14302
|
-
|
|
14521
|
+
{
|
|
14522
|
+
name: "timber-esm-init-fix",
|
|
14523
|
+
applyToEnvironment(environment) {
|
|
14524
|
+
return environment.name === "rsc" || environment.name === "ssr";
|
|
14525
|
+
},
|
|
14526
|
+
renderChunk(code) {
|
|
14527
|
+
const lazy = "var __esmMin = (fn, res) => () => (fn && (res = fn(fn = 0)), res);";
|
|
14528
|
+
if (!code.includes(lazy)) return null;
|
|
14529
|
+
const eager = [
|
|
14530
|
+
"var __esmMin = (fn, res) => {",
|
|
14531
|
+
" var l = () => { if (fn) { var f = fn; try { res = f(); fn = 0; } catch(e) {} } return res; };",
|
|
14532
|
+
" l();",
|
|
14533
|
+
" return l;",
|
|
14534
|
+
"};"
|
|
14535
|
+
].join(" ");
|
|
14536
|
+
return {
|
|
14537
|
+
code: code.replace(lazy, eager),
|
|
14538
|
+
map: null
|
|
14539
|
+
};
|
|
14540
|
+
}
|
|
14541
|
+
},
|
|
14542
|
+
{
|
|
14543
|
+
name: "timber-create-require-fix",
|
|
14544
|
+
applyToEnvironment(environment) {
|
|
14545
|
+
return environment.name === "rsc" || environment.name === "ssr";
|
|
14546
|
+
},
|
|
14547
|
+
renderChunk(code) {
|
|
14548
|
+
const pattern = "createRequire(import.meta.url)";
|
|
14549
|
+
if (!code.includes(pattern)) return null;
|
|
14550
|
+
return {
|
|
14551
|
+
code: code.replace(pattern, "createRequire(import.meta.url || \"file:///app\")"),
|
|
14552
|
+
map: null
|
|
14553
|
+
};
|
|
14554
|
+
}
|
|
14303
14555
|
}
|
|
14304
|
-
|
|
14556
|
+
];
|
|
14305
14557
|
}
|
|
14306
14558
|
//#endregion
|
|
14307
14559
|
//#region src/plugins/adapter-build.ts
|
|
@@ -14323,6 +14575,7 @@ function timberAdapterBuild(ctx) {
|
|
|
14323
14575
|
modulepreload: {}
|
|
14324
14576
|
} : ctx.buildManifest;
|
|
14325
14577
|
manifestInit = `globalThis.__TIMBER_BUILD_MANIFEST__ = ${JSON.stringify(manifest)};\n`;
|
|
14578
|
+
if (ctx.deploymentId) manifestInit += `globalThis.__TIMBER_DEPLOYMENT_ID__ = ${JSON.stringify(ctx.deploymentId)};\n`;
|
|
14326
14579
|
}
|
|
14327
14580
|
if (ctx.clientJavascript.disabled) await stripJsFromRscAssetsManifests(buildDir);
|
|
14328
14581
|
const adapterConfig = {
|
|
@@ -14679,6 +14932,65 @@ function createNoopTimer() {
|
|
|
14679
14932
|
};
|
|
14680
14933
|
}
|
|
14681
14934
|
//#endregion
|
|
14935
|
+
//#region src/server/action-encryption.ts
|
|
14936
|
+
/**
|
|
14937
|
+
* Regex for safe `defineEncryptionKey` expressions.
|
|
14938
|
+
*
|
|
14939
|
+
* The RSC plugin inlines this expression verbatim into generated JavaScript.
|
|
14940
|
+
* We restrict it to `process.env.<UPPER_SNAKE_CASE>` to prevent code injection.
|
|
14941
|
+
* See "Known Security Considerations" at the top of this file.
|
|
14942
|
+
*/
|
|
14943
|
+
var SAFE_KEY_EXPR = /^process\.env\.[A-Z_][A-Z0-9_]*$/;
|
|
14944
|
+
/**
|
|
14945
|
+
* Build the `defineEncryptionKey` expression for the RSC plugin.
|
|
14946
|
+
*
|
|
14947
|
+
* The RSC plugin accepts a JavaScript expression string that will be
|
|
14948
|
+
* inlined into the encryption runtime module. At runtime, this expression
|
|
14949
|
+
* must evaluate to the base64-encoded encryption key.
|
|
14950
|
+
*
|
|
14951
|
+
* Priority:
|
|
14952
|
+
* 1. `TIMBER_ACTIONS_ENCRYPTION_KEY` env var (for cross-build key sharing
|
|
14953
|
+
* in rolling/blue-green deployments)
|
|
14954
|
+
* 2. Auto-generated at build time (RSC plugin default — embedded in bundle,
|
|
14955
|
+
* consistent across all instances of the same build)
|
|
14956
|
+
*
|
|
14957
|
+
* For env var keys, we generate a runtime expression that reads the env var.
|
|
14958
|
+
* For auto-generated keys, we return undefined and let the RSC plugin handle it.
|
|
14959
|
+
*/
|
|
14960
|
+
function resolveEncryptionKeyExpression() {
|
|
14961
|
+
const envKey = process.env.TIMBER_ACTIONS_ENCRYPTION_KEY;
|
|
14962
|
+
if (envKey) {
|
|
14963
|
+
validateKeyFormat(envKey);
|
|
14964
|
+
const expr = "process.env.TIMBER_ACTIONS_ENCRYPTION_KEY";
|
|
14965
|
+
if (!SAFE_KEY_EXPR.test(expr)) throw new Error(`Unsafe encryption key expression: ${expr}`);
|
|
14966
|
+
return expr;
|
|
14967
|
+
}
|
|
14968
|
+
}
|
|
14969
|
+
/**
|
|
14970
|
+
* Determine whether action encryption should be enabled.
|
|
14971
|
+
*
|
|
14972
|
+
* Encryption is always enabled in production. In dev mode, it's enabled
|
|
14973
|
+
* by default but can be disabled via config for debugging.
|
|
14974
|
+
*/
|
|
14975
|
+
function shouldEnableEncryption(isDev, config) {
|
|
14976
|
+
if (!isDev) return true;
|
|
14977
|
+
if (config?.disableInDev) return false;
|
|
14978
|
+
return true;
|
|
14979
|
+
}
|
|
14980
|
+
/**
|
|
14981
|
+
* Validate that a key string is a valid base64-encoded 256-bit key.
|
|
14982
|
+
* Throws a descriptive error if the key is malformed.
|
|
14983
|
+
*/
|
|
14984
|
+
function validateKeyFormat(key) {
|
|
14985
|
+
try {
|
|
14986
|
+
const bytes = atob(key).length;
|
|
14987
|
+
if (bytes !== 32) throw new Error(`TIMBER_ACTIONS_ENCRYPTION_KEY must be a base64-encoded 256-bit (32-byte) key. Got ${bytes} bytes. Generate one with: node -e "console.log(require('crypto').randomBytes(32).toString('base64'))"`);
|
|
14988
|
+
} catch (error) {
|
|
14989
|
+
if (error instanceof Error && error.message.includes("TIMBER_ACTIONS_ENCRYPTION_KEY")) throw error;
|
|
14990
|
+
throw new Error("TIMBER_ACTIONS_ENCRYPTION_KEY is not valid base64. Generate a key with: node -e \"console.log(require('crypto').randomBytes(32).toString('base64'))\"");
|
|
14991
|
+
}
|
|
14992
|
+
}
|
|
14993
|
+
//#endregion
|
|
14682
14994
|
//#region src/index.ts
|
|
14683
14995
|
/**
|
|
14684
14996
|
* Resolve `clientJavascript` into a fully resolved config.
|
|
@@ -14721,10 +15033,7 @@ function resolveAppDir(root, configAppDir) {
|
|
|
14721
15033
|
}
|
|
14722
15034
|
function createPluginContext(config, root) {
|
|
14723
15035
|
const projectRoot = root ?? process.cwd();
|
|
14724
|
-
const resolvedConfig = {
|
|
14725
|
-
output: "server",
|
|
14726
|
-
...config
|
|
14727
|
-
};
|
|
15036
|
+
const resolvedConfig = { ...config };
|
|
14728
15037
|
return {
|
|
14729
15038
|
config: resolvedConfig,
|
|
14730
15039
|
clientJavascript: resolveClientJavascript(resolvedConfig),
|
|
@@ -14733,22 +15042,28 @@ function createPluginContext(config, root) {
|
|
|
14733
15042
|
root: projectRoot,
|
|
14734
15043
|
dev: false,
|
|
14735
15044
|
buildManifest: null,
|
|
15045
|
+
deploymentId: null,
|
|
14736
15046
|
timer: createStartupTimer()
|
|
14737
15047
|
};
|
|
14738
15048
|
}
|
|
14739
15049
|
/**
|
|
14740
15050
|
* Load timber.config.ts (or .js, .mjs) from the project root.
|
|
14741
15051
|
* Returns the config object or null if no config file is found.
|
|
15052
|
+
*
|
|
15053
|
+
* Uses require() which works for ESM modules on Node 22.12+.
|
|
15054
|
+
* This keeps timber() synchronous — no async config loading needed.
|
|
14742
15055
|
*/
|
|
14743
|
-
|
|
14744
|
-
|
|
15056
|
+
function loadTimberConfigFile(root) {
|
|
15057
|
+
const configNames = [
|
|
14745
15058
|
"timber.config.ts",
|
|
14746
15059
|
"timber.config.js",
|
|
14747
15060
|
"timber.config.mjs"
|
|
14748
|
-
]
|
|
15061
|
+
];
|
|
15062
|
+
const req = createRequire(join(root, "package.json"));
|
|
15063
|
+
for (const name of configNames) {
|
|
14749
15064
|
const configPath = join(root, name);
|
|
14750
15065
|
if (existsSync(configPath)) {
|
|
14751
|
-
const mod =
|
|
15066
|
+
const mod = req(configPath);
|
|
14752
15067
|
return mod.default ?? mod;
|
|
14753
15068
|
}
|
|
14754
15069
|
}
|
|
@@ -14763,10 +15078,7 @@ async function loadTimberConfigFile(root) {
|
|
|
14763
15078
|
*/
|
|
14764
15079
|
function warnConfigConflicts(inline, fileConfig) {
|
|
14765
15080
|
const conflicts = [];
|
|
14766
|
-
for (const key of Object.keys(fileConfig))
|
|
14767
|
-
if (key === "output") continue;
|
|
14768
|
-
if (key in inline && inline[key] !== void 0) conflicts.push(key);
|
|
14769
|
-
}
|
|
15081
|
+
for (const key of Object.keys(fileConfig)) if (key in inline && inline[key] !== void 0) conflicts.push(key);
|
|
14770
15082
|
if (conflicts.length > 0) console.warn(`[timber] Config conflict: ${conflicts.map((k) => `"${k}"`).join(", ")} set in both vite.config.ts (inline) and timber.config.ts. Move all config to timber.config.ts to avoid confusion. The inline value from vite.config.ts will be used.`);
|
|
14771
15083
|
return conflicts;
|
|
14772
15084
|
}
|
|
@@ -14794,21 +15106,92 @@ function mergeFileConfig(ctx, fileConfig) {
|
|
|
14794
15106
|
} } : {}
|
|
14795
15107
|
};
|
|
14796
15108
|
}
|
|
15109
|
+
/**
|
|
15110
|
+
* Resolve the React Compiler plugin via @rolldown/plugin-babel.
|
|
15111
|
+
*
|
|
15112
|
+
* Uses the `reactCompilerPreset` from @vitejs/plugin-react, which:
|
|
15113
|
+
* - Uses Babel ONLY for the compiler pass (OXC handles JSX)
|
|
15114
|
+
* - Automatically scopes to client environment via applyToEnvironmentHook
|
|
15115
|
+
* - Uses react/compiler-runtime built into React 19
|
|
15116
|
+
*
|
|
15117
|
+
* @rolldown/plugin-babel and babel-plugin-react-compiler are optional peer deps.
|
|
15118
|
+
* If either is missing, require() fails with a clear error message.
|
|
15119
|
+
*/
|
|
15120
|
+
function resolveReactCompilerPlugin(config, req) {
|
|
15121
|
+
let babel;
|
|
15122
|
+
try {
|
|
15123
|
+
babel = req("@rolldown/plugin-babel");
|
|
15124
|
+
} catch {
|
|
15125
|
+
throw new Error("[timber] reactCompiler requires @rolldown/plugin-babel. Install it: pnpm add -D @rolldown/plugin-babel babel-plugin-react-compiler");
|
|
15126
|
+
}
|
|
15127
|
+
const options = typeof config === "object" ? config : {};
|
|
15128
|
+
return (babel.default ?? babel)({ presets: [reactCompilerPreset(options)] });
|
|
15129
|
+
}
|
|
15130
|
+
/**
|
|
15131
|
+
* Build the options object for @vitejs/plugin-rsc.
|
|
15132
|
+
*
|
|
15133
|
+
* Uses a getter for `enableActionEncryption` so the RSC plugin reads
|
|
15134
|
+
* the value lazily — after ctx.dev is set in configResolved. This lets
|
|
15135
|
+
* `actionEncryption.disableInDev` work correctly even though the RSC
|
|
15136
|
+
* plugin is created before Vite resolves the command.
|
|
15137
|
+
*/
|
|
15138
|
+
function createRscOptions(ctx, encryptionKeyExpr) {
|
|
15139
|
+
const options = {
|
|
15140
|
+
serverHandler: false,
|
|
15141
|
+
customClientEntry: true,
|
|
15142
|
+
entries: {
|
|
15143
|
+
rsc: "virtual:timber-rsc-entry",
|
|
15144
|
+
ssr: "virtual:timber-ssr-entry",
|
|
15145
|
+
client: "virtual:timber-browser-entry"
|
|
15146
|
+
},
|
|
15147
|
+
clientChunks: (meta) => clientChunkGroup(meta, ctx.appDir)
|
|
15148
|
+
};
|
|
15149
|
+
Object.defineProperty(options, "enableActionEncryption", {
|
|
15150
|
+
get() {
|
|
15151
|
+
return shouldEnableEncryption(ctx.dev, ctx.config.actionEncryption);
|
|
15152
|
+
},
|
|
15153
|
+
enumerable: true
|
|
15154
|
+
});
|
|
15155
|
+
if (encryptionKeyExpr) options.defineEncryptionKey = encryptionKeyExpr;
|
|
15156
|
+
return options;
|
|
15157
|
+
}
|
|
14797
15158
|
function timberCache(_ctx) {
|
|
14798
15159
|
return cacheTransformPlugin();
|
|
14799
15160
|
}
|
|
15161
|
+
/**
|
|
15162
|
+
* Create the timber Vite plugin array.
|
|
15163
|
+
*
|
|
15164
|
+
* Loads timber.config.ts and all dependencies synchronously before
|
|
15165
|
+
* constructing the plugin array. This ensures ALL plugins — including
|
|
15166
|
+
* the RSC plugin and React Compiler — see the fully merged config
|
|
15167
|
+
* (inline + file-based). No async, no deferred config, no stale reads.
|
|
15168
|
+
*
|
|
15169
|
+
* Requires Node >= 22.12 for synchronous require() of ESM modules
|
|
15170
|
+
* (@vitejs/plugin-rsc is ESM-only).
|
|
15171
|
+
*
|
|
15172
|
+
* Previous versions used async loading and deferred config merging,
|
|
15173
|
+
* causing file-based config for reactCompiler, actionEncryption, and
|
|
15174
|
+
* output mode to be silently ignored. See TIM-451.
|
|
15175
|
+
*/
|
|
14800
15176
|
function timber(config) {
|
|
14801
15177
|
const ctx = createPluginContext(config);
|
|
15178
|
+
const consumerRequire = createRequire(join(process.cwd(), "package.json"));
|
|
15179
|
+
ctx.timer.start("rsc-plugin-import");
|
|
15180
|
+
const rscMod = consumerRequire("@vitejs/plugin-rsc");
|
|
15181
|
+
const vitePluginRsc = rscMod.default ?? rscMod;
|
|
15182
|
+
ctx.timer.end("rsc-plugin-import");
|
|
15183
|
+
const encryptionKeyExpr = resolveEncryptionKeyExpression();
|
|
14802
15184
|
const rootSync = {
|
|
14803
15185
|
name: "timber-root-sync",
|
|
14804
|
-
|
|
14805
|
-
const
|
|
15186
|
+
config(userConfig, { command }) {
|
|
15187
|
+
const viteRoot = resolve(userConfig.root ?? process.cwd());
|
|
14806
15188
|
ctx.timer.start("config-load");
|
|
14807
|
-
const fileConfig =
|
|
15189
|
+
const fileConfig = loadTimberConfigFile(viteRoot);
|
|
14808
15190
|
if (fileConfig) {
|
|
14809
15191
|
mergeFileConfig(ctx, fileConfig);
|
|
14810
15192
|
ctx.clientJavascript = resolveClientJavascript(ctx.config);
|
|
14811
15193
|
}
|
|
15194
|
+
ctx.config.output ??= "server";
|
|
14812
15195
|
ctx.timer.end("config-load");
|
|
14813
15196
|
if (command === "build") return { oxc: { jsx: { development: false } } };
|
|
14814
15197
|
},
|
|
@@ -14820,33 +15203,31 @@ function timber(config) {
|
|
|
14820
15203
|
else ctx.timer.start("dev-server-setup");
|
|
14821
15204
|
}
|
|
14822
15205
|
};
|
|
14823
|
-
const
|
|
14824
|
-
|
|
14825
|
-
const
|
|
14826
|
-
|
|
14827
|
-
|
|
14828
|
-
|
|
14829
|
-
|
|
14830
|
-
|
|
14831
|
-
|
|
14832
|
-
|
|
14833
|
-
|
|
14834
|
-
}
|
|
14835
|
-
});
|
|
14836
|
-
});
|
|
15206
|
+
const reactCompilerPlugins = [];
|
|
15207
|
+
if (config?.reactCompiler) reactCompilerPlugins.push(resolveReactCompilerPlugin(config.reactCompiler, consumerRequire));
|
|
15208
|
+
const lazyReactCompiler = {
|
|
15209
|
+
name: "timber-react-compiler",
|
|
15210
|
+
configResolved() {
|
|
15211
|
+
if (config?.reactCompiler || !ctx.config.reactCompiler) return;
|
|
15212
|
+
const resolved = resolveReactCompilerPlugin(ctx.config.reactCompiler, consumerRequire);
|
|
15213
|
+
for (const key of Object.keys(resolved)) if (key !== "name") lazyReactCompiler[key] = resolved[key];
|
|
15214
|
+
}
|
|
15215
|
+
};
|
|
15216
|
+
// @vitejs/plugin-rsc handles:
|
|
14837
15217
|
return [
|
|
14838
15218
|
rootSync,
|
|
14839
15219
|
timberReactProd(),
|
|
14840
15220
|
react(),
|
|
15221
|
+
...reactCompilerPlugins,
|
|
15222
|
+
lazyReactCompiler,
|
|
14841
15223
|
timberServerActionExports(),
|
|
14842
|
-
|
|
15224
|
+
vitePluginRsc(createRscOptions(ctx, encryptionKeyExpr)),
|
|
14843
15225
|
timberShims(ctx),
|
|
14844
15226
|
timberRouting(ctx),
|
|
14845
15227
|
timberEntries(ctx),
|
|
14846
15228
|
timberBuildManifest(ctx),
|
|
14847
15229
|
timberCache(ctx),
|
|
14848
15230
|
timberStaticBuild(ctx),
|
|
14849
|
-
timberDynamicTransform(ctx),
|
|
14850
15231
|
timberFonts(ctx),
|
|
14851
15232
|
timberMdx(ctx),
|
|
14852
15233
|
timberContent(ctx),
|
|
@@ -14858,7 +15239,28 @@ function timber(config) {
|
|
|
14858
15239
|
timberDevServer(ctx)
|
|
14859
15240
|
];
|
|
14860
15241
|
}
|
|
15242
|
+
/**
|
|
15243
|
+
* Type-safe helper for timber.config.ts files.
|
|
15244
|
+
*
|
|
15245
|
+
* A pass-through identity function that provides autocomplete and
|
|
15246
|
+
* type checking for timber configuration. No runtime validation —
|
|
15247
|
+
* purely a DX convenience (same pattern as Vite's defineConfig).
|
|
15248
|
+
*
|
|
15249
|
+
* @example
|
|
15250
|
+
* ```ts
|
|
15251
|
+
* // timber.config.ts
|
|
15252
|
+
* import { defineConfig } from '@timber-js/app';
|
|
15253
|
+
*
|
|
15254
|
+
* export default defineConfig({
|
|
15255
|
+
* output: 'server',
|
|
15256
|
+
* pageExtensions: ['tsx', 'ts', 'mdx'],
|
|
15257
|
+
* });
|
|
15258
|
+
* ```
|
|
15259
|
+
*/
|
|
15260
|
+
function defineConfig(config) {
|
|
15261
|
+
return config;
|
|
15262
|
+
}
|
|
14861
15263
|
//#endregion
|
|
14862
|
-
export { timber as default, timber, resolveAppDir, resolveClientJavascript, warnConfigConflicts };
|
|
15264
|
+
export { timber as default, timber, defineConfig, loadTimberConfigFile, resolveAppDir, resolveClientJavascript, warnConfigConflicts };
|
|
14863
15265
|
|
|
14864
15266
|
//# sourceMappingURL=index.js.map
|