Skip to content

Commit 5bbaffb

Browse files
MaxKlessjaysoo
andauthored
feat(core): add metagenerator for convert-to-inferred (#27672)
<!-- Please make sure you have read the submission guidelines before posting an PR --> <!-- https://github.com/nrwl/nx/blob/master/CONTRIBUTING.md#-submitting-a-pr --> <!-- Please make sure that your commit message follows our format --> <!-- Example: `fix(nx): must begin with lowercase` --> <!-- If this is a particularly complex change or feature addition, you can request a dedicated Nx release for this pull request branch. Mention someone from the Nx team or the `@nrwl/nx-pipelines-reviewers` and they will confirm if the PR warrants its own release for testing purposes, and generate it for you if appropriate. --> ## Current Behavior The DX to migrate to project crystal isn't great right now - people have to run `convert-to-inferred` generators manually. ## Expected Behavior We should provide a smoother way to migrate, so we introduced an `@nx/workspace:infer-targets` generator that can handle multiple conversions at once, either for the whole workspace or at a project level. --------- Co-authored-by: Jack Hsu <[email protected]>
1 parent 7232b39 commit 5bbaffb

File tree

25 files changed

+539
-43
lines changed

25 files changed

+539
-43
lines changed

docs/generated/manifests/menus.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10391,6 +10391,14 @@
1039110391
"children": [],
1039210392
"isExternal": false,
1039310393
"disableCollapsible": false
10394+
},
10395+
{
10396+
"id": "infer-targets",
10397+
"path": "/nx-api/workspace/generators/infer-targets",
10398+
"name": "infer-targets",
10399+
"children": [],
10400+
"isExternal": false,
10401+
"disableCollapsible": false
1039410402
}
1039510403
],
1039610404
"isExternal": false,

docs/generated/manifests/nx-api.json

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3487,6 +3487,15 @@
34873487
"originalFilePath": "/packages/workspace/src/generators/ci-workflow/schema.json",
34883488
"path": "/nx-api/workspace/generators/ci-workflow",
34893489
"type": "generator"
3490+
},
3491+
"/nx-api/workspace/generators/infer-targets": {
3492+
"description": "Convert Nx projects to use inferred targets.",
3493+
"file": "generated/packages/workspace/generators/infer-targets.json",
3494+
"hidden": false,
3495+
"name": "infer-targets",
3496+
"originalFilePath": "/packages/workspace/src/generators/infer-targets/schema.json",
3497+
"path": "/nx-api/workspace/generators/infer-targets",
3498+
"type": "generator"
34903499
}
34913500
},
34923501
"path": "/nx-api/workspace"

docs/generated/packages-metadata.json

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3451,6 +3451,15 @@
34513451
"originalFilePath": "/packages/workspace/src/generators/ci-workflow/schema.json",
34523452
"path": "workspace/generators/ci-workflow",
34533453
"type": "generator"
3454+
},
3455+
{
3456+
"description": "Convert Nx projects to use inferred targets.",
3457+
"file": "generated/packages/workspace/generators/infer-targets.json",
3458+
"hidden": false,
3459+
"name": "infer-targets",
3460+
"originalFilePath": "/packages/workspace/src/generators/infer-targets/schema.json",
3461+
"path": "workspace/generators/infer-targets",
3462+
"type": "generator"
34543463
}
34553464
],
34563465
"githubRoot": "https://github.com/nrwl/nx/blob/master",
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
{
2+
"name": "infer-targets",
3+
"factory": "./src/generators/infer-targets/infer-targets",
4+
"schema": {
5+
"$schema": "https://json-schema.org/schema",
6+
"$id": "InferTargets",
7+
"title": "",
8+
"type": "object",
9+
"description": "Convert Nx projects to use inferred targets.",
10+
"properties": {
11+
"project": {
12+
"type": "string",
13+
"description": "The project to convert to use inferred targets.",
14+
"x-priority": "important"
15+
},
16+
"plugins": {
17+
"type": "array",
18+
"description": "The plugins used to infer targets. For example @nx/eslint or @nx/jest",
19+
"items": { "type": "string" }
20+
},
21+
"skipFormat": {
22+
"type": "boolean",
23+
"description": "Whether to format files.",
24+
"default": false
25+
}
26+
},
27+
"presets": []
28+
},
29+
"description": "Convert Nx projects to use inferred targets.",
30+
"implementation": "/packages/workspace/src/generators/infer-targets/infer-targets.ts",
31+
"aliases": [],
32+
"hidden": false,
33+
"path": "/packages/workspace/src/generators/infer-targets/schema.json",
34+
"type": "generator"
35+
}

