111 lines
3.0 KiB
JavaScript
111 lines
3.0 KiB
JavaScript
import { spawnSync } from "node:child_process";
|
|
import fs from "node:fs";
|
|
import path from "node:path";
|
|
import { fileURLToPath } from "node:url";
|
|
|
|
function isBunInstall() {
|
|
const ua = process.env.npm_config_user_agent ?? "";
|
|
return ua.includes("bun/");
|
|
}
|
|
|
|
function getRepoRoot() {
|
|
const here = path.dirname(fileURLToPath(import.meta.url));
|
|
return path.resolve(here, "..");
|
|
}
|
|
|
|
function run(cmd, args, opts = {}) {
|
|
const res = spawnSync(cmd, args, { stdio: "inherit", ...opts });
|
|
if (typeof res.status === "number") return res.status;
|
|
return 1;
|
|
}
|
|
|
|
function applyPatchIfNeeded(opts) {
|
|
const patchPath = path.resolve(opts.patchPath);
|
|
if (!fs.existsSync(patchPath)) {
|
|
throw new Error(`missing patch: ${patchPath}`);
|
|
}
|
|
|
|
let targetDir = path.resolve(opts.targetDir);
|
|
if (!fs.existsSync(targetDir) || !fs.statSync(targetDir).isDirectory()) {
|
|
console.warn(`[postinstall] skip missing target: ${targetDir}`);
|
|
return;
|
|
}
|
|
|
|
// Resolve symlinks to avoid "beyond a symbolic link" errors from git apply
|
|
// (bun/pnpm use symlinks in node_modules)
|
|
targetDir = fs.realpathSync(targetDir);
|
|
|
|
const gitArgsBase = ["apply", "--unsafe-paths", "--whitespace=nowarn"];
|
|
const reverseCheck = [
|
|
...gitArgsBase,
|
|
"--reverse",
|
|
"--check",
|
|
"--directory",
|
|
targetDir,
|
|
patchPath,
|
|
];
|
|
const forwardCheck = [
|
|
...gitArgsBase,
|
|
"--check",
|
|
"--directory",
|
|
targetDir,
|
|
patchPath,
|
|
];
|
|
const apply = [...gitArgsBase, "--directory", targetDir, patchPath];
|
|
|
|
// Already applied?
|
|
if (run("git", reverseCheck, { stdio: "ignore" }) === 0) {
|
|
return;
|
|
}
|
|
|
|
if (run("git", forwardCheck, { stdio: "ignore" }) !== 0) {
|
|
throw new Error(`patch does not apply cleanly: ${path.basename(patchPath)}`);
|
|
}
|
|
|
|
const status = run("git", apply);
|
|
if (status !== 0) {
|
|
throw new Error(`failed applying patch: ${path.basename(patchPath)}`);
|
|
}
|
|
}
|
|
|
|
function extractPackageName(key) {
|
|
if (key.startsWith("@")) {
|
|
const idx = key.indexOf("@", 1);
|
|
if (idx === -1) return key;
|
|
return key.slice(0, idx);
|
|
}
|
|
const idx = key.lastIndexOf("@");
|
|
if (idx <= 0) return key;
|
|
return key.slice(0, idx);
|
|
}
|
|
|
|
function main() {
|
|
if (!isBunInstall()) return;
|
|
|
|
const repoRoot = getRepoRoot();
|
|
process.chdir(repoRoot);
|
|
|
|
const pkgPath = path.join(repoRoot, "package.json");
|
|
const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
|
|
const patched = pkg?.pnpm?.patchedDependencies ?? {};
|
|
|
|
// Bun does not support pnpm.patchedDependencies. Apply these patch files to
|
|
// node_modules packages as a best-effort compatibility layer.
|
|
for (const [key, relPatchPath] of Object.entries(patched)) {
|
|
if (typeof relPatchPath !== "string" || !relPatchPath.trim()) continue;
|
|
const pkgName = extractPackageName(String(key));
|
|
if (!pkgName) continue;
|
|
applyPatchIfNeeded({
|
|
targetDir: path.join("node_modules", ...pkgName.split("/")),
|
|
patchPath: relPatchPath,
|
|
});
|
|
}
|
|
}
|
|
|
|
try {
|
|
main();
|
|
} catch (err) {
|
|
console.error(String(err));
|
|
process.exit(1);
|
|
}
|