@timber-js/app 0.2.0-alpha.152 → 0.2.0-alpha.153

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.
Files changed (56) hide show
  1. package/dist/_chunks/actions-8lJZYYGq.js +298 -0
  2. package/dist/_chunks/actions-8lJZYYGq.js.map +1 -0
  3. package/dist/_chunks/{cache-api-Co-yI-gP.js → cache-api-e1E4i3Vy.js} +3 -26
  4. package/dist/_chunks/cache-api-e1E4i3Vy.js.map +1 -0
  5. package/dist/_chunks/{cli-schema-sync-DJDdyEae.js → cli-schema-sync-ChsRzw8o.js} +2 -2
  6. package/dist/_chunks/{cli-schema-sync-DJDdyEae.js.map → cli-schema-sync-ChsRzw8o.js.map} +1 -1
  7. package/dist/_chunks/{cli-schema-sync-D_IhHEUW.js → cli-schema-sync-D4urWkeV.js} +1 -1
  8. package/dist/_chunks/{actions-a0RczDnB.js → logger-DvmCWhN9.js} +665 -274
  9. package/dist/_chunks/logger-DvmCWhN9.js.map +1 -0
  10. package/dist/_chunks/plugin-context-GfDWsdd2.js.map +1 -1
  11. package/dist/_chunks/{walkers-DvWDj453.js → walkers-DZuFKqFX.js} +2 -2
  12. package/dist/_chunks/{walkers-DvWDj453.js.map → walkers-DZuFKqFX.js.map} +1 -1
  13. package/dist/cache/index.js +1 -1
  14. package/dist/cli.js +1 -1
  15. package/dist/client/browser-dev.d.ts +3 -0
  16. package/dist/client/browser-dev.d.ts.map +1 -1
  17. package/dist/config-types.d.ts +8 -0
  18. package/dist/config-types.d.ts.map +1 -1
  19. package/dist/config-validation.d.ts.map +1 -1
  20. package/dist/index.js +193 -48
  21. package/dist/index.js.map +1 -1
  22. package/dist/plugin-context.d.ts +9 -0
  23. package/dist/plugin-context.d.ts.map +1 -1
  24. package/dist/plugins/build-report.d.ts +13 -0
  25. package/dist/plugins/build-report.d.ts.map +1 -1
  26. package/dist/plugins/chunks.d.ts +11 -4
  27. package/dist/plugins/chunks.d.ts.map +1 -1
  28. package/dist/plugins/client-chunks.d.ts +23 -22
  29. package/dist/plugins/client-chunks.d.ts.map +1 -1
  30. package/dist/routing/index.js +2 -2
  31. package/dist/server/index.js +2 -2
  32. package/dist/server/internal.js +65 -2
  33. package/dist/server/internal.js.map +1 -1
  34. package/dist/server/logger.d.ts.map +1 -1
  35. package/dist/server/pipeline.d.ts.map +1 -1
  36. package/dist/server/primitives.d.ts +8 -0
  37. package/dist/server/primitives.d.ts.map +1 -1
  38. package/dist/server/stream-utils.d.ts +11 -0
  39. package/dist/server/stream-utils.d.ts.map +1 -1
  40. package/package.json +4 -4
  41. package/src/client/browser-dev.ts +3 -0
  42. package/src/config-types.ts +8 -0
  43. package/src/config-validation.ts +16 -0
  44. package/src/index.ts +2 -2
  45. package/src/plugin-context.ts +9 -0
  46. package/src/plugins/build-report.ts +105 -10
  47. package/src/plugins/chunks.ts +74 -5
  48. package/src/plugins/client-chunks.ts +73 -42
  49. package/src/server/logger.ts +5 -0
  50. package/src/server/pipeline.ts +13 -0
  51. package/src/server/primitives.ts +29 -0
  52. package/src/server/stream-utils.ts +73 -0
  53. package/dist/_chunks/actions-a0RczDnB.js.map +0 -1
  54. package/dist/_chunks/cache-api-Co-yI-gP.js.map +0 -1
  55. package/dist/_chunks/logger-CM0clugc.js +0 -645
  56. package/dist/_chunks/logger-CM0clugc.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../src/server/logger.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../src/server/logger.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"pipeline.d.ts","sourceRoot":"","sources":["../../src/server/pipeline.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"pipeline.d.ts","sourceRoot":"","sources":["../../src/server/pipeline.ts"],"names":[],"mappings":"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"}
@@ -70,6 +70,14 @@ export declare class RedirectSignal extends Error {
70
70
  readonly status: number;
71
71
  constructor(location: string, status: number);
72
72
  }
73
+ /**
74
+ * Returns true if the error is a framework control-flow signal (RedirectSignal
75
+ * or DenySignal) rather than a genuine application error. Works both for direct
76
+ * instances (RSC-side) and deserialized digest errors (SSR cross-boundary).
77
+ *
78
+ * See also: isFrameworkSignalError() in client/browser-dev.ts (client-only).
79
+ */
80
+ export declare function isControlFlowSignal(error: unknown): boolean;
73
81
  /**
74
82
  * Options for redirect() — alternative to passing a bare status code.
75
83
  */
@@ -1 +1 @@
1
- {"version":3,"file":"primitives.d.ts","sourceRoot":"","sources":["../../src/server/primitives.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"primitives.d.ts","sourceRoot":"","sources":["../../src/server/primitives.ts"],"names":[],"mappings":"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"}
@@ -33,4 +33,15 @@ export interface TeeOptions {
33
33
  * The returned streams are drop-in replacements for the native tee branches.
34
34
  */
35
35
  export declare function teeWithErrorPropagation(stream: ReadableStream<Uint8Array>, options?: TeeOptions): [ReadableStream<Uint8Array>, ReadableStream<Uint8Array>];
36
+ /**
37
+ * Wrap a response body with stream-lifecycle timing instrumentation.
38
+ *
39
+ * Logs firstChunk, lastChunk, close, maxGap (largest inter-chunk gap),
40
+ * chunk count, and byte count on stream termination (clean close OR
41
+ * cancellation/disconnect). Gated behind `serverTiming: 'detailed'`.
42
+ *
43
+ * Uses a manual reader pump to guarantee logging on all termination
44
+ * paths — TransformStream.flush() is not called on cancellation.
45
+ */
46
+ export declare function wrapResponseWithStreamTiming(body: ReadableStream<Uint8Array>, startTime: number): ReadableStream<Uint8Array>;
36
47
  //# sourceMappingURL=stream-utils.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"stream-utils.d.ts","sourceRoot":"","sources":["../../src/server/stream-utils.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAKH,MAAM,WAAW,UAAU;IACzB;;;;;;OAMG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,uBAAuB,CACrC,MAAM,EAAE,cAAc,CAAC,UAAU,CAAC,EAClC,OAAO,CAAC,EAAE,UAAU,GACnB,CAAC,cAAc,CAAC,UAAU,CAAC,EAAE,cAAc,CAAC,UAAU,CAAC,CAAC,CAmK1D"}
1
+ {"version":3,"file":"stream-utils.d.ts","sourceRoot":"","sources":["../../src/server/stream-utils.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAKH,MAAM,WAAW,UAAU;IACzB;;;;;;OAMG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,uBAAuB,CACrC,MAAM,EAAE,cAAc,CAAC,UAAU,CAAC,EAClC,OAAO,CAAC,EAAE,UAAU,GACnB,CAAC,cAAc,CAAC,UAAU,CAAC,EAAE,cAAc,CAAC,UAAU,CAAC,CAAC,CAmK1D;AAED;;;;;;;;;GASG;AACH,wBAAgB,4BAA4B,CAC1C,IAAI,EAAE,cAAc,CAAC,UAAU,CAAC,EAChC,SAAS,EAAE,MAAM,GAChB,cAAc,CAAC,UAAU,CAAC,CA0D5B"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@timber-js/app",
3
- "version": "0.2.0-alpha.152",
3
+ "version": "0.2.0-alpha.153",
4
4
  "description": "Vite-native React framework built for Servers and Serverless Platforms — correct HTTP semantics, real status codes, pages that work without JavaScript",
5
5
  "keywords": [
6
6
  "cloudflare-workers",
@@ -116,10 +116,10 @@
116
116
  },
117
117
  "dependencies": {
118
118
  "@opentelemetry/api": "^1.9.1",
119
- "@opentelemetry/context-async-hooks": "^2.7.1",
120
- "@opentelemetry/sdk-trace-base": "^2.7.1",
119
+ "@opentelemetry/context-async-hooks": "^2.8.0",
120
+ "@opentelemetry/sdk-trace-base": "^2.8.0",
121
121
  "magic-string": "^0.30.21",
122
- "nitro": "3.0.260603-beta"
122
+ "nitro": "3.0.260610-beta"
123
123
  },
124
124
  "peerDependencies": {
125
125
  "@content-collections/core": "^0.14.0",
@@ -279,6 +279,9 @@ export function setupClientErrorForwarding(hot: Pick<HotInterface, 'send'>): voi
279
279
  * (RedirectSignal, DenySignal). These must never surface as dev overlays —
280
280
  * they are expected during navigation and are handled by the error boundary
281
281
  * and router. Exported for tests.
282
+ *
283
+ * See also: isControlFlowSignal() in server/primitives.ts (server-side,
284
+ * handles both instanceof and digest detection).
282
285
  */
283
286
  export function isFrameworkSignalError(error: unknown): boolean {
284
287
  if (!error || typeof error !== 'object') return false;
@@ -177,6 +177,14 @@ export interface TimberUserConfig {
177
177
  * logging.
178
178
  */
179
179
  serverTiming?: 'detailed' | 'total' | false;
180
+ /**
181
+ * Build-time performance budgets. When exceeded, the build report emits
182
+ * a warning (does not fail the build).
183
+ */
184
+ budget?: {
185
+ /** Warn when any route's first-load JS (gzip) exceeds this many bytes. */
186
+ firstLoadJs?: number;
187
+ };
180
188
  /**
181
189
  * Override the app directory location. By default, timber auto-detects
182
190
  * `app/` at the project root, falling back to `src/app/`.
@@ -49,6 +49,7 @@ const KNOWN_CONFIG_KEYS = {
49
49
  sitemap: true,
50
50
  buildDir: true,
51
51
  topLoader: true,
52
+ budget: true,
52
53
  } satisfies Record<keyof TimberUserConfig, true>;
53
54
 
54
55
  const knownKeys = new Set<string>(Object.keys(KNOWN_CONFIG_KEYS));
@@ -180,6 +181,21 @@ export function validateConfig(config: TimberUserConfig): ConfigError[] {
180
181
  }
181
182
  }
182
183
 
184
+ // budget
185
+ if (config.budget != null && typeof config.budget === 'object') {
186
+ if (
187
+ config.budget.firstLoadJs !== undefined &&
188
+ (typeof config.budget.firstLoadJs !== 'number' || config.budget.firstLoadJs <= 0)
189
+ ) {
190
+ errors.push({
191
+ field: 'budget.firstLoadJs',
192
+ message: `budget.firstLoadJs must be a positive number in bytes (got ${JSON.stringify(config.budget.firstLoadJs)}).`,
193
+ value: config.budget.firstLoadJs,
194
+ suggestion: 'Example: budget: { firstLoadJs: 150_000 } for a ~150 kB limit.',
195
+ });
196
+ }
197
+ }
198
+
183
199
  for (const key of Object.keys(config)) {
184
200
  if (!knownKeys.has(key)) {
185
201
  errors.push({
package/src/index.ts CHANGED
@@ -156,7 +156,7 @@ function createRscOptions(
156
156
  // Layout-boundary grouping is the middle ground: components under the same
157
157
  // layout segment share a chunk. See design/27-chunking-strategy.md, TIM-440, TIM-499.
158
158
  clientChunks: (meta: { id: string; normalizedId: string; serverChunk: string }) =>
159
- clientChunkGroup(meta, ctx.appDir, ctx.root),
159
+ clientChunkGroup(meta, ctx.appDir, ctx.root, ctx.sharedModuleFacades),
160
160
  };
161
161
 
162
162
  // Bound args encryption — AES-256-GCM authenticated encryption for
@@ -538,7 +538,7 @@ export function timber(config?: TimberUserConfig): PluginOption[] {
538
538
  timberContent(ctx),
539
539
  timberServerBundle(), // Bundle all deps in server environments for prod
540
540
  timberCloudflareWasm(ctx), // Externalize WASM for Cloudflare Workers
541
- timberChunks(),
541
+ timberChunks(ctx),
542
542
  timberBuildReport(ctx), // Post-build: route table with bundle sizes
543
543
  timberAdapterBuild(ctx), // Post-build: invoke adapter.buildOutput()
544
544
  timberDevLogs(ctx), // Dev-only: forward server console.* to browser console
@@ -94,6 +94,15 @@ export interface PluginContext {
94
94
  * populate per-route modulepreload entries from clientReferenceDeps.
95
95
  */
96
96
  routeClientRefKeys?: Map<string, Set<string>>;
97
+ /**
98
+ * Maps absolute module IDs of client references in non-facade RSC
99
+ * chunks to the absolute paths of their consuming facade modules.
100
+ * Built during the RSC `generateBundle` phase by timber-chunks,
101
+ * consumed by `clientChunkGroup` to reclassify `shared:` modules
102
+ * that are only used by a single route boundary.
103
+ * See design/27-chunking-strategy.md, TIM-1107.
104
+ */
105
+ sharedModuleFacades?: Map<string, string[]>;
97
106
  }
98
107
 
99
108
  // ── App directory resolution ──────────────────────────────────────────────
@@ -13,6 +13,7 @@
13
13
  * Task: TIM-287
14
14
  */
15
15
 
16
+ import { relative } from 'node:path';
16
17
  import { gzipSync } from 'node:zlib';
17
18
  import type { Plugin, Logger } from 'vite';
18
19
  import type { PluginContext } from '../plugin-context.js';
@@ -160,6 +161,21 @@ export function buildRouteReport(entries: RouteEntry[], sharedSize: number): str
160
161
  return lines;
161
162
  }
162
163
 
164
+ /** Check route entries against a First-Load-JS budget and return warning lines. */
165
+ export function buildWarnings(entries: RouteEntry[], budget?: { firstLoadJs?: number }): string[] {
166
+ if (!budget?.firstLoadJs) return [];
167
+ const limit = budget.firstLoadJs;
168
+ const warnings: string[] = [];
169
+ for (const entry of entries) {
170
+ if (entry.firstLoadSize > limit) {
171
+ warnings.push(
172
+ `⚠ ${entry.path} first-load JS (${formatSize(entry.firstLoadSize)}) exceeds budget (${formatSize(limit)})`
173
+ );
174
+ }
175
+ }
176
+ return warnings;
177
+ }
178
+
163
179
  function pad(str: string, width: number, align: 'left' | 'right' = 'right'): string {
164
180
  const gap = Math.max(0, width - stripAnsi(str).length);
165
181
  return align === 'left' ? ' '.repeat(gap) + str : str + ' '.repeat(gap);
@@ -180,6 +196,7 @@ interface ChunkSize {
180
196
  interface OutputChunkLike {
181
197
  type: 'chunk' | 'asset';
182
198
  fileName: string;
199
+ name?: string;
183
200
  code?: string;
184
201
  source?: string | Uint8Array;
185
202
  modules?: Record<string, unknown>;
@@ -219,13 +236,62 @@ export function findChunkForFile(
219
236
  return null;
220
237
  }
221
238
 
239
+ // ─── Route → chunk group matching ────────────────────────────────────────
240
+
241
+ /**
242
+ * Derive the expected client chunk group prefix for a route.
243
+ *
244
+ * Mirrors the `clientChunkGroup` logic: extract the first non-group segment
245
+ * from the route's file path under appDir. Routes at the root level or
246
+ * without a matching segment return null (their JS is in client-shared).
247
+ */
248
+ function routeChunkGroupPrefix(entryFilePath: string, appDir: string, root: string): string | null {
249
+ const appPrefix = relative(root, appDir).replace(/\\/g, '/') + '/';
250
+ const relPath = relative(root, entryFilePath).replace(/\\/g, '/');
251
+ if (!relPath.startsWith(appPrefix)) return null;
252
+
253
+ const withinApp = relPath.slice(appPrefix.length);
254
+ const segments = withinApp.split('/');
255
+ for (const seg of segments) {
256
+ if (seg.includes('.')) break;
257
+ if (seg.startsWith('(') && seg.endsWith(')')) continue;
258
+ return `client-${seg}`;
259
+ }
260
+ return null;
261
+ }
262
+
263
+ /**
264
+ * Find all client bundle chunks belonging to a chunk group.
265
+ *
266
+ * Uses the chunk's `name` property (set by Rolldown from the `clientChunks`
267
+ * callback's return value) for exact matching. The `name` is the group name
268
+ * itself (e.g. `client-docs`), not the hashed filename.
269
+ */
270
+ export function findChunksForGroup(
271
+ groupName: string,
272
+ chunkSizes: Map<string, ChunkSize>,
273
+ bundle?: Record<string, OutputChunkLike>
274
+ ): string[] {
275
+ if (!bundle) return [];
276
+ const matched: string[] = [];
277
+ for (const [fileName, item] of Object.entries(bundle)) {
278
+ if (item.type !== 'chunk') continue;
279
+ if (item.name === groupName && chunkSizes.has(fileName)) {
280
+ matched.push(fileName);
281
+ }
282
+ }
283
+ return matched;
284
+ }
285
+
222
286
  // ─── Build route entries from collected data ──────────────────────────────
223
287
 
224
288
  function buildEntries(
225
289
  routeTree: RouteTree,
226
290
  chunkSizes: Map<string, ChunkSize>,
227
291
  bundle: Record<string, OutputChunkLike>,
228
- outputMode: 'server' | 'static'
292
+ outputMode: 'server' | 'static',
293
+ appDir?: string,
294
+ root?: string
229
295
  ): { entries: RouteEntry[]; sharedGzip: number } {
230
296
  const routeInfos = collectRoutes(routeTree);
231
297
 
@@ -233,7 +299,7 @@ function buildEntries(
233
299
  let totalGzip = 0;
234
300
  for (const s of chunkSizes.values()) totalGzip += s.gzip;
235
301
 
236
- // Per-route sizes and track route-specific chunks
302
+ // Per-route sizes. Track route-specific chunks to compute shared size.
237
303
  const routeChunkFiles = new Set<string>();
238
304
  const entries: RouteEntry[] = [];
239
305
 
@@ -242,14 +308,36 @@ function buildEntries(
242
308
  let gzip = 0;
243
309
 
244
310
  if (info.entryFilePath) {
245
- const chunk = findChunkForFile(info.entryFilePath, bundle);
246
- if (chunk) {
247
- const s = chunkSizes.get(chunk);
248
- if (s) {
249
- raw = s.raw;
250
- gzip = s.gzip;
311
+ // Try chunk group matching first (works for server component routes
312
+ // whose client JS is in client-<segment> chunks, not in the route's
313
+ // own page.tsx chunk). Falls back to direct chunk lookup.
314
+ let matched = false;
315
+ if (appDir && root) {
316
+ const groupPrefix = routeChunkGroupPrefix(info.entryFilePath, appDir, root);
317
+ if (groupPrefix) {
318
+ const chunks = findChunksForGroup(groupPrefix, chunkSizes, bundle);
319
+ for (const chunk of chunks) {
320
+ const s = chunkSizes.get(chunk);
321
+ if (s) {
322
+ raw += s.raw;
323
+ gzip += s.gzip;
324
+ }
325
+ routeChunkFiles.add(chunk);
326
+ }
327
+ if (chunks.length > 0) matched = true;
328
+ }
329
+ }
330
+
331
+ if (!matched) {
332
+ const chunk = findChunkForFile(info.entryFilePath, bundle);
333
+ if (chunk) {
334
+ const s = chunkSizes.get(chunk);
335
+ if (s) {
336
+ raw = s.raw;
337
+ gzip = s.gzip;
338
+ }
339
+ routeChunkFiles.add(chunk);
251
340
  }
252
- routeChunkFiles.add(chunk);
253
341
  }
254
342
  }
255
343
 
@@ -348,12 +436,19 @@ export function timberBuildReport(ctx: PluginContext): Plugin {
348
436
  })),
349
437
  sharedGzip: 0,
350
438
  }
351
- : buildEntries(ctx.routeTree, chunkSizes, clientBundle, outputMode);
439
+ : buildEntries(ctx.routeTree, chunkSizes, clientBundle, outputMode, ctx.appDir, ctx.root);
352
440
  const elapsed = ((performance.now() - buildStart) / 1000).toFixed(2);
353
441
  const lines = buildRouteReport(entries, sharedGzip);
354
442
 
355
443
  logger.info('');
356
444
  for (const line of lines) logger.info(line);
445
+
446
+ const warnings = buildWarnings(entries, ctx.config.budget);
447
+ if (warnings.length > 0) {
448
+ logger.info('');
449
+ for (const w of warnings) logger.warn(w);
450
+ }
451
+
357
452
  logger.info('');
358
453
  logger.info(
359
454
  `✓ built ${entries.length} routes for all three environments (rsc, ssr, client) in ${elapsed}s`
@@ -13,10 +13,16 @@
13
13
  * - Per-route chunks (page/layout components, only when route-specific)
14
14
  * - rolldown-runtime (Rolldown's own output, unavoidable)
15
15
  *
16
+ * Additionally, this plugin builds a facade ancestry map during the RSC
17
+ * build so that `clientChunkGroup` can reclassify `shared:` client
18
+ * references that are only consumed by a single route boundary.
19
+ * See TIM-1107, design/27-chunking-strategy.md.
20
+ *
16
21
  * Design docs: 27-chunking-strategy.md
17
22
  */
18
23
 
19
- import type { Plugin } from 'vite';
24
+ import type { Plugin, Rollup } from 'vite';
25
+ import type { PluginContext } from '../plugin-context';
20
26
 
21
27
  /**
22
28
  * Check if a module is part of the timber framework runtime.
@@ -36,12 +42,71 @@ export function isTimberRuntime(id: string): boolean {
36
42
  );
37
43
  }
38
44
 
45
+ type OutputChunk = Rollup.OutputChunk;
46
+
47
+ /**
48
+ * Build a map from absolute module IDs in non-facade RSC chunks to the
49
+ * absolute facade module paths that transitively import them.
50
+ *
51
+ * Used by `clientChunkGroup` to reclassify `shared:` client references
52
+ * that are only consumed by a single route boundary (TIM-1107).
53
+ */
54
+ function buildFacadeAncestryMap(bundle: Rollup.OutputBundle): Map<string, string[]> {
55
+ const chunks = new Map<string, OutputChunk>();
56
+ for (const [file, asset] of Object.entries(bundle)) {
57
+ if (asset.type === 'chunk') chunks.set(file, asset);
58
+ }
59
+
60
+ // Reverse import graph: which chunks import this chunk file?
61
+ const importedBy = new Map<string, string[]>();
62
+ for (const [file, chunk] of chunks) {
63
+ for (const imp of chunk.imports) {
64
+ let parents = importedBy.get(imp);
65
+ if (!parents) {
66
+ parents = [];
67
+ importedBy.set(imp, parents);
68
+ }
69
+ parents.push(file);
70
+ }
71
+ }
72
+
73
+ // Trace upward from a chunk to find all facade modules that consume it
74
+ function facadeAncestors(file: string, visited: Set<string>): string[] {
75
+ if (visited.has(file)) return [];
76
+ visited.add(file);
77
+ const chunk = chunks.get(file);
78
+ if (!chunk) return [];
79
+ if (chunk.facadeModuleId) return [chunk.facadeModuleId];
80
+ const parents = importedBy.get(file) ?? [];
81
+ const result: string[] = [];
82
+ for (const p of parents) {
83
+ for (const f of facadeAncestors(p, visited)) result.push(f);
84
+ }
85
+ return result;
86
+ }
87
+
88
+ const moduleToFacades = new Map<string, string[]>();
89
+ for (const [file, chunk] of chunks) {
90
+ if (chunk.facadeModuleId) continue;
91
+ const facades = facadeAncestors(file, new Set());
92
+ if (facades.length === 0) continue;
93
+ // Deduplicate facades
94
+ const unique = [...new Set(facades)];
95
+ for (const modId of chunk.moduleIds) {
96
+ moduleToFacades.set(modId, unique);
97
+ }
98
+ }
99
+
100
+ return moduleToFacades;
101
+ }
102
+
39
103
  /**
40
104
  * Create the timber-chunks Vite plugin.
41
105
  *
42
- * Currently a no-op — Rolldown's default splitting produces the desired
43
- * output (one main bundle + per-route chunks). The plugin is retained as
44
- * a hook point for future chunking adjustments.
106
+ * Rolldown's default splitting produces the desired output (one main
107
+ * bundle + per-route chunks). This plugin's main job is building the
108
+ * facade ancestry map during the RSC build so `clientChunkGroup` can
109
+ * reclassify shared client references to their correct route boundary.
45
110
  *
46
111
  * NOTE: Ideally we'd use Rolldown's `format: 'app'` (module registry format)
47
112
  * which guarantees single-copy module semantics like webpack/Turbopack.
@@ -51,8 +116,12 @@ export function isTimberRuntime(id: string): boolean {
51
116
  *
52
117
  * Design docs: 27-chunking-strategy.md
53
118
  */
54
- export function timberChunks(): Plugin {
119
+ export function timberChunks(ctx: PluginContext): Plugin {
55
120
  return {
56
121
  name: 'timber-chunks',
122
+ generateBundle(_options, bundle) {
123
+ if (this.environment?.name !== 'rsc') return;
124
+ ctx.sharedModuleFacades = buildFacadeAncestryMap(bundle);
125
+ },
57
126
  };
58
127
  }
@@ -1,13 +1,17 @@
1
1
  /**
2
2
  * Client chunk grouping strategy for @vitejs/plugin-rsc.
3
3
  *
4
- * Groups client reference modules by layout boundary to balance route-scoped
5
- * code splitting with HTTP request count. A constant group name would collapse
6
- * all routes into one chunk (every page downloads every client component).
7
- * Per-serverChunk grouping creates many sub-500B files. Layout-boundary
8
- * grouping is the middle ground.
4
+ * Groups client reference modules by the **consuming route's** layout boundary
5
+ * (derived from `meta.serverChunk`), not by the module's own file location.
6
+ * This ensures components in shared directories (e.g. `components/`) land in
7
+ * the chunk of the route that uses them, not in a single catch-all chunk.
9
8
  *
10
- * See design/27-chunking-strategy.md, TIM-440, TIM-499.
9
+ * For `shared:` modules (client references in non-facade RSC chunks), the
10
+ * function consults a facade ancestry map built during the RSC build. If all
11
+ * consuming facades belong to the same route boundary, the module is
12
+ * reclassified to that boundary instead of falling through to `client-shared`.
13
+ *
14
+ * See design/27-chunking-strategy.md, TIM-440, TIM-499, TIM-1104, TIM-1107.
11
15
  */
12
16
 
13
17
  import { relative } from 'node:path';
@@ -44,37 +48,68 @@ function findAppBoundary(
44
48
  return { inApp: true, segment: null };
45
49
  }
46
50
 
51
+ /**
52
+ * For a `shared:` client reference, check the facade ancestry map to
53
+ * see if all consuming facades belong to the same route boundary.
54
+ * Returns the boundary segment name, or null if the module is
55
+ * genuinely shared across multiple boundaries.
56
+ */
57
+ function reclassifySharedModule(
58
+ meta: { id: string; normalizedId: string },
59
+ appPrefix: string,
60
+ root: string,
61
+ sharedModuleFacades: Map<string, string[]>
62
+ ): string | null {
63
+ const facades = sharedModuleFacades.get(meta.id);
64
+ if (!facades || facades.length === 0) return null;
65
+
66
+ const boundaries = new Set<string>();
67
+ for (const facade of facades) {
68
+ const relFacade = relative(root, facade).replace(/\\/g, '/');
69
+ const result = findAppBoundary(relFacade, appPrefix);
70
+ if (result.inApp && result.segment) {
71
+ boundaries.add(result.segment);
72
+ } else if (result.inApp) {
73
+ // Root-level facade — module is used at root, keep shared
74
+ return null;
75
+ } else {
76
+ // Facade outside app/ (e.g. src/posts/foo.mdx) — can't derive a
77
+ // safe common boundary, so keep shared to avoid pulling outside-app
78
+ // deps into a route chunk they don't belong to.
79
+ return null;
80
+ }
81
+ }
82
+
83
+ if (boundaries.size === 1) return [...boundaries][0];
84
+ return null;
85
+ }
86
+
47
87
  /**
48
88
  * Derive a chunk group name for a client reference module.
49
89
  *
50
- * Groups by the first non-group route segment under appDir so that all
51
- * client components belonging to the same layout boundary land in one
52
- * chunk. For example:
90
+ * Always derives the group from the **consuming route** (`serverChunk`'s
91
+ * facade path), not from the module's own file location. This prevents
92
+ * components in shared directories (e.g. `app/components/`, `src/lib/`)
93
+ * from collapsing into a single chunk when they're only used by one route.
94
+ *
95
+ * For `shared:` modules (non-facade RSC chunks), consults the facade
96
+ * ancestry map to reclassify modules that are only consumed by a single
97
+ * route boundary. This prevents large dependencies from polluting
98
+ * `client-shared` when they're route-scoped (TIM-1107).
99
+ *
100
+ * Examples:
53
101
  * - `facade:app/dashboard/settings/page.tsx` → `"client-dashboard"`
54
102
  * - `facade:app/(group-a)/group-page-a/page.tsx` → `"client-group-page-a"`
55
103
  * - `facade:app/layout.tsx` (root layout) → `"client-shared"`
56
- * - `shared:...` (shared across chunks) → `"client-shared"`
57
- *
58
- * When a 'use client' module lives outside the app directory (e.g. in
59
- * src/posts/ or src/components/), the function falls back to deriving
60
- * the layout boundary from the serverChunk's facade path. This prevents
61
- * lazily-loaded client dependencies (e.g. via import.meta.glob) from
62
- * being hoisted into the shared chunk when they're only used by a
63
- * specific route.
64
- *
65
- * This balances route-scoped code splitting with HTTP request count:
66
- * fewer chunks than per-serverChunk, but still avoids downloading every
67
- * client component on every page.
104
+ * - `shared:...` (shared across chunks) → reclassified or `"client-shared"`
68
105
  */
69
106
  export function clientChunkGroup(
70
107
  meta: { id: string; normalizedId: string; serverChunk: string },
71
108
  appDir: string,
72
- root: string
109
+ root: string,
110
+ sharedModuleFacades?: Map<string, string[]>
73
111
  ): string {
74
- const { normalizedId, serverChunk } = meta;
75
-
76
- // Shared chunks (not associated with a single route entry) get one group.
77
- if (serverChunk.startsWith('shared:')) return 'client-shared';
112
+ const { serverChunk } = meta;
78
113
 
79
114
  // Compute the root-relative app directory prefix once. Both normalizedId
80
115
  // and serverChunk paths from the RSC plugin are root-relative (via
@@ -82,32 +117,28 @@ export function clientChunkGroup(
82
117
  // no ambiguity with content directories that happen to share a name.
83
118
  const appPrefix = relative(root, appDir).replace(/\\/g, '/') + '/';
84
119
 
85
- // Try the client module's own path first.
86
- const result = findAppBoundary(normalizedId, appPrefix);
87
- if (result.inApp && result.segment) return `client-${result.segment}`;
88
- if (result.inApp) return 'client-shared';
120
+ if (serverChunk.startsWith('shared:')) {
121
+ if (sharedModuleFacades) {
122
+ const boundary = reclassifySharedModule(meta, appPrefix, root, sharedModuleFacades);
123
+ if (boundary) return `client-${boundary}`;
124
+ }
125
+ return 'client-shared';
126
+ }
89
127
 
90
- // The client module lives outside the app directory (e.g. src/posts/,
91
- // src/components/). Fall back to the serverChunk's facade path — the
92
- // RSC entry point that imported this module — to derive the route's
93
- // layout boundary. This keeps dependencies of lazily-loaded 'use client'
94
- // components (e.g. from import.meta.glob) in per-route async chunks
95
- // instead of hoisting them into client-shared.
128
+ // Derive the group from the serverChunk's facade path — the RSC entry
129
+ // point that references this client module. This groups by the consuming
130
+ // route's layout boundary, not the module's own file location (TIM-1104).
96
131
  if (serverChunk.startsWith('facade:')) {
97
132
  const facadePath = serverChunk.slice('facade:'.length).replace(/\\/g, '/');
98
133
  const facadeResult = findAppBoundary(facadePath, appPrefix);
99
134
  if (facadeResult.inApp && facadeResult.segment) return `client-${facadeResult.segment}`;
100
135
  if (facadeResult.inApp) return 'client-shared';
101
136
 
102
- // Both module and facade are outside app/. Derive a chunk name from
103
- // the full facade path to keep these isolated from client-shared.
104
- // Uses the full path (minus extension) to avoid collisions between
105
- // facades with the same basename (e.g. foo/index.mdx vs bar/index.mdx).
137
+ // Facade is outside app/ (e.g. src/posts/foo.mdx via import.meta.glob).
138
+ // Derive a chunk name from the full facade path to keep these isolated.
106
139
  const facadeName = facadePath.replace(/\.[^.]+$/, '').replace(/\//g, '-');
107
140
  if (facadeName) return `client-${facadeName}`;
108
141
  }
109
142
 
110
- // Root-level files (layout.tsx, page.tsx directly in app/) go into the
111
- // shared group since the root layout is loaded on every page.
112
143
  return 'client-shared';
113
144
  }