@nx/cypress 24.0.0-pr.37405.acdaf31 → 24.0.0-rc.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/dist/internal.d.ts +1 -1
  2. package/dist/plugins/cypress-preset.js +47 -3
  3. package/dist/src/executors/cypress/{cypress.impl.d.ts → schema.d.ts} +0 -8
  4. package/dist/src/executors/cypress/schema.js +2 -0
  5. package/dist/src/generators/base-setup/base-setup.d.ts +5 -1
  6. package/dist/src/generators/base-setup/files/config-js-cjs/cypress.config.js__ext__ +1 -1
  7. package/dist/src/generators/base-setup/files/config-js-esm/cypress.config.js__ext__ +1 -1
  8. package/dist/src/generators/base-setup/files/config-ts-cjs/cypress.config.ts__ext__ +1 -1
  9. package/dist/src/generators/base-setup/files/config-ts-esm/cypress.config.ts__ext__ +1 -1
  10. package/dist/src/generators/component-configuration/component-configuration.js +7 -57
  11. package/dist/src/generators/component-configuration/schema.d.ts +0 -2
  12. package/dist/src/generators/configuration/configuration.d.ts +0 -5
  13. package/dist/src/generators/configuration/configuration.js +16 -68
  14. package/dist/src/generators/configuration/schema.json +0 -6
  15. package/dist/src/generators/convert-to-inferred/convert-to-inferred.js +26 -0
  16. package/dist/src/generators/convert-to-inferred/lib/add-build-target-to-ct-preset.d.ts +7 -0
  17. package/dist/src/generators/convert-to-inferred/lib/add-build-target-to-ct-preset.js +71 -0
  18. package/dist/src/generators/convert-to-inferred/lib/add-dev-server-target-to-config.js +3 -2
  19. package/dist/src/generators/convert-to-inferred/lib/add-exclude-spec-pattern.js +2 -1
  20. package/dist/src/generators/convert-to-inferred/lib/upsert-baseUrl.js +2 -1
  21. package/dist/src/generators/init/init.js +2 -45
  22. package/dist/src/generators/init/schema.d.ts +0 -1
  23. package/dist/src/migrations/update-23-3-0/adapt-query-command-overwrites.md +1 -1
  24. package/dist/src/migrations/update-23-3-0/ai-instructions-for-cypress-16.md +2 -2
  25. package/dist/src/migrations/update-23-3-0/upgrade-to-cypress-16.js +3 -2
  26. package/dist/src/migrations/update-24-0-0/finish-inferred-conversion.md +12 -0
  27. package/dist/src/migrations/update-24-0-0/remove-deprecated-executors.d.ts +6 -0
  28. package/dist/src/migrations/update-24-0-0/remove-deprecated-executors.js +10 -0
  29. package/dist/src/migrations/update-24-0-0/remove-deprecated-executors.md +61 -0
  30. package/dist/src/plugins/plugin.d.ts +8 -0
  31. package/dist/src/plugins/plugin.js +23 -5
  32. package/dist/src/utils/add-linter.d.ts +0 -5
  33. package/dist/src/utils/add-linter.js +1 -2
  34. package/dist/src/utils/config.d.ts +4 -0
  35. package/dist/src/utils/config.js +47 -2
  36. package/dist/src/utils/find-target-options.js +0 -24
  37. package/executors.json +1 -7
  38. package/migrations.json +8 -0
  39. package/package.json +6 -7
  40. package/dist/src/executors/cypress/cypress.impl.js +0 -136
  41. package/dist/src/executors/cypress/schema.json +0 -186
  42. package/dist/src/utils/deprecation.d.ts +0 -3
  43. package/dist/src/utils/deprecation.js +0 -20
  44. package/dist/src/utils/start-dev-server.d.ts +0 -9
  45. package/dist/src/utils/start-dev-server.js +0 -165
@@ -2,4 +2,4 @@ export * from './src/utils/versions';
2
2
  export * from './src/utils/ct-helpers';
3
3
  export * from './src/utils/find-target-options';
4
4
  export * from './src/utils/config';
5
- export type { CypressExecutorOptions } from './src/executors/cypress/cypress.impl';
5
+ export type { CypressExecutorOptions } from './src/executors/cypress/schema';
@@ -130,6 +130,19 @@ function nxE2EPreset(pathToConfig, options) {
130
130
  if (options?.bundler === 'vite') {
131
131
  on('file:preprocessor', (0, preprocessor_vite_1.default)(options?.viteConfigOverrides));
132
132
  }
133
+ // Set by Nx when the web server task already reported it is ready
134
+ const readyUrl = process.env.NX_READY_URL;
135
+ if (readyUrl && isBaseUrlOverridden(config, options)) {
136
+ console.log(`Testing against ${config.baseUrl} from --config or CYPRESS_BASE_URL, not the server Nx started on ${readyUrl}`);
137
+ await waitForServer(config.baseUrl, options?.webServerConfig, (ms) => `${config.baseUrl} did not respond within ${ms}ms. This can be configured in cypress.config.ts.`);
138
+ return;
139
+ }
140
+ if (readyUrl) {
141
+ config.baseUrl = (0, devkit_1.getReadyUrl)(config.baseUrl);
142
+ console.log(`Testing against the server Nx started on ${config.baseUrl}`);
143
+ // Cypress only applies config changes returned from setupNodeEvents.
144
+ return config;
145
+ }
133
146
  if (!options?.webServerCommands) {
134
147
  return;
135
148
  }
@@ -158,23 +171,54 @@ function nxE2EPreset(pathToConfig, options) {
158
171
  };
159
172
  return baseConfig;
160
173
  }
