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
JavaScript
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
};