Files
suaveplan/scripts/lint/check-wasm-subpath-deps.test.ts
2026-05-27 23:47:53 +01:00

111 lines
3.7 KiB
TypeScript

import { mkdtemp, mkdir, rm, writeFile } from "node:fs/promises";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { afterEach, beforeEach, describe, expect, it } from "bun:test";
import {
banReason,
checkWasmSubpathDeps,
extractSpecifiers,
} from "./check-wasm-subpath-deps.js";
let tmp: string;
beforeEach(async () => {
tmp = await mkdtemp(join(tmpdir(), "wasm-deps-"));
});
afterEach(async () => {
await rm(tmp, { recursive: true, force: true });
});
async function writeFixture(rel: string, content: string): Promise<string> {
const full = join(tmp, rel);
await mkdir(join(full, ".."), { recursive: true });
await writeFile(full, content, "utf8");
return full;
}
describe("check-wasm-subpath-deps", () => {
it("banReason flags node: specifiers", { timeout: 5000 }, () => {
expect(banReason("node:fs")).toMatch(/node:/);
});
it("banReason flags exact bans", { timeout: 5000 }, () => {
expect(banReason("hono")).toMatch(/banned package/);
expect(banReason("@suaveplan/api-host")).toMatch(/banned package/);
});
it("banReason flags prefix bans", { timeout: 5000 }, () => {
expect(banReason("@hono/zod-validator")).toMatch(/banned prefix/);
expect(banReason("@opentelemetry/sdk-metrics")).toMatch(
/banned prefix/
);
expect(banReason("@opentelemetry/sdk-trace-base")).toMatch(
/banned prefix/
);
});
it("banReason passes safe specifiers", { timeout: 5000 }, () => {
expect(banReason("@opentelemetry/api")).toBeNull();
expect(banReason("zod")).toBeNull();
expect(banReason("@suaveplan/error")).toBeNull();
});
it("extractSpecifiers picks up named, default, and type imports", {
timeout: 5000,
}, () => {
const src = `
import x from "a";
import { b } from "b";
import type { C } from "c";
export { d } from "d";
// import "ignored-in-comment";
`;
const out = extractSpecifiers(src);
expect(out).toContain("a");
expect(out).toContain("b");
expect(out).toContain("c");
expect(out).toContain("d");
expect(out).not.toContain("ignored-in-comment");
});
it("rejects banned specifier in wasm subpath", {
timeout: 5000,
}, async () => {
// Build a tiny fixture: entry imports "hono" directly.
const entry = await writeFixture(
"packages/example/wasm/index.ts",
`import { Hono } from "hono";\nexport const app = new Hono();\n`
);
const hits = await checkWasmSubpathDeps({ repoRoot: tmp, entry });
expect(hits.length).toBeGreaterThan(0);
const hononHit = hits.find((h) => h.specifier === "hono");
expect(hononHit).toBeDefined();
});
it("accepts a clean entry with only allowed specifiers", {
timeout: 5000,
}, async () => {
const entry = await writeFixture(
"packages/example/wasm/index.ts",
`import z from "zod";\nimport { foo } from "./foo.js";\n`
);
await writeFixture(
"packages/example/wasm/foo.ts",
"export const foo = 1;\n"
);
const hits = await checkWasmSubpathDeps({ repoRoot: tmp, entry });
expect(hits).toEqual([]);
});
it("detects node:* specifier in entry", { timeout: 5000 }, async () => {
const entry = await writeFixture(
"packages/example/wasm/index.ts",
`import fs from "node:fs";\n`
);
const hits = await checkWasmSubpathDeps({ repoRoot: tmp, entry });
expect(hits.length).toBeGreaterThan(0);
expect(hits[0]?.specifier).toBe("node:fs");
});
});