Files
quixos-nix-helpers/component-type-batch.mjs
T

50 lines
2.3 KiB
JavaScript
Raw Normal View History

import path from "node:path";
import { createRequire } from "node:module";
const [tsc, count, ...args] = process.argv.slice(2);
const ts = createRequire(import.meta.url)(path.resolve(path.dirname(tsc), "../lib/typescript.js"));
const parsed = ts.parseCommandLine(args);
const witnesses = parsed.fileNames.slice(0, Number(count));
const shared = parsed.fileNames.slice(Number(count));
const host = ts.createCompilerHost(parsed.options, true);
// These programs only discover source closures; they do not bind or check.
// Sharing immutable parsed source files avoids reading/parsing the same libs
// for every witness. Only the final combined program performs typechecking.
const sources = new Map();
const getSourceFile = host.getSourceFile;
host.getSourceFile = (filename, ...options) => {
if (!sources.has(filename)) sources.set(filename, getSourceFile(filename, ...options));
return sources.get(filename);
};
for (const method of ["fileExists", "directoryExists", "getDirectories", "readFile", "realpath"]) {
if (!host[method]) continue;
const original = host[method],
cache = new Map();
host[method] = (filename) => {
if (!cache.has(filename)) cache.set(filename, original(filename));
return cache.get(filename);
};
}
const closures = witnesses.map(
(witness) => new Set(ts.createProgram([witness, ...shared], parsed.options, host).getSourceFiles()),
);
const common = new Set([...closures[0]].filter((source) => closures.every((closure) => closure.has(source))));
const ambient = (node) =>
ts.isModuleDeclaration(node) || ts.isNamespaceExportDeclaration(node) || ts.forEachChild(node, ambient);
// A file that is visible to just some witnesses must not contribute globals
// or ambient module declarations to the others. Otherwise a combined success
// could hide an independent failure. Script files are conservatively unsafe.
if (
closures.some((closure) =>
[...closure].some((source) => !common.has(source) && (!ts.isExternalModule(source) || ambient(source))),
)
)
process.exit(2);
const program = ts.createProgram(parsed.fileNames, parsed.options, host);
const diagnostics = [...parsed.errors, ...ts.getPreEmitDiagnostics(program)];
if (diagnostics.length) {
console.error(ts.formatDiagnosticsWithColorAndContext(diagnostics, host));
process.exit(1);
}