125 lines
4.2 KiB
TypeScript
125 lines
4.2 KiB
TypeScript
import { expect, test } from "bun:test";
|
|
import { mkdtempSync, rmSync, utimesSync, writeFileSync } from "node:fs";
|
|
import { tmpdir } from "node:os";
|
|
import { join } from "node:path";
|
|
import {
|
|
compileWireArtifacts,
|
|
compileWireArtifactsAsync,
|
|
getWrnCompileMetrics,
|
|
invalidateModule,
|
|
loadModule,
|
|
resetWrnCompileMetrics,
|
|
setCompileCacheDir,
|
|
setDevCompilerPipeline,
|
|
} from "../src/pipeline.ts";
|
|
|
|
test("development compilation awaits plugin AST and code transforms", async () => {
|
|
const root = mkdtempSync(join(tmpdir(), "wrnexus-plugin-pipeline-"));
|
|
const file = join(root, "page.wrn");
|
|
let astTransformed = false;
|
|
setCompileCacheDir(join(root, ".wrnexus"));
|
|
setDevCompilerPipeline({
|
|
async transformAst(ast) {
|
|
await Promise.resolve();
|
|
astTransformed = true;
|
|
return ast;
|
|
},
|
|
async transformCode(code) {
|
|
await Promise.resolve();
|
|
return `${code}\nexport const pluginTransformed = true;\n`;
|
|
},
|
|
virtualModules: new Map(),
|
|
});
|
|
try {
|
|
writeFileSync(file, "page Home { view { <h1>Plugin</h1> } }\n");
|
|
const artifact = await compileWireArtifactsAsync(file);
|
|
expect(astTransformed).toBeTrue();
|
|
expect((await import(artifact.main)).pluginTransformed).toBeTrue();
|
|
} finally {
|
|
setDevCompilerPipeline(null);
|
|
rmSync(root, { recursive: true, force: true });
|
|
}
|
|
});
|
|
|
|
test("WRN compilation exposes cache hit, miss, timing, and error metrics", () => {
|
|
const root = mkdtempSync(join(tmpdir(), "wrnexus-cache-metrics-"));
|
|
const file = join(root, "page.wrn");
|
|
setCompileCacheDir(join(root, ".wrnexus"));
|
|
resetWrnCompileMetrics();
|
|
try {
|
|
writeFileSync(file, "page Home { view { <h1>Metrics</h1> } }\n");
|
|
compileWireArtifacts(file);
|
|
compileWireArtifacts(file);
|
|
writeFileSync(file, "page Broken { view { <h1> } }\n");
|
|
expect(() => compileWireArtifacts(file)).toThrow();
|
|
expect(getWrnCompileMetrics()).toMatchObject({
|
|
hits: 1,
|
|
misses: 2,
|
|
compilations: 1,
|
|
errors: 1,
|
|
});
|
|
expect(getWrnCompileMetrics().totalDurationMs).toBeGreaterThanOrEqual(0);
|
|
} finally {
|
|
rmSync(root, { recursive: true, force: true });
|
|
}
|
|
});
|
|
|
|
test("invalidateModule loads changed server modules without restarting the process", async () => {
|
|
const root = mkdtempSync(join(tmpdir(), "wrnexus-hmr-"));
|
|
const file = join(root, "page.ts");
|
|
setCompileCacheDir(join(root, ".wrnexus"));
|
|
|
|
try {
|
|
writeFileSync(file, "export const value = 1;\n");
|
|
expect((await loadModule(file)).value).toBe(1);
|
|
|
|
writeFileSync(file, "export const value = 2;\n");
|
|
expect((await loadModule(file)).value).toBe(1);
|
|
|
|
invalidateModule(file);
|
|
expect((await loadModule(file)).value).toBe(2);
|
|
} finally {
|
|
rmSync(root, { recursive: true, force: true });
|
|
}
|
|
});
|
|
|
|
test("invalidateModule recompiles changed WRN files with watcher-normalized separators", async () => {
|
|
const root = mkdtempSync(join(tmpdir(), "wrnexus-hmr-"));
|
|
const file = join(root, "page.wrn");
|
|
setCompileCacheDir(join(root, ".wrnexus"));
|
|
|
|
try {
|
|
writeFileSync(file, "page Home { view { <h1>First</h1> } }\n");
|
|
const first = (await loadModule(file)).default as (ctx: unknown) => unknown;
|
|
expect(String(first({}))).toContain("First");
|
|
|
|
writeFileSync(file, "page Home { view { <h1>Second</h1> } }\n");
|
|
invalidateModule(file.replace(/\\/g, "/"));
|
|
const second = (await loadModule(file)).default as (ctx: unknown) => unknown;
|
|
expect(String(second({}))).toContain("Second");
|
|
} finally {
|
|
rmSync(root, { recursive: true, force: true });
|
|
}
|
|
});
|
|
|
|
test("WRN cache follows content when a replacement has an older mtime", async () => {
|
|
const root = mkdtempSync(join(tmpdir(), "wrnexus-cache-"));
|
|
const file = join(root, "component.wrn");
|
|
setCompileCacheDir(join(root, ".wrnexus"));
|
|
|
|
try {
|
|
writeFileSync(file, "component Example { view { <p>Old</p> } }\n");
|
|
const first = (await loadModule(file)).render as () => unknown;
|
|
expect(String(first())).toContain("Old");
|
|
|
|
writeFileSync(file, "component Example { view { <p>Current</p> } }\n");
|
|
utimesSync(file, new Date(0), new Date(0));
|
|
invalidateModule(file);
|
|
|
|
const current = (await loadModule(file)).render as () => unknown;
|
|
expect(String(current())).toContain("Current");
|
|
} finally {
|
|
rmSync(root, { recursive: true, force: true });
|
|
}
|
|
});
|