Skip to content

Commit

Permalink
fix(run): fully defer to Nx for dep detection when nx.json exists (le…
Browse files Browse the repository at this point in the history
  • Loading branch information
fahslaj authored and Ross Rhodes committed Jan 3, 2023
1 parent d1dca9a commit cd2df6c
Show file tree
Hide file tree
Showing 6 changed files with 197 additions and 29 deletions.
27 changes: 18 additions & 9 deletions commands/run/index.js
Expand Up @@ -219,15 +219,10 @@ class RunCommand extends Command {
}

prepNxOptions() {
const { readNxJson } = require("nx/src/config/configuration");
const nxJson = readNxJson();
const nxJsonExists = existsSync(path.join(this.project.rootPath, "nx.json"));
const useParallel = this.options.parallel && !nxJsonExists;

const targetDependenciesAreDefined =
Object.keys(nxJson.targetDependencies || nxJson.targetDefaults || {}).length > 0;
const targetDependencies =
this.toposort && !useParallel && !targetDependenciesAreDefined
this.toposort && !this.options.parallel && !nxJsonExists
? {
[this.script]: [
{
Expand All @@ -250,7 +245,7 @@ class RunCommand extends Command {
* To match lerna's own behavior (via pMap's default concurrency), we set parallel to a very large number if
* the flag has been set (we can't use Infinity because that would cause issues with the task runner).
*/
parallel: useParallel ? 999 : this.concurrency,
parallel: this.options.parallel && !nxJsonExists ? 999 : this.concurrency,
nxBail: this.bail,
nxIgnoreCycles: !this.options.rejectCycles,
skipNxCache: this.options.skipNxCache,
Expand All @@ -261,10 +256,24 @@ class RunCommand extends Command {
if (nxJsonExists) {
this.logger.verbose(this.name, "nx.json was found. Task dependencies will be automatically included.");

if (this.options.parallel || this.options.sort !== undefined || this.options.includeDependencies) {
if (this.options.parallel || this.options.sort !== undefined) {
this.logger.warn(
this.name,
`"parallel", "sort", "no-sort", and "include-dependencies" are ignored when nx.json exists. See https://lerna.js.org/docs/recipes/using-lerna-powered-by-nx-to-run-tasks for details.`
`"parallel", "sort", and "no-sort" are ignored when nx.json exists. See https://lerna.js.org/docs/recipes/using-lerna-powered-by-nx-to-run-tasks for details.`
);
}

if (this.options.includeDependencies) {
this.logger.info(
this.name,
`Using the "include-dependencies" option when nx.json exists will include both task dependencies detected by Nx and project dependencies detected by Lerna. See https://lerna.js.org/docs/recipes/using-lerna-powered-by-nx-to-run-tasks#--include-dependencies for details.`
);
}

if (this.options.ignore) {
this.logger.info(
this.name,
`Using the "ignore" option when nx.json exists will exclude only tasks that are not determined to be required by Nx. See https://lerna.js.org/docs/recipes/using-lerna-powered-by-nx-to-run-tasks#--ignore for details.`
);
}
} else {
Expand Down
169 changes: 162 additions & 7 deletions e2e/tests/lerna-run/lerna-run-nx-include-dependencies.spec.ts
Expand Up @@ -89,10 +89,8 @@ describe("lerna-run-nx-include-dependencies", () => {
> package-X:print-name --silent
> package-X@0.0.0 print-name
> echo test-package-X "--silent"
test-package-X --silent
Expand All @@ -111,14 +109,45 @@ describe("lerna-run-nx-include-dependencies", () => {
});

describe("with nx enabled and with nx.json", () => {
it("should include dependencies by default", async () => {
it("should not include package dependencies by default", async () => {
await fixture.addNxToWorkspace();

const output = await fixture.lerna("run print-name --scope package-3 -- --silent");

expect(output.combinedOutput).toMatchInlineSnapshot(`
> Lerna (powered by Nx) Running target print-name for project package-X and 2 task(s) it depends on
> package-X:print-name --silent
> package-X@0.0.0 print-name
> echo test-package-X "--silent"
test-package-X --silent
> Lerna (powered by Nx) Successfully ran target print-name for project package-X
lerna notice cli v999.9.9-e2e.0
lerna verb rootPath /tmp/lerna-e2e/lerna-run-nx-include-dependencies/lerna-workspace
lerna notice filter including "package-X"
lerna info filter [ 'package-X' ]
lerna verb run nx.json was found. Task dependencies will be automatically included.
`);
});

it("should include package dependencies with --include-dependencies", async () => {
await fixture.addNxToWorkspace();

const output = await fixture.lerna("run print-name --scope package-3 --include-dependencies");

expect(output.combinedOutput).toMatchInlineSnapshot(`
> Lerna (powered by Nx) Running target print-name for 3 project(s):
- package-X
- package-X
- package-X
Expand All @@ -138,13 +167,77 @@ test-package-X
test-package-X
> package-X:print-name --silent
> package-X:print-name
> package-X@0.0.0 print-name
> echo test-package-X "--silent"
> echo test-package-X
test-package-X --silent
test-package-X
> Lerna (powered by Nx) Successfully ran target print-name for 3 projects
lerna notice cli v999.9.9-e2e.0
lerna verb rootPath /tmp/lerna-e2e/lerna-run-nx-include-dependencies/lerna-workspace
lerna notice filter including "package-X"
lerna notice filter including dependencies
lerna info filter [ 'package-X' ]
lerna verb run nx.json was found. Task dependencies will be automatically included.
lerna info run Using the "include-dependencies" option when nx.json exists will include both task dependencies detected by Nx and project dependencies detected by Lerna. See https://lerna.js.org/docs/recipes/using-lerna-powered-by-nx-to-run-tasks#--include-dependencies for details.
`);
});
});

describe("with explicit Nx task dependencies", () => {
it("should include dependencies", async () => {
await fixture.addNxToWorkspace();

await fixture.updateJson("packages/package-3/package.json", (json) => ({
...json,
nx: {
targets: {
"print-name": {
inputs: [],
outputs: [],
dependsOn: ["^print-name"],
},
},
},
}));

const output = await fixture.lerna("run print-name --scope package-3");

expect(output.combinedOutput).toMatchInlineSnapshot(`
> Lerna (powered by Nx) Running target print-name for project package-X and 2 task(s) it depends on
> package-X:print-name
> package-X@0.0.0 print-name
> echo test-package-X
test-package-X
> package-X:print-name
> package-X@0.0.0 print-name
> echo test-package-X
test-package-X
> package-X:print-name
> package-X@0.0.0 print-name
> echo test-package-X
test-package-X
Expand All @@ -157,6 +250,68 @@ lerna notice filter including "package-X"
lerna info filter [ 'package-X' ]
lerna verb run nx.json was found. Task dependencies will be automatically included.
`);
});

it("with --ignore should still include dependencies", async () => {
await fixture.addNxToWorkspace();

await fixture.updateJson("packages/package-3/package.json", (json) => ({
...json,
nx: {
targets: {
"print-name": {
inputs: [],
outputs: [],
dependsOn: ["^print-name"],
},
},
},
}));

const output = await fixture.lerna("run print-name --scope package-3 --ignore package-1");

expect(output.combinedOutput).toMatchInlineSnapshot(`
> Lerna (powered by Nx) Running target print-name for project package-X and 2 task(s) it depends on
> package-X:print-name
> package-X@0.0.0 print-name
> echo test-package-X
test-package-X
> package-X:print-name
> package-X@0.0.0 print-name
> echo test-package-X
test-package-X
> package-X:print-name
> package-X@0.0.0 print-name
> echo test-package-X
test-package-X
> Lerna (powered by Nx) Successfully ran target print-name for project package-X
lerna notice cli v999.9.9-e2e.0
lerna verb rootPath /tmp/lerna-e2e/lerna-run-nx-include-dependencies/lerna-workspace
lerna notice filter including "package-X"
lerna notice filter excluding "package-X"
lerna info filter [ 'package-X', '!package-X' ]
lerna verb run nx.json was found. Task dependencies will be automatically included.
lerna info run Using the "ignore" option when nx.json exists will exclude only tasks that are not determined to be required by Nx. See https://lerna.js.org/docs/recipes/using-lerna-powered-by-nx-to-run-tasks#--ignore for details.
`);
});
});
Expand Down
8 changes: 4 additions & 4 deletions e2e/tests/lerna-run/lerna-run-nx-incompatible-options.spec.ts
Expand Up @@ -145,7 +145,7 @@ test-package-X
lerna notice cli v999.9.9-e2e.0
lerna WARN run "parallel", "sort", "no-sort", and "include-dependencies" are ignored when nx.json exists. See https://lerna.js.org/docs/recipes/using-lerna-powered-by-nx-to-run-tasks for details.
lerna WARN run "parallel", "sort", and "no-sort" are ignored when nx.json exists. See https://lerna.js.org/docs/recipes/using-lerna-powered-by-nx-to-run-tasks for details.
`);
});
Expand Down Expand Up @@ -193,7 +193,7 @@ test-package-X
lerna notice cli v999.9.9-e2e.0
lerna WARN run "parallel", "sort", "no-sort", and "include-dependencies" are ignored when nx.json exists. See https://lerna.js.org/docs/recipes/using-lerna-powered-by-nx-to-run-tasks for details.
lerna WARN run "parallel", "sort", and "no-sort" are ignored when nx.json exists. See https://lerna.js.org/docs/recipes/using-lerna-powered-by-nx-to-run-tasks for details.
`);
});
Expand Down Expand Up @@ -241,7 +241,7 @@ test-package-X
lerna notice cli v999.9.9-e2e.0
lerna WARN run "parallel", "sort", "no-sort", and "include-dependencies" are ignored when nx.json exists. See https://lerna.js.org/docs/recipes/using-lerna-powered-by-nx-to-run-tasks for details.
lerna WARN run "parallel", "sort", and "no-sort" are ignored when nx.json exists. See https://lerna.js.org/docs/recipes/using-lerna-powered-by-nx-to-run-tasks for details.
`);
});
Expand Down Expand Up @@ -290,7 +290,7 @@ test-package-X
lerna notice cli v999.9.9-e2e.0
lerna notice filter including dependencies
lerna WARN run "parallel", "sort", "no-sort", and "include-dependencies" are ignored when nx.json exists. See https://lerna.js.org/docs/recipes/using-lerna-powered-by-nx-to-run-tasks for details.
lerna info run Using the "include-dependencies" option when nx.json exists will include both task dependencies detected by Nx and project dependencies detected by Lerna. See https://lerna.js.org/docs/recipes/using-lerna-powered-by-nx-to-run-tasks#--include-dependencies for details.
`);
});
Expand Down
4 changes: 0 additions & 4 deletions e2e/tests/lerna-run/lerna-run.spec.ts
Expand Up @@ -396,10 +396,8 @@ lerna notice cli v999.9.9-e2e.0
> package-X:print-name-run-one-only
> package-X@0.0.0 print-name-run-one-only
> echo test-package-X-run-one-only
test-package-X-run-one-only
Expand All @@ -419,10 +417,8 @@ lerna notice cli v999.9.9-e2e.0
> package-X:"print:name:run-one-only"
> package-X@0.0.0 print:name:run-one-only
> echo test-package-X-run-one-only-with-colon
test-package-X-run-one-only-with-colon
Expand Down
12 changes: 8 additions & 4 deletions website/docs/lerna-and-nx.md
Expand Up @@ -8,7 +8,7 @@ type: explainer

Nrwl (the company behind the open source build system Nx) has taken over [stewardship of Lerna](https://dev.to/nrwl/lerna-is-dead-long-live-lerna-3jal). [Nx](https://nx.dev) is a build system developed by ex-Googlers and utilizes many of the techniques used by internal Google tools. Lerna v5 is the first release under this new stewardship, updating outdated packages and starting to do some cleanup on the repository itself. Starting with v5.1+, Lerna comes with the new possibility to integrate Nx and defer a lot of the task scheduling work to it.

The following is a high level overview of what each tool provides. Note that all of the existing Lerna commands will continue to function as they have. Adding Nx or Nx Cloud simply improves what you're already doing.
The following is a high level overview of what each tool provides. Note that all of the existing Lerna commands will continue to function as they have. Adding Nx or Nx Cloud simply improves what you're already doing.

## Lerna

Expand All @@ -27,7 +27,7 @@ Free and open source
- `npm install lerna`
- `npx lerna init`

-----
---

## Nx

Expand All @@ -50,7 +50,11 @@ Free and open source
- Set `"useNx": true` in `lerna.json`
- Continue using Lerna as usual

------
:::note
When Lerna is set to use Nx and detects `nx.json` in the workspace, it will defer to Nx to detect task dependencies. Some options for `lerna run` will behave differently. See [Using Lerna (Powered by Nx) to Run Tasks](./recipes/using-lerna-powered-by-nx-to-run-tasks) for more details.
:::

---

## Nx Cloud

Expand All @@ -63,7 +67,7 @@ Free and open source

Free for open source projects

For closed source repositories, the first 500 computation hours per month are free. Most repositories do not exceed this limit. $1 per computation hour after that.
For closed source repositories, the first 500 computation hours per month are free. Most repositories do not exceed this limit. $1 per computation hour after that.

### Set up

Expand Down
Expand Up @@ -27,7 +27,11 @@ If you want to limit the concurrency of tasks, you can still use the [concurrenc

Lerna by itself does not have knowledge of which tasks depend on others, so it defaults to excluding tasks on dependent projects when using [filter options](https://github.com/lerna/lerna/tree/6cb8ab2d4af7ce25c812e8fb05cd04650105705f/core/filter-options#lernafilter-options) and relies on `--include-dependencies` to manually specify that dependent projects' tasks should be included.

This is no longer a problem when Lerna uses Nx to run tasks. Nx, utilizing its [task graph](https://nx.dev/concepts/mental-model#the-task-graph), will automatically run dependent tasks first when necessary, so `--include-dependencies` is obsolete.
This is no longer a problem when Lerna uses Nx to run tasks. Nx, utilizing its [task graph](https://nx.dev/concepts/mental-model#the-task-graph), will automatically run dependent tasks first when necessary, so `--include-dependencies` is obsolete. However, it can still be used to include project dependencies that Lerna detects but Nx does not deem necessary and would otherwise exclude.

### `--ignore`

When used with Nx, `--ignore` will never cause `lerna run` to exclude any tasks that are deemed to be required by the Nx [task graph](https://nx.dev/concepts/mental-model#the-task-graph).

:::tip

Expand Down

0 comments on commit cd2df6c

Please sign in to comment.