@timber-js/app 0.2.0-alpha.4 → 0.2.0-alpha.6

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 (33) hide show
  1. package/dist/_chunks/debug-gwlJkDuf.js +108 -0
  2. package/dist/_chunks/debug-gwlJkDuf.js.map +1 -0
  3. package/dist/_chunks/{format-CwdaB0_2.js → format-DviM89f0.js} +2 -2
  4. package/dist/_chunks/{format-CwdaB0_2.js.map → format-DviM89f0.js.map} +1 -1
  5. package/dist/_chunks/{request-context-CZJi4CuK.js → request-context-DIkVh_jG.js} +2 -2
  6. package/dist/_chunks/{request-context-CZJi4CuK.js.map → request-context-DIkVh_jG.js.map} +1 -1
  7. package/dist/cookies/index.js +1 -1
  8. package/dist/fonts/local.d.ts +4 -2
  9. package/dist/fonts/local.d.ts.map +1 -1
  10. package/dist/index.js +197 -13
  11. package/dist/index.js.map +1 -1
  12. package/dist/plugins/entries.d.ts +7 -0
  13. package/dist/plugins/entries.d.ts.map +1 -1
  14. package/dist/plugins/fonts.d.ts +2 -1
  15. package/dist/plugins/fonts.d.ts.map +1 -1
  16. package/dist/plugins/mdx.d.ts +6 -0
  17. package/dist/plugins/mdx.d.ts.map +1 -1
  18. package/dist/server/action-client.d.ts.map +1 -1
  19. package/dist/server/debug.d.ts +46 -15
  20. package/dist/server/debug.d.ts.map +1 -1
  21. package/dist/server/index.js +4 -4
  22. package/dist/server/index.js.map +1 -1
  23. package/dist/server/rsc-entry/index.d.ts.map +1 -1
  24. package/package.json +1 -1
  25. package/src/fonts/local.ts +7 -3
  26. package/src/plugins/entries.ts +7 -4
  27. package/src/plugins/fonts.ts +110 -5
  28. package/src/plugins/mdx.ts +9 -5
  29. package/src/server/action-client.ts +7 -4
  30. package/src/server/debug.ts +55 -17
  31. package/src/server/rsc-entry/index.ts +17 -6
  32. package/dist/_chunks/debug-B4WUeqJ-.js +0 -75
  33. package/dist/_chunks/debug-B4WUeqJ-.js.map +0 -1
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
- import { r as setViteServer, t as formatSize } from "./_chunks/format-CwdaB0_2.js";
1
+ import { r as setViteServer, t as formatSize } from "./_chunks/format-DviM89f0.js";
2
2
  import { i as scanRoutes, n as generateRouteMap, t as collectInterceptionRewrites } from "./_chunks/interception-BOoWmLUA.js";
3
3
  import { existsSync, readFileSync } from "node:fs";
4
- import { dirname, extname, join, resolve } from "node:path";
4
+ import { dirname, extname, join, normalize, resolve } from "node:path";
5
5
  import { fileURLToPath, pathToFileURL } from "node:url";
6
6
  import { createRequire } from "node:module";
7
7
  import react from "@vitejs/plugin-react";
