paperclip/scripts/run-typecheck-build-gaps.mjs

94 lines
2.5 KiB
JavaScript

#!/usr/bin/env node
import { readFileSync } from "node:fs";
import path from "node:path";
import { spawnSync } from "node:child_process";
const repoRoot = process.cwd();
function fail(message) {
console.error(`[typecheck:build-gaps] ${message}`);
process.exit(1);
}
function run(command, args) {
const result = spawnSync(command, args, {
cwd: repoRoot,
stdio: "inherit",
});
if (result.error) {
console.error(`[typecheck:build-gaps] Failed to spawn ${command}: ${result.error.message}`);
process.exit(1);
}
if (result.status !== 0) {
process.exit(result.status ?? 1);
}
}
function readJson(filePath) {
return JSON.parse(readFileSync(filePath, "utf8"));
}
function listWorkspacePackages() {
const result = spawnSync("pnpm", ["ls", "-r", "--depth", "-1", "--json"], {
cwd: repoRoot,
encoding: "utf8",
});
if (result.error) {
fail(`Unable to spawn pnpm to list workspace packages: ${result.error.message}`);
}
if (result.status !== 0) {
fail("Unable to list pnpm workspace packages.");
}
return JSON.parse(result.stdout);
}
function buildSkipsTypeScript(pkg) {
const buildScript = pkg.scripts?.build;
if (typeof buildScript !== "string") {
return false;
}
return !/\btsc\b/.test(buildScript);
}
const workspacePackages = listWorkspacePackages();
const buildGapCandidates = workspacePackages
.filter((workspacePkg) => workspacePkg.path !== repoRoot)
.map((workspacePkg) => ({
name: workspacePkg.name,
path: workspacePkg.path,
pkg: readJson(path.join(workspacePkg.path, "package.json")),
}))
.filter(({ pkg }) => buildSkipsTypeScript(pkg));
const packagesMissingTypecheck = buildGapCandidates.filter(
({ pkg }) => typeof pkg.scripts?.typecheck !== "string",
);
if (packagesMissingTypecheck.length > 0) {
const missingNames = packagesMissingTypecheck.map((workspacePkg) => workspacePkg.name).join(", ");
fail(
`Workspace packages with build scripts that skip tsc must define a typecheck script. Missing: ${missingNames}`,
);
}
const buildGapPackages = buildGapCandidates.filter(
({ pkg }) => typeof pkg.scripts?.typecheck === "string",
);
console.log(
`[typecheck:build-gaps] typechecking ${buildGapPackages.length} workspace(s): ${buildGapPackages.map(({ name }) => name).join(", ") || "(none)"}`,
);
if (buildGapPackages.length === 0) {
process.exit(0);
}
run("pnpm", ["--filter", "@paperclipai/plugin-sdk", "ensure-build-deps"]);
for (const workspacePkg of buildGapPackages) {
run("pnpm", ["--filter", workspacePkg.name, "typecheck"]);
}