Skip to content

Commit

Permalink
feat(core): convert commands to use nx project graph instead of legac…
Browse files Browse the repository at this point in the history
…y package graph (#3667)
  • Loading branch information
fahslaj committed May 8, 2023
1 parent 5b61c9d commit 8e813c4
Show file tree
Hide file tree
Showing 271 changed files with 10,182 additions and 4,730 deletions.
2 changes: 1 addition & 1 deletion integration/project.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "integration",
"$schema": "../node_modules/nx/schemas/project-schema.json",
"projectType": "library",
"implicitDependencies": ["lerna", "legacy-package-management"],
"implicitDependencies": ["lerna", "legacy-package-management", "legacy-structure-commands-create"],
"targets": {
"lint": {
"executor": "@nrwl/linter:eslint",
Expand Down
3 changes: 2 additions & 1 deletion libs/commands/add/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-nocheck

import { Command, getFilteredPackages, ValidationError, npmConf } from "@lerna/core";
import { npmConf, ValidationError } from "@lerna/core";
import { Command, getFilteredPackages } from "@lerna/legacy-core";
import dedent from "dedent";
import npa from "npm-package-arg";
import pMap from "p-map";
Expand Down
12 changes: 7 additions & 5 deletions libs/commands/bootstrap/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,22 @@
// @ts-nocheck

import {
Command,
createRunner,
getFilteredPackages,
hasNpmVersion,
npmInstall,
npmInstallDependencies,
PackageGraph,
pulseTillDone,
rimrafDir,
ValidationError,
} from "@lerna/core";
import {
Command,
getFilteredPackages,
PackageGraph,
runTopologically,
symlinkBinary,
symlinkDependencies,
ValidationError,
} from "@lerna/core";
} from "@lerna/legacy-core";
import dedent from "dedent";
import getPort from "get-port";
import npa from "npm-package-arg";
Expand Down
4 changes: 3 additions & 1 deletion libs/commands/bootstrap/src/lib/bootstrap-command.spec.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import {
createSymlink as _createSymlink,
hasNpmVersion as _hasNpmVersion,
npmInstall as _npmInstall,
npmInstallDependencies as _npmInstallDependencies,
rimrafDir as _rimrafDir,
runLifecycle as _runLifecycle,
} from "@lerna/core";
import { createSymlink as _createSymlink } from "@lerna/legacy-core";
import {
commandRunner,
initFixtureFactory,
Expand All @@ -17,6 +17,8 @@ import path from "path";

// eslint-disable-next-line jest/no-mocks-import
jest.mock("@lerna/core", () => require("@lerna/test-helpers/__mocks__/@lerna/core"));
// eslint-disable-next-line jest/no-mocks-import
jest.mock("@lerna/legacy-core", () => require("@lerna/test-helpers/__mocks__/@lerna/legacy-core"));

const npmInstall = jest.mocked(_npmInstall);
const npmInstallDependencies = jest.mocked(_npmInstallDependencies);
Expand Down
22 changes: 13 additions & 9 deletions libs/commands/changed/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
import { collectUpdates, Command, CommandConfigOptions, listableFormat, output } from "@lerna/core";
import {
collectProjectUpdates,
Command,
CommandConfigOptions,
listableFormatProjects,
output,
} from "@lerna/core";

module.exports = function factory(argv: NodeJS.Process["argv"]) {
return new ChangedCommand(argv);
Expand All @@ -11,7 +17,7 @@ interface ChangedCommandOptions extends CommandConfigOptions {
}

class ChangedCommand extends Command<ChangedCommandOptions> {
result: ReturnType<typeof listableFormat>;
result: ReturnType<typeof listableFormatProjects>;

get otherCommandConfigs() {
// back-compat
Expand All @@ -28,17 +34,15 @@ class ChangedCommand extends Command<ChangedCommandOptions> {
}
}

const updates = collectUpdates(
this.packageGraph.rawPackageList,
this.packageGraph,
const projectsWithPackage = Object.values(this.projectGraph.nodes).filter((node) => !!node.package);
const updates = collectProjectUpdates(
projectsWithPackage,
this.projectGraph,
this.execOpts,
this.options
);

this.result = listableFormat(
updates.map((node) => node.pkg),
this.options
);
this.result = listableFormatProjects(updates, this.projectGraph, this.options);

if (this.result.count === 0) {
this.logger.info("", "No changed packages found");
Expand Down
2 changes: 1 addition & 1 deletion libs/commands/changed/src/lib/changed-command.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { output as _output, collectUpdates as _collectUpdates } from "@lerna/core";
import { collectProjectUpdates as _collectUpdates, output as _output } from "@lerna/core";
import {
initFixtureFactory,
commandRunner,
Expand Down
3 changes: 2 additions & 1 deletion libs/commands/clean/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-nocheck

import { Command, getFilteredPackages, promptConfirmation, pulseTillDone, rimrafDir } from "@lerna/core";
import { promptConfirmation, pulseTillDone, rimrafDir } from "@lerna/core";
import { Command, getFilteredPackages } from "@lerna/legacy-core";
import pMap from "p-map";
import path from "path";

Expand Down
3 changes: 2 additions & 1 deletion libs/commands/create/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-nocheck

import { Command, npmConf, ValidationError } from "@lerna/core";
import { npmConf, ValidationError } from "@lerna/core";
import { Command } from "@lerna/legacy-core";
import dedent from "dedent";
import fs from "fs-extra";
import npa from "npm-package-arg";
Expand Down
7 changes: 5 additions & 2 deletions libs/commands/diff/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Command, CommandConfigOptions, ValidationError } from "@lerna/core";
import { Command, CommandConfigOptions, getPackage, ValidationError } from "@lerna/core";

// eslint-disable-next-line @typescript-eslint/no-var-requires
const { getLastCommit } = require("./lib/get-last-commit");
Expand Down Expand Up @@ -26,7 +26,10 @@ class DiffCommand extends Command<DiffCommandOptions> {
let targetPackage;

if (packageName) {
targetPackage = this.packageGraph.get(packageName);
const project = Object.values(this.projectGraph.nodes).find(
(p) => p.package && getPackage(p).name === packageName
);
targetPackage = project && getPackage(project);

if (!targetPackage) {
throw new ValidationError("ENOPKG", `Cannot diff, the package '${packageName}' does not exist.`);
Expand Down
142 changes: 84 additions & 58 deletions libs/commands/exec/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
// TODO: refactor based on TS feedback
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-nocheck

import { Command, getFilteredPackages, Profiler, runTopologically, ValidationError } from "@lerna/core";
import {
Command,
CommandConfigOptions,
filterProjects,
getPackage,
Package,
Profiler,
ProjectGraphProjectNodeWithPackage,
runProjectsTopologically,
ValidationError,
} from "@lerna/core";
import pMap from "p-map";

// eslint-disable-next-line @typescript-eslint/no-var-requires
Expand All @@ -12,12 +18,35 @@ module.exports = function factory(argv: NodeJS.Process["argv"]) {
return new ExecCommand(argv);
};

interface ExecCommandConfigOptions extends CommandConfigOptions {
cmd?: string;
args?: string[];
bail?: boolean;
prefix?: boolean;
parallel?: boolean;
profile?: boolean;
profileLocation?: string;
rejectCycles?: boolean;
}

class ExecCommand extends Command {
options: ExecCommandConfigOptions;

command?: string;
args?: string[];
bail?: boolean;
prefix?: boolean;
env?: NodeJS.ProcessEnv;
filteredProjects?: ProjectGraphProjectNodeWithPackage[];
count?: number;
packagePlural?: string;
joinedCommand?: string;

get requiresGit() {
return false;
}

override initialize() {
override async initialize() {
const dashedArgs = this.options["--"] || [];

this.command = this.options.cmd || dashedArgs.shift();
Expand All @@ -35,21 +64,14 @@ class ExecCommand extends Command {
// so cache it here to reduce churn during tighter loops
this.env = Object.assign({}, process.env);

let chain = Promise.resolve();

chain = chain.then(() => getFilteredPackages(this.packageGraph, this.execOpts, this.options));
chain = chain.then((filteredPackages) => {
this.filteredPackages = filteredPackages;
});
this.filteredProjects = filterProjects(this.projectGraph, this.execOpts, this.options);

return chain.then(() => {
this.count = this.filteredPackages.length;
this.packagePlural = this.count === 1 ? "package" : "packages";
this.joinedCommand = [this.command].concat(this.args).join(" ");
});
this.count = this.filteredProjects.length;
this.packagePlural = this.count === 1 ? "package" : "packages";
this.joinedCommand = [this.command].concat(this.args).join(" ");
}

override execute() {
override async execute() {
this.logger.info(
"",
"Executing command in %d %s: %j",
Expand All @@ -58,51 +80,48 @@ class ExecCommand extends Command {
this.joinedCommand
);

let chain = Promise.resolve();

let runCommand: () => Promise<unknown>;
if (this.options.parallel) {
chain = chain.then(() => this.runCommandInPackagesParallel());
runCommand = () => this.runCommandInPackagesParallel();
} else if (this.toposort) {
chain = chain.then(() => this.runCommandInPackagesTopological());
runCommand = () => this.runCommandInPackagesTopological();
} else {
chain = chain.then(() => this.runCommandInPackagesLexical());
runCommand = () => this.runCommandInPackagesLexical();
}

if (this.bail) {
// only the first error is caught
chain = chain.catch((err) => {
try {
await runCommand();
} catch (err) {
process.exitCode = err.exitCode;

// rethrow to halt chain and log properly
throw err;
});
}
} else {
const results = (await runCommand()) as { failed: boolean; exitCode: number }[];
// detect error (if any) from collected results
chain = chain.then((results) => {
/* istanbul ignore else */
if (results.some((result) => result.failed)) {
// propagate "highest" error code, it's probably the most useful
const codes = results.filter((result) => result.failed).map((result) => result.exitCode);
const exitCode = Math.max(...codes, 1);

this.logger.error("", "Received non-zero exit code %d during execution", exitCode);
process.exitCode = exitCode;
}
});
if (results.some((result) => result.failed)) {
// propagate "highest" error code, it's probably the most useful
const codes = results.filter((result) => result.failed).map((result) => result.exitCode);
const exitCode = Math.max(...codes, 1);

this.logger.error("", "Received non-zero exit code %d during execution", exitCode);
process.exitCode = exitCode;
}
}

return chain.then(() => {
this.logger.success(
"exec",
"Executed command in %d %s: %j",
this.count,
this.packagePlural,
this.joinedCommand
);
});
this.logger.success(
"exec",
"Executed command in %d %s: %j",
this.count,
this.packagePlural,
this.joinedCommand
);
}

getOpts(pkg) {
private getOpts(pkg: Package) {
// these options are passed _directly_ to execa
return {
cwd: pkg.location,
Expand All @@ -117,15 +136,15 @@ class ExecCommand extends Command {
};
}

getRunner() {
private getRunner() {
return this.options.stream
? (pkg) => this.runCommandInPackageStreaming(pkg)
: (pkg) => this.runCommandInPackageCapturing(pkg);
? (pkg: Package) => this.runCommandInPackageStreaming(pkg)
: (pkg: Package) => this.runCommandInPackageCapturing(pkg);
}

runCommandInPackagesTopological() {
let profiler;
let runner;
private runCommandInPackagesTopological() {
let profiler: Profiler;
let runner: (pkg: Package) => Promise<unknown>;

if (this.options.profile) {
profiler = new Profiler({
Expand All @@ -140,10 +159,15 @@ class ExecCommand extends Command {
runner = this.getRunner();
}

let chain = runTopologically(this.filteredPackages, runner, {
concurrency: this.concurrency,
rejectCycles: this.options.rejectCycles,
});
let chain = runProjectsTopologically(
this.filteredProjects,
this.projectGraph,
(p) => runner(getPackage(p)),
{
concurrency: this.concurrency,
rejectCycles: this.options.rejectCycles,
}
);

if (profiler) {
chain = chain.then((results) => profiler.output().then(() => results));
Expand All @@ -153,11 +177,13 @@ class ExecCommand extends Command {
}

runCommandInPackagesParallel() {
return pMap(this.filteredPackages, (pkg) => this.runCommandInPackageStreaming(pkg));
return pMap(this.filteredProjects, (p) => this.runCommandInPackageStreaming(getPackage(p)));
}

runCommandInPackagesLexical() {
return pMap(this.filteredPackages, this.getRunner(), { concurrency: this.concurrency });
return pMap(this.filteredProjects, (p) => this.getRunner()(getPackage(p)), {
concurrency: this.concurrency,
});
}

runCommandInPackageStreaming(pkg) {
Expand Down
4 changes: 4 additions & 0 deletions libs/commands/init/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@ class InitCommand extends Command<InitCommandOptions> {
this.logger.verbose(this.name, "skipping preparations");
}

async detectProjects() {
this.logger.verbose(this.name, "skipping project detection");
}

override initialize() {
this.exact = this.options.exact;
this.lernaVersion = this.options.lernaVersion;
Expand Down

0 comments on commit 8e813c4

Please sign in to comment.