@timber-js/app 0.2.0-alpha.186 → 0.2.0-alpha.188
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/{actions-C-Rw9vPc.js → actions-35jnMdeJ.js} +4 -3
- package/dist/_chunks/{actions-C-Rw9vPc.js.map → actions-35jnMdeJ.js.map} +1 -1
- package/dist/_chunks/als-registry-C6kcfprT.js +41 -0
- package/dist/_chunks/als-registry-C6kcfprT.js.map +1 -0
- package/dist/_chunks/{als-slots-mFweg276.js → als-slots-BEEIPKYm.js} +3 -4
- package/dist/_chunks/{als-slots-mFweg276.js.map → als-slots-BEEIPKYm.js.map} +1 -1
- package/dist/_chunks/{cache-api-Cd0VZ_Pd.js → cache-api-DjNrIWRR.js} +7 -13
- package/dist/_chunks/cache-api-DjNrIWRR.js.map +1 -0
- package/dist/_chunks/cli-check-CpmN7Nh-.js +256 -0
- package/dist/_chunks/cli-check-CpmN7Nh-.js.map +1 -0
- package/dist/_chunks/cli-schema-sync-CGMp_Psg.js +298 -0
- package/dist/_chunks/cli-schema-sync-CGMp_Psg.js.map +1 -0
- package/dist/_chunks/{cloudflare-Cs0uZXea.js → cloudflare-CGP6BZKO.js} +4 -3
- package/dist/_chunks/{cloudflare-Cs0uZXea.js.map → cloudflare-CGP6BZKO.js.map} +1 -1
- package/dist/_chunks/convention-lint-kXsgc_-7.js +784 -0
- package/dist/_chunks/convention-lint-kXsgc_-7.js.map +1 -0
- package/dist/_chunks/{error-boundary-DpYRI_I1.js → error-boundary-D-ODYX41.js} +25 -2
- package/dist/_chunks/error-boundary-D-ODYX41.js.map +1 -0
- package/dist/_chunks/file-cache-DmX7OqZP.js +454 -0
- package/dist/_chunks/file-cache-DmX7OqZP.js.map +1 -0
- package/dist/_chunks/{logger-N7e5auP0.js → logger-D8xJZXIN.js} +27 -40
- package/dist/_chunks/logger-D8xJZXIN.js.map +1 -0
- package/dist/_chunks/mdx-file-C005ay-P.js +25 -0
- package/dist/_chunks/mdx-file-C005ay-P.js.map +1 -0
- package/dist/_chunks/{plugin-context-DEGLSJs3.js → plugin-context-rCinWLiE.js} +7 -2
- package/dist/_chunks/{plugin-context-DEGLSJs3.js.map → plugin-context-rCinWLiE.js.map} +1 -1
- package/dist/_chunks/purge-store-Byr8XOjU.js +14 -0
- package/dist/_chunks/purge-store-Byr8XOjU.js.map +1 -0
- package/dist/_chunks/{resolve-schema-Dz3fcFUo.js → resolve-schema-5ma5pp1b.js} +2 -2
- package/dist/_chunks/{resolve-schema-Dz3fcFUo.js.map → resolve-schema-5ma5pp1b.js.map} +1 -1
- package/dist/_chunks/rsc-media-type-DRqE_lD_.js +46 -0
- package/dist/_chunks/rsc-media-type-DRqE_lD_.js.map +1 -0
- package/dist/_chunks/{cli-schema-sync-CKgHC2MB.js → scanner-C8b0Gcw3.js} +5 -298
- package/dist/_chunks/scanner-C8b0Gcw3.js.map +1 -0
- package/dist/_chunks/schema-bridge-Cc2Gngu1.js +199 -0
- package/dist/_chunks/schema-bridge-Cc2Gngu1.js.map +1 -0
- package/dist/_chunks/segment-classify-C539Pa2O.js.map +1 -1
- package/dist/_chunks/{param-value-C8TNYchQ.js → segment-context-CjOlyB8Y.js} +33 -2
- package/dist/_chunks/segment-context-CjOlyB8Y.js.map +1 -0
- package/dist/_chunks/segment-keys-BawYuNFO.js.map +1 -1
- package/dist/_chunks/{use-query-states-DFvWd-EA.js → use-query-states-BbU5Ge1V.js} +74 -26
- package/dist/_chunks/use-query-states-BbU5Ge1V.js.map +1 -0
- package/dist/_chunks/{navigation-root-B29qg0_T.js → use-segment-params-C4r4BD9T.js} +129 -5
- package/dist/_chunks/use-segment-params-C4r4BD9T.js.map +1 -0
- package/dist/_chunks/walkers-RzN6AFjr.js +141 -0
- package/dist/_chunks/walkers-RzN6AFjr.js.map +1 -0
- package/dist/adapters/cloudflare-dev.js +1 -1
- package/dist/adapters/cloudflare-kv-cache.js +1 -1
- package/dist/adapters/cloudflare.d.ts.map +1 -1
- package/dist/adapters/cloudflare.js +1 -1
- package/dist/adapters/compress-module.d.ts +12 -0
- package/dist/adapters/compress-module.d.ts.map +1 -1
- package/dist/adapters/nitro.js +54 -2
- package/dist/adapters/nitro.js.map +1 -1
- package/dist/cache/index.js +1 -1
- package/dist/cdn/cloudflare-purge.js +30 -0
- package/dist/cdn/cloudflare-purge.js.map +1 -0
- package/dist/cdn/fastly-purge.js +33 -0
- package/dist/cdn/fastly-purge.js.map +1 -0
- package/dist/cdn/index.js +94 -0
- package/dist/cdn/index.js.map +1 -0
- package/dist/cdn/workers-cache-purge.js +35 -0
- package/dist/cdn/workers-cache-purge.js.map +1 -0
- package/dist/cli-check.d.ts +153 -0
- package/dist/cli-check.d.ts.map +1 -0
- package/dist/cli.d.ts +34 -8
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +46 -21
- package/dist/cli.js.map +1 -1
- package/dist/client/browser-entry/action-dispatch.d.ts.map +1 -1
- package/dist/client/browser-entry/index.d.ts +1 -1
- package/dist/client/browser-entry/index.d.ts.map +1 -1
- package/dist/client/error-boundary.d.ts +6 -0
- package/dist/client/error-boundary.d.ts.map +1 -1
- package/dist/client/error-boundary.js +1 -1
- package/dist/client/index.d.ts +1 -0
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +27 -33
- package/dist/client/index.js.map +1 -1
- package/dist/client/internal.js +33 -17
- package/dist/client/internal.js.map +1 -1
- package/dist/client/link.d.ts +1 -7
- package/dist/client/link.d.ts.map +1 -1
- package/dist/client/navigation-commit.d.ts +16 -0
- package/dist/client/navigation-commit.d.ts.map +1 -1
- package/dist/client/router-pipeline.d.ts.map +1 -1
- package/dist/client/router.d.ts.map +1 -1
- package/dist/client/rsc-fetch.d.ts +9 -1
- package/dist/client/rsc-fetch.d.ts.map +1 -1
- package/dist/client/segment-cache.d.ts +14 -0
- package/dist/client/segment-cache.d.ts.map +1 -1
- package/dist/client/use-query-states.d.ts +9 -3
- package/dist/client/use-query-states.d.ts.map +1 -1
- package/dist/codec.js +1 -1
- package/dist/cookies/define-cookie.d.ts.map +1 -1
- package/dist/cookies/index.js +2 -2
- package/dist/cookies/index.js.map +1 -1
- package/dist/index.d.ts +4 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +142 -477
- package/dist/index.js.map +1 -1
- package/dist/params/index.js +1 -1
- package/dist/plugin-context.d.ts +15 -0
- package/dist/plugin-context.d.ts.map +1 -1
- package/dist/plugins/routing.d.ts +0 -9
- package/dist/plugins/routing.d.ts.map +1 -1
- package/dist/plugins/shims.d.ts.map +1 -1
- package/dist/plugins/static-build.d.ts +24 -0
- package/dist/plugins/static-build.d.ts.map +1 -1
- package/dist/routing/codegen-shared.d.ts +3 -44
- package/dist/routing/codegen-shared.d.ts.map +1 -1
- package/dist/routing/codegen-types.d.ts +10 -31
- package/dist/routing/codegen-types.d.ts.map +1 -1
- package/dist/routing/codegen-write.d.ts +51 -0
- package/dist/routing/codegen-write.d.ts.map +1 -0
- package/dist/routing/codegen.d.ts.map +1 -1
- package/dist/routing/convention-lint.d.ts +18 -4
- package/dist/routing/convention-lint.d.ts.map +1 -1
- package/dist/routing/export-detect.d.ts +16 -0
- package/dist/routing/export-detect.d.ts.map +1 -1
- package/dist/routing/index.js +3 -2
- package/dist/routing/link-codegen.d.ts +19 -4
- package/dist/routing/link-codegen.d.ts.map +1 -1
- package/dist/routing/manifest-codegen.d.ts +1 -7
- package/dist/routing/manifest-codegen.d.ts.map +1 -1
- package/dist/routing/segment-keys.d.ts +22 -0
- package/dist/routing/segment-keys.d.ts.map +1 -1
- package/dist/routing/types.d.ts +0 -6
- package/dist/routing/types.d.ts.map +1 -1
- package/dist/schema-bridge.d.ts +60 -9
- package/dist/schema-bridge.d.ts.map +1 -1
- package/dist/search-params/define.d.ts +62 -8
- package/dist/search-params/define.d.ts.map +1 -1
- package/dist/search-params/index.d.ts +0 -1
- package/dist/search-params/index.d.ts.map +1 -1
- package/dist/search-params/index.js +66 -29
- package/dist/search-params/index.js.map +1 -1
- package/dist/search-params/parse-total.d.ts +70 -0
- package/dist/search-params/parse-total.d.ts.map +1 -0
- package/dist/search-params/wrappers.d.ts +26 -3
- package/dist/search-params/wrappers.d.ts.map +1 -1
- package/dist/server/access-gate.d.ts +19 -8
- package/dist/server/access-gate.d.ts.map +1 -1
- package/dist/server/action-handler.d.ts.map +1 -1
- package/dist/server/als-registry.d.ts +16 -0
- package/dist/server/als-registry.d.ts.map +1 -1
- package/dist/server/chain-url-parts.d.ts +2 -3
- package/dist/server/chain-url-parts.d.ts.map +1 -1
- package/dist/server/compress.d.ts.map +1 -1
- package/dist/server/deny-boundary.d.ts +148 -15
- package/dist/server/deny-boundary.d.ts.map +1 -1
- package/dist/server/deny-renderer.d.ts +2 -2
- package/dist/server/deny-renderer.d.ts.map +1 -1
- package/dist/server/error-boundary-wrapper.d.ts +85 -15
- package/dist/server/error-boundary-wrapper.d.ts.map +1 -1
- package/dist/server/index.d.ts +0 -1
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +3 -2
- package/dist/server/index.js.map +1 -1
- package/dist/server/internal.d.ts +3 -1
- package/dist/server/internal.d.ts.map +1 -1
- package/dist/server/internal.js +344 -237
- package/dist/server/internal.js.map +1 -1
- package/dist/server/metadata-collector.d.ts +52 -0
- package/dist/server/metadata-collector.d.ts.map +1 -0
- package/dist/server/param-coercion.d.ts +12 -5
- package/dist/server/param-coercion.d.ts.map +1 -1
- package/dist/server/pipeline-helpers.d.ts.map +1 -1
- package/dist/server/pipeline-outcome.d.ts.map +1 -1
- package/dist/server/pipeline-phases.d.ts.map +1 -1
- package/dist/server/primitives.d.ts +23 -0
- package/dist/server/primitives.d.ts.map +1 -1
- package/dist/server/route-element-builder.d.ts +1 -12
- package/dist/server/route-element-builder.d.ts.map +1 -1
- package/dist/server/rsc-cache-key-guard.d.ts.map +1 -1
- package/dist/server/rsc-entry/deny-fallback.d.ts.map +1 -1
- package/dist/server/rsc-entry/error-renderer.d.ts +1 -1
- package/dist/server/rsc-entry/error-renderer.d.ts.map +1 -1
- package/dist/server/rsc-entry/helpers.d.ts +0 -7
- package/dist/server/rsc-entry/helpers.d.ts.map +1 -1
- package/dist/server/rsc-entry/index.d.ts +0 -1
- package/dist/server/rsc-entry/index.d.ts.map +1 -1
- package/dist/server/rsc-entry/render-route.d.ts.map +1 -1
- package/dist/server/rsc-entry/revalidate-renderer.d.ts.map +1 -1
- package/dist/server/rsc-entry/rsc-payload.d.ts +1 -3
- package/dist/server/rsc-entry/rsc-payload.d.ts.map +1 -1
- package/dist/server/rsc-entry/rsc-stream.d.ts +12 -0
- package/dist/server/rsc-entry/rsc-stream.d.ts.map +1 -1
- package/dist/server/rsc-entry/ssr-renderer.d.ts +0 -2
- package/dist/server/rsc-entry/ssr-renderer.d.ts.map +1 -1
- package/dist/server/skippable-prefix.d.ts +3 -0
- package/dist/server/skippable-prefix.d.ts.map +1 -1
- package/dist/server/slot-resolver.d.ts +18 -19
- package/dist/server/slot-resolver.d.ts.map +1 -1
- package/dist/server/ssr-bridge-types.d.ts +11 -0
- package/dist/server/ssr-bridge-types.d.ts.map +1 -1
- package/dist/server/ssr-entry.d.ts +0 -1
- package/dist/server/ssr-entry.d.ts.map +1 -1
- package/dist/server/state-tree-diff.d.ts +9 -16
- package/dist/server/state-tree-diff.d.ts.map +1 -1
- package/dist/server/static-generator.d.ts.map +1 -1
- package/dist/server/status-code-resolver.d.ts +8 -1
- package/dist/server/status-code-resolver.d.ts.map +1 -1
- package/dist/server/tree-builder.d.ts +28 -43
- package/dist/server/tree-builder.d.ts.map +1 -1
- package/dist/server/types.d.ts +12 -7
- package/dist/server/types.d.ts.map +1 -1
- package/dist/server/utils/element-type.d.ts +40 -0
- package/dist/server/utils/element-type.d.ts.map +1 -0
- package/dist/shared/rsc-media-type.d.ts +40 -0
- package/dist/shared/rsc-media-type.d.ts.map +1 -0
- package/dist/shared/segment-info.d.ts +7 -0
- package/dist/shared/segment-info.d.ts.map +1 -1
- package/docs/api/30-api-server.mdx +1 -1
- package/docs/api/33-api-search-params.mdx +38 -16
- package/docs/api/35-api-typescript.mdx +3 -3
- package/docs/api/36-cli.mdx +34 -7
- package/docs/learn/00-introduction.mdx +1 -1
- package/docs/learn/02-pages-and-layouts.mdx +1 -1
- package/docs/learn/05-typed-params.mdx +8 -8
- package/docs/learn/07-typed-routes.mdx +12 -7
- package/docs/learn/11-error-handling.mdx +16 -0
- package/docs/more/01-advanced-routing.mdx +1 -1
- package/docs/more/03-coming-from-nextjs.mdx +2 -2
- package/docs/more/50-ai-agent-instructions.mdx +6 -4
- package/package.json +8 -5
- package/src/adapters/cloudflare.ts +4 -1
- package/src/adapters/compress-module.ts +79 -1
- package/src/cli-check.ts +458 -0
- package/src/cli.ts +59 -24
- package/src/client/browser-entry/action-dispatch.ts +2 -1
- package/src/client/browser-entry/index.ts +0 -5
- package/src/client/error-boundary.tsx +65 -1
- package/src/client/index.ts +14 -3
- package/src/client/link.tsx +65 -64
- package/src/client/navigation-commit.ts +27 -4
- package/src/client/params-context.ts +4 -4
- package/src/client/router-pipeline.ts +4 -0
- package/src/client/router.ts +1 -0
- package/src/client/rsc-fetch.ts +14 -4
- package/src/client/segment-cache.ts +26 -5
- package/src/client/use-query-states.ts +102 -39
- package/src/cookies/define-cookie.ts +6 -1
- package/src/index.ts +20 -3
- package/src/plugin-context.ts +26 -0
- package/src/plugins/routing.ts +84 -146
- package/src/plugins/shims.ts +0 -1
- package/src/plugins/static-build.ts +78 -24
- package/src/routing/codegen-shared.ts +3 -79
- package/src/routing/codegen-types.ts +10 -31
- package/src/routing/codegen-write.ts +139 -0
- package/src/routing/codegen.ts +56 -182
- package/src/routing/convention-lint.ts +139 -40
- package/src/routing/export-detect.ts +151 -7
- package/src/routing/link-codegen.ts +32 -65
- package/src/routing/manifest-codegen.ts +1 -59
- package/src/routing/scanner.ts +3 -3
- package/src/routing/segment-keys.ts +37 -0
- package/src/routing/types.ts +0 -6
- package/src/schema-bridge.ts +180 -58
- package/src/search-params/define.ts +102 -37
- package/src/search-params/index.ts +0 -1
- package/src/search-params/parse-total.ts +78 -0
- package/src/search-params/wrappers.ts +60 -11
- package/src/server/access-gate.tsx +61 -68
- package/src/server/action-handler.ts +1 -4
- package/src/server/als-registry.ts +16 -0
- package/src/server/chain-url-parts.ts +2 -3
- package/src/server/compress.ts +9 -1
- package/src/server/deny-boundary.ts +269 -41
- package/src/server/deny-renderer.ts +32 -21
- package/src/server/error-boundary-wrapper.ts +166 -79
- package/src/server/index.ts +1 -3
- package/src/server/internal.ts +2 -2
- package/src/server/metadata-collector.ts +115 -0
- package/src/server/param-coercion.ts +13 -61
- package/src/server/pipeline-helpers.ts +2 -2
- package/src/server/pipeline-outcome.ts +2 -1
- package/src/server/pipeline-phases.ts +9 -9
- package/src/server/primitives.ts +25 -0
- package/src/server/route-element-builder.ts +169 -194
- package/src/server/rsc-cache-key-guard.ts +2 -8
- package/src/server/rsc-entry/deny-fallback.ts +3 -2
- package/src/server/rsc-entry/error-renderer.ts +31 -11
- package/src/server/rsc-entry/helpers.ts +3 -12
- package/src/server/rsc-entry/index.ts +0 -5
- package/src/server/rsc-entry/render-route.ts +14 -11
- package/src/server/rsc-entry/revalidate-renderer.ts +2 -1
- package/src/server/rsc-entry/rsc-payload.ts +104 -20
- package/src/server/rsc-entry/rsc-stream.ts +25 -2
- package/src/server/rsc-entry/ssr-renderer.ts +28 -16
- package/src/server/skippable-prefix.ts +21 -39
- package/src/server/slot-resolver.ts +69 -189
- package/src/server/ssr-bridge-types.ts +12 -0
- package/src/server/ssr-entry.ts +8 -6
- package/src/server/state-tree-diff.ts +11 -62
- package/src/server/static-generator.ts +3 -2
- package/src/server/status-code-resolver.ts +28 -11
- package/src/server/tree-builder.ts +35 -228
- package/src/server/types.ts +12 -7
- package/src/server/utils/element-type.ts +72 -0
- package/src/shared/rsc-media-type.ts +43 -0
- package/src/shared/segment-info.ts +7 -0
- package/dist/_chunks/cache-api-Cd0VZ_Pd.js.map +0 -1
- package/dist/_chunks/cli-schema-sync-CKgHC2MB.js.map +0 -1
- package/dist/_chunks/error-boundary-DpYRI_I1.js.map +0 -1
- package/dist/_chunks/logger-N7e5auP0.js.map +0 -1
- package/dist/_chunks/navigation-root-B29qg0_T.js.map +0 -1
- package/dist/_chunks/param-value-C8TNYchQ.js.map +0 -1
- package/dist/_chunks/registry-DbJPKoBp.js +0 -20
- package/dist/_chunks/registry-DbJPKoBp.js.map +0 -1
- package/dist/_chunks/schema-bridge-DT_Tn0Xf.js +0 -119
- package/dist/_chunks/schema-bridge-DT_Tn0Xf.js.map +0 -1
- package/dist/_chunks/segment-context-ZDnXDkbz.js +0 -34
- package/dist/_chunks/segment-context-ZDnXDkbz.js.map +0 -1
- package/dist/_chunks/use-query-states-DFvWd-EA.js.map +0 -1
- package/dist/_chunks/use-segment-params-ClyUNq4d.js +0 -128
- package/dist/_chunks/use-segment-params-ClyUNq4d.js.map +0 -1
- package/dist/_chunks/walkers-BhhwI9TD.js +0 -936
- package/dist/_chunks/walkers-BhhwI9TD.js.map +0 -1
- package/dist/search-params/registry.d.ts +0 -20
- package/dist/search-params/registry.d.ts.map +0 -1
- package/dist/segment-params/define.d.ts +0 -83
- package/dist/segment-params/define.d.ts.map +0 -1
- package/dist/segment-params/index.d.ts +0 -3
- package/dist/segment-params/index.d.ts.map +0 -1
- package/dist/segment-params/index.js +0 -70
- package/dist/segment-params/index.js.map +0 -1
- package/src/search-params/registry.ts +0 -31
- package/src/segment-params/define.ts +0 -226
- package/src/segment-params/index.ts +0 -9
package/dist/index.js
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { a as __esmMin, o as __exportAll, s as __toCommonJS } from "./_chunks/dist-BA3u1z3W.js";
|
|
2
|
+
import { t as RSC_CONTENT_TYPE } from "./_chunks/rsc-media-type-DRqE_lD_.js";
|
|
2
3
|
import { a as canonicalize, d as isMetadataRouteServePath, u as isDynamicMetadataExtension } from "./_chunks/segment-keys-BawYuNFO.js";
|
|
3
|
-
import {
|
|
4
|
-
import { h as swallow } from "./_chunks/logger-
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
4
|
+
import { n as collectInterceptionRewrites, r as matchUrlParts, t as scanRoutes } from "./_chunks/scanner-C8b0Gcw3.js";
|
|
5
|
+
import { h as swallow } from "./_chunks/logger-D8xJZXIN.js";
|
|
6
|
+
import { c as writeRouteCodegen, i as getPrerenderExport, l as buildDirToRouteMap, n as formatConventionWarnings, r as lintConventions, s as flushCodegenWrites, t as checkAppDirExists } from "./_chunks/convention-lint-kXsgc_-7.js";
|
|
7
|
+
import { c as resolveClientJavascript, i as mergeFileConfig, l as createNoopTimer, o as resolveAppDir, r as loadTimberConfigFile, s as resolveBuildDir, t as createPluginContext } from "./_chunks/plugin-context-rCinWLiE.js";
|
|
8
|
+
import { r as validateSchemaAgainstRoutes, t as collectLeafRoutes } from "./_chunks/walkers-RzN6AFjr.js";
|
|
7
9
|
import { i as withBasePath, n as normalizeBasePath, t as isRelativeBase } from "./_chunks/base-path-DaQrzbez.js";
|
|
8
10
|
import { t as rscPayloadPath } from "./_chunks/rsc-payload-path-B_LBodc2.js";
|
|
9
11
|
import { a as REDIRECTS_FILE, n as detectFsIdentity, o as RESERVED_OUTPUT_PATHS, r as fsKey, t as EXACT_FS_IDENTITY } from "./_chunks/fs-identity-D7vb6GdI.js";
|
|
@@ -286,8 +288,8 @@ function classifyStack(stack, projectRoot) {
|
|
|
286
288
|
*
|
|
287
289
|
* Design doc: 21-dev-server.md §"Error Overlay"
|
|
288
290
|
*/
|
|
289
|
-
var noValidate
|
|
290
|
-
var style
|
|
291
|
+
var noValidate = { validateStream: false };
|
|
292
|
+
var style = (fmt, text) => styleText(fmt, text, noValidate);
|
|
291
293
|
/**
|
|
292
294
|
* Wrap text in an OSC 8 hyperlink escape sequence.
|
|
293
295
|
*
|
|
@@ -309,7 +311,7 @@ function hyperlink(text, url) {
|
|
|
309
311
|
function fileLink(filePath, line, col) {
|
|
310
312
|
const display = line ? `${filePath}:${line}${col ? `:${col}` : ""}` : filePath;
|
|
311
313
|
const base = pathToFileURL(filePath).href;
|
|
312
|
-
return style
|
|
314
|
+
return style(["cyan", "underline"], hyperlink(display, line ? `${base}:${line}${col ? `:${col}` : ""}` : base));
|
|
313
315
|
}
|
|
314
316
|
var BOX = {
|
|
315
317
|
topLeft: "╭",
|
|
@@ -328,9 +330,9 @@ var BOX = {
|
|
|
328
330
|
function box(lines, borderFormat, width = 80) {
|
|
329
331
|
const bar = BOX.horizontal.repeat(width - 2);
|
|
330
332
|
const output = [];
|
|
331
|
-
output.push(style
|
|
332
|
-
for (const line of lines) output.push(`${style
|
|
333
|
-
output.push(style
|
|
333
|
+
output.push(style(borderFormat, `${BOX.topLeft}${bar}${BOX.topRight}`));
|
|
334
|
+
for (const line of lines) output.push(`${style(borderFormat, BOX.vertical)} ${line}`);
|
|
335
|
+
output.push(style(borderFormat, `${BOX.bottomLeft}${bar}${BOX.bottomRight}`));
|
|
334
336
|
return output.join("\n");
|
|
335
337
|
}
|
|
336
338
|
/**
|
|
@@ -351,32 +353,32 @@ function formatTerminalError$1(error, phase, projectRoot) {
|
|
|
351
353
|
const appFrames = frames.filter((f) => f.type === "app");
|
|
352
354
|
const internalCount = frames.filter((f) => f.type !== "app").length;
|
|
353
355
|
const boxLines = [];
|
|
354
|
-
boxLines.push(style
|
|
356
|
+
boxLines.push(style(["red", "bold"], `${PHASE_LABELS$1[phase]} Error`));
|
|
355
357
|
boxLines.push("");
|
|
356
|
-
for (const msgLine of error.message.split("\n")) boxLines.push(style
|
|
358
|
+
for (const msgLine of error.message.split("\n")) boxLines.push(style("red", msgLine));
|
|
357
359
|
if (loc) {
|
|
358
360
|
boxLines.push("");
|
|
359
361
|
const relPath = loc.file.startsWith(projectRoot) ? loc.file.slice(projectRoot.length + 1) : loc.file;
|
|
360
|
-
boxLines.push(`${style
|
|
362
|
+
boxLines.push(`${style("bold", "→")} ${fileLink(loc.file, loc.line, loc.column)} ${style("dim", `(${relPath})`)}`);
|
|
361
363
|
}
|
|
362
364
|
sections.push(box(boxLines, "red"));
|
|
363
365
|
if (componentStack) {
|
|
364
366
|
sections.push("");
|
|
365
|
-
sections.push(` ${style
|
|
366
|
-
for (const csLine of componentStack.trim().split("\n")) sections.push(` ${style
|
|
367
|
+
sections.push(` ${style("bold", "Component Stack:")}`);
|
|
368
|
+
for (const csLine of componentStack.trim().split("\n")) sections.push(` ${style("dim", csLine.trim())}`);
|
|
367
369
|
}
|
|
368
370
|
if (appFrames.length > 0) {
|
|
369
371
|
sections.push("");
|
|
370
|
-
sections.push(` ${style
|
|
372
|
+
sections.push(` ${style("bold", "Application Frames:")}`);
|
|
371
373
|
for (let i = 0; i < appFrames.length; i++) {
|
|
372
374
|
const f = appFrames[i];
|
|
373
375
|
if (f.file && f.line) {
|
|
374
|
-
const prefix = i === 0 ? style
|
|
375
|
-
sections.push(` ${prefix} ${fileLink(f.file, f.line, f.col)} ${style
|
|
376
|
+
const prefix = i === 0 ? style("bold", "▸") : " ";
|
|
377
|
+
sections.push(` ${prefix} ${fileLink(f.file, f.line, f.col)} ${style("dim", extractFnName(f.raw))}`);
|
|
376
378
|
} else sections.push(` ${f.raw}`);
|
|
377
379
|
}
|
|
378
380
|
}
|
|
379
|
-
if (internalCount > 0) sections.push(` ${style
|
|
381
|
+
if (internalCount > 0) sections.push(` ${style("dim", `… ${internalCount} internal frame${internalCount !== 1 ? "s" : ""} hidden`)}`);
|
|
380
382
|
sections.push("");
|
|
381
383
|
return sections.join("\n");
|
|
382
384
|
}
|
|
@@ -1378,7 +1380,7 @@ var COMPRESSIBLE_TYPES = /* @__PURE__ */ new Set([
|
|
|
1378
1380
|
"text/plain",
|
|
1379
1381
|
"text/xml",
|
|
1380
1382
|
"text/javascript",
|
|
1381
|
-
|
|
1383
|
+
RSC_CONTENT_TYPE,
|
|
1382
1384
|
"application/json",
|
|
1383
1385
|
"application/javascript",
|
|
1384
1386
|
"application/xml",
|
|
@@ -2204,69 +2206,10 @@ function timberMdx(ctx) {
|
|
|
2204
2206
|
};
|
|
2205
2207
|
}
|
|
2206
2208
|
//#endregion
|
|
2207
|
-
//#region src/routing/segment-codegen.ts
|
|
2208
|
-
/**
|
|
2209
|
-
* $segment type generation and directory-to-route mapping.
|
|
2210
|
-
*
|
|
2211
|
-
* Generates per-route $segment.d.ts files in .timber/types/ that mirror
|
|
2212
|
-
* the app/ directory structure. TypeScript's rootDirs feature makes these
|
|
2213
|
-
* importable as './$segment' from route files.
|
|
2214
|
-
*
|
|
2215
|
-
* Design doc: design/41-global-params.md §The $segment Module
|
|
2216
|
-
*/
|
|
2217
|
-
/**
|
|
2218
|
-
* Build a map of absolute filesystem directory → segment info.
|
|
2219
|
-
* Used by resolveId to map a $segment import's importer to the correct route,
|
|
2220
|
-
* and by writeSegmentTypes to generate per-route $segment.d.ts files.
|
|
2221
|
-
*
|
|
2222
|
-
* Walks every segment (not just leaves) so layouts and middleware
|
|
2223
|
-
* at any depth can import $segment.
|
|
2224
|
-
*/
|
|
2225
|
-
function buildDirToRouteMap(tree, appDir) {
|
|
2226
|
-
const map = /* @__PURE__ */ new Map();
|
|
2227
|
-
function walk(node, fsPath, treePath) {
|
|
2228
|
-
map.set(fsPath, {
|
|
2229
|
-
route: node.urlPath || "/",
|
|
2230
|
-
segmentPath: treePath || "/"
|
|
2231
|
-
});
|
|
2232
|
-
for (const child of node.children) walk(child, join(fsPath, child.segmentName), `${treePath}/${child.segmentName}`);
|
|
2233
|
-
for (const [slotName, slotNode] of Object.entries(node.slots)) walk(slotNode, join(fsPath, `@${slotName}`), `${treePath}/@${slotName}`);
|
|
2234
|
-
}
|
|
2235
|
-
walk(tree.root, appDir, "");
|
|
2236
|
-
return map;
|
|
2237
|
-
}
|
|
2238
|
-
/**
|
|
2239
|
-
* Generate the full $segment.d.ts content for a segment.
|
|
2240
|
-
*/
|
|
2241
|
-
function generateSegmentDts(info) {
|
|
2242
|
-
return [
|
|
2243
|
-
`export const ROUTE: '${info.route}';`,
|
|
2244
|
-
`export const SEGMENT_PATH: '${info.segmentPath}';`,
|
|
2245
|
-
""
|
|
2246
|
-
].join("\n");
|
|
2247
|
-
}
|
|
2248
|
-
/**
|
|
2249
|
-
* Generate per-route $segment.d.ts files in .timber/types/ mirroring
|
|
2250
|
-
* the app/ directory structure. These files are importable via TypeScript's
|
|
2251
|
-
* rootDirs feature.
|
|
2252
|
-
*/
|
|
2253
|
-
async function writeSegmentTypes(routeTree, appDir, root) {
|
|
2254
|
-
const typesBase = join(root, ".timber", "types", appDir.slice(root.length + 1));
|
|
2255
|
-
const map = buildDirToRouteMap(routeTree, appDir);
|
|
2256
|
-
const writes = [];
|
|
2257
|
-
for (const [fsDir, info] of map) {
|
|
2258
|
-
const segmentDir = join(typesBase, fsDir.slice(appDir.length));
|
|
2259
|
-
const segmentFile = join(segmentDir, "$segment.d.ts");
|
|
2260
|
-
const content = generateSegmentDts(info);
|
|
2261
|
-
writes.push(mkdir(segmentDir, { recursive: true }).then(() => writeFile(segmentFile, content, "utf-8")));
|
|
2262
|
-
}
|
|
2263
|
-
await Promise.all(writes);
|
|
2264
|
-
}
|
|
2265
|
-
//#endregion
|
|
2266
2209
|
//#region src/routing/manifest-codegen.ts
|
|
2267
2210
|
/**
|
|
2268
2211
|
* Route manifest codegen — generates virtual module source for the route
|
|
2269
|
-
* manifest
|
|
2212
|
+
* manifest and the global schema loader.
|
|
2270
2213
|
*
|
|
2271
2214
|
* Extracted from plugins/routing.ts to keep it under the 500-line budget.
|
|
2272
2215
|
* See design/18-build-system.md §"Virtual Modules".
|
|
@@ -2281,8 +2224,7 @@ var SINGLE_FILE_KEYS = [
|
|
|
2281
2224
|
"route",
|
|
2282
2225
|
"error",
|
|
2283
2226
|
"default",
|
|
2284
|
-
"denied"
|
|
2285
|
-
"params"
|
|
2227
|
+
"denied"
|
|
2286
2228
|
];
|
|
2287
2229
|
var RECORD_FILE_KEYS = [
|
|
2288
2230
|
"statusFiles",
|
|
@@ -2382,50 +2324,6 @@ function generateManifestModule(tree, viteRoot) {
|
|
|
2382
2324
|
].filter((line) => line !== void 0 && line !== "").join("\n");
|
|
2383
2325
|
}
|
|
2384
2326
|
/**
|
|
2385
|
-
* Walk the route tree and collect every page that exports a
|
|
2386
|
-
* `searchParams` definition, then emit a module that statically imports
|
|
2387
|
-
* each export and registers it into the shared search-params registry.
|
|
2388
|
-
*/
|
|
2389
|
-
function generateSearchParamsRegistryModule(tree) {
|
|
2390
|
-
const entries = [];
|
|
2391
|
-
function walk(node) {
|
|
2392
|
-
if (node.page) {
|
|
2393
|
-
if (node.params && fileHasSearchParamsExport(node.params.filePath)) entries.push({
|
|
2394
|
-
urlPath: node.urlPath,
|
|
2395
|
-
filePath: node.params.filePath
|
|
2396
|
-
});
|
|
2397
|
-
else if (fileHasSearchParamsExport(node.page.filePath)) entries.push({
|
|
2398
|
-
urlPath: node.urlPath,
|
|
2399
|
-
filePath: node.page.filePath
|
|
2400
|
-
});
|
|
2401
|
-
}
|
|
2402
|
-
for (const child of node.children) walk(child);
|
|
2403
|
-
for (const slot of Object.values(node.slots)) walk(slot);
|
|
2404
|
-
}
|
|
2405
|
-
walk(tree.root);
|
|
2406
|
-
entries.sort((a, b) => a.urlPath.localeCompare(b.urlPath));
|
|
2407
|
-
const lines = [];
|
|
2408
|
-
lines.push("// Auto-generated search-params registry — do not edit.");
|
|
2409
|
-
lines.push("// Generated by timber-routing plugin (TIM-830).");
|
|
2410
|
-
lines.push("");
|
|
2411
|
-
lines.push("import { registerSearchParams } from '@timber-js/app/search-params';");
|
|
2412
|
-
lines.push("");
|
|
2413
|
-
entries.forEach((entry, i) => {
|
|
2414
|
-
lines.push(`import { searchParams as r${i} } from ${JSON.stringify(entry.filePath)};`);
|
|
2415
|
-
});
|
|
2416
|
-
lines.push("");
|
|
2417
|
-
entries.forEach((entry, i) => {
|
|
2418
|
-
lines.push(`registerSearchParams(${JSON.stringify(entry.urlPath)}, r${i});`);
|
|
2419
|
-
});
|
|
2420
|
-
lines.push("");
|
|
2421
|
-
lines.push("export {};");
|
|
2422
|
-
lines.push("");
|
|
2423
|
-
return lines.join("\n");
|
|
2424
|
-
}
|
|
2425
|
-
function fileHasSearchParamsExport(filePath) {
|
|
2426
|
-
return fileHasExport(filePath, "searchParams");
|
|
2427
|
-
}
|
|
2428
|
-
/**
|
|
2429
2327
|
* Generate the virtual:timber-schema module.
|
|
2430
2328
|
*
|
|
2431
2329
|
* When app/schema.{ts,tsx,js,jsx} exists, generates a module that
|
|
@@ -2457,348 +2355,34 @@ function generateSchemaModule(appDir) {
|
|
|
2457
2355
|
].join("\n");
|
|
2458
2356
|
}
|
|
2459
2357
|
//#endregion
|
|
2460
|
-
//#region src/routing/convention-lint.ts
|
|
2461
|
-
/**
|
|
2462
|
-
* Convention linter — validates common misconfigurations in the route tree.
|
|
2463
|
-
*
|
|
2464
|
-
* Runs at scan time (build and dev startup). Each check produces a warning
|
|
2465
|
-
* with the file path, what's wrong, and what to do about it.
|
|
2466
|
-
*
|
|
2467
|
-
* These are warnings, not errors — they don't block the build. The goal is
|
|
2468
|
-
* to catch issues that would otherwise produce cryptic runtime behavior
|
|
2469
|
-
* (silent 404s, empty pages, confusing React errors).
|
|
2470
|
-
*
|
|
2471
|
-
* Design doc: 07-routing.md, 10-error-handling.md
|
|
2472
|
-
*/
|
|
2473
|
-
/**
|
|
2474
|
-
* Run all convention lint checks on a route tree.
|
|
2475
|
-
*
|
|
2476
|
-
* Returns an array of warnings. Empty array means everything looks good.
|
|
2477
|
-
*/
|
|
2478
|
-
function lintConventions(tree, appDir) {
|
|
2479
|
-
const warnings = [];
|
|
2480
|
-
checkEmptyApp(tree.root, appDir, warnings);
|
|
2481
|
-
checkRouteExports(tree.root, warnings);
|
|
2482
|
-
checkRootLayout(tree.root, warnings);
|
|
2483
|
-
checkDefaultExports(tree.root, warnings);
|
|
2484
|
-
checkErrorDirectives(tree.root, warnings);
|
|
2485
|
-
if (tree.globalError && isScriptExtension(tree.globalError.extension)) checkFileUseClientDirective(tree.globalError.filePath, "global-error", warnings);
|
|
2486
|
-
return warnings;
|
|
2487
|
-
}
|
|
2488
|
-
/**
|
|
2489
|
-
* Warn when the app/ directory has no routable files at all.
|
|
2490
|
-
*
|
|
2491
|
-
* This catches the "I created app/ but didn't add any pages" case where
|
|
2492
|
-
* every request silently 404s with no guidance.
|
|
2493
|
-
*/
|
|
2494
|
-
function checkEmptyApp(root, appDir, warnings) {
|
|
2495
|
-
if (hasAnyRoutable(root)) return;
|
|
2496
|
-
warnings.push({
|
|
2497
|
-
id: "EMPTY_APP",
|
|
2498
|
-
summary: "No pages or route handlers found in app/",
|
|
2499
|
-
details: ` Directory: ${appDir}\n\n Your app/ directory has no page.tsx or route.ts files.
|
|
2500
|
-
Every request will return 404.
|
|
2501
|
-
|
|
2502
|
-
To fix: Create app/page.tsx with a default export:
|
|
2503
|
-
|
|
2504
|
-
export default function Home() {
|
|
2505
|
-
return <h1>Hello</h1>;
|
|
2506
|
-
}
|
|
2507
|
-
`,
|
|
2508
|
-
level: "warn"
|
|
2509
|
-
});
|
|
2510
|
-
}
|
|
2511
|
-
/**
|
|
2512
|
-
* Check if a segment tree has any routable files (page or route) anywhere.
|
|
2513
|
-
*/
|
|
2514
|
-
function hasAnyRoutable(node) {
|
|
2515
|
-
if (node.page || node.route) return true;
|
|
2516
|
-
for (const child of node.children) if (hasAnyRoutable(child)) return true;
|
|
2517
|
-
for (const slot of Object.values(node.slots)) if (hasAnyRoutable(slot)) return true;
|
|
2518
|
-
return false;
|
|
2519
|
-
}
|
|
2520
|
-
/**
|
|
2521
|
-
* Check if a segment tree has any page files (not just route handlers).
|
|
2522
|
-
* Used by checkRootLayout to avoid false positives for API-only apps (TIM-794).
|
|
2523
|
-
* API-only apps (just route.ts handlers) don't need a root layout.
|
|
2524
|
-
*/
|
|
2525
|
-
function hasAnyPage(node) {
|
|
2526
|
-
if (node.page) return true;
|
|
2527
|
-
for (const child of node.children) if (hasAnyPage(child)) return true;
|
|
2528
|
-
for (const slot of Object.values(node.slots)) if (hasAnyPage(slot)) return true;
|
|
2529
|
-
return false;
|
|
2530
|
-
}
|
|
2531
|
-
/** HTTP methods that route.ts can export. */
|
|
2532
|
-
var HTTP_METHODS = [
|
|
2533
|
-
"GET",
|
|
2534
|
-
"POST",
|
|
2535
|
-
"PUT",
|
|
2536
|
-
"PATCH",
|
|
2537
|
-
"DELETE",
|
|
2538
|
-
"HEAD",
|
|
2539
|
-
"OPTIONS"
|
|
2540
|
-
];
|
|
2541
|
-
/**
|
|
2542
|
-
* Warn when a route.ts file doesn't appear to export any recognized HTTP methods.
|
|
2543
|
-
*
|
|
2544
|
-
* Uses AST-based detection via parseAst for precise matching.
|
|
2545
|
-
* Falls back silently when the file can't be parsed (syntax errors produce
|
|
2546
|
-
* no spurious warning — the TS compiler will catch those).
|
|
2547
|
-
*/
|
|
2548
|
-
function checkRouteExports(node, warnings) {
|
|
2549
|
-
if (node.route) {
|
|
2550
|
-
const filePath = node.route.filePath;
|
|
2551
|
-
try {
|
|
2552
|
-
if (fileHasAnyExport(filePath, HTTP_METHODS).size === 0 && !fileHasStarExport(filePath)) {
|
|
2553
|
-
const hint = fileHasDefaultExport(filePath) ? " It looks like you have a default export. route.ts uses named exports\n for HTTP methods (GET, POST, etc.), not a default export.\n" : "";
|
|
2554
|
-
warnings.push({
|
|
2555
|
-
id: "ROUTE_NO_METHODS",
|
|
2556
|
-
summary: `route.ts has no HTTP method exports: ${filePath}`,
|
|
2557
|
-
details: ` File: ${filePath}\n\n route.ts files must export named HTTP method handlers.
|
|
2558
|
-
Without them, the route matches but returns 405 Method Not Allowed.
|
|
2559
|
-
|
|
2560
|
-
` + hint + " Example:\n\n export function GET(ctx: RouteContext) {\n return Response.json({ hello: 'world' });\n }\n",
|
|
2561
|
-
level: "warn"
|
|
2562
|
-
});
|
|
2563
|
-
}
|
|
2564
|
-
} catch (err) {
|
|
2565
|
-
swallow(err, `convention-lint: unreadable route.ts ${filePath}`);
|
|
2566
|
-
}
|
|
2567
|
-
}
|
|
2568
|
-
for (const child of node.children) checkRouteExports(child, warnings);
|
|
2569
|
-
for (const slot of Object.values(node.slots)) checkRouteExports(slot, warnings);
|
|
2570
|
-
}
|
|
2571
|
-
/**
|
|
2572
|
-
* Warn when the root segment has no layout.tsx.
|
|
2573
|
-
*
|
|
2574
|
-
* Without a root layout, there's no HTML shell (<html>, <body>).
|
|
2575
|
-
* The page will render but may have hydration issues or no proper document structure.
|
|
2576
|
-
*/
|
|
2577
|
-
function checkRootLayout(root, warnings) {
|
|
2578
|
-
if (root.layout) return;
|
|
2579
|
-
if (!hasAnyPage(root)) return;
|
|
2580
|
-
warnings.push({
|
|
2581
|
-
id: "NO_ROOT_LAYOUT",
|
|
2582
|
-
summary: "No root layout.tsx found",
|
|
2583
|
-
details: " Your app has pages but no root layout.\n Without app/layout.tsx, pages have no <html> or <body> wrapper.\n\n To fix: Create app/layout.tsx:\n\n export default function RootLayout({ children }: { children: React.ReactNode }) {\n return (\n <html lang=\"en\">\n <body>{children}</body>\n </html>\n );\n }\n",
|
|
2584
|
-
level: "warn"
|
|
2585
|
-
});
|
|
2586
|
-
}
|
|
2587
|
-
/**
|
|
2588
|
-
* Warn when page.tsx or layout.tsx files don't have a default export.
|
|
2589
|
-
*
|
|
2590
|
-
* Without a default export:
|
|
2591
|
-
* - page.tsx: the page renders nothing (empty content)
|
|
2592
|
-
* - layout.tsx: the layout module loads but has no component to render
|
|
2593
|
-
*
|
|
2594
|
-
* Uses AST-based detection via parseAst for precise matching. Falls back
|
|
2595
|
-
* silently when the file can't be parsed (no spurious warning on syntax errors).
|
|
2596
|
-
*/
|
|
2597
|
-
function checkDefaultExports(node, warnings) {
|
|
2598
|
-
if (node.page && isScriptExtension(node.page.extension)) checkFileDefaultExport(node.page.filePath, "page", warnings);
|
|
2599
|
-
if (node.layout && isScriptExtension(node.layout.extension)) checkFileDefaultExport(node.layout.filePath, "layout", warnings);
|
|
2600
|
-
for (const child of node.children) checkDefaultExports(child, warnings);
|
|
2601
|
-
for (const slot of Object.values(node.slots)) checkDefaultExports(slot, warnings);
|
|
2602
|
-
}
|
|
2603
|
-
function isScriptExtension(ext) {
|
|
2604
|
-
return ext === "tsx" || ext === "ts" || ext === "jsx" || ext === "js";
|
|
2605
|
-
}
|
|
2606
|
-
function checkFileDefaultExport(filePath, fileType, warnings) {
|
|
2607
|
-
try {
|
|
2608
|
-
if (!fileHasDefaultExport(filePath)) warnings.push({
|
|
2609
|
-
id: `NO_DEFAULT_EXPORT:${filePath}`,
|
|
2610
|
-
summary: `${fileType}.tsx has no default export: ${filePath}`,
|
|
2611
|
-
details: ` File: ${filePath}\n\n ${fileType}.tsx files must export a default React component.\n Without a default export, the ${fileType === "page" ? "page renders nothing" : "layout has no component to wrap children"}.\n\n To fix: Add a default export:\n\n export default function My${fileType === "page" ? "Page" : "Layout"}(${fileType === "layout" ? "{ children }" : ""}) {\n return ${fileType === "layout" ? "<div>{children}</div>" : "<h1>Hello</h1>"};\n }\n`,
|
|
2612
|
-
level: "warn"
|
|
2613
|
-
});
|
|
2614
|
-
} catch (err) {
|
|
2615
|
-
swallow(err, `convention-lint: unreadable ${fileType} file ${filePath}`);
|
|
2616
|
-
}
|
|
2617
|
-
}
|
|
2618
|
-
/**
|
|
2619
|
-
* Warn when error.tsx or global-error.tsx is missing 'use client'.
|
|
2620
|
-
*
|
|
2621
|
-
* React's error boundary API requires client components. Without the directive,
|
|
2622
|
-
* the error boundary silently fails — the component renders as a server component
|
|
2623
|
-
* with no error catching. Users discover this only via cryptic runtime errors.
|
|
2624
|
-
*/
|
|
2625
|
-
function checkErrorDirectives(node, warnings) {
|
|
2626
|
-
if (node.error && isScriptExtension(node.error.extension)) checkFileUseClientDirective(node.error.filePath, "error", warnings);
|
|
2627
|
-
for (const child of node.children) checkErrorDirectives(child, warnings);
|
|
2628
|
-
for (const slot of Object.values(node.slots)) checkErrorDirectives(slot, warnings);
|
|
2629
|
-
}
|
|
2630
|
-
function checkFileUseClientDirective(filePath, fileType, warnings) {
|
|
2631
|
-
try {
|
|
2632
|
-
if (!fileHasDirective(filePath, "use client")) warnings.push({
|
|
2633
|
-
id: `ERROR_NO_USE_CLIENT:${filePath}`,
|
|
2634
|
-
summary: `${fileType}.tsx is missing 'use client': ${filePath}`,
|
|
2635
|
-
details: ` File: ${filePath}\n\n ${fileType}.tsx must start with 'use client' because React error\n boundaries require client components. Without it, the error boundary
|
|
2636
|
-
silently fails and errors are uncaught.
|
|
2637
|
-
|
|
2638
|
-
To fix: Add 'use client' as the first line of the file:
|
|
2639
|
-
|
|
2640
|
-
'use client';
|
|
2641
|
-
|
|
2642
|
-
export default function ${fileType === "global-error" ? "GlobalError" : "ErrorBoundary"}({ error, reset }) {\n return <div><h2>Something went wrong</h2><button onClick={reset}>Try again</button></div>;
|
|
2643
|
-
}
|
|
2644
|
-
`,
|
|
2645
|
-
level: "warn"
|
|
2646
|
-
});
|
|
2647
|
-
} catch (err) {
|
|
2648
|
-
swallow(err, `convention-lint: unreadable ${fileType} file ${filePath}`);
|
|
2649
|
-
}
|
|
2650
|
-
}
|
|
2651
|
-
/**
|
|
2652
|
-
* Check if the app/ directory exists. Called before scanning.
|
|
2653
|
-
* Returns a warning if missing, or null if the directory exists.
|
|
2654
|
-
*/
|
|
2655
|
-
function checkAppDirExists(appDir) {
|
|
2656
|
-
if (existsSync(appDir)) return null;
|
|
2657
|
-
return {
|
|
2658
|
-
id: "NO_APP_DIR",
|
|
2659
|
-
summary: "No app/ directory found",
|
|
2660
|
-
details: ` Expected: ${appDir}\n\n timber.js requires an app/ directory for file-system routing.
|
|
2661
|
-
Every request will return 404 until you create it.
|
|
2662
|
-
|
|
2663
|
-
To fix: Create the app/ directory with a page:
|
|
2664
|
-
|
|
2665
|
-
mkdir -p app
|
|
2666
|
-
# Create app/layout.tsx and app/page.tsx
|
|
2667
|
-
`,
|
|
2668
|
-
level: "error"
|
|
2669
|
-
};
|
|
2670
|
-
}
|
|
2671
|
-
var noValidate = { validateStream: false };
|
|
2672
|
-
var style = (fmt, text) => styleText(fmt, text, noValidate);
|
|
2673
|
-
/**
|
|
2674
|
-
* Format warnings for terminal output.
|
|
2675
|
-
*
|
|
2676
|
-
* Groups by severity, uses colors, and includes fix suggestions.
|
|
2677
|
-
*/
|
|
2678
|
-
function formatConventionWarnings(warnings) {
|
|
2679
|
-
if (warnings.length === 0) return "";
|
|
2680
|
-
const errors = warnings.filter((w) => w.level === "error");
|
|
2681
|
-
const warns = warnings.filter((w) => w.level === "warn");
|
|
2682
|
-
const lines = [];
|
|
2683
|
-
if (errors.length > 0) {
|
|
2684
|
-
lines.push(`${style(["red", "bold"], "[timber]")} ${style("red", `${errors.length} configuration error${errors.length !== 1 ? "s" : ""}:`)}`);
|
|
2685
|
-
for (const e of errors) {
|
|
2686
|
-
lines.push("");
|
|
2687
|
-
lines.push(` ${style("red", "✗")} ${e.summary}`);
|
|
2688
|
-
lines.push(style("dim", e.details));
|
|
2689
|
-
}
|
|
2690
|
-
}
|
|
2691
|
-
if (warns.length > 0) {
|
|
2692
|
-
if (errors.length > 0) lines.push("");
|
|
2693
|
-
lines.push(`${style(["yellow", "bold"], "[timber]")} ${style("yellow", `${warns.length} configuration warning${warns.length !== 1 ? "s" : ""}:`)}`);
|
|
2694
|
-
for (const w of warns) {
|
|
2695
|
-
lines.push("");
|
|
2696
|
-
lines.push(` ${style("yellow", "⚠")} ${w.summary}`);
|
|
2697
|
-
lines.push(style("dim", w.details));
|
|
2698
|
-
}
|
|
2699
|
-
}
|
|
2700
|
-
return lines.join("\n");
|
|
2701
|
-
}
|
|
2702
|
-
//#endregion
|
|
2703
2358
|
//#region src/plugins/routing.ts
|
|
2704
2359
|
var VIRTUAL_MODULE_ID$1 = "virtual:timber-route-manifest";
|
|
2705
2360
|
var RESOLVED_VIRTUAL_ID$1 = `\0${VIRTUAL_MODULE_ID$1}`;
|
|
2706
|
-
var SEARCH_PARAMS_REGISTRY_ID = "virtual:timber-search-params-registry";
|
|
2707
|
-
var RESOLVED_SEARCH_PARAMS_REGISTRY_ID = `\0${SEARCH_PARAMS_REGISTRY_ID}`;
|
|
2708
2361
|
var SCHEMA_ID = "virtual:timber-schema";
|
|
2709
2362
|
var RESOLVED_SCHEMA_ID = `\0${SCHEMA_ID}`;
|
|
2710
2363
|
var SEGMENT_PREFIX = "\0$segment:";
|
|
2711
2364
|
/**
|
|
2712
2365
|
* File convention names we track for changes that require manifest regeneration.
|
|
2713
2366
|
*/
|
|
2714
|
-
var ROUTE_FILE_PATTERNS = /\/(page|layout|middleware|access|route|error|global-error|default|denied|
|
|
2367
|
+
var ROUTE_FILE_PATTERNS = /\/(page|layout|middleware|access|route|error|global-error|default|denied|schema|\d{3}|[45]xx|sitemap|robots|manifest|favicon|icon|opengraph-image|apple-icon)\./;
|
|
2715
2368
|
/**
|
|
2716
2369
|
* Create the timber-routing Vite plugin.
|
|
2717
2370
|
*
|
|
2718
2371
|
* Hooks: resolveId, load, buildStart, configureServer
|
|
2719
2372
|
*/
|
|
2720
|
-
/** Absolute path to the generated route map declaration file. */
|
|
2721
|
-
var CODEGEN_OUTPUT = ".timber/timber-routes.d.ts";
|
|
2722
|
-
/**
|
|
2723
|
-
* Content of the timber-env.d.ts file written to the project root.
|
|
2724
|
-
*
|
|
2725
|
-
* Uses a triple-slash reference so TypeScript auto-discovers the generated
|
|
2726
|
-
* route map without the user needing to edit tsconfig.json — same pattern
|
|
2727
|
-
* as Next.js's next-env.d.ts.
|
|
2728
|
-
*
|
|
2729
|
-
* Also references `vite/client` so side-effect imports like
|
|
2730
|
-
* `import './globals.css'` type-check without ts(2882), and so
|
|
2731
|
-
* `import.meta.env`, `import.meta.hot`, `import.meta.glob`, asset
|
|
2732
|
-
* imports (`*.png`, `*.svg`, …), CSS modules, and `?url`/`?raw`/
|
|
2733
|
-
* `?inline` query variants all resolve without the user editing
|
|
2734
|
-
* tsconfig.json themselves. Next.js's next-env.d.ts plays the same
|
|
2735
|
-
* role via `next/types/global`.
|
|
2736
|
-
*
|
|
2737
|
-
* `vite` is a peer dep of `@timber-js/app`, so the reference always
|
|
2738
|
-
* resolves in a correctly-installed project.
|
|
2739
|
-
*/
|
|
2740
|
-
var TIMBER_ENV_DTS = [
|
|
2741
|
-
"// This file is auto-generated by timber.js. Do not edit.",
|
|
2742
|
-
"// It ensures TypeScript picks up the generated route types and",
|
|
2743
|
-
"// Vite-provided ambient types (CSS, assets, import.meta.env, HMR).",
|
|
2744
|
-
"/* eslint-disable @typescript-eslint/triple-slash-reference */",
|
|
2745
|
-
"/// <reference path=\".timber/timber-routes.d.ts\" />",
|
|
2746
|
-
"/// <reference types=\"vite/client\" />",
|
|
2747
|
-
""
|
|
2748
|
-
].join("\n");
|
|
2749
|
-
/**
|
|
2750
|
-
* Codegen writes that have been started but have not yet landed on disk.
|
|
2751
|
-
*
|
|
2752
|
-
* `writeCodegen` is deliberately unawaited on the hot path, which leaves a
|
|
2753
|
-
* caller no way to know the files exist yet. Anything that tears down the
|
|
2754
|
-
* project root — a build process exiting, a test removing its temp root —
|
|
2755
|
-
* then races the writes: `rmSync` empties a directory that a pending `mkdir`
|
|
2756
|
-
* immediately recreates, and the removal fails with ENOTEMPTY.
|
|
2757
|
-
*
|
|
2758
|
-
* Every started write registers here and deregisters when it settles, so
|
|
2759
|
-
* `flushCodegenWrites()` can await them instead of guessing at a delay.
|
|
2760
|
-
*/
|
|
2761
|
-
var pendingCodegenWrites = /* @__PURE__ */ new Set();
|
|
2762
|
-
/**
|
|
2763
|
-
* Resolve once every codegen write started so far has landed on disk.
|
|
2764
|
-
*
|
|
2765
|
-
* Safe to call at any time and from anywhere — writes are tracked per process,
|
|
2766
|
-
* not per plugin instance, so a caller does not need a handle on the plugin
|
|
2767
|
-
* that started them. Never rejects: individual write failures are already
|
|
2768
|
-
* swallowed and logged by `writeCodegen`.
|
|
2769
|
-
*/
|
|
2770
|
-
async function flushCodegenWrites() {
|
|
2771
|
-
while (pendingCodegenWrites.size > 0) {
|
|
2772
|
-
const inFlight = [...pendingCodegenWrites];
|
|
2773
|
-
await Promise.all(inFlight);
|
|
2774
|
-
for (const write of inFlight) pendingCodegenWrites.delete(write);
|
|
2775
|
-
}
|
|
2776
|
-
}
|
|
2777
2373
|
/**
|
|
2778
2374
|
* Write the generated route map and the timber-env.d.ts reference file.
|
|
2779
2375
|
*
|
|
2780
|
-
* Not awaited
|
|
2781
|
-
*
|
|
2782
|
-
*
|
|
2376
|
+
* Not awaited, to avoid blocking the hot path — use `flushCodegenWrites()` to
|
|
2377
|
+
* wait for the writes to land. Failures are logged, not thrown: a missing .d.ts
|
|
2378
|
+
* is a developer UX issue in dev/build, not a runtime failure. (`timber check`
|
|
2379
|
+
* calls `writeRouteCodegen` directly and does treat a failure as fatal.)
|
|
2783
2380
|
*/
|
|
2784
2381
|
function writeCodegen(ctx) {
|
|
2785
2382
|
if (!ctx.routeTree) return;
|
|
2786
|
-
|
|
2787
|
-
const content = generateRouteMap(ctx.routeTree, {
|
|
2788
|
-
appDir: ctx.appDir,
|
|
2789
|
-
outputDir: timberDir
|
|
2790
|
-
});
|
|
2791
|
-
const routesPath = join(ctx.root, CODEGEN_OUTPUT);
|
|
2792
|
-
const envPath = join(ctx.root, "timber-env.d.ts");
|
|
2793
|
-
const done = mkdir(timberDir, { recursive: true }).then(() => Promise.all([
|
|
2794
|
-
writeFile(routesPath, content, "utf-8"),
|
|
2795
|
-
writeFile(envPath, TIMBER_ENV_DTS, "utf-8"),
|
|
2796
|
-
writeSegmentTypes(ctx.routeTree, ctx.appDir, ctx.root)
|
|
2797
|
-
])).then(() => void 0).catch((err) => {
|
|
2383
|
+
writeRouteCodegen(ctx.routeTree, ctx.appDir, ctx.root).catch((err) => {
|
|
2798
2384
|
console.warn("[timber] Failed to write codegen output:", err);
|
|
2799
2385
|
});
|
|
2800
|
-
pendingCodegenWrites.add(done);
|
|
2801
|
-
done.finally(() => pendingCodegenWrites.delete(done));
|
|
2802
2386
|
}
|
|
2803
2387
|
function timberRouting(ctx) {
|
|
2804
2388
|
/** Track warned files to avoid repeating warnings on rescan. */
|
|
@@ -2820,12 +2404,16 @@ function timberRouting(ctx) {
|
|
|
2820
2404
|
return;
|
|
2821
2405
|
}
|
|
2822
2406
|
ctx.timer.start("route-scan");
|
|
2823
|
-
|
|
2407
|
+
const tree = scanRoutes(ctx.appDir, { pageExtensions: ctx.config.pageExtensions });
|
|
2824
2408
|
ctx.timer.end("route-scan");
|
|
2409
|
+
const conventionWarnings = lintConventions(tree, ctx.appDir);
|
|
2410
|
+
const fatal = conventionWarnings.filter((w) => w.level === "error");
|
|
2411
|
+
if (fatal.length > 0) throw new Error(formatConventionWarnings(fatal, { color: false }));
|
|
2412
|
+
ctx.routeTree = tree;
|
|
2825
2413
|
dirToRoute = buildDirToRouteMap(ctx.routeTree, ctx.appDir);
|
|
2826
2414
|
writeCodegen(ctx);
|
|
2827
2415
|
warnSchemaValidation(ctx);
|
|
2828
|
-
const newConventionWarnings =
|
|
2416
|
+
const newConventionWarnings = conventionWarnings.filter((w) => !warnedFiles.has(w.id));
|
|
2829
2417
|
if (newConventionWarnings.length > 0) {
|
|
2830
2418
|
for (const w of newConventionWarnings) warnedFiles.add(w.id);
|
|
2831
2419
|
const formatted = formatConventionWarnings(newConventionWarnings);
|
|
@@ -2846,7 +2434,6 @@ function timberRouting(ctx) {
|
|
|
2846
2434
|
const cleanId = id.startsWith("\0") ? id.slice(1) : id;
|
|
2847
2435
|
if (cleanId === VIRTUAL_MODULE_ID$1) return RESOLVED_VIRTUAL_ID$1;
|
|
2848
2436
|
if (cleanId.endsWith(`/${VIRTUAL_MODULE_ID$1}`)) return RESOLVED_VIRTUAL_ID$1;
|
|
2849
|
-
if (cleanId === SEARCH_PARAMS_REGISTRY_ID || cleanId.endsWith(`/${SEARCH_PARAMS_REGISTRY_ID}`)) return RESOLVED_SEARCH_PARAMS_REGISTRY_ID;
|
|
2850
2437
|
if (cleanId === SCHEMA_ID || cleanId.endsWith(`/${SCHEMA_ID}`)) return RESOLVED_SCHEMA_ID;
|
|
2851
2438
|
if (importer && (cleanId === "./$segment" || cleanId === "./$segment.js" || cleanId === "./$segment.ts")) {
|
|
2852
2439
|
const cleanImporter = importer.startsWith("\0") ? importer.slice(1) : importer;
|
|
@@ -2867,10 +2454,6 @@ function timberRouting(ctx) {
|
|
|
2867
2454
|
if (!ctx.routeTree) rescan();
|
|
2868
2455
|
return generateManifestModule(ctx.routeTree, ctx.root);
|
|
2869
2456
|
}
|
|
2870
|
-
if (id === RESOLVED_SEARCH_PARAMS_REGISTRY_ID) {
|
|
2871
|
-
if (!ctx.routeTree) rescan();
|
|
2872
|
-
return generateSearchParamsRegistryModule(ctx.routeTree);
|
|
2873
|
-
}
|
|
2874
2457
|
if (id === RESOLVED_SCHEMA_ID) return generateSchemaModule(ctx.appDir);
|
|
2875
2458
|
if (id.startsWith(SEGMENT_PREFIX)) {
|
|
2876
2459
|
const info = JSON.parse(id.slice(10));
|
|
@@ -2917,13 +2500,59 @@ function timberRouting(ctx) {
|
|
|
2917
2500
|
/** Snapshot of the last generated manifest, used to detect structural changes. */
|
|
2918
2501
|
let lastManifest = ctx.routeTree ? generateManifestModule(ctx.routeTree, ctx.root) : "";
|
|
2919
2502
|
/**
|
|
2503
|
+
* Rescan without letting a rejected route tree kill the dev server.
|
|
2504
|
+
*
|
|
2505
|
+
* `rescan()` throws on hard misconfigurations — duplicate conventions,
|
|
2506
|
+
* route collisions, a status file that is not a client component. Those
|
|
2507
|
+
* throws are correct at startup and in `vite build`, but here they run
|
|
2508
|
+
* inside a chokidar listener, where an exception is an unhandled
|
|
2509
|
+
* rejection that takes the process down mid-edit. Report and keep the
|
|
2510
|
+
* last good tree so the server stays up and the fix is one save away.
|
|
2511
|
+
*
|
|
2512
|
+
* Returns false when the scan failed, so callers skip the manifest
|
|
2513
|
+
* regeneration that would otherwise publish a stale tree as fresh.
|
|
2514
|
+
*/
|
|
2515
|
+
/**
|
|
2516
|
+
* True while the last rescan is still failing.
|
|
2517
|
+
*
|
|
2518
|
+
* Recovery has to force a reload even when the manifest is byte-identical.
|
|
2519
|
+
* Adding a missing `'use client'` back to a status file changes no route
|
|
2520
|
+
* metadata, so the content-change path would send nothing and leave the
|
|
2521
|
+
* error overlay on screen over a page that is now fine.
|
|
2522
|
+
*/
|
|
2523
|
+
let scanFailed = false;
|
|
2524
|
+
const rescanSafely = () => {
|
|
2525
|
+
try {
|
|
2526
|
+
rescan();
|
|
2527
|
+
if (scanFailed) {
|
|
2528
|
+
scanFailed = false;
|
|
2529
|
+
lastManifest = ctx.routeTree ? generateManifestModule(ctx.routeTree, ctx.root) : "";
|
|
2530
|
+
invalidateManifest(devServer);
|
|
2531
|
+
}
|
|
2532
|
+
return true;
|
|
2533
|
+
} catch (err) {
|
|
2534
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
2535
|
+
process.stderr.write(`[timber] Route scan failed — keeping previous routes.\n${message}\n`);
|
|
2536
|
+
scanFailed = true;
|
|
2537
|
+
devServer.hot.send({
|
|
2538
|
+
type: "error",
|
|
2539
|
+
err: {
|
|
2540
|
+
message,
|
|
2541
|
+
stack: "",
|
|
2542
|
+
plugin: "timber-routing"
|
|
2543
|
+
}
|
|
2544
|
+
});
|
|
2545
|
+
return false;
|
|
2546
|
+
}
|
|
2547
|
+
};
|
|
2548
|
+
/**
|
|
2920
2549
|
* Handle a route-significant file being added or removed.
|
|
2921
2550
|
* Always triggers a full-reload since the route tree structure changed.
|
|
2922
2551
|
*/
|
|
2923
2552
|
const handleStructuralChange = (filePath) => {
|
|
2924
2553
|
if (!filePath.startsWith(ctx.appDir)) return;
|
|
2925
2554
|
if (!ROUTE_FILE_PATTERNS.test(filePath)) return;
|
|
2926
|
-
|
|
2555
|
+
if (!rescanSafely()) return;
|
|
2927
2556
|
lastManifest = ctx.routeTree ? generateManifestModule(ctx.routeTree, ctx.root) : "";
|
|
2928
2557
|
invalidateManifest(devServer);
|
|
2929
2558
|
};
|
|
@@ -2938,7 +2567,7 @@ function timberRouting(ctx) {
|
|
|
2938
2567
|
const handleContentChange = (filePath) => {
|
|
2939
2568
|
if (!filePath.startsWith(ctx.appDir)) return;
|
|
2940
2569
|
if (!ROUTE_FILE_PATTERNS.test(filePath)) return;
|
|
2941
|
-
|
|
2570
|
+
if (!rescanSafely()) return;
|
|
2942
2571
|
const newManifest = ctx.routeTree ? generateManifestModule(ctx.routeTree, ctx.root) : "";
|
|
2943
2572
|
if (newManifest !== lastManifest) {
|
|
2944
2573
|
lastManifest = newManifest;
|
|
@@ -2988,11 +2617,7 @@ function invalidateManifest(server) {
|
|
|
2988
2617
|
for (const envName of Object.keys(server.environments)) {
|
|
2989
2618
|
const env = server.environments[envName];
|
|
2990
2619
|
if (!env?.moduleGraph) continue;
|
|
2991
|
-
for (const virtualId of [
|
|
2992
|
-
RESOLVED_VIRTUAL_ID$1,
|
|
2993
|
-
RESOLVED_SEARCH_PARAMS_REGISTRY_ID,
|
|
2994
|
-
RESOLVED_SCHEMA_ID
|
|
2995
|
-
]) {
|
|
2620
|
+
for (const virtualId of [RESOLVED_VIRTUAL_ID$1, RESOLVED_SCHEMA_ID]) {
|
|
2996
2621
|
const mod = env.moduleGraph.getModuleById(virtualId);
|
|
2997
2622
|
if (mod) env.moduleGraph.invalidateModule(mod);
|
|
2998
2623
|
}
|
|
@@ -3030,7 +2655,6 @@ var SUBPATH_SRC_MAP = {
|
|
|
3030
2655
|
"cookies": "cookies/index.ts",
|
|
3031
2656
|
"params": "params/index.ts",
|
|
3032
2657
|
"search-params": "search-params/index.ts",
|
|
3033
|
-
"segment-params": "segment-params/index.ts",
|
|
3034
2658
|
"cache/stores/memory": "cache/stores/memory.ts",
|
|
3035
2659
|
"cache/stores/redis": "cache/stores/redis.ts",
|
|
3036
2660
|
"cache/stores/cloudflare-kv": "cache/stores/cloudflare-kv.ts",
|
|
@@ -6769,6 +6393,45 @@ async function cleanConditionalArtifacts(staticDir) {
|
|
|
6769
6393
|
await rm(join(staticDir, REDIRECTS_FILE), { force: true }).catch(() => {});
|
|
6770
6394
|
}
|
|
6771
6395
|
/**
|
|
6396
|
+
* Run `fn` with `MessageChannel` instrumented, then stop every channel opened
|
|
6397
|
+
* while it ran from holding the process open.
|
|
6398
|
+
*
|
|
6399
|
+
* Static generation is the only build pass that renders **HTML** in the build
|
|
6400
|
+
* process: it imports the built app bundle and drives the full SSR pipeline
|
|
6401
|
+
* through it. React's scheduler opens a `MessageChannel` at module scope in
|
|
6402
|
+
* that bundle (`scheduler`'s DOM build) and never closes it, and a ref'd
|
|
6403
|
+
* `MessagePort` keeps Node's event loop alive on its own — so `timber build`
|
|
6404
|
+
* finished the site, printed its summary, and then hung forever. Server-mode
|
|
6405
|
+
* builds are unaffected because nothing there renders through react-dom.
|
|
6406
|
+
*
|
|
6407
|
+
* `unref()` is the whole fix: it leaves the port working and only drops the
|
|
6408
|
+
* "keep the process alive" property. It is applied **after** `fn` resolves, so
|
|
6409
|
+
* a render in flight can never be cut short by the process exiting under it —
|
|
6410
|
+
* an unref'd port would let Node exit while the scheduler still had a
|
|
6411
|
+
* continuation queued, which is a silently truncated page rather than a hang.
|
|
6412
|
+
*
|
|
6413
|
+
* Nothing renders after this pass. If that ever changes, the release has to
|
|
6414
|
+
* move to the end of whatever renders last.
|
|
6415
|
+
*
|
|
6416
|
+
* @internal Exported for testing.
|
|
6417
|
+
*/
|
|
6418
|
+
async function withSchedulerPortsReleased(fn) {
|
|
6419
|
+
const opened = [];
|
|
6420
|
+
const OriginalMessageChannel = globalThis.MessageChannel;
|
|
6421
|
+
globalThis.MessageChannel = class extends OriginalMessageChannel {
|
|
6422
|
+
constructor() {
|
|
6423
|
+
super();
|
|
6424
|
+
opened.push(this.port1, this.port2);
|
|
6425
|
+
}
|
|
6426
|
+
};
|
|
6427
|
+
try {
|
|
6428
|
+
return await fn();
|
|
6429
|
+
} finally {
|
|
6430
|
+
globalThis.MessageChannel = OriginalMessageChannel;
|
|
6431
|
+
for (const port of opened) port.unref?.();
|
|
6432
|
+
}
|
|
6433
|
+
}
|
|
6434
|
+
/**
|
|
6772
6435
|
* Load the built RSC entry and run the static generation pass.
|
|
6773
6436
|
*
|
|
6774
6437
|
* Unlike prebuilt capture, this needs the FULL pipeline handler, so
|
|
@@ -6783,27 +6446,29 @@ async function runStaticGeneration(ctx) {
|
|
|
6783
6446
|
g.__TIMBER_CAPTURE_MODE__ = false;
|
|
6784
6447
|
if (ctx.buildManifest) await mergeRscAssetsManifest(ctx.buildDir, ctx.root, ctx.buildManifest, ctx.routeClientRefKeys);
|
|
6785
6448
|
console.log("[timber] Static generation: building site…");
|
|
6786
|
-
let mod;
|
|
6787
|
-
try {
|
|
6788
|
-
mod = await import(
|
|
6789
|
-
/* @vite-ignore */
|
|
6790
|
-
`${pathToFileURL(entryPath).href}?t=${Date.now()}`
|
|
6791
|
-
);
|
|
6792
|
-
} catch (error) {
|
|
6793
|
-
throw new Error(`[timber] Static generation: failed to load the built RSC entry (${entryPath}). The full request handler must initialize for static rendering.`, { cause: error });
|
|
6794
|
-
}
|
|
6795
|
-
const generate = mod.__timber_generateStaticSite;
|
|
6796
|
-
if (typeof generate !== "function") throw new Error("[timber] Static generation: built RSC entry does not export __timber_generateStaticSite — rebuild from a clean build directory");
|
|
6797
6449
|
const staticDir = join(ctx.buildDir, "static-pages");
|
|
6798
6450
|
await cleanConditionalArtifacts(staticDir);
|
|
6799
6451
|
await mkdir(staticDir, { recursive: true });
|
|
6800
6452
|
const sitemapConfig = ctx.config.sitemap;
|
|
6801
6453
|
const autoSitemapEnabled = Boolean(sitemapConfig?.enabled && sitemapConfig?.baseUrl);
|
|
6802
6454
|
const writer = new StaticSiteWriter(staticDir, await detectFsIdentity(staticDir));
|
|
6803
|
-
const summary = await
|
|
6804
|
-
|
|
6805
|
-
|
|
6806
|
-
|
|
6455
|
+
const summary = await withSchedulerPortsReleased(async () => {
|
|
6456
|
+
let mod;
|
|
6457
|
+
try {
|
|
6458
|
+
mod = await import(
|
|
6459
|
+
/* @vite-ignore */
|
|
6460
|
+
`${pathToFileURL(entryPath).href}?t=${Date.now()}`
|
|
6461
|
+
);
|
|
6462
|
+
} catch (error) {
|
|
6463
|
+
throw new Error(`[timber] Static generation: failed to load the built RSC entry (${entryPath}). The full request handler must initialize for static rendering.`, { cause: error });
|
|
6464
|
+
}
|
|
6465
|
+
const generate = mod.__timber_generateStaticSite;
|
|
6466
|
+
if (typeof generate !== "function") throw new Error("[timber] Static generation: built RSC entry does not export __timber_generateStaticSite — rebuild from a clean build directory");
|
|
6467
|
+
return generate({
|
|
6468
|
+
autoSitemapEnabled,
|
|
6469
|
+
renderTimeoutMs: ctx.config.renderTimeoutMs,
|
|
6470
|
+
onEntry: (entry) => writer.write(entry)
|
|
6471
|
+
});
|
|
6807
6472
|
});
|
|
6808
6473
|
const totalUrls = summary.rendered + summary.errors.length;
|
|
6809
6474
|
console.log(`[timber] Static generation: rendered ${summary.rendered}/${totalUrls} entries.` + (summary.errors.length > 0 ? ` ${summary.errors.length} failed.` : "") + (summary.redirects.length > 0 ? ` ${summary.redirects.length} redirect(s).` : ""));
|
|
@@ -10935,6 +10600,7 @@ function timber(config) {
|
|
|
10935
10600
|
const earlyFileConfig = loadTimberConfigFile(process.cwd());
|
|
10936
10601
|
const rootSync = {
|
|
10937
10602
|
name: "timber-root-sync",
|
|
10603
|
+
api: { getContext: () => ctx },
|
|
10938
10604
|
async config(userConfig, { command, isPreview }) {
|
|
10939
10605
|
const viteRoot = resolve(userConfig.root ?? process.cwd());
|
|
10940
10606
|
ctx.timer.start("config-load");
|
|
@@ -10991,7 +10657,6 @@ function timber(config) {
|
|
|
10991
10657
|
"@timber-js/app/cookies",
|
|
10992
10658
|
"@timber-js/app/cache",
|
|
10993
10659
|
"@timber-js/app/search-params",
|
|
10994
|
-
"@timber-js/app/segment-params",
|
|
10995
10660
|
"@timber-js/app/params",
|
|
10996
10661
|
"@timber-js/app/codec"
|
|
10997
10662
|
];
|