@timber-js/app 0.2.0-alpha.65 → 0.2.0-alpha.67

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.
@@ -16,6 +16,7 @@
16
16
  import type { Plugin } from 'vite';
17
17
  import type { PluginContext } from '../index.js';
18
18
  import type { ExtractedFont, GoogleFontConfig } from '../fonts/types.js';
19
+ import type { FontFaceDescriptor } from '../fonts/types.js';
19
20
  /**
20
21
  * Registry of fonts extracted during transform.
21
22
  * Keyed by a unique font ID derived from family + config.
@@ -59,17 +60,23 @@ export declare function parseGoogleFontFamilies(source: string): Map<string, str
59
60
  /**
60
61
  * Generate CSS for a single extracted font.
61
62
  *
62
- * Includes @font-face rules (for local fonts), fallback @font-face,
63
+ * Includes @font-face rules (for local and Google fonts), fallback @font-face,
63
64
  * and the scoped class rule.
65
+ *
66
+ * For Google fonts, pass the resolved FontFaceDescriptor[] from either
67
+ * `generateProductionFontFaces()` (production) or `resolveDevFontFaces()` (dev).
64
68
  */
65
- export declare function generateFontCss(font: ExtractedFont): string;
69
+ export declare function generateFontCss(font: ExtractedFont, googleFaces?: FontFaceDescriptor[]): string;
66
70
  /**
67
71
  * Generate the CSS output for all extracted fonts.
68
72
  *
69
- * Includes @font-face rules for local fonts, fallback @font-face rules,
70
- * and scoped classes.
73
+ * Includes @font-face rules for local and Google fonts, fallback @font-face
74
+ * rules, and scoped classes.
75
+ *
76
+ * `googleFontFacesMap` provides pre-resolved FontFaceDescriptor[] for each
77
+ * Google font ID (keyed by ExtractedFont.id).
71
78
  */
