@timber-js/app 0.2.0-alpha.34 → 0.2.0-alpha.36
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/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-B3Gypr3D.js → debug-ECi_61pb.js} +1 -1
- package/dist/_chunks/{debug-B3Gypr3D.js.map → debug-ECi_61pb.js.map} +1 -1
- package/dist/_chunks/define-cookie-w5GWm_bL.js +93 -0
- package/dist/_chunks/define-cookie-w5GWm_bL.js.map +1 -0
- package/dist/_chunks/error-boundary-TYEQJZ1-.js +211 -0
- package/dist/_chunks/error-boundary-TYEQJZ1-.js.map +1 -0
- package/dist/_chunks/{format-RyoGQL74.js → format-cX7wzEp2.js} +2 -2
- package/dist/_chunks/{format-RyoGQL74.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-BQUC8PHn.js → request-context-CZz_T0Bc.js} +40 -71
- package/dist/_chunks/request-context-CZz_T0Bc.js.map +1 -0
- package/dist/_chunks/segment-context-Dpq2XOKg.js +34 -0
- package/dist/_chunks/segment-context-Dpq2XOKg.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-CemImE6h.js → tracing-BPyIzIdu.js} +2 -2
- package/dist/_chunks/{tracing-CemImE6h.js.map → tracing-BPyIzIdu.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-C1SN725w.js +331 -0
- package/dist/_chunks/wrappers-C1SN725w.js.map +1 -0
- package/dist/cache/index.js +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 +2 -2
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +193 -90
- package/dist/client/index.js.map +1 -1
- package/dist/client/link.d.ts +8 -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/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/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 -81
- package/dist/index.d.ts +87 -12
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +346 -210
- 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/entries.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/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-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 +4 -4
- package/dist/server/als-registry.d.ts.map +1 -1
- package/dist/server/build-manifest.d.ts +2 -2
- 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/form-data.d.ts +29 -0
- package/dist/server/form-data.d.ts.map +1 -1
- package/dist/server/html-injectors.d.ts +3 -9
- package/dist/server/html-injectors.d.ts.map +1 -1
- package/dist/server/index.d.ts +1 -1
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +1819 -1629
- package/dist/server/index.js.map +1 -1
- package/dist/server/node-stream-transforms.d.ts.map +1 -1
- package/dist/server/pipeline.d.ts.map +1 -1
- package/dist/server/request-context.d.ts +28 -40
- 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-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/index.d.ts.map +1 -1
- package/dist/server/rsc-entry/ssr-renderer.d.ts.map +1 -1
- package/dist/server/slot-resolver.d.ts.map +1 -1
- package/dist/server/ssr-entry.d.ts.map +1 -1
- package/dist/server/ssr-render.d.ts +3 -0
- package/dist/server/ssr-render.d.ts.map +1 -1
- package/dist/server/tree-builder.d.ts +12 -8
- 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/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 +12 -8
- package/src/client/browser-entry.ts +58 -25
- package/src/client/error-boundary.tsx +18 -1
- package/src/client/index.ts +9 -1
- package/src/client/link.tsx +9 -9
- package/src/client/navigation-context.ts +2 -2
- package/src/client/router.ts +102 -55
- package/src/client/rsc-fetch.ts +63 -2
- package/src/client/segment-cache.ts +1 -1
- package/src/client/stale-reload.ts +28 -0
- package/src/client/top-loader.tsx +2 -2
- 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/index.ts +255 -65
- 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/entries.ts +3 -6
- 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/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 +38 -8
- package/src/server/action-encryption.ts +144 -0
- package/src/server/action-handler.ts +16 -0
- package/src/server/als-registry.ts +4 -4
- package/src/server/build-manifest.ts +4 -4
- 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/form-data.ts +76 -0
- package/src/server/html-injectors.ts +50 -58
- package/src/server/index.ts +2 -4
- package/src/server/node-stream-transforms.ts +65 -54
- package/src/server/pipeline.ts +98 -26
- package/src/server/request-context.ts +49 -124
- package/src/server/route-element-builder.ts +102 -99
- package/src/server/route-matcher.ts +2 -2
- package/src/server/rsc-entry/error-renderer.ts +5 -3
- package/src/server/rsc-entry/index.ts +26 -11
- package/src/server/rsc-entry/rsc-payload.ts +2 -2
- package/src/server/rsc-entry/ssr-renderer.ts +13 -5
- package/src/server/slot-resolver.ts +204 -206
- package/src/server/ssr-entry.ts +3 -1
- package/src/server/ssr-render.ts +3 -0
- package/src/server/tree-builder.ts +84 -48
- package/src/server/types.ts +1 -3
- package/src/server/version-skew.ts +104 -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/interception-BOoWmLUA.js.map +0 -1
- package/dist/_chunks/request-context-BQUC8PHn.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/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/dist/index.js
CHANGED
|
@@ -1,38 +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
5
|
import { dirname, extname, join, normalize, resolve } from "node:path";
|
|
5
|
-
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
6
6
|
import { createRequire } from "node:module";
|
|
7
|
-
import react from "@vitejs/plugin-react";
|
|
7
|
+
import react, { reactCompilerPreset } from "@vitejs/plugin-react";
|
|
8
8
|
import { constants, createGzip, gzipSync } from "node:zlib";
|
|
9
9
|
import { Readable } from "node:stream";
|
|
10
|
+
import { fileURLToPath } from "node:url";
|
|
10
11
|
import { mkdir, readFile, stat, writeFile } from "node:fs/promises";
|
|
11
|
-
import { createHash } from "node:crypto";
|
|
12
|
+
import { createHash, randomUUID } from "node:crypto";
|
|
12
13
|
import { performance as performance$1 } from "node:perf_hooks";
|
|
13
|
-
//#region \0rolldown/runtime.js
|
|
14
|
-
var __create = Object.create;
|
|
15
|
-
var __defProp = Object.defineProperty;
|
|
16
|
-
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
17
|
-
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
18
|
-
var __getProtoOf = Object.getPrototypeOf;
|
|
19
|
-
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
20
|
-
var __commonJSMin = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
|
|
21
|
-
var __copyProps = (to, from, except, desc) => {
|
|
22
|
-
if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
|
|
23
|
-
key = keys[i];
|
|
24
|
-
if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
|
|
25
|
-
get: ((k) => from[k]).bind(null, key),
|
|
26
|
-
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
27
|
-
});
|
|
28
|
-
}
|
|
29
|
-
return to;
|
|
30
|
-
};
|
|
31
|
-
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
|
|
32
|
-
value: mod,
|
|
33
|
-
enumerable: true
|
|
34
|
-
}) : target, mod));
|
|
35
|
-
//#endregion
|
|
36
14
|
//#region ../../node_modules/.pnpm/acorn@8.16.0/node_modules/acorn/dist/acorn.mjs
|
|
37
15
|
var astralIdentifierCodes = [
|
|
38
16
|
509,
|
|
@@ -11928,7 +11906,6 @@ function stripRootPrefix(id, root) {
|
|
|
11928
11906
|
* Serializes output mode and feature flags for runtime consumption.
|
|
11929
11907
|
*/
|
|
11930
11908
|
function generateConfigModule(ctx) {
|
|
11931
|
-
const cookieSecrets = ctx.config.cookies?.secrets ?? (ctx.config.cookies?.secret ? [ctx.config.cookies.secret] : void 0);
|
|
11932
11909
|
const runtimeConfig = {
|
|
11933
11910
|
output: ctx.config.output ?? "server",
|
|
11934
11911
|
csrf: ctx.config.csrf ?? true,
|
|
@@ -11937,10 +11914,10 @@ function generateConfigModule(ctx) {
|
|
|
11937
11914
|
dev: ctx.dev ?? false,
|
|
11938
11915
|
slowPhaseMs: ctx.config.dev?.slowPhaseMs ?? 200,
|
|
11939
11916
|
slowRequestMs: ctx.config.slowRequestMs ?? 3e3,
|
|
11940
|
-
cookieSecrets,
|
|
11941
11917
|
topLoader: ctx.config.topLoader,
|
|
11942
11918
|
debug: ctx.config.debug ?? false,
|
|
11943
|
-
serverTiming: ctx.config.serverTiming
|
|
11919
|
+
serverTiming: ctx.config.serverTiming,
|
|
11920
|
+
deploymentId: ctx.deploymentId ?? null
|
|
11944
11921
|
};
|
|
11945
11922
|
return [
|
|
11946
11923
|
"// Auto-generated runtime config — do not edit.",
|
|
@@ -12163,7 +12140,8 @@ var CLIENT_REQUIRED_EXTENSIONS = new Set([
|
|
|
12163
12140
|
* that are missing it.
|
|
12164
12141
|
*
|
|
12165
12142
|
* MDX and JSON status files are excluded — MDX files are server components
|
|
12166
|
-
* by design
|
|
12143
|
+
* by design (pre-rendered as elements via fallbackElement, see TIM-503),
|
|
12144
|
+
* and JSON files are data, not components.
|
|
12167
12145
|
*/
|
|
12168
12146
|
function lintStatusFileDirectives(tree) {
|
|
12169
12147
|
const warnings = [];
|
|
@@ -12208,7 +12186,7 @@ var RESOLVED_VIRTUAL_ID$1 = `\0${VIRTUAL_MODULE_ID$1}`;
|
|
|
12208
12186
|
/**
|
|
12209
12187
|
* File convention names we track for changes that require manifest regeneration.
|
|
12210
12188
|
*/
|
|
12211
|
-
var ROUTE_FILE_PATTERNS = /\/(page|layout|middleware|access|route|error|default|denied
|
|
12189
|
+
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)\./;
|
|
12212
12190
|
/**
|
|
12213
12191
|
* Create the timber-routing Vite plugin.
|
|
12214
12192
|
*
|
|
@@ -12283,14 +12261,40 @@ function timberRouting(ctx) {
|
|
|
12283
12261
|
configureServer(devServer) {
|
|
12284
12262
|
rescan();
|
|
12285
12263
|
devServer.watcher.add(ctx.appDir);
|
|
12286
|
-
|
|
12264
|
+
/** Snapshot of the last generated manifest, used to detect structural changes. */
|
|
12265
|
+
let lastManifest = ctx.routeTree ? generateManifestModule(ctx.routeTree) : "";
|
|
12266
|
+
/**
|
|
12267
|
+
* Handle a route-significant file being added or removed.
|
|
12268
|
+
* Always triggers a full-reload since the route tree structure changed.
|
|
12269
|
+
*/
|
|
12270
|
+
const handleStructuralChange = (filePath) => {
|
|
12287
12271
|
if (!filePath.startsWith(ctx.appDir)) return;
|
|
12288
12272
|
if (!ROUTE_FILE_PATTERNS.test(filePath)) return;
|
|
12289
12273
|
rescan();
|
|
12274
|
+
lastManifest = ctx.routeTree ? generateManifestModule(ctx.routeTree) : "";
|
|
12290
12275
|
invalidateManifest(devServer);
|
|
12291
12276
|
};
|
|
12292
|
-
|
|
12293
|
-
|
|
12277
|
+
/**
|
|
12278
|
+
* Handle a route file's content changing.
|
|
12279
|
+
*
|
|
12280
|
+
* Most content edits (JSX changes, fixing typos) don't affect route
|
|
12281
|
+
* metadata — Vite's React Fast Refresh handles those via normal HMR.
|
|
12282
|
+
* Only rescan and full-reload when route metadata actually changed
|
|
12283
|
+
* (e.g., searchParams export added/removed, metadata export changed).
|
|
12284
|
+
*/
|
|
12285
|
+
const handleContentChange = (filePath) => {
|
|
12286
|
+
if (!filePath.startsWith(ctx.appDir)) return;
|
|
12287
|
+
if (!ROUTE_FILE_PATTERNS.test(filePath)) return;
|
|
12288
|
+
rescan();
|
|
12289
|
+
const newManifest = ctx.routeTree ? generateManifestModule(ctx.routeTree) : "";
|
|
12290
|
+
if (newManifest !== lastManifest) {
|
|
12291
|
+
lastManifest = newManifest;
|
|
12292
|
+
invalidateManifest(devServer);
|
|
12293
|
+
}
|
|
12294
|
+
};
|
|
12295
|
+
devServer.watcher.on("add", handleStructuralChange);
|
|
12296
|
+
devServer.watcher.on("unlink", handleStructuralChange);
|
|
12297
|
+
devServer.watcher.on("change", handleContentChange);
|
|
12294
12298
|
}
|
|
12295
12299
|
};
|
|
12296
12300
|
}
|
|
@@ -12371,10 +12375,6 @@ function generateManifestModule(tree) {
|
|
|
12371
12375
|
const v = addImport(node.denied);
|
|
12372
12376
|
parts.push(`${nextIndent}denied: { load: ${v}, filePath: ${JSON.stringify(node.denied.filePath)} },`);
|
|
12373
12377
|
}
|
|
12374
|
-
if (node.searchParams) {
|
|
12375
|
-
const v = addImport(node.searchParams);
|
|
12376
|
-
parts.push(`${nextIndent}searchParams: { load: ${v}, filePath: ${JSON.stringify(node.searchParams.filePath)} },`);
|
|
12377
|
-
}
|
|
12378
12378
|
if (node.statusFiles && node.statusFiles.size > 0) {
|
|
12379
12379
|
const statusEntries = [];
|
|
12380
12380
|
for (const [code, file] of node.statusFiles) {
|
|
@@ -13877,16 +13877,14 @@ function validateStaticMode(code, fileId, options) {
|
|
|
13877
13877
|
* - transform: Validates source files for static mode violations
|
|
13878
13878
|
*/
|
|
13879
13879
|
function timberStaticBuild(ctx) {
|
|
13880
|
-
const isStatic = ctx.config.output === "static";
|
|
13881
|
-
const clientJavascriptDisabled = ctx.clientJavascript.disabled;
|
|
13882
13880
|
return {
|
|
13883
13881
|
name: "timber-static-build",
|
|
13884
13882
|
transform(code, id) {
|
|
13885
|
-
if (!
|
|
13883
|
+
if (!(ctx.config.output === "static")) return null;
|
|
13886
13884
|
if (id.includes("node_modules")) return null;
|
|
13887
13885
|
if (!id.includes("/app/") && !id.startsWith("app/")) return null;
|
|
13888
13886
|
if (!/\.[jt]sx?$/.test(id)) return null;
|
|
13889
|
-
const errors = validateStaticMode(code, id, { clientJavascriptDisabled });
|
|
13887
|
+
const errors = validateStaticMode(code, id, { clientJavascriptDisabled: ctx.clientJavascript.disabled });
|
|
13890
13888
|
if (errors.length > 0) {
|
|
13891
13889
|
const messages = errors.map((e) => `[timber] Static mode error in ${e.file}${e.line ? `:${e.line}` : ""}: ${e.message}`);
|
|
13892
13890
|
this.error(messages.join("\n\n"));
|
|
@@ -13896,95 +13894,6 @@ function timberStaticBuild(ctx) {
|
|
|
13896
13894
|
};
|
|
13897
13895
|
}
|
|
13898
13896
|
//#endregion
|
|
13899
|
-
//#region src/plugins/dynamic-transform.ts
|
|
13900
|
-
/**
|
|
13901
|
-
* Quick check: does this source file contain 'use dynamic' anywhere?
|
|
13902
|
-
* Used as a fast bail-out before doing expensive AST parsing.
|
|
13903
|
-
*/
|
|
13904
|
-
function containsUseDynamic(code) {
|
|
13905
|
-
return containsDirective(code, "use dynamic");
|
|
13906
|
-
}
|
|
13907
|
-
/**
|
|
13908
|
-
* Find function declarations/expressions containing 'use dynamic' and
|
|
13909
|
-
* transform them into markDynamic() calls.
|
|
13910
|
-
*
|
|
13911
|
-
* Input:
|
|
13912
|
-
* ```tsx
|
|
13913
|
-
* export default async function AddToCartButton({ productId }) {
|
|
13914
|
-
* 'use dynamic'
|
|
13915
|
-
* const user = await getUser()
|
|
13916
|
-
* return <button>Add to cart</button>
|
|
13917
|
-
* }
|
|
13918
|
-
* ```
|
|
13919
|
-
*
|
|
13920
|
-
* Output:
|
|
13921
|
-
* ```tsx
|
|
13922
|
-
* import { markDynamic as __markDynamic } from '@timber-js/app/runtime';
|
|
13923
|
-
* export default async function AddToCartButton({ productId }) {
|
|
13924
|
-
* __markDynamic();
|
|
13925
|
-
* const user = await getUser()
|
|
13926
|
-
* return <button>Add to cart</button>
|
|
13927
|
-
* }
|
|
13928
|
-
* ```
|
|
13929
|
-
*
|
|
13930
|
-
* The markDynamic() call registers the component boundary as dynamic
|
|
13931
|
-
* at render time. The pre-render pass uses this to know which subtrees
|
|
13932
|
-
* to skip and leave as holes for per-request rendering.
|
|
13933
|
-
*/
|
|
13934
|
-
function transformUseDynamic(code) {
|
|
13935
|
-
if (!containsUseDynamic(code)) return null;
|
|
13936
|
-
const functions = findFunctionsWithDirective(code, "use dynamic");
|
|
13937
|
-
if (functions.length === 0) return null;
|
|
13938
|
-
let result = code;
|
|
13939
|
-
for (const fn of functions) {
|
|
13940
|
-
const cleanBody = fn.bodyContent.replace(/['"]use dynamic['"];?/, "__markDynamic();");
|
|
13941
|
-
result = result.slice(0, fn.bodyStart) + cleanBody + result.slice(fn.bodyEnd);
|
|
13942
|
-
}
|
|
13943
|
-
result = `import { markDynamic as __markDynamic } from '@timber-js/app/runtime';\n` + result;
|
|
13944
|
-
return {
|
|
13945
|
-
code: result,
|
|
13946
|
-
map: null
|
|
13947
|
-
};
|
|
13948
|
-
}
|
|
13949
|
-
/**
|
|
13950
|
-
* In `output: 'static'` mode, `'use dynamic'` is a build error.
|
|
13951
|
-
* Static mode renders everything at build time — there is no per-request
|
|
13952
|
-
* rendering to opt into.
|
|
13953
|
-
*/
|
|
13954
|
-
function validateNoDynamicInStaticMode(code) {
|
|
13955
|
-
if (!containsUseDynamic(code)) return null;
|
|
13956
|
-
const functions = findFunctionsWithDirective(code, "use dynamic");
|
|
13957
|
-
if (functions.length === 0) return null;
|
|
13958
|
-
return {
|
|
13959
|
-
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'.",
|
|
13960
|
-
line: functions[functions.length - 1].directiveLine
|
|
13961
|
-
};
|
|
13962
|
-
}
|
|
13963
|
-
/**
|
|
13964
|
-
* Create the timber-dynamic-transform Vite plugin.
|
|
13965
|
-
*
|
|
13966
|
-
* In server mode: transforms 'use dynamic' into markDynamic() calls.
|
|
13967
|
-
* In static mode: rejects 'use dynamic' as a build error.
|
|
13968
|
-
*/
|
|
13969
|
-
function timberDynamicTransform(ctx) {
|
|
13970
|
-
const isStatic = ctx.config.output === "static";
|
|
13971
|
-
return {
|
|
13972
|
-
name: "timber-dynamic-transform",
|
|
13973
|
-
transform(code, id) {
|
|
13974
|
-
if (id.includes("node_modules")) return null;
|
|
13975
|
-
if (!id.includes("/app/") && !id.startsWith("app/")) return null;
|
|
13976
|
-
if (!/\.[jt]sx?$/.test(id)) return null;
|
|
13977
|
-
if (!containsUseDynamic(code)) return null;
|
|
13978
|
-
if (isStatic) {
|
|
13979
|
-
const error = validateNoDynamicInStaticMode(code);
|
|
13980
|
-
if (error) this.error(`[timber] Static mode error in ${id}${error.line ? `:${error.line}` : ""}: ${error.message}`);
|
|
13981
|
-
return null;
|
|
13982
|
-
}
|
|
13983
|
-
return transformUseDynamic(code);
|
|
13984
|
-
}
|
|
13985
|
-
};
|
|
13986
|
-
}
|
|
13987
|
-
//#endregion
|
|
13988
13897
|
//#region src/plugins/server-action-exports.ts
|
|
13989
13898
|
var jsxParser = Parser.extend((0, import_acorn_jsx.default)());
|
|
13990
13899
|
/**
|
|
@@ -14124,6 +14033,23 @@ function rewriteServerActionExportsFallback(code) {
|
|
|
14124
14033
|
}
|
|
14125
14034
|
//#endregion
|
|
14126
14035
|
//#region src/plugins/build-manifest.ts
|
|
14036
|
+
/**
|
|
14037
|
+
* timber-build-manifest — Vite sub-plugin for build asset manifest generation.
|
|
14038
|
+
*
|
|
14039
|
+
* Provides `virtual:timber-build-manifest` which exports a BuildManifest
|
|
14040
|
+
* mapping route segment file paths to their CSS, JS, and modulepreload
|
|
14041
|
+
* output chunks.
|
|
14042
|
+
*
|
|
14043
|
+
* - Dev mode: exports an empty manifest (Vite HMR handles CSS/JS).
|
|
14044
|
+
* - Build mode: virtual module reads from globalThis.__TIMBER_BUILD_MANIFEST__
|
|
14045
|
+
* at runtime. The actual manifest data is injected by the adapter via a
|
|
14046
|
+
* _timber-manifest-init.js module that runs before the RSC handler.
|
|
14047
|
+
*
|
|
14048
|
+
* The generateBundle hook (client env only) extracts CSS/JS/modulepreload
|
|
14049
|
+
* data from the Rollup bundle and populates ctx.buildManifest.
|
|
14050
|
+
*
|
|
14051
|
+
* Design docs: 18-build-system.md §"Build Manifest", 02-rendering-pipeline.md §"Early Hints"
|
|
14052
|
+
*/
|
|
14127
14053
|
var VIRTUAL_MODULE_ID = "virtual:timber-build-manifest";
|
|
14128
14054
|
var RESOLVED_VIRTUAL_ID = `\0${VIRTUAL_MODULE_ID}`;
|
|
14129
14055
|
/**
|
|
@@ -14199,6 +14125,7 @@ function timberBuildManifest(ctx) {
|
|
|
14199
14125
|
configResolved(config) {
|
|
14200
14126
|
resolvedBase = config.base;
|
|
14201
14127
|
isDev = config.command === "serve";
|
|
14128
|
+
if (!isDev && !ctx.deploymentId) ctx.deploymentId = randomUUID().replace(/-/g, "").slice(0, 16);
|
|
14202
14129
|
},
|
|
14203
14130
|
resolveId(id) {
|
|
14204
14131
|
const cleanId = id.startsWith("\0") ? id.slice(1) : id;
|
|
@@ -14469,55 +14396,115 @@ function timberChunks() {
|
|
|
14469
14396
|
return { name: "timber-chunks" };
|
|
14470
14397
|
}
|
|
14471
14398
|
//#endregion
|
|
14399
|
+
//#region src/plugins/client-chunks.ts
|
|
14400
|
+
/**
|
|
14401
|
+
* Client chunk grouping strategy for @vitejs/plugin-rsc.
|
|
14402
|
+
*
|
|
14403
|
+
* Groups client reference modules by layout boundary to balance route-scoped
|
|
14404
|
+
* code splitting with HTTP request count. A constant group name would collapse
|
|
14405
|
+
* all routes into one chunk (every page downloads every client component).
|
|
14406
|
+
* Per-serverChunk grouping creates many sub-500B files. Layout-boundary
|
|
14407
|
+
* grouping is the middle ground.
|
|
14408
|
+
*
|
|
14409
|
+
* See design/27-chunking-strategy.md, TIM-440, TIM-499.
|
|
14410
|
+
*/
|
|
14411
|
+
/**
|
|
14412
|
+
* Derive a chunk group name for a client reference module.
|
|
14413
|
+
*
|
|
14414
|
+
* Groups by the first non-group route segment under appDir so that all
|
|
14415
|
+
* client components belonging to the same layout boundary land in one
|
|
14416
|
+
* chunk. For example:
|
|
14417
|
+
* - `facade:app/dashboard/settings/page.tsx` → `"client-dashboard"`
|
|
14418
|
+
* - `facade:app/(group-a)/group-page-a/page.tsx` → `"client-group-page-a"`
|
|
14419
|
+
* - `facade:app/layout.tsx` (root layout) → `"client-shared"`
|
|
14420
|
+
* - `shared:...` (shared across chunks) → `"client-shared"`
|
|
14421
|
+
*
|
|
14422
|
+
* This balances route-scoped code splitting with HTTP request count:
|
|
14423
|
+
* fewer chunks than per-serverChunk, but still avoids downloading every
|
|
14424
|
+
* client component on every page.
|
|
14425
|
+
*/
|
|
14426
|
+
function clientChunkGroup(meta, appDir) {
|
|
14427
|
+
const { normalizedId, serverChunk } = meta;
|
|
14428
|
+
if (serverChunk.startsWith("shared:")) return "client-shared";
|
|
14429
|
+
const relPath = normalizedId.replace(/\\/g, "/");
|
|
14430
|
+
const appDirName = appDir.replace(/\\/g, "/").split("/").pop() || "app";
|
|
14431
|
+
const appIdx = relPath.indexOf(appDirName + "/");
|
|
14432
|
+
if (appIdx === -1) return "client-shared";
|
|
14433
|
+
const segments = relPath.slice(appIdx + appDirName.length + 1).split("/");
|
|
14434
|
+
for (const seg of segments) {
|
|
14435
|
+
if (seg.includes(".")) break;
|
|
14436
|
+
if (seg.startsWith("(") && seg.endsWith(")")) continue;
|
|
14437
|
+
return `client-${seg}`;
|
|
14438
|
+
}
|
|
14439
|
+
return "client-shared";
|
|
14440
|
+
}
|
|
14441
|
+
//#endregion
|
|
14472
14442
|
//#region src/plugins/server-bundle.ts
|
|
14473
14443
|
function timberServerBundle() {
|
|
14474
|
-
return [
|
|
14475
|
-
|
|
14476
|
-
|
|
14477
|
-
|
|
14478
|
-
|
|
14479
|
-
|
|
14480
|
-
|
|
14481
|
-
|
|
14482
|
-
|
|
14483
|
-
|
|
14484
|
-
|
|
14485
|
-
|
|
14486
|
-
|
|
14487
|
-
|
|
14488
|
-
|
|
14489
|
-
|
|
14490
|
-
|
|
14491
|
-
|
|
14492
|
-
|
|
14493
|
-
|
|
14494
|
-
|
|
14495
|
-
|
|
14444
|
+
return [
|
|
14445
|
+
{
|
|
14446
|
+
name: "timber-server-bundle",
|
|
14447
|
+
config(_cfg, { command }) {
|
|
14448
|
+
if (command === "serve") return { environments: {
|
|
14449
|
+
rsc: { resolve: { noExternal: ["server-only", "client-only"] } },
|
|
14450
|
+
ssr: { resolve: { noExternal: [
|
|
14451
|
+
"server-only",
|
|
14452
|
+
"client-only",
|
|
14453
|
+
"nuqs"
|
|
14454
|
+
] } }
|
|
14455
|
+
} };
|
|
14456
|
+
const serverDefine = { "process.env.NODE_ENV": JSON.stringify("production") };
|
|
14457
|
+
return {
|
|
14458
|
+
ssr: { target: "webworker" },
|
|
14459
|
+
environments: {
|
|
14460
|
+
rsc: {
|
|
14461
|
+
resolve: { noExternal: true },
|
|
14462
|
+
define: serverDefine
|
|
14463
|
+
},
|
|
14464
|
+
ssr: {
|
|
14465
|
+
resolve: { noExternal: true },
|
|
14466
|
+
define: serverDefine
|
|
14467
|
+
}
|
|
14496
14468
|
}
|
|
14497
|
-
}
|
|
14498
|
-
}
|
|
14499
|
-
}
|
|
14500
|
-
}, {
|
|
14501
|
-
name: "timber-esm-init-fix",
|
|
14502
|
-
applyToEnvironment(environment) {
|
|
14503
|
-
return environment.name === "rsc" || environment.name === "ssr";
|
|
14469
|
+
};
|
|
14470
|
+
}
|
|
14504
14471
|
},
|
|
14505
|
-
|
|
14506
|
-
|
|
14507
|
-
|
|
14508
|
-
|
|
14509
|
-
|
|
14510
|
-
|
|
14511
|
-
"
|
|
14512
|
-
|
|
14513
|
-
|
|
14514
|
-
|
|
14515
|
-
|
|
14516
|
-
|
|
14517
|
-
|
|
14518
|
-
|
|
14472
|
+
{
|
|
14473
|
+
name: "timber-esm-init-fix",
|
|
14474
|
+
applyToEnvironment(environment) {
|
|
14475
|
+
return environment.name === "rsc" || environment.name === "ssr";
|
|
14476
|
+
},
|
|
14477
|
+
renderChunk(code) {
|
|
14478
|
+
const lazy = "var __esmMin = (fn, res) => () => (fn && (res = fn(fn = 0)), res);";
|
|
14479
|
+
if (!code.includes(lazy)) return null;
|
|
14480
|
+
const eager = [
|
|
14481
|
+
"var __esmMin = (fn, res) => {",
|
|
14482
|
+
" var l = () => { if (fn) { var f = fn; try { res = f(); fn = 0; } catch(e) {} } return res; };",
|
|
14483
|
+
" l();",
|
|
14484
|
+
" return l;",
|
|
14485
|
+
"};"
|
|
14486
|
+
].join(" ");
|
|
14487
|
+
return {
|
|
14488
|
+
code: code.replace(lazy, eager),
|
|
14489
|
+
map: null
|
|
14490
|
+
};
|
|
14491
|
+
}
|
|
14492
|
+
},
|
|
14493
|
+
{
|
|
14494
|
+
name: "timber-create-require-fix",
|
|
14495
|
+
applyToEnvironment(environment) {
|
|
14496
|
+
return environment.name === "rsc" || environment.name === "ssr";
|
|
14497
|
+
},
|
|
14498
|
+
renderChunk(code) {
|
|
14499
|
+
const pattern = "createRequire(import.meta.url)";
|
|
14500
|
+
if (!code.includes(pattern)) return null;
|
|
14501
|
+
return {
|
|
14502
|
+
code: code.replace(pattern, "createRequire(import.meta.url || \"file:///app\")"),
|
|
14503
|
+
map: null
|
|
14504
|
+
};
|
|
14505
|
+
}
|
|
14519
14506
|
}
|
|
14520
|
-
|
|
14507
|
+
];
|
|
14521
14508
|
}
|
|
14522
14509
|
//#endregion
|
|
14523
14510
|
//#region src/plugins/adapter-build.ts
|
|
@@ -14539,6 +14526,7 @@ function timberAdapterBuild(ctx) {
|
|
|
14539
14526
|
modulepreload: {}
|
|
14540
14527
|
} : ctx.buildManifest;
|
|
14541
14528
|
manifestInit = `globalThis.__TIMBER_BUILD_MANIFEST__ = ${JSON.stringify(manifest)};\n`;
|
|
14529
|
+
if (ctx.deploymentId) manifestInit += `globalThis.__TIMBER_DEPLOYMENT_ID__ = ${JSON.stringify(ctx.deploymentId)};\n`;
|
|
14542
14530
|
}
|
|
14543
14531
|
if (ctx.clientJavascript.disabled) await stripJsFromRscAssetsManifests(buildDir);
|
|
14544
14532
|
const adapterConfig = {
|
|
@@ -14895,6 +14883,65 @@ function createNoopTimer() {
|
|
|
14895
14883
|
};
|
|
14896
14884
|
}
|
|
14897
14885
|
//#endregion
|
|
14886
|
+
//#region src/server/action-encryption.ts
|
|
14887
|
+
/**
|
|
14888
|
+
* Regex for safe `defineEncryptionKey` expressions.
|
|
14889
|
+
*
|
|
14890
|
+
* The RSC plugin inlines this expression verbatim into generated JavaScript.
|
|
14891
|
+
* We restrict it to `process.env.<UPPER_SNAKE_CASE>` to prevent code injection.
|
|
14892
|
+
* See "Known Security Considerations" at the top of this file.
|
|
14893
|
+
*/
|
|
14894
|
+
var SAFE_KEY_EXPR = /^process\.env\.[A-Z_][A-Z0-9_]*$/;
|
|
14895
|
+
/**
|
|
14896
|
+
* Build the `defineEncryptionKey` expression for the RSC plugin.
|
|
14897
|
+
*
|
|
14898
|
+
* The RSC plugin accepts a JavaScript expression string that will be
|
|
14899
|
+
* inlined into the encryption runtime module. At runtime, this expression
|
|
14900
|
+
* must evaluate to the base64-encoded encryption key.
|
|
14901
|
+
*
|
|
14902
|
+
* Priority:
|
|
14903
|
+
* 1. `TIMBER_ACTIONS_ENCRYPTION_KEY` env var (for cross-build key sharing
|
|
14904
|
+
* in rolling/blue-green deployments)
|
|
14905
|
+
* 2. Auto-generated at build time (RSC plugin default — embedded in bundle,
|
|
14906
|
+
* consistent across all instances of the same build)
|
|
14907
|
+
*
|
|
14908
|
+
* For env var keys, we generate a runtime expression that reads the env var.
|
|
14909
|
+
* For auto-generated keys, we return undefined and let the RSC plugin handle it.
|
|
14910
|
+
*/
|
|
14911
|
+
function resolveEncryptionKeyExpression() {
|
|
14912
|
+
const envKey = process.env.TIMBER_ACTIONS_ENCRYPTION_KEY;
|
|
14913
|
+
if (envKey) {
|
|
14914
|
+
validateKeyFormat(envKey);
|
|
14915
|
+
const expr = "process.env.TIMBER_ACTIONS_ENCRYPTION_KEY";
|
|
14916
|
+
if (!SAFE_KEY_EXPR.test(expr)) throw new Error(`Unsafe encryption key expression: ${expr}`);
|
|
14917
|
+
return expr;
|
|
14918
|
+
}
|
|
14919
|
+
}
|
|
14920
|
+
/**
|
|
14921
|
+
* Determine whether action encryption should be enabled.
|
|
14922
|
+
*
|
|
14923
|
+
* Encryption is always enabled in production. In dev mode, it's enabled
|
|
14924
|
+
* by default but can be disabled via config for debugging.
|
|
14925
|
+
*/
|
|
14926
|
+
function shouldEnableEncryption(isDev, config) {
|
|
14927
|
+
if (!isDev) return true;
|
|
14928
|
+
if (config?.disableInDev) return false;
|
|
14929
|
+
return true;
|
|
14930
|
+
}
|
|
14931
|
+
/**
|
|
14932
|
+
* Validate that a key string is a valid base64-encoded 256-bit key.
|
|
14933
|
+
* Throws a descriptive error if the key is malformed.
|
|
14934
|
+
*/
|
|
14935
|
+
function validateKeyFormat(key) {
|
|
14936
|
+
try {
|
|
14937
|
+
const bytes = atob(key).length;
|
|
14938
|
+
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'))"`);
|
|
14939
|
+
} catch (error) {
|
|
14940
|
+
if (error instanceof Error && error.message.includes("TIMBER_ACTIONS_ENCRYPTION_KEY")) throw error;
|
|
14941
|
+
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'))\"");
|
|
14942
|
+
}
|
|
14943
|
+
}
|
|
14944
|
+
//#endregion
|
|
14898
14945
|
//#region src/index.ts
|
|
14899
14946
|
/**
|
|
14900
14947
|
* Resolve `clientJavascript` into a fully resolved config.
|
|
@@ -14937,10 +14984,7 @@ function resolveAppDir(root, configAppDir) {
|
|
|
14937
14984
|
}
|
|
14938
14985
|
function createPluginContext(config, root) {
|
|
14939
14986
|
const projectRoot = root ?? process.cwd();
|
|
14940
|
-
const resolvedConfig = {
|
|
14941
|
-
output: "server",
|
|
14942
|
-
...config
|
|
14943
|
-
};
|
|
14987
|
+
const resolvedConfig = { ...config };
|
|
14944
14988
|
return {
|
|
14945
14989
|
config: resolvedConfig,
|
|
14946
14990
|
clientJavascript: resolveClientJavascript(resolvedConfig),
|
|
@@ -14949,22 +14993,28 @@ function createPluginContext(config, root) {
|
|
|
14949
14993
|
root: projectRoot,
|
|
14950
14994
|
dev: false,
|
|
14951
14995
|
buildManifest: null,
|
|
14996
|
+
deploymentId: null,
|
|
14952
14997
|
timer: createStartupTimer()
|
|
14953
14998
|
};
|
|
14954
14999
|
}
|
|
14955
15000
|
/**
|
|
14956
15001
|
* Load timber.config.ts (or .js, .mjs) from the project root.
|
|
14957
15002
|
* Returns the config object or null if no config file is found.
|
|
15003
|
+
*
|
|
15004
|
+
* Uses require() which works for ESM modules on Node 22.12+.
|
|
15005
|
+
* This keeps timber() synchronous — no async config loading needed.
|
|
14958
15006
|
*/
|
|
14959
|
-
|
|
14960
|
-
|
|
15007
|
+
function loadTimberConfigFile(root) {
|
|
15008
|
+
const configNames = [
|
|
14961
15009
|
"timber.config.ts",
|
|
14962
15010
|
"timber.config.js",
|
|
14963
15011
|
"timber.config.mjs"
|
|
14964
|
-
]
|
|
15012
|
+
];
|
|
15013
|
+
const req = createRequire(join(root, "package.json"));
|
|
15014
|
+
for (const name of configNames) {
|
|
14965
15015
|
const configPath = join(root, name);
|
|
14966
15016
|
if (existsSync(configPath)) {
|
|
14967
|
-
const mod =
|
|
15017
|
+
const mod = req(configPath);
|
|
14968
15018
|
return mod.default ?? mod;
|
|
14969
15019
|
}
|
|
14970
15020
|
}
|
|
@@ -14979,10 +15029,7 @@ async function loadTimberConfigFile(root) {
|
|
|
14979
15029
|
*/
|
|
14980
15030
|
function warnConfigConflicts(inline, fileConfig) {
|
|
14981
15031
|
const conflicts = [];
|
|
14982
|
-
for (const key of Object.keys(fileConfig))
|
|
14983
|
-
if (key === "output") continue;
|
|
14984
|
-
if (key in inline && inline[key] !== void 0) conflicts.push(key);
|
|
14985
|
-
}
|
|
15032
|
+
for (const key of Object.keys(fileConfig)) if (key in inline && inline[key] !== void 0) conflicts.push(key);
|
|
14986
15033
|
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.`);
|
|
14987
15034
|
return conflicts;
|
|
14988
15035
|
}
|
|
@@ -15010,21 +15057,92 @@ function mergeFileConfig(ctx, fileConfig) {
|
|
|
15010
15057
|
} } : {}
|
|
15011
15058
|
};
|
|
15012
15059
|
}
|
|
15060
|
+
/**
|
|
15061
|
+
* Resolve the React Compiler plugin via @rolldown/plugin-babel.
|
|
15062
|
+
*
|
|
15063
|
+
* Uses the `reactCompilerPreset` from @vitejs/plugin-react, which:
|
|
15064
|
+
* - Uses Babel ONLY for the compiler pass (OXC handles JSX)
|
|
15065
|
+
* - Automatically scopes to client environment via applyToEnvironmentHook
|
|
15066
|
+
* - Uses react/compiler-runtime built into React 19
|
|
15067
|
+
*
|
|
15068
|
+
* @rolldown/plugin-babel and babel-plugin-react-compiler are optional peer deps.
|
|
15069
|
+
* If either is missing, require() fails with a clear error message.
|
|
15070
|
+
*/
|
|
15071
|
+
function resolveReactCompilerPlugin(config, req) {
|
|
15072
|
+
let babel;
|
|
15073
|
+
try {
|
|
15074
|
+
babel = req("@rolldown/plugin-babel");
|
|
15075
|
+
} catch {
|
|
15076
|
+
throw new Error("[timber] reactCompiler requires @rolldown/plugin-babel. Install it: pnpm add -D @rolldown/plugin-babel babel-plugin-react-compiler");
|
|
15077
|
+
}
|
|
15078
|
+
const options = typeof config === "object" ? config : {};
|
|
15079
|
+
return (babel.default ?? babel)({ presets: [reactCompilerPreset(options)] });
|
|
15080
|
+
}
|
|
15081
|
+
/**
|
|
15082
|
+
* Build the options object for @vitejs/plugin-rsc.
|
|
15083
|
+
*
|
|
15084
|
+
* Uses a getter for `enableActionEncryption` so the RSC plugin reads
|
|
15085
|
+
* the value lazily — after ctx.dev is set in configResolved. This lets
|
|
15086
|
+
* `actionEncryption.disableInDev` work correctly even though the RSC
|
|
15087
|
+
* plugin is created before Vite resolves the command.
|
|
15088
|
+
*/
|
|
15089
|
+
function createRscOptions(ctx, encryptionKeyExpr) {
|
|
15090
|
+
const options = {
|
|
15091
|
+
serverHandler: false,
|
|
15092
|
+
customClientEntry: true,
|
|
15093
|
+
entries: {
|
|
15094
|
+
rsc: "virtual:timber-rsc-entry",
|
|
15095
|
+
ssr: "virtual:timber-ssr-entry",
|
|
15096
|
+
client: "virtual:timber-browser-entry"
|
|
15097
|
+
},
|
|
15098
|
+
clientChunks: (meta) => clientChunkGroup(meta, ctx.appDir)
|
|
15099
|
+
};
|
|
15100
|
+
Object.defineProperty(options, "enableActionEncryption", {
|
|
15101
|
+
get() {
|
|
15102
|
+
return shouldEnableEncryption(ctx.dev, ctx.config.actionEncryption);
|
|
15103
|
+
},
|
|
15104
|
+
enumerable: true
|
|
15105
|
+
});
|
|
15106
|
+
if (encryptionKeyExpr) options.defineEncryptionKey = encryptionKeyExpr;
|
|
15107
|
+
return options;
|
|
15108
|
+
}
|
|
15013
15109
|
function timberCache(_ctx) {
|
|
15014
15110
|
return cacheTransformPlugin();
|
|
15015
15111
|
}
|
|
15112
|
+
/**
|
|
15113
|
+
* Create the timber Vite plugin array.
|
|
15114
|
+
*
|
|
15115
|
+
* Loads timber.config.ts and all dependencies synchronously before
|
|
15116
|
+
* constructing the plugin array. This ensures ALL plugins — including
|
|
15117
|
+
* the RSC plugin and React Compiler — see the fully merged config
|
|
15118
|
+
* (inline + file-based). No async, no deferred config, no stale reads.
|
|
15119
|
+
*
|
|
15120
|
+
* Requires Node >= 22.12 for synchronous require() of ESM modules
|
|
15121
|
+
* (@vitejs/plugin-rsc is ESM-only).
|
|
15122
|
+
*
|
|
15123
|
+
* Previous versions used async loading and deferred config merging,
|
|
15124
|
+
* causing file-based config for reactCompiler, actionEncryption, and
|
|
15125
|
+
* output mode to be silently ignored. See TIM-451.
|
|
15126
|
+
*/
|
|
15016
15127
|
function timber(config) {
|
|
15017
15128
|
const ctx = createPluginContext(config);
|
|
15129
|
+
const consumerRequire = createRequire(join(process.cwd(), "package.json"));
|
|
15130
|
+
ctx.timer.start("rsc-plugin-import");
|
|
15131
|
+
const rscMod = consumerRequire("@vitejs/plugin-rsc");
|
|
15132
|
+
const vitePluginRsc = rscMod.default ?? rscMod;
|
|
15133
|
+
ctx.timer.end("rsc-plugin-import");
|
|
15134
|
+
const encryptionKeyExpr = resolveEncryptionKeyExpression();
|
|
15018
15135
|
const rootSync = {
|
|
15019
15136
|
name: "timber-root-sync",
|
|
15020
|
-
|
|
15021
|
-
const
|
|
15137
|
+
config(userConfig, { command }) {
|
|
15138
|
+
const viteRoot = resolve(userConfig.root ?? process.cwd());
|
|
15022
15139
|
ctx.timer.start("config-load");
|
|
15023
|
-
const fileConfig =
|
|
15140
|
+
const fileConfig = loadTimberConfigFile(viteRoot);
|
|
15024
15141
|
if (fileConfig) {
|
|
15025
15142
|
mergeFileConfig(ctx, fileConfig);
|
|
15026
15143
|
ctx.clientJavascript = resolveClientJavascript(ctx.config);
|
|
15027
15144
|
}
|
|
15145
|
+
ctx.config.output ??= "server";
|
|
15028
15146
|
ctx.timer.end("config-load");
|
|
15029
15147
|
if (command === "build") return { oxc: { jsx: { development: false } } };
|
|
15030
15148
|
},
|
|
@@ -15036,34 +15154,31 @@ function timber(config) {
|
|
|
15036
15154
|
else ctx.timer.start("dev-server-setup");
|
|
15037
15155
|
}
|
|
15038
15156
|
};
|
|
15039
|
-
const
|
|
15040
|
-
|
|
15041
|
-
const
|
|
15042
|
-
|
|
15043
|
-
|
|
15044
|
-
|
|
15045
|
-
|
|
15046
|
-
|
|
15047
|
-
|
|
15048
|
-
|
|
15049
|
-
|
|
15050
|
-
},
|
|
15051
|
-
clientChunks: () => "client-refs"
|
|
15052
|
-
});
|
|
15053
|
-
});
|
|
15157
|
+
const reactCompilerPlugins = [];
|
|
15158
|
+
if (config?.reactCompiler) reactCompilerPlugins.push(resolveReactCompilerPlugin(config.reactCompiler, consumerRequire));
|
|
15159
|
+
const lazyReactCompiler = {
|
|
15160
|
+
name: "timber-react-compiler",
|
|
15161
|
+
configResolved() {
|
|
15162
|
+
if (config?.reactCompiler || !ctx.config.reactCompiler) return;
|
|
15163
|
+
const resolved = resolveReactCompilerPlugin(ctx.config.reactCompiler, consumerRequire);
|
|
15164
|
+
for (const key of Object.keys(resolved)) if (key !== "name") lazyReactCompiler[key] = resolved[key];
|
|
15165
|
+
}
|
|
15166
|
+
};
|
|
15167
|
+
// @vitejs/plugin-rsc handles:
|
|
15054
15168
|
return [
|
|
15055
15169
|
rootSync,
|
|
15056
15170
|
timberReactProd(),
|
|
15057
15171
|
react(),
|
|
15172
|
+
...reactCompilerPlugins,
|
|
15173
|
+
lazyReactCompiler,
|
|
15058
15174
|
timberServerActionExports(),
|
|
15059
|
-
|
|
15175
|
+
vitePluginRsc(createRscOptions(ctx, encryptionKeyExpr)),
|
|
15060
15176
|
timberShims(ctx),
|
|
15061
15177
|
timberRouting(ctx),
|
|
15062
15178
|
timberEntries(ctx),
|
|
15063
15179
|
timberBuildManifest(ctx),
|
|
15064
15180
|
timberCache(ctx),
|
|
15065
15181
|
timberStaticBuild(ctx),
|
|
15066
|
-
timberDynamicTransform(ctx),
|
|
15067
15182
|
timberFonts(ctx),
|
|
15068
15183
|
timberMdx(ctx),
|
|
15069
15184
|
timberContent(ctx),
|
|
@@ -15075,7 +15190,28 @@ function timber(config) {
|
|
|
15075
15190
|
timberDevServer(ctx)
|
|
15076
15191
|
];
|
|
15077
15192
|
}
|
|
15193
|
+
/**
|
|
15194
|
+
* Type-safe helper for timber.config.ts files.
|
|
15195
|
+
*
|
|
15196
|
+
* A pass-through identity function that provides autocomplete and
|
|
15197
|
+
* type checking for timber configuration. No runtime validation —
|
|
15198
|
+
* purely a DX convenience (same pattern as Vite's defineConfig).
|
|
15199
|
+
*
|
|
15200
|
+
* @example
|
|
15201
|
+
* ```ts
|
|
15202
|
+
* // timber.config.ts
|
|
15203
|
+
* import { defineConfig } from '@timber-js/app';
|
|
15204
|
+
*
|
|
15205
|
+
* export default defineConfig({
|
|
15206
|
+
* output: 'server',
|
|
15207
|
+
* pageExtensions: ['tsx', 'ts', 'mdx'],
|
|
15208
|
+
* });
|
|
15209
|
+
* ```
|
|
15210
|
+
*/
|
|
15211
|
+
function defineConfig(config) {
|
|
15212
|
+
return config;
|
|
15213
|
+
}
|
|
15078
15214
|
//#endregion
|
|
15079
|
-
export { timber as default, timber, resolveAppDir, resolveClientJavascript, warnConfigConflicts };
|
|
15215
|
+
export { timber as default, timber, defineConfig, loadTimberConfigFile, resolveAppDir, resolveClientJavascript, warnConfigConflicts };
|
|
15080
15216
|
|
|
15081
15217
|
//# sourceMappingURL=index.js.map
|