@nx/cypress 16.8.0-beta.4 → 16.8.0-beta.5

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 (27) hide show
  1. package/package.json +6 -6
  2. package/plugins/cypress-preset.js +15 -6
  3. package/src/executors/cypress/cypress.impl.js +179 -210
  4. package/src/generators/base-setup/base-setup.js +15 -5
  5. package/src/generators/component-configuration/component-configuration.js +42 -30
  6. package/src/generators/configuration/configuration.js +87 -70
  7. package/src/generators/convert-tslint-to-eslint/convert-tslint-to-eslint.js +75 -78
  8. package/src/generators/cypress-project/cypress-project.js +76 -63
  9. package/src/generators/init/init.js +16 -18
  10. package/src/generators/migrate-to-cypress-11/conversion.util.js +22 -16
  11. package/src/generators/migrate-to-cypress-11/migrate-to-cypress-11.js +38 -35
  12. package/src/migrations/update-12-8-0/remove-typescript-plugin.js +23 -26
  13. package/src/migrations/update-14-6-1/update-cypress-configs-presets.js +79 -68
  14. package/src/migrations/update-14-7-0/update-cypress-version-if-10.js +1 -2
  15. package/src/migrations/update-15-0-0/add-cypress-inputs.js +24 -29
  16. package/src/migrations/update-15-0-0/update-cy-mount-usage.js +25 -29
  17. package/src/migrations/update-15-1-0/cypress-11.js +34 -37
  18. package/src/migrations/update-15-5-0/update-to-cypress-12.js +3 -5
  19. package/src/migrations/update-16-0-0-add-nx-packages/update-16-0-0-add-nx-packages.js +3 -6
  20. package/src/migrations/update-16-2-0/update-cy-tsconfig.js +25 -24
  21. package/src/migrations/update-16-4-0/tsconfig-sourcemaps.js +52 -49
  22. package/src/plugins/preprocessor-vite.js +7 -5
  23. package/src/utils/add-linter.js +78 -81
  24. package/src/utils/config.js +54 -62
  25. package/src/utils/ct-helpers.js +6 -8
  26. package/src/utils/cypress-version.js +1 -1
  27. package/src/utils/find-target-options.js +37 -43
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nx/cypress",
3
- "version": "16.8.0-beta.4",
3
+ "version": "16.8.0-beta.5",
4
4
  "private": false,
5
5
  "description": "The Nx Plugin for Cypress contains executors and generators allowing your workspace to use the powerful Cypress integration testing capabilities.",
6
6
  "repository": {
@@ -34,10 +34,10 @@
34
34
  "migrations": "./migrations.json"
35
35
  },
36
36
  "dependencies": {
37
- "@nrwl/cypress": "16.8.0-beta.4",
38
- "@nx/devkit": "16.8.0-beta.4",
39
- "@nx/js": "16.8.0-beta.4",
40
- "@nx/linter": "16.8.0-beta.4",
37
+ "@nrwl/cypress": "16.8.0-beta.5",
38
+ "@nx/devkit": "16.8.0-beta.5",
39
+ "@nx/js": "16.8.0-beta.5",
40
+ "@nx/linter": "16.8.0-beta.5",
41
41
  "@phenomnomnominal/tsquery": "~5.0.1",
42
42
  "detect-port": "^1.5.1",
43
43
  "semver": "7.5.3",
@@ -55,5 +55,5 @@
55
55
  "access": "public"
56
56
  },
57
57
  "type": "commonjs",
58
- "gitHead": "c9aad2dc8c2b34531cf61549bff872e68c481e4e"
58
+ "gitHead": "ad2d1e8a55b2d1b358bb5e7483343d9757ff2c53"
59
59
  }
