@timber-js/app 0.2.0-alpha.4 → 0.2.0-alpha.41
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +8 -0
- package/dist/_chunks/{als-registry-B7DbZ2hS.js → als-registry-Ba7URUIn.js} +1 -1
- package/dist/_chunks/als-registry-Ba7URUIn.js.map +1 -0
- package/dist/_chunks/chunk-DYhsFzuS.js +33 -0
- package/dist/_chunks/debug-ECi_61pb.js +108 -0
- package/dist/_chunks/debug-ECi_61pb.js.map +1 -0
- package/dist/_chunks/define-cookie-BmKbSyp0.js +93 -0
- package/dist/_chunks/define-cookie-BmKbSyp0.js.map +1 -0
- package/dist/_chunks/error-boundary-BAN3751q.js +211 -0
- package/dist/_chunks/error-boundary-BAN3751q.js.map +1 -0
- package/dist/_chunks/{format-CwdaB0_2.js → format-cX7wzEp2.js} +2 -2
- package/dist/_chunks/{format-CwdaB0_2.js.map → format-cX7wzEp2.js.map} +1 -1
- package/dist/_chunks/{interception-BOoWmLUA.js → interception-D2djYaIm.js} +112 -77
- package/dist/_chunks/interception-D2djYaIm.js.map +1 -0
- package/dist/_chunks/{metadata-routes-Cjmvi3rQ.js → metadata-routes-BU684ls2.js} +1 -1
- package/dist/_chunks/{metadata-routes-Cjmvi3rQ.js.map → metadata-routes-BU684ls2.js.map} +1 -1
- package/dist/_chunks/{request-context-CZJi4CuK.js → request-context-BxYIJM24.js} +93 -69
- package/dist/_chunks/request-context-BxYIJM24.js.map +1 -0
- package/dist/_chunks/segment-context-C6byCyZU.js +69 -0
- package/dist/_chunks/segment-context-C6byCyZU.js.map +1 -0
- package/dist/_chunks/stale-reload-C0ValzG7.js +47 -0
- package/dist/_chunks/stale-reload-C0ValzG7.js.map +1 -0
- package/dist/_chunks/{tracing-Cwn7697K.js → tracing-CuXiCP5p.js} +17 -3
- package/dist/_chunks/{tracing-Cwn7697K.js.map → tracing-CuXiCP5p.js.map} +1 -1
- package/dist/_chunks/{use-query-states-D5KaffOK.js → use-query-states-BvW0TKDn.js} +1 -1
- package/dist/_chunks/{use-query-states-D5KaffOK.js.map → use-query-states-BvW0TKDn.js.map} +1 -1
- package/dist/_chunks/wrappers-C6J0nNji.js +331 -0
- package/dist/_chunks/wrappers-C6J0nNji.js.map +1 -0
- package/dist/adapters/compress-module.d.ts.map +1 -1
- package/dist/adapters/nitro.d.ts +17 -1
- package/dist/adapters/nitro.d.ts.map +1 -1
- package/dist/adapters/nitro.js +56 -13
- package/dist/adapters/nitro.js.map +1 -1
- package/dist/cache/fast-hash.d.ts +22 -0
- package/dist/cache/fast-hash.d.ts.map +1 -0
- package/dist/cache/index.d.ts +5 -2
- package/dist/cache/index.d.ts.map +1 -1
- package/dist/cache/index.js +88 -18
- package/dist/cache/index.js.map +1 -1
- package/dist/cache/register-cached-function.d.ts.map +1 -1
- package/dist/cache/singleflight.d.ts +18 -1
- package/dist/cache/singleflight.d.ts.map +1 -1
- package/dist/cache/timber-cache.d.ts.map +1 -1
- package/dist/client/error-boundary.d.ts +10 -1
- package/dist/client/error-boundary.d.ts.map +1 -1
- package/dist/client/error-boundary.js +1 -125
- package/dist/client/index.d.ts +3 -2
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +213 -93
- package/dist/client/index.js.map +1 -1
- package/dist/client/link.d.ts +22 -8
- package/dist/client/link.d.ts.map +1 -1
- package/dist/client/navigation-context.d.ts +2 -2
- package/dist/client/router.d.ts +25 -3
- package/dist/client/router.d.ts.map +1 -1
- package/dist/client/rsc-fetch.d.ts +23 -2
- package/dist/client/rsc-fetch.d.ts.map +1 -1
- package/dist/client/segment-cache.d.ts +1 -1
- package/dist/client/segment-cache.d.ts.map +1 -1
- package/dist/client/segment-context.d.ts +1 -1
- package/dist/client/segment-context.d.ts.map +1 -1
- package/dist/client/segment-merger.d.ts.map +1 -1
- package/dist/client/stale-reload.d.ts +15 -0
- package/dist/client/stale-reload.d.ts.map +1 -1
- package/dist/client/top-loader.d.ts +1 -1
- package/dist/client/top-loader.d.ts.map +1 -1
- package/dist/client/transition-root.d.ts +1 -1
- package/dist/client/transition-root.d.ts.map +1 -1
- package/dist/client/use-params.d.ts +2 -2
- package/dist/client/use-params.d.ts.map +1 -1
- package/dist/client/use-query-states.d.ts +1 -1
- package/dist/codec.d.ts +21 -0
- package/dist/codec.d.ts.map +1 -0
- package/dist/cookies/define-cookie.d.ts +33 -12
- package/dist/cookies/define-cookie.d.ts.map +1 -1
- package/dist/cookies/index.js +1 -83
- package/dist/fonts/css.d.ts +1 -0
- package/dist/fonts/css.d.ts.map +1 -1
- package/dist/fonts/local.d.ts +4 -2
- package/dist/fonts/local.d.ts.map +1 -1
- package/dist/index.d.ts +112 -35
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +635 -233
- package/dist/index.js.map +1 -1
- package/dist/params/define.d.ts +76 -0
- package/dist/params/define.d.ts.map +1 -0
- package/dist/params/index.d.ts +8 -0
- package/dist/params/index.d.ts.map +1 -0
- package/dist/params/index.js +104 -0
- package/dist/params/index.js.map +1 -0
- package/dist/plugins/adapter-build.d.ts.map +1 -1
- package/dist/plugins/build-manifest.d.ts.map +1 -1
- package/dist/plugins/client-chunks.d.ts +32 -0
- package/dist/plugins/client-chunks.d.ts.map +1 -0
- package/dist/plugins/dev-error-overlay.d.ts +26 -1
- package/dist/plugins/dev-error-overlay.d.ts.map +1 -1
- package/dist/plugins/entries.d.ts +7 -0
- package/dist/plugins/entries.d.ts.map +1 -1
- package/dist/plugins/fonts.d.ts +9 -1
- package/dist/plugins/fonts.d.ts.map +1 -1
- package/dist/plugins/mdx.d.ts +6 -0
- package/dist/plugins/mdx.d.ts.map +1 -1
- package/dist/plugins/routing.d.ts.map +1 -1
- package/dist/plugins/server-bundle.d.ts.map +1 -1
- package/dist/plugins/static-build.d.ts.map +1 -1
- package/dist/routing/codegen.d.ts +2 -2
- package/dist/routing/codegen.d.ts.map +1 -1
- package/dist/routing/index.js +1 -1
- package/dist/routing/scanner.d.ts.map +1 -1
- package/dist/routing/status-file-lint.d.ts +2 -1
- package/dist/routing/status-file-lint.d.ts.map +1 -1
- package/dist/routing/types.d.ts +6 -4
- package/dist/routing/types.d.ts.map +1 -1
- package/dist/rsc-runtime/rsc.d.ts +1 -1
- package/dist/rsc-runtime/rsc.d.ts.map +1 -1
- package/dist/rsc-runtime/ssr.d.ts +12 -0
- package/dist/rsc-runtime/ssr.d.ts.map +1 -1
- package/dist/search-params/codecs.d.ts +1 -1
- package/dist/search-params/define.d.ts +153 -0
- package/dist/search-params/define.d.ts.map +1 -0
- package/dist/search-params/index.d.ts +4 -5
- package/dist/search-params/index.d.ts.map +1 -1
- package/dist/search-params/index.js +3 -474
- package/dist/search-params/registry.d.ts +1 -1
- package/dist/search-params/wrappers.d.ts +53 -0
- package/dist/search-params/wrappers.d.ts.map +1 -0
- package/dist/server/access-gate.d.ts +4 -0
- package/dist/server/access-gate.d.ts.map +1 -1
- package/dist/server/action-client.d.ts.map +1 -1
- package/dist/server/action-encryption.d.ts +76 -0
- package/dist/server/action-encryption.d.ts.map +1 -0
- package/dist/server/action-handler.d.ts.map +1 -1
- package/dist/server/als-registry.d.ts +18 -4
- package/dist/server/als-registry.d.ts.map +1 -1
- package/dist/server/build-manifest.d.ts +2 -2
- package/dist/server/debug.d.ts +46 -15
- package/dist/server/debug.d.ts.map +1 -1
- package/dist/server/default-logger.d.ts +22 -0
- package/dist/server/default-logger.d.ts.map +1 -0
- package/dist/server/deny-renderer.d.ts.map +1 -1
- package/dist/server/early-hints.d.ts +13 -5
- package/dist/server/early-hints.d.ts.map +1 -1
- package/dist/server/error-boundary-wrapper.d.ts +4 -0
- package/dist/server/error-boundary-wrapper.d.ts.map +1 -1
- package/dist/server/flight-injection-state.d.ts +78 -0
- package/dist/server/flight-injection-state.d.ts.map +1 -0
- package/dist/server/flight-scripts.d.ts +39 -0
- package/dist/server/flight-scripts.d.ts.map +1 -0
- package/dist/server/flush.d.ts.map +1 -1
- package/dist/server/form-data.d.ts +29 -0
- package/dist/server/form-data.d.ts.map +1 -1
- package/dist/server/html-injectors.d.ts +5 -11
- package/dist/server/html-injectors.d.ts.map +1 -1
- package/dist/server/index.d.ts +4 -2
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +1975 -1649
- package/dist/server/index.js.map +1 -1
- package/dist/server/logger.d.ts +24 -7
- package/dist/server/logger.d.ts.map +1 -1
- package/dist/server/node-stream-transforms.d.ts +77 -0
- package/dist/server/node-stream-transforms.d.ts.map +1 -0
- package/dist/server/pipeline.d.ts +7 -4
- package/dist/server/pipeline.d.ts.map +1 -1
- package/dist/server/primitives.d.ts +30 -3
- package/dist/server/primitives.d.ts.map +1 -1
- package/dist/server/render-timeout.d.ts +51 -0
- package/dist/server/render-timeout.d.ts.map +1 -0
- package/dist/server/request-context.d.ts +65 -38
- package/dist/server/request-context.d.ts.map +1 -1
- package/dist/server/route-element-builder.d.ts +7 -0
- package/dist/server/route-element-builder.d.ts.map +1 -1
- package/dist/server/route-handler.d.ts.map +1 -1
- package/dist/server/route-matcher.d.ts +2 -2
- package/dist/server/route-matcher.d.ts.map +1 -1
- package/dist/server/rsc-entry/error-renderer.d.ts.map +1 -1
- package/dist/server/rsc-entry/helpers.d.ts +46 -3
- package/dist/server/rsc-entry/helpers.d.ts.map +1 -1
- package/dist/server/rsc-entry/index.d.ts +6 -1
- package/dist/server/rsc-entry/index.d.ts.map +1 -1
- package/dist/server/rsc-entry/rsc-payload.d.ts.map +1 -1
- package/dist/server/rsc-entry/rsc-stream.d.ts +9 -0
- package/dist/server/rsc-entry/rsc-stream.d.ts.map +1 -1
- package/dist/server/rsc-entry/ssr-renderer.d.ts.map +1 -1
- package/dist/server/slot-resolver.d.ts +1 -1
- package/dist/server/slot-resolver.d.ts.map +1 -1
- package/dist/server/ssr-entry.d.ts +22 -0
- package/dist/server/ssr-entry.d.ts.map +1 -1
- package/dist/server/ssr-render.d.ts +39 -21
- package/dist/server/ssr-render.d.ts.map +1 -1
- package/dist/server/tracing.d.ts +10 -0
- package/dist/server/tracing.d.ts.map +1 -1
- package/dist/server/tree-builder.d.ts +19 -12
- package/dist/server/tree-builder.d.ts.map +1 -1
- package/dist/server/types.d.ts +1 -3
- package/dist/server/types.d.ts.map +1 -1
- package/dist/server/version-skew.d.ts +61 -0
- package/dist/server/version-skew.d.ts.map +1 -0
- package/dist/server/waituntil-bridge.d.ts.map +1 -1
- package/dist/shared/merge-search-params.d.ts +22 -0
- package/dist/shared/merge-search-params.d.ts.map +1 -0
- package/dist/shims/navigation-client.d.ts +1 -1
- package/dist/shims/navigation-client.d.ts.map +1 -1
- package/dist/shims/navigation.d.ts +1 -1
- package/dist/shims/navigation.d.ts.map +1 -1
- package/dist/utils/state-machine.d.ts +80 -0
- package/dist/utils/state-machine.d.ts.map +1 -0
- package/package.json +17 -14
- package/src/adapters/compress-module.ts +24 -4
- package/src/adapters/nitro.ts +58 -9
- package/src/cache/fast-hash.ts +34 -0
- package/src/cache/index.ts +5 -2
- package/src/cache/register-cached-function.ts +7 -3
- package/src/cache/singleflight.ts +62 -4
- package/src/cache/timber-cache.ts +34 -26
- package/src/cli.ts +0 -0
- package/src/client/browser-entry.ts +94 -90
- package/src/client/error-boundary.tsx +18 -1
- package/src/client/index.ts +10 -1
- package/src/client/link.tsx +78 -19
- package/src/client/navigation-context.ts +2 -2
- package/src/client/router.ts +105 -60
- package/src/client/rsc-fetch.ts +63 -2
- package/src/client/segment-cache.ts +1 -1
- package/src/client/segment-context.ts +6 -1
- package/src/client/segment-merger.ts +2 -8
- package/src/client/stale-reload.ts +32 -6
- package/src/client/top-loader.tsx +10 -9
- package/src/client/transition-root.tsx +7 -1
- package/src/client/use-params.ts +3 -3
- package/src/client/use-query-states.ts +1 -1
- package/src/codec.ts +21 -0
- package/src/cookies/define-cookie.ts +69 -18
- package/src/fonts/css.ts +2 -1
- package/src/fonts/local.ts +7 -3
- package/src/index.ts +280 -85
- package/src/params/define.ts +260 -0
- package/src/params/index.ts +28 -0
- package/src/plugins/adapter-build.ts +6 -0
- package/src/plugins/build-manifest.ts +11 -0
- package/src/plugins/client-chunks.ts +65 -0
- package/src/plugins/dev-error-overlay.ts +70 -1
- package/src/plugins/dev-server.ts +38 -4
- package/src/plugins/entries.ts +12 -11
- package/src/plugins/fonts.ts +171 -19
- package/src/plugins/mdx.ts +9 -5
- package/src/plugins/routing.ts +40 -14
- package/src/plugins/server-bundle.ts +32 -1
- package/src/plugins/shims.ts +1 -1
- package/src/plugins/static-build.ts +8 -4
- package/src/routing/codegen.ts +109 -88
- package/src/routing/scanner.ts +55 -6
- package/src/routing/status-file-lint.ts +2 -1
- package/src/routing/types.ts +7 -4
- package/src/rsc-runtime/rsc.ts +2 -0
- package/src/rsc-runtime/ssr.ts +50 -0
- package/src/rsc-runtime/vendor-types.d.ts +7 -0
- package/src/search-params/codecs.ts +1 -1
- package/src/search-params/define.ts +504 -0
- package/src/search-params/index.ts +12 -18
- package/src/search-params/registry.ts +1 -1
- package/src/search-params/wrappers.ts +85 -0
- package/src/server/access-gate.tsx +40 -9
- package/src/server/action-client.ts +14 -5
- package/src/server/action-encryption.ts +144 -0
- package/src/server/action-handler.ts +19 -2
- package/src/server/als-registry.ts +18 -4
- package/src/server/build-manifest.ts +4 -4
- package/src/server/compress.ts +25 -7
- package/src/server/debug.ts +55 -17
- package/src/server/default-logger.ts +98 -0
- package/src/server/deny-renderer.ts +2 -1
- package/src/server/early-hints.ts +36 -15
- package/src/server/error-boundary-wrapper.ts +57 -14
- package/src/server/flight-injection-state.ts +152 -0
- package/src/server/flight-scripts.ts +59 -0
- package/src/server/flush.ts +2 -1
- package/src/server/form-data.ts +76 -0
- package/src/server/html-injectors.ts +103 -66
- package/src/server/index.ts +9 -4
- package/src/server/logger.ts +38 -35
- package/src/server/node-stream-transforms.ts +381 -0
- package/src/server/pipeline.ts +131 -39
- package/src/server/primitives.ts +47 -5
- package/src/server/render-timeout.ts +108 -0
- package/src/server/request-context.ts +112 -119
- package/src/server/route-element-builder.ts +106 -114
- package/src/server/route-handler.ts +2 -1
- package/src/server/route-matcher.ts +2 -2
- package/src/server/rsc-entry/error-renderer.ts +5 -3
- package/src/server/rsc-entry/helpers.ts +122 -3
- package/src/server/rsc-entry/index.ts +125 -49
- package/src/server/rsc-entry/rsc-payload.ts +52 -12
- package/src/server/rsc-entry/rsc-stream.ts +33 -8
- package/src/server/rsc-entry/ssr-renderer.ts +40 -13
- package/src/server/slot-resolver.ts +199 -210
- package/src/server/ssr-entry.ts +168 -22
- package/src/server/ssr-render.ts +289 -67
- package/src/server/tracing.ts +23 -0
- package/src/server/tree-builder.ts +91 -57
- package/src/server/types.ts +1 -3
- package/src/server/version-skew.ts +104 -0
- package/src/server/waituntil-bridge.ts +4 -1
- package/src/shared/merge-search-params.ts +48 -0
- package/src/shims/navigation-client.ts +1 -1
- package/src/shims/navigation.ts +1 -1
- package/src/utils/state-machine.ts +111 -0
- package/dist/_chunks/als-registry-B7DbZ2hS.js.map +0 -1
- package/dist/_chunks/debug-B4WUeqJ-.js +0 -75
- package/dist/_chunks/debug-B4WUeqJ-.js.map +0 -1
- package/dist/_chunks/interception-BOoWmLUA.js.map +0 -1
- package/dist/_chunks/request-context-CZJi4CuK.js.map +0 -1
- package/dist/_chunks/ssr-data-MjmprTmO.js +0 -88
- package/dist/_chunks/ssr-data-MjmprTmO.js.map +0 -1
- package/dist/_chunks/use-cookie-DX-l1_5E.js +0 -91
- package/dist/_chunks/use-cookie-DX-l1_5E.js.map +0 -1
- package/dist/client/error-boundary.js.map +0 -1
- package/dist/cookies/index.js.map +0 -1
- package/dist/plugins/dynamic-transform.d.ts +0 -72
- package/dist/plugins/dynamic-transform.d.ts.map +0 -1
- package/dist/search-params/analyze.d.ts +0 -54
- package/dist/search-params/analyze.d.ts.map +0 -1
- package/dist/search-params/builtin-codecs.d.ts +0 -105
- package/dist/search-params/builtin-codecs.d.ts.map +0 -1
- package/dist/search-params/create.d.ts +0 -106
- package/dist/search-params/create.d.ts.map +0 -1
- package/dist/search-params/index.js.map +0 -1
- package/dist/server/prerender.d.ts +0 -77
- package/dist/server/prerender.d.ts.map +0 -1
- package/dist/server/response-cache.d.ts +0 -53
- package/dist/server/response-cache.d.ts.map +0 -1
- package/src/plugins/dynamic-transform.ts +0 -161
- package/src/search-params/analyze.ts +0 -192
- package/src/search-params/builtin-codecs.ts +0 -228
- package/src/search-params/create.ts +0 -321
- package/src/server/prerender.ts +0 -139
- package/src/server/response-cache.ts +0 -277
package/src/plugins/fonts.ts
CHANGED
|
@@ -14,13 +14,15 @@
|
|
|
14
14
|
* Design doc: 24-fonts.md
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
|
-
import type { Plugin } from 'vite';
|
|
17
|
+
import type { Plugin, ViteDevServer } from 'vite';
|
|
18
|
+
import { readFileSync, existsSync } from 'node:fs';
|
|
19
|
+
import { resolve, normalize } from 'node:path';
|
|
18
20
|
import type { PluginContext } from '#/index.js';
|
|
19
21
|
import type { ExtractedFont, GoogleFontConfig } from '#/fonts/types.js';
|
|
20
22
|
import type { ManifestFontEntry } from '#/server/build-manifest.js';
|
|
21
|
-
import { generateVariableClass, generateFontFamilyClass } from '#/fonts/css.js';
|
|
23
|
+
import { generateVariableClass, generateFontFamilyClass, generateFontFaces } from '#/fonts/css.js';
|
|
22
24
|
import { generateFallbackCss, buildFontStack } from '#/fonts/fallbacks.js';
|
|
23
|
-
import { processLocalFont } from '#/fonts/local.js';
|
|
25
|
+
import { processLocalFont, generateLocalFontFaces } from '#/fonts/local.js';
|
|
24
26
|
import { inferFontFormat } from '#/fonts/local.js';
|
|
25
27
|
import { downloadAndCacheFonts, type CachedFont } from '#/fonts/google.js';
|
|
26
28
|
import {
|
|
@@ -34,6 +36,23 @@ const VIRTUAL_LOCAL = '@timber/fonts/local';
|
|
|
34
36
|
const RESOLVED_GOOGLE = '\0@timber/fonts/google';
|
|
35
37
|
const RESOLVED_LOCAL = '\0@timber/fonts/local';
|
|
36
38
|
|
|
39
|
+
/**
|
|
40
|
+
* Virtual side-effect module that registers font CSS on globalThis.
|
|
41
|
+
*
|
|
42
|
+
* When a file calls localFont() or a Google font function, the transform
|
|
43
|
+
* hook injects `import 'virtual:timber-font-css-register'` into that file.
|
|
44
|
+
* This virtual module sets `globalThis.__timber_font_css` with the combined
|
|
45
|
+
* @font-face CSS. The RSC entry reads it at render time to inline a <style> tag.
|
|
46
|
+
*
|
|
47
|
+
* This approach avoids timing issues because:
|
|
48
|
+
* 1. The font file is in the RSC module graph (imported by layout.tsx)
|
|
49
|
+
* 2. The side-effect import is added to the font file during transform
|
|
50
|
+
* 3. When layout.tsx is loaded, fonts.ts runs → side-effect module runs → globalThis is set
|
|
51
|
+
* 4. RSC entry renders → reads globalThis → inlines <style>
|
|
52
|
+
*/
|
|
53
|
+
const VIRTUAL_FONT_CSS_REGISTER = 'virtual:timber-font-css-register';
|
|
54
|
+
const RESOLVED_FONT_CSS_REGISTER = '\0virtual:timber-font-css-register';
|
|
55
|
+
|
|
37
56
|
/**
|
|
38
57
|
* Registry of fonts extracted during transform.
|
|
39
58
|
* Keyed by a unique font ID derived from family + config.
|
|
@@ -242,27 +261,44 @@ function generateLocalVirtualModule(): string {
|
|
|
242
261
|
].join('\n');
|
|
243
262
|
}
|
|
244
263
|
|
|
264
|
+
/**
|
|
265
|
+
* Generate CSS for a single extracted font.
|
|
266
|
+
*
|
|
267
|
+
* Includes @font-face rules (for local fonts), fallback @font-face,
|
|
268
|
+
* and the scoped class rule.
|
|
269
|
+
*/
|
|
270
|
+
export function generateFontCss(font: ExtractedFont): string {
|
|
271
|
+
const cssParts: string[] = [];
|
|
272
|
+
|
|
273
|
+
if (font.provider === 'local' && font.localSources) {
|
|
274
|
+
const faces = generateLocalFontFaces(font.family, font.localSources, font.display);
|
|
275
|
+
const faceCss = generateFontFaces(faces);
|
|
276
|
+
if (faceCss) cssParts.push(faceCss);
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
const fallbackCss = generateFallbackCss(font.family);
|
|
280
|
+
if (fallbackCss) cssParts.push(fallbackCss);
|
|
281
|
+
|
|
282
|
+
if (font.variable) {
|
|
283
|
+
cssParts.push(generateVariableClass(font.className, font.variable, font.fontFamily));
|
|
284
|
+
} else {
|
|
285
|
+
cssParts.push(generateFontFamilyClass(font.className, font.fontFamily));
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
return cssParts.join('\n\n');
|
|
289
|
+
}
|
|
290
|
+
|
|
245
291
|
/**
|
|
246
292
|
* Generate the CSS output for all extracted fonts.
|
|
247
293
|
*
|
|
248
|
-
* Includes @font-face rules, fallback @font-face rules,
|
|
294
|
+
* Includes @font-face rules for local fonts, fallback @font-face rules,
|
|
295
|
+
* and scoped classes.
|
|
249
296
|
*/
|
|
250
297
|
export function generateAllFontCss(registry: FontRegistry): string {
|
|
251
298
|
const cssParts: string[] = [];
|
|
252
|
-
|
|
253
299
|
for (const font of registry.values()) {
|
|
254
|
-
|
|
255
|
-
const fallbackCss = generateFallbackCss(font.family);
|
|
256
|
-
if (fallbackCss) cssParts.push(fallbackCss);
|
|
257
|
-
|
|
258
|
-
// Generate scoped class
|
|
259
|
-
if (font.variable) {
|
|
260
|
-
cssParts.push(generateVariableClass(font.className, font.variable, font.fontFamily));
|
|
261
|
-
} else {
|
|
262
|
-
cssParts.push(generateFontFamilyClass(font.className, font.fontFamily));
|
|
263
|
-
}
|
|
300
|
+
cssParts.push(generateFontCss(font));
|
|
264
301
|
}
|
|
265
|
-
|
|
266
302
|
return cssParts.join('\n\n');
|
|
267
303
|
}
|
|
268
304
|
|
|
@@ -359,23 +395,112 @@ export function timberFonts(ctx: PluginContext): Plugin {
|
|
|
359
395
|
name: 'timber-fonts',
|
|
360
396
|
|
|
361
397
|
/**
|
|
362
|
-
* Resolve `@timber/fonts/google
|
|
398
|
+
* Resolve `@timber/fonts/google`, `@timber/fonts/local`,
|
|
399
|
+
* and `virtual:timber-font-css` virtual modules.
|
|
400
|
+
*
|
|
401
|
+
* Handles \0 prefix and root prefix stripping for RSC/SSR
|
|
402
|
+
* environments where the RSC plugin re-imports virtual modules
|
|
403
|
+
* with additional prefixes.
|
|
363
404
|
*/
|
|
364
405
|
resolveId(id: string) {
|
|
365
|
-
|
|
366
|
-
|
|
406
|
+
// Strip \0 prefix (RSC plugin re-imports)
|
|
407
|
+
let cleanId = id.startsWith('\0') ? id.slice(1) : id;
|
|
408
|
+
// Strip root prefix (SSR build entries)
|
|
409
|
+
if (cleanId.startsWith(ctx.root)) {
|
|
410
|
+
const stripped = cleanId.slice(ctx.root.length);
|
|
411
|
+
if (stripped.startsWith('/') || stripped.startsWith('\\')) {
|
|
412
|
+
cleanId = stripped.slice(1);
|
|
413
|
+
} else {
|
|
414
|
+
cleanId = stripped;
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
if (cleanId === VIRTUAL_GOOGLE) return RESOLVED_GOOGLE;
|
|
419
|
+
if (cleanId === VIRTUAL_LOCAL) return RESOLVED_LOCAL;
|
|
420
|
+
if (cleanId === VIRTUAL_FONT_CSS_REGISTER) return RESOLVED_FONT_CSS_REGISTER;
|
|
367
421
|
return null;
|
|
368
422
|
},
|
|
369
423
|
|
|
370
424
|
/**
|
|
371
425
|
* Return generated source for font virtual modules.
|
|
426
|
+
*
|
|
427
|
+
* `virtual:timber-font-css` exports the combined @font-face CSS
|
|
428
|
+
* as a string. The RSC entry imports it and inlines a <style> tag.
|
|
429
|
+
* Because this is loaded lazily (on first request), the font
|
|
430
|
+
* registry is always populated by the time it's needed.
|
|
372
431
|
*/
|
|
373
432
|
load(id: string) {
|
|
374
433
|
if (id === RESOLVED_GOOGLE) return generateGoogleVirtualModule(registry);
|
|
375
434
|
if (id === RESOLVED_LOCAL) return generateLocalVirtualModule();
|
|
435
|
+
|
|
436
|
+
if (id === RESOLVED_FONT_CSS_REGISTER) {
|
|
437
|
+
const css = generateAllFontCss(registry);
|
|
438
|
+
// Side-effect module: sets font CSS on globalThis for the RSC entry to read.
|
|
439
|
+
return `globalThis.__timber_font_css = ${JSON.stringify(css)};`;
|
|
440
|
+
}
|
|
376
441
|
return null;
|
|
377
442
|
},
|
|
378
443
|
|
|
444
|
+
/**
|
|
445
|
+
* Serve local font files and font CSS in dev mode under `/_timber/fonts/`.
|
|
446
|
+
*
|
|
447
|
+
* Serves:
|
|
448
|
+
* - `/_timber/fonts/fonts.css` — combined @font-face + scoped class CSS
|
|
449
|
+
* - `/_timber/fonts/<filename>` — individual font files from the registry
|
|
450
|
+
*
|
|
451
|
+
* Only files registered in the font registry are served.
|
|
452
|
+
* Paths are validated to prevent directory traversal.
|
|
453
|
+
*/
|
|
454
|
+
configureServer(server: ViteDevServer) {
|
|
455
|
+
server.middlewares.use((req, res, next) => {
|
|
456
|
+
const url = req.url;
|
|
457
|
+
if (!url || !url.startsWith('/_timber/fonts/')) return next();
|
|
458
|
+
|
|
459
|
+
const requestedFilename = url.slice('/_timber/fonts/'.length);
|
|
460
|
+
// Reject path traversal attempts
|
|
461
|
+
if (requestedFilename.includes('..') || requestedFilename.includes('/')) {
|
|
462
|
+
res.statusCode = 400;
|
|
463
|
+
res.end('Bad request');
|
|
464
|
+
return;
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
// Font CSS is now injected via Vite's CSS pipeline (virtual:timber-font-css modules).
|
|
468
|
+
// This middleware only serves font binary files (woff2, etc.).
|
|
469
|
+
|
|
470
|
+
// Find the matching font file in the registry
|
|
471
|
+
for (const font of registry.values()) {
|
|
472
|
+
if (font.provider !== 'local' || !font.localSources) continue;
|
|
473
|
+
for (const src of font.localSources) {
|
|
474
|
+
const basename = src.path.split('/').pop() ?? '';
|
|
475
|
+
if (basename === requestedFilename) {
|
|
476
|
+
const absolutePath = normalize(resolve(src.path));
|
|
477
|
+
if (!existsSync(absolutePath)) {
|
|
478
|
+
res.statusCode = 404;
|
|
479
|
+
res.end('Not found');
|
|
480
|
+
return;
|
|
481
|
+
}
|
|
482
|
+
const data = readFileSync(absolutePath);
|
|
483
|
+
const ext = absolutePath.split('.').pop()?.toLowerCase();
|
|
484
|
+
const mimeMap: Record<string, string> = {
|
|
485
|
+
woff2: 'font/woff2',
|
|
486
|
+
woff: 'font/woff',
|
|
487
|
+
ttf: 'font/ttf',
|
|
488
|
+
otf: 'font/otf',
|
|
489
|
+
eot: 'application/vnd.ms-fontopen',
|
|
490
|
+
};
|
|
491
|
+
res.setHeader('Content-Type', mimeMap[ext ?? ''] ?? 'application/octet-stream');
|
|
492
|
+
res.setHeader('Cache-Control', 'public, max-age=31536000, immutable');
|
|
493
|
+
res.setHeader('Access-Control-Allow-Origin', '*');
|
|
494
|
+
res.end(data);
|
|
495
|
+
return;
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
next();
|
|
501
|
+
});
|
|
502
|
+
},
|
|
503
|
+
|
|
379
504
|
/**
|
|
380
505
|
* Download and cache Google Fonts during production builds.
|
|
381
506
|
*
|
|
@@ -499,6 +624,11 @@ export function timberFonts(ctx: PluginContext): Plugin {
|
|
|
499
624
|
}
|
|
500
625
|
|
|
501
626
|
if (transformedCode !== code) {
|
|
627
|
+
// Inject side-effect import that registers font CSS on globalThis.
|
|
628
|
+
// The RSC entry reads globalThis.__timber_font_css to inline a <style> tag.
|
|
629
|
+
if (registry.size > 0) {
|
|
630
|
+
transformedCode = `import '${VIRTUAL_FONT_CSS_REGISTER}';\n` + transformedCode;
|
|
631
|
+
}
|
|
502
632
|
return { code: transformedCode, map: null };
|
|
503
633
|
}
|
|
504
634
|
|
|
@@ -525,6 +655,28 @@ export function timberFonts(ctx: PluginContext): Plugin {
|
|
|
525
655
|
});
|
|
526
656
|
}
|
|
527
657
|
|
|
658
|
+
// Emit local font files as assets
|
|
659
|
+
for (const font of registry.values()) {
|
|
660
|
+
if (font.provider !== 'local' || !font.localSources) continue;
|
|
661
|
+
for (const src of font.localSources) {
|
|
662
|
+
const absolutePath = normalize(resolve(src.path));
|
|
663
|
+
if (!existsSync(absolutePath)) {
|
|
664
|
+
this.warn(`Local font file not found: ${absolutePath}`);
|
|
665
|
+
continue;
|
|
666
|
+
}
|
|
667
|
+
const basename = src.path.split('/').pop() ?? src.path;
|
|
668
|
+
const data = readFileSync(absolutePath);
|
|
669
|
+
this.emitFile({
|
|
670
|
+
type: 'asset',
|
|
671
|
+
fileName: `_timber/fonts/${basename}`,
|
|
672
|
+
source: data,
|
|
673
|
+
});
|
|
674
|
+
}
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
// Font CSS is emitted by Vite's CSS pipeline via virtual:timber-font-css modules.
|
|
678
|
+
// We only need to emit font binary files and update the build manifest here.
|
|
679
|
+
|
|
528
680
|
if (!ctx.buildManifest) return;
|
|
529
681
|
|
|
530
682
|
// Build a lookup from font family → cached files for manifest entries
|
package/src/plugins/mdx.ts
CHANGED
|
@@ -16,19 +16,23 @@ import type { PluginContext } from '#/index.js';
|
|
|
16
16
|
const MDX_EXTENSIONS = ['mdx', 'md'];
|
|
17
17
|
|
|
18
18
|
/**
|
|
19
|
-
* Check if mdx-components.tsx (or .ts, .jsx, .js) exists at the project root
|
|
19
|
+
* Check if mdx-components.tsx (or .ts, .jsx, .js) exists at the project root
|
|
20
|
+
* or in src/. Root takes precedence, matching Next.js behavior.
|
|
20
21
|
* Returns the absolute path if found, otherwise undefined.
|
|
21
22
|
*/
|
|
22
|
-
function findMdxComponents(root: string): string | undefined {
|
|
23
|
+
export function findMdxComponents(root: string): string | undefined {
|
|
23
24
|
const candidates = [
|
|
24
25
|
'mdx-components.tsx',
|
|
25
26
|
'mdx-components.ts',
|
|
26
27
|
'mdx-components.jsx',
|
|
27
28
|
'mdx-components.js',
|
|
28
29
|
];
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
30
|
+
const dirs = [root, join(root, 'src')];
|
|
31
|
+
for (const dir of dirs) {
|
|
32
|
+
for (const name of candidates) {
|
|
33
|
+
const p = join(dir, name);
|
|
34
|
+
if (existsSync(p)) return p;
|
|
35
|
+
}
|
|
32
36
|
}
|
|
33
37
|
return undefined;
|
|
34
38
|
}
|
package/src/plugins/routing.ts
CHANGED
|
@@ -28,7 +28,7 @@ const RESOLVED_VIRTUAL_ID = `\0${VIRTUAL_MODULE_ID}`;
|
|
|
28
28
|
* File convention names we track for changes that require manifest regeneration.
|
|
29
29
|
*/
|
|
30
30
|
const ROUTE_FILE_PATTERNS =
|
|
31
|
-
/\/(page|layout|middleware|access|route|error|default|denied
|
|
31
|
+
/\/(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)\./;
|
|
32
32
|
|
|
33
33
|
/**
|
|
34
34
|
* Create the timber-routing Vite plugin.
|
|
@@ -168,20 +168,49 @@ export function timberRouting(ctx: PluginContext): Plugin {
|
|
|
168
168
|
// Watch the app directory
|
|
169
169
|
devServer.watcher.add(ctx.appDir);
|
|
170
170
|
|
|
171
|
-
|
|
172
|
-
|
|
171
|
+
/** Snapshot of the last generated manifest, used to detect structural changes. */
|
|
172
|
+
let lastManifest = ctx.routeTree ? generateManifestModule(ctx.routeTree) : '';
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Handle a route-significant file being added or removed.
|
|
176
|
+
* Always triggers a full-reload since the route tree structure changed.
|
|
177
|
+
*/
|
|
178
|
+
const handleStructuralChange = (filePath: string) => {
|
|
173
179
|
if (!filePath.startsWith(ctx.appDir)) return;
|
|
174
180
|
if (!ROUTE_FILE_PATTERNS.test(filePath)) return;
|
|
175
181
|
|
|
176
|
-
// Rescan the route tree
|
|
177
182
|
rescan();
|
|
178
|
-
|
|
179
|
-
// Invalidate the virtual module in all environments
|
|
183
|
+
lastManifest = ctx.routeTree ? generateManifestModule(ctx.routeTree) : '';
|
|
180
184
|
invalidateManifest(devServer);
|
|
181
185
|
};
|
|
182
186
|
|
|
183
|
-
|
|
184
|
-
|
|
187
|
+
/**
|
|
188
|
+
* Handle a route file's content changing.
|
|
189
|
+
*
|
|
190
|
+
* Most content edits (JSX changes, fixing typos) don't affect route
|
|
191
|
+
* metadata — Vite's React Fast Refresh handles those via normal HMR.
|
|
192
|
+
* Only rescan and full-reload when route metadata actually changed
|
|
193
|
+
* (e.g., searchParams export added/removed, metadata export changed).
|
|
194
|
+
*/
|
|
195
|
+
const handleContentChange = (filePath: string) => {
|
|
196
|
+
if (!filePath.startsWith(ctx.appDir)) return;
|
|
197
|
+
if (!ROUTE_FILE_PATTERNS.test(filePath)) return;
|
|
198
|
+
|
|
199
|
+
rescan();
|
|
200
|
+
const newManifest = ctx.routeTree ? generateManifestModule(ctx.routeTree) : '';
|
|
201
|
+
if (newManifest !== lastManifest) {
|
|
202
|
+
lastManifest = newManifest;
|
|
203
|
+
invalidateManifest(devServer);
|
|
204
|
+
}
|
|
205
|
+
// Otherwise: content edit didn't change route metadata — let Vite HMR handle it
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
devServer.watcher.on('add', handleStructuralChange);
|
|
209
|
+
devServer.watcher.on('unlink', handleStructuralChange);
|
|
210
|
+
// Watch content changes to page files — searchParams detection depends
|
|
211
|
+
// on file contents (export const searchParams), not just file presence.
|
|
212
|
+
// But only full-reload when route metadata actually changes.
|
|
213
|
+
devServer.watcher.on('change', handleContentChange);
|
|
185
214
|
// Also watch renames (which are add+unlink) — handled by the above
|
|
186
215
|
},
|
|
187
216
|
};
|
|
@@ -301,12 +330,9 @@ function generateManifestModule(tree: RouteTree): string {
|
|
|
301
330
|
`${nextIndent}denied: { load: ${v}, filePath: ${JSON.stringify(node.denied.filePath)} },`
|
|
302
331
|
);
|
|
303
332
|
}
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
`${nextIndent}searchParams: { load: ${v}, filePath: ${JSON.stringify(node.searchParams.filePath)} },`
|
|
308
|
-
);
|
|
309
|
-
}
|
|
333
|
+
// searchParams is now a named export from page.tsx, not a separate file.
|
|
334
|
+
// The page module's searchParams export is loaded via the page's lazy import.
|
|
335
|
+
// Runtime registration happens in the route loader using the page module.
|
|
310
336
|
|
|
311
337
|
// Status-code files
|
|
312
338
|
if (node.statusFiles && node.statusFiles.size > 0) {
|
|
@@ -137,5 +137,36 @@ export function timberServerBundle(): Plugin[] {
|
|
|
137
137
|
},
|
|
138
138
|
};
|
|
139
139
|
|
|
140
|
-
|
|
140
|
+
// Fix Rolldown's `createRequire(import.meta.url)` CJS interop shim for
|
|
141
|
+
// Cloudflare Workers. Rolldown emits this for CJS dependencies (e.g.
|
|
142
|
+
// @opentelemetry/context-async-hooks) that use `require()`. On Workers,
|
|
143
|
+
// `import.meta.url` is `undefined` for non-entry modules, causing:
|
|
144
|
+
// TypeError: The argument 'path' must be a file URL object, a file URL
|
|
145
|
+
// string, or an absolute path string. Received 'undefined'
|
|
146
|
+
//
|
|
147
|
+
// The fix: provide a fallback URL when `import.meta.url` is undefined.
|
|
148
|
+
// The actual URL doesn't matter — `createRequire` only needs it for
|
|
149
|
+
// resolving relative paths, but the only `__require()` calls are for
|
|
150
|
+
// Node built-ins (events, async_hooks) which resolve from any base.
|
|
151
|
+
//
|
|
152
|
+
// The top-level `ssr: { target: 'webworker' }` was supposed to prevent
|
|
153
|
+
// this, but it doesn't propagate to custom environments (rsc) in Vite's
|
|
154
|
+
// Environment API. See LOCAL-405.
|
|
155
|
+
const createRequireFixPlugin: Plugin = {
|
|
156
|
+
name: 'timber-create-require-fix',
|
|
157
|
+
applyToEnvironment(environment) {
|
|
158
|
+
return environment.name === 'rsc' || environment.name === 'ssr';
|
|
159
|
+
},
|
|
160
|
+
renderChunk(code) {
|
|
161
|
+
const pattern = 'createRequire(import.meta.url)';
|
|
162
|
+
if (!code.includes(pattern)) return null;
|
|
163
|
+
|
|
164
|
+
return {
|
|
165
|
+
code: code.replace(pattern, 'createRequire(import.meta.url || "file:///app")'),
|
|
166
|
+
map: null,
|
|
167
|
+
};
|
|
168
|
+
},
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
return [bundlePlugin, esmInitFixPlugin, createRequireFixPlugin];
|
|
141
172
|
}
|
package/src/plugins/shims.ts
CHANGED
|
@@ -137,7 +137,7 @@ export function timberShims(_ctx: PluginContext): Plugin {
|
|
|
137
137
|
// package.json exports), creating a module instance split: ssr-entry.ts
|
|
138
138
|
// registers the ALS-backed SSR data provider on the src/ instance of
|
|
139
139
|
// ssr-data.ts, but client component hooks read getSsrData() from the
|
|
140
|
-
// dist/ instance — which has no provider. Result: hooks like
|
|
140
|
+
// dist/ instance — which has no provider. Result: hooks like useSegmentParams()
|
|
141
141
|
// return empty defaults during SSR.
|
|
142
142
|
//
|
|
143
143
|
// This remap is SSR-only. The RSC environment still resolves to dist/
|
|
@@ -163,9 +163,6 @@ export function validateStaticMode(
|
|
|
163
163
|
* - transform: Validates source files for static mode violations
|
|
164
164
|
*/
|
|
165
165
|
export function timberStaticBuild(ctx: PluginContext): Plugin {
|
|
166
|
-
const isStatic = ctx.config.output === 'static';
|
|
167
|
-
const clientJavascriptDisabled = ctx.clientJavascript.disabled;
|
|
168
|
-
|
|
169
166
|
return {
|
|
170
167
|
name: 'timber-static-build',
|
|
171
168
|
|
|
@@ -177,6 +174,11 @@ export function timberStaticBuild(ctx: PluginContext): Plugin {
|
|
|
177
174
|
* - When client JS disabled: 'use client' / 'use server' directives → build error
|
|
178
175
|
*/
|
|
179
176
|
transform(code: string, id: string) {
|
|
177
|
+
// Read ctx.config lazily inside the hook — not at plugin construction
|
|
178
|
+
// time — so file-based config from timber.config.ts is respected.
|
|
179
|
+
// See TIM-451.
|
|
180
|
+
const isStatic = ctx.config.output === 'static';
|
|
181
|
+
|
|
180
182
|
// Only active in static mode
|
|
181
183
|
if (!isStatic) return null;
|
|
182
184
|
|
|
@@ -189,7 +191,9 @@ export function timberStaticBuild(ctx: PluginContext): Plugin {
|
|
|
189
191
|
// Only check JS/TS files
|
|
190
192
|
if (!/\.[jt]sx?$/.test(id)) return null;
|
|
191
193
|
|
|
192
|
-
const errors = validateStaticMode(code, id, {
|
|
194
|
+
const errors = validateStaticMode(code, id, {
|
|
195
|
+
clientJavascriptDisabled: ctx.clientJavascript.disabled,
|
|
196
|
+
});
|
|
193
197
|
|
|
194
198
|
if (errors.length > 0) {
|
|
195
199
|
// Format all errors into a single build error message
|