161
- function waitForServer(url, webServerConfig) {
174
+ // Cypress keeps the config file's values in rawJson, without --config, and
175
+ // reports CYPRESS_BASE_URL as from 'env'. A config file that reads
176
+ // CYPRESS_BASE_URL has it in rawJson too. Inferred CI targets pass ciBaseUrl.
177
+ function isBaseUrlOverridden(config, options) {
178
+ if (!config.baseUrl) {
179
+ return false;
180
+ }
181
+ if (config.resolved?.baseUrl?.from === 'env') {
182
+ return true;
183
+ }
184
+ const isInferredCiTarget = !!options?.ciWebServerCommand &&
185
+ config.env?.webServerCommand === options.ciWebServerCommand;
186
+ return (!isSameUrl(config.baseUrl, config.rawJson?.e2e?.baseUrl) &&
187
+ !(isInferredCiTarget && isSameUrl(config.baseUrl, options.ciBaseUrl)));
188
+ }
189
+ function isSameUrl(url, other) {
190
+ if (typeof other !== 'string') {
191
+ return false;
192
+ }
193
+ try {
194
+ return new URL(url).href === new URL(other).href;
195
+ }
196
+ catch {
197
+ return url === other;
198
+ }
199
+ }
200
+ function waitForServer(url, webServerConfig, timeoutMessage = (ms) => `Web server failed to start in ${ms}ms. This can be configured in cypress.config.ts.`) {
162
201
  return new Promise((resolve, reject) => {
163
202
  let pollTimeout;
164
203
  const { protocol } = new URL(url);
204
+ let settled = false;
165
205
  const timeoutDuration = webServerConfig?.timeout ?? 60 * 1000;
166
206
  const timeout = setTimeout(() => {
207
+ settled = true;
167
208
  clearTimeout(pollTimeout);
168
- reject(new Error(`Web server failed to start in ${timeoutDuration}ms. This can be configured in cypress.config.ts.`));
209
+ reject(new Error(timeoutMessage(timeoutDuration)));
169
210
  }, timeoutDuration);
170
211
  const makeRequest = protocol === 'https:' ? https_1.request : http_1.request;
171
212
  function pollForServer() {
172
213
  const request = makeRequest(url, () => {
214
+ settled = true;
173
215
  clearTimeout(timeout);
174
216
  resolve();
175
217
  });
176
218
  request.on('error', () => {
177
- pollTimeout = setTimeout(pollForServer, 100);
219
+ if (!settled) {
220
+ pollTimeout = setTimeout(pollForServer, 100);
221
+ }
178
222
  });
179
223
  // Don't forget to end the request
180
224
  request.end();
@@ -1,4 +1,3 @@
1
- import { ExecutorContext } from '@nx/devkit';
2
1
  export type Json = {
3
2
  [k: string]: any;
4
3
  };
@@ -7,10 +6,6 @@ export interface CypressExecutorOptions extends Json {
7
6
  watch?: boolean;
8
7
  devServerTarget?: string;
9
8
  headed?: boolean;
10
- /**
11
- * @deprecated Cypress runs headless by default. Use the --watch flag to
12
- * control head/headless behavior instead. It will be removed in Nx v23.
13
- **/
14
9
  headless?: boolean;
15
10
  exit?: boolean;
16
11
  key?: string;
@@ -33,6 +28,3 @@ export interface CypressExecutorOptions extends Json {
33
28
  runnerUi?: boolean;
34
29
  autoCancelAfterFailures?: boolean | number;
35
30
  }
36
- export default function cypressExecutor(options: CypressExecutorOptions, context: ExecutorContext): Promise<{
37
- success: any;
38
- }>;
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -15,8 +15,12 @@ export interface CypressBaseSetupSchema {
15
15
  * which merges in its own `component` block afterwards).
16
16
  */
17
17
  e2ePreset?: {
18
- /** Pre-formatted (indented) JSON of NxCypressE2EPresetOptions. */
18
+ /**
19
+ * NxCypressE2EPresetOptions rendered by `renderE2EPresetOptions`: indented
20
+ * JSON, except for values the config picks at runtime from `CI`.
21
+ */
19
22
  presetOptions: string;
23
+ /** A JS expression rendered by `renderE2EPresetOptions`, not a bare URL. */
20
24
  baseUrl?: string;
21
25
  };
22
26
  }
@@ -7,6 +7,6 @@ const { defineConfig } = require('cypress');
7
7
  module.exports = defineConfig({<% if (e2ePreset) { %>
8
8
  e2e: {
9
9
  ...nxE2EPreset(__filename, <%- presetOptions %>),<% if (baseUrl) { %>
10
- baseUrl: '<%- baseUrl %>',<% } %>
10
+ baseUrl: <%- baseUrl %>,<% } %>
11
11
  },
12
12
  <% } %>});
@@ -7,6 +7,6 @@ import { defineConfig } from 'cypress';
7
7
  export default defineConfig({<% if (e2ePreset) { %>
8
8
  e2e: {
9
9
  ...nxE2EPreset(import.meta.url, <%- presetOptions %>),<% if (baseUrl) { %>
10
- baseUrl: '<%- baseUrl %>',<% } %>
10
+ baseUrl: <%- baseUrl %>,<% } %>
11
11
  },
12
12
  <% } %>});
@@ -7,6 +7,6 @@ const { defineConfig } = require('cypress');
7
7
  module.exports = defineConfig({<% if (e2ePreset) { %>
8
8
  e2e: {
9
9
  ...nxE2EPreset(__filename, <%- presetOptions %>),<% if (baseUrl) { %>
10
- baseUrl: '<%- baseUrl %>',<% } %>
10
+ baseUrl: <%- baseUrl %>,<% } %>
11
11
  },
12
12
  <% } %>});
@@ -7,6 +7,6 @@ import { defineConfig } from 'cypress';
7
7
  export default defineConfig({<% if (e2ePreset) { %>
8
8
  e2e: {
9
9
  ...nxE2EPreset(import.meta.url, <%- presetOptions %>),<% if (baseUrl) { %>
10
- baseUrl: '<%- baseUrl %>',<% } %>
10
+ baseUrl: <%- baseUrl %>,<% } %>
11
11
  },
12
12
  <% } %>});
@@ -8,21 +8,17 @@ const devkit_1 = require("@nx/devkit");
8
8
  const internal_1 = require("@nx/devkit/internal");
9
9
  const internal_2 = require("@nx/js/internal");
10
10
  const assert_supported_cypress_version_1 = require("../../utils/assert-supported-cypress-version");
11
- const deprecation_1 = require("../../utils/deprecation");
12
11
  const versions_1 = require("../../utils/versions");
13
12
  const base_setup_1 = require("../base-setup/base-setup");
14
- const init_1 = tslib_1.__importDefault(require("../init/init"));
13
+ const init_1 = tslib_1.__importStar(require("../init/init"));
15
14
  function componentConfigurationGenerator(tree, options) {
16
- return componentConfigurationGeneratorInternal(tree, {
17
- addPlugin: false,
18
- ...options,
19
- });
15
+ return componentConfigurationGeneratorInternal(tree, options);
20
16
  }
21
17
  async function componentConfigurationGeneratorInternal(tree, options) {
22
18
  (0, assert_supported_cypress_version_1.assertSupportedCypressVersion)(tree);
23
19
  (0, internal_2.assertNotUsingTsSolutionSetup)(tree, 'cypress', 'component-configuration');
24
20
  const tasks = [];
25
- const opts = normalizeOptions(tree, options);
21
+ const opts = normalizeOptions(options);
26
22
  if (opts.bundler === 'vite') {
27
23
  (0, versions_1.assertViteSupportsInstalledCypress)(tree);
28
24
  }
@@ -38,29 +34,18 @@ async function componentConfigurationGeneratorInternal(tree, options) {
38
34
  skipFormat: true,
39
35
  }));