72
- export declare function generateAllFontCss(registry: FontRegistry): string;
79
+ export declare function generateAllFontCss(registry: FontRegistry, googleFontFacesMap?: Map<string, FontFaceDescriptor[]>): string;
73
80
  /**
74
81
  * Parse the local name used for the default import of `@timber/fonts/local`.
75
82
  *
@@ -1 +1 @@
1
- {"version":3,"file":"fonts.d.ts","sourceRoot":"","sources":["../../src/plugins/fonts.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAiB,MAAM,MAAM,CAAC;AAGlD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,KAAK,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAmCzE;;;GAGG;AACH,MAAM,MAAM,YAAY,GAAG,GAAG,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;AA4CtD;;;;;;;;;;GAUG;AACH,wBAAgB,iBAAiB,CAAC,UAAU,EAAE,MAAM,GAAG,gBAAgB,GAAG,IAAI,CAE7E;AAED;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,EAAE,GAAG,MAAM,GAAG,IAAI,CAE5F;AAUD;;;;;GAKG;AACH,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAkB/D;AAED;;;;;GAKG;AACH,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,MAAM,GAAG,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAqB3E;AAwED;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,aAAa,GAAG,MAAM,CAmB3D;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,YAAY,GAAG,MAAM,CAMjE;AAED;;;;;;GAMG;AACH,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAKtE;AAqED;;GAEG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,aAAa,GAAG,MAAM,CA4VtD"}
1
+ {"version":3,"file":"fonts.d.ts","sourceRoot":"","sources":["../../src/plugins/fonts.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAiB,MAAM,MAAM,CAAC;AAGlD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,KAAK,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAYzE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AA6B5D;;;GAGG;AACH,MAAM,MAAM,YAAY,GAAG,GAAG,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;AA4CtD;;;;;;;;;;GAUG;AACH,wBAAgB,iBAAiB,CAAC,UAAU,EAAE,MAAM,GAAG,gBAAgB,GAAG,IAAI,CAE7E;AAED;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,EAAE,GAAG,MAAM,GAAG,IAAI,CAE5F;AAUD;;;;;GAKG;AACH,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAkB/D;AAED;;;;;GAKG;AACH,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,MAAM,GAAG,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAqB3E;AAwED;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,aAAa,EAAE,WAAW,CAAC,EAAE,kBAAkB,EAAE,GAAG,MAAM,CAwB/F;AAED;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,CAChC,QAAQ,EAAE,YAAY,EACtB,kBAAkB,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,kBAAkB,EAAE,CAAC,GACrD,MAAM,CAOR;AAED;;;;;;GAMG;AACH,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAKtE;AAqED;;GAEG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,aAAa,GAAG,MAAM,CAqYtD"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@timber-js/app",
3
- "version": "0.2.0-alpha.65",
3
+ "version": "0.2.0-alpha.67",
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",
@@ -88,6 +88,11 @@
88
88
  "publishConfig": {
89
89
  "access": "public"
90
90
  },
91
+ "scripts": {
92
+ "build": "vite build --config vite.lib.config.ts && tsc --emitDeclarationOnly --project tsconfig.json --outDir dist",
93
+ "typecheck": "tsgo --noEmit",
94
+ "prepublishOnly": "pnpm run build"
95
+ },
91
96
  "dependencies": {
92
97
  "@opentelemetry/api": "^1.9.1",
93
98
  "@opentelemetry/context-async-hooks": "^2.6.1",
@@ -126,9 +131,5 @@
126
131
  },
127
132
  "engines": {
128
133
  "node": ">=22.12.0"
129
- },
130
- "scripts": {
131
- "build": "vite build --config vite.lib.config.ts && tsc --emitDeclarationOnly --project tsconfig.json --outDir dist",
132
- "typecheck": "tsgo --noEmit"
133
134
  }
134
- }
135
+ }
package/src/cli.ts CHANGED
File without changes
@@ -24,7 +24,13 @@ import { generateVariableClass, generateFontFamilyClass, generateFontFaces } fro
24
24
  import { generateFallbackCss, buildFontStack } from '../fonts/fallbacks.js';
25
25
  import { processLocalFont, generateLocalFontFaces } from '../fonts/local.js';
26
26
  import { inferFontFormat } from '../fonts/local.js';
27
- import { downloadAndCacheFonts, type CachedFont } from '../fonts/google.js';
27
+ import {
28
+ downloadAndCacheFonts,
29
+ generateProductionFontFaces,
30
+ resolveDevFontFaces,
31
+ type CachedFont,
32
+ } from '../fonts/google.js';
33
+ import type { FontFaceDescriptor } from '../fonts/types.js';
28
34
  import {
29
35
  extractFontConfigAst,
30
36
  extractLocalFontConfigAst,
@@ -264,10 +270,13 @@ function generateLocalVirtualModule(): string {
264
270
  /**
265
271
  * Generate CSS for a single extracted font.
266
272
  *
267
- * Includes @font-face rules (for local fonts), fallback @font-face,
273
+ * Includes @font-face rules (for local and Google fonts), fallback @font-face,
268
274
  * and the scoped class rule.
275
+ *
276
+ * For Google fonts, pass the resolved FontFaceDescriptor[] from either
277
+ * `generateProductionFontFaces()` (production) or `resolveDevFontFaces()` (dev).
269
278
  */
