| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129 |
- /**
- * prepack hook — replaces workspace:* with real version numbers in package.json.
- *
- * Shared by all publishable packages. Invoked as:
- * "prepack": "node ../../scripts/prepare-package-for-publish.mjs"
- *
- * Expects process.cwd() to be the package directory (npm/pnpm guarantee this).
- */
- import { readFile, writeFile, copyFile, rm } from "node:fs/promises";
- import { join, resolve } from "node:path";
- const packageDir = process.cwd();
- const packageJsonPath = join(packageDir, "package.json");
- const backupPath = join(packageDir, ".package.json.publish-backup");
- // Walk up to workspace root (contains pnpm-workspace.yaml)
- function findWorkspaceRoot(startDir) {
- let dir = startDir;
- for (let i = 0; i < 10; i++) {
- try {
- // Sync check not available in ESM, so we just hardcode the relative path
- // since all packages are at packages/<name>/
- return resolve(dir, "..", "..");
- } catch {
- dir = resolve(dir, "..");
- }
- }
- throw new Error("Could not find workspace root");
- }
- function normalizeWorkspaceSpecifier(specifier, version) {
- const value = specifier.slice("workspace:".length);
- if (value === "*" || value === "") return version;
- if (value === "^") return `^${version}`;
- if (value === "~") return `~${version}`;
- return value;
- }
- async function loadWorkspaceVersions(workspaceRoot) {
- const packagesDir = join(workspaceRoot, "packages");
- const { readdir } = await import("node:fs/promises");
- const entries = await readdir(packagesDir);
- const versions = new Map();
- for (const entry of entries) {
- try {
- const raw = await readFile(join(packagesDir, entry, "package.json"), "utf-8");
- const pkg = JSON.parse(raw);
- versions.set(pkg.name, pkg.version);
- } catch {
- // not a package dir
- }
- }
- return versions;
- }
- async function main() {
- const raw = await readFile(packageJsonPath, "utf-8");
- const pkg = JSON.parse(raw);
- // Check if there are any workspace: specifiers at all
- let hasWorkspaceDeps = false;
- for (const field of ["dependencies", "optionalDependencies", "peerDependencies", "devDependencies"]) {
- const deps = pkg[field];
- if (!deps) continue;
- for (const specifier of Object.values(deps)) {
- if (typeof specifier === "string" && specifier.startsWith("workspace:")) {
- hasWorkspaceDeps = true;
- break;
- }
- }
- if (hasWorkspaceDeps) break;
- }
- if (!hasWorkspaceDeps) {
- // Nothing to do — skip backup/rewrite to avoid unnecessary churn
- return;
- }
- const workspaceRoot = findWorkspaceRoot(packageDir);
- const versions = await loadWorkspaceVersions(workspaceRoot);
- // Backup original
- await copyFile(packageJsonPath, backupPath);
- for (const field of ["dependencies", "optionalDependencies", "peerDependencies", "devDependencies"]) {
- const deps = pkg[field];
- if (!deps) continue;
- for (const [name, specifier] of Object.entries(deps)) {
- if (typeof specifier !== "string" || !specifier.startsWith("workspace:")) continue;
- const version = versions.get(name);
- if (!version) {
- throw new Error(`Unable to resolve workspace dependency version for "${name}"`);
- }
- deps[name] = normalizeWorkspaceSpecifier(specifier, version);
- }
- }
- await writeFile(packageJsonPath, `${JSON.stringify(pkg, null, 2)}\n`, "utf-8");
- process.stderr.write(`[prepack] Replaced workspace:* deps in ${pkg.name}\n`);
- // Verify: re-read and confirm no workspace: references remain
- const verifyRaw = await readFile(packageJsonPath, "utf-8");
- const verifyPkg = JSON.parse(verifyRaw);
- const violations = [];
- for (const field of ["dependencies", "optionalDependencies", "peerDependencies"]) {
- const deps = verifyPkg[field];
- if (!deps) continue;
- for (const [name, specifier] of Object.entries(deps)) {
- if (typeof specifier === "string" && specifier.startsWith("workspace:")) {
- violations.push(` ${field}.${name}: ${specifier}`);
- }
- }
- }
- if (violations.length > 0) {
- process.stderr.write(
- `[prepack] FATAL: workspace: references remain after replacement!\n` +
- `${violations.join("\n")}\n`,
- );
- // Restore backup before aborting
- const original = await readFile(backupPath, "utf-8");
- await writeFile(packageJsonPath, original, "utf-8");
- await rm(backupPath, { force: true });
- process.exit(1);
- }
- }
- await main();
|