40
36
  }
41
- const nxJson = (0, devkit_1.readNxJson)(tree);
42
- const hasPlugin = nxJson.plugins?.some((p) => typeof p === 'string'
43
- ? p === '@nx/cypress/plugin'
44
- : p.plugin === '@nx/cypress/plugin');
37
+ await (0, init_1.addPlugin)(tree, await (0, devkit_1.createProjectGraphAsync)(), false);
45
38
  const projectConfig = (0, devkit_1.readProjectConfiguration)(tree, opts.project);
46
39
  addProjectFiles(tree, projectConfig, opts);
47
- if (!hasPlugin || opts.addExplicitTargets) {
48
- (0, deprecation_1.warnCypressExecutorGenerating)();
49
- addTargetToProject(tree, projectConfig, opts);
50
- }
51
- updateNxJsonConfiguration(tree, hasPlugin);
40
+ updateNxJsonConfiguration(tree);
52
41
  updateTsConfigForComponentTesting(tree, projectConfig);
53
42
  if (!opts.skipFormat) {
54
43
  await (0, devkit_1.formatFiles)(tree);
55
44
  }
56
45
  return (0, devkit_1.runTasksInSerial)(...tasks);
57
46
  }
58
- function normalizeOptions(tree, options) {
59
- const nxJson = (0, devkit_1.readNxJson)(tree);
60
- const addPlugin = process.env.NX_ADD_PLUGINS !== 'false' &&
61
- nxJson.useInferencePlugins !== false;
47
+ function normalizeOptions(options) {
62
48
  return {
63
- addPlugin,
64
49
  ...options,
65
50
  framework: options.framework ?? null,
66
51
  directory: options.directory ?? 'cypress',
@@ -107,17 +92,7 @@ function addProjectFiles(tree, projectConfig, opts) {
107
92
  ext: '',
108
93
  });
109
94
  }
110
- function addTargetToProject(tree, projectConfig, opts) {
111
- projectConfig.targets['component-test'] = {
112
- executor: '@nx/cypress:cypress',
113
- options: {
114
- cypressConfig: (0, devkit_1.joinPathFragments)(projectConfig.root, 'cypress.config.ts'),
115
- testingType: 'component',
116
- },
117
- };
118
- (0, devkit_1.updateProjectConfiguration)(tree, opts.project, projectConfig);
119
- }
120
- function updateNxJsonConfiguration(tree, hasPlugin) {
95
+ function updateNxJsonConfiguration(tree) {
121
96
  const nxJson = (0, devkit_1.readNxJson)(tree);
122
97
  const productionFileSet = nxJson.namedInputs?.production;
123
98
  if (productionFileSet) {
@@ -128,31 +103,6 @@ function updateNxJsonConfiguration(tree, hasPlugin) {
128
103
  '!{projectRoot}/cypress.config.[jt]s',
129
104
  ]));
130
105
  }
131
- if (!hasPlugin) {
132
- const cacheableOperations = nxJson.tasksRunnerOptions?.default?.options?.cacheableOperations;
133
- if (cacheableOperations &&
134
- !cacheableOperations.includes('component-test')) {
135
- cacheableOperations.push('component-test');
136
- }
137
- // Either a `target: 'component-test'` default or a default keyed on
138
- // the executor we're scaffolding will apply to the new target — pick
139
- // the more specific (target-keyed) when both are present.
140
- const existingForTarget = (0, internal_1.findTargetDefault)(nxJson.targetDefaults, {
141
- target: 'component-test',
142
- });
143
- const existingForExecutor = (0, internal_1.findTargetDefault)(nxJson.targetDefaults, {
144
- executor: '@nx/cypress:cypress',
145
- });
146
- const existing = existingForTarget ?? existingForExecutor;
147
- (0, internal_1.upsertTargetDefault)(tree, nxJson, {
148
- target: 'component-test',
149
- cache: existing?.cache ?? true,
150
- inputs: existing?.inputs ?? [
151
- 'default',
152
- productionFileSet ? '^production' : '^default',
153
- ],
154
- });
155
- }
156
106
  (0, devkit_1.updateNxJson)(tree, nxJson);
157
107
  }
