Files
2026-05-27 23:47:53 +01:00

110 lines
3.8 KiB
TypeScript

import { existsSync, readdirSync, readFileSync, statSync } from "node:fs";
import { dirname, join } from "node:path";
import { fileURLToPath } from "node:url";
import { describe, expect, it } from "@suaveplan/testing/runner";
const here = dirname(fileURLToPath(import.meta.url));
const srcRoot = join(here, "..", "src");
const distRoot = join(here, "..", "dist");
function walk(dir: string, out: string[] = []): string[] {
if (!existsSync(dir)) return out;
for (const entry of readdirSync(dir)) {
const p = join(dir, entry);
if (statSync(p).isDirectory()) walk(p, out);
else if (p.endsWith(".ts") || p.endsWith(".js")) out.push(p);
}
return out;
}
const FORBIDDEN_NODE_PATTERNS = [
/from\s+["']node:/,
/from\s+["']fs["']/,
/from\s+["']path["']/,
/from\s+["']crypto["']/,
];
describe("purity", () => {
it("source files do not import Node-only APIs at the top level", {
timeout: 5000,
}, () => {
const files = walk(srcRoot).filter(
(f) => !f.endsWith(".test.ts") && !f.endsWith(".d.ts")
);
const offenders: string[] = [];
for (const f of files) {
const text = readFileSync(f, "utf8");
for (const rx of FORBIDDEN_NODE_PATTERNS) {
if (rx.test(text)) offenders.push(`${f}: ${rx.source}`);
}
}
expect(offenders).toEqual([]);
});
it("@ffmpeg/core-mt is referenced only inside dynamic imports or comments", {
timeout: 5000,
}, () => {
const files = walk(srcRoot).filter(
(f) => !f.endsWith(".test.ts") && !f.endsWith(".d.ts")
);
for (const f of files) {
const text = readFileSync(f, "utf8");
const lines = text.split("\n");
for (const line of lines) {
if (!line.includes("@ffmpeg/core-mt")) continue;
const trimmed = line.trim();
if (trimmed.startsWith("//")) continue;
if (trimmed.startsWith("*")) continue;
if (line.includes("import(")) continue;
throw new Error(`disallowed reference in ${f}: ${line}`);
}
}
});
it("@ffmpeg/core is never reached via static import", {
timeout: 5000,
}, () => {
const files = walk(srcRoot).filter(
(f) => !f.endsWith(".test.ts") && !f.endsWith(".d.ts")
);
// Forbidden: `import … from "@ffmpeg/core"` / `import "@ffmpeg/core"`.
const STATIC_IMPORT =
/^\s*import\b[^;]*from\s+["']@ffmpeg\/core(?:-mt)?["']/;
for (const f of files) {
if (f.includes("ffmpeg-shim.d.ts")) continue;
const lines = readFileSync(f, "utf8").split("\n");
for (const line of lines) {
if (STATIC_IMPORT.test(line)) {
throw new Error(`static import in ${f}: ${line}`);
}
}
}
});
it("importing the root module mutates no global state", {
timeout: 5000,
}, async () => {
// The act of importing the module is the verification — no exception
// means no top-level throw, and no spec covers any mutation outside
// the module's own ESM closure.
const mod = await import("../src/index.js");
expect(mod.FfmpegWasmCodec).toBeDefined();
});
it("dist/ — when built — contains no node: references", {
timeout: 5000,
}, () => {
if (!existsSync(distRoot)) {
// pre-build environment — skip
return;
}
const files = walk(distRoot);
for (const f of files) {
const text = readFileSync(f, "utf8");
for (const rx of FORBIDDEN_NODE_PATTERNS) {
expect(rx.test(text)).toBe(false);
}
}
});
});