docs/shared/recipes/running-tasks/convert-to-inferred.md

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,17 @@ For the best experience, we recommend that you [migrate](/features/automate-upda
1919
npx nx migrate latest
2020
```
2121

22-
## Migrate a Plugin
22+
## Migrate All Plugins
23+
24+
You can use the `infer-targets` generator to quickly migrate all available plugins to use inferred tasks. See the sections below for more details on the individual plugins' migration processes.
25+
26+
```shell
27+
npx nx g infer-targets
28+
```
29+
30+
The generator will automatically detect all available `convert-to-inferred` generators and run the ones you choose. If you only want to try it on a single project, pass the `--project` option.
31+
32+
## Migrate a Single Plugin
2333

2434
Most of the official plugins come with a `convert-to-inferred` generator. This generator will
2535

@@ -42,7 +52,7 @@ None of the above
4252
For third-party plugins that provide `convert-to-inferred` generators, you should pick the `None of the above` option and type in the name of the package manually. Alternatively, you can also provide the package explicitly with `nx g <plugin>:convert-to-inferred`.
4353
{% /callout %}
4454

45-
We recommend that you migrate the plugins one at a time, and check that the configurations are correct before continuing to the next plugin. If you only want to try it on a single project, pass the `--project` option.
55+
We recommend that you check that the configurations are correct before continuing to the next plugin. If you only want to try it on a single project, pass the `--project` option.
4656

4757
## Understand the Migration Process
4858

@@ -96,7 +106,7 @@ For example, if we migrated the `@nx/vite` plugin for a single app (i.e. `nx g @
96106
You'll notice that the `serve` and `build` tasks are running the [Vite CLI](https://vitejs.dev/guide/cli.html) and there are no references to Nx executors. Since the targets directly invoke the Vite CLI, any options that may be passed to it can be passed via Nx commands. e.g. `nx serve demo --cors --port 8888` enables CORs and uses port `8888` using [Vite CLI options](https://vitejs.dev/guide/cli.html#options)
97107
The same CLI setup applies to other plugins as well.
98108

99-
- `@nx/cypess` calls the [Cypress CLI](https://docs.cypress.io/guides/guides/command-line)
109+
- `@nx/cypress` calls the [Cypress CLI](https://docs.cypress.io/guides/guides/command-line)
100110
- `@nx/playwright` calls the [Playwright CLI](https://playwright.dev/docs/test-cli)
101111
- `@nx/webpack` calls the [Webpack CLI](https://webpack.js.org/api/cli/)
102112
- etc.

docs/shared/reference/sitemap.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -755,3 +755,4 @@
755755
- [fix-configuration](/nx-api/workspace/generators/fix-configuration)
756756
- [npm-package](/nx-api/workspace/generators/npm-package)
757757
- [ci-workflow](/nx-api/workspace/generators/ci-workflow)
758+
- [infer-targets](/nx-api/workspace/generators/infer-targets)

e2e/nx/src/workspace.test.ts

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,150 @@ import {
1313
tmpProjPath,
1414
uniq,
1515
updateFile,
16+
updateJson,
1617
} from '@nx/e2e/utils';
1718
import { join } from 'path';
1819

1920
let proj: string;
2021

22+
describe('@nx/workspace:infer-targets', () => {
23+
beforeEach(() => {
24+
proj = newProject({
25+
packages: ['@nx/playwright', '@nx/remix', '@nx/eslint', '@nx/jest'],
26+
});
27+
});
28+
29+
afterEach(() => cleanupProject());
30+
31+
it('should run or skip conversions depending on whether executors are present', async () => {
32+
// default case, everything is generated with crystal, everything should be skipped
33+
const remixApp = uniq('remix');
34+
runCLI(
35+
`generate @nx/remix:app ${remixApp} --dir apps --unitTestRunner jest --e2eTestRunner=playwright --projectNameAndDirectoryFormat=as-provided --no-interactive`
36+
);
37+
38+
const output = runCLI(`generate infer-targets --no-interactive`);
39+
40+
expect(output).toContain('@nx/remix:convert-to-inferred - Skipped');
41+
expect(output).toContain('@nx/playwright:convert-to-inferred - Skipped');
42+
expect(output).toContain('@nx/eslint:convert-to-inferred - Skipped');
43+
expect(output).toContain('@nx/jest:convert-to-inferred - Skipped');
44+
45+
// if we make sure there are executors to convert, conversions will run
46+
updateJson('nx.json', (json) => {
47+
json.plugins = [];
48+
return json;
49+
});
50+
51+
updateJson(join('apps', remixApp, 'project.json'), (json) => {
52+
json.targets = {
53+
build: {
54+
executor: '@nx/remix:build',
55+
},
56+
lint: {
57+
executor: '@nx/eslint:lint',
58+
},
59+
};
60+
return json;
61+
});
62+
63+
const output2 = runCLI(`generate infer-targets --no-interactive`);
64+
65+
expect(output2).toContain('@nx/remix:convert-to-inferred - Success');
66+
expect(output2).toContain('@nx/eslint:convert-to-inferred - Success');
67+
});
68+
69+
it('should run or skip only specific conversions if --plugins is passed', async () => {
70+
// default case, everything is generated with crystal, relevant plugins should be skipped
71+
const remixApp = uniq('remix');
72+
runCLI(
73+
`generate @nx/remix:app ${remixApp} --dir apps --unitTestRunner jest --e2eTestRunner=playwright --projectNameAndDirectoryFormat=as-provided --no-interactive`
74+
);
75+
76+
const output = runCLI(
77+
`generate infer-targets --plugins=@nx/eslint,@nx/jest --no-interactive`
78+
);
79+
80+
expect(output).toContain('@nx/eslint:convert-to-inferred - Skipped');
81+
expect(output).toContain('@nx/jest:convert-to-inferred - Skipped');
82+
83+
expect(output).not.toContain('@nx/remix');
84+
expect(output).not.toContain('@nx/playwright');
85+
86+
// if we make sure there are executors to convert, relevant conversions will run
87+
updateJson('nx.json', (json) => {
88+
json.plugins = [];
89+
return json;
90+
});
91+
92+
updateJson(join('apps', remixApp, 'project.json'), (json) => {
93+
json.targets = {
94+
build: {
95+
executor: '@nx/remix:build',
96+
},
97+
lint: {
98+
executor: '@nx/eslint:lint',
99+
},
100+
};
101+
return json;
102+
});
103+
104+
const output2 = runCLI(
105+
`generate infer-targets --plugins=@nx/remix,@nx/eslint --no-interactive`
106+
);
107+
108+
expect(output2).toContain('@nx/remix:convert-to-inferred - Success');
109+
expect(output2).toContain('@nx/eslint:convert-to-inferred - Success');
110+
111+
expect(output2).not.toContain('@nx/jest');
112+
expect(output2).not.toContain('@nx/playwright');
113+
});
114+
115+
it('should run only specific conversions for a specific project if --project is passed', async () => {
116+
// even if we make sure there are executors for remix & remix-e2e, only remix conversions will run with --project option
117+
const remixApp = uniq('remix');
118+
runCLI(
119+
`generate @nx/remix:app ${remixApp} --dir apps --unitTestRunner jest --e2eTestRunner=playwright --projectNameAndDirectoryFormat=as-provided --no-interactive`
120+
);
121+
122+
updateJson('nx.json', (json) => {
123+
json.plugins = [];
124+
return json;
125+
});
126+
127+
updateJson(join('apps', remixApp, 'project.json'), (json) => {
128+
json.targets = {
129+
build: {
130+
executor: '@nx/remix:build',
131+
},
132+
lint: {
133+
executor: '@nx/eslint:lint',
134+
},
135+
};
136+
return json;
137+
});
138+
139+
updateJson(join('apps', `${remixApp}-e2e`, 'project.json'), (json) => {
140+
json.targets = {
141+
e2e: {
142+
executor: '@nx/playwright:playwright',
143+
},
144+
};
145+
return json;
146+
});
147+
148+
const output2 = runCLI(
149+
`generate infer-targets --project ${remixApp} --no-interactive`
150+
);
151+
152+
expect(output2).toContain('@nx/remix:convert-to-inferred - Success');
153+
expect(output2).toContain('@nx/eslint:convert-to-inferred - Success');
154+
155+
expect(output2).toContain('@nx/jest:convert-to-inferred - Skipped');
156+
expect(output2).toContain('@nx/playwright:convert-to-inferred - Skipped');
157+
});
158+
});
159+
21160
describe('@nx/workspace:convert-to-monorepo', () => {
22161
beforeEach(() => {
23162
proj = newProject({ packages: ['@nx/react', '@nx/js'] });

packages/cypress/src/generators/convert-to-inferred/convert-to-inferred.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,10 @@ import {
44
type TargetConfiguration,
55
type Tree,
66
} from '@nx/devkit';
7-
import { migrateProjectExecutorsToPlugin } from '@nx/devkit/src/generators/plugin-migrations/executor-to-plugin-migrator';
7+
import {
8+
migrateProjectExecutorsToPlugin,
9+
NoTargetsToMigrateError,
10+
} from '@nx/devkit/src/generators/plugin-migrations/executor-to-plugin-migrator';
811
import {
912
processTargetOutputs,
1013
toProjectRelativePath,
@@ -46,7 +49,7 @@ export async function convertToInferred(tree: Tree, options: Schema) {
4649
);
4750

4851
if (migratedProjects.size === 0) {
49-
throw new Error('Could not find any targets to migrate.');
52+
throw new NoTargetsToMigrateError();
5053
}
5154

5255
if (!options.skipFormat) {

packages/detox/src/generators/convert-to-inferred/convert-to-inferred.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,10 @@ import {
77
updateNxJson,
88
} from '@nx/devkit';
99
import { AggregatedLog } from '@nx/devkit/src/generators/plugin-migrations/aggregate-log-util';
10-
import { migrateProjectExecutorsToPluginV1 } from '@nx/devkit/src/generators/plugin-migrations/executor-to-plugin-migrator';
10+
import {
11+
migrateProjectExecutorsToPluginV1,
12+
NoTargetsToMigrateError,
13+
} from '@nx/devkit/src/generators/plugin-migrations/executor-to-plugin-migrator';
1114
import { createNodes } from '../../plugins/plugin';
1215
import { processBuildOptions } from './lib/process-build-options';
1316
import { postTargetTransformer } from './lib/post-target-transformer';
@@ -97,7 +100,7 @@ export async function convertToInferred(tree: Tree, options: Schema) {
97100
updateNxJson(tree, nxJson);
98101

99102
if (migratedProjects.size === 0) {
100-
throw new Error('Could not find any targets to migrate.');
103+
throw new NoTargetsToMigrateError();
101104
}
102105

103106
if (!options.skipFormat) {

0 commit comments

Comments
 (0)