158
108
  function updateTsConfigForComponentTesting(tree, projectConfig) {
@@ -4,12 +4,10 @@ export interface CypressComponentConfigurationSchema {
4
4
  directory?: string;
5
5
  bundler?: 'webpack' | 'vite';
6
6
  jsx?: boolean;
7
- addPlugin?: boolean;
8
7
  skipPackageJson?: boolean;
9
8
 
10
9
  /**
11
10
  * @internal
12
11
  */
13
- addExplicitTargets?: boolean;
14
12
  framework?: 'next';
15
13
  }
@@ -7,10 +7,6 @@ export interface CypressE2EConfigSchema {
7
7
  js?: boolean;
8
8
  skipFormat?: boolean;
9
9
  enableTypedLinting?: boolean;
10
- /**
11
- * @deprecated Use `enableTypedLinting` instead. This option will be removed in Nx v24.
12
- */
13
- setParserOptionsProject?: boolean;
14
10
  skipPackageJson?: boolean;
15
11
  bundler?: 'webpack' | 'vite' | 'none';
16
12
  devServerTarget?: string;
@@ -21,7 +17,6 @@ export interface CypressE2EConfigSchema {
21
17
  webServerCommands?: Record<string, string>;
22
18
  ciWebServerCommand?: string;
23
19
  ciBaseUrl?: string;
24
- addPlugin?: boolean;
25
20
  }
26
21
  export declare function configurationGenerator(tree: Tree, options: CypressE2EConfigSchema): Promise<GeneratorCallback>;
27
22
  export declare function configurationGeneratorInternal(tree: Tree, options: CypressE2EConfigSchema): Promise<GeneratorCallback>;
@@ -11,15 +11,11 @@ const path_1 = require("path");
11
11
  const add_linter_1 = require("../../utils/add-linter");
12
12
  const assert_supported_cypress_version_1 = require("../../utils/assert-supported-cypress-version");
13
13
  const config_1 = require("../../utils/config");
14
- const deprecation_1 = require("../../utils/deprecation");
15
14
  const versions_1 = require("../../utils/versions");
16
15
  const base_setup_1 = require("../base-setup/base-setup");
17
16
  const init_1 = tslib_1.__importStar(require("../init/init"));
18
17
  function configurationGenerator(tree, options) {
19
- return configurationGeneratorInternal(tree, {
20
- addPlugin: false,
21
- ...options,
22
- });
18
+ return configurationGeneratorInternal(tree, options);
23
19
  }
24
20
  async function configurationGeneratorInternal(tree, options) {
25
21
  (0, assert_supported_cypress_version_1.assertSupportedCypressVersion)(tree);
@@ -33,18 +29,10 @@ async function configurationGeneratorInternal(tree, options) {
33
29
  skipFormat: true,
34
30
  }));
35
31
  }
