@timber-js/app 0.2.0-alpha.5 → 0.2.0-alpha.7

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.
@@ -1 +1 @@
1
- {"version":3,"file":"entries.d.ts","sourceRoot":"","sources":["../../src/plugins/entries.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAC;AAInC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AA6GhD;;;;;GAKG;AACH,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAUrE;AAED;;;;;;;;GAQG;AACH,wBAAgB,6BAA6B,CAAC,mBAAmB,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,CAwBxF;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,aAAa,GAAG,MAAM,CAwExD"}
1
+ {"version":3,"file":"entries.d.ts","sourceRoot":"","sources":["../../src/plugins/entries.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAC;AAInC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AA8GhD;;;;;GAKG;AACH,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAUrE;AAED;;;;;;;;GAQG;AACH,wBAAgB,6BAA6B,CAAC,mBAAmB,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,CAwBxF;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,aAAa,GAAG,MAAM,CAwExD"}
@@ -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,YAAY,CAAC;AAChD,OAAO,KAAK,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAoBxE;;;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,kBAAkB,CAAC,QAAQ,EAAE,YAAY,GAAG,MAAM,CAwBjE;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,CA6TtD"}
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,YAAY,CAAC;AAChD,OAAO,KAAK,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAqBxE;;;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,kBAAkB,CAAC,QAAQ,EAAE,YAAY,GAAG,MAAM,CAwBjE;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,CAuUtD"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/server/rsc-entry/index.ts"],"names":[],"mappings":"AAgFA;;;;;GAKG;AACH,wBAAgB,0BAA0B,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,GAAG,IAAI,CAE/F;AAyZD,OAAO,EAAE,uBAAuB,EAAE,MAAM,gCAAgC,CAAC;AAIzE,OAAO,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;8BA7P3C,OAAO,KAAG,OAAO,CAAC,QAAQ,CAAC;AA+PhD,wBAAiE"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/server/rsc-entry/index.ts"],"names":[],"mappings":"AAgFA;;;;;GAKG;AACH,wBAAgB,0BAA0B,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,GAAG,IAAI,CAE/F;AAgaD,OAAO,EAAE,uBAAuB,EAAE,MAAM,gCAAgC,CAAC;AAIzE,OAAO,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;8BApQ3C,OAAO,KAAG,OAAO,CAAC,QAAQ,CAAC;AAsQhD,wBAAiE"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@timber-js/app",
3
- "version": "0.2.0-alpha.5",
3
+ "version": "0.2.0-alpha.7",
4
4
  "description": "Vite-native React framework for Cloudflare Workers — correct HTTP semantics, real status codes, pages that work without JavaScript",
5
5
  "keywords": [
6
6
  "cloudflare-workers",
package/src/index.ts CHANGED
@@ -223,6 +223,8 @@ export interface PluginContext {
223
223
  buildManifest: BuildManifest | null;
224
224
  /** Startup timer for profiling cold start phases (active in dev, no-op in prod) */
225
225
  timer: StartupTimer;
226
+ /** URL path to font CSS file, set by timber-fonts when fonts are registered */
227
+ fontCssUrl?: string;
226
228
  }
227
229
 
228
230
  /**
@@ -112,6 +112,7 @@ function generateConfigModule(ctx: PluginContext): string {
112
112
  topLoader: ctx.config.topLoader,
113
113
  responseCache: ctx.config.responseCache,
114
114
  debug: ctx.config.debug ?? false,
115
+ fontCssUrl: ctx.fontCssUrl ?? null,
115
116
  };
116
117
 
117
118
  return [
@@ -33,10 +33,11 @@ import {
33
33
 
34
34
  const VIRTUAL_GOOGLE = '@timber/fonts/google';
35
35
  const VIRTUAL_LOCAL = '@timber/fonts/local';
36
- const VIRTUAL_FONT_CSS = 'virtual:timber-fonts-css';
37
36
  const RESOLVED_GOOGLE = '\0@timber/fonts/google';
38
37
  const RESOLVED_LOCAL = '\0@timber/fonts/local';
39
- const RESOLVED_FONT_CSS = '\0virtual:timber-fonts-css';
38
+
39
+ /** URL path where font CSS is served (dev middleware and prod asset). */
40
+ const FONT_CSS_PATH = '/_timber/fonts/fonts.css';
40
41
 
41
42
  /**
42
43
  * Registry of fonts extracted during transform.
@@ -371,34 +372,32 @@ export function timberFonts(ctx: PluginContext): Plugin {
371
372
  name: 'timber-fonts',
372
373
 
373
374
  /**
374
- * Resolve `@timber/fonts/google`, `@timber/fonts/local`, and the
375
- * virtual font CSS module to internal IDs.
375
+ * Resolve `@timber/fonts/google` and `@timber/fonts/local` to virtual modules.
376
376
  */
377
377
  resolveId(id: string) {
378
378
  if (id === VIRTUAL_GOOGLE) return RESOLVED_GOOGLE;
379
379
  if (id === VIRTUAL_LOCAL) return RESOLVED_LOCAL;
380
- if (id === VIRTUAL_FONT_CSS) return RESOLVED_FONT_CSS;
381
380
  return null;
382
381
  },
383
382
 
384
383
  /**
385
384
  * Return generated source for font virtual modules.
386
- *
387
- * The font CSS virtual module returns the combined @font-face rules,
388
- * fallback CSS, and scoped classes for all registered fonts.
389
385
  */
390
386
  load(id: string) {
391
387
  if (id === RESOLVED_GOOGLE) return generateGoogleVirtualModule(registry);
392
388
  if (id === RESOLVED_LOCAL) return generateLocalVirtualModule();
393
- if (id === RESOLVED_FONT_CSS) return generateAllFontCss(registry);
394
389
  return null;
395
390
  },
396
391
 
397
392
  /**
398
- * Serve local font files in dev mode under `/_timber/fonts/`.
393
+ * Serve local font files and font CSS in dev mode under `/_timber/fonts/`.
399
394
  *
400
- * Only files that are registered in the font registry (via localSources)
401
- * are served. Paths are validated to prevent directory traversal.
395
+ * Serves:
396
+ * - `/_timber/fonts/fonts.css` combined @font-face + scoped class CSS
397
+ * - `/_timber/fonts/<filename>` — individual font files from the registry
398
+ *
399
+ * Only files registered in the font registry are served.
400
+ * Paths are validated to prevent directory traversal.
402
401
  */
403
402
  configureServer(server: ViteDevServer) {
404
403
  server.middlewares.use((req, res, next) => {
@@ -413,6 +412,15 @@ export function timberFonts(ctx: PluginContext): Plugin {
413
412
  return;
414
413
  }
415
414
 
415
+ // Serve generated font CSS
416
+ if (requestedFilename === 'fonts.css') {
417
+ const css = generateAllFontCss(registry);
418
+ res.setHeader('Content-Type', 'text/css');
419
+ res.setHeader('Cache-Control', 'no-cache');
420
+ res.end(css);
421
+ return;
422
+ }
423
+
416
424
  // Find the matching font file in the registry
417
425
  for (const font of registry.values()) {
418
426
  if (font.provider !== 'local' || !font.localSources) continue;
@@ -420,7 +428,6 @@ export function timberFonts(ctx: PluginContext): Plugin {
420
428
  const basename = src.path.split('/').pop() ?? '';
421
429
  if (basename === requestedFilename) {
422
430
  const absolutePath = normalize(resolve(src.path));
423
- // Verify the resolved path hasn't escaped (extra safety)
424
431
  if (!existsSync(absolutePath)) {
425
432
  res.statusCode = 404;
426
433
  res.end('Not found');
@@ -571,6 +578,10 @@ export function timberFonts(ctx: PluginContext): Plugin {
571
578
  }
572
579
 
573
580
  if (transformedCode !== code) {
581
+ // Mark that fonts are in use so the RSC entry injects a <link> tag.
582
+ if (registry.size > 0 && !ctx.fontCssUrl) {
583
+ ctx.fontCssUrl = FONT_CSS_PATH;
584
+ }
574
585
  return { code: transformedCode, map: null };
575
586
  }
576
587
 
@@ -385,6 +385,13 @@ async function renderRoute(
385
385
  headHtml += buildCssLinkTags(cssUrls);
386
386
  }
387
387
 
388
+ // Inject font CSS stylesheet — pure CSS, no JS needed.
389
+ // The URL is set by the timber-fonts plugin when fonts are registered.
390
+ const fontCssUrl = (config as { fontCssUrl?: string | null }).fontCssUrl;
391
+ if (fontCssUrl) {
392
+ headHtml += `<link rel="stylesheet" href="${fontCssUrl}">`;
393
+ }
394
+
388
395
  const fontEntries = collectRouteFonts(segments, typedManifest);
389
396
  if (fontEntries.length > 0) {
390
397
  headHtml += buildFontPreloadTags(fontEntries);