Skip to content

Commit

Permalink
fix(run): warn on incompatible arguments with useNx
Browse files Browse the repository at this point in the history
  • Loading branch information
ghiscoding committed Sep 21, 2022
1 parent cd0463b commit bc5e823
Show file tree
Hide file tree
Showing 3 changed files with 55 additions and 8 deletions.
27 changes: 27 additions & 0 deletions packages/run/README.md
Expand Up @@ -175,3 +175,30 @@ Example of `nx.json`:
}
}
```

When [Nx](https://nx.dev/) is installed and `nx.json` is detected in the current workspace with `useNx` set to `true` in `lerna.json`, Lerna will respect `nx.json` configuration during `lerna run` and delegate to the Nx task runner.

Nx will run tasks in an order and with a concurrency that it determines appropriate based on the task graph that it creates. For more information, see [Nx Mental Model: The Task Graph](https://nx.dev/concepts/mental-model#the-task-graph).

**This behavior allows Nx to run tasks in the most efficient way possible, but it also means that some existing options for `lerna run` become obsolete as explained below.**

#### Obsolete Options when `useNx` is enabled

##### `--sort` and `--no-sort`

Nx will always run tasks in the order it deems is correct based on its knowledge of project and task dependencies, so `--sort` and `--no-sort` have no effect.

##### `--parallel`

Nx will use the task graph to determine which tasks can be run in parallel and do so automatically, so `--parallel` has no effect.

> **Note** if you want to limit the concurrency of tasks, you can still use the [concurrency global option](https://github.com/lerna/lerna/blob/6cb8ab2d4af7ce25c812e8fb05cd04650105705f/core/global-options/README.md#--concurrency) to accomplish this.

##### `--include-dependencies`

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.

> **Tip** the effects on the options above will only apply if `nx.json` exists in the root. If `nx.json` does not exist and `useNx` is `true`, then they will behave just as they would with Lerna's base task runner (if `useNx` is `false`).
12 changes: 12 additions & 0 deletions packages/run/src/__tests__/run-command.spec.ts
Expand Up @@ -429,5 +429,17 @@ describe('RunCommand', () => {
await lernaRun(testDir)('my-cacheable-script', '--skip-nx-cache');
expect(collectedOutput).not.toContain('Nx read the output from the cache');
});

it('should display a console warning when using obsolete options with useNx', async () => {
collectedOutput = '';

await lernaRun(testDir)('my-script', '--sort');

const [logMessage] = loggingOutput('warn');
expect(logMessage).toContain(
'"parallel", "sort", "no-sort", and "include-dependencies" are ignored when nx.json exists.'
);
expect(collectedOutput).toContain('package-1');
});
});
});
24 changes: 16 additions & 8 deletions packages/run/src/run-command.ts
Expand Up @@ -8,7 +8,7 @@ import {
ValidationError,
} from '@lerna-lite/core';
import { FilterOptions, getFilteredPackages, Profiler } from '@lerna-lite/optional-cmd-common';
import fs from 'fs-extra';
import fs, { existsSync } from 'fs-extra';
import pMap from 'p-map';
import path from 'path';
import { performance } from 'perf_hooks';
Expand Down Expand Up @@ -251,11 +251,13 @@ export class RunCommand extends Command<RunCommandOption & FilterOptions> {
async prepNxOptions() {
const { readNxJson } = await import('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 =
// prettier-ignore
this.toposort && !this.options.parallel && !targetDependenciesAreDefined
this.toposort && !useParallel && !targetDependenciesAreDefined
? {
[this.script]: [
{
Expand All @@ -279,23 +281,29 @@ export class RunCommand extends Command<RunCommandOption & FilterOptions> {
* 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: this.options.parallel ? 999 : this.concurrency,
parallel: useParallel ? 999 : this.concurrency,
nxBail: this.bail,
nxIgnoreCycles: !this.options.rejectCycles,
skipNxCache: this.options.skipNxCache,
verbose: this.options.verbose,
__overrides__: this.args.map((t) => t.toString()),
};

const excludeTaskDependencies = !fs.existsSync(path.join(this.project.rootPath, 'nx.json'));
if (excludeTaskDependencies) {
this.logger.verbose(this.name, 'nx.json was not found. Task dependencies will not be automatically included.');
} else {
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) {
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.`
);
}
} else {
this.logger.verbose(this.name, 'nx.json was not found. Task dependencies will not be automatically included.');
}

const extraOptions = {
excludeTaskDependencies,
excludeTaskDependencies: !nxJsonExists,
};

return { targetDependencies, options, extraOptions };
Expand Down

0 comments on commit bc5e823

Please sign in to comment.