@lovrozagar/flare 0.1.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.
- package/LICENSE +21 -0
- package/README.md +11 -0
- package/package.json +146 -0
- package/scripts/download-fonts.ts +136 -0
- package/scripts/populate-fonts.ts +721 -0
- package/src/await.ts +2 -0
- package/src/boundaries/index.ts +99 -0
- package/src/broadcast/channel.ts +65 -0
- package/src/broadcast/context.ts +12 -0
- package/src/broadcast/index.ts +75 -0
- package/src/broadcast/provider.tsx +7 -0
- package/src/build-location.ts +2 -0
- package/src/caches/index.ts +384 -0
- package/src/client/index.ts +75 -0
- package/src/client.tsx +2 -0
- package/src/codegen/index.ts +41 -0
- package/src/components/await.tsx +140 -0
- package/src/components/dev-error-overlay.tsx +154 -0
- package/src/components/direction-script.tsx +22 -0
- package/src/components/index.ts +150 -0
- package/src/components/locale-script.tsx +26 -0
- package/src/components/reset-css.tsx +14 -0
- package/src/components/ssr-context.tsx +37 -0
- package/src/components/theme-script.tsx +22 -0
- package/src/components/view-transition-css.tsx +21 -0
- package/src/config/index.ts +84 -0
- package/src/create-client-only-fn.ts +1 -0
- package/src/create-isomorphic-fn.ts +2 -0
- package/src/create-layout.ts +1 -0
- package/src/create-page.ts +1 -0
- package/src/create-path-segment.ts +1 -0
- package/src/create-query-client-getter.ts +2 -0
- package/src/create-root-layout.ts +1 -0
- package/src/create-router.ts +8 -0
- package/src/create-server-only-fn.ts +1 -0
- package/src/dedupe/index.ts +151 -0
- package/src/defer/index.ts +82 -0
- package/src/direction/index.tsx +163 -0
- package/src/direction.ts +3 -0
- package/src/duration/index.ts +18 -0
- package/src/env-fn/index.ts +101 -0
- package/src/env-fn.ts +2 -0
- package/src/errors/index.ts +166 -0
- package/src/fonts/abeezee.ts +50 -0
- package/src/fonts/abel.ts +26 -0
- package/src/fonts/abril-fatface.ts +34 -0
- package/src/fonts/acme.ts +26 -0
- package/src/fonts/advent-pro.ts +92 -0
- package/src/fonts/alegreya-sans.ts +228 -0
- package/src/fonts/alegreya.ts +228 -0
- package/src/fonts/alex-brush.ts +42 -0
- package/src/fonts/alfa-slab-one.ts +42 -0
- package/src/fonts/alice.ts +48 -0
- package/src/fonts/amatic-sc.ts +94 -0
- package/src/fonts/amiri.ts +114 -0
- package/src/fonts/antic-slab.ts +26 -0
- package/src/fonts/anton.ts +42 -0
- package/src/fonts/archivo-black.ts +34 -0
- package/src/fonts/archivo-narrow.ts +114 -0
- package/src/fonts/archivo.ts +66 -0
- package/src/fonts/arimo.ts +256 -0
- package/src/fonts/arvo.ts +50 -0
- package/src/fonts/asap.ts +66 -0
- package/src/fonts/assistant.ts +64 -0
- package/src/fonts/baloo-2.ts +82 -0
- package/src/fonts/barlow-condensed.ts +114 -0
- package/src/fonts/barlow-semi-condensed.ts +114 -0
- package/src/fonts/barlow.ts +114 -0
- package/src/fonts/be-vietnam-pro.ts +114 -0
- package/src/fonts/bebas-neue.ts +34 -0
- package/src/fonts/besley.ts +82 -0
- package/src/fonts/biorhyme.ts +50 -0
- package/src/fonts/bitter.ts +94 -0
- package/src/fonts/black-ops-one.ts +49 -0
- package/src/fonts/bree-serif.ts +34 -0
- package/src/fonts/bricolage-grotesque.ts +66 -0
- package/src/fonts/cabin.ts +114 -0
- package/src/fonts/cairo.ts +66 -0
- package/src/fonts/cardo.ts +193 -0
- package/src/fonts/catamaran.ts +41 -0
- package/src/fonts/caveat.ts +78 -0
- package/src/fonts/chakra-petch.ts +142 -0
- package/src/fonts/chivo.ts +66 -0
- package/src/fonts/cinzel.ts +50 -0
- package/src/fonts/comfortaa.ts +109 -0
- package/src/fonts/commissioner.ts +64 -0
- package/src/fonts/component.tsx +27 -0
- package/src/fonts/cormorant-garamond.ts +171 -0
- package/src/fonts/cormorant.ts +170 -0
- package/src/fonts/courgette.ts +34 -0
- package/src/fonts/courier-prime.ts +82 -0
- package/src/fonts/create-font.ts +47 -0
- package/src/fonts/create-registry-font.ts +40 -0
- package/src/fonts/crete-round.ts +50 -0
- package/src/fonts/crimson-text.ts +114 -0
- package/src/fonts/css.ts +86 -0
- package/src/fonts/dancing-script.ts +66 -0
- package/src/fonts/dm-mono.ts +50 -0
- package/src/fonts/dm-sans.ts +50 -0
- package/src/fonts/dm-serif-display.ts +50 -0
- package/src/fonts/domine.ts +50 -0
- package/src/fonts/dosis.ts +66 -0
- package/src/fonts/eb-garamond.ts +228 -0
- package/src/fonts/eczar.ts +94 -0
- package/src/fonts/encode-sans.ts +42 -0
- package/src/fonts/epilogue.ts +66 -0
- package/src/fonts/exo-2.ts +94 -0
- package/src/fonts/exo.ts +66 -0
- package/src/fonts/figtree.ts +82 -0
- package/src/fonts/fira-code.ts +121 -0
- package/src/fonts/fira-sans-condensed.ts +228 -0
- package/src/fonts/fira-sans.ts +228 -0
- package/src/fonts/fjalla-one.ts +49 -0
- package/src/fonts/font-urls.gen.json +1962 -0
- package/src/fonts/fraunces.ts +66 -0
- package/src/fonts/fredoka.ts +64 -0
- package/src/fonts/gelasio.ts +114 -0
- package/src/fonts/gloria-hallelujah.ts +34 -0
- package/src/fonts/gothic-a1.ts +124 -0
- package/src/fonts/great-vibes.ts +64 -0
- package/src/fonts/gudea.ts +66 -0
- package/src/fonts/heebo.ts +57 -0
- package/src/fonts/hind-siliguri.ts +66 -0
- package/src/fonts/hind.ts +66 -0
- package/src/fonts/ibm-plex-mono.ts +172 -0
- package/src/fonts/ibm-plex-sans-arabic.ts +80 -0
- package/src/fonts/ibm-plex-sans.ts +199 -0
- package/src/fonts/ibm-plex-serif.ts +171 -0
- package/src/fonts/inconsolata.ts +66 -0
- package/src/fonts/index.ts +4 -0
- package/src/fonts/indie-flower.ts +34 -0
- package/src/fonts/instrument-sans.ts +82 -0
- package/src/fonts/instrument-serif.ts +50 -0
- package/src/fonts/inter-display.ts +52 -0
- package/src/fonts/inter-tight.ts +124 -0
- package/src/fonts/inter.ts +123 -0
- package/src/fonts/jetbrains-mono.ts +199 -0
- package/src/fonts/josefin-sans.ts +114 -0
- package/src/fonts/josefin-slab.ts +50 -0
- package/src/fonts/jost.ts +64 -0
- package/src/fonts/kalam.ts +66 -0
- package/src/fonts/kanit.ts +142 -0
- package/src/fonts/karla.ts +82 -0
- package/src/fonts/khand.ts +66 -0
- package/src/fonts/lato.ts +82 -0
- package/src/fonts/league-spartan.ts +42 -0
- package/src/fonts/lexend-deca.ts +42 -0
- package/src/fonts/lexend.ts +42 -0
- package/src/fonts/libre-baskerville.ts +82 -0
- package/src/fonts/libre-franklin.ts +95 -0
- package/src/fonts/lilita-one.ts +34 -0
- package/src/fonts/lobster-two.ts +50 -0
- package/src/fonts/lobster.ts +56 -0
- package/src/fonts/lora.ts +235 -0
- package/src/fonts/m-plus-rounded-1c.ts +138 -0
- package/src/fonts/manrope.ts +109 -0
- package/src/fonts/maven-pro.ts +66 -0
- package/src/fonts/merriweather-sans.ts +142 -0
- package/src/fonts/merriweather.ts +171 -0
- package/src/fonts/montserrat-alternates.ts +171 -0
- package/src/fonts/montserrat.ts +94 -0
- package/src/fonts/mukta.ts +66 -0
- package/src/fonts/mulish.ts +170 -0
- package/src/fonts/nanum-gothic.ts +34 -0
- package/src/fonts/nanum-myeongjo.ts +34 -0
- package/src/fonts/neuton.ts +66 -0
- package/src/fonts/newsreader.ts +114 -0
- package/src/fonts/noticia-text.ts +114 -0
- package/src/fonts/noto-sans-arabic.ts +58 -0
- package/src/fonts/noto-sans-hk.ts +49 -0
- package/src/fonts/noto-sans-jp.ts +49 -0
- package/src/fonts/noto-sans-kr.ts +49 -0
- package/src/fonts/noto-sans-sc.ts +49 -0
- package/src/fonts/noto-sans-tc.ts +49 -0
- package/src/fonts/noto-sans.ts +140 -0
- package/src/fonts/noto-serif-jp.ts +80 -0
- package/src/fonts/noto-serif.ts +140 -0
- package/src/fonts/nunito-sans.ts +170 -0
- package/src/fonts/nunito.ts +170 -0
- package/src/fonts/old-standard-tt.ts +133 -0
- package/src/fonts/onest.ts +48 -0
- package/src/fonts/open-sans.ts +340 -0
- package/src/fonts/orbitron.ts +34 -0
- package/src/fonts/oswald.ts +94 -0
- package/src/fonts/outfit.ts +34 -0
- package/src/fonts/overpass.ts +94 -0
- package/src/fonts/oxygen.ts +50 -0
- package/src/fonts/pacifico.ts +56 -0
- package/src/fonts/passion-one.ts +50 -0
- package/src/fonts/pathway-gothic-one.ts +34 -0
- package/src/fonts/permanent-marker.ts +26 -0
- package/src/fonts/philosopher.ts +172 -0
- package/src/fonts/play.ts +109 -0
- package/src/fonts/playfair-display.ts +142 -0
- package/src/fonts/plus-jakarta-sans.ts +142 -0
- package/src/fonts/poppins.ts +114 -0
- package/src/fonts/prompt.ts +142 -0
- package/src/fonts/pt-sans-narrow.ts +78 -0
- package/src/fonts/pt-sans.ts +138 -0
- package/src/fonts/pt-serif.ts +138 -0
- package/src/fonts/public-sans.ts +66 -0
- package/src/fonts/questrial.ts +42 -0
- package/src/fonts/quicksand.ts +66 -0
- package/src/fonts/rajdhani.ts +66 -0
- package/src/fonts/raleway.ts +94 -0
- package/src/fonts/recursive.ts +80 -0
- package/src/fonts/red-hat-display.ts +82 -0
- package/src/fonts/red-rose.ts +66 -0
- package/src/fonts/registry.gen.ts +173 -0
- package/src/fonts/righteous.ts +34 -0
- package/src/fonts/roboto-condensed.ts +124 -0
- package/src/fonts/roboto-flex.ts +64 -0
- package/src/fonts/roboto-mono.ts +199 -0
- package/src/fonts/roboto-slab.ts +72 -0
- package/src/fonts/roboto.ts +156 -0
- package/src/fonts/rokkitt.ts +66 -0
- package/src/fonts/rubik.ts +199 -0
- package/src/fonts/sacramento.ts +34 -0
- package/src/fonts/saira.ts +66 -0
- package/src/fonts/sarabun.ts +142 -0
- package/src/fonts/satisfy.ts +26 -0
- package/src/fonts/sawarabi-mincho.ts +41 -0
- package/src/fonts/shadows-into-light.ts +34 -0
- package/src/fonts/signika-negative.ts +66 -0
- package/src/fonts/signika.ts +66 -0
- package/src/fonts/slabo-27px.ts +34 -0
- package/src/fonts/sora.ts +50 -0
- package/src/fonts/source-code-pro.ts +228 -0
- package/src/fonts/source-sans-3.ts +228 -0
- package/src/fonts/source-serif-4.ts +199 -0
- package/src/fonts/space-grotesk.ts +66 -0
- package/src/fonts/space-mono.ts +114 -0
- package/src/fonts/spectral.ts +170 -0
- package/src/fonts/tajawal.ts +50 -0
- package/src/fonts/teko.ts +66 -0
- package/src/fonts/tenor-sans.ts +41 -0
- package/src/fonts/tinos.ts +256 -0
- package/src/fonts/titillium-web.ts +82 -0
- package/src/fonts/types.ts +43 -0
- package/src/fonts/ubuntu-condensed.ts +63 -0
- package/src/fonts/ubuntu-mono.ts +195 -0
- package/src/fonts/ubuntu.ts +195 -0
- package/src/fonts/unbounded.ts +94 -0
- package/src/fonts/unicode-ranges.gen.ts +28 -0
- package/src/fonts/urbanist.ts +50 -0
- package/src/fonts/varela-round.ts +49 -0
- package/src/fonts/vollkorn.ts +199 -0
- package/src/fonts/work-sans.ts +66 -0
- package/src/fonts/yanone-kaffeesatz.ts +128 -0
- package/src/fonts/yantramanav.ts +66 -0
- package/src/fonts/yellowtail.ts +34 -0
- package/src/fonts/young-serif.ts +34 -0
- package/src/fonts/zen-kaku-gothic-new.ts +64 -0
- package/src/fonts/zilla-slab.ts +82 -0
- package/src/form/index.tsx +205 -0
- package/src/generators/index.ts +1589 -0
- package/src/head-client/index.ts +649 -0
- package/src/history/index.ts +210 -0
- package/src/hooks.ts +61 -0
- package/src/hydrate/index.tsx +381 -0
- package/src/hydration/index.ts +187 -0
- package/src/i18n/index.ts +250 -0
- package/src/i18n/jsx.tsx +42 -0
- package/src/i18n/message-format.ts +174 -0
- package/src/image/index.tsx +473 -0
- package/src/image.ts +12 -0
- package/src/intercept-outlet/index.tsx +18 -0
- package/src/intercept-outlet.ts +2 -0
- package/src/internal/index.ts +143 -0
- package/src/internal/once-idle.ts +24 -0
- package/src/internal.ts +12 -0
- package/src/lazy/index.tsx +205 -0
- package/src/link/index.tsx +367 -0
- package/src/loader-pipeline/index.ts +639 -0
- package/src/locale/index.tsx +137 -0
- package/src/locale.ts +3 -0
- package/src/logger.ts +22 -0
- package/src/middleware/builtins/api-proxy.ts +64 -0
- package/src/middleware/builtins/cdn-proxy.ts +58 -0
- package/src/middleware/builtins/i18n.ts +256 -0
- package/src/middleware/builtins/keepalive.ts +24 -0
- package/src/middleware/builtins/markdown-negotiation.ts +141 -0
- package/src/middleware/builtins/static-assets.ts +32 -0
- package/src/middleware/index.ts +223 -0
- package/src/module-graph/index.ts +181 -0
- package/src/mount/index.ts +146 -0
- package/src/navigate.ts +1 -0
- package/src/navigation/index.ts +1278 -0
- package/src/navigation/types.ts +59 -0
- package/src/navigation-progress/index.tsx +247 -0
- package/src/navigation-progress.ts +1 -0
- package/src/ndjson-client/index.ts +376 -0
- package/src/ndjson-server/index.ts +284 -0
- package/src/outlet/index.tsx +633 -0
- package/src/outlet/types.ts +243 -0
- package/src/plugins/css-transform.ts +70 -0
- package/src/plugins/dev-cdn-cache.ts +454 -0
- package/src/plugins/dev-dashboard/client.tsx +2603 -0
- package/src/plugins/dev-dashboard/match-route-tree.ts +159 -0
- package/src/plugins/dev-dashboard/plugin.ts +404 -0
- package/src/plugins/dev-prerender.ts +168 -0
- package/src/plugins/dev-server.ts +327 -0
- package/src/plugins/generate-watch.ts +32 -0
- package/src/plugins/image-plugin.ts +171 -0
- package/src/plugins/index.ts +445 -0
- package/src/plugins/options.ts +37 -0
- package/src/plugins/parse-utils.ts +59 -0
- package/src/plugins/prerender-plugin.ts +144 -0
- package/src/plugins/purge.ts +242 -0
- package/src/plugins/server-fn.ts +641 -0
- package/src/plugins/service-worker.ts +225 -0
- package/src/plugins/solid-jsx.d.ts +12 -0
- package/src/plugins/sx-ast/atomic.ts +166 -0
- package/src/plugins/sx-ast/evaluator.ts +235 -0
- package/src/plugins/sx-ast/index.ts +372 -0
- package/src/plugins/sx-ast/parser.ts +22 -0
- package/src/plugins/sx-ast/rewrite.ts +763 -0
- package/src/plugins/sx.d.ts +14 -0
- package/src/plugins/tw-compile.ts +240 -0
- package/src/plugins/types.ts +41 -0
- package/src/plugins/virtual.d.ts +110 -0
- package/src/plugins/virtual.ts +184 -0
- package/src/preload/index.ts +77 -0
- package/src/prerender/index.ts +431 -0
- package/src/query-client/index.tsx +168 -0
- package/src/registry/index.ts +88 -0
- package/src/reset-css.ts +1 -0
- package/src/revalidation/index.ts +84 -0
- package/src/rewrite/index.ts +83 -0
- package/src/route-builder/create-layout.ts +655 -0
- package/src/route-builder/create-page.ts +806 -0
- package/src/route-builder/create-path-segment.ts +34 -0
- package/src/route-builder/create-root-layout.ts +608 -0
- package/src/route-builder/index.ts +49 -0
- package/src/route-builder/register.ts +242 -0
- package/src/route-builder/types.ts +294 -0
- package/src/router-config/index.ts +91 -0
- package/src/router-primitives/index.ts +25 -0
- package/src/router-primitives/location.ts +29 -0
- package/src/router-primitives/match-id.ts +111 -0
- package/src/router-primitives/paths.ts +109 -0
- package/src/router-primitives/tree.ts +243 -0
- package/src/router-primitives/types.ts +74 -0
- package/src/router.ts +31 -0
- package/src/search-engine/bing.ts +32 -0
- package/src/search-engine/google-jwt.ts +95 -0
- package/src/search-engine/google.ts +139 -0
- package/src/search-engine/index-now.ts +64 -0
- package/src/search-engine/index.ts +12 -0
- package/src/security/index.ts +230 -0
- package/src/server/index.ts +286 -0
- package/src/server-context/browser.ts +113 -0
- package/src/server-context/index.ts +217 -0
- package/src/server-fn/index.ts +710 -0
- package/src/server-fn-query.ts +169 -0
- package/src/server-handler/etag.ts +51 -0
- package/src/server-handler/headers.ts +5 -0
- package/src/server-handler/index.ts +1664 -0
- package/src/server-handler/types.ts +24 -0
- package/src/server-handler/validate-static-params.ts +130 -0
- package/src/server.ts +2 -0
- package/src/service-worker/template.ts +153 -0
- package/src/service-worker-hook/index.ts +70 -0
- package/src/sitemap/index.ts +231 -0
- package/src/ssr/critical-css.ts +150 -0
- package/src/ssr/head-prefix.ts +65 -0
- package/src/ssr/head.ts +259 -0
- package/src/ssr/index.tsx +879 -0
- package/src/state-parser/index.ts +266 -0
- package/src/store/filesystem.ts +131 -0
- package/src/store/index.ts +29 -0
- package/src/styles/index.ts +1200 -0
- package/src/styles/sx-types.ts +11 -0
- package/src/testing/index.ts +251 -0
- package/src/testing.ts +2 -0
- package/src/theme/index.tsx +232 -0
- package/src/theme.ts +3 -0
- package/src/tracing/index.ts +4 -0
- package/src/tracing/noop.ts +13 -0
- package/src/tracing/otel.ts +37 -0
- package/src/tracing/timing.ts +34 -0
- package/src/tracing/types.ts +9 -0
- package/src/url/index.ts +140 -0
- package/src/use-router.ts +11 -0
- package/src/use-suspense-query.ts +2 -0
- package/src/validation/index.ts +57 -0
- package/src/view-transition-css.ts +1 -0
|
@@ -0,0 +1,721 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Font generation pipeline — fetches Google Fonts CSS, parses it,
|
|
3
|
+
* and generates registry.gen.ts, unicode-ranges.gen.ts, and per-font files.
|
|
4
|
+
*
|
|
5
|
+
* Usage: bun run scripts/populate-fonts.ts
|
|
6
|
+
*/
|
|
7
|
+
import { createHash } from "node:crypto";
|
|
8
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
9
|
+
import { join } from "node:path";
|
|
10
|
+
|
|
11
|
+
function writeGenFile(filePath: string, body: string, generator: string): void {
|
|
12
|
+
const normalized = body.replace(/\n*$/, "\n");
|
|
13
|
+
const hash = createHash("sha256").update(normalized).digest("hex").slice(0, 12);
|
|
14
|
+
const header = `/* @generated by ${generator} — do not edit. checksum: ${hash} */`;
|
|
15
|
+
const fullContent = `${header}\n${normalized}`;
|
|
16
|
+
|
|
17
|
+
if (existsSync(filePath)) {
|
|
18
|
+
const existing = readFileSync(filePath, "utf-8");
|
|
19
|
+
const firstLine = existing.split("\n")[0] ?? "";
|
|
20
|
+
const match = firstLine.match(/checksum: ([0-9a-f]{12})/);
|
|
21
|
+
if (match?.[1] === hash) return;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
mkdirSync(join(filePath, ".."), { recursive: true });
|
|
25
|
+
writeFileSync(filePath, fullContent, "utf-8");
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/* ── curated font list ─────────────────────────────────────────── */
|
|
29
|
+
|
|
30
|
+
const FONTS = [
|
|
31
|
+
"ABeeZee",
|
|
32
|
+
"Abel",
|
|
33
|
+
"Abril Fatface",
|
|
34
|
+
"Acme",
|
|
35
|
+
"Alegreya",
|
|
36
|
+
"Alegreya Sans",
|
|
37
|
+
"Alfa Slab One",
|
|
38
|
+
"Amatic SC",
|
|
39
|
+
"Amiri",
|
|
40
|
+
"Antic Slab",
|
|
41
|
+
"Anton",
|
|
42
|
+
"Archivo",
|
|
43
|
+
"Archivo Black",
|
|
44
|
+
"Archivo Narrow",
|
|
45
|
+
"Arimo",
|
|
46
|
+
"Arvo",
|
|
47
|
+
"Asap",
|
|
48
|
+
"Assistant",
|
|
49
|
+
"Barlow",
|
|
50
|
+
"Barlow Condensed",
|
|
51
|
+
"Barlow Semi Condensed",
|
|
52
|
+
"Be Vietnam Pro",
|
|
53
|
+
"Bebas Neue",
|
|
54
|
+
"Bitter",
|
|
55
|
+
"Black Ops One",
|
|
56
|
+
"Bree Serif",
|
|
57
|
+
"Cabin",
|
|
58
|
+
"Cairo",
|
|
59
|
+
"Catamaran",
|
|
60
|
+
"Caveat",
|
|
61
|
+
"Chakra Petch",
|
|
62
|
+
"Cinzel",
|
|
63
|
+
"Comfortaa",
|
|
64
|
+
"Commissioner",
|
|
65
|
+
"Cormorant Garamond",
|
|
66
|
+
"Courgette",
|
|
67
|
+
"Crimson Text",
|
|
68
|
+
"DM Mono",
|
|
69
|
+
"DM Sans",
|
|
70
|
+
"DM Serif Display",
|
|
71
|
+
"Dancing Script",
|
|
72
|
+
"Domine",
|
|
73
|
+
"Dosis",
|
|
74
|
+
"EB Garamond",
|
|
75
|
+
"Encode Sans",
|
|
76
|
+
"Exo 2",
|
|
77
|
+
"Figtree",
|
|
78
|
+
"Fira Code",
|
|
79
|
+
"Fira Sans",
|
|
80
|
+
"Fira Sans Condensed",
|
|
81
|
+
"Fjalla One",
|
|
82
|
+
"Fredoka",
|
|
83
|
+
"Gelasio",
|
|
84
|
+
"Gloria Hallelujah",
|
|
85
|
+
"Gothic A1",
|
|
86
|
+
"Great Vibes",
|
|
87
|
+
"Heebo",
|
|
88
|
+
"Hind",
|
|
89
|
+
"Hind Siliguri",
|
|
90
|
+
"IBM Plex Mono",
|
|
91
|
+
"IBM Plex Sans",
|
|
92
|
+
"IBM Plex Sans Arabic",
|
|
93
|
+
"IBM Plex Serif",
|
|
94
|
+
"Inconsolata",
|
|
95
|
+
"Indie Flower",
|
|
96
|
+
"Inter",
|
|
97
|
+
"Inter Tight",
|
|
98
|
+
"JetBrains Mono",
|
|
99
|
+
"Josefin Sans",
|
|
100
|
+
"Josefin Slab",
|
|
101
|
+
"Jost",
|
|
102
|
+
"Kalam",
|
|
103
|
+
"Kanit",
|
|
104
|
+
"Karla",
|
|
105
|
+
"Khand",
|
|
106
|
+
"Lato",
|
|
107
|
+
"League Spartan",
|
|
108
|
+
"Lexend",
|
|
109
|
+
"Lexend Deca",
|
|
110
|
+
"Libre Baskerville",
|
|
111
|
+
"Libre Franklin",
|
|
112
|
+
"Lilita One",
|
|
113
|
+
"Lobster",
|
|
114
|
+
"Lobster Two",
|
|
115
|
+
"Lora",
|
|
116
|
+
"M PLUS Rounded 1c",
|
|
117
|
+
"Manrope",
|
|
118
|
+
"Maven Pro",
|
|
119
|
+
"Merriweather",
|
|
120
|
+
"Merriweather Sans",
|
|
121
|
+
"Montserrat",
|
|
122
|
+
"Montserrat Alternates",
|
|
123
|
+
"Mulish",
|
|
124
|
+
"Nanum Gothic",
|
|
125
|
+
"Neuton",
|
|
126
|
+
"Noticia Text",
|
|
127
|
+
"Noto Sans",
|
|
128
|
+
"Noto Sans Arabic",
|
|
129
|
+
"Noto Sans JP",
|
|
130
|
+
"Noto Sans KR",
|
|
131
|
+
"Noto Sans SC",
|
|
132
|
+
"Noto Sans TC",
|
|
133
|
+
"Noto Serif",
|
|
134
|
+
"Nunito",
|
|
135
|
+
"Nunito Sans",
|
|
136
|
+
"Old Standard TT",
|
|
137
|
+
"Onest",
|
|
138
|
+
"Open Sans",
|
|
139
|
+
"Orbitron",
|
|
140
|
+
"Oswald",
|
|
141
|
+
"Outfit",
|
|
142
|
+
"Overpass",
|
|
143
|
+
"Oxygen",
|
|
144
|
+
"PT Sans",
|
|
145
|
+
"PT Sans Narrow",
|
|
146
|
+
"PT Serif",
|
|
147
|
+
"Pacifico",
|
|
148
|
+
"Passion One",
|
|
149
|
+
"Pathway Gothic One",
|
|
150
|
+
"Permanent Marker",
|
|
151
|
+
"Philosopher",
|
|
152
|
+
"Play",
|
|
153
|
+
"Playfair Display",
|
|
154
|
+
"Plus Jakarta Sans",
|
|
155
|
+
"Poppins",
|
|
156
|
+
"Prompt",
|
|
157
|
+
"Public Sans",
|
|
158
|
+
"Quicksand",
|
|
159
|
+
"Rajdhani",
|
|
160
|
+
"Raleway",
|
|
161
|
+
"Recursive",
|
|
162
|
+
"Red Hat Display",
|
|
163
|
+
"Righteous",
|
|
164
|
+
"Roboto",
|
|
165
|
+
"Roboto Condensed",
|
|
166
|
+
"Roboto Flex",
|
|
167
|
+
"Roboto Mono",
|
|
168
|
+
"Roboto Slab",
|
|
169
|
+
"Rokkitt",
|
|
170
|
+
"Rubik",
|
|
171
|
+
"Saira",
|
|
172
|
+
"Satisfy",
|
|
173
|
+
"Sawarabi Mincho",
|
|
174
|
+
"Shadows Into Light",
|
|
175
|
+
"Signika Negative",
|
|
176
|
+
"Slabo 27px",
|
|
177
|
+
"Sora",
|
|
178
|
+
"Source Code Pro",
|
|
179
|
+
"Source Sans 3",
|
|
180
|
+
"Source Serif 4",
|
|
181
|
+
"Space Grotesk",
|
|
182
|
+
"Space Mono",
|
|
183
|
+
"Spectral",
|
|
184
|
+
"Teko",
|
|
185
|
+
"Titillium Web",
|
|
186
|
+
"Ubuntu",
|
|
187
|
+
"Ubuntu Condensed",
|
|
188
|
+
"Ubuntu Mono",
|
|
189
|
+
"Unbounded",
|
|
190
|
+
"Urbanist",
|
|
191
|
+
"Varela Round",
|
|
192
|
+
"Vollkorn",
|
|
193
|
+
"Work Sans",
|
|
194
|
+
"Yanone Kaffeesatz",
|
|
195
|
+
"Yellowtail",
|
|
196
|
+
"Zen Kaku Gothic New",
|
|
197
|
+
"Zilla Slab",
|
|
198
|
+
];
|
|
199
|
+
|
|
200
|
+
/* ── category lookup table ─────────────────────────────────────── */
|
|
201
|
+
|
|
202
|
+
const CATEGORY_MAP: Record<string, string> = {
|
|
203
|
+
ABeeZee: "sans-serif",
|
|
204
|
+
Abel: "sans-serif",
|
|
205
|
+
"Abril Fatface": "display",
|
|
206
|
+
Acme: "sans-serif",
|
|
207
|
+
Alegreya: "serif",
|
|
208
|
+
"Alegreya Sans": "sans-serif",
|
|
209
|
+
"Alfa Slab One": "display",
|
|
210
|
+
"Amatic SC": "handwriting",
|
|
211
|
+
Amiri: "serif",
|
|
212
|
+
"Antic Slab": "serif",
|
|
213
|
+
Anton: "sans-serif",
|
|
214
|
+
Archivo: "sans-serif",
|
|
215
|
+
"Archivo Black": "sans-serif",
|
|
216
|
+
"Archivo Narrow": "sans-serif",
|
|
217
|
+
Arimo: "sans-serif",
|
|
218
|
+
Arvo: "serif",
|
|
219
|
+
Asap: "sans-serif",
|
|
220
|
+
Assistant: "sans-serif",
|
|
221
|
+
Barlow: "sans-serif",
|
|
222
|
+
"Barlow Condensed": "sans-serif",
|
|
223
|
+
"Barlow Semi Condensed": "sans-serif",
|
|
224
|
+
"Be Vietnam Pro": "sans-serif",
|
|
225
|
+
"Bebas Neue": "sans-serif",
|
|
226
|
+
Bitter: "serif",
|
|
227
|
+
"Black Ops One": "display",
|
|
228
|
+
"Bree Serif": "serif",
|
|
229
|
+
Cabin: "sans-serif",
|
|
230
|
+
Cairo: "sans-serif",
|
|
231
|
+
Catamaran: "sans-serif",
|
|
232
|
+
Caveat: "handwriting",
|
|
233
|
+
"Chakra Petch": "sans-serif",
|
|
234
|
+
Cinzel: "serif",
|
|
235
|
+
Comfortaa: "display",
|
|
236
|
+
Commissioner: "sans-serif",
|
|
237
|
+
"Cormorant Garamond": "serif",
|
|
238
|
+
Courgette: "handwriting",
|
|
239
|
+
"Crimson Text": "serif",
|
|
240
|
+
"DM Mono": "monospace",
|
|
241
|
+
"DM Sans": "sans-serif",
|
|
242
|
+
"DM Serif Display": "serif",
|
|
243
|
+
"Dancing Script": "handwriting",
|
|
244
|
+
Domine: "serif",
|
|
245
|
+
Dosis: "sans-serif",
|
|
246
|
+
"EB Garamond": "serif",
|
|
247
|
+
"Encode Sans": "sans-serif",
|
|
248
|
+
"Exo 2": "sans-serif",
|
|
249
|
+
Figtree: "sans-serif",
|
|
250
|
+
"Fira Code": "monospace",
|
|
251
|
+
"Fira Sans": "sans-serif",
|
|
252
|
+
"Fira Sans Condensed": "sans-serif",
|
|
253
|
+
"Fjalla One": "sans-serif",
|
|
254
|
+
Fredoka: "sans-serif",
|
|
255
|
+
Gelasio: "serif",
|
|
256
|
+
"Gloria Hallelujah": "handwriting",
|
|
257
|
+
"Gothic A1": "sans-serif",
|
|
258
|
+
"Great Vibes": "handwriting",
|
|
259
|
+
Heebo: "sans-serif",
|
|
260
|
+
Hind: "sans-serif",
|
|
261
|
+
"Hind Siliguri": "sans-serif",
|
|
262
|
+
"IBM Plex Mono": "monospace",
|
|
263
|
+
"IBM Plex Sans": "sans-serif",
|
|
264
|
+
"IBM Plex Sans Arabic": "sans-serif",
|
|
265
|
+
"IBM Plex Serif": "serif",
|
|
266
|
+
Inconsolata: "monospace",
|
|
267
|
+
"Indie Flower": "handwriting",
|
|
268
|
+
Inter: "sans-serif",
|
|
269
|
+
"Inter Tight": "sans-serif",
|
|
270
|
+
"JetBrains Mono": "monospace",
|
|
271
|
+
"Josefin Sans": "sans-serif",
|
|
272
|
+
"Josefin Slab": "serif",
|
|
273
|
+
Jost: "sans-serif",
|
|
274
|
+
Kalam: "handwriting",
|
|
275
|
+
Kanit: "sans-serif",
|
|
276
|
+
Karla: "sans-serif",
|
|
277
|
+
Khand: "sans-serif",
|
|
278
|
+
Lato: "sans-serif",
|
|
279
|
+
"League Spartan": "sans-serif",
|
|
280
|
+
Lexend: "sans-serif",
|
|
281
|
+
"Lexend Deca": "sans-serif",
|
|
282
|
+
"Libre Baskerville": "serif",
|
|
283
|
+
"Libre Franklin": "sans-serif",
|
|
284
|
+
"Lilita One": "sans-serif",
|
|
285
|
+
Lobster: "display",
|
|
286
|
+
"Lobster Two": "display",
|
|
287
|
+
Lora: "serif",
|
|
288
|
+
"M PLUS Rounded 1c": "sans-serif",
|
|
289
|
+
Manrope: "sans-serif",
|
|
290
|
+
"Maven Pro": "sans-serif",
|
|
291
|
+
Merriweather: "serif",
|
|
292
|
+
"Merriweather Sans": "sans-serif",
|
|
293
|
+
Montserrat: "sans-serif",
|
|
294
|
+
"Montserrat Alternates": "sans-serif",
|
|
295
|
+
Mulish: "sans-serif",
|
|
296
|
+
"Nanum Gothic": "sans-serif",
|
|
297
|
+
Neuton: "serif",
|
|
298
|
+
"Noticia Text": "serif",
|
|
299
|
+
"Noto Sans": "sans-serif",
|
|
300
|
+
"Noto Sans Arabic": "sans-serif",
|
|
301
|
+
"Noto Sans JP": "sans-serif",
|
|
302
|
+
"Noto Sans KR": "sans-serif",
|
|
303
|
+
"Noto Sans SC": "sans-serif",
|
|
304
|
+
"Noto Sans TC": "sans-serif",
|
|
305
|
+
"Noto Serif": "serif",
|
|
306
|
+
Nunito: "sans-serif",
|
|
307
|
+
"Nunito Sans": "sans-serif",
|
|
308
|
+
"Old Standard TT": "serif",
|
|
309
|
+
Onest: "sans-serif",
|
|
310
|
+
"Open Sans": "sans-serif",
|
|
311
|
+
Orbitron: "sans-serif",
|
|
312
|
+
Oswald: "sans-serif",
|
|
313
|
+
Outfit: "sans-serif",
|
|
314
|
+
Overpass: "sans-serif",
|
|
315
|
+
Oxygen: "sans-serif",
|
|
316
|
+
"PT Sans": "sans-serif",
|
|
317
|
+
"PT Sans Narrow": "sans-serif",
|
|
318
|
+
"PT Serif": "serif",
|
|
319
|
+
Pacifico: "handwriting",
|
|
320
|
+
"Passion One": "display",
|
|
321
|
+
"Pathway Gothic One": "sans-serif",
|
|
322
|
+
"Permanent Marker": "handwriting",
|
|
323
|
+
Philosopher: "sans-serif",
|
|
324
|
+
Play: "sans-serif",
|
|
325
|
+
"Playfair Display": "serif",
|
|
326
|
+
"Plus Jakarta Sans": "sans-serif",
|
|
327
|
+
Poppins: "sans-serif",
|
|
328
|
+
Prompt: "sans-serif",
|
|
329
|
+
"Public Sans": "sans-serif",
|
|
330
|
+
Quicksand: "sans-serif",
|
|
331
|
+
Rajdhani: "sans-serif",
|
|
332
|
+
Raleway: "sans-serif",
|
|
333
|
+
Recursive: "sans-serif",
|
|
334
|
+
"Red Hat Display": "sans-serif",
|
|
335
|
+
Righteous: "display",
|
|
336
|
+
Roboto: "sans-serif",
|
|
337
|
+
"Roboto Condensed": "sans-serif",
|
|
338
|
+
"Roboto Flex": "sans-serif",
|
|
339
|
+
"Roboto Mono": "monospace",
|
|
340
|
+
"Roboto Slab": "serif",
|
|
341
|
+
Rokkitt: "serif",
|
|
342
|
+
Rubik: "sans-serif",
|
|
343
|
+
Saira: "sans-serif",
|
|
344
|
+
Satisfy: "handwriting",
|
|
345
|
+
"Sawarabi Mincho": "serif",
|
|
346
|
+
"Shadows Into Light": "handwriting",
|
|
347
|
+
"Signika Negative": "sans-serif",
|
|
348
|
+
"Slabo 27px": "serif",
|
|
349
|
+
Sora: "sans-serif",
|
|
350
|
+
"Source Code Pro": "monospace",
|
|
351
|
+
"Source Sans 3": "sans-serif",
|
|
352
|
+
"Source Serif 4": "serif",
|
|
353
|
+
"Space Grotesk": "sans-serif",
|
|
354
|
+
"Space Mono": "monospace",
|
|
355
|
+
Spectral: "serif",
|
|
356
|
+
Teko: "sans-serif",
|
|
357
|
+
"Titillium Web": "sans-serif",
|
|
358
|
+
Ubuntu: "sans-serif",
|
|
359
|
+
"Ubuntu Condensed": "sans-serif",
|
|
360
|
+
"Ubuntu Mono": "monospace",
|
|
361
|
+
Unbounded: "sans-serif",
|
|
362
|
+
Urbanist: "sans-serif",
|
|
363
|
+
"Varela Round": "sans-serif",
|
|
364
|
+
Vollkorn: "serif",
|
|
365
|
+
"Work Sans": "sans-serif",
|
|
366
|
+
"Yanone Kaffeesatz": "sans-serif",
|
|
367
|
+
Yellowtail: "handwriting",
|
|
368
|
+
"Zen Kaku Gothic New": "sans-serif",
|
|
369
|
+
"Zilla Slab": "serif",
|
|
370
|
+
};
|
|
371
|
+
|
|
372
|
+
/* ── fallback font for category ────────────────────────────────── */
|
|
373
|
+
|
|
374
|
+
const CATEGORY_FALLBACK: Record<string, string> = {
|
|
375
|
+
display: "Arial",
|
|
376
|
+
handwriting: "Arial",
|
|
377
|
+
monospace: "Courier New",
|
|
378
|
+
"sans-serif": "Arial",
|
|
379
|
+
serif: "Times New Roman",
|
|
380
|
+
};
|
|
381
|
+
|
|
382
|
+
/* ── capsizecss metrics loader ─────────────────────────────────── */
|
|
383
|
+
|
|
384
|
+
/* @capsizecss/metrics provides pre-calculated metrics for many fonts */
|
|
385
|
+
async function getFallbackMetrics(
|
|
386
|
+
family: string,
|
|
387
|
+
category: string,
|
|
388
|
+
): Promise<
|
|
389
|
+
| {
|
|
390
|
+
ascentOverride: string;
|
|
391
|
+
descentOverride: string;
|
|
392
|
+
fallbackFont: string;
|
|
393
|
+
lineGapOverride: string;
|
|
394
|
+
sizeAdjust: string;
|
|
395
|
+
}
|
|
396
|
+
| undefined
|
|
397
|
+
> {
|
|
398
|
+
const noSpaces = family.replace(/ /g, "");
|
|
399
|
+
const slug = noSpaces[0].toLowerCase() + noSpaces.slice(1);
|
|
400
|
+
try {
|
|
401
|
+
const mod = await import(`@capsizecss/metrics/${slug}`);
|
|
402
|
+
const metrics = mod.default ?? mod;
|
|
403
|
+
|
|
404
|
+
const fallbackFont = CATEGORY_FALLBACK[category] ?? "Arial";
|
|
405
|
+
let fallbackMetrics: {
|
|
406
|
+
ascent: number;
|
|
407
|
+
capHeight: number;
|
|
408
|
+
descent: number;
|
|
409
|
+
lineGap: number;
|
|
410
|
+
unitsPerEm: number;
|
|
411
|
+
};
|
|
412
|
+
|
|
413
|
+
try {
|
|
414
|
+
const fbNoSpaces = fallbackFont.replace(/ /g, "");
|
|
415
|
+
const fallbackSlug = fbNoSpaces[0].toLowerCase() + fbNoSpaces.slice(1);
|
|
416
|
+
const fallbackMod = await import(`@capsizecss/metrics/${fallbackSlug}`);
|
|
417
|
+
fallbackMetrics = fallbackMod.default ?? fallbackMod;
|
|
418
|
+
} catch {
|
|
419
|
+
return undefined;
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
/* calculate size-adjust and override values */
|
|
423
|
+
const sizeAdjust =
|
|
424
|
+
((metrics.unitsPerEm / fallbackMetrics.unitsPerEm) *
|
|
425
|
+
(fallbackMetrics.ascent - fallbackMetrics.descent + fallbackMetrics.lineGap)) /
|
|
426
|
+
(metrics.ascent - metrics.descent + metrics.lineGap);
|
|
427
|
+
|
|
428
|
+
const ascentOverride = metrics.ascent / (metrics.unitsPerEm * sizeAdjust);
|
|
429
|
+
const descentOverride = Math.abs(metrics.descent) / (metrics.unitsPerEm * sizeAdjust);
|
|
430
|
+
const lineGapOverride = metrics.lineGap / (metrics.unitsPerEm * sizeAdjust);
|
|
431
|
+
|
|
432
|
+
return {
|
|
433
|
+
ascentOverride: `${(ascentOverride * 100).toFixed(2)}%`,
|
|
434
|
+
descentOverride: `${(descentOverride * 100).toFixed(2)}%`,
|
|
435
|
+
fallbackFont,
|
|
436
|
+
lineGapOverride: `${(lineGapOverride * 100).toFixed(2)}%`,
|
|
437
|
+
sizeAdjust: `${(sizeAdjust * 100).toFixed(2)}%`,
|
|
438
|
+
};
|
|
439
|
+
} catch {
|
|
440
|
+
return undefined;
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
/* ── Google Fonts CSS parser ───────────────────────────────────── */
|
|
445
|
+
|
|
446
|
+
interface ParsedEntry {
|
|
447
|
+
family: string;
|
|
448
|
+
style: "italic" | "normal";
|
|
449
|
+
subset: string;
|
|
450
|
+
unicodeRange: string;
|
|
451
|
+
url: string;
|
|
452
|
+
weight: string;
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
function parseCss(css: string, family: string): ParsedEntry[] {
|
|
456
|
+
const entries: ParsedEntry[] = [];
|
|
457
|
+
|
|
458
|
+
/* match each @font-face block, preceded by a subset comment */
|
|
459
|
+
const blockRe = /\/\*\s*([a-zA-Z0-9-]+)\s*\*\/\s*@font-face\s*\{([^}]+)\}/g;
|
|
460
|
+
let match: RegExpExecArray | null;
|
|
461
|
+
|
|
462
|
+
while ((match = blockRe.exec(css)) !== null) {
|
|
463
|
+
const subset = match[1] ?? "";
|
|
464
|
+
const body = match[2] ?? "";
|
|
465
|
+
|
|
466
|
+
const styleMatch = /font-style:\s*(italic|normal)/.exec(body);
|
|
467
|
+
const weightMatch = /font-weight:\s*([\d\s]+)/.exec(body);
|
|
468
|
+
const srcMatch = /url\(([^)]+)\)\s*format\(['"]woff2['"]\)/.exec(body);
|
|
469
|
+
const rangeMatch = /unicode-range:\s*([^;]+)/.exec(body);
|
|
470
|
+
|
|
471
|
+
if (srcMatch && rangeMatch) {
|
|
472
|
+
entries.push({
|
|
473
|
+
family,
|
|
474
|
+
style: (styleMatch?.[1] as "italic" | "normal") ?? "normal",
|
|
475
|
+
subset,
|
|
476
|
+
unicodeRange: rangeMatch[1]?.trim() ?? "",
|
|
477
|
+
url: srcMatch[1]?.trim() ?? "",
|
|
478
|
+
weight: weightMatch?.[1]?.trim() ?? "400",
|
|
479
|
+
});
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
return entries;
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
async function fetchGoogleFontCss(family: string, variable: boolean): Promise<string | undefined> {
|
|
487
|
+
const encodedFamily = family.replace(/\s+/g, "+");
|
|
488
|
+
const spec = variable
|
|
489
|
+
? `${encodedFamily}:ital,wght@0,100..900;1,100..900`
|
|
490
|
+
: `${encodedFamily}:ital,wght@0,400;0,700;1,400;1,700`;
|
|
491
|
+
|
|
492
|
+
const url = `https://fonts.googleapis.com/css2?family=${spec}&display=swap`;
|
|
493
|
+
|
|
494
|
+
/* woff2 user-agent trick */
|
|
495
|
+
const res = await fetch(url, {
|
|
496
|
+
headers: {
|
|
497
|
+
"User-Agent":
|
|
498
|
+
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
|
|
499
|
+
},
|
|
500
|
+
});
|
|
501
|
+
|
|
502
|
+
if (!res.ok) return undefined;
|
|
503
|
+
return res.text();
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
/* ── slug helpers ──────────────────────────────────────────────── */
|
|
507
|
+
|
|
508
|
+
function toSlug(family: string): string {
|
|
509
|
+
return family.toLowerCase().replace(/\s+/g, "-");
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
function toCamelCase(family: string): string {
|
|
513
|
+
return family
|
|
514
|
+
.replace(/[^a-zA-Z0-9\s]/g, "")
|
|
515
|
+
.split(/\s+/)
|
|
516
|
+
.map((word, i) => {
|
|
517
|
+
const lower = word.toLowerCase();
|
|
518
|
+
if (i === 0) return lower;
|
|
519
|
+
return lower[0]?.toUpperCase() + lower.slice(1);
|
|
520
|
+
})
|
|
521
|
+
.join("");
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
/* ── output generation ─────────────────────────────────────────── */
|
|
525
|
+
|
|
526
|
+
interface FontGenData {
|
|
527
|
+
camelName: string;
|
|
528
|
+
category: string;
|
|
529
|
+
fallbackMetrics?: {
|
|
530
|
+
ascentOverride: string;
|
|
531
|
+
descentOverride: string;
|
|
532
|
+
fallbackFont: string;
|
|
533
|
+
lineGapOverride: string;
|
|
534
|
+
sizeAdjust: string;
|
|
535
|
+
};
|
|
536
|
+
family: string;
|
|
537
|
+
slug: string;
|
|
538
|
+
subsetEntries: Array<{
|
|
539
|
+
style: "italic" | "normal";
|
|
540
|
+
subset: string;
|
|
541
|
+
unicodeRange: string;
|
|
542
|
+
weight: string;
|
|
543
|
+
}>;
|
|
544
|
+
subsets: string[];
|
|
545
|
+
weights: number[] | string;
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
function generatePerFontFile(font: FontGenData): string {
|
|
549
|
+
const subsetsType = font.subsets.map((s) => `"${s}"`).join(" | ");
|
|
550
|
+
const slug = font.slug;
|
|
551
|
+
|
|
552
|
+
const entriesStr = font.subsetEntries
|
|
553
|
+
.map(
|
|
554
|
+
(e) =>
|
|
555
|
+
` { style: "${e.style}", subset: "${e.subset}", ` +
|
|
556
|
+
`unicodeRange: "${e.unicodeRange}", ` +
|
|
557
|
+
`url: "/fonts/${slug}/${buildFileName(e)}", ` +
|
|
558
|
+
`weight: ${JSON.stringify(e.weight)} }`,
|
|
559
|
+
)
|
|
560
|
+
.join(",\n");
|
|
561
|
+
|
|
562
|
+
const metricsStr = font.fallbackMetrics
|
|
563
|
+
? ` fallbackMetrics: ${JSON.stringify(font.fallbackMetrics, null, 4).replace(/\n/g, "\n ")},\n`
|
|
564
|
+
: "";
|
|
565
|
+
|
|
566
|
+
const weightsStr = typeof font.weights === "string" ? `"${font.weights}"` : JSON.stringify(font.weights);
|
|
567
|
+
|
|
568
|
+
return `import { createRegistryFont } from "./create-registry-font"
|
|
569
|
+
import type { Font } from "./types"
|
|
570
|
+
|
|
571
|
+
export const ${font.camelName}: Font<${subsetsType}> = createRegistryFont({
|
|
572
|
+
category: "${font.category}",
|
|
573
|
+
${metricsStr} family: "${font.family}",
|
|
574
|
+
subsetEntries: [
|
|
575
|
+
${entriesStr},
|
|
576
|
+
],
|
|
577
|
+
subsets: [${font.subsets.map((s) => `"${s}"`).join(", ")}],
|
|
578
|
+
weights: ${weightsStr},
|
|
579
|
+
})
|
|
580
|
+
`;
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
function buildFileName(entry: { style: "italic" | "normal"; subset: string; weight: string }): string {
|
|
584
|
+
const parts = [entry.subset];
|
|
585
|
+
if (entry.style === "italic") parts.push("i");
|
|
586
|
+
|
|
587
|
+
/* static weight: append weight number */
|
|
588
|
+
if (/^\d+$/.test(entry.weight)) {
|
|
589
|
+
parts.push(entry.weight);
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
return `${parts.join("-")}.woff2`;
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
/* ── main pipeline ─────────────────────────────────────────────── */
|
|
596
|
+
|
|
597
|
+
async function main(): Promise<void> {
|
|
598
|
+
const fontsDir = join(import.meta.dirname ?? ".", "..", "src", "fonts");
|
|
599
|
+
|
|
600
|
+
if (!existsSync(fontsDir)) {
|
|
601
|
+
mkdirSync(fontsDir, { recursive: true });
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
const allFonts: FontGenData[] = [];
|
|
605
|
+
const errors: string[] = [];
|
|
606
|
+
|
|
607
|
+
for (const family of FONTS) {
|
|
608
|
+
process.stdout.write(`Processing ${family}... `);
|
|
609
|
+
|
|
610
|
+
/* try variable first, fall back to static */
|
|
611
|
+
let css = await fetchGoogleFontCss(family, true);
|
|
612
|
+
let isVariable = true;
|
|
613
|
+
|
|
614
|
+
if (!css || css.trim().length < 50) {
|
|
615
|
+
css = await fetchGoogleFontCss(family, false);
|
|
616
|
+
isVariable = false;
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
if (!css) {
|
|
620
|
+
errors.push(family);
|
|
621
|
+
console.log("FAILED (no CSS)");
|
|
622
|
+
continue;
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
const entries = parseCss(css, family);
|
|
626
|
+
if (entries.length === 0) {
|
|
627
|
+
errors.push(family);
|
|
628
|
+
console.log("FAILED (no entries parsed)");
|
|
629
|
+
continue;
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
/* determine subsets + weights */
|
|
633
|
+
const subsets = [...new Set(entries.map((e) => e.subset))].sort();
|
|
634
|
+
|
|
635
|
+
let weights: number[] | string;
|
|
636
|
+
if (isVariable) {
|
|
637
|
+
/* use the range from the first entry */
|
|
638
|
+
const first = entries[0];
|
|
639
|
+
weights = first?.weight ?? "100 900";
|
|
640
|
+
} else {
|
|
641
|
+
weights = [...new Set(entries.map((e) => Number.parseInt(e.weight, 10)))].sort((a, b) => a - b);
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
/* deduplicate subset entries (same subset + style + weight = one entry) */
|
|
645
|
+
const seen = new Set<string>();
|
|
646
|
+
const deduped = entries.filter((e) => {
|
|
647
|
+
const key = `${e.subset}:${e.style}:${e.weight}`;
|
|
648
|
+
if (seen.has(key)) return false;
|
|
649
|
+
seen.add(key);
|
|
650
|
+
return true;
|
|
651
|
+
});
|
|
652
|
+
|
|
653
|
+
const category = CATEGORY_MAP[family] ?? "sans-serif";
|
|
654
|
+
const fallbackMetrics = await getFallbackMetrics(family, category);
|
|
655
|
+
|
|
656
|
+
const fontData: FontGenData = {
|
|
657
|
+
camelName: toCamelCase(family),
|
|
658
|
+
category,
|
|
659
|
+
fallbackMetrics,
|
|
660
|
+
family,
|
|
661
|
+
slug: toSlug(family),
|
|
662
|
+
subsetEntries: deduped.map((e) => ({
|
|
663
|
+
style: e.style,
|
|
664
|
+
subset: e.subset,
|
|
665
|
+
unicodeRange: e.unicodeRange,
|
|
666
|
+
weight: e.weight,
|
|
667
|
+
})),
|
|
668
|
+
subsets,
|
|
669
|
+
weights,
|
|
670
|
+
};
|
|
671
|
+
|
|
672
|
+
allFonts.push(fontData);
|
|
673
|
+
console.log(`OK (${subsets.length} subsets, ${deduped.length} entries)`);
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
/* generate per-font files */
|
|
677
|
+
for (const font of allFonts) {
|
|
678
|
+
const content = generatePerFontFile(font);
|
|
679
|
+
writeFileSync(join(fontsDir, `${font.slug}.ts`), content);
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
/* generate registry.gen.ts */
|
|
683
|
+
const registryEntries = allFonts
|
|
684
|
+
.map((f) => ` ["${f.family}", "${f.slug}", "${f.camelName}", "${f.category}"]`)
|
|
685
|
+
.join(",\n");
|
|
686
|
+
|
|
687
|
+
writeGenFile(
|
|
688
|
+
join(fontsDir, "registry.gen.ts"),
|
|
689
|
+
`\nexport const FONT_REGISTRY: Array<[family: string, slug: string, camelName: string, category: string]> = [\n${registryEntries},\n]\n`,
|
|
690
|
+
"flare/fonts",
|
|
691
|
+
);
|
|
692
|
+
|
|
693
|
+
/* generate unicode-ranges.gen.ts */
|
|
694
|
+
const allRanges = new Map<string, string>();
|
|
695
|
+
for (const font of allFonts) {
|
|
696
|
+
for (const entry of font.subsetEntries) {
|
|
697
|
+
if (!allRanges.has(entry.subset)) {
|
|
698
|
+
allRanges.set(entry.subset, entry.unicodeRange);
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
const rangeEntries = [...allRanges.entries()]
|
|
704
|
+
.sort(([a], [b]) => a.localeCompare(b))
|
|
705
|
+
.map(([subset, range]) => ` "${subset}": "${range}"`)
|
|
706
|
+
.join(",\n");
|
|
707
|
+
|
|
708
|
+
writeGenFile(
|
|
709
|
+
join(fontsDir, "unicode-ranges.gen.ts"),
|
|
710
|
+
`\nexport const UNICODE_RANGES: Record<string, string> = {\n${rangeEntries},\n}\n`,
|
|
711
|
+
"flare/fonts",
|
|
712
|
+
);
|
|
713
|
+
|
|
714
|
+
/* summary */
|
|
715
|
+
console.log(`\nGenerated ${allFonts.length} fonts`);
|
|
716
|
+
if (errors.length > 0) {
|
|
717
|
+
console.log(`Failed: ${errors.join(", ")}`);
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
main().catch(console.error);
|