UNPKG

lerna

Version:

Lerna is a fast, modern build system for managing and publishing multiple JavaScript/TypeScript packages from the same repository

446 lines (441 loc) 14.6 kB
import { Command, exec, isGitInitialized, npmlog_default } from "./chunk-WC2B4V4E.js"; // libs/commands/init/src/index.ts import { addDependenciesToPackageJson, getPackageManagerCommand, joinPathFragments, readJson, writeJson } from "@nx/devkit"; import { existsSync } from "fs"; import fs from "fs-extra"; import { FsTree, flushChanges } from "nx/src/generators/tree"; // libs/commands/init/src/lib/diff.ts var DIFF_DELETE = -1; var DIFF_EQUAL = 0; var DIFF_INSERT = 1; var noColor = (s) => s; var NO_DIFF_MESSAGE = "Compared values have no visual difference."; function diffLinesRaw(aLines, bLines) { const aLength = aLines.length; const bLength = bLines.length; const lcs = Array.from({ length: aLength + 1 }, () => new Array(bLength + 1).fill(0)); for (let i = aLength - 1; i >= 0; i--) { for (let j = bLength - 1; j >= 0; j--) { lcs[i][j] = aLines[i] === bLines[j] ? lcs[i + 1][j + 1] + 1 : Math.max(lcs[i + 1][j], lcs[i][j + 1]); } } const diffs = []; let aIndex = 0; let bIndex = 0; while (aIndex < aLength && bIndex < bLength) { if (aLines[aIndex] === bLines[bIndex]) { diffs.push([DIFF_EQUAL, aLines[aIndex]]); aIndex += 1; bIndex += 1; } else if (lcs[aIndex + 1][bIndex] >= lcs[aIndex][bIndex + 1]) { diffs.push([DIFF_DELETE, aLines[aIndex]]); aIndex += 1; } else { diffs.push([DIFF_INSERT, bLines[bIndex]]); bIndex += 1; } } for (; aIndex < aLength; aIndex += 1) { diffs.push([DIFF_DELETE, aLines[aIndex]]); } for (; bIndex < bLength; bIndex += 1) { diffs.push([DIFF_INSERT, bLines[bIndex]]); } return diffs; } function printChangeLine(line, color, indicator) { return line.length === 0 ? color(indicator) : color(`${indicator} ${line}`); } function printCommonLine(line, commonColor) { return line.length === 0 ? "" : commonColor(` ${line}`); } function createPatchMark(aStart, aEnd, bStart, bEnd, patchColor) { return patchColor(`@@ -${aStart + 1},${aEnd - aStart} +${bStart + 1},${bEnd - bStart} @@`); } function joinAlignedDiffsNoExpand(diffs, options) { const iLength = diffs.length; const nContextLines = options.contextLines; const nContextLines2 = nContextLines + nContextLines; let hasExcessAtStartOrEnd = false; let nExcessesBetweenChanges = 0; let i = 0; while (i !== iLength) { const iStart = i; while (i !== iLength && diffs[i][0] === DIFF_EQUAL) { i += 1; } if (iStart !== i) { if (iStart === 0) { if (i > nContextLines) { hasExcessAtStartOrEnd = true; } } else if (i === iLength) { if (i - iStart > nContextLines) { hasExcessAtStartOrEnd = true; } } else if (i - iStart > nContextLines2) { nExcessesBetweenChanges += 1; } } while (i !== iLength && diffs[i][0] !== DIFF_EQUAL) { i += 1; } } const hasPatch = nExcessesBetweenChanges !== 0 || hasExcessAtStartOrEnd; const lines = []; let jPatchMark = 0; if (hasPatch) { lines.push(""); } let aStart = 0; let bStart = 0; let aEnd = 0; let bEnd = 0; const pushCommonLine = (line) => { lines.push(printCommonLine(line, options.commonColor)); aEnd += 1; bEnd += 1; }; i = 0; while (i !== iLength) { let iStart = i; while (i !== iLength && diffs[i][0] === DIFF_EQUAL) { i += 1; } if (iStart !== i) { if (iStart === 0) { if (i > nContextLines) { iStart = i - nContextLines; aStart = iStart; bStart = iStart; aEnd = aStart; bEnd = bStart; } for (let iCommon = iStart; iCommon !== i; iCommon += 1) { pushCommonLine(diffs[iCommon][1]); } } else if (i === iLength) { const iEnd = i - iStart > nContextLines ? iStart + nContextLines : i; for (let iCommon = iStart; iCommon !== iEnd; iCommon += 1) { pushCommonLine(diffs[iCommon][1]); } } else { const nCommon = i - iStart; if (nCommon > nContextLines2) { const iEnd = iStart + nContextLines; for (let iCommon = iStart; iCommon !== iEnd; iCommon += 1) { pushCommonLine(diffs[iCommon][1]); } lines[jPatchMark] = createPatchMark(aStart, aEnd, bStart, bEnd, options.patchColor); jPatchMark = lines.length; lines.push(""); const nOmit = nCommon - nContextLines2; aStart = aEnd + nOmit; bStart = bEnd + nOmit; aEnd = aStart; bEnd = bStart; for (let iCommon = i - nContextLines; iCommon !== i; iCommon += 1) { pushCommonLine(diffs[iCommon][1]); } } else { for (let iCommon = iStart; iCommon !== i; iCommon += 1) { pushCommonLine(diffs[iCommon][1]); } } } } while (i !== iLength && diffs[i][0] === DIFF_DELETE) { lines.push(printChangeLine(diffs[i][1], options.aColor, "-")); aEnd += 1; i += 1; } while (i !== iLength && diffs[i][0] === DIFF_INSERT) { lines.push(printChangeLine(diffs[i][1], options.bColor, "+")); bEnd += 1; i += 1; } } if (hasPatch) { lines[jPatchMark] = createPatchMark(aStart, aEnd, bStart, bEnd, options.patchColor); } return lines.join("\n"); } function joinAlignedDiffsExpand(diffs, options) { return diffs.map(([type, line]) => { switch (type) { case DIFF_DELETE: return printChangeLine(line, options.aColor, "-"); case DIFF_INSERT: return printChangeLine(line, options.bColor, "+"); default: return printCommonLine(line, options.commonColor); } }).join("\n"); } function diff(a, b, options = {}) { if (a === b) { return NO_DIFF_MESSAGE; } const aLines = a.split("\n"); const bLines = b.split("\n"); const diffs = diffLinesRaw(a === "" ? [] : aLines, b === "" ? [] : bLines); const resolved = { contextLines: typeof options.contextLines === "number" ? options.contextLines : 5, aColor: options.aColor || noColor, bColor: options.bColor || noColor, commonColor: options.commonColor || noColor, patchColor: options.patchColor || noColor }; if (options.expand ?? true) { return joinAlignedDiffsExpand(diffs, resolved); } return joinAlignedDiffsNoExpand(diffs, resolved); } // libs/commands/init/src/index.ts var LARGE_BUFFER = 1024 * 1e6; var PACKAGE_GLOB = "packages/*"; function factory(args) { return new InitCommand(args); } var InitCommand = class { constructor(args) { this.args = args; npmlog_default.heading = "lerna"; this.logger = Command.createLogger(this.name, args.loglevel); this.logger.notice("cli", `v${this.args.lernaVersion}`); this.packageManager = this.detectPackageManager() || this.detectInvokedPackageManager() || "npm"; this.runner = this.execute(); } args; name = "init"; logger; cwd = process.cwd(); packageManager; runner; async execute() { const tree = new FsTree(this.cwd, false); const task = await this.generate(tree); const changes = tree.listChanges(); if (!changes.length) { return; } const isDryRun = this.args.dryRun; const { default: chalk } = await import("chalk"); function printDiff(before, after) { console.error( diff(before, after, { omitAnnotationLines: true, contextLines: 1, expand: false, aColor: chalk.red, bColor: chalk.green, commonColor: chalk.dim, patchColor: () => "" }) ); } if (isDryRun) { this.logger.info("", "The following file system updates will be made:"); } else { this.logger.info("", "Applying the following file system updates:"); } const indent = ""; changes.forEach((f) => { if (f.type === "CREATE") { console.error( `${indent}${chalk.green("CREATE")} ${f.path}${isDryRun ? chalk.yellow(" [preview]") : ""}` ); if (isDryRun) { printDiff("", f.content?.toString() || ""); } } else if (f.type === "UPDATE") { console.error( `${indent}${chalk.white("UPDATE")} ${f.path}${isDryRun ? chalk.yellow(" [preview]") : ""}` ); if (isDryRun) { const currentContentsOnDisk = fs.readFileSync(joinPathFragments(tree.root, f.path)).toString(); printDiff(currentContentsOnDisk, f.content?.toString() || ""); } } else if (f.type === "DELETE") { console.error(`${indent}${chalk.yellow("DELETE")} ${f.path}`); } }); if (!isDryRun) { flushChanges(this.cwd, changes); if (task) { await task(); } this.logger.success("", "Initialized Lerna files"); this.logger.info("", "New to Lerna? Check out the docs: https://lerna.js.org/docs/getting-started"); } else { this.logger.warn("", `The "dryRun" flag means no changes were made.`); } } // proxy "Promise" methods to "private" instance then(onResolved, onRejected) { return this.runner.then(onResolved, onRejected); } /* istanbul ignore next */ catch(onRejected) { return this.runner.catch(onRejected); } async generate(tree) { const defaultLernaJson = { $schema: "node_modules/lerna/schemas/lerna-schema.json", version: this.args.independent === true ? "independent" : "0.0.0" }; if (tree.exists("lerna.json")) { this.logger.error("", "Lerna has already been initialized for this repo."); this.logger.error( "", "If you are looking to ensure that your config is up to date with the latest and greatest, run `lerna repair` instead" ); return; } const lernaJson = defaultLernaJson; if (this.args.packages) { lernaJson.packages = this.args.packages; } if (this.packageManager !== "npm") { lernaJson.npmClient = this.packageManager; } if (!tree.exists("package.json")) { writeJson(tree, "lerna.json", lernaJson); const basePackageJson = { name: "root", private: true }; if (this.packageManager === "pnpm") { writeJson(tree, "package.json", basePackageJson); if (!tree.exists("pnpm-workspace.yaml")) { tree.write("pnpm-workspace.yaml", `packages: - '${PACKAGE_GLOB}' `); } } else { writeJson(tree, "package.json", { ...basePackageJson, workspaces: [PACKAGE_GLOB] }); } } else { if (this.args.packages || this.#hasWorkspacesConfigured(tree)) { writeJson(tree, "lerna.json", lernaJson); } else { this.logger.error( "", "Cannot initialize lerna because your package manager has not been configured to use `workspaces`, and you have not explicitly specified any packages to operate on" ); this.logger.error( "", "See https://lerna.js.org/docs/getting-started#adding-lerna-to-an-existing-repo for how to resolve this" ); return; } } addDependenciesToPackageJson( tree, {}, { lerna: this.args.exact ? this.args.lernaVersion : `^${this.args.lernaVersion}` } ); if (!tree.exists(".gitignore")) { tree.write(".gitignore", "node_modules/"); } return async () => { if (isGitInitialized(this.cwd)) { this.logger.info("", "Git is already initialized"); } else { this.logger.info("", "Initializing Git repository"); await exec("git", ["init"], { cwd: this.cwd, maxBuffer: 1024 }); } if (this.args.skipInstall === void 0) { this.logger.info("", `Using ${this.packageManager} to install packages`); const packageManagerCommand = getPackageManagerCommand(this.packageManager); const [command, ...args] = packageManagerCommand.install.split(" "); await exec(command, args, { cwd: this.cwd, maxBuffer: LARGE_BUFFER }); } }; } #hasWorkspacesConfigured(tree) { const packageJson = readJson(tree, "package.json"); return Array.isArray(packageJson.workspaces) || tree.exists("pnpm-workspace.yaml"); } detectPackageManager() { const packageManager = existsSync("bun.lockb") || existsSync("bun.lock") ? "bun" : existsSync("yarn.lock") ? "yarn" : existsSync("pnpm-lock.yaml") ? "pnpm" : existsSync("package-lock.json") ? "npm" : null; if (packageManager) { this.logger.verbose("", `Detected lock file for ${packageManager}`); } return packageManager; } getInvokerModule() { return process.argv[1] ? { filename: process.argv[1] } : null; } /** * Detects which package manager was used to invoke lerna init command * based on the main Module process that invokes the command * - npx returns 'npm' * - pnpx returns 'pnpm' * - yarn create returns 'yarn' * - bunx returns 'bun' */ detectInvokedPackageManager() { let detectedPackageManager = null; if (process.versions["bun"]) { this.logger.verbose("", "Detected package manager bun from process.versions.bun"); return "bun"; } const userAgent = process.env["npm_config_user_agent"]?.toLowerCase(); for (const pkgManager of ["pnpm", "yarn"]) { if (userAgent?.startsWith(`${pkgManager}/`)) { this.logger.verbose("", `Detected package manager ${pkgManager} from npm_config_user_agent`); return pkgManager; } } const invoker = this.getInvokerModule(); if (!invoker) { this.logger.verbose("", "Could not detect package manager from process"); return detectedPackageManager; } const invokerPath = (invoker.filename ?? invoker.path) || ""; if (!invokerPath) { this.logger.verbose("", "Could not detect package manager from process"); return detectedPackageManager; } const pathSegments = invokerPath.split(/[\\/]/); for (const pkgManager of ["bun", "pnpm", "yarn", "npm"]) { if (pathSegments.some((segment) => { const normalized = segment.replace(/^\./, ""); if (normalized === pkgManager || normalized.startsWith(`${pkgManager}@`)) { return true; } return pkgManager === "bun" && /^bunx(-|@|$)/.test(normalized); })) { this.logger.verbose("", `Detected package manager ${pkgManager} from process`); detectedPackageManager = pkgManager; break; } } return detectedPackageManager; } }; export { factory, InitCommand };