mirror of
https://github.com/vercel-labs/scriptc.git
synced 2026-10-21 05:50:55 +08:00
- Enforce oxlint and oxfmt for owned JavaScript and TypeScript with independent CI checks. - Preserve compiler import boundaries and protected fixtures while cleaning up lint findings. - Standardize source formatting and validate import, fixture, and embedded-source protections.
118 lines
4.2 KiB
TypeScript
118 lines
4.2 KiB
TypeScript
import { spawnSync } from "node:child_process";
|
|
import { mkdtemp, readFile, rm } from "node:fs/promises";
|
|
import { join } from "node:path";
|
|
import { expect, test } from "vitest";
|
|
import { compile } from "@scriptc/compiler";
|
|
import { balancedShardSelect } from "./shard.js";
|
|
|
|
const hasZig = spawnSync("zig", ["version"], { stdio: "ignore" }).status === 0;
|
|
const requestedMode = process.env["SCRIPTC_LLVM_TEST_MODE"];
|
|
if (requestedMode !== undefined && requestedMode !== "release" && requestedMode !== "dev") {
|
|
throw new Error("SCRIPTC_LLVM_TEST_MODE must be release or dev when set");
|
|
}
|
|
const wasiRunner = `
|
|
const {readFileSync} = require('node:fs');
|
|
const {WASI} = require('node:wasi');
|
|
const path = process.argv[1];
|
|
const wasi = new WASI({version:'preview1', args:[path], returnOnExit:true});
|
|
const module = new WebAssembly.Module(readFileSync(path));
|
|
const instance = new WebAssembly.Instance(module, wasi.getImportObject());
|
|
process.exitCode = wasi.start(instance);
|
|
`;
|
|
|
|
function run(command: string, args: string[]) {
|
|
const result = spawnSync(command, args, { encoding: "utf8", timeout: 30_000 });
|
|
if (result.error) throw result.error;
|
|
return {
|
|
stdout: result.stdout,
|
|
stderr: result.stderr,
|
|
status: result.status,
|
|
signal: result.signal,
|
|
};
|
|
}
|
|
|
|
// Relative scheduling estimates spread the large TSL imports and loaders
|
|
// across workers; every target/fixture pair remains in the complete gate.
|
|
const cases = balancedShardSelect(
|
|
[
|
|
"math",
|
|
"spatial",
|
|
"attributes",
|
|
"geometry",
|
|
"scene",
|
|
"materials",
|
|
"mesh",
|
|
"raycast-mesh",
|
|
"raycast-camera",
|
|
"raycast-lines-points",
|
|
"buffers-animation",
|
|
"collision",
|
|
"package-imports",
|
|
"curves",
|
|
"textures",
|
|
"math-extended",
|
|
"procedural-geometry",
|
|
"render-data",
|
|
"mesh-workflows",
|
|
"animation",
|
|
"loaders",
|
|
"gltf",
|
|
"nodes",
|
|
].flatMap((fixture) => ["native", "wasm32-wasi"].map((target) => ({ fixture, target }))),
|
|
({ fixture, target }) => `three:${fixture}:${target}`,
|
|
({ fixture }) => (fixture === "nodes" ? 8 : fixture === "loaders" || fixture === "gltf" ? 4 : 1),
|
|
);
|
|
test.for(cases)(
|
|
"published three.js $fixture runs statically through LLVM on $target",
|
|
{ timeout: 1_200_000 },
|
|
async ({ fixture, target }, context) => {
|
|
const entry = join(import.meta.dirname, `../fixtures/three/${fixture}.mjs`);
|
|
if (target === "wasm32-wasi" && !hasZig) context.skip();
|
|
const dir = await mkdtemp("/tmp/scriptc-three-");
|
|
const previousTarget = process.env["SCRIPTC_TARGET"];
|
|
const previousCc = process.env["SCRIPTC_CC"];
|
|
const wasm = target === "wasm32-wasi";
|
|
const sanitize = !wasm && process.env["SCRIPTC_SAN"] === "1";
|
|
try {
|
|
if (wasm) {
|
|
process.env["SCRIPTC_TARGET"] = target;
|
|
process.env["SCRIPTC_CC"] = "zigcc";
|
|
}
|
|
const result = await compile(entry, {
|
|
outDir: dir,
|
|
outPath: join(dir, wasm ? `${fixture}.wasm` : fixture),
|
|
backend: "llvm",
|
|
dynamic: false,
|
|
npmStatic: ["three"],
|
|
optimization: sanitize ? "dev" : (requestedMode ?? "release"),
|
|
sanitize,
|
|
});
|
|
if (!result.ok)
|
|
throw new Error(result.diagnostics.map((d) => `${d.code}: ${d.message}`).join("\n"));
|
|
expect(result.backend).toBe("llvm");
|
|
if (wasm)
|
|
expect([...(await readFile(result.binaryPath)).subarray(0, 4)]).toEqual([0, 97, 115, 109]);
|
|
const reference = run(process.execPath, ["--no-warnings", entry]);
|
|
expect(reference.status).toBe(0);
|
|
const actual = wasm
|
|
? run(process.execPath, ["--no-warnings", "-e", wasiRunner, result.binaryPath])
|
|
: run(result.binaryPath, []);
|
|
if (sanitize)
|
|
actual.stderr = actual.stderr
|
|
.split("\n")
|
|
.filter(
|
|
(line) =>
|
|
!/^==\d+==WARNING: ASan doesn't fully support makecontext\/swapcontext/.test(line),
|
|
)
|
|
.join("\n");
|
|
expect(actual).toEqual(reference);
|
|
} finally {
|
|
if (previousTarget === undefined) delete process.env["SCRIPTC_TARGET"];
|
|
else process.env["SCRIPTC_TARGET"] = previousTarget;
|
|
if (previousCc === undefined) delete process.env["SCRIPTC_CC"];
|
|
else process.env["SCRIPTC_CC"] = previousCc;
|
|
await rm(dir, { recursive: true, force: true });
|
|
}
|
|
},
|
|
);
|