@timber-js/app 0.2.0-alpha.102 → 0.2.0-alpha.104

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@timber-js/app",
3
- "version": "0.2.0-alpha.102",
3
+ "version": "0.2.0-alpha.104",
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",
@@ -110,11 +110,6 @@
110
110
  "publishConfig": {
111
111
  "access": "public"
112
112
  },
113
- "scripts": {
114
- "build": "vite build --config vite.lib.config.ts && tsc --emitDeclarationOnly --project tsconfig.json --outDir dist",
115
- "typecheck": "tsgo --noEmit",
116
- "prepublishOnly": "pnpm run build"
117
- },
118
113
  "dependencies": {
119
114
  "@opentelemetry/api": "^1.9.1",
120
115
  "@opentelemetry/context-async-hooks": "^2.6.1",
@@ -158,5 +153,9 @@
158
153
  },
159
154
  "engines": {
160
155
  "node": ">=22.12.0"
156
+ },
157
+ "scripts": {
158
+ "build": "vite build --config vite.lib.config.ts && tsc --emitDeclarationOnly --project tsconfig.json --outDir dist",
159
+ "typecheck": "tsgo --noEmit"
161
160
  }
162
- }
161
+ }
package/src/cli.ts CHANGED
File without changes
package/src/fonts/ast.ts CHANGED
@@ -590,8 +590,8 @@ interface CallExpressionNode extends PositionedNode {
590
590
 
591
591
  interface VariableDeclaratorNode extends PositionedNode {
592
592
  type: 'VariableDeclarator';
593
- id: AstNode;
594
- init: AstNode | null;
593
+ id: PositionedNode;
594
+ init: PositionedNode | null;
595
595
  }
596
596
 
597
597
  interface VariableDeclarationNode extends PositionedNode {
@@ -601,7 +601,43 @@ interface VariableDeclarationNode extends PositionedNode {
601
601
 
602
602
  interface ExpressionStatementNode extends PositionedNode {
603
603
  type: 'ExpressionStatement';
604
- expression: AstNode;
604
+ expression: PositionedNode;
605
+ }
606
+
607
+ interface ExportNamedDeclarationNode extends PositionedNode {
608
+ type: 'ExportNamedDeclaration';
609
+ declaration: VariableDeclarationNode | ExpressionStatementNode | null;
610
+ }
611
+
612
+ // ── Type guards for AST narrowing ────────────────────────────────────────────
613
+ //
614
+ // OXC's parsed nodes all share the base `PositionedNode` shape with
615
+ // `type: string`. TypeScript can't narrow a union that includes a
616
+ // `string`-typed catch-all, so we use explicit type guards instead of
617
+ // bare `.type ===` checks.
618
+
619
+ function isImportDeclaration(node: PositionedNode): node is ImportDeclarationNode {
620
+ return node.type === 'ImportDeclaration';
621
+ }
622
+
623
+ function isVariableDeclaration(node: PositionedNode): node is VariableDeclarationNode {
624
+ return node.type === 'VariableDeclaration';
625
+ }
626
+
627
+ function isExpressionStatement(node: PositionedNode): node is ExpressionStatementNode {
628
+ return node.type === 'ExpressionStatement';
629
+ }
630
+
631
+ function isExportNamedDeclaration(node: PositionedNode): node is ExportNamedDeclarationNode {
632
+ return node.type === 'ExportNamedDeclaration';
633
+ }
634
+
635
+ function isCallExpression(node: PositionedNode): node is CallExpressionNode {
636
+ return node.type === 'CallExpression';
637
+ }
638
+
639
+ function isIdentifier(node: AstNode): node is IdentifierNode {
640
+ return node.type === 'Identifier';
605
641
  }
606
642
 
607
643
  interface ProgramNode extends PositionedNode {
@@ -627,8 +663,8 @@ export function findFontCallSites(source: string): FontAnalysis | null {
627
663
  const importNodesToRemove: Array<{ start: number; end: number }> = [];
628
664
 
629
665
  for (const stmt of program.body) {
630
- if (stmt.type !== 'ImportDeclaration') continue;
631
- const importDecl = stmt as unknown as ImportDeclarationNode;
666
+ if (!isImportDeclaration(stmt)) continue;
667
+ const importDecl = stmt;
632
668
  const specSource = importDecl.source.value;
633
669
 
634
670
  let provider: 'google' | 'local' | null = null;
@@ -658,7 +694,7 @@ export function findFontCallSites(source: string): FontAnalysis | null {
658
694
  // member expressions in the call-site walk.
659
695
  }
660
696
 
661
- importNodesToRemove.push({ start: importDecl.start, end: importDecl.end });
697
+ importNodesToRemove.push({ start: stmt.start, end: stmt.end });
662
698
  }
663
699
 
664
700
  if (bindings.length === 0) return null;
@@ -669,27 +705,30 @@ export function findFontCallSites(source: string): FontAnalysis | null {
669
705
  let dynamicCall: string | null = null;
670
706
 
671
707
  for (const stmt of program.body) {
708
+ // Unwrap `export const roboto = Roboto(...)` — the AST wraps the
709
+ // VariableDeclaration inside an ExportNamedDeclaration node.
710
+ const effectiveStmt =
711
+ isExportNamedDeclaration(stmt) && stmt.declaration ? stmt.declaration : stmt;
712
+
672
713
  // VariableDeclaration: `const inter = Inter({ ... })`
673
- if (stmt.type === 'VariableDeclaration') {
674
- const varDecl = stmt as unknown as VariableDeclarationNode;
675
- for (const declarator of varDecl.declarations) {
676
- if (!declarator.init || declarator.init.type !== 'CallExpression') continue;
677
- const call = declarator.init as unknown as CallExpressionNode;
714
+ if (isVariableDeclaration(effectiveStmt)) {
715
+ for (const declarator of effectiveStmt.declarations) {
716
+ if (!declarator.init) continue;
717
+ if (!isCallExpression(declarator.init)) continue;
718
+ const call = declarator.init;
678
719
  const site = matchCallExpression(call, bindingsByName, source);
679
720
  if (!site) continue;
680
721
 
681
722
  if (site === 'dynamic') {
682
- if (!dynamicCall) {
683
- const calleeName = (call.callee as IdentifierNode).name;
723
+ if (!dynamicCall && isIdentifier(call.callee)) {
684
724
  const argStart = call.arguments[0].start;
685
725
  const argEnd = call.arguments[0].end;
686
- dynamicCall = `${calleeName}(${source.slice(argStart, argEnd)})`;
726
+ dynamicCall = `${call.callee.name}(${source.slice(argStart, argEnd)})`;
687
727
  }
688
728
  continue;
689
729
  }
690
730
 
691
- const assignedName =
692
- declarator.id.type === 'Identifier' ? (declarator.id as IdentifierNode).name : null;
731
+ const assignedName = isIdentifier(declarator.id) ? declarator.id.name : null;
693
732
 
694
733
  const { line, column } = offsetToLineColumn(source, call.start);
695
734
  callSites.push({
@@ -705,10 +744,9 @@ export function findFontCallSites(source: string): FontAnalysis | null {
705
744
  }
706
745
 
707
746
  // ExpressionStatement: `Inter({ ... })` as a bare call
708
- if (stmt.type === 'ExpressionStatement') {
709
- const exprStmt = stmt as unknown as ExpressionStatementNode;
710
- if (exprStmt.expression.type === 'CallExpression') {
711
- const call = exprStmt.expression as unknown as CallExpressionNode;
747
+ if (isExpressionStatement(effectiveStmt)) {
748
+ if (isCallExpression(effectiveStmt.expression)) {
749
+ const call = effectiveStmt.expression;
712
750
  const site = matchCallExpression(call, bindingsByName, source);
713
751
  if (site && site !== 'dynamic') {
714
752
  const { line, column } = offsetToLineColumn(source, call.start);
@@ -716,16 +754,15 @@ export function findFontCallSites(source: string): FontAnalysis | null {
716
754
  binding: site.binding,
717
755
  assignedName: null,
718
756
  configNode: site.configNode,
719
- replaceStart: (exprStmt as PositionedNode).start,
720
- replaceEnd: (exprStmt as PositionedNode).end,
757
+ replaceStart: effectiveStmt.start,
758
+ replaceEnd: effectiveStmt.end,
721
759
  line,
722
760
  column,
723
761
  });
724
- } else if (site === 'dynamic' && !dynamicCall) {
725
- const calleeName = (call.callee as IdentifierNode).name;
762
+ } else if (site === 'dynamic' && !dynamicCall && isIdentifier(call.callee)) {
726
763
  const argStart = call.arguments[0].start;
727
764
  const argEnd = call.arguments[0].end;
728
- dynamicCall = `${calleeName}(${source.slice(argStart, argEnd)})`;
765
+ dynamicCall = `${call.callee.name}(${source.slice(argStart, argEnd)})`;
729
766
  }
730
767
  }
731
768
  }
@@ -817,8 +854,8 @@ function matchCallExpression(
817
854
  bindings: Map<string, FontBinding>,
818
855
  _source: string
819
856
  ): { binding: FontBinding; configNode: AstNode } | 'dynamic' | null {
820
- if (call.callee.type !== 'Identifier') return null;
821
- const calleeName = (call.callee as IdentifierNode).name;
857
+ if (!isIdentifier(call.callee)) return null;
858
+ const calleeName = call.callee.name;
822
859
  const binding = bindings.get(calleeName);
823
860
  if (!binding) return null;
824
861
 
package/src/index.ts CHANGED
@@ -369,10 +369,28 @@ export function timber(config?: TimberUserConfig): PluginOption[] {
369
369
  envOutDirs[envName] = { build: { outDir: join(buildOutDir, envName) } };
370
370
  }
371
371
 
372
+ // Exclude @timber-js/app packages from the dep optimizer.
373
+ // The shims plugin intercepts @timber-js/app/* imports per-environment
374
+ // (e.g. replacing @timber-js/app/server with a stub in the client env).
375
+ // The dep optimizer runs before per-environment resolution, so it would
376
+ // pre-bundle @timber-js/app/cookies with the real server code inlined,
377
+ // pulling AsyncLocalStorage into the browser bundle.
378
+ const timberOptimizeDepsExclude = [
379
+ '@timber-js/app',
380
+ '@timber-js/app/server',
381
+ '@timber-js/app/client',
382
+ '@timber-js/app/cookies',
383
+ '@timber-js/app/cache',
384
+ '@timber-js/app/search-params',
385
+ '@timber-js/app/segment-params',
386
+ '@timber-js/app/codec',
387
+ ];
388
+
372
389
  if (command === 'build') {
373
390
  return {
374
391
  build: { outDir: buildOutDir },
375
392
  environments: envOutDirs,
393
+ optimizeDeps: { exclude: timberOptimizeDepsExclude },
376
394
  oxc: {
377
395
  jsx: {
378
396
  development: false,
@@ -408,6 +426,7 @@ export function timber(config?: TimberUserConfig): PluginOption[] {
408
426
  return {
409
427
  build: { outDir: buildOutDir },
410
428
  environments: envOutDirs,
429
+ optimizeDeps: { exclude: timberOptimizeDepsExclude },
411
430
  server: serverConfig,
412
431
  preview: previewConfig,
413
432
  };