| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140 |
- /**
- * Verify that source manifests are publishable once prepack normalization runs.
- *
- * Usage:
- * node scripts/verify-no-workspace-protocol.mjs packages/cli packages/core
- * node ../../scripts/verify-no-workspace-protocol.mjs .
- *
- * This script is safe to run on source manifests before prepack.
- *
- * Checks two invariants before publish:
- * 1. workspace:* / workspace:^ / workspace:~ references can be normalized to real versions
- * 2. non-workspace internal dependencies already point at the current workspace version
- */
- import { access, readdir, readFile } from "node:fs/promises";
- import { join, resolve } from "node:path";
- const dirs = process.argv.slice(2);
- if (dirs.length === 0) {
- process.stderr.write("Usage: node verify-no-workspace-protocol.mjs <pkg-dir> [<pkg-dir>...]\n");
- process.exit(1);
- }
- async function exists(path) {
- try {
- await access(path);
- return true;
- } catch {
- return false;
- }
- }
- async function findWorkspaceRoot(startDir) {
- let dir = resolve(startDir);
- for (let i = 0; i < 10; i++) {
- if (await exists(join(dir, "packages"))) {
- return dir;
- }
- const parent = resolve(dir, "..");
- if (parent === dir) break;
- dir = parent;
- }
- throw new Error(`Could not find workspace root from ${startDir}`);
- }
- async function loadWorkspaceVersions(workspaceRoot) {
- const packagesDir = join(workspaceRoot, "packages");
- 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 {
- // ignore directories that are not publishable packages
- }
- }
- return versions;
- }
- 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;
- }
- let failed = false;
- const workspaceRoot = await findWorkspaceRoot(process.cwd());
- const workspaceVersions = await loadWorkspaceVersions(workspaceRoot);
- for (const dirArg of dirs) {
- const dir = resolve(process.cwd(), dirArg);
- const packageJsonPath = join(dir, "package.json");
- const raw = await readFile(packageJsonPath, "utf-8");
- const pkg = JSON.parse(raw);
- let dirFailed = false;
- for (const field of ["dependencies", "optionalDependencies", "peerDependencies"]) {
- const deps = pkg[field];
- if (!deps) continue;
- for (const [name, specifier] of Object.entries(deps)) {
- const workspaceVersion = workspaceVersions.get(name);
- if (typeof specifier !== "string") {
- continue;
- }
- if (specifier.startsWith("workspace:")) {
- if (!workspaceVersion) {
- process.stderr.write(`FAIL: ${dir} — ${field}.${name}: ${specifier} (workspace package not found)\n`);
- dirFailed = true;
- failed = true;
- continue;
- }
- const normalized = normalizeWorkspaceSpecifier(specifier, workspaceVersion);
- if (
- normalized !== workspaceVersion
- && normalized !== `^${workspaceVersion}`
- && normalized !== `~${workspaceVersion}`
- ) {
- process.stderr.write(
- `FAIL: ${dir} — ${field}.${name}: ${specifier} normalizes to ${normalized}, expected ${workspaceVersion}, ^${workspaceVersion}, or ~${workspaceVersion}\n`,
- );
- dirFailed = true;
- failed = true;
- }
- continue;
- }
- if (
- workspaceVersion
- && specifier !== workspaceVersion
- && specifier !== `^${workspaceVersion}`
- && specifier !== `~${workspaceVersion}`
- ) {
- process.stderr.write(
- `FAIL: ${dir} — ${field}.${name}: expected ${workspaceVersion}, ^${workspaceVersion}, or ~${workspaceVersion}, got ${specifier}\n`,
- );
- dirFailed = true;
- failed = true;
- }
- }
- }
- if (!dirFailed) {
- process.stderr.write(`OK: ${dir}\n`);
- }
- }
- if (failed) {
- process.exit(1);
- }
|