@puzzle-ui/theme 0.3.0 → 0.5.0

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 (62) hide show
  1. package/README.md +124 -84
  2. package/dist/cli.cjs +96 -0
  3. package/dist/cli.cjs.map +1 -0
  4. package/dist/cli.d.cts +1 -0
  5. package/dist/cli.d.ts +1 -0
  6. package/dist/cli.js +97 -0
  7. package/dist/cli.js.map +1 -0
  8. package/dist/constants.cjs +21 -21
  9. package/dist/constants.cjs.map +1 -1
  10. package/dist/constants.d.cts +8 -8
  11. package/dist/constants.d.ts +8 -8
  12. package/dist/constants.js +21 -21
  13. package/dist/constants.js.map +1 -1
  14. package/dist/fonts/constants.cjs +3 -3
  15. package/dist/fonts/constants.cjs.map +1 -1
  16. package/dist/fonts/constants.js +3 -3
  17. package/dist/fonts/constants.js.map +1 -1
  18. package/dist/fonts/create-font-preload-filename.cjs +9 -0
  19. package/dist/fonts/create-font-preload-filename.cjs.map +1 -0
  20. package/dist/fonts/create-font-preload-filename.js +9 -0
  21. package/dist/fonts/create-font-preload-filename.js.map +1 -0
  22. package/dist/fonts/create-google-font-url.cjs +3 -2
  23. package/dist/fonts/create-google-font-url.cjs.map +1 -1
  24. package/dist/fonts/create-google-font-url.js +3 -2
  25. package/dist/fonts/create-google-font-url.js.map +1 -1
  26. package/dist/fonts/create-local-font-stylesheet-url.cjs +10 -0
  27. package/dist/fonts/create-local-font-stylesheet-url.cjs.map +1 -0
  28. package/dist/fonts/create-local-font-stylesheet-url.js +10 -0
  29. package/dist/fonts/create-local-font-stylesheet-url.js.map +1 -0
  30. package/dist/fonts/download-google-fonts.cjs +138 -0
  31. package/dist/fonts/download-google-fonts.cjs.map +1 -0
  32. package/dist/fonts/download-google-fonts.js +138 -0
  33. package/dist/fonts/download-google-fonts.js.map +1 -0
  34. package/dist/fonts/google-font.cjs +18 -8
  35. package/dist/fonts/google-font.cjs.map +1 -1
  36. package/dist/fonts/google-font.d.cts +11 -3
  37. package/dist/fonts/google-font.d.ts +11 -3
  38. package/dist/fonts/google-font.js +18 -8
  39. package/dist/fonts/google-font.js.map +1 -1
  40. package/dist/fonts/index.d.cts +2 -2
  41. package/dist/fonts/resolve-font-config.cjs +4 -1
  42. package/dist/fonts/resolve-font-config.cjs.map +1 -1
  43. package/dist/fonts/resolve-font-config.js +4 -1
  44. package/dist/fonts/resolve-font-config.js.map +1 -1
  45. package/dist/fonts/resolve-font-stylesheets.cjs +7 -1
  46. package/dist/fonts/resolve-font-stylesheets.cjs.map +1 -1
  47. package/dist/fonts/resolve-font-stylesheets.js +7 -1
  48. package/dist/fonts/resolve-font-stylesheets.js.map +1 -1
  49. package/dist/fonts/types.d.cts +12 -1
  50. package/dist/fonts/types.d.ts +12 -1
  51. package/dist/global.css +24 -24
  52. package/dist/index.d.cts +2 -2
  53. package/dist/index.d.ts +2 -2
  54. package/dist/theme-scopes.css +3 -3
  55. package/dist/theme.cjs +26 -4
  56. package/dist/theme.cjs.map +1 -1
  57. package/dist/theme.css +16 -16
  58. package/dist/theme.d.cts +27 -3
  59. package/dist/theme.d.ts +27 -3
  60. package/dist/theme.js +26 -4
  61. package/dist/theme.js.map +1 -1
  62. package/package.json +11 -4