@@ -8,7 +8,7 @@ const preprocessor_vite_1 = require("../src/plugins/preprocessor-vite");
8
8
  function nxBaseCypressPreset(pathToConfig, options) {
9
9
  // used to set babel settings for react CT.
10
10
  process.env.NX_CYPRESS_COMPONENT_TEST =
11
- (options === null || options === void 0 ? void 0 : options.testingType) === 'component' ? 'true' : 'false';
11
+ options?.testingType === 'component' ? 'true' : 'false';
12
12
  // prevent from placing path outside the root of the workspace
13
13
  // if they pass in a file or directory
14
14
  const normalizedPath = (0, fs_1.lstatSync)(pathToConfig).isDirectory()
@@ -43,12 +43,21 @@ exports.nxBaseCypressPreset = nxBaseCypressPreset;
43
43
  * @param pathToConfig will be used to construct the output paths for videos and screenshots
44
44
  */
45
45
  function nxE2EPreset(pathToConfig, options) {
46
- const basePath = (options === null || options === void 0 ? void 0 : options.cypressDir) || 'src';
47
- const baseConfig = Object.assign(Object.assign({}, nxBaseCypressPreset(pathToConfig)), { fileServerFolder: '.', supportFile: `${basePath}/support/e2e.ts`, specPattern: `${basePath}/**/*.cy.{js,jsx,ts,tsx}`, fixturesFolder: `${basePath}/fixtures` });
48
- if ((options === null || options === void 0 ? void 0 : options.bundler) === 'vite') {
49
- return Object.assign(Object.assign({}, baseConfig), { setupNodeEvents(on) {
46
+ const basePath = options?.cypressDir || 'src';
47
+ const baseConfig = {
48
+ ...nxBaseCypressPreset(pathToConfig),
49
+ fileServerFolder: '.',
50
+ supportFile: `${basePath}/support/e2e.ts`,
51
+ specPattern: `${basePath}/**/*.cy.{js,jsx,ts,tsx}`,
52
+ fixturesFolder: `${basePath}/fixtures`,
53
+ };
54
+ if (options?.bundler === 'vite') {
55
+ return {
56
+ ...baseConfig,
57
+ setupNodeEvents(on) {
50
58
  on('file:preprocessor', (0, preprocessor_vite_1.default)());
51
- } });
59
+ },
60
+ };
52
61
  }
53
62
  return baseConfig;
54
63
  }
@@ -1,6 +1,5 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- const tslib_1 = require("tslib");
4
3
  const devkit_1 = require("@nx/devkit");
5
4
  const executor_utils_1 = require("nx/src/command-line/run/executor-utils");
6
5
  const fs_1 = require("fs");
@@ -9,48 +8,35 @@ const ct_helpers_1 = require("../../utils/ct-helpers");
9
8
  const cypress_version_1 = require("../../utils/cypress-version");
10
9
  const detectPort = require("detect-port");
11
10
  const Cypress = require('cypress'); // @NOTE: Importing via ES6 messes the whole test dependencies.
12
- function cypressExecutor(options, context) {
13
- var _a, e_1, _b, _c;
14
- return tslib_1.__awaiter(this, void 0, void 0, function* () {
15
- options = normalizeOptions(options, context);
16
- // this is used by cypress component testing presets to build the executor contexts with the correct configuration options.
17
- process.env.NX_CYPRESS_TARGET_CONFIGURATION = context.configurationName;
18
- let success;
11
+ async function cypressExecutor(options, context) {
12
+ options = normalizeOptions(options, context);
13
+ // this is used by cypress component testing presets to build the executor contexts with the correct configuration options.
14
+ process.env.NX_CYPRESS_TARGET_CONFIGURATION = context.configurationName;
15
+ let success;
16
+ for await (const devServerValues of startDevServer(options, context)) {
19
17
  try {
20
- for (var _d = true, _e = tslib_1.__asyncValues(startDevServer(options, context)), _f; _f = yield _e.next(), _a = _f.done, !_a; _d = true) {
21
- _c = _f.value;
22
- _d = false;
23
- const devServerValues = _c;
24
- try {
25
- success = yield runCypress(devServerValues.baseUrl, Object.assign(Object.assign({}, options), { portLockFilePath: devServerValues.portLockFilePath }));
26
- if (!options.watch)
27
- break;
28
- }
29
- catch (e) {
30
- devkit_1.logger.error(e.message);
31
- success = false;
32
- if (!options.watch)
33
- break;
34
- }
35
- }
18
+ success = await runCypress(devServerValues.baseUrl, {
19
+ ...options,
20
+ portLockFilePath: devServerValues.portLockFilePath,
21
+ });
22
+ if (!options.watch)
23
+ break;
36
24
  }
37
- catch (e_1_1) { e_1 = { error: e_1_1 }; }
38
- finally {
39
- try {
40
- if (!_d && !_a && (_b = _e.return)) yield _b.call(_e);
41
- }
42
- finally { if (e_1) throw e_1.error; }
25
+ catch (e) {
26
+ devkit_1.logger.error(e.message);
27
+ success = false;
28
+ if (!options.watch)
29
+ break;
43
30
  }
44
- return { success };
45
- });
31
+ }
32
+ return { success };
46
33
  }
47
34
  exports.default = cypressExecutor;
48
35
  function normalizeOptions(options, context) {
49
- var _a, _b, _c;
50
36
  options.env = options.env || {};
51
37
  if (options.testingType === 'component') {
52
- const project = (_b = (_a = context === null || context === void 0 ? void 0 : context.projectGraph) === null || _a === void 0 ? void 0 : _a.nodes) === null || _b === void 0 ? void 0 : _b[context.projectName];
53
- if ((_c = project === null || project === void 0 ? void 0 : project.data) === null || _c === void 0 ? void 0 : _c.root) {
38
+ const project = context?.projectGraph?.nodes?.[context.projectName];
39
+ if (project?.data?.root) {
54
40
  options.ctTailwindPath = (0, ct_helpers_1.getTempTailwindPath)(context);
55
41
  }
56
42
  }
@@ -104,202 +90,186 @@ A generator to migrate from v8 to v10 is provided. See https://nx.dev/cypress/v1
104
90
  `);
105
91
  }
106
92
  }
107
- function startDevServer(opts, context) {
108
- var _a, _b, _c;
109
- return tslib_1.__asyncGenerator(this, arguments, function* startDevServer_1() {
110
- var _d, e_2, _e, _f;
111
- // no dev server, return the provisioned base url
112
- if (!opts.devServerTarget || opts.skipServe) {
113
- yield yield tslib_1.__await({ baseUrl: opts.baseUrl });
114
- return yield tslib_1.__await(void 0);
115
- }
116
- const parsedDevServerTarget = (0, devkit_1.parseTargetString)(opts.devServerTarget, context.projectGraph);
117
- const [targetSupportsWatchOpt] = getValueFromSchema(context, parsedDevServerTarget, 'watch');
118
- const overrides = Object.assign({}, (targetSupportsWatchOpt ? { watch: opts.watch } : {}));
119
- if (opts.port === 'cypress-auto') {
120
- const freePort = yield tslib_1.__await(getPortForProject(context, parsedDevServerTarget));
121
- overrides['port'] = freePort;
122
- }
123
- else if (opts.port !== undefined) {
124
- overrides['port'] = opts.port;
125
- // zero is a special case that means any valid port so there is no reason to try to 'lock it'
126
- if (opts.port !== 0) {
127
- const didLock = attemptToLockPort(opts.port);
128
- if (!didLock) {
129
- devkit_1.logger.warn((0, devkit_1.stripIndents) `${opts.port} is potentially already in use by another cypress run.
93
+ async function* startDevServer(opts, context) {
94
+ // no dev server, return the provisioned base url
95
+ if (!opts.devServerTarget || opts.skipServe) {
96
+ yield { baseUrl: opts.baseUrl };
97
+ return;
98
+ }
99
+ const parsedDevServerTarget = (0, devkit_1.parseTargetString)(opts.devServerTarget, context.projectGraph);
100
+ const [targetSupportsWatchOpt] = getValueFromSchema(context, parsedDevServerTarget, 'watch');
101
+ const overrides = {
102
+ // @NOTE: Do not forward watch option if not supported by the target dev server,
103
+ // this is relevant for running Cypress against dev server target that does not support this option,
104
+ // for instance @nguniversal/builders:ssr-dev-server.
105
+ ...(targetSupportsWatchOpt ? { watch: opts.watch } : {}),
106
+ };
107
+ if (opts.port === 'cypress-auto') {
108
+ const freePort = await getPortForProject(context, parsedDevServerTarget);
109
+ overrides['port'] = freePort;
110
+ }
111
+ else if (opts.port !== undefined) {
112
+ overrides['port'] = opts.port;
113
+ // zero is a special case that means any valid port so there is no reason to try to 'lock it'
114
+ if (opts.port !== 0) {
115
+ const didLock = attemptToLockPort(opts.port);
116
+ if (!didLock) {
117
+ devkit_1.logger.warn((0, devkit_1.stripIndents) `${opts.port} is potentially already in use by another cypress run.
130
118
  If the port is in use, try using a different port value or passing --port='cypress-auto' to find a free port.`);
131
- }
132
- }
133
- }
134
- try {
135
- for (var _g = true, _h = tslib_1.__asyncValues(yield tslib_1.__await((0, devkit_1.runExecutor)(parsedDevServerTarget, overrides, context))), _j; _j = yield tslib_1.__await(_h.next()), _d = _j.done, !_d; _g = true) {
136
- _f = _j.value;
137
- _g = false;
138
- const output = _f;
139
- if (!output.success && !opts.watch)
140
- throw new Error('Could not compile application files');
141
- if (!opts.baseUrl &&
142
- !output.baseUrl &&
143
- !((_a = output.info) === null || _a === void 0 ? void 0 : _a.baseUrl) &&
144
- ((_b = output.info) === null || _b === void 0 ? void 0 : _b.port)) {
145
- output.baseUrl = `http://localhost:${output.info.port}`;
146
- }
147
- yield yield tslib_1.__await({
148
- baseUrl: opts.baseUrl || output.baseUrl || ((_c = output.info) === null || _c === void 0 ? void 0 : _c.baseUrl),
149
- portLockFilePath: overrides.port && (0, path_1.join)(__dirname, `${overrides.port}.txt`),
150
- });
151
119
  }
152
120
  }
153
- catch (e_2_1) { e_2 = { error: e_2_1 }; }
154
- finally {
155
- try {
156
- if (!_g && !_d && (_e = _h.return)) yield tslib_1.__await(_e.call(_h));
157
- }
158
- finally { if (e_2) throw e_2.error; }
121
+ }
122
+ for await (const output of await (0, devkit_1.runExecutor)(parsedDevServerTarget, overrides, context)) {
123
+ if (!output.success && !opts.watch)
124
+ throw new Error('Could not compile application files');
125
+ if (!opts.baseUrl &&
126
+ !output.baseUrl &&
127
+ !output.info?.baseUrl &&
128
+ output.info?.port) {
129
+ output.baseUrl = `http://localhost:${output.info.port}`;
159
130
  }
160
- });
131
+ yield {
132
+ baseUrl: opts.baseUrl || output.baseUrl || output.info?.baseUrl,
133
+ portLockFilePath: overrides.port && (0, path_1.join)(__dirname, `${overrides.port}.txt`),
134
+ };
135
+ }
161
136
  }
162
137
  /**
163
138
  * @whatItDoes Initialize the Cypress test runner with the provided project configuration.
164
139
  * By default, Cypress will run tests from the CLI without the GUI and provide directly the results in the console output.
165
140
  * If `watch` is `true`: Open Cypress in the interactive GUI to interact directly with the application.
166
141
  */
167
- function runCypress(baseUrl, opts) {
168
- var _a, _b;
169
- return tslib_1.__awaiter(this, void 0, void 0, function* () {
170
- const cypressVersion = (0, cypress_version_1.installedCypressVersion)();
171
- // Cypress expects the folder where a cypress config is present
172
- const projectFolderPath = (0, path_1.dirname)(opts.cypressConfig);
173
- const options = {
174
- project: projectFolderPath,
175
- configFile: (0, path_1.basename)(opts.cypressConfig),
142
+ async function runCypress(baseUrl, opts) {
143
+ const cypressVersion = (0, cypress_version_1.installedCypressVersion)();
144
+ // Cypress expects the folder where a cypress config is present
145
+ const projectFolderPath = (0, path_1.dirname)(opts.cypressConfig);
146
+ const options = {
147
+ project: projectFolderPath,
148
+ configFile: (0, path_1.basename)(opts.cypressConfig),
149
+ };
150
+ // If not, will use the `baseUrl` normally from `cypress.json`
151
+ if (baseUrl) {
152
+ options.config = { baseUrl };
153
+ }
154
+ if (opts.browser) {
155
+ options.browser = opts.browser;
156
+ }
157
+ if (opts.env) {
158
+ options.env = opts.env;
159
+ }
160
+ if (opts.spec) {
161
+ options.spec = opts.spec;
162
+ }
163
+ options.tag = opts.tag;
164
+ options.exit = opts.exit;
165
+ options.headed = opts.headed;
166
+ if (opts.headless) {
167
+ options.headless = opts.headless;
168
+ }
169
+ options.record = opts.record;
170
+ options.key = opts.key;
171
+ options.parallel = opts.parallel;
172
+ options.ciBuildId = opts.ciBuildId?.toString();
173
+ options.group = opts.group;
174
+ // renamed in cy 10
175
+ if (cypressVersion >= 10) {
176
+ options.config ??= {};
177
+ options.config[opts.testingType] = {
178
+ excludeSpecPattern: opts.ignoreTestFiles,
176
179
  };
177
- // If not, will use the `baseUrl` normally from `cypress.json`
178
- if (baseUrl) {
179
- options.config = { baseUrl };
180
- }
181
- if (opts.browser) {
182
- options.browser = opts.browser;
183
- }
184
- if (opts.env) {
185
- options.env = opts.env;
186
- }
187
- if (opts.spec) {
188
- options.spec = opts.spec;
189
- }
190
- options.tag = opts.tag;
191
- options.exit = opts.exit;
192
- options.headed = opts.headed;
193
- if (opts.headless) {
194
- options.headless = opts.headless;
195
- }
196
- options.record = opts.record;
197
- options.key = opts.key;
198
- options.parallel = opts.parallel;
199
- options.ciBuildId = (_a = opts.ciBuildId) === null || _a === void 0 ? void 0 : _a.toString();
200
- options.group = opts.group;
201
- // renamed in cy 10
202
- if (cypressVersion >= 10) {
203
- (_b = options.config) !== null && _b !== void 0 ? _b : (options.config = {});
204
- options.config[opts.testingType] = {
205
- excludeSpecPattern: opts.ignoreTestFiles,
206
- };
207
- }
208
- else {
209
- options.ignoreTestFiles = opts.ignoreTestFiles;
210
- }
211
- if (opts.reporter) {
212
- options.reporter = opts.reporter;
213
- }
214
- if (opts.reporterOptions) {
215
- options.reporterOptions = opts.reporterOptions;
216
- }
217
- if (opts.quiet) {
218
- options.quiet = opts.quiet;
219
- }
220
- options.testingType = opts.testingType;
221
- const result = yield (opts.watch
222
- ? Cypress.open(options)
223
- : Cypress.run(options));
224
- cleanupTmpFile(opts.ctTailwindPath);
225
- cleanupTmpFile(opts.portLockFilePath);
226
- if (process.env.NX_VERBOSE_LOGGING === 'true' && opts.portLockFilePath) {
227
- (0, fs_1.readdirSync)((0, path_1.dirname)(opts.portLockFilePath)).forEach((f) => {
228
- if (f.endsWith('.txt')) {
229
- devkit_1.logger.debug(`Lock file ${f} still present`);
230
- }
231
- });
232
- }
233
- /**
234
- * `cypress.open` is returning `0` and is not of the same type as `cypress.run`.
235
- * `cypress.open` is the graphical UI, so it will be obvious to know what wasn't
236
- * working. Forcing the build to success when `cypress.open` is used.
237
- */
238
- return !result.totalFailed && !result.failures;
239
- });
180
+ }
181
+ else {
182
+ options.ignoreTestFiles = opts.ignoreTestFiles;
183
+ }
184
+ if (opts.reporter) {
185
+ options.reporter = opts.reporter;
186
+ }
187
+ if (opts.reporterOptions) {
188
+ options.reporterOptions = opts.reporterOptions;
189
+ }
190
+ if (opts.quiet) {
191
+ options.quiet = opts.quiet;
192
+ }
193
+ options.testingType = opts.testingType;
194
+ const result = await (opts.watch
195
+ ? Cypress.open(options)
196
+ : Cypress.run(options));
197
+ cleanupTmpFile(opts.ctTailwindPath);
198
+ cleanupTmpFile(opts.portLockFilePath);
199
+ if (process.env.NX_VERBOSE_LOGGING === 'true' && opts.portLockFilePath) {
200
+ (0, fs_1.readdirSync)((0, path_1.dirname)(opts.portLockFilePath)).forEach((f) => {
201
+ if (f.endsWith('.txt')) {
202
+ devkit_1.logger.debug(`Lock file ${f} still present`);
203
+ }
204
+ });
205
+ }
206
+ /**
207
+ * `cypress.open` is returning `0` and is not of the same type as `cypress.run`.
208
+ * `cypress.open` is the graphical UI, so it will be obvious to know what wasn't
209
+ * working. Forcing the build to success when `cypress.open` is used.
210
+ */
211
+ return !result.totalFailed && !result.failures;
240
212
  }
241
213
  /**
242
214
  * try to find a free port for the project to run on
243
215
  * will return undefined if no port is found or the project doesn't have a port option
244
216
  **/
245
- function getPortForProject(context, target, defaultPort = 4200) {
246
- return tslib_1.__awaiter(this, void 0, void 0, function* () {
247
- const fmtTarget = (0, devkit_1.targetToTargetString)(target);
248
- const [hasPortOpt, schemaPortValue] = getValueFromSchema(context, target, 'port');
249
- let freePort;
250
- if (hasPortOpt) {
251
- let normalizedPortValue;
252
- if (!schemaPortValue) {
253
- devkit_1.logger.info(`NX ${fmtTarget} did not have a defined port value, checking for free port with the default value of ${defaultPort}`);
254
- normalizedPortValue = defaultPort;
255
- }
256
- else {
257
- normalizedPortValue = Number(schemaPortValue);
258
- }
259
- if (isNaN(normalizedPortValue)) {
260
- devkit_1.output.warn({
261
- title: `Port Not a Number`,
262
- bodyLines: [
263
- `The port value found was not a number or can't be parsed to a number`,
264
- `When reading the devServerTarget (${fmtTarget}) schema, expected ${schemaPortValue} to be a number but got NaN.`,
265
- `Nx will use the default value of ${defaultPort} instead.`,
266
- `You can manually specify a port by setting the 'port' option`,
267
- ],
268
- });
269
- normalizedPortValue = defaultPort;
270
- }
271
- try {
272
- let attempts = 0;
273
- // make sure when this check happens in parallel,
274
- // we don't let the same port be used by multiple projects
275
- do {
276
- freePort = yield detectPort(freePort || normalizedPortValue);
277
- if (attemptToLockPort(freePort)) {
278
- break;
279
- }
280
- attempts++;
281
- // increment port in case the lock file isn't cleaned up
282
- freePort++;
283
- } while (attempts < 20);
284
- devkit_1.logger.info(`NX Using port ${freePort} for ${fmtTarget}`);
285
- }
286
- catch (err) {
287
- throw new Error((0, devkit_1.stripIndents) `Unable to find a free port for the dev server, ${fmtTarget}.
288
- You can disable auto port detection by specifing a port or not passing a value to --port`);
289
- }
217
+ async function getPortForProject(context, target, defaultPort = 4200) {
218
+ const fmtTarget = (0, devkit_1.targetToTargetString)(target);
219
+ const [hasPortOpt, schemaPortValue] = getValueFromSchema(context, target, 'port');
220
+ let freePort;
221
+ if (hasPortOpt) {
222
+ let normalizedPortValue;
223
+ if (!schemaPortValue) {
224
+ devkit_1.logger.info(`NX ${fmtTarget} did not have a defined port value, checking for free port with the default value of ${defaultPort}`);
225
+ normalizedPortValue = defaultPort;
290
226
  }
291
227
  else {
228
+ normalizedPortValue = Number(schemaPortValue);
229
+ }
230
+ if (isNaN(normalizedPortValue)) {
292
231
  devkit_1.output.warn({
293
- title: `No Port Option Found`,
232
+ title: `Port Not a Number`,
294
233
  bodyLines: [
295
- `The 'port' option is set to 'cypress-auto', but the devServerTarget (${fmtTarget}) does not have a port option.`,
296
- `Because of this, Nx is unable to verify the port is free before starting the dev server.`,
297
- `This might cause issues if the devServerTarget is trying to use a port that is already in use.`,
234
+ `The port value found was not a number or can't be parsed to a number`,
235
+ `When reading the devServerTarget (${fmtTarget}) schema, expected ${schemaPortValue} to be a number but got NaN.`,
236
+ `Nx will use the default value of ${defaultPort} instead.`,
237
+ `You can manually specify a port by setting the 'port' option`,
298
238
  ],
299
239
  });
240
+ normalizedPortValue = defaultPort;
300
241
  }
301
- return freePort;
302
- });
242
+ try {
243
+ let attempts = 0;
244
+ // make sure when this check happens in parallel,
245
+ // we don't let the same port be used by multiple projects
246
+ do {
247
+ freePort = await detectPort(freePort || normalizedPortValue);
248
+ if (attemptToLockPort(freePort)) {
249
+ break;
250
+ }
251
+ attempts++;
252
+ // increment port in case the lock file isn't cleaned up
253
+ freePort++;
254
+ } while (attempts < 20);
255
+ devkit_1.logger.info(`NX Using port ${freePort} for ${fmtTarget}`);
256
+ }
257
+ catch (err) {
258
+ throw new Error((0, devkit_1.stripIndents) `Unable to find a free port for the dev server, ${fmtTarget}.
259
+ You can disable auto port detection by specifing a port or not passing a value to --port`);
260
+ }
261
+ }
262
+ else {
263
+ devkit_1.output.warn({
264
+ title: `No Port Option Found`,
265
+ bodyLines: [
266
+ `The 'port' option is set to 'cypress-auto', but the devServerTarget (${fmtTarget}) does not have a port option.`,
267
+ `Because of this, Nx is unable to verify the port is free before starting the dev server.`,
268
+ `This might cause issues if the devServerTarget is trying to use a port that is already in use.`,
269
+ ],
270
+ });
271
+ }
272
+ return freePort;
303
273
  }
304
274
  /**
305
275
  * Check if the given target has the given property in it's options.
@@ -307,7 +277,6 @@ You can disable auto port detection by specifing a port or not passing a value t
307
277
  * the value will be undefined even if it's in the executor schema.
308
278
  **/
309
279
  function getValueFromSchema(context, target, property) {
310
- var _a, _b;
311
280
  let targetOpts;
312
281
  try {
313
282
  targetOpts = (0, devkit_1.readTargetOptions)(target, context);
@@ -322,7 +291,7 @@ ${e.message || e}`);
322
291
  if (!targetHasOpt) {
323
292
  // NOTE: readTargetOptions doesn't apply non defaulted values, i.e. @nx/vite has a port options but is optional
324
293
  // so we double check the schema if readTargetOptions didn't return a value for the property
325
- const projectConfig = (_b = (_a = context.projectsConfigurations) === null || _a === void 0 ? void 0 : _a.projects) === null || _b === void 0 ? void 0 : _b[target.project];
294
+ const projectConfig = context.projectsConfigurations?.projects?.[target.project];
326
295
  const targetConfig = projectConfig.targets[target.target];
327
296
  const [collection, executor] = targetConfig.executor.split(':');
328
297
  const { schema } = (0, executor_utils_1.getExecutorInformation)(collection, executor, context.root);
@@ -10,9 +10,15 @@ function addBaseCypressSetup(tree, options) {
10
10
  return;
11
11
  }
12
12
  const opts = normalizeOptions(tree, projectConfig, options);
13
- (0, devkit_1.generateFiles)(tree, (0, path_1.join)(__dirname, 'files'), projectConfig.root, Object.assign(Object.assign({}, opts), { offsetFromRoot: (0, devkit_1.offsetFromRoot)(projectConfig.root), offsetFromProjectRoot: opts.hasTsConfig ? opts.offsetFromProjectRoot : '', tsConfigPath: opts.hasTsConfig
13
+ (0, devkit_1.generateFiles)(tree, (0, path_1.join)(__dirname, 'files'), projectConfig.root, {
14
+ ...opts,
15
+ offsetFromRoot: (0, devkit_1.offsetFromRoot)(projectConfig.root),
16
+ offsetFromProjectRoot: opts.hasTsConfig ? opts.offsetFromProjectRoot : '',
17
+ tsConfigPath: opts.hasTsConfig
14
18
  ? `${opts.offsetFromProjectRoot}tsconfig.json`
15
- : (0, js_1.getRelativePathToRootTsConfig)(tree, projectConfig.root), ext: '' }));
19
+ : (0, js_1.getRelativePathToRootTsConfig)(tree, projectConfig.root),
20
+ ext: '',
21
+ });
16
22
  if (opts.hasTsConfig) {
17
23
  (0, devkit_1.updateJson)(tree, (0, devkit_1.joinPathFragments)(projectConfig.root, 'tsconfig.json'), (json) => {
18
24
  // tsconfig doesn't have references so it shouldn't add them
@@ -35,12 +41,16 @@ function addBaseCypressSetup(tree, options) {
35
41
  }
36
42
  exports.addBaseCypressSetup = addBaseCypressSetup;
37
43
  function normalizeOptions(tree, projectConfig, options) {
38
- var _a;
39
- (_a = options.directory) !== null && _a !== void 0 ? _a : (options.directory = 'cypress');
44
+ options.directory ??= 'cypress';
40
45
  const offsetFromProjectRoot = options.directory
41
46
  .split('/')
42
47
  .map((_) => '..')
43
48
  .join('/');
44
49
  const hasTsConfig = tree.exists((0, devkit_1.joinPathFragments)(projectConfig.root, 'tsconfig.json'));
45
- return Object.assign(Object.assign({}, options), { projectConfig, offsetFromProjectRoot: `${offsetFromProjectRoot}/`, hasTsConfig });
50
+ return {
51
+ ...options,
52
+ projectConfig,
53
+ offsetFromProjectRoot: `${offsetFromProjectRoot}/`,
54
+ hasTsConfig,
55
+ };
46
56
  }