Files
WRNexusJS/scripts/build-editor-extension.mjs
Clintchiz 72e4d3eceb
Quality / quality (ubuntu-latest) (push) Failing after 12m19s
Quality / quality (windows-latest) (push) Canceled after 0s
release: WRNexusJS 0.8.0
2026-08-04 12:19:09 +05:30

142 lines
4.5 KiB
JavaScript

import { createHash } from "node:crypto";
import {
existsSync,
mkdtempSync,
readFileSync,
readdirSync,
rmSync,
statSync,
writeFileSync,
} from "node:fs";
import { tmpdir } from "node:os";
import { join, relative, resolve, sep } from "node:path";
import { spawnSync } from "node:child_process";
import process from "node:process";
import { fileURLToPath } from "node:url";
const root = resolve(import.meta.dirname, "..");
const source = join(root, "editors", "vscode", "src", "extension.js");
const destination = join(root, "editors", "vscode", "src", "extension.bundle.cjs");
const scriptPath = fileURLToPath(import.meta.url);
const check = process.argv.includes("--check");
const temporary = mkdtempSync(join(tmpdir(), "wrnexus-editor-extension-"));
const output = join(temporary, "extension.bundle.cjs");
function normalizeText(value) {
return value.replace(/\r\n?/g, "\n");
}
function sha256(value) {
return createHash("sha256").update(value).digest("hex");
}
function walk(directory) {
const files = [];
for (const entry of readdirSync(directory)) {
const path = join(directory, entry);
const stat = statSync(path);
if (stat.isDirectory()) {
files.push(...walk(path));
continue;
}
if (!stat.isFile()) continue;
if (!/\.(?:js|json|cjs)$/.test(path)) continue;
if (path === destination || path.endsWith(`${sep}language-server.cjs`)) continue;
files.push(path);
}
return files;
}
const sourceFiles = [
...walk(join(root, "editors", "vscode", "src")),
join(root, "editors", "vscode", "package.json"),
join(root, "editors", "vscode", "language-configuration.json"),
].sort();
function fingerprint(files) {
const hash = createHash("sha256");
for (const file of files) {
hash.update(relative(root, file).split(sep).join("/"));
hash.update("\0");
hash.update(normalizeText(readFileSync(file, "utf8")));
hash.update("\0");
}
return hash.digest("hex");
}
const sourceHash = fingerprint(sourceFiles);
const generatorHash = sha256(normalizeText(readFileSync(scriptPath, "utf8")));
function stampBundle(bundle) {
const withoutOldStamp = normalizeText(bundle)
.replace(/^\/\/ WRN editor extension source hash: [a-f0-9]{64}\n/m, "")
.replace(/^\/\/ WRN editor extension generator hash: [a-f0-9]{64}\n/m, "");
const lines = withoutOldStamp.split("\n");
const insertion = lines[0]?.startsWith("#!") ? 1 : 0;
lines.splice(
insertion,
0,
`// WRN editor extension source hash: ${sourceHash}`,
`// WRN editor extension generator hash: ${generatorHash}`,
);
return `${lines.join("\n").replace(/\n+$/, "")}\n`;
}
function verifySyntax(file) {
const result = spawnSync(process.execPath, ["--check", file], {
cwd: root,
encoding: "utf8",
timeout: 10_000,
});
if (result.error) throw result.error;
if (result.status !== 0) {
throw new Error(
`VS Code extension bundle syntax check failed.\n${result.stderr || result.stdout || `exit ${result.status}`}`,
);
}
}
try {
const built = spawnSync(
process.env.WRNEXUS_BUN_BINARY || "bun",
["build", source, "--target=node", "--format=cjs", "--external=vscode", `--outfile=${output}`],
{ cwd: root, encoding: "utf8" },
);
if (built.status !== 0) {
throw new Error(built.stderr || built.stdout || "extension bundle failed");
}
const generated = stampBundle(readFileSync(output, "utf8"));
writeFileSync(output, generated, "utf8");
verifySyntax(output);
if (check) {
const existing = existsSync(destination)
? normalizeText(readFileSync(destination, "utf8"))
: "";
const embeddedSourceHash = existing.match(
/^\/\/ WRN editor extension source hash: ([a-f0-9]{64})$/m,
)?.[1];
const embeddedGeneratorHash = existing.match(
/^\/\/ WRN editor extension generator hash: ([a-f0-9]{64})$/m,
)?.[1];
const exact = existing === generated;
const sourceCompatible =
embeddedSourceHash === sourceHash && embeddedGeneratorHash === generatorHash;
if (!exact && !sourceCompatible) {
throw new Error(
`${relative(root, destination)} is stale. Run bun run --cwd editors/vscode build and commit the result.`,
);
}
verifySyntax(destination);
process.stdout.write(
`Verified ${relative(root, destination)} matches the extension sources and has valid Node syntax.\n`,
);
} else {
writeFileSync(destination, generated, "utf8");
process.stdout.write(`Built ${relative(root, destination)}.\n`);
}
} finally {
rmSync(temporary, { recursive: true, force: true });
}