import * as fs from "node:fs";
import * as os from "node:os";
import * as path from "node:path";
import { afterEach, beforeEach, describe, expect, it } from "vitest";
import { extractGoogleFonts, extractPlatform } from "./phase-analyze";
// `extractPlatform` runs four strategies in order:
// 1. deno.json imports referencing apps/{platform}/
// 2. apps/{platform}.ts file existence
// 3. apps/site.ts loose string match against platform name + "platform"
// 4. .deco/blocks filenames hinting at platform
// Strategy 3 is the false-positive trap that ate Magento sites before #211:
// a production site's apps/site.ts imports `apps/vtex/mod.ts` for the color
// palette, so the content matches `"vtex"` even though the real platform is
// declared via `apps/magento.ts`. The fix adds "magento" to the platforms
// list so Strategy 2 fires first and short-circuits before Strategy 3 can
// over-match.
describe("extractGoogleFonts", () => {
let tmp: string;
beforeEach(() => {
tmp = fs.mkdtempSync(path.join(os.tmpdir(), "extract-gfonts-"));
fs.mkdirSync(path.join(tmp, "routes"), { recursive: true });
});
afterEach(() => {
fs.rmSync(tmp, { recursive: true, force: true });
});
it("extracts preconnects and stylesheet from _app.tsx", () => {
fs.writeFileSync(
path.join(tmp, "routes", "_app.tsx"),
`
`,
);
const result = extractGoogleFonts(tmp);
expect(result.preconnects).toEqual([
"https://fonts.googleapis.com",
"https://fonts.gstatic.com",
]);
expect(result.stylesheets).toEqual([
"https://fonts.googleapis.com/css2?family=Inter:wght@400;700&display=swap",
]);
});
it("deduplicates URLs present in both _app.tsx and _layout.tsx", () => {
const sharedUrl = "https://fonts.googleapis.com/css2?family=Roboto&display=swap";
fs.writeFileSync(
path.join(tmp, "routes", "_app.tsx"),
``,
);
fs.writeFileSync(
path.join(tmp, "routes", "_layout.tsx"),
``,
);
const result = extractGoogleFonts(tmp);
expect(result.stylesheets).toHaveLength(1);
});
it("extracts Google Fonts @import from tailwind.css", () => {
fs.writeFileSync(
path.join(tmp, "tailwind.css"),
`@import url('https://fonts.googleapis.com/css2?family=Poppins&display=swap');`,
);
const result = extractGoogleFonts(tmp);
expect(result.stylesheets).toEqual([
"https://fonts.googleapis.com/css2?family=Poppins&display=swap",
]);
});
it("returns empty arrays when no Google Fonts are declared", () => {
fs.writeFileSync(path.join(tmp, "routes", "_app.tsx"), ``);
const result = extractGoogleFonts(tmp);
expect(result.preconnects).toHaveLength(0);
expect(result.stylesheets).toHaveLength(0);
});
});
describe("extractPlatform", () => {
let tmp: string;
beforeEach(() => {
tmp = fs.mkdtempSync(path.join(os.tmpdir(), "extract-platform-"));
fs.mkdirSync(path.join(tmp, "apps"), { recursive: true });
});
afterEach(() => {
fs.rmSync(tmp, { recursive: true, force: true });
});
it("detects magento from apps/magento.ts (a production site shape)", () => {
fs.writeFileSync(path.join(tmp, "apps", "magento.ts"), "export default {};\n");
// Also write a site.ts that imports vtex for color palettes — this is
// what tripped Strategy 3 before #211.
fs.writeFileSync(
path.join(tmp, "apps", "site.ts"),
`import { color as vtex } from "apps/vtex/mod.ts";\n` +
`export interface State { platform: string }\n`,
);
expect(extractPlatform(tmp)).toBe("magento");
});
it("detects vtex from apps/vtex.ts", () => {
fs.writeFileSync(path.join(tmp, "apps", "vtex.ts"), "export default { account: \"foo\" };\n");
expect(extractPlatform(tmp)).toBe("vtex");
});
it("falls back to custom when no platform signal exists", () => {
fs.writeFileSync(path.join(tmp, "apps", "site.ts"), "export default {};\n");
expect(extractPlatform(tmp)).toBe("custom");
});
it("prefers magento over vtex when both an apps/magento.ts AND a vtex string match exist", () => {
// Reproduce the exact production trap: apps/magento.ts is the real signal,
// but apps/site.ts loosely contains "vtex" + "platform".
fs.writeFileSync(path.join(tmp, "apps", "magento.ts"), "export default {};\n");
fs.writeFileSync(
path.join(tmp, "apps", "site.ts"),
`import "apps/vtex/mod.ts";\nexport interface State { platform: string }\n`,
);
expect(extractPlatform(tmp)).toBe("magento");
});
});