@timber-js/app 0.2.0-alpha.8 → 0.2.0-alpha.9
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/index.d.ts +0 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +38 -34
- package/dist/index.js.map +1 -1
- package/dist/plugins/fonts.d.ts +7 -0
- package/dist/plugins/fonts.d.ts.map +1 -1
- package/dist/server/rsc-entry/index.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/index.ts +1 -2
- package/src/plugins/entries.ts +1 -1
- package/src/plugins/fonts.ts +54 -43
- package/src/server/rsc-entry/index.ts +7 -5
package/dist/plugins/fonts.d.ts
CHANGED
|
@@ -56,6 +56,13 @@ export declare function parseGoogleFontImports(source: string): string[];
|
|
|
56
56
|
* e.g. { Inter: 'Inter', JetBrains_Mono: 'JetBrains Mono' }
|
|
57
57
|
*/
|
|
58
58
|
export declare function parseGoogleFontFamilies(source: string): Map<string, string>;
|
|
59
|
+
/**
|
|
60
|
+
* Generate CSS for a single extracted font.
|
|
61
|
+
*
|
|
62
|
+
* Includes @font-face rules (for local fonts), fallback @font-face,
|
|
63
|
+
* and the scoped class rule.
|
|
64
|
+
*/
|
|
65
|
+
export declare function generateFontCss(font: ExtractedFont): string;
|
|
59
66
|
/**
|
|
60
67
|
* Generate the CSS output for all extracted fonts.
|
|
61
68
|
*
|
|
@@ -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;
|
|
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;AA4BxE;;;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,CAkUtD"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/server/rsc-entry/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/server/rsc-entry/index.ts"],"names":[],"mappings":"AAkFA;;;;;GAKG;AACH,wBAAgB,0BAA0B,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,GAAG,IAAI,CAE/F;AAiaD,OAAO,EAAE,uBAAuB,EAAE,MAAM,gCAAgC,CAAC;AAIzE,OAAO,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;8BArQ3C,OAAO,KAAG,OAAO,CAAC,QAAQ,CAAC;AAuQhD,wBAAiE"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@timber-js/app",
|
|
3
|
-
"version": "0.2.0-alpha.
|
|
3
|
+
"version": "0.2.0-alpha.9",
|
|
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,8 +223,7 @@ 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
|
-
|
|
227
|
-
fontCssUrl?: string;
|
|
226
|
+
|
|
228
227
|
}
|
|
229
228
|
|
|
230
229
|
/**
|
package/src/plugins/entries.ts
CHANGED
package/src/plugins/fonts.ts
CHANGED
|
@@ -36,8 +36,15 @@ const VIRTUAL_LOCAL = '@timber/fonts/local';
|
|
|
36
36
|
const RESOLVED_GOOGLE = '\0@timber/fonts/google';
|
|
37
37
|
const RESOLVED_LOCAL = '\0@timber/fonts/local';
|
|
38
38
|
|
|
39
|
-
/**
|
|
40
|
-
|
|
39
|
+
/**
|
|
40
|
+
* Virtual module that exports the combined font CSS string.
|
|
41
|
+
*
|
|
42
|
+
* The RSC entry imports this module and inlines it as a <style> tag.
|
|
43
|
+
* Unlike a config-based approach, this module is loaded lazily (on first
|
|
44
|
+
* request), so it always has up-to-date font data from the registry.
|
|
45
|
+
*/
|
|
46
|
+
const VIRTUAL_FONT_CSS = 'virtual:timber-font-css';
|
|
47
|
+
const RESOLVED_FONT_CSS = '\0virtual:timber-font-css';
|
|
41
48
|
|
|
42
49
|
/**
|
|
43
50
|
* Registry of fonts extracted during transform.
|
|
@@ -247,6 +254,33 @@ function generateLocalVirtualModule(): string {
|
|
|
247
254
|
].join('\n');
|
|
248
255
|
}
|
|
249
256
|
|
|
257
|
+
/**
|
|
258
|
+
* Generate CSS for a single extracted font.
|
|
259
|
+
*
|
|
260
|
+
* Includes @font-face rules (for local fonts), fallback @font-face,
|
|
261
|
+
* and the scoped class rule.
|
|
262
|
+
*/
|
|
263
|
+
export function generateFontCss(font: ExtractedFont): string {
|
|
264
|
+
const cssParts: string[] = [];
|
|
265
|
+
|
|
266
|
+
if (font.provider === 'local' && font.localSources) {
|
|
267
|
+
const faces = generateLocalFontFaces(font.family, font.localSources, font.display);
|
|
268
|
+
const faceCss = generateFontFaces(faces);
|
|
269
|
+
if (faceCss) cssParts.push(faceCss);
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
const fallbackCss = generateFallbackCss(font.family);
|
|
273
|
+
if (fallbackCss) cssParts.push(fallbackCss);
|
|
274
|
+
|
|
275
|
+
if (font.variable) {
|
|
276
|
+
cssParts.push(generateVariableClass(font.className, font.variable, font.fontFamily));
|
|
277
|
+
} else {
|
|
278
|
+
cssParts.push(generateFontFamilyClass(font.className, font.fontFamily));
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
return cssParts.join('\n\n');
|
|
282
|
+
}
|
|
283
|
+
|
|
250
284
|
/**
|
|
251
285
|
* Generate the CSS output for all extracted fonts.
|
|
252
286
|
*
|
|
@@ -255,27 +289,9 @@ function generateLocalVirtualModule(): string {
|
|
|
255
289
|
*/
|
|
256
290
|
export function generateAllFontCss(registry: FontRegistry): string {
|
|
257
291
|
const cssParts: string[] = [];
|
|
258
|
-
|
|
259
292
|
for (const font of registry.values()) {
|
|
260
|
-
|
|
261
|
-
if (font.provider === 'local' && font.localSources) {
|
|
262
|
-
const faces = generateLocalFontFaces(font.family, font.localSources, font.display);
|
|
263
|
-
const faceCss = generateFontFaces(faces);
|
|
264
|
-
if (faceCss) cssParts.push(faceCss);
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
// Generate fallback @font-face if metrics are available
|
|
268
|
-
const fallbackCss = generateFallbackCss(font.family);
|
|
269
|
-
if (fallbackCss) cssParts.push(fallbackCss);
|
|
270
|
-
|
|
271
|
-
// Generate scoped class
|
|
272
|
-
if (font.variable) {
|
|
273
|
-
cssParts.push(generateVariableClass(font.className, font.variable, font.fontFamily));
|
|
274
|
-
} else {
|
|
275
|
-
cssParts.push(generateFontFamilyClass(font.className, font.fontFamily));
|
|
276
|
-
}
|
|
293
|
+
cssParts.push(generateFontCss(font));
|
|
277
294
|
}
|
|
278
|
-
|
|
279
295
|
return cssParts.join('\n\n');
|
|
280
296
|
}
|
|
281
297
|
|
|
@@ -372,20 +388,32 @@ export function timberFonts(ctx: PluginContext): Plugin {
|
|
|
372
388
|
name: 'timber-fonts',
|
|
373
389
|
|
|
374
390
|
/**
|
|
375
|
-
* Resolve `@timber/fonts/google
|
|
391
|
+
* Resolve `@timber/fonts/google`, `@timber/fonts/local`,
|
|
392
|
+
* and `virtual:timber-font-css` virtual modules.
|
|
376
393
|
*/
|
|
377
394
|
resolveId(id: string) {
|
|
378
395
|
if (id === VIRTUAL_GOOGLE) return RESOLVED_GOOGLE;
|
|
379
396
|
if (id === VIRTUAL_LOCAL) return RESOLVED_LOCAL;
|
|
397
|
+
if (id === VIRTUAL_FONT_CSS) return RESOLVED_FONT_CSS;
|
|
380
398
|
return null;
|
|
381
399
|
},
|
|
382
400
|
|
|
383
401
|
/**
|
|
384
402
|
* Return generated source for font virtual modules.
|
|
403
|
+
*
|
|
404
|
+
* `virtual:timber-font-css` exports the combined @font-face CSS
|
|
405
|
+
* as a string. The RSC entry imports it and inlines a <style> tag.
|
|
406
|
+
* Because this is loaded lazily (on first request), the font
|
|
407
|
+
* registry is always populated by the time it's needed.
|
|
385
408
|
*/
|
|
386
409
|
load(id: string) {
|
|
387
410
|
if (id === RESOLVED_GOOGLE) return generateGoogleVirtualModule(registry);
|
|
388
411
|
if (id === RESOLVED_LOCAL) return generateLocalVirtualModule();
|
|
412
|
+
|
|
413
|
+
if (id === RESOLVED_FONT_CSS) {
|
|
414
|
+
const css = generateAllFontCss(registry);
|
|
415
|
+
return `export default ${JSON.stringify(css)};`;
|
|
416
|
+
}
|
|
389
417
|
return null;
|
|
390
418
|
},
|
|
391
419
|
|
|
@@ -412,14 +440,8 @@ export function timberFonts(ctx: PluginContext): Plugin {
|
|
|
412
440
|
return;
|
|
413
441
|
}
|
|
414
442
|
|
|
415
|
-
//
|
|
416
|
-
|
|
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
|
-
}
|
|
443
|
+
// Font CSS is now injected via Vite's CSS pipeline (virtual:timber-font-css modules).
|
|
444
|
+
// This middleware only serves font binary files (woff2, etc.).
|
|
423
445
|
|
|
424
446
|
// Find the matching font file in the registry
|
|
425
447
|
for (const font of registry.values()) {
|
|
@@ -578,10 +600,6 @@ export function timberFonts(ctx: PluginContext): Plugin {
|
|
|
578
600
|
}
|
|
579
601
|
|
|
580
602
|
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
|
-
}
|
|
585
603
|
return { code: transformedCode, map: null };
|
|
586
604
|
}
|
|
587
605
|
|
|
@@ -627,15 +645,8 @@ export function timberFonts(ctx: PluginContext): Plugin {
|
|
|
627
645
|
}
|
|
628
646
|
}
|
|
629
647
|
|
|
630
|
-
//
|
|
631
|
-
|
|
632
|
-
if (fontCss) {
|
|
633
|
-
this.emitFile({
|
|
634
|
-
type: 'asset',
|
|
635
|
-
fileName: '_timber/fonts/fonts.css',
|
|
636
|
-
source: fontCss,
|
|
637
|
-
});
|
|
638
|
-
}
|
|
648
|
+
// Font CSS is emitted by Vite's CSS pipeline via virtual:timber-font-css modules.
|
|
649
|
+
// We only need to emit font binary files and update the build manifest here.
|
|
639
650
|
|
|
640
651
|
if (!ctx.buildManifest) return;
|
|
641
652
|
|
|
@@ -23,6 +23,8 @@ import config from 'virtual:timber-config';
|
|
|
23
23
|
import buildManifest from 'virtual:timber-build-manifest';
|
|
24
24
|
// @ts-expect-error — virtual module provided by timber-entries plugin
|
|
25
25
|
import loadUserInstrumentation from 'virtual:timber-instrumentation';
|
|
26
|
+
// @ts-expect-error — virtual module provided by timber-fonts plugin
|
|
27
|
+
import fontCss from 'virtual:timber-font-css';
|
|
26
28
|
|
|
27
29
|
import type { FormRerender } from '#/server/action-handler.js';
|
|
28
30
|
import { handleActionRequest, isActionRequest } from '#/server/action-handler.js';
|
|
@@ -386,11 +388,11 @@ async function renderRoute(
|
|
|
386
388
|
headHtml += buildCssLinkTags(cssUrls);
|
|
387
389
|
}
|
|
388
390
|
|
|
389
|
-
//
|
|
390
|
-
// The
|
|
391
|
-
|
|
392
|
-
if (
|
|
393
|
-
headHtml += `<
|
|
391
|
+
// Inline font CSS as a <style> tag — @font-face rules and scoped classes.
|
|
392
|
+
// The virtual:timber-font-css module is loaded lazily, so the font registry
|
|
393
|
+
// is always populated by the time we get here (no timing issues).
|
|
394
|
+
if (fontCss) {
|
|
395
|
+
headHtml += `<style data-timber-fonts>${fontCss}</style>`;
|
|
394
396
|
}
|
|
395
397
|
|
|
396
398
|
const fontEntries = collectRouteFonts(segments, typedManifest);
|