36
- else if (opts.addPlugin) {
32
+ else {
37
33
  await (0, init_1.addPlugin)(tree, projectGraph, false);
38
34
  }
39
- const nxJson = (0, devkit_1.readNxJson)(tree);
40
- const hasPlugin = nxJson.plugins?.some((p) => typeof p === 'string'
41
- ? p === '@nx/cypress/plugin'
42
- : p.plugin === '@nx/cypress/plugin');
43
- await addFiles(tree, opts, projectGraph, hasPlugin);
44
- if (!hasPlugin) {
45
- (0, deprecation_1.warnCypressExecutorGenerating)();
46
- addTarget(tree, opts, projectGraph);
47
- }
35
+ await addFiles(tree, opts, projectGraph);
48
36
  const projectTsConfigPath = (0, devkit_1.joinPathFragments)(opts.projectRoot, 'tsconfig.json');
49
37
  if (tree.exists(projectTsConfigPath)) {
50
38
  (0, devkit_1.updateJson)(tree, projectTsConfigPath, (json) => {
@@ -130,10 +118,6 @@ In this case you need to provide a devServerTarget,'<projectName>:<targetName>[:
130
118
  if (!options.baseUrl && !devServerTarget) {
131
119
  throw new Error('Either baseUrl or devServerTarget must be provided');
132
120
  }
133
- const nxJson = (0, devkit_1.readNxJson)(tree);
134
- options.addPlugin ??=
135
- process.env.NX_ADD_PLUGINS !== 'false' &&
136
- nxJson.useInferencePlugins !== false;
137
121
  return {
138
122
  ...options,
139
123
  bundler: options.bundler ?? 'webpack',
@@ -166,7 +150,7 @@ async function promptForMissingServeData(projectName) {
166
150
  baseUrl: `http://localhost:${port}`,
167
151
  };
168
152
  }
169
- async function addFiles(tree, options, projectGraph, hasPlugin) {
153
+ async function addFiles(tree, options, projectGraph) {
170
154
  const projectConfig = (0, devkit_1.readProjectConfiguration)(tree, options.project);
171
155
  const hasTsConfig = tree.exists((0, devkit_1.joinPathFragments)(projectConfig.root, 'tsconfig.json'));
172
156
  const offsetFromProjectRoot = options.directory
@@ -194,12 +178,12 @@ async function addFiles(tree, options, projectGraph, hasPlugin) {
194
178
  let webServerCommands;
195
179
  let ciWebServerCommand;
196
180
  let ciBaseUrl;
197
- if (hasPlugin && options.webServerCommands && options.ciWebServerCommand) {
181
+ if (options.webServerCommands && options.ciWebServerCommand) {
198
182
  webServerCommands = options.webServerCommands;
199
183
  ciWebServerCommand = options.ciWebServerCommand;
200
184
  ciBaseUrl = options.ciBaseUrl;
201
185
  }
202
- else if (hasPlugin && options.devServerTarget) {
186
+ else if (options.devServerTarget) {
203
187
  webServerCommands = {};
204
188
  webServerCommands.default = 'nx run ' + options.devServerTarget;
205
189
  const parsedTarget = (0, devkit_1.parseTargetString)(options.devServerTarget, projectGraph);
@@ -214,6 +198,13 @@ async function addFiles(tree, options, projectGraph, hasPlugin) {
214
198
  ciWebServerCommand = `nx run ${parsedTarget.project}:serve-static`;
215
199
  }
216
200
  }
201
+ if (typeof options.port === 'number') {
202
+ for (const name of Object.keys(webServerCommands ?? {})) {
203
+ webServerCommands[name] += ` --port=${options.port}`;
204
+ }
205
+ if (ciWebServerCommand)
206
+ ciWebServerCommand += ` --port=${options.port}`;
207
+ }
217
208
  const e2ePresetOptions = {
218
209
  cypressDir: options.directory,
219
210
  bundler: options.bundler === 'vite' ? 'vite' : undefined,
@@ -221,6 +212,7 @@ async function addFiles(tree, options, projectGraph, hasPlugin) {
221
212
  ciWebServerCommand,
222
213
  ciBaseUrl,
223
214
  };
215
+ const renderedPreset = (0, config_1.renderE2EPresetOptions)(e2ePresetOptions, options.baseUrl);
224
216
  (0, base_setup_1.addBaseCypressSetup)(tree, {
225
217
  project: options.project,
226
218
  directory: options.directory,
@@ -229,10 +221,8 @@ async function addFiles(tree, options, projectGraph, hasPlugin) {
229
221
  // Generate a fresh config already containing the e2e preset (base-setup
230
222
  // no-ops when a config already exists - see the merge below).
231
223
  e2ePreset: {
232
- presetOptions: JSON.stringify(e2ePresetOptions, null, 2)
233
- .split('\n')
234
- .join('\n '),
235
- baseUrl: options.baseUrl,
224
+ presetOptions: renderedPreset.presetOptions.split('\n').join('\n '),
225
+ baseUrl: renderedPreset.baseUrl,
236
226
  },
237
227
  });
238
228
  if (cypressConfigExists) {
@@ -242,48 +232,6 @@ async function addFiles(tree, options, projectGraph, hasPlugin) {
242
232
  (0, devkit_1.toJS)(tree);
243
233
  }
244
234
  }
245
- function addTarget(tree, opts, projectGraph) {
246
- const projectConfig = (0, devkit_1.readProjectConfiguration)(tree, opts.project);
247
- projectConfig.targets ??= {};
248
- projectConfig.targets.e2e = {
249
- executor: '@nx/cypress:cypress',
250
- options: {
251
- cypressConfig: (0, devkit_1.joinPathFragments)(projectConfig.root, `cypress.config.${opts.js ? 'js' : 'ts'}`),
252
- testingType: 'e2e',
253
- },
254
- };
255
- if (opts.devServerTarget) {
256
- const parsedTarget = (0, devkit_1.parseTargetString)(opts.devServerTarget, projectGraph);
257
- projectConfig.targets.e2e.options = {
258
- ...projectConfig.targets.e2e.options,
259
- devServerTarget: opts.devServerTarget,
260
- port: opts.port,
261
- };
262
- const devServerProjectConfig = (0, devkit_1.readProjectConfiguration)(tree, parsedTarget.project);
263
- // Add production e2e target if serve target is found
264
- if (parsedTarget.configuration !== 'production' &&
265
- devServerProjectConfig.targets?.[parsedTarget.target]?.configurations?.['production']) {
266
- projectConfig.targets.e2e.configurations ??= {};
267
- projectConfig.targets.e2e.configurations['production'] = {
268
- devServerTarget: `${parsedTarget.project}:${parsedTarget.target}:production`,
269
- };
270
- }
271
- // Add ci/static e2e target if serve target is found
272
- if (devServerProjectConfig.targets?.['serve-static']) {
273
- projectConfig.targets.e2e.configurations ??= {};
274
- projectConfig.targets.e2e.configurations.ci = {
275
- devServerTarget: `${parsedTarget.project}:serve-static`,
276
- };
277
- }
278
- }
279
- else if (opts.baseUrl) {
280
- projectConfig.targets.e2e.options = {
281
- ...projectConfig.targets.e2e.options,
282
- baseUrl: opts.baseUrl,
283
- };
284
- }
285
- (0, devkit_1.updateProjectConfiguration)(tree, opts.project, projectConfig);
286
- }
287
235
  function createPackageJson(tree, options) {
288
236
  const projectConfig = (0, devkit_1.readProjectConfiguration)(tree, options.project);
289
237
  const packageJsonPath = (0, devkit_1.joinPathFragments)(projectConfig.root, 'package.json');
@@ -65,12 +65,6 @@
65
65
  "description": "Whether to enable typed linting. For flat configs, this configures the recommended `parserOptions.projectService` and `tsconfigRootDir`. For legacy `.eslintrc` configs, this configures `parserOptions.project`. We do not enable this by default for lint performance reasons.",
66
66
  "default": false
67
67
  },
68
- "setParserOptionsProject": {
69
- "type": "boolean",
70
- "description": "Deprecated alias for `enableTypedLinting`.",
71
- "default": false,
72
- "x-deprecated": "Use `enableTypedLinting` instead. This option will be removed in Nx v24."
73
- },
74
68
  "skipPackageJson": {
75
69
  "type": "boolean",
76
70
  "default": false,
@@ -5,6 +5,7 @@ const internal_1 = require("@nx/devkit/internal");
5
5
  const devkit_1 = require("@nx/devkit");
6
6
  const plugin_1 = require("../../plugins/plugin");
7
7
  const assert_supported_cypress_version_1 = require("../../utils/assert-supported-cypress-version");
8
+ const add_build_target_to_ct_preset_1 = require("./lib/add-build-target-to-ct-preset");
8
9
  const add_dev_server_target_to_config_1 = require("./lib/add-dev-server-target-to-config");
9
10
  const add_exclude_spec_pattern_1 = require("./lib/add-exclude-spec-pattern");
10
11
  const target_options_map_1 = require("./lib/target-options-map");
@@ -32,6 +33,9 @@ async function convertToInferred(tree, options) {
32
33
  }
33
34
  }
34
35
  function postTargetTransformer(target, tree, projectDetails, inferredTargetConfiguration) {
36
+ if (target.options?.testingType === 'component') {
37
+ moveBuildTargetToComponentTestingPreset(tree, target, projectDetails.projectName);
38
+ }
35
39
  if (target.options) {
36
40
  handlePropertiesInOptions(tree, target.options, projectDetails.root, target);
37
41
  if (Object.keys(target.options).length === 0) {
@@ -62,6 +66,28 @@ function postTargetTransformer(target, tree, projectDetails, inferredTargetConfi
62
66
  }
63
67
  return target;
64
68
  }
69
+ function moveBuildTargetToComponentTestingPreset(tree, target, projectName) {
70
+ const defaultConfiguration = target.defaultConfiguration
71
+ ? target.configurations?.[target.defaultConfiguration]
72
+ : undefined;
73
+ const buildTarget = defaultConfiguration?.devServerTarget ?? target.options.devServerTarget;
74
+ const { cypressConfig } = target.options;
75
+ if (buildTarget &&
76
+ !(cypressConfig &&
77
+ (0, add_build_target_to_ct_preset_1.addBuildTargetToComponentTestingPreset)(tree, cypressConfig, buildTarget))) {
78
+ devkit_1.logger.warn(`Could not set the component testing build target of project "${projectName}" in its Cypress config. Pass it to nxComponentTestingPreset as buildTarget: "${buildTarget}".`);
79
+ }
80
+ const removeDevServerTarget = (options, location) => {
81
+ if (options?.devServerTarget && options.devServerTarget !== buildTarget) {
82
+ devkit_1.logger.warn(`Removed devServerTarget "${options.devServerTarget}" from the ${location} of project "${projectName}". Component testing uses the single buildTarget passed to nxComponentTestingPreset.`);
83
+ }
84
+ delete options?.devServerTarget;
85
+ };
86
+ removeDevServerTarget(target.options, 'options');
87
+ for (const [name, configuration] of Object.entries(target.configurations ?? {})) {
88
+ removeDevServerTarget(configuration, `"${name}" configuration`);
89
+ }
90
+ }
65
91
  function handlePropertiesInOptions(tree, options, projectRoot, target) {
66
92
  let configFilePath;
67
93
  if ('cypressConfig' in options) {
@@ -0,0 +1,7 @@
1
+ import type { Tree } from '@nx/devkit';
2
+ /**
3
+ * Sets `buildTarget` in the `nxComponentTestingPreset` options. A non-empty
4
+ * string literal is kept and any other value gets an `||` fallback. Returns
5
+ * false when there is no such call or the value can't be resolved statically.
6
+ */
7
+ export declare function addBuildTargetToComponentTestingPreset(tree: Tree, configFilePath: string, buildTarget: string): boolean;
@@ -0,0 +1,71 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.addBuildTargetToComponentTestingPreset = addBuildTargetToComponentTestingPreset;
4
+ const internal_1 = require("@nx/js/internal");
5
+ const tsquery_1 = require("@phenomnomnominal/tsquery");
6
+ const migrations_1 = require("../../../utils/migrations");
7
+ let ts;
8
+ /**
9
+ * Sets `buildTarget` in the `nxComponentTestingPreset` options. A non-empty
10
+ * string literal is kept and any other value gets an `||` fallback. Returns
11
+ * false when there is no such call or the value can't be resolved statically.
12
+ */
13
+ function addBuildTargetToComponentTestingPreset(tree, configFilePath, buildTarget) {
14
+ ts ??= (0, internal_1.ensureTypescript)();
15
+ const contents = tree.read(configFilePath, 'utf-8');
16
+ if (contents === null) {
17
+ return false;
18
+ }
19
+ const presetCall = (0, tsquery_1.query)((0, migrations_1.parseSourceFile)(configFilePath, contents), 'CallExpression').find((call) => ts.isIdentifier(call.expression) &&
20
+ call.expression.text === 'nxComponentTestingPreset');
21
+ const [pathToConfig, options] = presetCall?.arguments ?? [];
22
+ if (!pathToConfig) {
23
+ return false;
24
+ }
25
+ const replace = (start, end, text) => tree.write(configFilePath, `${contents.slice(0, start)}${text}${contents.slice(end)}`);
26
+ const buildTargetLiteral = JSON.stringify(buildTarget);
27
+ if (!options) {
28
+ const insertAt = pathToConfig.getEnd();
29
+ replace(insertAt, insertAt, `, { buildTarget: ${buildTargetLiteral} }`);
30
+ return true;
31
+ }
32
+ if (!ts.isObjectLiteralExpression(options)) {
33
+ return false;
34
+ }
35
+ const effectiveProperty = [...options.properties]
36
+ .reverse()
37
+ .find((property) => ts.isSpreadAssignment(property) ||
38
+ isUnresolvedComputedName(property.name) ||
39
+ (0, migrations_1.getPropertyName)(property.name) === 'buildTarget');
40
+ if (!effectiveProperty) {
41
+ // `__proto__: value` sets the prototype, which can provide buildTarget
42
+ if (options.properties.some((property) => ts.isPropertyAssignment(property) &&
43
+ !ts.isComputedPropertyName(property.name) &&
44
+ (0, migrations_1.getPropertyName)(property.name) === '__proto__')) {
45
+ return false;
46
+ }
47
+ const insertAt = options.getStart() + 1;
48
+ replace(insertAt, insertAt, ` buildTarget: ${buildTargetLiteral},`);
49
+ return true;
50
+ }
51
+ if (ts.isShorthandPropertyAssignment(effectiveProperty)) {
52
+ replace(effectiveProperty.getStart(), effectiveProperty.getEnd(), `buildTarget: buildTarget || ${buildTargetLiteral}`);
53
+ return true;
54
+ }
55
+ if (!ts.isPropertyAssignment(effectiveProperty) ||
56
+ isUnresolvedComputedName(effectiveProperty.name)) {
57
+ return false;
58
+ }
59
+ const value = effectiveProperty.initializer;
60
+ if (ts.isStringLiteralLike(value) && value.text) {
61
+ return true;
62
+ }
63
+ // The factory parenthesizes the left operand when precedence requires it
64
+ const needsParentheses = ts.isParenthesizedExpression(ts.factory.createBinaryExpression(value, ts.SyntaxKind.BarBarToken, ts.factory.createStringLiteral(buildTarget)).left);
65
+ const valueText = value.getText();
66
+ replace(value.getStart(), value.getEnd(), `${needsParentheses ? `(${valueText})` : valueText} || ${buildTargetLiteral}`);
67
+ return true;
68
+ }
69
+ function isUnresolvedComputedName(name) {
70
+ return ts.isComputedPropertyName(name) && (0, migrations_1.getPropertyName)(name) === null;
71
+ }
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.addDevServerTargetToConfig = addDevServerTargetToConfig;
4
4
  const tsquery_1 = require("@phenomnomnominal/tsquery");
5
+ const migrations_1 = require("../../../utils/migrations");
5
6
  /**
6
7
  * Add or update the webServerCommands and ciWebServerCommand options in the Cypress Config
7
8
  * Scenarios Covered:
@@ -14,7 +15,7 @@ const tsquery_1 = require("@phenomnomnominal/tsquery");
14
15
  */
15
16
  function addDevServerTargetToConfig(tree, configFilePath, webServerCommands, ciDevServerTarget) {
16
17
  let configFileContents = tree.read(configFilePath, 'utf-8');
17
- let sourceFile = (0, tsquery_1.ast)(configFileContents);
18
+ let sourceFile = (0, migrations_1.parseSourceFile)(configFilePath, configFileContents);
18
19
  const NX_E2E_PRESET_SELECTOR = 'PropertyAssignment:has(Identifier[name=e2e]) CallExpression:has(Identifier[name=nxE2EPreset])';
19
20
  const nxE2ePresetOptionsNodes = (0, tsquery_1.query)(sourceFile, NX_E2E_PRESET_SELECTOR);
20
21
  if (nxE2ePresetOptionsNodes.length !== 0) {
@@ -38,7 +39,7 @@ function addDevServerTargetToConfig(tree, configFilePath, webServerCommands, ciD
38
39
  }
39
40
  if (ciDevServerTarget) {
40
41
  configFileContents = tree.read(configFilePath, 'utf-8');
41
- sourceFile = (0, tsquery_1.ast)(configFileContents);
42
+ sourceFile = (0, migrations_1.parseSourceFile)(configFilePath, configFileContents);
42
43
  nxE2ePresetOptionsNode = (0, tsquery_1.query)(sourceFile, NX_E2E_PRESET_SELECTOR)[0];
43
44
  const NX_E2E_PRESET_OPTIONS_SELECTOR = 'PropertyAssignment:has(Identifier[name=e2e]) CallExpression:has(Identifier[name=nxE2EPreset]) > ObjectLiteralExpression';
44
45
  const optionsObjectNodes = (0, tsquery_1.query)(sourceFile, NX_E2E_PRESET_OPTIONS_SELECTOR);
@@ -2,9 +2,10 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.addExcludeSpecPattern = addExcludeSpecPattern;
4
4
  const tsquery_1 = require("@phenomnomnominal/tsquery");
5
+ const migrations_1 = require("../../../utils/migrations");
5
6
  function addExcludeSpecPattern(tree, configFilePath, excludeSpecPattern) {
6
7
  let configFileContents = tree.read(configFilePath, 'utf-8');
7
- let sourceFile = (0, tsquery_1.ast)(configFileContents);
8
+ let sourceFile = (0, migrations_1.parseSourceFile)(configFilePath, configFileContents);
8
9
  const E2E_CONFIG_SELECTOR = 'PropertyAssignment:has(Identifier[name=e2e]) > ObjectLiteralExpression';
9
10
  const e2eConfigNodes = (0, tsquery_1.query)(sourceFile, E2E_CONFIG_SELECTOR);
10
11
  if (e2eConfigNodes.length !== 0) {
@@ -2,9 +2,10 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.upsertBaseUrl = upsertBaseUrl;
4
4
  const tsquery_1 = require("@phenomnomnominal/tsquery");
5
+ const migrations_1 = require("../../../utils/migrations");
5
6
  function upsertBaseUrl(tree, configFilePath, baseUrlValueInProject) {
6
7
  const configFileContents = tree.read(configFilePath, 'utf-8');
7
- const sourceFile = (0, tsquery_1.ast)(configFileContents);
8
+ const sourceFile = (0, migrations_1.parseSourceFile)(configFilePath, configFileContents);
8
9
  const BASE_URL_SELECTOR = 'PropertyAssignment:has(Identifier[name=e2e]) PropertyAssignment:has(Identifier[name="baseUrl"])';
9
10
  const baseUrlNodes = (0, tsquery_1.query)(sourceFile, BASE_URL_SELECTOR);
10
11
  if (baseUrlNodes.length !== 0) {