@timber-js/app 0.2.0-alpha.157 → 0.2.0-alpha.159
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-CqE7SW7E.js → actions-CBbP1hHO.js} +22 -6
- package/dist/_chunks/actions-CBbP1hHO.js.map +1 -0
- package/dist/_chunks/{build-output-helper-C8q4Zpf5.js → build-output-helper-DF5uScqS.js} +4 -1
- package/dist/_chunks/{build-output-helper-C8q4Zpf5.js.map → build-output-helper-DF5uScqS.js.map} +1 -1
- package/dist/_chunks/cache-api-DsWEYwVZ.js +1120 -0
- package/dist/_chunks/cache-api-DsWEYwVZ.js.map +1 -0
- package/dist/_chunks/{cli-schema-sync-DcmQrzJG.js → cli-schema-sync-Bivx4Rn7.js} +4 -3
- package/dist/_chunks/{cli-schema-sync-DcmQrzJG.js.map → cli-schema-sync-Bivx4Rn7.js.map} +1 -1
- package/dist/_chunks/dist-BA3u1z3W.js +423 -0
- package/dist/_chunks/dist-BA3u1z3W.js.map +1 -0
- package/dist/_chunks/{logger-C5v1IMuh.js → logger-CrRmZMlo.js} +81 -56
- package/dist/_chunks/logger-CrRmZMlo.js.map +1 -0
- package/dist/_chunks/{plugin-context-DUl3dnCF.js → plugin-context-DeAxFRMq.js} +3 -36
- package/dist/_chunks/plugin-context-DeAxFRMq.js.map +1 -0
- package/dist/_chunks/sizeof-lXffQ1Vr.js +35 -0
- package/dist/_chunks/sizeof-lXffQ1Vr.js.map +1 -0
- package/dist/_chunks/{walkers-D5jCGPum.js → walkers-DXbJkeCY.js} +217 -19
- package/dist/_chunks/walkers-DXbJkeCY.js.map +1 -0
- package/dist/adapters/build-output-helper.d.ts.map +1 -1
- package/dist/adapters/cloudflare.d.ts +28 -0
- package/dist/adapters/cloudflare.d.ts.map +1 -1
- package/dist/adapters/cloudflare.js +143 -9
- package/dist/adapters/cloudflare.js.map +1 -1
- package/dist/adapters/nitro.js +1 -1
- package/dist/cache/cache-api.d.ts +2 -0
- package/dist/cache/cache-api.d.ts.map +1 -1
- package/dist/cache/handler-store.d.ts +2 -0
- package/dist/cache/handler-store.d.ts.map +1 -1
- package/dist/cache/index.d.ts +2 -0
- package/dist/cache/index.d.ts.map +1 -1
- package/dist/cache/index.js +3 -186
- package/dist/cache/redis-handler.d.ts +17 -5
- package/dist/cache/redis-handler.d.ts.map +1 -1
- package/dist/cache/singleflight.d.ts +1 -1
- package/dist/cache/singleflight.d.ts.map +1 -1
- package/dist/cache/store.d.ts +53 -0
- package/dist/cache/store.d.ts.map +1 -0
- package/dist/cache/stores/cloudflare-kv.d.ts +38 -0
- package/dist/cache/stores/cloudflare-kv.d.ts.map +1 -0
- package/dist/cache/stores/cloudflare-kv.js +29 -0
- package/dist/cache/stores/cloudflare-kv.js.map +1 -0
- package/dist/cache/stores/memory.d.ts +42 -0
- package/dist/cache/stores/memory.d.ts.map +1 -0
- package/dist/cache/stores/memory.js +90 -0
- package/dist/cache/stores/memory.js.map +1 -0
- package/dist/cache/stores/redis.d.ts +69 -0
- package/dist/cache/stores/redis.d.ts.map +1 -0
- package/dist/cache/stores/redis.js +35 -0
- package/dist/cache/stores/redis.js.map +1 -0
- package/dist/cache/stores/vercel.d.ts +36 -0
- package/dist/cache/stores/vercel.d.ts.map +1 -0
- package/dist/cache/stores/vercel.js +33 -0
- package/dist/cache/stores/vercel.js.map +1 -0
- package/dist/cache/tag-aware-handler.d.ts +51 -0
- package/dist/cache/tag-aware-handler.d.ts.map +1 -0
- package/dist/cache/timber-cache.d.ts.map +1 -1
- package/dist/cdn/cloudflare-purge.d.ts +23 -0
- package/dist/cdn/cloudflare-purge.d.ts.map +1 -0
- package/dist/cdn/fastly-purge.d.ts +25 -0
- package/dist/cdn/fastly-purge.d.ts.map +1 -0
- package/dist/cdn/headers.d.ts +82 -0
- package/dist/cdn/headers.d.ts.map +1 -0
- package/dist/cdn/index.d.ts +5 -0
- package/dist/cdn/index.d.ts.map +1 -0
- package/dist/cdn/purge-store.d.ts +23 -0
- package/dist/cdn/purge-store.d.ts.map +1 -0
- package/dist/cli.js +2 -2
- package/dist/client/browser-entry/action-dispatch.d.ts.map +1 -1
- package/dist/client/browser-entry/post-hydration.d.ts.map +1 -1
- package/dist/client/browser-entry/router-init.d.ts.map +1 -1
- package/dist/client/history.d.ts +0 -3
- package/dist/client/history.d.ts.map +1 -1
- package/dist/client/index.js +0 -1
- package/dist/client/index.js.map +1 -1
- package/dist/client/internal.js +11 -86
- package/dist/client/internal.js.map +1 -1
- package/dist/client/navigation-api.d.ts +1 -3
- package/dist/client/navigation-api.d.ts.map +1 -1
- package/dist/client/router.d.ts +1 -4
- package/dist/client/router.d.ts.map +1 -1
- package/dist/client/rsc-fetch.d.ts +1 -11
- package/dist/client/rsc-fetch.d.ts.map +1 -1
- package/dist/client/segment-cache.d.ts +1 -3
- package/dist/client/segment-cache.d.ts.map +1 -1
- package/dist/client/use-cookie.d.ts.map +1 -1
- package/dist/config-types.d.ts +3 -1
- package/dist/config-types.d.ts.map +1 -1
- package/dist/config-validation.d.ts.map +1 -1
- package/dist/cookies/index.js +15 -11
- package/dist/cookies/index.js.map +1 -1
- package/dist/dev-tools/browser-logs.d.ts +28 -72
- package/dist/dev-tools/browser-logs.d.ts.map +1 -1
- package/dist/dev-tools/index.d.ts +1 -1
- package/dist/dev-tools/index.d.ts.map +1 -1
- package/dist/dev-tools/logger.d.ts.map +1 -1
- package/dist/dev-tools/terminal.d.ts.map +1 -1
- package/dist/fonts/transform.d.ts +4 -0
- package/dist/fonts/transform.d.ts.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1269 -359
- package/dist/index.js.map +1 -1
- package/dist/plugin-context.d.ts +36 -0
- package/dist/plugin-context.d.ts.map +1 -1
- package/dist/plugins/build-report.d.ts +21 -0
- package/dist/plugins/build-report.d.ts.map +1 -1
- package/dist/plugins/cache.d.ts +5 -14
- package/dist/plugins/cache.d.ts.map +1 -1
- package/dist/plugins/callsite-ast.d.ts +95 -0
- package/dist/plugins/callsite-ast.d.ts.map +1 -0
- package/dist/plugins/callsite-jsx.d.ts +35 -0
- package/dist/plugins/callsite-jsx.d.ts.map +1 -0
- package/dist/plugins/entries.d.ts.map +1 -1
- package/dist/plugins/fonts.d.ts.map +1 -1
- package/dist/plugins/prebuilt-capture.d.ts +60 -0
- package/dist/plugins/prebuilt-capture.d.ts.map +1 -0
- package/dist/plugins/prebuilt.d.ts +82 -0
- package/dist/plugins/prebuilt.d.ts.map +1 -0
- package/dist/plugins/prerender-sugar.d.ts +42 -0
- package/dist/plugins/prerender-sugar.d.ts.map +1 -0
- package/dist/plugins/server-bundle.d.ts.map +1 -1
- package/dist/plugins/shims.d.ts.map +1 -1
- package/dist/plugins/static-build.d.ts.map +1 -1
- package/dist/routing/convention-lint.d.ts.map +1 -1
- package/dist/routing/export-detect.d.ts +56 -0
- package/dist/routing/export-detect.d.ts.map +1 -0
- package/dist/routing/index.js +2 -2
- package/dist/routing/manifest-codegen.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/server/action-handler.d.ts.map +1 -1
- package/dist/server/actions.d.ts +0 -3
- package/dist/server/actions.d.ts.map +1 -1
- package/dist/server/als-registry.d.ts +7 -0
- package/dist/server/als-registry.d.ts.map +1 -1
- package/dist/server/cookie-parsing.d.ts +15 -1
- package/dist/server/cookie-parsing.d.ts.map +1 -1
- package/dist/server/deny-renderer.d.ts.map +1 -1
- package/dist/server/form-data.d.ts +12 -0
- package/dist/server/form-data.d.ts.map +1 -1
- package/dist/server/html-injectors.d.ts +1 -0
- package/dist/server/html-injectors.d.ts.map +1 -1
- package/dist/server/index.js +2 -2
- package/dist/server/internal.d.ts +6 -1
- package/dist/server/internal.d.ts.map +1 -1
- package/dist/server/internal.js +563 -74
- package/dist/server/internal.js.map +1 -1
- package/dist/server/prebuilt/cache-key.d.ts +58 -0
- package/dist/server/prebuilt/cache-key.d.ts.map +1 -0
- package/dist/server/prebuilt/capture-state.d.ts +57 -0
- package/dist/server/prebuilt/capture-state.d.ts.map +1 -0
- package/dist/server/prebuilt/manifest-tag-index.d.ts +27 -0
- package/dist/server/prebuilt/manifest-tag-index.d.ts.map +1 -0
- package/dist/server/prebuilt/overlay.d.ts +100 -0
- package/dist/server/prebuilt/overlay.d.ts.map +1 -0
- package/dist/server/prebuilt/payload-source.d.ts +75 -0
- package/dist/server/prebuilt/payload-source.d.ts.map +1 -0
- package/dist/server/prebuilt/synthetic-store.d.ts +64 -0
- package/dist/server/prebuilt/synthetic-store.d.ts.map +1 -0
- package/dist/server/prebuilt-builder.d.ts +110 -0
- package/dist/server/prebuilt-builder.d.ts.map +1 -0
- package/dist/server/prebuilt-runtime.d.ts +101 -0
- package/dist/server/prebuilt-runtime.d.ts.map +1 -0
- package/dist/server/route-element-builder.d.ts +10 -8
- package/dist/server/route-element-builder.d.ts.map +1 -1
- package/dist/server/rsc-entry/helpers.d.ts +0 -5
- package/dist/server/rsc-entry/helpers.d.ts.map +1 -1
- package/dist/server/rsc-entry/index.d.ts +10 -0
- 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 +3 -4
- package/dist/server/rsc-entry/rsc-payload.d.ts.map +1 -1
- package/dist/server/sitemap-generator.d.ts +6 -6
- package/dist/server/sitemap-generator.d.ts.map +1 -1
- package/dist/server/tracing.d.ts +37 -3
- package/dist/server/tracing.d.ts.map +1 -1
- package/docs/api/32-api-cache.mdx +10 -4
- package/docs/learn/00-introduction.mdx +8 -13
- package/docs/learn/02-pages-and-layouts.mdx +9 -0
- package/docs/learn/06-forms-and-actions.mdx +0 -4
- package/docs/learn/07-typed-routes.mdx +127 -0
- package/docs/learn/09-caching.mdx +12 -1
- package/docs/learn/10-middleware.mdx +1 -0
- package/docs/learn/11-error-handling.mdx +9 -0
- package/docs/learn/13-configuration.mdx +28 -0
- package/docs/learn/14-deploying.mdx +52 -0
- package/docs/more/06-instrumentation.mdx +27 -1
- package/docs/more/07-security.mdx +8 -2
- package/docs/more/08-developer-experience.mdx +134 -0
- package/package.json +31 -2
- package/src/adapters/build-output-helper.ts +10 -0
- package/src/adapters/cloudflare.ts +178 -4
- package/src/cache/cache-api.ts +60 -0
- package/src/cache/handler-store.ts +8 -0
- package/src/cache/index.ts +4 -0
- package/src/cache/redis-handler.ts +25 -13
- package/src/cache/singleflight.ts +11 -11
- package/src/cache/store.ts +71 -0
- package/src/cache/stores/cloudflare-kv.ts +52 -0
- package/src/cache/stores/memory.ts +125 -0
- package/src/cache/stores/redis.ts +97 -0
- package/src/cache/stores/vercel.ts +59 -0
- package/src/cache/tag-aware-handler.ts +233 -0
- package/src/cache/timber-cache.ts +17 -7
- package/src/cdn/cloudflare-purge.ts +50 -0
- package/src/cdn/fastly-purge.ts +63 -0
- package/src/cdn/headers.ts +127 -0
- package/src/cdn/index.ts +16 -0
- package/src/cdn/purge-store.ts +34 -0
- package/src/client/browser-entry/action-dispatch.ts +1 -10
- package/src/client/browser-entry/post-hydration.ts +0 -1
- package/src/client/browser-entry/router-init.ts +0 -5
- package/src/client/history.ts +0 -4
- package/src/client/navigation-api.ts +5 -11
- package/src/client/router.ts +9 -43
- package/src/client/rsc-fetch.ts +5 -27
- package/src/client/segment-cache.ts +1 -4
- package/src/client/use-cookie.ts +18 -12
- package/src/config-types.ts +3 -1
- package/src/config-validation.ts +1 -0
- package/src/dev-tools/browser-logs.ts +37 -273
- package/src/dev-tools/index.ts +2 -2
- package/src/dev-tools/instrumentation.ts +1 -1
- package/src/dev-tools/logger.ts +33 -51
- package/src/dev-tools/terminal.ts +21 -22
- package/src/fonts/transform.ts +4 -43
- package/src/index.ts +16 -3
- package/src/plugin-context.ts +36 -0
- package/src/plugins/build-report.ts +133 -1
- package/src/plugins/cache.ts +32 -131
- package/src/plugins/callsite-ast.ts +167 -0
- package/src/plugins/callsite-jsx.ts +243 -0
- package/src/plugins/entries.ts +23 -4
- package/src/plugins/fonts.ts +16 -2
- package/src/plugins/prebuilt-capture.ts +208 -0
- package/src/plugins/prebuilt.ts +673 -0
- package/src/plugins/prerender-sugar.ts +329 -0
- package/src/plugins/server-bundle.ts +12 -0
- package/src/plugins/shims.ts +9 -0
- package/src/plugins/static-build.ts +0 -1
- package/src/routing/codegen.ts +1 -21
- package/src/routing/convention-lint.ts +85 -49
- package/src/routing/export-detect.ts +304 -0
- package/src/routing/manifest-codegen.ts +2 -11
- package/src/rsc-runtime/rsc.ts +7 -0
- package/src/server/action-handler.ts +11 -27
- package/src/server/actions.ts +3 -4
- package/src/server/als-registry.ts +7 -0
- package/src/server/cookie-parsing.ts +75 -59
- package/src/server/deny-renderer.ts +7 -15
- package/src/server/form-data.ts +23 -7
- package/src/server/html-injectors.ts +11 -3
- package/src/server/internal.ts +17 -1
- package/src/server/prebuilt/cache-key.ts +224 -0
- package/src/server/prebuilt/capture-state.ts +89 -0
- package/src/server/prebuilt/manifest-tag-index.ts +46 -0
- package/src/server/prebuilt/overlay.ts +256 -0
- package/src/server/prebuilt/payload-source.ts +192 -0
- package/src/server/prebuilt/synthetic-store.ts +108 -0
- package/src/server/prebuilt-builder.ts +640 -0
- package/src/server/prebuilt-runtime.ts +706 -0
- package/src/server/route-element-builder.ts +40 -12
- package/src/server/rsc-entry/helpers.ts +0 -17
- package/src/server/rsc-entry/index.ts +83 -0
- package/src/server/rsc-entry/render-route.ts +6 -33
- package/src/server/rsc-entry/revalidate-renderer.ts +16 -11
- package/src/server/rsc-entry/rsc-payload.ts +5 -11
- package/src/server/sitemap-generator.ts +16 -10
- package/src/server/tracing.ts +88 -3
- package/dist/_chunks/actions-CqE7SW7E.js.map +0 -1
- package/dist/_chunks/cache-api-Cbyh3RW4.js +0 -401
- package/dist/_chunks/cache-api-Cbyh3RW4.js.map +0 -1
- package/dist/_chunks/logger-C5v1IMuh.js.map +0 -1
- package/dist/_chunks/plugin-context-DUl3dnCF.js.map +0 -1
- package/dist/_chunks/validation-CnKhGJsp.js +0 -113
- package/dist/_chunks/validation-CnKhGJsp.js.map +0 -1
- package/dist/_chunks/walkers-D5jCGPum.js.map +0 -1
- package/dist/cache/index.js.map +0 -1
- package/dist/client/head.d.ts +0 -16
- package/dist/client/head.d.ts.map +0 -1
- package/dist/client/navigation-api-types.d.ts +0 -90
- package/dist/client/navigation-api-types.d.ts.map +0 -1
- package/dist/server/client-module-map.d.ts +0 -47
- package/dist/server/client-module-map.d.ts.map +0 -1
- package/dist/shared/head-header.d.ts +0 -54
- package/dist/shared/head-header.d.ts.map +0 -1
- package/src/client/head.ts +0 -61
- package/src/client/navigation-api-types.ts +0 -112
- package/src/server/client-module-map.ts +0 -58
- package/src/shared/head-header.ts +0 -69
package/src/fonts/transform.ts
CHANGED
|
@@ -139,32 +139,6 @@ function buildFontResultLiteral(extracted: ExtractedFont): string {
|
|
|
139
139
|
/** Type for the Vite plugin's `this.error` callback. */
|
|
140
140
|
type EmitError = (msg: string) => never;
|
|
141
141
|
|
|
142
|
-
/**
|
|
143
|
-
* File extensions that may contain real font imports. Only JS/TS module
|
|
144
|
-
* variants are scanned. Excluding non-JS extensions (.mdx, .md, .css,
|
|
145
|
-
* .json, etc.) prevents false-positive matches on text that mentions a
|
|
146
|
-
* font specifier inside documentation, code fences, or asset metadata.
|
|
147
|
-
*
|
|
148
|
-
* See TIM-840.
|
|
149
|
-
*/
|
|
150
|
-
const FONT_IMPORT_SCANNABLE_EXTENSIONS = new Set([
|
|
151
|
-
'.js',
|
|
152
|
-
'.jsx',
|
|
153
|
-
'.ts',
|
|
154
|
-
'.tsx',
|
|
155
|
-
'.mjs',
|
|
156
|
-
'.cjs',
|
|
157
|
-
'.mts',
|
|
158
|
-
'.cts',
|
|
159
|
-
]);
|
|
160
|
-
|
|
161
|
-
function hasScannableExtension(id: string): boolean {
|
|
162
|
-
const path = id.split('?', 1)[0];
|
|
163
|
-
const dot = path.lastIndexOf('.');
|
|
164
|
-
if (dot === -1) return false;
|
|
165
|
-
return FONT_IMPORT_SCANNABLE_EXTENSIONS.has(path.slice(dot).toLowerCase());
|
|
166
|
-
}
|
|
167
|
-
|
|
168
142
|
/**
|
|
169
143
|
* Run the timber-fonts transform pass on a single source file.
|
|
170
144
|
*
|
|
@@ -174,6 +148,10 @@ function hasScannableExtension(id: string): boolean {
|
|
|
174
148
|
* source map alongside the transformed code.
|
|
175
149
|
*
|
|
176
150
|
* Returns `null` if the file does not import from any font virtual module.
|
|
151
|
+
*
|
|
152
|
+
* Pre-filtering (extension allow-list, node_modules exclusion, and code
|
|
153
|
+
* substring checks) is handled by Vite 8's rolldown-native `transform.filter`
|
|
154
|
+
* in the plugin hook definition (plugins/fonts.ts). See TIM-840, TIM-1147.
|
|
177
155
|
*/
|
|
178
156
|
export function runFontsTransform(
|
|
179
157
|
code: string,
|
|
@@ -181,23 +159,6 @@ export function runFontsTransform(
|
|
|
181
159
|
pipeline: FontPipeline,
|
|
182
160
|
emitError: EmitError
|
|
183
161
|
): { code: string; map: ReturnType<MagicString['generateMap']> } | null {
|
|
184
|
-
// Skip virtual modules and node_modules
|
|
185
|
-
if (id.startsWith('\0') || id.includes('node_modules')) return null;
|
|
186
|
-
|
|
187
|
-
// Only scan JS/TS module variants (see TIM-840).
|
|
188
|
-
if (!hasScannableExtension(id)) return null;
|
|
189
|
-
|
|
190
|
-
// Fast pre-filter: skip files that don't mention any font module.
|
|
191
|
-
const hasGoogleImport =
|
|
192
|
-
code.includes('@timber/fonts/google') ||
|
|
193
|
-
code.includes('@timber-js/app/fonts/google') ||
|
|
194
|
-
code.includes('next/font/google');
|
|
195
|
-
const hasLocalImport =
|
|
196
|
-
code.includes('@timber/fonts/local') ||
|
|
197
|
-
code.includes('@timber-js/app/fonts/local') ||
|
|
198
|
-
code.includes('next/font/local');
|
|
199
|
-
if (!hasGoogleImport && !hasLocalImport) return null;
|
|
200
|
-
|
|
201
162
|
// Single parse + AST walk.
|
|
202
163
|
const analysis = findFontCallSites(code);
|
|
203
164
|
if (!analysis) return null;
|
package/src/index.ts
CHANGED
|
@@ -24,12 +24,14 @@ import { timberFonts } from './plugins/fonts';
|
|
|
24
24
|
import { timberStaticBuild } from './plugins/static-build';
|
|
25
25
|
import { timberBuildManifest } from './plugins/build-manifest';
|
|
26
26
|
import { timberDevLogs } from './dev-tools/logs';
|
|
27
|
-
import {
|
|
27
|
+
import { resolveForwardConsole } from './dev-tools/browser-logs';
|
|
28
28
|
import { timberReactProd } from './plugins/react-prod';
|
|
29
29
|
import { timberChunks } from './plugins/chunks';
|
|
30
30
|
import { clientChunkGroup } from './plugins/client-chunks';
|
|
31
31
|
import { timberServerBundle } from './plugins/server-bundle';
|
|
32
32
|
import { timberCacheTransform } from './plugins/cache';
|
|
33
|
+
import { timberPrebuilt } from './plugins/prebuilt';
|
|
34
|
+
import { timberPrerenderSugar } from './plugins/prerender-sugar';
|
|
33
35
|
import { timberCloudflareWasm } from './plugins/cloudflare-wasm';
|
|
34
36
|
import { timberAdapterBuild } from './plugins/adapter-build';
|
|
35
37
|
import { timberBuildReport } from './plugins/build-report';
|
|
@@ -416,7 +418,11 @@ export function timber(config?: TimberUserConfig): PluginOption[] {
|
|
|
416
418
|
// auto-bump from 3000. Explicit preview uses strictPort: true
|
|
417
419
|
// so conflicts fail loudly, matching the dev behavior and the
|
|
418
420
|
// TIM-842 spec.
|
|
419
|
-
const serverConfig: {
|
|
421
|
+
const serverConfig: {
|
|
422
|
+
port?: number;
|
|
423
|
+
strictPort?: boolean;
|
|
424
|
+
forwardConsole?: boolean | { unhandledErrors?: boolean; logLevels?: string[] };
|
|
425
|
+
} = {};
|
|
420
426
|
const previewConfig: { port?: number; strictPort?: boolean } = {};
|
|
421
427
|
if (resolvedDevPort != null) {
|
|
422
428
|
if (!isPreview) {
|
|
@@ -426,6 +432,12 @@ export function timber(config?: TimberUserConfig): PluginOption[] {
|
|
|
426
432
|
previewConfig.port = resolvedDevPort;
|
|
427
433
|
previewConfig.strictPort = resolvedDevPortExplicit;
|
|
428
434
|
}
|
|
435
|
+
|
|
436
|
+
// Enable Vite 8's built-in browser→terminal console forwarding.
|
|
437
|
+
// Vite defaults this OFF for non-agent dev; timber enables it based
|
|
438
|
+
// on the devBrowserLogs config (default: 'warn'). See TIM-1152.
|
|
439
|
+
serverConfig.forwardConsole = resolveForwardConsole(ctx.config.devBrowserLogs);
|
|
440
|
+
|
|
429
441
|
return {
|
|
430
442
|
build: { outDir: buildOutDir },
|
|
431
443
|
environments: envOutDirs,
|
|
@@ -535,6 +547,8 @@ export function timber(config?: TimberUserConfig): PluginOption[] {
|
|
|
535
547
|
timberFonts(ctx),
|
|
536
548
|
timberMdx(ctx),
|
|
537
549
|
timberCacheTransform(ctx),
|
|
550
|
+
timberPrerenderSugar(ctx), // export const prerender → cache.component() desugaring
|
|
551
|
+
timberPrebuilt(ctx), // cache.component() → __prebuilt() callsite rewrite
|
|
538
552
|
timberContent(ctx),
|
|
539
553
|
timberServerBundle(), // Bundle all deps in server environments for prod
|
|
540
554
|
timberCloudflareWasm(ctx), // Externalize WASM for Cloudflare Workers
|
|
@@ -542,7 +556,6 @@ export function timber(config?: TimberUserConfig): PluginOption[] {
|
|
|
542
556
|
timberBuildReport(ctx), // Post-build: route table with bundle sizes
|
|
543
557
|
timberAdapterBuild(ctx), // Post-build: invoke adapter.buildOutput()
|
|
544
558
|
timberDevLogs(ctx), // Dev-only: forward server console.* to browser console
|
|
545
|
-
timberDevBrowserLogs(ctx), // Dev-only: forward browser console.* to server terminal
|
|
546
559
|
timberDevServer(ctx), // Must be last — configureServer post-hook runs after all watchers
|
|
547
560
|
];
|
|
548
561
|
}
|
package/src/plugin-context.ts
CHANGED
|
@@ -13,6 +13,7 @@ import { join, resolve } from 'node:path';
|
|
|
13
13
|
import { createRequire } from 'node:module';
|
|
14
14
|
import type { RouteTree } from './routing/types';
|
|
15
15
|
import type { BuildManifest } from './server/build-manifest';
|
|
16
|
+
import type { CaptureSummary } from './server/prebuilt-builder';
|
|
16
17
|
import type { StartupTimer } from './utils/startup-timer';
|
|
17
18
|
import { createStartupTimer } from './utils/startup-timer';
|
|
18
19
|
import type { TimberUserConfig, ClientJavascriptConfig } from './config-types.js';
|
|
@@ -103,6 +104,41 @@ export interface PluginContext {
|
|
|
103
104
|
* See design/27-chunking-strategy.md, TIM-1107.
|
|
104
105
|
*/
|
|
105
106
|
sharedModuleFacades?: Map<string, string[]>;
|
|
107
|
+
/**
|
|
108
|
+
* Module ID → prebuilt component IDs declared in it. Recorded by the
|
|
109
|
+
* timber-prebuilt transform (RSC environment, production builds only),
|
|
110
|
+
* consumed by the RSC `generateBundle` module-graph walk that computes
|
|
111
|
+
* `routePrebuiltComponents`. See design/44-render-at-build-time.md.
|
|
112
|
+
*/
|
|
113
|
+
prebuiltModuleComponents?: Map<string, string[]>;
|
|
114
|
+
/**
|
|
115
|
+
* Module ID → mapping of variable name to component ID for the prebuilt
|
|
116
|
+
* components declared in that module. Used at `generateBundle` time to
|
|
117
|
+
* trace imports from other modules back to specific component IDs.
|
|
118
|
+
*/
|
|
119
|
+
prebuiltBindingNames?: Map<string, Map<string, string>>;
|
|
120
|
+
/**
|
|
121
|
+
* Component ID → distinct static call-site prop objects captured from
|
|
122
|
+
* JSX usage sites during the transform and generateBundle phases.
|
|
123
|
+
* Consumed by the post-build capture pass to render with actual props
|
|
124
|
+
* instead of empty `{}`. See design/44 §Props Derivation.
|
|
125
|
+
*/
|
|
126
|
+
prebuiltCallSiteProps?: Map<string, Record<string, unknown>[]>;
|
|
127
|
+
/**
|
|
128
|
+
* Route file path (page or layout, absolute) → prebuilt component IDs
|
|
129
|
+
* transitively reachable from it. Computed during RSC `generateBundle`,
|
|
130
|
+
* consumed by the post-build capture pass (TIM-1119) to decide which
|
|
131
|
+
* components to render under which route's param combos.
|
|
132
|
+
*/
|
|
133
|
+
routePrebuiltComponents?: Map<string, string[]>;
|
|
134
|
+
/**
|
|
135
|
+
* Page file paths that were desugared from `export const prerender` by
|
|
136
|
+
* timber-prerender-sugar. The prebuilt transform uses this to verify
|
|
137
|
+
* the generated `cache.component(...)` call was picked up.
|
|
138
|
+
*/
|
|
139
|
+
prerenderSugarPages?: Set<string>;
|
|
140
|
+
/** Post-build prebuilt capture summary (populated by timber-prebuilt, consumed by build-report). */
|
|
141
|
+
captureSummary?: CaptureSummary;
|
|
106
142
|
}
|
|
107
143
|
|
|
108
144
|
// ── App directory resolution ──────────────────────────────────────────────
|
|
@@ -13,11 +13,14 @@
|
|
|
13
13
|
* Task: TIM-287
|
|
14
14
|
*/
|
|
15
15
|
|
|
16
|
-
import {
|
|
16
|
+
import { readFileSync } from 'node:fs';
|
|
17
|
+
import { join, relative } from 'node:path';
|
|
17
18
|
import { gzipSync } from 'node:zlib';
|
|
18
19
|
import type { Plugin, Logger } from 'vite';
|
|
19
20
|
import type { PluginContext } from '../plugin-context.js';
|
|
20
21
|
import type { SegmentNode, RouteTree } from '../routing/types.js';
|
|
22
|
+
import type { CaptureSummary } from '../server/prebuilt-builder.js';
|
|
23
|
+
import type { PrebuiltManifest } from '../server/prebuilt/payload-source.js';
|
|
21
24
|
import { collectLeafRoutes } from '../routing/walkers.js';
|
|
22
25
|
import { formatSize } from '../utils/format.js';
|
|
23
26
|
|
|
@@ -186,6 +189,120 @@ function stripAnsi(str: string): string {
|
|
|
186
189
|
return str.replace(/\u001b\[\d+m/g, '');
|
|
187
190
|
}
|
|
188
191
|
|
|
192
|
+
// ─── Prebuilt component stats ────────────────────────────────────────────
|
|
193
|
+
|
|
194
|
+
export interface PrebuiltComponentStats {
|
|
195
|
+
componentId: string;
|
|
196
|
+
entryCount: number;
|
|
197
|
+
totalBytes: number;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
export interface PrebuiltReportData {
|
|
201
|
+
components: PrebuiltComponentStats[];
|
|
202
|
+
totalEntries: number;
|
|
203
|
+
totalBytes: number;
|
|
204
|
+
skippedCount: number;
|
|
205
|
+
skippedReasons: Array<{ componentId: string; route: string; message: string }>;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
export function loadPrebuiltManifest(buildDir: string): PrebuiltManifest | null {
|
|
209
|
+
try {
|
|
210
|
+
const raw = readFileSync(join(buildDir, 'prebuilt', 'manifest.json'), 'utf-8');
|
|
211
|
+
return JSON.parse(raw) as PrebuiltManifest;
|
|
212
|
+
} catch {
|
|
213
|
+
return null;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
export function buildPrebuiltReportData(
|
|
218
|
+
manifest: PrebuiltManifest | null,
|
|
219
|
+
summary: CaptureSummary | undefined
|
|
220
|
+
): PrebuiltReportData | null {
|
|
221
|
+
const hasCaptures = summary && (summary.captured > 0 || summary.skipped.length > 0);
|
|
222
|
+
if (!manifest && !hasCaptures) return null;
|
|
223
|
+
|
|
224
|
+
const components: PrebuiltComponentStats[] = [];
|
|
225
|
+
let totalEntries = 0;
|
|
226
|
+
let totalBytes = 0;
|
|
227
|
+
|
|
228
|
+
if (manifest) {
|
|
229
|
+
for (const [componentId, component] of Object.entries(manifest)) {
|
|
230
|
+
let entryCount = 0;
|
|
231
|
+
let componentBytes = 0;
|
|
232
|
+
for (const entry of Object.values(component.entries)) {
|
|
233
|
+
entryCount++;
|
|
234
|
+
componentBytes += entry.bytes ?? 0;
|
|
235
|
+
}
|
|
236
|
+
components.push({ componentId, entryCount, totalBytes: componentBytes });
|
|
237
|
+
totalEntries += entryCount;
|
|
238
|
+
totalBytes += componentBytes;
|
|
239
|
+
}
|
|
240
|
+
components.sort((a, b) => a.componentId.localeCompare(b.componentId));
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
const skippedCount = summary?.skipped.length ?? 0;
|
|
244
|
+
const skippedReasons = (summary?.skipped ?? []).map((s) => ({
|
|
245
|
+
componentId: s.componentId,
|
|
246
|
+
route: s.routeUrlPath,
|
|
247
|
+
message: s.message,
|
|
248
|
+
}));
|
|
249
|
+
|
|
250
|
+
return { components, totalEntries, totalBytes, skippedCount, skippedReasons };
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
export function buildPrebuiltReport(data: PrebuiltReportData): string[] {
|
|
254
|
+
const lines: string[] = [];
|
|
255
|
+
|
|
256
|
+
const total = data.totalEntries + data.skippedCount;
|
|
257
|
+
lines.push(
|
|
258
|
+
`Prebuilt ${data.totalEntries}/${total} entries across ` +
|
|
259
|
+
`${data.components.length} component${data.components.length !== 1 ? 's' : ''}` +
|
|
260
|
+
(data.totalBytes > 0 ? ` (${formatSize(data.totalBytes)} total)` : '') +
|
|
261
|
+
'.' +
|
|
262
|
+
(data.skippedCount > 0 ? ` ${data.skippedCount} skipped due to errors.` : '')
|
|
263
|
+
);
|
|
264
|
+
|
|
265
|
+
if (data.components.length > 0) {
|
|
266
|
+
lines.push('');
|
|
267
|
+
const nameHeader = 'Component';
|
|
268
|
+
const entriesHeader = 'Entries';
|
|
269
|
+
const sizeHeader = 'Size';
|
|
270
|
+
|
|
271
|
+
const nameW = Math.max(
|
|
272
|
+
nameHeader.length + 2,
|
|
273
|
+
...data.components.map((c) => c.componentId.length + 4)
|
|
274
|
+
);
|
|
275
|
+
const entriesW = Math.max(entriesHeader.length, 8);
|
|
276
|
+
const sizeW = Math.max(sizeHeader.length, 10);
|
|
277
|
+
const totalW = nameW + entriesW + sizeW + 4;
|
|
278
|
+
const sep = '─'.repeat(totalW);
|
|
279
|
+
|
|
280
|
+
lines.push(
|
|
281
|
+
`${pad(nameHeader, nameW)} ${pad(entriesHeader, entriesW, 'left')} ${pad(sizeHeader, sizeW, 'left')}`
|
|
282
|
+
);
|
|
283
|
+
lines.push(sep);
|
|
284
|
+
|
|
285
|
+
for (const comp of data.components) {
|
|
286
|
+
const nameStr = ` ◆ ${comp.componentId}`;
|
|
287
|
+
const entriesStr = String(comp.entryCount);
|
|
288
|
+
const sizeStr = formatSize(comp.totalBytes);
|
|
289
|
+
lines.push(
|
|
290
|
+
`${pad(nameStr, nameW)} ${pad(entriesStr, entriesW, 'left')} ${pad(sizeStr, sizeW, 'left')}`
|
|
291
|
+
);
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
if (data.skippedReasons.length > 0) {
|
|
296
|
+
lines.push('');
|
|
297
|
+
lines.push('Skipped entries:');
|
|
298
|
+
for (const s of data.skippedReasons) {
|
|
299
|
+
lines.push(` ⚠ ${s.componentId} (${s.route}) — ${s.message}`);
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
return lines;
|
|
304
|
+
}
|
|
305
|
+
|
|
189
306
|
// ─── Bundle analysis ──────────────────────────────────────────────────────
|
|
190
307
|
|
|
191
308
|
interface ChunkSize {
|
|
@@ -455,5 +572,20 @@ export function timberBuildReport(ctx: PluginContext): Plugin {
|
|
|
455
572
|
);
|
|
456
573
|
logger.info('');
|
|
457
574
|
},
|
|
575
|
+
|
|
576
|
+
buildApp: {
|
|
577
|
+
order: 'post' as const,
|
|
578
|
+
async handler() {
|
|
579
|
+
if (ctx.dev || !logger) return;
|
|
580
|
+
const manifest = loadPrebuiltManifest(ctx.buildDir);
|
|
581
|
+
const prebuiltData = buildPrebuiltReportData(manifest, ctx.captureSummary);
|
|
582
|
+
if (!prebuiltData) return;
|
|
583
|
+
|
|
584
|
+
logger.info('');
|
|
585
|
+
const prebuiltLines = buildPrebuiltReport(prebuiltData);
|
|
586
|
+
for (const line of prebuiltLines) logger.info(line);
|
|
587
|
+
logger.info('');
|
|
588
|
+
},
|
|
589
|
+
},
|
|
458
590
|
};
|
|
459
591
|
}
|
package/src/plugins/cache.ts
CHANGED
|
@@ -47,6 +47,9 @@
|
|
|
47
47
|
* referencing the same bindings — the same function, where an ID swap is
|
|
48
48
|
* harmless.
|
|
49
49
|
*
|
|
50
|
+
* AST helpers and the ID derivation are shared with timber-prebuilt
|
|
51
|
+
* (plugins/prebuilt.ts) via plugins/callsite-ast.ts.
|
|
52
|
+
*
|
|
50
53
|
* See TIM-997, TIM-1053, design/06-caching.md §"Cache Key Security".
|
|
51
54
|
*/
|
|
52
55
|
|
|
@@ -55,84 +58,20 @@ import MagicString from 'magic-string';
|
|
|
55
58
|
import { parseAst } from 'vite';
|
|
56
59
|
import type { Plugin } from 'vite';
|
|
57
60
|
import type { PluginContext } from '../plugin-context.js';
|
|
58
|
-
import {
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
interface ImportDeclarationNode extends PositionedNode {
|
|
75
|
-
type: 'ImportDeclaration';
|
|
76
|
-
source: { value: string };
|
|
77
|
-
specifiers: Array<
|
|
78
|
-
PositionedNode & {
|
|
79
|
-
type: string;
|
|
80
|
-
local: IdentifierNode;
|
|
81
|
-
imported?: IdentifierNode;
|
|
82
|
-
}
|
|
83
|
-
>;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
interface CallExpressionNode extends PositionedNode {
|
|
87
|
-
type: 'CallExpression';
|
|
88
|
-
callee: PositionedNode;
|
|
89
|
-
arguments: PositionedNode[];
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
interface MemberExpressionNode extends PositionedNode {
|
|
93
|
-
type: 'MemberExpression';
|
|
94
|
-
object: PositionedNode;
|
|
95
|
-
property: PositionedNode;
|
|
96
|
-
computed: boolean;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
interface ProgramNode extends PositionedNode {
|
|
100
|
-
type: 'Program';
|
|
101
|
-
body: PositionedNode[];
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
interface CacheBindings {
|
|
105
|
-
/** Local names bound to `cache` via named imports (`import { cache as c }`). */
|
|
106
|
-
named: Set<string>;
|
|
107
|
-
/** Local names of namespace imports (`import * as ns`) of a cache module. */
|
|
108
|
-
namespaces: Set<string>;
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
/**
|
|
112
|
-
* Find the local binding name(s) for `cache` imported from the cache module —
|
|
113
|
-
* both named imports and namespace imports (TIM-1054).
|
|
114
|
-
*/
|
|
115
|
-
function findCacheBindings(program: ProgramNode): CacheBindings {
|
|
116
|
-
const named = new Set<string>();
|
|
117
|
-
const namespaces = new Set<string>();
|
|
118
|
-
for (const stmt of program.body) {
|
|
119
|
-
if (stmt.type !== 'ImportDeclaration') continue;
|
|
120
|
-
const decl = stmt as unknown as ImportDeclarationNode;
|
|
121
|
-
if (!CACHE_MODULE_SOURCES.has(decl.source.value)) continue;
|
|
122
|
-
for (const spec of decl.specifiers) {
|
|
123
|
-
if (spec.type === 'ImportNamespaceSpecifier') {
|
|
124
|
-
namespaces.add(spec.local.name);
|
|
125
|
-
continue;
|
|
126
|
-
}
|
|
127
|
-
if (spec.type !== 'ImportSpecifier') continue;
|
|
128
|
-
const importedName = spec.imported?.name ?? spec.local.name;
|
|
129
|
-
if (importedName === 'cache') {
|
|
130
|
-
named.add(spec.local.name);
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
return { named, namespaces };
|
|
135
|
-
}
|
|
61
|
+
import {
|
|
62
|
+
CACHE_MODULE_SOURCES,
|
|
63
|
+
findCallSites,
|
|
64
|
+
findImportBindings,
|
|
65
|
+
generateStableCallsiteId,
|
|
66
|
+
} from './callsite-ast.js';
|
|
67
|
+
import type {
|
|
68
|
+
CallExpressionNode,
|
|
69
|
+
IdentifierNode,
|
|
70
|
+
ImportBindings,
|
|
71
|
+
MemberExpressionNode,
|
|
72
|
+
PositionedNode,
|
|
73
|
+
ProgramNode,
|
|
74
|
+
} from './callsite-ast.js';
|
|
136
75
|
|
|
137
76
|
/**
|
|
138
77
|
* Is this callee a reference to the imported `cache` function — either a bare
|
|
@@ -140,7 +79,7 @@ function findCacheBindings(program: ProgramNode): CacheBindings {
|
|
|
140
79
|
* Computed access (`ns['cache']`) is deliberately not matched; it falls back
|
|
141
80
|
* to the runtime fnId derivation.
|
|
142
81
|
*/
|
|
143
|
-
function isCacheCallee(callee: PositionedNode, bindings:
|
|
82
|
+
function isCacheCallee(callee: PositionedNode, bindings: ImportBindings): boolean {
|
|
144
83
|
if (callee.type === 'Identifier') {
|
|
145
84
|
return bindings.named.has((callee as IdentifierNode).name);
|
|
146
85
|
}
|
|
@@ -158,54 +97,8 @@ function isCacheCallee(callee: PositionedNode, bindings: CacheBindings): boolean
|
|
|
158
97
|
}
|
|
159
98
|
|
|
160
99
|
/**
|
|
161
|
-
*
|
|
162
|
-
*
|
|
163
|
-
* cache() is typically called at module scope, but we also handle calls
|
|
164
|
-
* inside functions/blocks for completeness.
|
|
165
|
-
*/
|
|
166
|
-
function findCacheCallSites(
|
|
167
|
-
node: PositionedNode,
|
|
168
|
-
cacheBindings: CacheBindings,
|
|
169
|
-
results: CallExpressionNode[]
|
|
170
|
-
): void {
|
|
171
|
-
if (node.type === 'CallExpression') {
|
|
172
|
-
const call = node as unknown as CallExpressionNode;
|
|
173
|
-
if (call.arguments.length === 2 && isCacheCallee(call.callee, cacheBindings)) {
|
|
174
|
-
results.push(call);
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
for (const key of Object.keys(node)) {
|
|
178
|
-
if (key === 'type' || key === 'start' || key === 'end') continue;
|
|
179
|
-
const val = node[key];
|
|
180
|
-
if (val && typeof val === 'object') {
|
|
181
|
-
if (Array.isArray(val)) {
|
|
182
|
-
for (const item of val) {
|
|
183
|
-
if (item && typeof item === 'object' && typeof item.type === 'string') {
|
|
184
|
-
findCacheCallSites(item as PositionedNode, cacheBindings, results);
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
} else if (typeof (val as PositionedNode).type === 'string') {
|
|
188
|
-
findCacheCallSites(val as PositionedNode, cacheBindings, results);
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
/**
|
|
195
|
-
* Generate a stable callsite ID from a module path, the source text of the
|
|
196
|
-
* enclosing top-level statement, the source text of the cache(...) call
|
|
197
|
-
* expression, and an occurrence index among fully byte-identical
|
|
198
|
-
* (statement, call) pairs in the same file.
|
|
199
|
-
*
|
|
200
|
-
* The ID is deterministic and:
|
|
201
|
-
* - Stable across cold starts AND across builds/deploys for unchanged calls —
|
|
202
|
-
* inserting, deleting, or reordering other cache() calls does not affect it
|
|
203
|
-
* - Unique across different modules (relPath component)
|
|
204
|
-
* - Unique across distinct cache() calls within a module (statement + span
|
|
205
|
-
* hashes), with fully identical duplicates disambiguated by source-order
|
|
206
|
-
* occurrence
|
|
207
|
-
* - Fresh when the wrapped fn, opts, or enclosing statement change, which
|
|
208
|
-
* intentionally invalidates the old persisted entries
|
|
100
|
+
* Generate a stable callsite ID for a cache() call. See callsite-ast.ts for
|
|
101
|
+
* the derivation properties.
|
|
209
102
|
*/
|
|
210
103
|
function generateStableId(
|
|
211
104
|
relativeModulePath: string,
|
|
@@ -213,8 +106,12 @@ function generateStableId(
|
|
|
213
106
|
callSpanText: string,
|
|
214
107
|
occurrence: number
|
|
215
108
|
): string {
|
|
216
|
-
return
|
|
217
|
-
|
|
109
|
+
return generateStableCallsiteId(
|
|
110
|
+
relativeModulePath,
|
|
111
|
+
'cache',
|
|
112
|
+
statementText,
|
|
113
|
+
callSpanText,
|
|
114
|
+
occurrence
|
|
218
115
|
);
|
|
219
116
|
}
|
|
220
117
|
|
|
@@ -234,7 +131,7 @@ export function timberCacheTransform(ctx: PluginContext): Plugin {
|
|
|
234
131
|
return;
|
|
235
132
|
}
|
|
236
133
|
|
|
237
|
-
const cacheBindings =
|
|
134
|
+
const cacheBindings = findImportBindings(program, CACHE_MODULE_SOURCES, 'cache');
|
|
238
135
|
if (cacheBindings.named.size === 0 && cacheBindings.namespaces.size === 0) return;
|
|
239
136
|
|
|
240
137
|
// Collect call sites per top-level statement so each call carries its
|
|
@@ -242,7 +139,11 @@ export function timberCacheTransform(ctx: PluginContext): Plugin {
|
|
|
242
139
|
const callSites: Array<{ call: CallExpressionNode; stmt: PositionedNode }> = [];
|
|
243
140
|
for (const stmt of program.body) {
|
|
244
141
|
const calls: CallExpressionNode[] = [];
|
|
245
|
-
|
|
142
|
+
findCallSites(
|
|
143
|
+
stmt,
|
|
144
|
+
(call) => call.arguments.length === 2 && isCacheCallee(call.callee, cacheBindings),
|
|
145
|
+
calls
|
|
146
|
+
);
|
|
246
147
|
for (const call of calls) callSites.push({ call, stmt });
|
|
247
148
|
}
|
|
248
149
|
if (callSites.length === 0) return;
|