@@ -11953,12 +11953,14 @@ function generateConfigModule(ctx) {
11953
11953
  * extensions as timber.config.ts detection.
11954
11954
  */
11955
11955
  function detectInstrumentationFile(root) {
11956
- for (const ext of [
11956
+ const extensions = [
11957
11957
  ".ts",
11958
11958
  ".js",
11959
11959
  ".mjs"
11960
- ]) {
11961
- const candidate = resolve(root, `instrumentation${ext}`);
11960
+ ];
11961
+ const dirs = [root, resolve(root, "src")];
11962
+ for (const dir of dirs) for (const ext of extensions) {
11963
+ const candidate = resolve(dir, `instrumentation${ext}`);
11962
11964
  if (existsSync(candidate)) return candidate;
11963
11965
  }
11964
11966
  return null;
@@ -12018,17 +12020,20 @@ function timberEntries(ctx) {
12018
12020
  //#region src/plugins/mdx.ts
12019
12021
  var MDX_EXTENSIONS = ["mdx", "md"];
12020
12022
  /**
12021
- * Check if mdx-components.tsx (or .ts, .jsx, .js) exists at the project root.
12023
+ * Check if mdx-components.tsx (or .ts, .jsx, .js) exists at the project root
12024
+ * or in src/. Root takes precedence, matching Next.js behavior.
12022
12025
  * Returns the absolute path if found, otherwise undefined.
12023
12026
  */
12024
12027
  function findMdxComponents(root) {
12025
- for (const name of [
12028
+ const candidates = [
12026
12029
  "mdx-components.tsx",
12027
12030
  "mdx-components.ts",
12028
12031
  "mdx-components.jsx",
12029
12032
  "mdx-components.js"
12030
- ]) {
12031
- const p = join(root, name);
12033
+ ];
12034
+ const dirs = [root, join(root, "src")];
12035
+ for (const dir of dirs) for (const name of candidates) {
12036
+ const p = join(dir, name);
12032
12037
  if (existsSync(p)) return p;
12033
12038
  }
12034
12039
  }
@@ -12542,6 +12547,58 @@ export const coerce = stub;
12542
12547
  };
12543
12548
  }
12544
12549
  //#endregion
12550
+ //#region src/fonts/css.ts
12551
+ /**
12552
+ * Generate a single `@font-face` CSS rule from a descriptor.
12553
+ */
12554
+ function generateFontFace(desc) {
12555
+ const lines = [];
12556
+ lines.push("@font-face {");
12557
+ lines.push(` font-family: '${desc.family}';`);
12558
+ lines.push(` src: ${desc.src};`);
12559
+ if (desc.weight) lines.push(` font-weight: ${desc.weight};`);
12560
+ if (desc.style) lines.push(` font-style: ${desc.style};`);
12561
+ if (desc.display) lines.push(` font-display: ${desc.display};`);
12562
+ if (desc.unicodeRange) lines.push(` unicode-range: ${desc.unicodeRange};`);
12563
+ lines.push("}");
12564
+ return lines.join("\n");
12565
+ }
12566
+ /**
12567
+ * Generate multiple `@font-face` rules from an array of descriptors.
12568
+ */
12569
+ function generateFontFaces(descriptors) {
12570
+ return descriptors.map(generateFontFace).join("\n\n");
12571
+ }
12572
+ /**
12573
+ * Generate a scoped CSS class that sets a CSS custom property for the font.
12574
+ *
12575
+ * Example output:
12576
+ * ```css
12577
+ * .timber-font-inter {
12578
+ * --font-sans: 'Inter', 'Inter Fallback', system-ui, sans-serif;
12579
+ * }
12580
+ * ```
12581
+ */
12582
+ function generateVariableClass(className, variable, fontFamily) {
12583
+ return `.${className} {\n ${variable}: ${fontFamily};\n}`;
12584
+ }
12585
+ /**
12586
+ * Generate a scoped CSS class that applies font-family directly.
12587
+ *
12588
+ * Used when no `variable` is specified — the className applies
12589
+ * the font-family inline instead of through a CSS custom property.
12590
+ *
12591
+ * Example output:
12592
+ * ```css
12593
+ * .timber-font-inter {
12594
+ * font-family: 'Inter', 'Inter Fallback', system-ui, sans-serif;
12595
+ * }
12596
+ * ```
12597
+ */
12598
+ function generateFontFamilyClass(className, fontFamily) {
12599
+ return `.${className} {\n font-family: ${fontFamily};\n}`;
12600
+ }
12601
+ //#endregion
12545
12602
  //#region src/fonts/fallbacks.ts
12546
12603
  /**
12547
12604
  * Lookup table for commonly used Google Fonts.
@@ -12680,6 +12737,26 @@ function getGenericFamily(family) {
12680
12737
  return "sans-serif";
12681
12738
  }
12682
12739
  /**
12740
+ * Generate the full CSS for a size-adjusted fallback font.
12741
+ *
12742
+ * This produces a complete @font-face block with override descriptors
12743
+ * that FontFaceDescriptor doesn't natively support.
12744
+ */
12745
+ function generateFallbackCss(family) {
12746
+ const metrics = FALLBACK_METRICS[family.toLowerCase()];
12747
+ if (!metrics) return null;
12748
+ return [
12749
+ "@font-face {",
12750
+ ` font-family: '${`${family} Fallback`}';`,
12751
+ ` src: local('${metrics.fallbackFont}');`,
12752
+ ` size-adjust: ${metrics.sizeAdjust}%;`,
12753
+ ` ascent-override: ${metrics.ascentOverride}%;`,
12754
+ ` descent-override: ${metrics.descentOverride}%;`,
12755
+ ` line-gap-override: ${metrics.lineGapOverride}%;`,
12756
+ "}"
12757
+ ].join("\n");
12758
+ }
12759
+ /**
12683
12760
  * Check whether fallback metrics are available for a font family.
12684
12761
  */
12685
12762
  function hasFallbackMetrics(family) {
@@ -13028,6 +13105,26 @@ function generateFamilyName(sources) {
13028
13105
  return stem.replace(/[-_]?(Regular|Bold|Italic|Light|Medium|SemiBold|ExtraBold|Thin|Black|Heavy)$/i, "") || stem;
13029
13106
  }
13030
13107
  /**
13108
+ * Generate @font-face descriptors for local font sources.
13109
+ *
13110
+ * Each source entry produces one @font-face rule. The `src` descriptor
13111
+ * uses a `url()` pointing to the served path under `/_timber/fonts/`.
13112
+ * The `urlPrefix` defaults to `/_timber/fonts` — the path used by both
13113
+ * the dev server middleware and the production build output.
13114
+ */
13115
+ function generateLocalFontFaces(family, sources, display, urlPrefix = "/_timber/fonts") {
13116
+ return sources.map((entry) => {
13117
+ const format = inferFontFormat(entry.path);
13118
+ return {
13119
+ family,
13120
+ src: `url('${urlPrefix}/${entry.path.split("/").pop() ?? entry.path}') format('${format}')`,
13121
+ weight: entry.weight,
13122
+ style: entry.style,
13123
+ display
13124
+ };
13125
+ });
13126
+ }
13127
+ /**
13031
13128
  * Build the className for a local font, following the same convention
13032
13129
  * as Google fonts: `timber-font-<lowercase-hyphenated-family>`.
13033
13130
  */
@@ -13260,8 +13357,10 @@ async function isCacheHit(metaPath, dataPath) {
13260
13357
  //#region src/plugins/fonts.ts
13261
13358
  var VIRTUAL_GOOGLE = "@timber/fonts/google";
13262
13359
  var VIRTUAL_LOCAL = "@timber/fonts/local";
13360
+ var VIRTUAL_FONT_CSS = "virtual:timber-fonts.css";
13263
13361
  var RESOLVED_GOOGLE = "\0@timber/fonts/google";
13264
13362
  var RESOLVED_LOCAL = "\0@timber/fonts/local";
13363
+ var RESOLVED_FONT_CSS = "\0virtual:timber-fonts.css";
13265
13364
  /**
13266
13365
  * Convert a font family name to a PascalCase export name.
13267
13366
  * e.g. "JetBrains Mono" → "JetBrains_Mono"
@@ -13409,6 +13508,26 @@ function generateLocalVirtualModule() {
13409
13508
  ].join("\n");
13410
13509
  }
13411
13510
  /**
13511
+ * Generate the CSS output for all extracted fonts.
13512
+ *
13513
+ * Includes @font-face rules for local fonts, fallback @font-face rules,
13514
+ * and scoped classes.
13515
+ */
13516
+ function generateAllFontCss(registry) {
13517
+ const cssParts = [];
13518
+ for (const font of registry.values()) {
13519
+ if (font.provider === "local" && font.localSources) {
13520
+ const faceCss = generateFontFaces(generateLocalFontFaces(font.family, font.localSources, font.display));
13521
+ if (faceCss) cssParts.push(faceCss);
13522
+ }
13523
+ const fallbackCss = generateFallbackCss(font.family);
13524
+ if (fallbackCss) cssParts.push(fallbackCss);
13525
+ if (font.variable) cssParts.push(generateVariableClass(font.className, font.variable, font.fontFamily));
13526
+ else cssParts.push(generateFontFamilyClass(font.className, font.fontFamily));
13527
+ }
13528
+ return cssParts.join("\n\n");
13529
+ }
13530
+ /**
13412
13531
  * Parse the local name used for the default import of `@timber/fonts/local`.
13413
13532
  *
13414
13533
  * Handles:
@@ -13461,13 +13580,52 @@ function timberFonts(ctx) {
13461
13580
  resolveId(id) {
13462
13581
  if (id === VIRTUAL_GOOGLE) return RESOLVED_GOOGLE;
13463
13582
  if (id === VIRTUAL_LOCAL) return RESOLVED_LOCAL;
13583
+ if (id === VIRTUAL_FONT_CSS) return RESOLVED_FONT_CSS;
13464
13584
  return null;
13465
13585
  },
13466
13586
  load(id) {
13467
13587
  if (id === RESOLVED_GOOGLE) return generateGoogleVirtualModule(registry);
13468
13588
  if (id === RESOLVED_LOCAL) return generateLocalVirtualModule();
13589
+ if (id === RESOLVED_FONT_CSS) return generateAllFontCss(registry);
13469
13590
  return null;
13470
13591
  },
13592
+ configureServer(server) {
13593
+ server.middlewares.use((req, res, next) => {
13594
+ const url = req.url;
13595
+ if (!url || !url.startsWith("/_timber/fonts/")) return next();
13596
+ const requestedFilename = url.slice(15);
13597
+ if (requestedFilename.includes("..") || requestedFilename.includes("/")) {
13598
+ res.statusCode = 400;
13599
+ res.end("Bad request");
13600
+ return;
13601
+ }
13602
+ for (const font of registry.values()) {
13603
+ if (font.provider !== "local" || !font.localSources) continue;
13604
+ for (const src of font.localSources) if ((src.path.split("/").pop() ?? "") === requestedFilename) {
13605
+ const absolutePath = normalize(resolve(src.path));
13606
+ if (!existsSync(absolutePath)) {
13607
+ res.statusCode = 404;
13608
+ res.end("Not found");
13609
+ return;
13610
+ }
13611
+ const data = readFileSync(absolutePath);
13612
+ const ext = absolutePath.split(".").pop()?.toLowerCase();
13613
+ res.setHeader("Content-Type", {
13614
+ woff2: "font/woff2",
13615
+ woff: "font/woff",
13616
+ ttf: "font/ttf",
13617
+ otf: "font/otf",
13618
+ eot: "application/vnd.ms-fontopen"
13619
+ }[ext ?? ""] ?? "application/octet-stream");
13620
+ res.setHeader("Cache-Control", "public, max-age=31536000, immutable");
13621
+ res.setHeader("Access-Control-Allow-Origin", "*");
13622
+ res.end(data);
13623
+ return;
13624
+ }
13625
+ }
13626
+ next();
13627
+ });
13628
+ },
13471
13629
  async buildStart() {
13472
13630
  if (ctx.dev) return;
13473
13631
  const googleFonts = [...registry.values()].filter((f) => f.provider === "google");
@@ -13523,10 +13681,13 @@ function timberFonts(ctx) {
13523
13681
  }
13524
13682
  }
13525
13683
  if (hasLocalImport) transformedCode = transformLocalFonts(transformedCode, code, id, registry, this.error.bind(this));
13526
- if (transformedCode !== code) return {
13527
- code: transformedCode,
13528
- map: null
13529
- };
13684
+ if (transformedCode !== code) {
13685
+ transformedCode = `import '${VIRTUAL_FONT_CSS}';\n` + transformedCode;
13686
+ return {
13687
+ code: transformedCode,
13688
+ map: null
13689
+ };
13690
+ }
13530
13691
  return null;
13531
13692
  },
13532
13693
  generateBundle() {
@@ -13535,6 +13696,29 @@ function timberFonts(ctx) {
13535
13696
  fileName: `_timber/fonts/${cf.hashedFilename}`,
13536
13697
  source: cf.data
13537
13698
  });
13699
+ for (const font of registry.values()) {
13700
+ if (font.provider !== "local" || !font.localSources) continue;
13701
+ for (const src of font.localSources) {
13702
+ const absolutePath = normalize(resolve(src.path));
13703
+ if (!existsSync(absolutePath)) {
13704
+ this.warn(`Local font file not found: ${absolutePath}`);
13705
+ continue;
13706
+ }
13707
+ const basename = src.path.split("/").pop() ?? src.path;
13708
+ const data = readFileSync(absolutePath);
13709
+ this.emitFile({
13710
+ type: "asset",
13711
+ fileName: `_timber/fonts/${basename}`,
13712
+ source: data
13713
+ });
13714
+ }
13715
+ }
13716
+ const fontCss = generateAllFontCss(registry);
13717
+ if (fontCss) this.emitFile({
13718
+ type: "asset",
13719
+ fileName: "_timber/fonts/fonts.css",
13720
+ source: fontCss
13721
+ });
13538
13722
  if (!ctx.buildManifest) return;
13539
13723
  const cachedByFamily = /* @__PURE__ */ new Map();
13540
13724
  for (const cf of cachedFonts) {