diff --git a/scripts/postinstall.js b/scripts/postinstall.js index cc504933f..6e3abb25f 100644 --- a/scripts/postinstall.js +++ b/scripts/postinstall.js @@ -2,6 +2,24 @@ import fs from "node:fs"; import path from "node:path"; import { fileURLToPath } from "node:url"; +function detectPackageManager(ua = process.env.npm_config_user_agent ?? "") { + // Examples: + // - "pnpm/10.23.0 npm/? node/v22.21.1 darwin arm64" + // - "npm/10.9.4 node/v22.12.0 linux x64" + // - "bun/1.2.2" + const normalized = String(ua).trim(); + if (normalized.startsWith("pnpm/")) return "pnpm"; + if (normalized.startsWith("bun/")) return "bun"; + if (normalized.startsWith("npm/")) return "npm"; + if (normalized.startsWith("yarn/")) return "yarn"; + return "unknown"; +} + +function shouldApplyPnpmPatchedDependenciesFallback(pm = detectPackageManager()) { + // pnpm already applies pnpm.patchedDependencies itself; re-applying would fail. + return pm !== "pnpm"; +} + function getRepoRoot() { const here = path.dirname(fileURLToPath(import.meta.url)); return path.resolve(here, ".."); @@ -195,6 +213,10 @@ function main() { const repoRoot = getRepoRoot(); process.chdir(repoRoot); + if (!shouldApplyPnpmPatchedDependenciesFallback()) { + return; + } + const pkgPath = path.join(repoRoot, "package.json"); const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8")); const patched = pkg?.pnpm?.patchedDependencies ?? {}; @@ -230,5 +252,7 @@ export { applyPatchFile, applyPatchSet, applyPatchToFile, + detectPackageManager, parsePatch, + shouldApplyPnpmPatchedDependenciesFallback, }; diff --git a/src/postinstall-patcher.test.ts b/src/postinstall-patcher.test.ts index 855905eb5..79afd2c51 100644 --- a/src/postinstall-patcher.test.ts +++ b/src/postinstall-patcher.test.ts @@ -3,13 +3,32 @@ import os from "node:os"; import path from "node:path"; import { describe, expect, it } from "vitest"; -import { applyPatchSet } from "../scripts/postinstall.js"; +import { + applyPatchSet, + detectPackageManager, + shouldApplyPnpmPatchedDependenciesFallback, +} from "../scripts/postinstall.js"; function makeTempDir() { return fs.mkdtempSync(path.join(os.tmpdir(), "clawdbot-patch-")); } describe("postinstall patcher", () => { + it("detects package manager from user agent", () => { + expect(detectPackageManager("pnpm/10.0.0 npm/? node/v22.0.0")).toBe("pnpm"); + expect(detectPackageManager("npm/10.9.0 node/v22.0.0")).toBe("npm"); + expect(detectPackageManager("bun/1.2.2")).toBe("bun"); + expect(detectPackageManager("yarn/4.0.0 npm/? node/v22.0.0")).toBe("yarn"); + expect(detectPackageManager("")).toBe("unknown"); + }); + + it("skips pnpm.patchedDependencies fallback for pnpm", () => { + expect(shouldApplyPnpmPatchedDependenciesFallback("pnpm")).toBe(false); + expect(shouldApplyPnpmPatchedDependenciesFallback("npm")).toBe(true); + expect(shouldApplyPnpmPatchedDependenciesFallback("bun")).toBe(true); + expect(shouldApplyPnpmPatchedDependenciesFallback("unknown")).toBe(true); + }); + it("applies a simple patch", () => { const dir = makeTempDir(); const target = path.join(dir, "lib");