UNPKG

astro

Version:

Astro is a modern site builder with web best practices, performance, and DX front-of-mind.

97 lines (96 loc) • 3.58 kB
import { exec } from "../exec.js"; // Packages that integrations install (directly or transitively) whose build scripts must run. const BUILD_DEPENDENCIES = { cloudflare: ["workerd"] }; /** * Returns how to approve the build scripts of dependencies installed by the given integrations * when running `pnpm add`. `getVersion` is only called when such dependencies exist. * * pnpm v11+ enables `strictDepBuilds` by default, which makes `pnpm add` exit with * `ERR_PNPM_IGNORED_BUILDS` when a new dependency has an unapproved build script. * * - pnpm v11.23+: pass `--allow-build=<pkg>`, which pnpm merges into the existing `allowBuilds`. * - pnpm v11.0–v11.22: `--allow-build` replaces the existing `allowBuilds` map rather than merging * into it, so the packages are merged into the existing map via `pnpm config set` instead. * - Older or unknown versions: nothing. They don't fail on unapproved builds and may not support * the flag (pnpm v9 rejects it as an unknown option). * * @see https://pnpm.io/settings/build#strictdepbuilds * @see https://pnpm.io/settings/build#allowbuilds * @see https://pnpm.io/cli/add#--allow-build * @see https://github.com/pnpm/pnpm/issues/13872 (`--allow-build` merges since v11.23.0) */ export async function getPnpmBuildApproval(integrationIds, getVersion, runPnpm) { const packages = [...new Set(integrationIds.flatMap((id) => BUILD_DEPENDENCIES[id] ?? []))]; if (packages.length === 0) return { flags: [] }; const [major, minor] = (await getVersion() ?? "").split(".").map((n) => Number.parseInt(n, 10)); if (!(major >= 11)) return { flags: [] }; if (major > 11 || minor >= 23) { return { flags: packages.map((name) => `--allow-build=${name}`) }; } return { flags: [], approve: () => mergeAllowBuildsConfig(packages, runPnpm) }; } /** Adds `packages` to the `allowBuilds` setting of the pnpm workspace, keeping existing entries. */ async function mergeAllowBuildsConfig(packages, runPnpm) { // `allowBuilds` lives in the workspace root's `pnpm-workspace.yaml`. `--workspace-root` targets // it from nested packages, but errors outside a workspace. In that case, pnpm creates // `pnpm-workspace.yaml` in the current directory. // - `pnpm config get/set`: https://pnpm.io/cli/config // - `--workspace-root`: https://pnpm.io/pnpm-cli#-w---workspace-root let scope = ["--workspace-root"]; let current; try { current = await runPnpm([ ...scope, "config", "get", "allowBuilds", "--json" ]); } catch { scope = []; current = await runPnpm([ "config", "get", "allowBuilds", "--json" ]); } const merged = mergeAllowBuilds(current, packages); await runPnpm([ ...scope, "config", "set", "--location", "project", "allowBuilds", JSON.stringify(merged), "--json" ]); } /** Merges `packages` as approved into the JSON output of `pnpm config get allowBuilds --json`. */ export function mergeAllowBuilds(current, packages) { let existing; try { existing = JSON.parse(current); } catch {} const merged = existing && typeof existing === "object" && !Array.isArray(existing) ? { ...existing } : {}; for (const name of packages) merged[name] = true; return merged; } /** Returns the version of the `pnpm` binary run in `cwd`, or `undefined` if it can't be determined. */ export async function getPnpmVersion(cwd) { try { return (await runPnpmIn(cwd)(["--version"])).trim(); } catch { return undefined; } } export function runPnpmIn(cwd) { return async (args) => { const { stdout } = await exec("pnpm", args, { nodeOptions: { cwd } }); return stdout; }; }