Files
cloudflare__vinext/scripts/generate-google-fonts.js
T
Steve Faulkner 7325f0fc7d feat(font): replace generated google font catalog with virtual import rewrites (#656)
Reworks the font-catalog removal from #262 using per-import virtual modules so named imports keep normal ESM semantics, aliased/default calls still self-host in build mode, and dependency imports are transformed too.

Credit to @yunus25jmi1 for the original PR and direction in #262.
2026-03-22 14:06:29 -05:00

120 lines
3.3 KiB
JavaScript

#!/usr/bin/env node
const fs = require("node:fs");
const path = require("node:path");
const METADATA_URL = "https://fonts.google.com/metadata/fonts";
function toExportName(family) {
return family
.replace(/[^0-9A-Za-z]+/g, "_")
.replace(/^_+|_+$/g, "")
.replace(/_+/g, "_");
}
function assertValidExports(names) {
const invalid = names.filter((name) => !/^[A-Za-z_$][0-9A-Za-z_$]*$/.test(name));
if (invalid.length > 0) {
throw new Error(`Invalid export identifiers: ${invalid.slice(0, 10).join(", ")}`);
}
const seen = new Set();
const dupes = new Set();
for (const name of names) {
if (seen.has(name)) dupes.add(name);
seen.add(name);
}
if (dupes.size > 0) {
throw new Error(`Duplicate export identifiers: ${Array.from(dupes).slice(0, 10).join(", ")}`);
}
}
function stripXssiPrefix(text) {
if (text.startsWith(")]}'")) {
return text.replace(/^\)\]\}'\n?/, "");
}
return text;
}
async function loadMetadata(metadataPath) {
if (metadataPath) {
const text = fs.readFileSync(metadataPath, "utf-8");
return JSON.parse(stripXssiPrefix(text));
}
const res = await fetch(METADATA_URL);
if (!res.ok) {
throw new Error(`Failed to fetch metadata: ${res.status} ${res.statusText}`);
}
const text = await res.text();
return JSON.parse(stripXssiPrefix(text));
}
function writeFixture(families) {
const fixturePath = path.join(process.cwd(), "tests/fixtures/google-fonts.json");
const data = {
source: METADATA_URL,
generatedAt: new Date().toISOString().slice(0, 10),
count: families.length,
families,
};
fs.mkdirSync(path.dirname(fixturePath), { recursive: true });
fs.writeFileSync(fixturePath, JSON.stringify(data, null, 2) + "\n");
}
function writeGeneratedDts(exportNames) {
const outPath = path.join(
process.cwd(),
"packages/vinext/src/shims/next-shims-font-google.generated.d.ts",
);
const lines = [];
lines.push("// Generated by scripts/generate-google-fonts.js");
lines.push(`// Source: ${METADATA_URL}`);
lines.push("// @generated");
lines.push('declare module "next/font/google" {');
lines.push(" export function createFontLoader(family: string): FontLoader;");
for (const name of exportNames) {
lines.push(` export const ${name}: FontLoader;`);
}
lines.push("}");
lines.push("");
fs.writeFileSync(outPath, lines.join("\n"));
}
async function main() {
const args = process.argv.slice(2);
let metadataPath;
for (let i = 0; i < args.length; i += 1) {
if (args[i] === "--metadata-path") {
metadataPath = args[i + 1];
i += 1;
}
}
if (!metadataPath && process.env.GOOGLE_FONTS_METADATA_PATH) {
metadataPath = process.env.GOOGLE_FONTS_METADATA_PATH;
}
const metadata = await loadMetadata(metadataPath);
const families = metadata.familyMetadataList?.map((f) => f.family) ?? [];
if (families.length === 0) {
throw new Error("No font families found in metadata");
}
const entries = families.map((family) => ({
family,
exportName: toExportName(family),
}));
entries.sort((a, b) => a.exportName.localeCompare(b.exportName));
const exportNames = entries.map((e) => e.exportName);
assertValidExports(exportNames);
writeFixture(families);
writeGeneratedDts(exportNames);
console.log(`Generated ${entries.length} fonts`);
}
main().catch((err) => {
console.error(err);
process.exit(1);
});