270
- export function generateFontCss(font: ExtractedFont): string {
279
+ export function generateFontCss(font: ExtractedFont, googleFaces?: FontFaceDescriptor[]): string {
271
280
  const cssParts: string[] = [];
272
281
 
273
282
  if (font.provider === 'local' && font.localSources) {
@@ -276,6 +285,11 @@ export function generateFontCss(font: ExtractedFont): string {
276
285
  if (faceCss) cssParts.push(faceCss);
277
286
  }
278
287
 
288
+ if (font.provider === 'google' && googleFaces && googleFaces.length > 0) {
289
+ const faceCss = generateFontFaces(googleFaces);
290
+ if (faceCss) cssParts.push(faceCss);
291
+ }
292
+
279
293
  const fallbackCss = generateFallbackCss(font.family);
280
294
  if (fallbackCss) cssParts.push(fallbackCss);
281
295
 
@@ -291,13 +305,20 @@ export function generateFontCss(font: ExtractedFont): string {
291
305
  /**
292
306
  * Generate the CSS output for all extracted fonts.
293
307
  *
294
- * Includes @font-face rules for local fonts, fallback @font-face rules,
295
- * and scoped classes.
308
+ * Includes @font-face rules for local and Google fonts, fallback @font-face
309
+ * rules, and scoped classes.
310
+ *
311
+ * `googleFontFacesMap` provides pre-resolved FontFaceDescriptor[] for each
312
+ * Google font ID (keyed by ExtractedFont.id).
296
313
  */
297
- export function generateAllFontCss(registry: FontRegistry): string {
314
+ export function generateAllFontCss(
315
+ registry: FontRegistry,
316
+ googleFontFacesMap?: Map<string, FontFaceDescriptor[]>
317
+ ): string {
298
318
  const cssParts: string[] = [];
299
319
  for (const font of registry.values()) {
300
- cssParts.push(generateFontCss(font));
320
+ const googleFaces = googleFontFacesMap?.get(font.id);
321
+ cssParts.push(generateFontCss(font, googleFaces));
301
322
  }
302
323
  return cssParts.join('\n\n');
303
324
  }
@@ -390,6 +411,11 @@ export function timberFonts(ctx: PluginContext): Plugin {
390
411
  const registry: FontRegistry = new Map();
391
412
  /** Fonts downloaded during buildStart (production only). */
392
413
  let cachedFonts: CachedFont[] = [];
414
+ /**
415
+ * Pre-resolved @font-face descriptors for Google fonts, keyed by font ID.
416
+ * Populated in buildStart (production) or lazily in load (dev).
417
+ */
418
+ const googleFontFacesMap = new Map<string, FontFaceDescriptor[]>();
393
419
 
394
420
  return {
395
421
  name: 'timber-fonts',
@@ -429,12 +455,32 @@ export function timberFonts(ctx: PluginContext): Plugin {
429
455
  * Because this is loaded lazily (on first request), the font
430
456
  * registry is always populated by the time it's needed.
431
457
  */
432
- load(id: string) {
458
+ async load(id: string) {
433
459
  if (id === RESOLVED_GOOGLE) return generateGoogleVirtualModule(registry);
434
460
  if (id === RESOLVED_LOCAL) return generateLocalVirtualModule();
435
461
 
436
462
  if (id === RESOLVED_FONT_CSS_REGISTER) {
437
- const css = generateAllFontCss(registry);
463
+ // In dev mode, resolve Google font faces from CDN on demand.
464
+ if (ctx.dev) {
465
+ const googleFonts = [...registry.values()].filter((f) => f.provider === 'google');
466
+ for (const font of googleFonts) {
467
+ if (!googleFontFacesMap.has(font.id)) {
468
+ try {
469
+ const faces = await resolveDevFontFaces(font);
470
+ googleFontFacesMap.set(font.id, faces);
471
+ } catch (e) {
472
+ // In dev mode, fail gracefully — fonts fall back to system fonts.
473
+ const msg = e instanceof Error ? e.message : String(e);
474
+ console.warn(
475
+ `[timber-fonts] Failed to resolve Google font "${font.family}": ${msg}`
476
+ );
477
+ googleFontFacesMap.set(font.id, []);
478
+ }
479
+ }
480
+ }
481
+ }
482
+
483
+ const css = generateAllFontCss(registry, googleFontFacesMap);
438
484
  // Side-effect module: sets font CSS on globalThis for the RSC entry to read.
439
485
  return `globalThis.__timber_font_css = ${JSON.stringify(css)};`;
440
486
  }
@@ -515,6 +561,22 @@ export function timberFonts(ctx: PluginContext): Plugin {
515
561
  if (googleFonts.length === 0) return;
516
562
 
517
563
  cachedFonts = await downloadAndCacheFonts(googleFonts, ctx.root);
564
+
565
+ // Build a family→CachedFont[] lookup, then generate production @font-face
566
+ // descriptors for each registered Google font.
567
+ const cachedByFamily = new Map<string, CachedFont[]>();
568
+ for (const cf of cachedFonts) {
569
+ const key = cf.face.family.toLowerCase();
570
+ const arr = cachedByFamily.get(key) ?? [];
571
+ arr.push(cf);
572
+ cachedByFamily.set(key, arr);
573
+ }
574
+
575
+ for (const font of googleFonts) {
576
+ const familyCached = cachedByFamily.get(font.family.toLowerCase()) ?? [];
577
+ const faces = generateProductionFontFaces(familyCached, font.display);
578
+ googleFontFacesMap.set(font.id, faces);
579
+ }
518
580
  },
519
581
 
520
582
  /**
package/LICENSE DELETED
@@ -1,8 +0,0 @@
1
- DONTFUCKINGUSE LICENSE
2
-
3
- Copyright (c) 2025 Daniel Saewitz
4
-
5
- This software may not be used, copied, modified, merged, published,
6
- distributed, sublicensed, or sold by anyone other than the copyright holder.
7
-
8
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND.