UNPKG

@alexaegis/standard-version

Version:
176 lines (175 loc) 6.5 kB
"use strict"; Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" }); const node_path = require("node:path"); const glob = require("glob"); const jsYaml = require("js-yaml"); const node_fs = require("node:fs"); const PACKAGE_JSON_NAME = "package.json"; const PACKAGE_JSON_DEPENDENCY_FIELDS = [ "dependencies", "devDependencies", "optionalDependencies", "peerDependencies" ]; const collectPackages = () => { const workspaceRoot = getWorkspaceRoot(); if (!workspaceRoot) { throw new Error("not in a workspace"); } const pnpmWorkspace = jsYaml.load( node_fs.readFileSync(node_path.join(workspaceRoot, "pnpm-workspace.yaml"), { encoding: "utf8" }) ); const workspacePackageJsonPath = node_path.join(workspaceRoot, PACKAGE_JSON_NAME); const workspacePackageJson = JSON.parse( node_fs.readFileSync(workspacePackageJsonPath, { encoding: "utf8" }) ); let workspaces = normalizePackageJsonWorkspacesField(workspacePackageJson.workspaces); if (pnpmWorkspace.packages) { workspaces = [...workspaces, ...pnpmWorkspace.packages]; } const packagePaths = glob.globSync(workspaces, { ignore: ["node_modules"], cwd: workspaceRoot, absolute: true }).filter((path) => node_fs.statSync(path).isDirectory()); const workspacePackage = { packageKind: "root", packagePath: workspaceRoot, packageJson: workspacePackageJson, packageJsonPath: workspacePackageJsonPath, workspacePackagePatterns: workspaces, packagePathFromRootPackage: "." }; const subPackages = packagePaths.map((packagePath) => { const packageJsonPath = node_path.join(packagePath, PACKAGE_JSON_NAME); try { return { packageKind: "regular", packagePath: packagePath.toString(), packageJsonPath, packageJson: JSON.parse( node_fs.readFileSync(packageJsonPath, { encoding: "utf8" }) ), packagePathFromRootPackage: node_path.relative(workspaceRoot, node_path.dirname(packageJsonPath)) }; } catch { return void 0; } }).filter((pkg) => !!pkg); return { workspacePackage, subPackages }; }; const getWorkspaceRoot = (cwd = process.cwd()) => { return collectPackageJsonPathsUpDirectoryTree(cwd)[0]; }; const collectPackageJsonPathsUpDirectoryTree = (cwd = process.cwd()) => { return collectPackageJsonPathsUpDirectoryTreeInternal(cwd); }; const collectPackageJsonPathsUpDirectoryTreeInternal = (cwd, collection = []) => { const path = node_path.normalize(cwd); if (node_fs.existsSync(node_path.join(path, "package.json"))) { collection.unshift(path); } const parentPath = node_path.join(path, ".."); if (parentPath !== path) { return collectPackageJsonPathsUpDirectoryTreeInternal(parentPath, collection); } return collection; }; const normalizePackageJsonWorkspacesField = (packageJsonWorkspaces) => { if (Array.isArray(packageJsonWorkspaces)) { return packageJsonWorkspaces; } else if (packageJsonWorkspaces) { return [ ...packageJsonWorkspaces.packages ?? [], ...packageJsonWorkspaces.nohoist ?? [] ]; } else { return []; } }; const createGenericUpdater = (packages) => { return { readVersion: (contents) => { const results = [...contents.matchAll(/"version": "(.*)"/g)]; return results[0]?.[1] ?? "0.0.0"; }, writeVersion: (contents, version) => { return packages.map((localPackage) => localPackage.packageJson.name).filter((name) => !!name).reduce( (r, localPackageName) => r.replaceAll( new RegExp(`"${localPackageName}": "(workspace:)([~^])?.*"`, "g"), `"${localPackageName}": "$1$2"` // the workspace protocol does not include versions but keeps the specifier. This is how pnpm leaves them on install. ).replaceAll( new RegExp(`"${localPackageName}": "(?!workspace:)([~^])?.*"`, "g"), `"${localPackageName}": "$1${version}"` ).replaceAll( new RegExp(`${localPackageName}@([^s \r]+)`, "g"), `${localPackageName}@${version}` ), contents.replace(/"version": ".*"/, `"version": "${version}"`) ); } }; }; const workspaceDependencyVersionRegexp = /^(workspace:)([\^~])?.*$/g; const nonWorkspaceDependencyVersionRegexp = /^(?!workspace:)([\^~])?.*$/g; const createPackageJsonUpdater = (packages) => { return { readVersion: (contents) => { const results = [...contents.matchAll(/"version": "(.*)"/g)]; return results[0]?.[1] ?? "0.0.0"; }, writeVersion: (contents, version) => { const packageJson = JSON.parse(contents); const indent = " "; const newline = contents.includes("\r\n") ? "\r\n" : "\n"; packageJson.version = version; for (const localPackageName of packages.map((localPackage) => localPackage.packageJson.name).filter((name) => !!name)) { for (const dependencyField of PACKAGE_JSON_DEPENDENCY_FIELDS) { const dependencies = packageJson[dependencyField]; const localDependency = dependencies?.[localPackageName]; if (dependencies && localDependency) { dependencies[localPackageName] = localDependency.replaceAll(workspaceDependencyVersionRegexp, "$1$2").replaceAll(nonWorkspaceDependencyVersionRegexp, `$1${version}`); } } } const json = JSON.stringify(packageJson, void 0, indent); if (newline === "\r\n") { return json.replaceAll("\n", "\r\n") + "\r\n"; } return json + "\n"; } }; }; const createStandardVersionConfig = () => { const { workspacePackage, subPackages } = collectPackages(); const packageJsonUpdater = createPackageJsonUpdater(subPackages); const genericUpdater = createGenericUpdater(subPackages); return { scripts: { postbump: "pnpm install", prechangelog: "git add pnpm-lock.yaml" }, bumpFiles: [ { filename: workspacePackage.packageJsonPath, updater: packageJsonUpdater }, { filename: node_path.join(workspacePackage.packagePath, "readme.md"), updater: genericUpdater }, ...subPackages.map((pkg) => ({ filename: pkg.packageJsonPath, updater: packageJsonUpdater })), ...subPackages.map((pkg) => ({ filename: node_path.join(pkg.packagePath, "readme.md"), updater: genericUpdater })) ] }; }; exports.collectPackages = collectPackages; exports.createStandardVersionConfig = createStandardVersionConfig;