@@ -1 +1 @@
1
- {"version":3,"file":"create-google-font-url.cjs","names":["resolveFontConfig"],"sources":["../../src/fonts/create-google-font-url.ts"],"sourcesContent":["import { resolveFontConfig } from \"./resolve-font-config\";\nimport type { CreateGoogleFontUrlOptions } from \"./types\";\n\nconst GOOGLE_FONTS_CSS2_URL = \"https://fonts.googleapis.com/css2\";\n\nexport const createGoogleFontUrl = (options: CreateGoogleFontUrlOptions) => {\n const font = resolveFontConfig(options);\n const hasItalic = font.styles.includes(\"italic\");\n const tuples = font.styles.flatMap((style) =>\n font.weights.map((weight) => ({\n italic: style === \"italic\" ? 1 : 0,\n weight,\n })),\n );\n\n const familySpec = hasItalic\n ? [\n font.family,\n \":ital,wght@\",\n tuples.map(({ italic, weight }) => `${italic},${weight}`).join(\";\"),\n ].join(\"\")\n : [font.family, \":wght@\", font.weights.join(\";\")].join(\"\");\n\n const params = new URLSearchParams();\n params.set(\"family\", familySpec);\n params.set(\"display\", font.display);\n\n return `${GOOGLE_FONTS_CSS2_URL}?${params.toString()}`;\n};\n"],"mappings":";;AAGA,MAAM,wBAAwB;AAE9B,MAAa,uBAAuB,YAAwC;CAC1E,MAAM,OAAOA,4BAAAA,kBAAkB,OAAO;CACtC,MAAM,YAAY,KAAK,OAAO,SAAS,QAAQ;CAC/C,MAAM,SAAS,KAAK,OAAO,SAAS,UAClC,KAAK,QAAQ,KAAK,YAAY;EAC5B,QAAQ,UAAU,WAAW,IAAI;EACjC;CACF,EAAE,CACJ;CAEA,MAAM,aAAa,YACf;EACE,KAAK;EACL;EACA,OAAO,KAAK,EAAE,QAAQ,aAAa,GAAG,OAAO,GAAG,QAAQ,CAAC,CAAC,KAAK,GAAG;CACpE,CAAC,CAAC,KAAK,EAAE,IACT;EAAC,KAAK;EAAQ;EAAU,KAAK,QAAQ,KAAK,GAAG;CAAC,CAAC,CAAC,KAAK,EAAE;CAE3D,MAAM,SAAS,IAAI,gBAAgB;CACnC,OAAO,IAAI,UAAU,UAAU;CAC/B,OAAO,IAAI,WAAW,KAAK,OAAO;CAElC,OAAO,GAAG,sBAAsB,GAAG,OAAO,SAAS;AACrD"}
1
+ {"version":3,"file":"create-google-font-url.cjs","names":["resolveFontConfig"],"sources":["../../src/fonts/create-google-font-url.ts"],"sourcesContent":["import { resolveFontConfig } from \"./resolve-font-config\";\nimport type { CreateGoogleFontUrlOptions } from \"./types\";\n\nconst GOOGLE_FONTS_CSS2_URL = \"https://fonts.googleapis.com/css2\";\n\nexport const createGoogleFontUrl = (options: CreateGoogleFontUrlOptions) => {\n const font = resolveFontConfig(options);\n const hasItalic = font.styles.includes(\"italic\");\n const weights = font.weightRange\n ? [font.weightRange.join(\"..\")]\n : font.weights.map(String);\n const tuples = font.styles.flatMap((style) =>\n weights.map((weight) => ({\n italic: style === \"italic\" ? 1 : 0,\n weight,\n })),\n );\n\n const familySpec = hasItalic\n ? [\n font.family,\n \":ital,wght@\",\n tuples.map(({ italic, weight }) => `${italic},${weight}`).join(\";\"),\n ].join(\"\")\n : [font.family, \":wght@\", weights.join(\";\")].join(\"\");\n\n const params = new URLSearchParams();\n params.set(\"family\", familySpec);\n params.set(\"display\", font.display);\n\n return `${GOOGLE_FONTS_CSS2_URL}?${params.toString()}`;\n};\n"],"mappings":";;AAGA,MAAM,wBAAwB;AAE9B,MAAa,uBAAuB,YAAwC;CAC1E,MAAM,OAAOA,4BAAAA,kBAAkB,OAAO;CACtC,MAAM,YAAY,KAAK,OAAO,SAAS,QAAQ;CAC/C,MAAM,UAAU,KAAK,cACjB,CAAC,KAAK,YAAY,KAAK,IAAI,CAAC,IAC5B,KAAK,QAAQ,IAAI,MAAM;CAC3B,MAAM,SAAS,KAAK,OAAO,SAAS,UAClC,QAAQ,KAAK,YAAY;EACvB,QAAQ,UAAU,WAAW,IAAI;EACjC;CACF,EAAE,CACJ;CAEA,MAAM,aAAa,YACf;EACE,KAAK;EACL;EACA,OAAO,KAAK,EAAE,QAAQ,aAAa,GAAG,OAAO,GAAG,QAAQ,CAAC,CAAC,KAAK,GAAG;CACpE,CAAC,CAAC,KAAK,EAAE,IACT;EAAC,KAAK;EAAQ;EAAU,QAAQ,KAAK,GAAG;CAAC,CAAC,CAAC,KAAK,EAAE;CAEtD,MAAM,SAAS,IAAI,gBAAgB;CACnC,OAAO,IAAI,UAAU,UAAU;CAC/B,OAAO,IAAI,WAAW,KAAK,OAAO;CAElC,OAAO,GAAG,sBAAsB,GAAG,OAAO,SAAS;AACrD"}
@@ -4,7 +4,8 @@ const GOOGLE_FONTS_CSS2_URL = "https://fonts.googleapis.com/css2";
4
4
  const createGoogleFontUrl = (options) => {
5
5
  const font = resolveFontConfig(options);
6
6
  const hasItalic = font.styles.includes("italic");
7
- const tuples = font.styles.flatMap((style) => font.weights.map((weight) => ({
7
+ const weights = font.weightRange ? [font.weightRange.join("..")] : font.weights.map(String);
8
+ const tuples = font.styles.flatMap((style) => weights.map((weight) => ({
8
9
  italic: style === "italic" ? 1 : 0,
9
10
  weight
10
11
  })));
@@ -15,7 +16,7 @@ const createGoogleFontUrl = (options) => {
15
16
  ].join("") : [
16
17
  font.family,
17
18
  ":wght@",
18
- font.weights.join(";")
19
+ weights.join(";")
19
20
  ].join("");
20
21
  const params = new URLSearchParams();
21
22
  params.set("family", familySpec);
@@ -1 +1 @@
1
- {"version":3,"file":"create-google-font-url.js","names":[],"sources":["../../src/fonts/create-google-font-url.ts"],"sourcesContent":["import { resolveFontConfig } from \"./resolve-font-config\";\nimport type { CreateGoogleFontUrlOptions } from \"./types\";\n\nconst GOOGLE_FONTS_CSS2_URL = \"https://fonts.googleapis.com/css2\";\n\nexport const createGoogleFontUrl = (options: CreateGoogleFontUrlOptions) => {\n const font = resolveFontConfig(options);\n const hasItalic = font.styles.includes(\"italic\");\n const tuples = font.styles.flatMap((style) =>\n font.weights.map((weight) => ({\n italic: style === \"italic\" ? 1 : 0,\n weight,\n })),\n );\n\n const familySpec = hasItalic\n ? [\n font.family,\n \":ital,wght@\",\n tuples.map(({ italic, weight }) => `${italic},${weight}`).join(\";\"),\n ].join(\"\")\n : [font.family, \":wght@\", font.weights.join(\";\")].join(\"\");\n\n const params = new URLSearchParams();\n params.set(\"family\", familySpec);\n params.set(\"display\", font.display);\n\n return `${GOOGLE_FONTS_CSS2_URL}?${params.toString()}`;\n};\n"],"mappings":";;AAGA,MAAM,wBAAwB;AAE9B,MAAa,uBAAuB,YAAwC;CAC1E,MAAM,OAAO,kBAAkB,OAAO;CACtC,MAAM,YAAY,KAAK,OAAO,SAAS,QAAQ;CAC/C,MAAM,SAAS,KAAK,OAAO,SAAS,UAClC,KAAK,QAAQ,KAAK,YAAY;EAC5B,QAAQ,UAAU,WAAW,IAAI;EACjC;CACF,EAAE,CACJ;CAEA,MAAM,aAAa,YACf;EACE,KAAK;EACL;EACA,OAAO,KAAK,EAAE,QAAQ,aAAa,GAAG,OAAO,GAAG,QAAQ,CAAC,CAAC,KAAK,GAAG;CACpE,CAAC,CAAC,KAAK,EAAE,IACT;EAAC,KAAK;EAAQ;EAAU,KAAK,QAAQ,KAAK,GAAG;CAAC,CAAC,CAAC,KAAK,EAAE;CAE3D,MAAM,SAAS,IAAI,gBAAgB;CACnC,OAAO,IAAI,UAAU,UAAU;CAC/B,OAAO,IAAI,WAAW,KAAK,OAAO;CAElC,OAAO,GAAG,sBAAsB,GAAG,OAAO,SAAS;AACrD"}
1
+ {"version":3,"file":"create-google-font-url.js","names":[],"sources":["../../src/fonts/create-google-font-url.ts"],"sourcesContent":["import { resolveFontConfig } from \"./resolve-font-config\";\nimport type { CreateGoogleFontUrlOptions } from \"./types\";\n\nconst GOOGLE_FONTS_CSS2_URL = \"https://fonts.googleapis.com/css2\";\n\nexport const createGoogleFontUrl = (options: CreateGoogleFontUrlOptions) => {\n const font = resolveFontConfig(options);\n const hasItalic = font.styles.includes(\"italic\");\n const weights = font.weightRange\n ? [font.weightRange.join(\"..\")]\n : font.weights.map(String);\n const tuples = font.styles.flatMap((style) =>\n weights.map((weight) => ({\n italic: style === \"italic\" ? 1 : 0,\n weight,\n })),\n );\n\n const familySpec = hasItalic\n ? [\n font.family,\n \":ital,wght@\",\n tuples.map(({ italic, weight }) => `${italic},${weight}`).join(\";\"),\n ].join(\"\")\n : [font.family, \":wght@\", weights.join(\";\")].join(\"\");\n\n const params = new URLSearchParams();\n params.set(\"family\", familySpec);\n params.set(\"display\", font.display);\n\n return `${GOOGLE_FONTS_CSS2_URL}?${params.toString()}`;\n};\n"],"mappings":";;AAGA,MAAM,wBAAwB;AAE9B,MAAa,uBAAuB,YAAwC;CAC1E,MAAM,OAAO,kBAAkB,OAAO;CACtC,MAAM,YAAY,KAAK,OAAO,SAAS,QAAQ;CAC/C,MAAM,UAAU,KAAK,cACjB,CAAC,KAAK,YAAY,KAAK,IAAI,CAAC,IAC5B,KAAK,QAAQ,IAAI,MAAM;CAC3B,MAAM,SAAS,KAAK,OAAO,SAAS,UAClC,QAAQ,KAAK,YAAY;EACvB,QAAQ,UAAU,WAAW,IAAI;EACjC;CACF,EAAE,CACJ;CAEA,MAAM,aAAa,YACf;EACE,KAAK;EACL;EACA,OAAO,KAAK,EAAE,QAAQ,aAAa,GAAG,OAAO,GAAG,QAAQ,CAAC,CAAC,KAAK,GAAG;CACpE,CAAC,CAAC,KAAK,EAAE,IACT;EAAC,KAAK;EAAQ;EAAU,QAAQ,KAAK,GAAG;CAAC,CAAC,CAAC,KAAK,EAAE;CAEtD,MAAM,SAAS,IAAI,gBAAgB;CACnC,OAAO,IAAI,UAAU,UAAU;CAC/B,OAAO,IAAI,WAAW,KAAK,OAAO;CAElC,OAAO,GAAG,sBAAsB,GAAG,OAAO,SAAS;AACrD"}
@@ -0,0 +1,10 @@
1
+ // src/fonts/create-local-font-stylesheet-url.ts
2
+ const LOCAL_FONT_STYLESHEET_NAME = "puzzle-fonts.css";
3
+ const createLocalFontStylesheetUrl = (basePath = "/fonts") => {
4
+ if (typeof basePath !== "string" || !basePath.startsWith("/") || basePath.startsWith("//") || /[\u0000-\u0020\u007f?#\\]/.test(basePath)) throw new TypeError("fontBasePath must be a root-relative URL path, such as \"/fonts\".");
5
+ if (basePath.split("/").filter(Boolean).some((segment) => segment === "." || segment === "..")) throw new TypeError("fontBasePath must not contain relative path segments.");
6
+ return `${basePath.replace(/\/+$/, "")}/${LOCAL_FONT_STYLESHEET_NAME}`;
7
+ };
8
+ exports.createLocalFontStylesheetUrl = createLocalFontStylesheetUrl;
9
+
10
+ //# sourceMappingURL=create-local-font-stylesheet-url.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create-local-font-stylesheet-url.cjs","names":[],"sources":["../../src/fonts/create-local-font-stylesheet-url.ts"],"sourcesContent":["const LOCAL_FONT_STYLESHEET_NAME = \"puzzle-fonts.css\";\n\nexport const createLocalFontStylesheetUrl = (basePath = \"/fonts\"): string => {\n if (\n typeof basePath !== \"string\" ||\n !basePath.startsWith(\"/\") ||\n basePath.startsWith(\"//\") ||\n /[\\u0000-\\u0020\\u007f?#\\\\]/.test(basePath)\n ) {\n throw new TypeError(\n 'fontBasePath must be a root-relative URL path, such as \"/fonts\".',\n );\n }\n\n const segments = basePath.split(\"/\").filter(Boolean);\n\n if (segments.some((segment) => segment === \".\" || segment === \"..\")) {\n throw new TypeError(\n \"fontBasePath must not contain relative path segments.\",\n );\n }\n\n const normalizedBasePath = basePath.replace(/\\/+$/, \"\");\n\n return `${normalizedBasePath}/${LOCAL_FONT_STYLESHEET_NAME}`;\n};\n"],"mappings":";AAAA,MAAM,6BAA6B;AAEnC,MAAa,gCAAgC,WAAW,aAAqB;CAC3E,IACE,OAAO,aAAa,YACpB,CAAC,SAAS,WAAW,GAAG,KACxB,SAAS,WAAW,IAAI,KACxB,4BAA4B,KAAK,QAAQ,GAEzC,MAAM,IAAI,UACR,oEACF;CAKF,IAFiB,SAAS,MAAM,GAAG,CAAC,CAAC,OAAO,OAEjC,CAAC,CAAC,MAAM,YAAY,YAAY,OAAO,YAAY,IAAI,GAChE,MAAM,IAAI,UACR,uDACF;CAKF,OAAO,GAFoB,SAAS,QAAQ,QAAQ,EAEzB,EAAE,GAAG;AAClC"}
@@ -0,0 +1,10 @@
1
+ // src/fonts/create-local-font-stylesheet-url.ts
2
+ const LOCAL_FONT_STYLESHEET_NAME = "puzzle-fonts.css";
3
+ const createLocalFontStylesheetUrl = (basePath = "/fonts") => {
4
+ if (typeof basePath !== "string" || !basePath.startsWith("/") || basePath.startsWith("//") || /[\u0000-\u0020\u007f?#\\]/.test(basePath)) throw new TypeError("fontBasePath must be a root-relative URL path, such as \"/fonts\".");
5
+ if (basePath.split("/").filter(Boolean).some((segment) => segment === "." || segment === "..")) throw new TypeError("fontBasePath must not contain relative path segments.");
6
+ return `${basePath.replace(/\/+$/, "")}/${LOCAL_FONT_STYLESHEET_NAME}`;
7
+ };
8
+ export { createLocalFontStylesheetUrl };
9
+
10
+ //# sourceMappingURL=create-local-font-stylesheet-url.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create-local-font-stylesheet-url.js","names":[],"sources":["../../src/fonts/create-local-font-stylesheet-url.ts"],"sourcesContent":["const LOCAL_FONT_STYLESHEET_NAME = \"puzzle-fonts.css\";\n\nexport const createLocalFontStylesheetUrl = (basePath = \"/fonts\"): string => {\n if (\n typeof basePath !== \"string\" ||\n !basePath.startsWith(\"/\") ||\n basePath.startsWith(\"//\") ||\n /[\\u0000-\\u0020\\u007f?#\\\\]/.test(basePath)\n ) {\n throw new TypeError(\n 'fontBasePath must be a root-relative URL path, such as \"/fonts\".',\n );\n }\n\n const segments = basePath.split(\"/\").filter(Boolean);\n\n if (segments.some((segment) => segment === \".\" || segment === \"..\")) {\n throw new TypeError(\n \"fontBasePath must not contain relative path segments.\",\n );\n }\n\n const normalizedBasePath = basePath.replace(/\\/+$/, \"\");\n\n return `${normalizedBasePath}/${LOCAL_FONT_STYLESHEET_NAME}`;\n};\n"],"mappings":";AAAA,MAAM,6BAA6B;AAEnC,MAAa,gCAAgC,WAAW,aAAqB;CAC3E,IACE,OAAO,aAAa,YACpB,CAAC,SAAS,WAAW,GAAG,KACxB,SAAS,WAAW,IAAI,KACxB,4BAA4B,KAAK,QAAQ,GAEzC,MAAM,IAAI,UACR,oEACF;CAKF,IAFiB,SAAS,MAAM,GAAG,CAAC,CAAC,OAAO,OAEjC,CAAC,CAAC,MAAM,YAAY,YAAY,OAAO,YAAY,IAAI,GAChE,MAAM,IAAI,UACR,uDACF;CAKF,OAAO,GAFoB,SAAS,QAAQ,QAAQ,EAEzB,EAAE,GAAG;AAClC"}
@@ -0,0 +1,138 @@
1
+ const require_resolve_font_stylesheets = require("./resolve-font-stylesheets.cjs");
2
+ const require_create_font_preload_filename = require("./create-font-preload-filename.cjs");
3
+ let node_fs_promises = require("node:fs/promises");
4
+ let node_path = require("node:path");
5
+ let node_crypto = require("node:crypto");
6
+ // src/fonts/download-google-fonts.ts
7
+ const GOOGLE_FONTS_CSS_HEADERS = {
8
+ accept: "text/css,*/*;q=0.1",
9
+ "user-agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36"
10
+ };
11
+ const FONT_FACE_REGEX = /(?:\/\*\s*([^*]+?)\s*\*\/\s*)?@font-face\s*\{([^}]*)\}/gi;
12
+ const FONT_URL_REGEX = /url\(\s*(?:"([^"]+)"|'([^']+)'|([^)"'\s]+))\s*\)/i;
13
+ const FONT_FORMAT_REGEX = /format\(\s*["']?([^"')]+)["']?\s*\)/i;
14
+ const FONT_STYLE_REGEX = /font-style\s*:\s*([^;]+)/i;
15
+ const GENERATED_CSS_FILENAME = "puzzle-fonts.css";
16
+ const createHashValue = (value) => {
17
+ return (0, node_crypto.createHash)("sha256").update(value).digest("hex");
18
+ };
19
+ const parseFontFaces = (css, family) => {
20
+ const matches = [...css.matchAll(FONT_FACE_REGEX)];
21
+ if (matches.length === 0) throw new Error(`Google Fonts returned no @font-face declarations for "${family}".`);
22
+ return matches.map((match) => {
23
+ const body = match[2];
24
+ if (body === void 0) throw new Error(`Could not parse a font-face declaration for "${family}".`);
25
+ const urlMatch = body.match(FONT_URL_REGEX);
26
+ const fontUrl = urlMatch?.[1] ?? urlMatch?.[2] ?? urlMatch?.[3];
27
+ if (!fontUrl || !urlMatch) throw new Error(`No font URL found in a font-face declaration for "${family}".`);
28
+ let parsedUrl;
29
+ try {
30
+ parsedUrl = new URL(fontUrl);
31
+ } catch {
32
+ throw new Error(`Google Fonts returned an invalid font URL for "${family}".`);
33
+ }
34
+ if (parsedUrl.protocol !== "https:" || parsedUrl.hostname !== "fonts.gstatic.com" || !parsedUrl.pathname.toLowerCase().endsWith(".woff2")) throw new Error(`Google Fonts returned an unsupported font URL: ${fontUrl}`);
35
+ const format = body.match(FONT_FORMAT_REGEX)?.[1];
36
+ if (format && format.toLowerCase() !== "woff2") throw new Error(`Expected a WOFF2 font for "${family}", received "${format}".`);
37
+ return {
38
+ body,
39
+ url: fontUrl,
40
+ originalUrl: urlMatch[0],
41
+ subset: match[1]?.trim(),
42
+ style: body.match(FONT_STYLE_REGEX)?.[1]?.trim() ?? "normal"
43
+ };
44
+ });
45
+ };
46
+ const hasCurrentGeneratedFonts = async (cssPath, outputDir, configHash) => {
47
+ try {
48
+ const css = await (0, node_fs_promises.readFile)(cssPath, "utf8");
49
+ if (!css.includes(`puzzle-ui-fonts-sha256:${configHash}`)) return false;
50
+ const referencedFiles = [...css.matchAll(/url\(\s*["']?\.\/([^)"']+)["']?\s*\)/g)].map((match) => match[1]).filter((name) => name !== void 0);
51
+ if (referencedFiles.length === 0) return false;
52
+ await Promise.all(referencedFiles.map((filename) => (0, node_fs_promises.access)((0, node_path.join)(outputDir, filename))));
53
+ return true;
54
+ } catch {
55
+ return false;
56
+ }
57
+ };
58
+ const downloadGoogleFonts = async ({ fonts, outputDir, force = false, subsets, fetcher = fetch }) => {
59
+ if (subsets !== void 0 && (subsets.length === 0 || subsets.some((subset) => !/^[a-z]+(?:-[a-z]+)*$/.test(subset)))) throw new TypeError("subsets must be a non-empty list of Google Fonts subset names.");
60
+ const stylesheets = require_resolve_font_stylesheets.resolveFontStylesheets(fonts);
61
+ if (stylesheets.length === 0) throw new Error("No fonts configured. Add at least one font to puzzle-fonts.json.");
62
+ await (0, node_fs_promises.mkdir)(outputDir, { recursive: true });
63
+ const cssPath = (0, node_path.join)(outputDir, GENERATED_CSS_FILENAME);
64
+ const configHash = createHashValue(JSON.stringify([
65
+ "preload-v1",
66
+ subsets ? [...new Set(subsets)].sort() : null,
67
+ stylesheets.map((stylesheet) => stylesheet.href).sort()
68
+ ]));
69
+ if (!force && await hasCurrentGeneratedFonts(cssPath, outputDir, configHash)) return {
70
+ cssPath,
71
+ fontFiles: 0,
72
+ skipped: true
73
+ };
74
+ const fontFiles = /* @__PURE__ */ new Map();
75
+ const preloadFiles = /* @__PURE__ */ new Map();
76
+ const generatedFaces = [];
77
+ const downloadFont = async (url, family) => {
78
+ const existing = fontFiles.get(url);
79
+ if (existing) return existing;
80
+ const filename = `font-${createHashValue(url).slice(0, 16)}.woff2`;
81
+ const response = await fetcher(url);
82
+ if (!response.ok) throw new Error(`Could not download font "${family}": HTTP ${response.status}.`);
83
+ const buffer = new Uint8Array(await response.arrayBuffer());
84
+ if (buffer.byteLength === 0) throw new Error(`Google Fonts returned an empty font file for "${family}".`);
85
+ await (0, node_fs_promises.writeFile)((0, node_path.join)(outputDir, filename), buffer);
86
+ fontFiles.set(url, filename);
87
+ return filename;
88
+ };
89
+ for (const stylesheet of stylesheets) {
90
+ const response = await fetcher(stylesheet.href, { headers: GOOGLE_FONTS_CSS_HEADERS });
91
+ if (!response.ok) {
92
+ const details = (await response.text()).slice(0, 500);
93
+ throw new Error([
94
+ `Google Fonts request failed for "${stylesheet.family}".`,
95
+ `HTTP ${response.status}.`,
96
+ details
97
+ ].filter(Boolean).join(" "));
98
+ }
99
+ const css = await response.text();
100
+ const faces = parseFontFaces(css, stylesheet.family);
101
+ if (subsets) {
102
+ for (const subset of subsets) if (!faces.some((face) => face.subset === subset)) throw new Error(`Google Fonts returned no "${subset}" subset for "${stylesheet.family}".`);
103
+ }
104
+ const latinNormalFace = faces.find((face) => face.subset === "latin" && face.style.toLowerCase() === "normal");
105
+ const preloadFilename = latinNormalFace && (!subsets || subsets.includes("latin")) ? require_create_font_preload_filename.createFontPreloadFilename(stylesheet.family) : null;
106
+ if (latinNormalFace && preloadFilename) {
107
+ const originalFilename = await downloadFont(latinNormalFace.url, stylesheet.family);
108
+ if (!preloadFiles.get(stylesheet.family.toLowerCase())) {
109
+ const originalBytes = await (0, node_fs_promises.readFile)((0, node_path.join)(outputDir, originalFilename));
110
+ await (0, node_fs_promises.writeFile)((0, node_path.join)(outputDir, preloadFilename), originalBytes);
111
+ preloadFiles.set(stylesheet.family.toLowerCase(), preloadFilename);
112
+ }
113
+ }
114
+ for (const face of faces) {
115
+ if (subsets && (!face.subset || !subsets.includes(face.subset))) continue;
116
+ const filename = await downloadFont(face.url, stylesheet.family);
117
+ const targetFilename = face === latinNormalFace && preloadFilename !== null ? preloadFilename : filename;
118
+ const rewrittenBody = face.body.replace(face.originalUrl, `url("./${targetFilename}")`);
119
+ generatedFaces.push(`@font-face {\n${rewrittenBody.trim()}\n}`);
120
+ }
121
+ }
122
+ const generatedCss = [
123
+ "/* Generated by @puzzle-ui/theme. Do not edit. */",
124
+ `/* puzzle-ui-fonts-sha256:${configHash} */`,
125
+ "",
126
+ generatedFaces.join("\n\n"),
127
+ ""
128
+ ].join("\n");
129
+ await (0, node_fs_promises.writeFile)(cssPath, generatedCss, "utf8");
130
+ return {
131
+ cssPath,
132
+ fontFiles: fontFiles.size,
133
+ skipped: false
134
+ };
135
+ };
136
+ exports.downloadGoogleFonts = downloadGoogleFonts;
137
+
138
+ //# sourceMappingURL=download-google-fonts.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"download-google-fonts.cjs","names":["createHash","readFile","access","join","resolveFontStylesheets","mkdir","writeFile","createFontPreloadFilename"],"sources":["../../src/fonts/download-google-fonts.ts"],"sourcesContent":["import { createHash } from \"node:crypto\";\nimport { access, mkdir, readFile, writeFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\n\nimport { resolveFontStylesheets } from \"./resolve-font-stylesheets\";\nimport type { FontRole, ThemeFonts } from \"./types\";\nimport { createFontPreloadFilename } from \"./create-font-preload-filename\";\n\nconst GOOGLE_FONTS_USER_AGENT =\n \"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 \" +\n \"(KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36\";\n\nconst GOOGLE_FONTS_CSS_HEADERS = {\n accept: \"text/css,*/*;q=0.1\",\n \"user-agent\": GOOGLE_FONTS_USER_AGENT,\n};\n\nconst FONT_FACE_REGEX =\n /(?:\\/\\*\\s*([^*]+?)\\s*\\*\\/\\s*)?@font-face\\s*\\{([^}]*)\\}/gi;\n\nconst FONT_URL_REGEX = /url\\(\\s*(?:\"([^\"]+)\"|'([^']+)'|([^)\"'\\s]+))\\s*\\)/i;\n\nconst FONT_FORMAT_REGEX = /format\\(\\s*[\"']?([^\"')]+)[\"']?\\s*\\)/i;\n\nconst FONT_STYLE_REGEX = /font-style\\s*:\\s*([^;]+)/i;\n\nconst GENERATED_CSS_FILENAME = \"puzzle-fonts.css\";\n\nconst FONT_ROLES: readonly FontRole[] = [\"heading\", \"body\", \"accent\"];\n\nconst createHashValue = (value: string): string => {\n return createHash(\"sha256\").update(value).digest(\"hex\");\n};\n\ntype ParsedFontFace = {\n body: string;\n url: string;\n originalUrl: string;\n subset?: string;\n style: string;\n};\n\ntype DownloadGoogleFontsOptions = {\n fonts: ThemeFonts;\n outputDir: string;\n force?: boolean;\n subsets?: readonly string[];\n fetcher?: (input: string, init?: RequestInit) => Promise<Response>;\n};\n\nexport type DownloadGoogleFontsResult = {\n cssPath: string;\n fontFiles: number;\n skipped: boolean;\n};\n\nconst parseFontFaces = (css: string, family: string): ParsedFontFace[] => {\n const matches = [...css.matchAll(FONT_FACE_REGEX)];\n\n if (matches.length === 0) {\n throw new Error(\n `Google Fonts returned no @font-face declarations for \"${family}\".`,\n );\n }\n\n return matches.map((match) => {\n const body = match[2];\n\n if (body === undefined) {\n throw new Error(\n `Could not parse a font-face declaration for \"${family}\".`,\n );\n }\n\n const urlMatch = body.match(FONT_URL_REGEX);\n\n const fontUrl = urlMatch?.[1] ?? urlMatch?.[2] ?? urlMatch?.[3];\n\n if (!fontUrl || !urlMatch) {\n throw new Error(\n `No font URL found in a font-face declaration for \"${family}\".`,\n );\n }\n\n let parsedUrl: URL;\n\n try {\n parsedUrl = new URL(fontUrl);\n } catch {\n throw new Error(\n `Google Fonts returned an invalid font URL for \"${family}\".`,\n );\n }\n\n if (\n parsedUrl.protocol !== \"https:\" ||\n parsedUrl.hostname !== \"fonts.gstatic.com\" ||\n !parsedUrl.pathname.toLowerCase().endsWith(\".woff2\")\n ) {\n throw new Error(\n `Google Fonts returned an unsupported font URL: ${fontUrl}`,\n );\n }\n\n const format = body.match(FONT_FORMAT_REGEX)?.[1];\n\n if (format && format.toLowerCase() !== \"woff2\") {\n throw new Error(\n `Expected a WOFF2 font for \"${family}\", received \"${format}\".`,\n );\n }\n\n return {\n body,\n url: fontUrl,\n originalUrl: urlMatch[0],\n subset: match[1]?.trim(),\n style: body.match(FONT_STYLE_REGEX)?.[1]?.trim() ?? \"normal\",\n };\n });\n};\n\nconst hasCurrentGeneratedFonts = async (\n cssPath: string,\n outputDir: string,\n configHash: string,\n): Promise<boolean> => {\n try {\n const css = await readFile(cssPath, \"utf8\");\n\n if (!css.includes(`puzzle-ui-fonts-sha256:${configHash}`)) {\n return false;\n }\n\n const referencedFiles = [\n ...css.matchAll(/url\\(\\s*[\"']?\\.\\/([^)\"']+)[\"']?\\s*\\)/g),\n ]\n .map((match) => match[1])\n .filter((name): name is string => name !== undefined);\n\n if (referencedFiles.length === 0) {\n return false;\n }\n\n await Promise.all(\n referencedFiles.map((filename) => access(join(outputDir, filename))),\n );\n\n return true;\n } catch {\n return false;\n }\n};\n\nexport const downloadGoogleFonts = async ({\n fonts,\n outputDir,\n force = false,\n subsets,\n fetcher = fetch,\n}: DownloadGoogleFontsOptions): Promise<DownloadGoogleFontsResult> => {\n if (\n subsets !== undefined &&\n (subsets.length === 0 ||\n subsets.some((subset) => !/^[a-z]+(?:-[a-z]+)*$/.test(subset)))\n ) {\n throw new TypeError(\n \"subsets must be a non-empty list of Google Fonts subset names.\",\n );\n }\n\n const stylesheets = resolveFontStylesheets(fonts);\n\n if (stylesheets.length === 0) {\n throw new Error(\n \"No fonts configured. Add at least one font to puzzle-fonts.json.\",\n );\n }\n\n await mkdir(outputDir, { recursive: true });\n\n const cssPath = join(outputDir, GENERATED_CSS_FILENAME);\n\n const configHash = createHashValue(\n JSON.stringify([\n \"preload-v1\",\n subsets ? [...new Set(subsets)].sort() : null,\n stylesheets.map((stylesheet) => stylesheet.href).sort(),\n ]),\n );\n\n if (\n !force &&\n (await hasCurrentGeneratedFonts(cssPath, outputDir, configHash))\n ) {\n return {\n cssPath,\n fontFiles: 0,\n skipped: true,\n };\n }\n\n const fontFiles = new Map<string, string>();\n const preloadFiles = new Map<string, string>();\n const generatedFaces: string[] = [];\n\n const downloadFont = async (url: string, family: string): Promise<string> => {\n const existing = fontFiles.get(url);\n\n if (existing) {\n return existing;\n }\n\n const filename = `font-${createHashValue(url).slice(0, 16)}.woff2`;\n\n const response = await fetcher(url);\n\n if (!response.ok) {\n throw new Error(\n `Could not download font \"${family}\": HTTP ${response.status}.`,\n );\n }\n\n const buffer = new Uint8Array(await response.arrayBuffer());\n\n if (buffer.byteLength === 0) {\n throw new Error(\n `Google Fonts returned an empty font file for \"${family}\".`,\n );\n }\n\n await writeFile(join(outputDir, filename), buffer);\n\n fontFiles.set(url, filename);\n\n return filename;\n };\n\n for (const stylesheet of stylesheets) {\n const response = await fetcher(stylesheet.href, {\n headers: GOOGLE_FONTS_CSS_HEADERS,\n });\n\n if (!response.ok) {\n const details = (await response.text()).slice(0, 500);\n\n throw new Error(\n [\n `Google Fonts request failed for \"${stylesheet.family}\".`,\n `HTTP ${response.status}.`,\n details,\n ]\n .filter(Boolean)\n .join(\" \"),\n );\n }\n\n const css = await response.text();\n\n const faces = parseFontFaces(css, stylesheet.family);\n\n if (subsets) {\n for (const subset of subsets) {\n if (!faces.some((face) => face.subset === subset)) {\n throw new Error(\n `Google Fonts returned no \"${subset}\" subset for \"${stylesheet.family}\".`,\n );\n }\n }\n }\n\n const latinNormalFace = faces.find(\n (face) =>\n face.subset === \"latin\" && face.style.toLowerCase() === \"normal\",\n );\n\n const preloadFilename =\n latinNormalFace && (!subsets || subsets.includes(\"latin\"))\n ? createFontPreloadFilename(stylesheet.family)\n : null;\n\n if (latinNormalFace && preloadFilename) {\n const originalFilename = await downloadFont(\n latinNormalFace.url,\n stylesheet.family,\n );\n\n const existingPreload = preloadFiles.get(stylesheet.family.toLowerCase());\n\n if (!existingPreload) {\n const originalBytes = await readFile(join(outputDir, originalFilename));\n\n await writeFile(join(outputDir, preloadFilename), originalBytes);\n\n preloadFiles.set(stylesheet.family.toLowerCase(), preloadFilename);\n }\n }\n\n for (const face of faces) {\n if (subsets && (!face.subset || !subsets.includes(face.subset))) {\n continue;\n }\n\n const filename = await downloadFont(face.url, stylesheet.family);\n\n const isPreloadFace =\n face === latinNormalFace && preloadFilename !== null;\n\n const targetFilename = isPreloadFace ? preloadFilename : filename;\n\n const rewrittenBody = face.body.replace(\n face.originalUrl,\n `url(\"./${targetFilename}\")`,\n );\n\n generatedFaces.push(`@font-face {\\n${rewrittenBody.trim()}\\n}`);\n }\n }\n\n const generatedCss = [\n \"/* Generated by @puzzle-ui/theme. Do not edit. */\",\n `/* puzzle-ui-fonts-sha256:${configHash} */`,\n \"\",\n generatedFaces.join(\"\\n\\n\"),\n \"\",\n ].join(\"\\n\");\n\n await writeFile(cssPath, generatedCss, \"utf8\");\n\n return {\n cssPath,\n fontFiles: fontFiles.size,\n skipped: false,\n };\n};\n"],"mappings":";;;;;;AAYA,MAAM,2BAA2B;CAC/B,QAAQ;CACR,cAAc;AAChB;AAEA,MAAM,kBACJ;AAEF,MAAM,iBAAiB;AAEvB,MAAM,oBAAoB;AAE1B,MAAM,mBAAmB;AAEzB,MAAM,yBAAyB;AAI/B,MAAM,mBAAmB,UAA0B;CACjD,QAAA,GAAOA,YAAAA,WAAAA,CAAW,QAAQ,CAAC,CAAC,OAAO,KAAK,CAAC,CAAC,OAAO,KAAK;AACxD;AAwBA,MAAM,kBAAkB,KAAa,WAAqC;CACxE,MAAM,UAAU,CAAC,GAAG,IAAI,SAAS,eAAe,CAAC;CAEjD,IAAI,QAAQ,WAAW,GACrB,MAAM,IAAI,MACR,yDAAyD,OAAO,GAClE;CAGF,OAAO,QAAQ,KAAK,UAAU;EAC5B,MAAM,OAAO,MAAM;EAEnB,IAAI,SAAS,KAAA,GACX,MAAM,IAAI,MACR,gDAAgD,OAAO,GACzD;EAGF,MAAM,WAAW,KAAK,MAAM,cAAc;EAE1C,MAAM,UAAU,WAAW,MAAM,WAAW,MAAM,WAAW;EAE7D,IAAI,CAAC,WAAW,CAAC,UACf,MAAM,IAAI,MACR,qDAAqD,OAAO,GAC9D;EAGF,IAAI;EAEJ,IAAI;GACF,YAAY,IAAI,IAAI,OAAO;EAC7B,QAAQ;GACN,MAAM,IAAI,MACR,kDAAkD,OAAO,GAC3D;EACF;EAEA,IACE,UAAU,aAAa,YACvB,UAAU,aAAa,uBACvB,CAAC,UAAU,SAAS,YAAY,CAAC,CAAC,SAAS,QAAQ,GAEnD,MAAM,IAAI,MACR,kDAAkD,SACpD;EAGF,MAAM,SAAS,KAAK,MAAM,iBAAiB,CAAC,GAAG;EAE/C,IAAI,UAAU,OAAO,YAAY,MAAM,SACrC,MAAM,IAAI,MACR,8BAA8B,OAAO,eAAe,OAAO,GAC7D;EAGF,OAAO;GACL;GACA,KAAK;GACL,aAAa,SAAS;GACtB,QAAQ,MAAM,EAAE,EAAE,KAAK;GACvB,OAAO,KAAK,MAAM,gBAAgB,CAAC,GAAG,EAAE,EAAE,KAAK,KAAK;EACtD;CACF,CAAC;AACH;AAEA,MAAM,2BAA2B,OAC/B,SACA,WACA,eACqB;CACrB,IAAI;EACF,MAAM,MAAM,OAAA,GAAMC,iBAAAA,SAAAA,CAAS,SAAS,MAAM;EAE1C,IAAI,CAAC,IAAI,SAAS,0BAA0B,YAAY,GACtD,OAAO;EAGT,MAAM,kBAAkB,CACtB,GAAG,IAAI,SAAS,uCAAuC,CACzD,CAAC,CACE,KAAK,UAAU,MAAM,EAAE,CAAC,CACxB,QAAQ,SAAyB,SAAS,KAAA,CAAS;EAEtD,IAAI,gBAAgB,WAAW,GAC7B,OAAO;EAGT,MAAM,QAAQ,IACZ,gBAAgB,KAAK,cAAA,GAAaC,iBAAAA,OAAAA,EAAAA,GAAOC,UAAAA,KAAAA,CAAK,WAAW,QAAQ,CAAC,CAAC,CACrE;EAEA,OAAO;CACT,QAAQ;EACN,OAAO;CACT;AACF;AAEA,MAAa,sBAAsB,OAAO,EACxC,OACA,WACA,QAAQ,OACR,SACA,UAAU,YAC0D;CACpE,IACE,YAAY,KAAA,MACX,QAAQ,WAAW,KAClB,QAAQ,MAAM,WAAW,CAAC,uBAAuB,KAAK,MAAM,CAAC,IAE/D,MAAM,IAAI,UACR,gEACF;CAGF,MAAM,cAAcC,iCAAAA,uBAAuB,KAAK;CAEhD,IAAI,YAAY,WAAW,GACzB,MAAM,IAAI,MACR,kEACF;CAGF,OAAA,GAAMC,iBAAAA,MAAAA,CAAM,WAAW,EAAE,WAAW,KAAK,CAAC;CAE1C,MAAM,WAAA,GAAUF,UAAAA,KAAAA,CAAK,WAAW,sBAAsB;CAEtD,MAAM,aAAa,gBACjB,KAAK,UAAU;EACb;EACA,UAAU,CAAC,GAAG,IAAI,IAAI,OAAO,CAAC,CAAC,CAAC,KAAK,IAAI;EACzC,YAAY,KAAK,eAAe,WAAW,IAAI,CAAC,CAAC,KAAK;CACxD,CAAC,CACH;CAEA,IACE,CAAC,SACA,MAAM,yBAAyB,SAAS,WAAW,UAAU,GAE9D,OAAO;EACL;EACA,WAAW;EACX,SAAS;CACX;CAGF,MAAM,4BAAY,IAAI,IAAoB;CAC1C,MAAM,+BAAe,IAAI,IAAoB;CAC7C,MAAM,iBAA2B,CAAC;CAElC,MAAM,eAAe,OAAO,KAAa,WAAoC;EAC3E,MAAM,WAAW,UAAU,IAAI,GAAG;EAElC,IAAI,UACF,OAAO;EAGT,MAAM,WAAW,QAAQ,gBAAgB,GAAG,CAAC,CAAC,MAAM,GAAG,EAAE,EAAE;EAE3D,MAAM,WAAW,MAAM,QAAQ,GAAG;EAElC,IAAI,CAAC,SAAS,IACZ,MAAM,IAAI,MACR,4BAA4B,OAAO,UAAU,SAAS,OAAO,EAC/D;EAGF,MAAM,SAAS,IAAI,WAAW,MAAM,SAAS,YAAY,CAAC;EAE1D,IAAI,OAAO,eAAe,GACxB,MAAM,IAAI,MACR,iDAAiD,OAAO,GAC1D;EAGF,OAAA,GAAMG,iBAAAA,UAAAA,EAAAA,GAAUH,UAAAA,KAAAA,CAAK,WAAW,QAAQ,GAAG,MAAM;EAEjD,UAAU,IAAI,KAAK,QAAQ;EAE3B,OAAO;CACT;CAEA,KAAK,MAAM,cAAc,aAAa;EACpC,MAAM,WAAW,MAAM,QAAQ,WAAW,MAAM,EAC9C,SAAS,yBACX,CAAC;EAED,IAAI,CAAC,SAAS,IAAI;GAChB,MAAM,WAAW,MAAM,SAAS,KAAK,EAAA,CAAG,MAAM,GAAG,GAAG;GAEpD,MAAM,IAAI,MACR;IACE,oCAAoC,WAAW,OAAO;IACtD,QAAQ,SAAS,OAAO;IACxB;GACF,CAAC,CACE,OAAO,OAAO,CAAC,CACf,KAAK,GAAG,CACb;EACF;EAEA,MAAM,MAAM,MAAM,SAAS,KAAK;EAEhC,MAAM,QAAQ,eAAe,KAAK,WAAW,MAAM;EAEnD,IAAI,SACG;QAAA,MAAM,UAAU,SACnB,IAAI,CAAC,MAAM,MAAM,SAAS,KAAK,WAAW,MAAM,GAC9C,MAAM,IAAI,MACR,6BAA6B,OAAO,gBAAgB,WAAW,OAAO,GACxE;EAAA;EAKN,MAAM,kBAAkB,MAAM,MAC3B,SACC,KAAK,WAAW,WAAW,KAAK,MAAM,YAAY,MAAM,QAC5D;EAEA,MAAM,kBACJ,oBAAoB,CAAC,WAAW,QAAQ,SAAS,OAAO,KACpDI,qCAAAA,0BAA0B,WAAW,MAAM,IAC3C;EAEN,IAAI,mBAAmB,iBAAiB;GACtC,MAAM,mBAAmB,MAAM,aAC7B,gBAAgB,KAChB,WAAW,MACb;GAIA,IAAI,CAFoB,aAAa,IAAI,WAAW,OAAO,YAAY,CAEpD,GAAG;IACpB,MAAM,gBAAgB,OAAA,GAAMN,iBAAAA,SAAAA,EAAAA,GAASE,UAAAA,KAAAA,CAAK,WAAW,gBAAgB,CAAC;IAEtE,OAAA,GAAMG,iBAAAA,UAAAA,EAAAA,GAAUH,UAAAA,KAAAA,CAAK,WAAW,eAAe,GAAG,aAAa;IAE/D,aAAa,IAAI,WAAW,OAAO,YAAY,GAAG,eAAe;GACnE;EACF;EAEA,KAAK,MAAM,QAAQ,OAAO;GACxB,IAAI,YAAY,CAAC,KAAK,UAAU,CAAC,QAAQ,SAAS,KAAK,MAAM,IAC3D;GAGF,MAAM,WAAW,MAAM,aAAa,KAAK,KAAK,WAAW,MAAM;GAK/D,MAAM,iBAFJ,SAAS,mBAAmB,oBAAoB,OAEX,kBAAkB;GAEzD,MAAM,gBAAgB,KAAK,KAAK,QAC9B,KAAK,aACL,UAAU,eAAe,GAC3B;GAEA,eAAe,KAAK,iBAAiB,cAAc,KAAK,EAAE,IAAI;EAChE;CACF;CAEA,MAAM,eAAe;EACnB;EACA,6BAA6B,WAAW;EACxC;EACA,eAAe,KAAK,MAAM;EAC1B;CACF,CAAC,CAAC,KAAK,IAAI;CAEX,OAAA,GAAMG,iBAAAA,UAAAA,CAAU,SAAS,cAAc,MAAM;CAE7C,OAAO;EACL;EACA,WAAW,UAAU;EACrB,SAAS;CACX;AACF"}
@@ -0,0 +1,138 @@
1
+ import { resolveFontStylesheets } from "./resolve-font-stylesheets.js";
2
+ import { createFontPreloadFilename } from "./create-font-preload-filename.js";
3
+ import { access, mkdir, readFile, writeFile } from "node:fs/promises";
4
+ import { join } from "node:path";
5
+ import { createHash } from "node:crypto";
6
+ // src/fonts/download-google-fonts.ts
7
+ const GOOGLE_FONTS_CSS_HEADERS = {
8
+ accept: "text/css,*/*;q=0.1",
9
+ "user-agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36"
10
+ };
11
+ const FONT_FACE_REGEX = /(?:\/\*\s*([^*]+?)\s*\*\/\s*)?@font-face\s*\{([^}]*)\}/gi;
12
+ const FONT_URL_REGEX = /url\(\s*(?:"([^"]+)"|'([^']+)'|([^)"'\s]+))\s*\)/i;
13
+ const FONT_FORMAT_REGEX = /format\(\s*["']?([^"')]+)["']?\s*\)/i;
14
+ const FONT_STYLE_REGEX = /font-style\s*:\s*([^;]+)/i;
15
+ const GENERATED_CSS_FILENAME = "puzzle-fonts.css";
16
+ const createHashValue = (value) => {
17
+ return createHash("sha256").update(value).digest("hex");
18
+ };
19
+ const parseFontFaces = (css, family) => {
20
+ const matches = [...css.matchAll(FONT_FACE_REGEX)];
21
+ if (matches.length === 0) throw new Error(`Google Fonts returned no @font-face declarations for "${family}".`);
22
+ return matches.map((match) => {
23
+ const body = match[2];
24
+ if (body === void 0) throw new Error(`Could not parse a font-face declaration for "${family}".`);
25
+ const urlMatch = body.match(FONT_URL_REGEX);
26
+ const fontUrl = urlMatch?.[1] ?? urlMatch?.[2] ?? urlMatch?.[3];
27
+ if (!fontUrl || !urlMatch) throw new Error(`No font URL found in a font-face declaration for "${family}".`);
28
+ let parsedUrl;
29
+ try {
30
+ parsedUrl = new URL(fontUrl);
31
+ } catch {
32
+ throw new Error(`Google Fonts returned an invalid font URL for "${family}".`);
33
+ }
34
+ if (parsedUrl.protocol !== "https:" || parsedUrl.hostname !== "fonts.gstatic.com" || !parsedUrl.pathname.toLowerCase().endsWith(".woff2")) throw new Error(`Google Fonts returned an unsupported font URL: ${fontUrl}`);
35
+ const format = body.match(FONT_FORMAT_REGEX)?.[1];
36
+ if (format && format.toLowerCase() !== "woff2") throw new Error(`Expected a WOFF2 font for "${family}", received "${format}".`);
37
+ return {
38
+ body,
39
+ url: fontUrl,
40
+ originalUrl: urlMatch[0],
41
+ subset: match[1]?.trim(),
42
+ style: body.match(FONT_STYLE_REGEX)?.[1]?.trim() ?? "normal"
43
+ };
44
+ });
45
+ };
46
+ const hasCurrentGeneratedFonts = async (cssPath, outputDir, configHash) => {
47
+ try {
48
+ const css = await readFile(cssPath, "utf8");
49
+ if (!css.includes(`puzzle-ui-fonts-sha256:${configHash}`)) return false;
50
+ const referencedFiles = [...css.matchAll(/url\(\s*["']?\.\/([^)"']+)["']?\s*\)/g)].map((match) => match[1]).filter((name) => name !== void 0);
51
+ if (referencedFiles.length === 0) return false;
52
+ await Promise.all(referencedFiles.map((filename) => access(join(outputDir, filename))));
53
+ return true;
54
+ } catch {
55
+ return false;
56
+ }
57
+ };
58
+ const downloadGoogleFonts = async ({ fonts, outputDir, force = false, subsets, fetcher = fetch }) => {
59
+ if (subsets !== void 0 && (subsets.length === 0 || subsets.some((subset) => !/^[a-z]+(?:-[a-z]+)*$/.test(subset)))) throw new TypeError("subsets must be a non-empty list of Google Fonts subset names.");
60
+ const stylesheets = resolveFontStylesheets(fonts);
61
+ if (stylesheets.length === 0) throw new Error("No fonts configured. Add at least one font to puzzle-fonts.json.");
62
+ await mkdir(outputDir, { recursive: true });
63
+ const cssPath = join(outputDir, GENERATED_CSS_FILENAME);
64
+ const configHash = createHashValue(JSON.stringify([
65
+ "preload-v1",
66
+ subsets ? [...new Set(subsets)].sort() : null,
67
+ stylesheets.map((stylesheet) => stylesheet.href).sort()
68
+ ]));
69
+ if (!force && await hasCurrentGeneratedFonts(cssPath, outputDir, configHash)) return {
70
+ cssPath,
71
+ fontFiles: 0,
72
+ skipped: true
73
+ };
74
+ const fontFiles = /* @__PURE__ */ new Map();
75
+ const preloadFiles = /* @__PURE__ */ new Map();
76
+ const generatedFaces = [];
77
+ const downloadFont = async (url, family) => {
78
+ const existing = fontFiles.get(url);
79
+ if (existing) return existing;
80
+ const filename = `font-${createHashValue(url).slice(0, 16)}.woff2`;
81
+ const response = await fetcher(url);
82
+ if (!response.ok) throw new Error(`Could not download font "${family}": HTTP ${response.status}.`);
83
+ const buffer = new Uint8Array(await response.arrayBuffer());
84
+ if (buffer.byteLength === 0) throw new Error(`Google Fonts returned an empty font file for "${family}".`);
85
+ await writeFile(join(outputDir, filename), buffer);
86
+ fontFiles.set(url, filename);
87
+ return filename;
88
+ };
89
+ for (const stylesheet of stylesheets) {
90
+ const response = await fetcher(stylesheet.href, { headers: GOOGLE_FONTS_CSS_HEADERS });
91
+ if (!response.ok) {
92
+ const details = (await response.text()).slice(0, 500);
93
+ throw new Error([
94
+ `Google Fonts request failed for "${stylesheet.family}".`,
95
+ `HTTP ${response.status}.`,
96
+ details
97
+ ].filter(Boolean).join(" "));
98
+ }
99
+ const css = await response.text();
100
+ const faces = parseFontFaces(css, stylesheet.family);
101
+ if (subsets) {
102
+ for (const subset of subsets) if (!faces.some((face) => face.subset === subset)) throw new Error(`Google Fonts returned no "${subset}" subset for "${stylesheet.family}".`);
103
+ }
104
+ const latinNormalFace = faces.find((face) => face.subset === "latin" && face.style.toLowerCase() === "normal");
105
+ const preloadFilename = latinNormalFace && (!subsets || subsets.includes("latin")) ? createFontPreloadFilename(stylesheet.family) : null;
106
+ if (latinNormalFace && preloadFilename) {
107
+ const originalFilename = await downloadFont(latinNormalFace.url, stylesheet.family);
108
+ if (!preloadFiles.get(stylesheet.family.toLowerCase())) {
109
+ const originalBytes = await readFile(join(outputDir, originalFilename));
110
+ await writeFile(join(outputDir, preloadFilename), originalBytes);
111
+ preloadFiles.set(stylesheet.family.toLowerCase(), preloadFilename);
112
+ }
113
+ }
114
+ for (const face of faces) {
115
+ if (subsets && (!face.subset || !subsets.includes(face.subset))) continue;
116
+ const filename = await downloadFont(face.url, stylesheet.family);
117
+ const targetFilename = face === latinNormalFace && preloadFilename !== null ? preloadFilename : filename;
118
+ const rewrittenBody = face.body.replace(face.originalUrl, `url("./${targetFilename}")`);
119
+ generatedFaces.push(`@font-face {\n${rewrittenBody.trim()}\n}`);
120
+ }
121
+ }
122
+ const generatedCss = [
123
+ "/* Generated by @puzzle-ui/theme. Do not edit. */",
124
+ `/* puzzle-ui-fonts-sha256:${configHash} */`,
125
+ "",
126
+ generatedFaces.join("\n\n"),
127
+ ""
128
+ ].join("\n");
129
+ await writeFile(cssPath, generatedCss, "utf8");
130
+ return {
131
+ cssPath,
132
+ fontFiles: fontFiles.size,
133
+ skipped: false
134
+ };
135
+ };
136
+ export { downloadGoogleFonts };
137
+
138
+ //# sourceMappingURL=download-google-fonts.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"download-google-fonts.js","names":[],"sources":["../../src/fonts/download-google-fonts.ts"],"sourcesContent":["import { createHash } from \"node:crypto\";\nimport { access, mkdir, readFile, writeFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\n\nimport { resolveFontStylesheets } from \"./resolve-font-stylesheets\";\nimport type { FontRole, ThemeFonts } from \"./types\";\nimport { createFontPreloadFilename } from \"./create-font-preload-filename\";\n\nconst GOOGLE_FONTS_USER_AGENT =\n \"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 \" +\n \"(KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36\";\n\nconst GOOGLE_FONTS_CSS_HEADERS = {\n accept: \"text/css,*/*;q=0.1\",\n \"user-agent\": GOOGLE_FONTS_USER_AGENT,\n};\n\nconst FONT_FACE_REGEX =\n /(?:\\/\\*\\s*([^*]+?)\\s*\\*\\/\\s*)?@font-face\\s*\\{([^}]*)\\}/gi;\n\nconst FONT_URL_REGEX = /url\\(\\s*(?:\"([^\"]+)\"|'([^']+)'|([^)\"'\\s]+))\\s*\\)/i;\n\nconst FONT_FORMAT_REGEX = /format\\(\\s*[\"']?([^\"')]+)[\"']?\\s*\\)/i;\n\nconst FONT_STYLE_REGEX = /font-style\\s*:\\s*([^;]+)/i;\n\nconst GENERATED_CSS_FILENAME = \"puzzle-fonts.css\";\n\nconst FONT_ROLES: readonly FontRole[] = [\"heading\", \"body\", \"accent\"];\n\nconst createHashValue = (value: string): string => {\n return createHash(\"sha256\").update(value).digest(\"hex\");\n};\n\ntype ParsedFontFace = {\n body: string;\n url: string;\n originalUrl: string;\n subset?: string;\n style: string;\n};\n\ntype DownloadGoogleFontsOptions = {\n fonts: ThemeFonts;\n outputDir: string;\n force?: boolean;\n subsets?: readonly string[];\n fetcher?: (input: string, init?: RequestInit) => Promise<Response>;\n};\n\nexport type DownloadGoogleFontsResult = {\n cssPath: string;\n fontFiles: number;\n skipped: boolean;\n};\n\nconst parseFontFaces = (css: string, family: string): ParsedFontFace[] => {\n const matches = [...css.matchAll(FONT_FACE_REGEX)];\n\n if (matches.length === 0) {\n throw new Error(\n `Google Fonts returned no @font-face declarations for \"${family}\".`,\n );\n }\n\n return matches.map((match) => {\n const body = match[2];\n\n if (body === undefined) {\n throw new Error(\n `Could not parse a font-face declaration for \"${family}\".`,\n );\n }\n\n const urlMatch = body.match(FONT_URL_REGEX);\n\n const fontUrl = urlMatch?.[1] ?? urlMatch?.[2] ?? urlMatch?.[3];\n\n if (!fontUrl || !urlMatch) {\n throw new Error(\n `No font URL found in a font-face declaration for \"${family}\".`,\n );\n }\n\n let parsedUrl: URL;\n\n try {\n parsedUrl = new URL(fontUrl);\n } catch {\n throw new Error(\n `Google Fonts returned an invalid font URL for \"${family}\".`,\n );\n }\n\n if (\n parsedUrl.protocol !== \"https:\" ||\n parsedUrl.hostname !== \"fonts.gstatic.com\" ||\n !parsedUrl.pathname.toLowerCase().endsWith(\".woff2\")\n ) {\n throw new Error(\n `Google Fonts returned an unsupported font URL: ${fontUrl}`,\n );\n }\n\n const format = body.match(FONT_FORMAT_REGEX)?.[1];\n\n if (format && format.toLowerCase() !== \"woff2\") {\n throw new Error(\n `Expected a WOFF2 font for \"${family}\", received \"${format}\".`,\n );\n }\n\n return {\n body,\n url: fontUrl,\n originalUrl: urlMatch[0],\n subset: match[1]?.trim(),\n style: body.match(FONT_STYLE_REGEX)?.[1]?.trim() ?? \"normal\",\n };\n });\n};\n\nconst hasCurrentGeneratedFonts = async (\n cssPath: string,\n outputDir: string,\n configHash: string,\n): Promise<boolean> => {\n try {\n const css = await readFile(cssPath, \"utf8\");\n\n if (!css.includes(`puzzle-ui-fonts-sha256:${configHash}`)) {\n return false;\n }\n\n const referencedFiles = [\n ...css.matchAll(/url\\(\\s*[\"']?\\.\\/([^)\"']+)[\"']?\\s*\\)/g),\n ]\n .map((match) => match[1])\n .filter((name): name is string => name !== undefined);\n\n if (referencedFiles.length === 0) {\n return false;\n }\n\n await Promise.all(\n referencedFiles.map((filename) => access(join(outputDir, filename))),\n );\n\n return true;\n } catch {\n return false;\n }\n};\n\nexport const downloadGoogleFonts = async ({\n fonts,\n outputDir,\n force = false,\n subsets,\n fetcher = fetch,\n}: DownloadGoogleFontsOptions): Promise<DownloadGoogleFontsResult> => {\n if (\n subsets !== undefined &&\n (subsets.length === 0 ||\n subsets.some((subset) => !/^[a-z]+(?:-[a-z]+)*$/.test(subset)))\n ) {\n throw new TypeError(\n \"subsets must be a non-empty list of Google Fonts subset names.\",\n );\n }\n\n const stylesheets = resolveFontStylesheets(fonts);\n\n if (stylesheets.length === 0) {\n throw new Error(\n \"No fonts configured. Add at least one font to puzzle-fonts.json.\",\n );\n }\n\n await mkdir(outputDir, { recursive: true });\n\n const cssPath = join(outputDir, GENERATED_CSS_FILENAME);\n\n const configHash = createHashValue(\n JSON.stringify([\n \"preload-v1\",\n subsets ? [...new Set(subsets)].sort() : null,\n stylesheets.map((stylesheet) => stylesheet.href).sort(),\n ]),\n );\n\n if (\n !force &&\n (await hasCurrentGeneratedFonts(cssPath, outputDir, configHash))\n ) {\n return {\n cssPath,\n fontFiles: 0,\n skipped: true,\n };\n }\n\n const fontFiles = new Map<string, string>();\n const preloadFiles = new Map<string, string>();\n const generatedFaces: string[] = [];\n\n const downloadFont = async (url: string, family: string): Promise<string> => {\n const existing = fontFiles.get(url);\n\n if (existing) {\n return existing;\n }\n\n const filename = `font-${createHashValue(url).slice(0, 16)}.woff2`;\n\n const response = await fetcher(url);\n\n if (!response.ok) {\n throw new Error(\n `Could not download font \"${family}\": HTTP ${response.status}.`,\n );\n }\n\n const buffer = new Uint8Array(await response.arrayBuffer());\n\n if (buffer.byteLength === 0) {\n throw new Error(\n `Google Fonts returned an empty font file for \"${family}\".`,\n );\n }\n\n await writeFile(join(outputDir, filename), buffer);\n\n fontFiles.set(url, filename);\n\n return filename;\n };\n\n for (const stylesheet of stylesheets) {\n const response = await fetcher(stylesheet.href, {\n headers: GOOGLE_FONTS_CSS_HEADERS,\n });\n\n if (!response.ok) {\n const details = (await response.text()).slice(0, 500);\n\n throw new Error(\n [\n `Google Fonts request failed for \"${stylesheet.family}\".`,\n `HTTP ${response.status}.`,\n details,\n ]\n .filter(Boolean)\n .join(\" \"),\n );\n }\n\n const css = await response.text();\n\n const faces = parseFontFaces(css, stylesheet.family);\n\n if (subsets) {\n for (const subset of subsets) {\n if (!faces.some((face) => face.subset === subset)) {\n throw new Error(\n `Google Fonts returned no \"${subset}\" subset for \"${stylesheet.family}\".`,\n );\n }\n }\n }\n\n const latinNormalFace = faces.find(\n (face) =>\n face.subset === \"latin\" && face.style.toLowerCase() === \"normal\",\n );\n\n const preloadFilename =\n latinNormalFace && (!subsets || subsets.includes(\"latin\"))\n ? createFontPreloadFilename(stylesheet.family)\n : null;\n\n if (latinNormalFace && preloadFilename) {\n const originalFilename = await downloadFont(\n latinNormalFace.url,\n stylesheet.family,\n );\n\n const existingPreload = preloadFiles.get(stylesheet.family.toLowerCase());\n\n if (!existingPreload) {\n const originalBytes = await readFile(join(outputDir, originalFilename));\n\n await writeFile(join(outputDir, preloadFilename), originalBytes);\n\n preloadFiles.set(stylesheet.family.toLowerCase(), preloadFilename);\n }\n }\n\n for (const face of faces) {\n if (subsets && (!face.subset || !subsets.includes(face.subset))) {\n continue;\n }\n\n const filename = await downloadFont(face.url, stylesheet.family);\n\n const isPreloadFace =\n face === latinNormalFace && preloadFilename !== null;\n\n const targetFilename = isPreloadFace ? preloadFilename : filename;\n\n const rewrittenBody = face.body.replace(\n face.originalUrl,\n `url(\"./${targetFilename}\")`,\n );\n\n generatedFaces.push(`@font-face {\\n${rewrittenBody.trim()}\\n}`);\n }\n }\n\n const generatedCss = [\n \"/* Generated by @puzzle-ui/theme. Do not edit. */\",\n `/* puzzle-ui-fonts-sha256:${configHash} */`,\n \"\",\n generatedFaces.join(\"\\n\\n\"),\n \"\",\n ].join(\"\\n\");\n\n await writeFile(cssPath, generatedCss, \"utf8\");\n\n return {\n cssPath,\n fontFiles: fontFiles.size,\n skipped: false,\n };\n};\n"],"mappings":"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"}
@@ -1,9 +1,14 @@
1
1
  const require_create_theme_variables = require("../create-theme-variables.cjs");
2
2
  const require_resolve_font_stylesheets = require("./resolve-font-stylesheets.cjs");
3
+ const require_create_local_font_stylesheet_url = require("./create-local-font-stylesheet-url.cjs");
3
4
  let react_jsx_runtime = require("react/jsx-runtime");
4
5
  // src/fonts/google-font.tsx
5
- /** Standalone font loader; Theme is recommended when you want the full token system. */
6
- const GoogleFont = ({ family, weights, styles, display, fallback, role = "body", nonce }) => {
6
+ /**
7
+ * Standalone font loader.
8
+ *
9
+ * Theme is recommended when you want the full token system.
10
+ */
11
+ const GoogleFont = ({ family, weights, styles, display, fallback, role = "body", nonce, fontSource = "google", fontBasePath = "/fonts" }) => {
7
12
  const fonts = { [role]: {
8
13
  family,
9
14
  weights,
@@ -12,25 +17,30 @@ const GoogleFont = ({ family, weights, styles, display, fallback, role = "body",
12
17
  fallback
13
18
  } };
14
19
  const fontStylesheets = require_resolve_font_stylesheets.resolveFontStylesheets(fonts);
20
+ const localFontStylesheet = fontSource === "local" && fontStylesheets.length > 0 ? require_create_local_font_stylesheet_url.createLocalFontStylesheetUrl(fontBasePath) : null;
15
21
  const resolved = require_create_theme_variables.resolveThemeTokens();
16
22
  const variables = require_create_theme_variables.createThemeVariables(fonts, resolved);
17
23
  const fontVariable = `--puzzle-font-${role}`;
18
24
  const declarations = `${fontVariable}: ${variables[fontVariable]};`;
19
25
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
20
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("link", {
26
+ fontSource === "google" && fontStylesheets.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("link", {
21
27
  rel: "preconnect",
22
28
  href: "https://fonts.googleapis.com"
23
- }),
24
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("link", {
29
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("link", {
25
30
  rel: "preconnect",
26
31
  href: "https://fonts.gstatic.com",
27
32
  crossOrigin: "anonymous"
28
- }),
29
- fontStylesheets.map(({ href }) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("link", {
33
+ })] }) : null,
34
+ fontSource === "google" ? fontStylesheets.map(({ href }) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("link", {
30
35
  rel: "stylesheet",
31
36
  href,
32
37
  precedence: "default"
33
- }, href)),
38
+ }, href)) : null,
39
+ localFontStylesheet ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("link", {
40
+ rel: "stylesheet",
41
+ href: localFontStylesheet,
42
+ precedence: "default"
43
+ }) : null,
34
44
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("style", {
35
45
  nonce,
36
46
  children: `:root { ${declarations} }`
@@ -1 +1 @@
1
- {"version":3,"file":"google-font.cjs","names":["resolveFontStylesheets","resolveThemeTokens","createThemeVariables"],"sources":["../../src/fonts/google-font.tsx"],"sourcesContent":["import {\n createThemeVariables,\n resolveThemeTokens,\n} from \"../create-theme-variables\";\nimport { resolveFontStylesheets } from \"./resolve-font-stylesheets\";\nimport type { FontConfig } from \"./types\";\n\n/** Standalone font loader; Theme is recommended when you want the full token system. */\nconst GoogleFont = ({\n family,\n weights,\n styles,\n display,\n fallback,\n role = \"body\",\n nonce,\n}: FontConfig) => {\n const fonts = { [role]: { family, weights, styles, display, fallback } };\n const fontStylesheets = resolveFontStylesheets(fonts);\n const resolved = resolveThemeTokens();\n const variables = createThemeVariables(fonts, resolved);\n const fontVariable = `--puzzle-font-${role}`;\n const declarations = `${fontVariable}: ${variables[fontVariable]};`;\n\n return (\n <>\n <link rel=\"preconnect\" href=\"https://fonts.googleapis.com\" />\n <link\n rel=\"preconnect\"\n href=\"https://fonts.gstatic.com\"\n crossOrigin=\"anonymous\"\n />\n {fontStylesheets.map(({ href }) => (\n <link key={href} rel=\"stylesheet\" href={href} precedence=\"default\" />\n ))}\n <style nonce={nonce}>{`:root { ${declarations} }`}</style>\n </>\n );\n};\n\nexport { GoogleFont };\n"],"mappings":";;;;;AAQA,MAAM,cAAc,EAClB,QACA,SACA,QACA,SACA,UACA,OAAO,QACP,YACgB;CAChB,MAAM,QAAQ,GAAG,OAAO;EAAE;EAAQ;EAAS;EAAQ;EAAS;CAAS,EAAE;CACvE,MAAM,kBAAkBA,iCAAAA,uBAAuB,KAAK;CACpD,MAAM,WAAWC,+BAAAA,mBAAmB;CACpC,MAAM,YAAYC,+BAAAA,qBAAqB,OAAO,QAAQ;CACtD,MAAM,eAAe,iBAAiB;CACtC,MAAM,eAAe,GAAG,aAAa,IAAI,UAAU,cAAc;CAEjE,OACE,iBAAA,GAAA,kBAAA,KAAA,CAAA,kBAAA,UAAA,EAAA,UAAA;EACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;GAAM,KAAI;GAAa,MAAK;EAAgC,CAAA;EAC5D,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;GACE,KAAI;GACJ,MAAK;GACL,aAAY;EACb,CAAA;EACA,gBAAgB,KAAK,EAAE,WACtB,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;GAAiB,KAAI;GAAmB;GAAM,YAAW;EAAW,GAAzD,IAAyD,CACrE;EACD,iBAAA,GAAA,kBAAA,IAAA,CAAC,SAAD;GAAc;GAAQ,UAAA,WAAW,aAAa;EAAW,CAAA;CACzD,EAAA,CAAA;AAEN"}
1
+ {"version":3,"file":"google-font.cjs","names":["resolveFontStylesheets","createLocalFontStylesheetUrl","resolveThemeTokens","createThemeVariables"],"sources":["../../src/fonts/google-font.tsx"],"sourcesContent":["import {\n createThemeVariables,\n resolveThemeTokens,\n} from \"../create-theme-variables\";\n\nimport { resolveFontStylesheets } from \"./resolve-font-stylesheets\";\nimport { createLocalFontStylesheetUrl } from \"./create-local-font-stylesheet-url\";\n\nimport type { FontConfig, FontSource } from \"./types\";\n\ntype GoogleFontProps = FontConfig & {\n fontSource?: FontSource;\n fontBasePath?: string;\n};\n\n/**\n * Standalone font loader.\n *\n * Theme is recommended when you want the full token system.\n */\nconst GoogleFont = ({\n family,\n weights,\n styles,\n display,\n fallback,\n role = \"body\",\n nonce,\n fontSource = \"google\",\n fontBasePath = \"/fonts\",\n}: GoogleFontProps) => {\n const fonts = {\n [role]: {\n family,\n weights,\n styles,\n display,\n fallback,\n },\n };\n\n const fontStylesheets = resolveFontStylesheets(fonts);\n\n const localFontStylesheet =\n fontSource === \"local\" && fontStylesheets.length > 0\n ? createLocalFontStylesheetUrl(fontBasePath)\n : null;\n\n const resolved = resolveThemeTokens();\n\n const variables = createThemeVariables(fonts, resolved);\n\n const fontVariable = `--puzzle-font-${role}`;\n\n const declarations = `${fontVariable}: ${variables[fontVariable]};`;\n\n return (\n <>\n {fontSource === \"google\" && fontStylesheets.length > 0 ? (\n <>\n <link rel=\"preconnect\" href=\"https://fonts.googleapis.com\" />\n\n <link\n rel=\"preconnect\"\n href=\"https://fonts.gstatic.com\"\n crossOrigin=\"anonymous\"\n />\n </>\n ) : null}\n\n {fontSource === \"google\"\n ? fontStylesheets.map(({ href }) => (\n <link\n key={href}\n rel=\"stylesheet\"\n href={href}\n precedence=\"default\"\n />\n ))\n : null}\n\n {localFontStylesheet ? (\n <link\n rel=\"stylesheet\"\n href={localFontStylesheet}\n precedence=\"default\"\n />\n ) : null}\n\n <style nonce={nonce}>{`:root { ${declarations} }`}</style>\n </>\n );\n};\n\nexport { GoogleFont };\n"],"mappings":";;;;;;;;;;AAoBA,MAAM,cAAc,EAClB,QACA,SACA,QACA,SACA,UACA,OAAO,QACP,OACA,aAAa,UACb,eAAe,eACM;CACrB,MAAM,QAAQ,GACX,OAAO;EACN;EACA;EACA;EACA;EACA;CACF,EACF;CAEA,MAAM,kBAAkBA,iCAAAA,uBAAuB,KAAK;CAEpD,MAAM,sBACJ,eAAe,WAAW,gBAAgB,SAAS,IAC/CC,yCAAAA,6BAA6B,YAAY,IACzC;CAEN,MAAM,WAAWC,+BAAAA,mBAAmB;CAEpC,MAAM,YAAYC,+BAAAA,qBAAqB,OAAO,QAAQ;CAEtD,MAAM,eAAe,iBAAiB;CAEtC,MAAM,eAAe,GAAG,aAAa,IAAI,UAAU,cAAc;CAEjE,OACE,iBAAA,GAAA,kBAAA,KAAA,CAAA,kBAAA,UAAA,EAAA,UAAA;EACG,eAAe,YAAY,gBAAgB,SAAS,IACnD,iBAAA,GAAA,kBAAA,KAAA,CAAA,kBAAA,UAAA,EAAA,UAAA,CACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;GAAM,KAAI;GAAa,MAAK;EAAgC,CAAA,GAE5D,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;GACE,KAAI;GACJ,MAAK;GACL,aAAY;EACb,CAAA,CACD,EAAA,CAAA,IACA;EAEH,eAAe,WACZ,gBAAgB,KAAK,EAAE,WACrB,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;GAEE,KAAI;GACE;GACN,YAAW;EACZ,GAJM,IAIN,CACF,IACD;EAEH,sBACC,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;GACE,KAAI;GACJ,MAAM;GACN,YAAW;EACZ,CAAA,IACC;EAEJ,iBAAA,GAAA,kBAAA,IAAA,CAAC,SAAD;GAAc;GAAQ,UAAA,WAAW,aAAa;EAAW,CAAA;CACzD,EAAA,CAAA;AAEN"}
@@ -1,4 +1,12 @@
1
- import { FontConfig } from "./types.cjs";
2
- /** Standalone font loader; Theme is recommended when you want the full token system. */
3
- declare const GoogleFont: ({ family, weights, styles, display, fallback, role, nonce }: FontConfig) => import("react").JSX.Element;
1
+ import { FontConfig, FontSource } from "./types.cjs";
2
+ type GoogleFontProps = FontConfig & {
3
+ fontSource?: FontSource;
4
+ fontBasePath?: string;
5
+ };
6
+ /**
7
+ * Standalone font loader.
8
+ *
9
+ * Theme is recommended when you want the full token system.
10
+ */
11
+ declare const GoogleFont: ({ family, weights, styles, display, fallback, role, nonce, fontSource, fontBasePath }: GoogleFontProps) => import("react").JSX.Element;
4
12
  export { GoogleFont };
@@ -1,4 +1,12 @@
1
- import { FontConfig } from "./types.js";
2
- /** Standalone font loader; Theme is recommended when you want the full token system. */
3
- declare const GoogleFont: ({ family, weights, styles, display, fallback, role, nonce }: FontConfig) => import("react").JSX.Element;
1
+ import { FontConfig, FontSource } from "./types.js";
2
+ type GoogleFontProps = FontConfig & {
3
+ fontSource?: FontSource;
4
+ fontBasePath?: string;
5
+ };
6
+ /**
7
+ * Standalone font loader.
8
+ *
9
+ * Theme is recommended when you want the full token system.
10
+ */
11
+ declare const GoogleFont: ({ family, weights, styles, display, fallback, role, nonce, fontSource, fontBasePath }: GoogleFontProps) => import("react").JSX.Element;
4
12
  export { GoogleFont };
@@ -1,9 +1,14 @@
1
1
  import { createThemeVariables, resolveThemeTokens } from "../create-theme-variables.js";
2
2
  import { resolveFontStylesheets } from "./resolve-font-stylesheets.js";
3
+ import { createLocalFontStylesheetUrl } from "./create-local-font-stylesheet-url.js";
3
4
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
4
5
  // src/fonts/google-font.tsx
5
- /** Standalone font loader; Theme is recommended when you want the full token system. */
6
- const GoogleFont = ({ family, weights, styles, display, fallback, role = "body", nonce }) => {
6
+ /**
7
+ * Standalone font loader.
8
+ *
9
+ * Theme is recommended when you want the full token system.
10
+ */
11
+ const GoogleFont = ({ family, weights, styles, display, fallback, role = "body", nonce, fontSource = "google", fontBasePath = "/fonts" }) => {
7
12
  const fonts = { [role]: {
8
13
  family,
9
14
  weights,
@@ -12,25 +17,30 @@ const GoogleFont = ({ family, weights, styles, display, fallback, role = "body",
12
17
  fallback
13
18
  } };
14
19
  const fontStylesheets = resolveFontStylesheets(fonts);
20
+ const localFontStylesheet = fontSource === "local" && fontStylesheets.length > 0 ? createLocalFontStylesheetUrl(fontBasePath) : null;
15
21
  const resolved = resolveThemeTokens();
16
22
  const variables = createThemeVariables(fonts, resolved);
17
23
  const fontVariable = `--puzzle-font-${role}`;
18
24
  const declarations = `${fontVariable}: ${variables[fontVariable]};`;
19
25
  return /* @__PURE__ */ jsxs(Fragment, { children: [
20
- /* @__PURE__ */ jsx("link", {
26
+ fontSource === "google" && fontStylesheets.length > 0 ? /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("link", {
21
27
  rel: "preconnect",
22
28
  href: "https://fonts.googleapis.com"
23
- }),
24
- /* @__PURE__ */ jsx("link", {
29
+ }), /* @__PURE__ */ jsx("link", {
25
30
  rel: "preconnect",
26
31
  href: "https://fonts.gstatic.com",
27
32
  crossOrigin: "anonymous"
28
- }),
29
- fontStylesheets.map(({ href }) => /* @__PURE__ */ jsx("link", {
33
+ })] }) : null,
34
+ fontSource === "google" ? fontStylesheets.map(({ href }) => /* @__PURE__ */ jsx("link", {
30
35
  rel: "stylesheet",
31
36
  href,
32
37
  precedence: "default"
33
- }, href)),
38
+ }, href)) : null,
39
+ localFontStylesheet ? /* @__PURE__ */ jsx("link", {
40
+ rel: "stylesheet",
41
+ href: localFontStylesheet,
42
+ precedence: "default"
43
+ }) : null,
34
44
  /* @__PURE__ */ jsx("style", {
35
45
  nonce,
36
46
  children: `:root { ${declarations} }`
@@ -1 +1 @@
1
- {"version":3,"file":"google-font.js","names":[],"sources":["../../src/fonts/google-font.tsx"],"sourcesContent":["import {\n createThemeVariables,\n resolveThemeTokens,\n} from \"../create-theme-variables\";\nimport { resolveFontStylesheets } from \"./resolve-font-stylesheets\";\nimport type { FontConfig } from \"./types\";\n\n/** Standalone font loader; Theme is recommended when you want the full token system. */\nconst GoogleFont = ({\n family,\n weights,\n styles,\n display,\n fallback,\n role = \"body\",\n nonce,\n}: FontConfig) => {\n const fonts = { [role]: { family, weights, styles, display, fallback } };\n const fontStylesheets = resolveFontStylesheets(fonts);\n const resolved = resolveThemeTokens();\n const variables = createThemeVariables(fonts, resolved);\n const fontVariable = `--puzzle-font-${role}`;\n const declarations = `${fontVariable}: ${variables[fontVariable]};`;\n\n return (\n <>\n <link rel=\"preconnect\" href=\"https://fonts.googleapis.com\" />\n <link\n rel=\"preconnect\"\n href=\"https://fonts.gstatic.com\"\n crossOrigin=\"anonymous\"\n />\n {fontStylesheets.map(({ href }) => (\n <link key={href} rel=\"stylesheet\" href={href} precedence=\"default\" />\n ))}\n <style nonce={nonce}>{`:root { ${declarations} }`}</style>\n </>\n );\n};\n\nexport { GoogleFont };\n"],"mappings":";;;;;AAQA,MAAM,cAAc,EAClB,QACA,SACA,QACA,SACA,UACA,OAAO,QACP,YACgB;CAChB,MAAM,QAAQ,GAAG,OAAO;EAAE;EAAQ;EAAS;EAAQ;EAAS;CAAS,EAAE;CACvE,MAAM,kBAAkB,uBAAuB,KAAK;CACpD,MAAM,WAAW,mBAAmB;CACpC,MAAM,YAAY,qBAAqB,OAAO,QAAQ;CACtD,MAAM,eAAe,iBAAiB;CACtC,MAAM,eAAe,GAAG,aAAa,IAAI,UAAU,cAAc;CAEjE,OACE,qBAAA,UAAA,EAAA,UAAA;EACE,oBAAC,QAAD;GAAM,KAAI;GAAa,MAAK;EAAgC,CAAA;EAC5D,oBAAC,QAAD;GACE,KAAI;GACJ,MAAK;GACL,aAAY;EACb,CAAA;EACA,gBAAgB,KAAK,EAAE,WACtB,oBAAC,QAAD;GAAiB,KAAI;GAAmB;GAAM,YAAW;EAAW,GAAzD,IAAyD,CACrE;EACD,oBAAC,SAAD;GAAc;GAAQ,UAAA,WAAW,aAAa;EAAW,CAAA;CACzD,EAAA,CAAA;AAEN"}
1
+ {"version":3,"file":"google-font.js","names":[],"sources":["../../src/fonts/google-font.tsx"],"sourcesContent":["import {\n createThemeVariables,\n resolveThemeTokens,\n} from \"../create-theme-variables\";\n\nimport { resolveFontStylesheets } from \"./resolve-font-stylesheets\";\nimport { createLocalFontStylesheetUrl } from \"./create-local-font-stylesheet-url\";\n\nimport type { FontConfig, FontSource } from \"./types\";\n\ntype GoogleFontProps = FontConfig & {\n fontSource?: FontSource;\n fontBasePath?: string;\n};\n\n/**\n * Standalone font loader.\n *\n * Theme is recommended when you want the full token system.\n */\nconst GoogleFont = ({\n family,\n weights,\n styles,\n display,\n fallback,\n role = \"body\",\n nonce,\n fontSource = \"google\",\n fontBasePath = \"/fonts\",\n}: GoogleFontProps) => {\n const fonts = {\n [role]: {\n family,\n weights,\n styles,\n display,\n fallback,\n },\n };\n\n const fontStylesheets = resolveFontStylesheets(fonts);\n\n const localFontStylesheet =\n fontSource === \"local\" && fontStylesheets.length > 0\n ? createLocalFontStylesheetUrl(fontBasePath)\n : null;\n\n const resolved = resolveThemeTokens();\n\n const variables = createThemeVariables(fonts, resolved);\n\n const fontVariable = `--puzzle-font-${role}`;\n\n const declarations = `${fontVariable}: ${variables[fontVariable]};`;\n\n return (\n <>\n {fontSource === \"google\" && fontStylesheets.length > 0 ? (\n <>\n <link rel=\"preconnect\" href=\"https://fonts.googleapis.com\" />\n\n <link\n rel=\"preconnect\"\n href=\"https://fonts.gstatic.com\"\n crossOrigin=\"anonymous\"\n />\n </>\n ) : null}\n\n {fontSource === \"google\"\n ? fontStylesheets.map(({ href }) => (\n <link\n key={href}\n rel=\"stylesheet\"\n href={href}\n precedence=\"default\"\n />\n ))\n : null}\n\n {localFontStylesheet ? (\n <link\n rel=\"stylesheet\"\n href={localFontStylesheet}\n precedence=\"default\"\n />\n ) : null}\n\n <style nonce={nonce}>{`:root { ${declarations} }`}</style>\n </>\n );\n};\n\nexport { GoogleFont };\n"],"mappings":";;;;;;;;;;AAoBA,MAAM,cAAc,EAClB,QACA,SACA,QACA,SACA,UACA,OAAO,QACP,OACA,aAAa,UACb,eAAe,eACM;CACrB,MAAM,QAAQ,GACX,OAAO;EACN;EACA;EACA;EACA;EACA;CACF,EACF;CAEA,MAAM,kBAAkB,uBAAuB,KAAK;CAEpD,MAAM,sBACJ,eAAe,WAAW,gBAAgB,SAAS,IAC/C,6BAA6B,YAAY,IACzC;CAEN,MAAM,WAAW,mBAAmB;CAEpC,MAAM,YAAY,qBAAqB,OAAO,QAAQ;CAEtD,MAAM,eAAe,iBAAiB;CAEtC,MAAM,eAAe,GAAG,aAAa,IAAI,UAAU,cAAc;CAEjE,OACE,qBAAA,UAAA,EAAA,UAAA;EACG,eAAe,YAAY,gBAAgB,SAAS,IACnD,qBAAA,UAAA,EAAA,UAAA,CACE,oBAAC,QAAD;GAAM,KAAI;GAAa,MAAK;EAAgC,CAAA,GAE5D,oBAAC,QAAD;GACE,KAAI;GACJ,MAAK;GACL,aAAY;EACb,CAAA,CACD,EAAA,CAAA,IACA;EAEH,eAAe,WACZ,gBAAgB,KAAK,EAAE,WACrB,oBAAC,QAAD;GAEE,KAAI;GACE;GACN,YAAW;EACZ,GAJM,IAIN,CACF,IACD;EAEH,sBACC,oBAAC,QAAD;GACE,KAAI;GACJ,MAAM;GACN,YAAW;EACZ,CAAA,IACC;EAEJ,oBAAC,SAAD;GAAc;GAAQ,UAAA,WAAW,aAAa;EAAW,CAAA;CACzD,EAAA,CAAA;AAEN"}
@@ -1,4 +1,4 @@
1
1
  import { createGoogleFontUrl } from "./create-google-font-url.cjs";
2
2
  import { GoogleFont } from "./google-font.cjs";
3
- import { FontConfig, FontDisplay, FontRole, FontStyle, FontStylesheet, ResolvedFontConfig, ThemeFonts } from "./types.cjs";
4
- export { type FontConfig, type FontDisplay, type FontRole, type FontStyle, type FontStylesheet, GoogleFont, type ResolvedFontConfig, type ThemeFonts, createGoogleFontUrl };
3
+ import { FontConfig, FontDisplay, FontPreload, FontRole, FontSource, FontStyle, FontStylesheet, ResolvedFontConfig, ThemeFonts } from "./types.cjs";
4
+ export { type FontConfig, type FontDisplay, type FontPreload, type FontRole, type FontSource, type FontStyle, type FontStylesheet, GoogleFont, type ResolvedFontConfig, type ThemeFonts, createGoogleFontUrl };