mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-09-03 04:38:33 +00:00
fix(cli): resolve producer font-localization at runtime so vitest transform doesn't fail
The CLI test job builds with --filter '!@hyperframes/producer', and render.ts
imports producer only as a type — so a static import("@hyperframes/producer")
in the font-localization helper failed Vitest's transform-time module
resolution ("Failed to resolve entry for package"), breaking checkBrowser
tests and the helper's own test. Keep the specifier out of the static module
graph (@vite-ignore + variable specifier) so it resolves at runtime only:
production/installed CLI has producer in node_modules and localizes fonts;
the test env fail-opens to the plain bundle. Localizer is now injectable so
the helper's unit tests cover it without needing producer resolvable.
This commit is contained in:
@@ -4,11 +4,6 @@ vi.mock("@hyperframes/core/compiler", () => ({
|
||||
bundleToSingleHtml: vi.fn(async () => "<html><body>bundled</body></html>"),
|
||||
}));
|
||||
|
||||
const injectMock = vi.fn(async (html: string) => html.replace("bundled", "bundled+fonts"));
|
||||
vi.mock("@hyperframes/producer", () => ({
|
||||
injectDeterministicFontFaces: (html: string) => injectMock(html),
|
||||
}));
|
||||
|
||||
import { bundleWithLocalizedFonts } from "./bundleWithLocalizedFonts.js";
|
||||
|
||||
afterEach(() => {
|
||||
@@ -16,17 +11,19 @@ afterEach(() => {
|
||||
});
|
||||
|
||||
describe("bundleWithLocalizedFonts", () => {
|
||||
it("localizes fonts on top of the plain bundle", async () => {
|
||||
const html = await bundleWithLocalizedFonts("/project");
|
||||
expect(injectMock).toHaveBeenCalledOnce();
|
||||
it("runs the injected font localizer over the plain bundle", async () => {
|
||||
const localize = vi.fn(async (html: string) => html.replace("bundled", "bundled+fonts"));
|
||||
const html = await bundleWithLocalizedFonts("/project", localize);
|
||||
expect(localize).toHaveBeenCalledOnce();
|
||||
expect(localize).toHaveBeenCalledWith("<html><body>bundled</body></html>");
|
||||
expect(html).toBe("<html><body>bundled+fonts</body></html>");
|
||||
});
|
||||
|
||||
it("falls open to the plain bundle when font localization throws", async () => {
|
||||
injectMock.mockRejectedValueOnce(new Error("offline / fetch layer unavailable"));
|
||||
const html = await bundleWithLocalizedFonts("/project");
|
||||
// Never worse than a plain bundleToSingleHtml — the remote <link> still
|
||||
// loads at capture time as before.
|
||||
expect(html).toBe("<html><body>bundled</body></html>");
|
||||
it("returns the localizer's output verbatim (localization is the last step)", async () => {
|
||||
const html = await bundleWithLocalizedFonts(
|
||||
"/project",
|
||||
async () => "<html>embedded-face</html>",
|
||||
);
|
||||
expect(html).toBe("<html>embedded-face</html>");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -16,16 +16,37 @@
|
||||
* underlying injector leaves the HTML unchanged, so this never makes a bundle
|
||||
* worse than plain `bundleToSingleHtml`.
|
||||
*/
|
||||
export async function bundleWithLocalizedFonts(projectDir: string): Promise<string> {
|
||||
export async function bundleWithLocalizedFonts(
|
||||
projectDir: string,
|
||||
// Injectable for tests. Production callers omit it and get the producer
|
||||
// font-localization pass, resolved lazily at runtime (see localizeWithProducer).
|
||||
localizeFonts: (html: string) => Promise<string> = localizeWithProducer,
|
||||
): Promise<string> {
|
||||
const { bundleToSingleHtml } = await import("@hyperframes/core/compiler");
|
||||
const html = await bundleToSingleHtml(projectDir);
|
||||
return localizeFonts(html);
|
||||
}
|
||||
|
||||
/**
|
||||
* Run the render pipeline's `injectDeterministicFontFaces` pass, 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 doesn't build 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.
|
||||
*
|
||||
* Fail-open: if producer can't be resolved or a fetch layer throws, return the
|
||||
* HTML unchanged so a bundle is never worse than plain `bundleToSingleHtml`.
|
||||
*/
|
||||
async function localizeWithProducer(html: string): Promise<string> {
|
||||
try {
|
||||
const { injectDeterministicFontFaces } = await import("@hyperframes/producer");
|
||||
const producerSpecifier = "@hyperframes/producer";
|
||||
const { injectDeterministicFontFaces } = (await import(
|
||||
/* @vite-ignore */ producerSpecifier
|
||||
)) as typeof import("@hyperframes/producer");
|
||||
return await injectDeterministicFontFaces(html);
|
||||
} catch {
|
||||
// Producer/font localization unavailable (or a fetch layer threw) — fall
|
||||
// back to the plain bundle. Fonts declared via remote <link> still load at
|
||||
// capture time as before; we just lose the deterministic embed.
|
||||
return html;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user