mirror of
https://github.com/vercel-labs/scriptc.git
synced 2026-10-27 06:43:19 +08:00
chalk's callable builder (a function whose prototype chain is set with
Object.setPrototypeOf, with style getters from Object.defineProperties)
already ran on the checked-dynamic model. What failed was the code
around it. Each fix is general and sits on a path that used to end in a
refusal or a runtime fence, or is confined to JavaScript files:
- package.json "imports" inside a --npm-static package resolve to the
shipped JS with the "node" condition hoisted, like the tsgo fs shadow;
- JavaScript object literals that the file reshapes at run time (keyed
writes with runtime keys, delete, Object.defineProperty/ies) map to
the checked-dynamic type, with the literals nested in them;
- a JavaScript class whose only member is a constructor that returns a
value and never reads `this` answers that value from `new` (the return
used to be discarded silently);
- `const { env } = process` binds the data member in JavaScript;
- `o && x` / `o || x` with an always-truthy object, array or function
left operand;
- Number.parseInt/parseFloat over checked-dynamic values apply ToString;
- Function.prototype as a value is one compiler-emitted function;
- calls of String.prototype-only names (bold, italics, ...) on untyped
receivers call the receiver's own or inherited member; a string
receiver throws the refusal at run time.
Programs without these patterns compile to identical bytes: the runtime
benchmark workloads, the self-hosted compiler (LLVM and executable) and
the TypeScript corpus IR were compared against the baseline.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
98 lines
3.3 KiB
TypeScript
98 lines
3.3 KiB
TypeScript
/* chalk 5 compiled from its published JavaScript (tests/fixtures/npm/cases/
|
|
* chalk5): the program runs under Node and as a native binary with each
|
|
* color-detection input supports-color reads — FORCE_COLOR values and the
|
|
* --color/--no-color flags — and stdout, stderr and the exit status must
|
|
* match byte for byte. npm.test.ts runs the same case once in the
|
|
* harness's own environment. */
|
|
import { execFile } from "node:child_process";
|
|
import { mkdirSync } from "node:fs";
|
|
import { dirname, join } from "node:path";
|
|
import { promisify } from "node:util";
|
|
import { beforeAll, describe, expect, test } from "vitest";
|
|
import { compile } from "@scriptc/compiler";
|
|
|
|
const execFileAsync = promisify(execFile);
|
|
const repoRoot = join(import.meta.dirname, "../..");
|
|
const entry = join(repoRoot, "tests/fixtures/npm/cases/chalk5/main.ts");
|
|
const outDir = join(repoRoot, "node_modules/.cache/scriptc-tests/chalk5");
|
|
const sanitize = process.env["SCRIPTC_SAN"] === "1";
|
|
|
|
interface RunResult {
|
|
stdout: string;
|
|
stderr: string;
|
|
exitCode: number;
|
|
}
|
|
|
|
async function run(cmd: string, args: string[], env: NodeJS.ProcessEnv): Promise<RunResult> {
|
|
try {
|
|
const pending = execFileAsync(cmd, args, { cwd: dirname(entry), env, encoding: "utf8" });
|
|
pending.child.stdin?.end();
|
|
const { stdout, stderr } = await pending;
|
|
return { stdout, stderr, exitCode: 0 };
|
|
} catch (err) {
|
|
const e = err as { code?: unknown; stdout?: string; stderr?: string };
|
|
if (typeof e.stdout !== "string" || typeof e.stderr !== "string") throw err;
|
|
return {
|
|
stdout: e.stdout,
|
|
stderr: e.stderr,
|
|
exitCode: typeof e.code === "number" ? e.code : -1,
|
|
};
|
|
}
|
|
}
|
|
|
|
/** The harness environment without the color inputs, plus `force`. */
|
|
function colorEnv(force: string | undefined): NodeJS.ProcessEnv {
|
|
const env = { ...process.env };
|
|
delete env["FORCE_COLOR"];
|
|
delete env["NO_COLOR"];
|
|
delete env["NODE_DISABLE_COLORS"];
|
|
if (force !== undefined) env["FORCE_COLOR"] = force;
|
|
return env;
|
|
}
|
|
|
|
const cases: { name: string; force: string | undefined; argv: string[] }[] = [
|
|
{ name: "FORCE_COLOR unset", force: undefined, argv: [] },
|
|
...["0", "1", "2", "3", "true", "false", ""].map((force) => ({
|
|
name: `FORCE_COLOR=${JSON.stringify(force)}`,
|
|
force,
|
|
argv: [],
|
|
})),
|
|
...[
|
|
["--no-color"],
|
|
["--color"],
|
|
["--color=256"],
|
|
["--color=16m"],
|
|
["--color", "--", "--no-color"],
|
|
].map((argv) => ({ name: argv.join(" "), force: undefined, argv })),
|
|
{ name: "FORCE_COLOR=3 --no-color", force: "3", argv: ["--no-color"] },
|
|
];
|
|
|
|
let binary = "";
|
|
|
|
beforeAll(async () => {
|
|
mkdirSync(outDir, { recursive: true });
|
|
const result = await compile(entry, { outPath: join(outDir, "program"), outDir, sanitize });
|
|
if (!result.ok)
|
|
throw new Error(result.diagnostics.map((d) => `${d.code}: ${d.message}`).join("\n"));
|
|
binary = result.binaryPath;
|
|
}, 300_000);
|
|
|
|
describe("chalk 5 color detection matches Node", () => {
|
|
for (const c of cases) {
|
|
test(
|
|
c.name,
|
|
async () => {
|
|
const env = colorEnv(c.force);
|
|
const [node, native] = await Promise.all([
|
|
run(process.execPath, [entry, ...c.argv], env),
|
|
run(binary, c.argv, env),
|
|
]);
|
|
expect(native.stdout).toBe(node.stdout);
|
|
expect(native.stderr).toBe(node.stderr);
|
|
expect(native.exitCode).toBe(node.exitCode);
|
|
},
|
|
60_000,
|
|
);
|
|
}
|
|
});
|