Files
Chris Tate c08ab955ae Enforce linting and consistent source formatting
- 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.
2026-10-05 17:51:24 -05:00

137 lines
5.4 KiB
JavaScript

import {
constants,
copyFileSync,
chmodSync,
existsSync,
mkdirSync,
readFileSync,
realpathSync,
renameSync,
rmSync,
writeFileSync,
} from "node:fs";
import { createRequire } from "node:module";
import { dirname, isAbsolute, join, relative, resolve } from "node:path";
import { fileURLToPath } from "node:url";
export function nativeCliPackage(platform, architecture, libc) {
const host = `${platform}-${architecture}`;
if (host === "darwin-arm64" || host === "darwin-x64") return `@scriptc/cli-${host}`;
if (host === "win32-x64") return "@scriptc/cli-win32-x64-msvc";
if (host === "linux-x64" || host === "linux-arm64") {
if (libc !== "glibc" && libc !== "musl")
throw new Error("could not identify the Linux C library");
return `@scriptc/cli-${host}-${libc === "musl" ? "musl" : "gnu"}`;
}
throw new Error(`scriptc has no native command for ${host}`);
}
function hostLibc() {
if (process.platform !== "linux") return null;
const report = process.report.getReport();
if (report.header.glibcVersionRuntime) return "glibc";
if (report.sharedObjects.some((path) => /(?:^|\/)ld-musl-|libc\.musl-/.test(path))) return "musl";
// A statically linked Node has no loaded libc in its process report.
// Only select musl when its loader is present for this architecture.
const arch = process.arch === "arm64" ? "aarch64" : "x86_64";
return existsSync(`/lib/ld-musl-${arch}.so.1`) ? "musl" : null;
}
export function relocateToolchain(manifest, sourceDirectory, destinationDirectory) {
const relocated = { ...manifest };
const pathFrom = (value) => relative(destinationDirectory, resolve(sourceDirectory, value));
for (const name of [
"ts7",
"llvm_package",
"runtime_pack",
"runtime_sources",
"declarations",
"comptime",
"wasi_node_runner",
]) {
if (typeof relocated[name] === "string") relocated[name] = pathFrom(relocated[name]);
}
for (const name of ["linker", "dsymutil", "archiver", "relocatable_linker"]) {
const value = relocated[name];
if (typeof value === "string" && !isAbsolute(value) && /[/\\]/.test(value))
relocated[name] = pathFrom(value);
}
if (relocated.runtime_packs)
relocated.runtime_packs = relocated.runtime_packs.map((pack) => ({
...pack,
path: pathFrom(pack.path),
}));
return relocated;
}
export function installNativeCli(
directory,
packageName = nativeCliPackage(process.platform, process.arch, hostLibc()),
) {
directory = realpathSync(directory);
const require = createRequire(join(directory, "package.json"));
const packageManifest = JSON.parse(readFileSync(join(directory, "package.json"), "utf8"));
const version = packageManifest.version;
let platformManifest;
try {
platformManifest = require.resolve(`${packageName}/package.json`);
} catch {
throw new Error(
`scriptc requires ${packageName}@${version}; reinstall with optional dependencies enabled`,
);
}
const identity = JSON.parse(readFileSync(platformManifest, "utf8"));
if (identity.name !== packageName || identity.version !== version)
throw new Error(`scriptc requires ${packageName}@${version}, found ${identity.version}`);
const sourceDirectory = join(dirname(platformManifest), "dist", "bin");
const source = join(sourceDirectory, process.platform === "win32" ? "scriptc.exe" : "scriptc");
const original = JSON.parse(readFileSync(source + ".json", "utf8"));
if (original.schema !== "scriptc.native-toolchain.v1" || original.compiler_version !== version)
throw new Error("native compiler toolchain version does not match this installation");
const bin = join(directory, "bin");
mkdirSync(bin, { recursive: true });
const manifest = relocateToolchain(original, sourceDirectory, bin);
// npm normalizes modes for payloads outside package bin entries. These
// tools are launched directly by the compiler after installation.
if (process.platform !== "win32") {
for (const path of [
manifest.ts7,
manifest.comptime,
join(manifest.llvm_package, "bin/scriptc-llvm-codegen"),
]) {
chmodSync(resolve(bin, path), 0o755);
}
}
// The common filename lets npm's Windows shim invoke a native executable.
// POSIX bin links also execute this file directly, without a JS launcher.
const destination = join(bin, "scriptc.exe");
const staged = destination + `.install-${process.pid}`;
try {
copyFileSync(source, staged, constants.COPYFILE_FICLONE);
chmodSync(staged, 0o755);
writeFileSync(staged + ".json", JSON.stringify(manifest, null, 2) + "\n");
renameSync(staged + ".json", destination + ".json");
renameSync(staged, destination);
} finally {
rmSync(staged, { force: true });
rmSync(staged + ".json", { force: true });
}
return destination;
}
if (process.argv[1] && resolve(process.argv[1]) === fileURLToPath(import.meta.url)) {
try {
const directory = resolve(dirname(fileURLToPath(import.meta.url)), "..");
const manifest = JSON.parse(readFileSync(join(directory, "package.json"), "utf8"));
// A source checkout builds the native distribution separately. pnpm pack
// replaces workspace ranges with release versions before publication.
const workspace = Object.values(manifest.optionalDependencies ?? {}).some((value) =>
String(value).startsWith("workspace:"),
);
if (!workspace) installNativeCli(directory);
} catch (error) {
process.stderr.write(`scriptc: ${error instanceof Error ? error.message : String(error)}\n`);
process.exitCode = 1;
}
}