diff --git a/packages/cli/src/utils/bundleWithLocalizedFonts.test.ts b/packages/cli/src/utils/bundleWithLocalizedFonts.test.ts index fb7e7a751..1d9300b40 100644 --- a/packages/cli/src/utils/bundleWithLocalizedFonts.test.ts +++ b/packages/cli/src/utils/bundleWithLocalizedFonts.test.ts @@ -1,9 +1,19 @@ import { afterEach, describe, expect, it, vi } from "vitest"; +const producerMocks = vi.hoisted(() => ({ + loadProducer: vi.fn(async () => ({ + injectDeterministicFontFaces: async (html: string) => `${html}`, + })), +})); + vi.mock("@hyperframes/core/compiler", () => ({ bundleToSingleHtml: vi.fn(async () => "bundled"), })); +vi.mock("./producer.js", () => ({ + loadProducer: producerMocks.loadProducer, +})); + import { __resetFontLocalizationWarningsForTests, bundleWithLocalizedFonts, @@ -31,6 +41,12 @@ describe("bundleWithLocalizedFonts (call-site integration)", () => { }); describe("localizeWithProducer", () => { + it("loads the injector through the producer module bundled with the CLI", async () => { + const out = await localizeWithProducer(""); + expect(producerMocks.loadProducer).toHaveBeenCalledOnce(); + expect(out).toBe(""); + }); + it("embeds fonts when the injector is available", async () => { const inject = vi.fn(async (html: string) => `${html}`); const warn = vi.fn(); diff --git a/packages/cli/src/utils/bundleWithLocalizedFonts.ts b/packages/cli/src/utils/bundleWithLocalizedFonts.ts index 46dd6a8cf..1e71e1119 100644 --- a/packages/cli/src/utils/bundleWithLocalizedFonts.ts +++ b/packages/cli/src/utils/bundleWithLocalizedFonts.ts @@ -29,14 +29,11 @@ export async function bundleWithLocalizedFonts( type FontInjector = (html: string) => Promise; /** - * Load the render pipeline's `injectDeterministicFontFaces`, resolving - * `@hyperframes/producer` at RUNTIME only. The specifier is kept out of the - * bundler's/test-runner's static module graph (`@vite-ignore` + a variable - * specifier) on purpose: the CLI test job builds with `--filter - * '!@hyperframes/producer'`, so a static `import("@hyperframes/producer")` - * would fail Vitest's transform-time resolution. At runtime — the built CLI or - * an installed package — producer is a real dependency and resolves via - * node_modules. + * Load the render pipeline's `injectDeterministicFontFaces` through the CLI's + * canonical producer loader. That loader uses a literal dynamic import, which + * lets tsup see and inline producer into the published CLI bundle. A variable + * bare-package import is invisible to tsup and silently fails after `npm + * install`, because producer is intentionally only a workspace devDependency. * * Returns `null` (not a throw) when the module simply isn't available in this * environment, so the caller can treat "producer absent" — a benign, expected @@ -44,8 +41,8 @@ type FontInjector = (html: string) => Promise; */ async function loadFontInjector(): Promise { try { - const producerSpecifier = "@hyperframes/producer"; - const mod = (await import(/* @vite-ignore */ producerSpecifier)) as { + const { loadProducer } = await import("./producer.js"); + const mod = (await loadProducer()) as { injectDeterministicFontFaces?: FontInjector; }; return mod.injectDeterministicFontFaces ?? null;