fix(cli): keep font localizer process ownership explicit

This commit is contained in:
Miguel Ángel
2026-08-26 01:11:38 +00:00
parent 38f8f9250a
commit ec68d40cc6
3 changed files with 39 additions and 48 deletions
@@ -7,5 +7,6 @@ if (error) {
console.error(error); console.error(error);
process.exitCode = 1; process.exitCode = 1;
} else { } else {
await import("../dist/fontLocalizeCli.js"); const { main } = await import("../dist/fontLocalizeCli.js");
process.exitCode = await main();
} }
+16 -12
View File
@@ -1,3 +1,4 @@
// fallow-ignore-file unused-file
import { injectDeterministicFontFaces } from "@hyperframes/producer"; import { injectDeterministicFontFaces } from "@hyperframes/producer";
import { runFontLocalize } from "./fontLocalize.js"; import { runFontLocalize } from "./fontLocalize.js";
@@ -9,15 +10,18 @@ async function readStdin(): Promise<string> {
return Buffer.concat(chunks).toString("utf8"); return Buffer.concat(chunks).toString("utf8");
} }
process.exitCode = await runFontLocalize( /** Standalone-entry main; the bin wrapper owns the actual process exit code. */
{ export async function main(): Promise<number> {
readInput: readStdin, return runFontLocalize(
writeOutput: (value) => process.stdout.write(value), {
writeError: (value) => process.stderr.write(value), readInput: readStdin,
}, writeOutput: (value) => process.stdout.write(value),
(html) => writeError: (value) => process.stderr.write(value),
injectDeterministicFontFaces(html, { },
failClosedFontFetch: true, (html) =>
allowSystemFontCapture: false, injectDeterministicFontFaces(html, {
}), failClosedFontFetch: true,
); allowSystemFontCapture: false,
}),
);
}
@@ -1,22 +1,28 @@
import { describe, expect, it } from "bun:test"; import { describe, expect, it } from "bun:test";
import { injectDeterministicFontFaces } from "./deterministicFonts.js"; import { injectDeterministicFontFaces } from "./deterministicFonts.js";
async function requestedGoogleFontUrl(html: string): Promise<URL> {
let requestedUrl = "";
const fetchImpl = (async (input: unknown) => {
requestedUrl = String(input);
return new Response("", { status: 400 });
}) as unknown as typeof fetch;
await injectDeterministicFontFaces(html, {
fetchImpl,
allowSystemFontCapture: false,
});
return new URL(requestedUrl);
}
describe("Google Fonts text subsetting", () => { describe("Google Fonts text subsetting", () => {
it("sends the composition character set to the CSS API", async () => { it("sends the composition character set to the CSS API", async () => {
let requestedUrl = ""; const url = await requestedGoogleFontUrl(
const fetchImpl = (async (input: unknown) => {
requestedUrl = String(input);
return new Response("", { status: 400 });
}) as unknown as typeof fetch;
await injectDeterministicFontFaces(
`<!doctype html><html><head><style> `<!doctype html><html><head><style>
h1 { font-family: "Noto Performance Test", sans-serif; } h1 { font-family: "Noto Performance Test", sans-serif; }
</style></head><body><h1>旅行ランキング</h1></body></html>`, </style></head><body><h1>旅行ランキング</h1></body></html>`,
{ fetchImpl, allowSystemFontCapture: false },
); );
const url = new URL(requestedUrl);
const text = url.searchParams.get("text") ?? ""; const text = url.searchParams.get("text") ?? "";
for (const character of new Set("旅行ランキング")) { for (const character of new Set("旅行ランキング")) {
expect(text).toContain(character); expect(text).toContain(character);
@@ -24,48 +30,29 @@ describe("Google Fonts text subsetting", () => {
}); });
it("includes decoded HTML entities from visible composition text", async () => { it("includes decoded HTML entities from visible composition text", async () => {
let requestedUrl = ""; const url = await requestedGoogleFontUrl(
const fetchImpl = (async (input: unknown) => {
requestedUrl = String(input);
return new Response("", { status: 400 });
}) as unknown as typeof fetch;
await injectDeterministicFontFaces(
`<!doctype html><html><head><style> `<!doctype html><html><head><style>
h1 { font-family: "Noto Performance Test", sans-serif; } h1 { font-family: "Noto Performance Test", sans-serif; }
</style></head><body><h1>&#x65C5;&#34892;</h1></body></html>`, </style></head><body><h1>&#x65C5;&#34892;</h1></body></html>`,
{ fetchImpl, allowSystemFontCapture: false },
); );
expect(new URL(requestedUrl).searchParams.get("text")).toContain("旅行"); expect(url.searchParams.get("text")).toContain("旅行");
}); });
it("includes case variants for transformed supplemental alias weights", async () => { it("includes case variants for transformed supplemental alias weights", async () => {
let requestedUrl = ""; const url = await requestedGoogleFontUrl(
const fetchImpl = (async (input: unknown) => {
requestedUrl = String(input);
return new Response("", { status: 400 });
}) as unknown as typeof fetch;
await injectDeterministicFontFaces(
`<!doctype html><html><head><style> `<!doctype html><html><head><style>
h1 { font-family: "Inter", sans-serif; font-weight: 800; text-transform: uppercase; } h1 { font-family: "Inter", sans-serif; font-weight: 800; text-transform: uppercase; }
</style></head><body><h1>Your Kidney Transplant:<br/>What Happens Next</h1></body></html>`, </style></head><body><h1>Your Kidney Transplant:<br/>What Happens Next</h1></body></html>`,
{ fetchImpl, allowSystemFontCapture: false },
); );
const text = new URL(requestedUrl).searchParams.get("text") ?? ""; const text = url.searchParams.get("text") ?? "";
for (const character of new Set("YOUR KIDNEY TRANSPLANT:WHAT HAPPENS NEXT")) { for (const character of new Set("YOUR KIDNEY TRANSPLANT:WHAT HAPPENS NEXT")) {
expect(text).toContain(character); expect(text).toContain(character);
} }
}); });
it("falls back to the full font when case closure exceeds the text URL budget", async () => { it("falls back to the full font when case closure exceeds the text URL budget", async () => {
let requestedUrl = "";
const fetchImpl = (async (input: unknown) => {
requestedUrl = String(input);
return new Response("", { status: 400 });
}) as unknown as typeof fetch;
const caseChangingCharacters = Array.from({ length: 0x500 }, (_, index) => const caseChangingCharacters = Array.from({ length: 0x500 }, (_, index) =>
String.fromCodePoint(index), String.fromCodePoint(index),
) )
@@ -73,13 +60,12 @@ describe("Google Fonts text subsetting", () => {
.slice(0, 300) .slice(0, 300)
.join(""); .join("");
await injectDeterministicFontFaces( const url = await requestedGoogleFontUrl(
`<!doctype html><html><head><style> `<!doctype html><html><head><style>
p { font-family: "Inter", sans-serif; font-weight: 800; text-transform: uppercase; } p { font-family: "Inter", sans-serif; font-weight: 800; text-transform: uppercase; }
</style></head><body><p>${caseChangingCharacters}</p></body></html>`, </style></head><body><p>${caseChangingCharacters}</p></body></html>`,
{ fetchImpl, allowSystemFontCapture: false },
); );
expect(new URL(requestedUrl).searchParams.has("text")).toBe(false); expect(url.searchParams.has("text")).toBe(false);
}); });
}); });