From 9940503102c1f26e2ef8f43e75c013b555883696 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miguel=20=C3=81ngel?= Date: Mon, 13 Jul 2026 02:01:28 -0400 Subject: [PATCH] fix(producer): dedupe local font embedding by resolved path (#2317) --- .../src/services/htmlCompiler.test.ts | 35 +++++++++++++++++++ .../producer/src/services/htmlCompiler.ts | 21 ++++++----- 2 files changed, 48 insertions(+), 8 deletions(-) diff --git a/packages/producer/src/services/htmlCompiler.test.ts b/packages/producer/src/services/htmlCompiler.test.ts index 55dcb4751..7253af9ff 100644 --- a/packages/producer/src/services/htmlCompiler.test.ts +++ b/packages/producer/src/services/htmlCompiler.test.ts @@ -4,6 +4,7 @@ import { mkdtempSync, writeFileSync, mkdirSync } from "node:fs"; import { tmpdir } from "node:os"; import { join } from "node:path"; import { parseHTML } from "linkedom"; +import { defaultLogger } from "../logger.js"; import { collectExternalAssets, compileForRender, @@ -858,6 +859,40 @@ describe("system-primary font normalization", () => { }); }); +describe("local font embedding", () => { + it("embeds one font file once when sub-compositions use equivalent relative paths", async () => { + const projectDir = mkdtempSync(join(tmpdir(), "hf-local-font-dedupe-")); + const assetsDir = join(projectDir, "assets"); + mkdirSync(assetsDir, { recursive: true }); + writeFileSync(join(assetsDir, "shared.woff2"), "fake-woff2"); + writeFileSync( + join(projectDir, "index.html"), + ` + +
Text
+`, + ); + + const originalInfo = defaultLogger.info; + const embeddedMessages: string[] = []; + defaultLogger.info = (message) => { + if (message.includes("Embedded local font file")) embeddedMessages.push(message); + }; + + try { + await compileForRender(projectDir, join(projectDir, "index.html"), projectDir); + } finally { + defaultLogger.info = originalInfo; + } + + expect(embeddedMessages).toHaveLength(1); + }); +}); + describe("template-wrapped sub-composition media offsets", () => { function writeTemplateWrappedProject( hostAttrs: string, diff --git a/packages/producer/src/services/htmlCompiler.ts b/packages/producer/src/services/htmlCompiler.ts index 65d20b145..c9b52dd38 100644 --- a/packages/producer/src/services/htmlCompiler.ts +++ b/packages/producer/src/services/htmlCompiler.ts @@ -1605,7 +1605,8 @@ async function embedLocalFontFaces(html: string, projectDir: string): Promise]*>([\s\S]*?)<\/style>/gi; const fontFaceRe = /@font-face\s*\{([^}]*)\}/gi; let result = html; - const embedded = new Set(); + const embeddedPaths = new Set(); + const dataUriByAbsolutePath = new Map(); let styleMatch: RegExpExecArray | null; while ((styleMatch = styleBlockRe.exec(html)) !== null) { @@ -1618,19 +1619,23 @@ async function embedLocalFontFaces(html: string, projectDir: string): Promise