nx 19.2.0 → 19.2.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -19,7 +19,6 @@ const nx_json_1 = require("../../config/nx-json");
19
19
  const daemon_socket_messenger_1 = require("./daemon-socket-messenger");
20
20
  const cache_1 = require("../cache");
21
21
  const error_types_1 = require("../../project-graph/error-types");
22
- const dotenv_1 = require("../../utils/dotenv");
23
22
  const get_nx_workspace_files_1 = require("../message-types/get-nx-workspace-files");
24
23
  const get_context_file_data_1 = require("../message-types/get-context-file-data");
25
24
  const get_files_in_directory_1 = require("../message-types/get-files-in-directory");
@@ -41,7 +40,6 @@ class DaemonClient {
41
40
  this._daemonReady = null;
42
41
  this._out = null;
43
42
  this._err = null;
44
- (0, dotenv_1.loadRootEnvFiles)(workspace_root_1.workspaceRoot);
45
43
  try {
46
44
  this.nxJson = (0, configuration_1.readNxJson)();
47
45
  }
@@ -42,7 +42,7 @@ export interface NormalizedRunCommandsOptions extends RunCommandsOptions {
42
42
  [k: string]: any;
43
43
  };
44
44
  unparsedCommandArgs?: {
45
- [k: string]: string;
45
+ [k: string]: string | string[];
46
46
  };
47
47
  args?: string;
48
48
  }
@@ -29,7 +29,9 @@ const propKeys = [
29
29
  'command',
30
30
  'commands',
31
31
  'color',
32
+ 'no-color',
32
33
  'parallel',
34
+ 'no-parallel',
33
35
  'readyWhen',
34
36
  'cwd',
35
37
  'args',
@@ -131,6 +133,7 @@ function normalizeOptions(options) {
131
133
  'parse-numbers': false,
132
134
  'parse-positional-numbers': false,
133
135
  'dot-notation': false,
136
+ 'camel-case-expansion': false,
134
137
  },
135
138
  });
136
139
  options.unknownOptions = Object.keys(options)
@@ -294,21 +297,25 @@ function interpolateArgsIntoCommand(command, opts, forwardAllArgs) {
294
297
  else if (forwardAllArgs) {
295
298
  let args = '';
296
299
  if (Object.keys(opts.unknownOptions ?? {}).length > 0) {
297
- args +=
298
- ' ' +
299
- Object.keys(opts.unknownOptions)
300
- .filter((k) => typeof opts.unknownOptions[k] !== 'object' &&
301
- opts.parsedArgs[k] === opts.unknownOptions[k])
302
- .map((k) => `--${k}=${opts.unknownOptions[k]}`)
303
- .join(' ');
300
+ const unknownOptionsArgs = Object.keys(opts.unknownOptions)
301
+ .filter((k) => typeof opts.unknownOptions[k] !== 'object' &&
302
+ opts.parsedArgs[k] === opts.unknownOptions[k])
303
+ .map((k) => `--${k}=${opts.unknownOptions[k]}`)
304
+ .map(wrapArgIntoQuotesIfNeeded)
305
+ .join(' ');
306
+ if (unknownOptionsArgs) {
307
+ args += ` ${unknownOptionsArgs}`;
308
+ }
304
309
  }
305
310
  if (opts.args) {
306
311
  args += ` ${opts.args}`;
307
312
  }
308
313
  if (opts.__unparsed__?.length > 0) {
309
- const filterdParsedOptions = filterPropKeysFromUnParsedOptions(opts.__unparsed__, opts.unparsedCommandArgs);
314
+ const filterdParsedOptions = filterPropKeysFromUnParsedOptions(opts.__unparsed__, opts.parsedArgs);
310
315
  if (filterdParsedOptions.length > 0) {
311
- args += ` ${filterdParsedOptions.join(' ')}`;
316
+ args += ` ${filterdParsedOptions
317
+ .map(wrapArgIntoQuotesIfNeeded)
318
+ .join(' ')}`;
312
319
  }
313
320
  }
314
321
  return `${command}${args}`;
@@ -332,13 +339,14 @@ function parseArgs(unparsedCommandArgs, unknownOptions, args) {
332
339
  * @param unparsedCommandArgs e.g. { prop1: 'value1', prop2: 'value2', args: 'test'}
333
340
  * @returns filtered options that are not part of the propKeys array e.g. ['--prop1', 'value1', '--prop2=value2']
334
341
  */
335
- function filterPropKeysFromUnParsedOptions(__unparsed__, unparsedCommandArgs = {}) {
342
+ function filterPropKeysFromUnParsedOptions(__unparsed__, parseArgs = {}) {
336
343
  const parsedOptions = [];
337
344
  for (let index = 0; index < __unparsed__.length; index++) {
338
345
  const element = __unparsed__[index];
339
346
  if (element.startsWith('--')) {
340
347
  const key = element.replace('--', '');
341
348
  if (element.includes('=')) {
349
+ // key can be in the format of --key=value or --key.subkey=value (e.g. env.foo=bar)
342
350
  if (!propKeys.includes(key.split('=')[0].split('.')[0])) {
343
351
  // check if the key is part of the propKeys array
344
352
  parsedOptions.push(element);
@@ -348,7 +356,8 @@ function filterPropKeysFromUnParsedOptions(__unparsed__, unparsedCommandArgs = {
348
356
  // check if the next element is a value for the key
349
357
  if (propKeys.includes(key)) {
350
358
  if (index + 1 < __unparsed__.length &&
351
- __unparsed__[index + 1] === unparsedCommandArgs[key]) {
359
+ parseArgs[key] &&
360
+ __unparsed__[index + 1].toString() === parseArgs[key].toString()) {
352
361
  index++; // skip the next element
353
362
  }
354
363
  }
@@ -417,3 +426,20 @@ function registerProcessListener() {
417
426
  // will store results to the cache and will terminate this process
418
427
  });
419
428
  }
429
+ function wrapArgIntoQuotesIfNeeded(arg) {
430
+ if (arg.includes('=')) {
431
+ const [key, value] = arg.split('=');
432
+ if (key.startsWith('--') &&
433
+ value.includes(' ') &&
434
+ !(value[0] === "'" || value[0] === '"')) {
435
+ return `${key}="${value}"`;
436
+ }
437
+ return arg;
438
+ }
439
+ else if (arg.includes(' ') && !(arg[0] === "'" || arg[0] === '"')) {
440
+ return `"${arg}"`;
441
+ }
442
+ else {
443
+ return arg;
444
+ }
445
+ }
@@ -10,7 +10,7 @@ exports.hashArray = hashArray;
10
10
  function hashObject(obj) {
11
11
  const { hashArray } = require('../native');
12
12
  const parts = [];
13
- for (const key of Object.keys(obj).sort()) {
13
+ for (const key of Object.keys(obj ?? {}).sort()) {
14
14
  parts.push(key);
15
15
  parts.push(JSON.stringify(obj[key]));
16
16
  }
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.TargetProjectLocator = exports.isBuiltinModuleImport = void 0;
4
4
  const node_module_1 = require("node:module");
5
5
  const node_path_1 = require("node:path");
6
+ const semver_1 = require("semver");
6
7
  const find_project_for_path_1 = require("../../../../project-graph/utils/find-project-for-path");
7
8
  const fileutils_1 = require("../../../../utils/fileutils");
8
9
  const workspace_root_1 = require("../../../../utils/workspace-root");
@@ -138,7 +139,8 @@ class TargetProjectLocator {
138
139
  this.npmResolutionCache.set(npmImportForProject, externalNodeName);
139
140
  return externalNodeName;
140
141
  }
141
- const npmProjectKey = `npm:${externalPackageJson.name}@${externalPackageJson.version}`;
142
+ const version = (0, semver_1.clean)(externalPackageJson.version);
143
+ const npmProjectKey = `npm:${externalPackageJson.name}@${version}`;
142
144
  const matchingExternalNode = this.npmProjects[npmProjectKey];
143
145
  if (!matchingExternalNode) {
144
146
  return null;
@@ -38,7 +38,16 @@ function buildPackageJsonWorkspacesMatcher(workspaceRoot, readJson) {
38
38
  positivePatterns.push('**/package.json');
39
39
  }
40
40
  return (p) => positivePatterns.some((positive) => (0, minimatch_1.minimatch)(p, positive)) &&
41
- !negativePatterns.some((negative) => (0, minimatch_1.minimatch)(p, negative));
41
+ /**
42
+ * minimatch will return true if the given p is NOT excluded by the negative pattern.
43
+ *
44
+ * For example if the negative pattern is "!packages/vite", then the given p "packages/vite" will return false,
45
+ * the given p "packages/something-else/package.json" will return true.
46
+ *
47
+ * Therefore, we need to ensure that every negative pattern returns true to validate that the given p is not
48
+ * excluded by any of the negative patterns.
49
+ */
50
+ negativePatterns.every((negative) => (0, minimatch_1.minimatch)(p, negative));
42
51
  }
43
52
  exports.buildPackageJsonWorkspacesMatcher = buildPackageJsonWorkspacesMatcher;
44
53
  function createNodeFromPackageJson(pkgJsonPath, root) {
@@ -161,6 +161,16 @@ class AggregateCreateNodesError extends Error {
161
161
  this.errors = errors;
162
162
  this.partialResults = partialResults;
163
163
  this.name = this.constructor.name;
164
+ if (
165
+ // Errors should be an array
166
+ !Array.isArray(errors) ||
167
+ !errors.every(
168
+ // Where every element is a tuple
169
+ (errorTuple) => Array.isArray(errorTuple) &&
170
+ // That has a length of 2
171
+ errorTuple.length === 2)) {
172
+ throw new Error('AggregateCreateNodesError must be constructed with an array of tuples where the first element is a filename or undefined and the second element is the underlying error.');
173
+ }
164
174
  }
165
175
  }
166
176
  exports.AggregateCreateNodesError = AggregateCreateNodesError;
@@ -45,7 +45,7 @@ class LoadedNxPlugin {
45
45
  throw e;
46
46
  }
47
47
  // The underlying plugin errored out. We can't know any partial results.
48
- throw new error_types_1.AggregateCreateNodesError([null, e], []);
48
+ throw new error_types_1.AggregateCreateNodesError([[null, e]], []);
49
49
  }
50
50
  finally {
51
51
  performance.mark(`${plugin.name}:createNodes - end`);
@@ -39,7 +39,7 @@ function loadRemoteNxPlugin(plugin, root) {
39
39
  const exitHandler = createWorkerExitHandler(worker, pendingPromises);
40
40
  const cleanupFunction = () => {
41
41
  worker.off('exit', exitHandler);
42
- shutdownPluginWorker(worker, pendingPromises);
42
+ shutdownPluginWorker(worker);
43
43
  };
44
44
  cleanupFunctions.add(cleanupFunction);
45
45
  return new Promise((res, rej) => {
@@ -48,13 +48,10 @@ function loadRemoteNxPlugin(plugin, root) {
48
48
  });
49
49
  }
50
50
  exports.loadRemoteNxPlugin = loadRemoteNxPlugin;
51
- async function shutdownPluginWorker(worker, pendingPromises) {
51
+ function shutdownPluginWorker(worker) {
52
52
  // Clears the plugin cache so no refs to the workers are held
53
53
  internal_api_1.nxPluginCache.clear();
54
54
  // logger.verbose(`[plugin-pool] starting worker shutdown`);
55
- // Other things may be interacting with the worker.
56
- // Wait for all pending promises to be done before killing the worker
57
- await Promise.all(Array.from(pendingPromises.values()).map(({ promise }) => promise));
58
55
  worker.kill('SIGINT');
59
56
  }
60
57
  /**
@@ -76,11 +76,29 @@ function normalizeImplicitDependencies(source, implicitDependencies, projects) {
76
76
  if (!implicitDependencies?.length) {
77
77
  return implicitDependencies ?? [];
78
78
  }
79
- const matches = (0, find_matching_projects_1.findMatchingProjects)(implicitDependencies, projects);
80
- return (matches
81
- .filter((x) => x !== source)
82
- // implicit dependencies that start with ! should hang around, to be processed by
83
- // implicit-project-dependencies.ts after explicit deps are added to graph.
84
- .concat(implicitDependencies.filter((x) => x.startsWith('!'))));
79
+ // Implicit dependencies handle negatives in a different
80
+ // way from most other `projects` fields. This is because
81
+ // they are used for multiple purposes.
82
+ const positivePatterns = [];
83
+ const negativePatterns = [];
84
+ for (const dep of implicitDependencies) {
85
+ if (dep.startsWith('!')) {
86
+ negativePatterns.push(dep);
87
+ }
88
+ else {
89
+ positivePatterns.push(dep);
90
+ }
91
+ }
92
+ // Finds all projects that match a positive pattern and are not excluded by a negative pattern
93
+ const deps = positivePatterns.length
94
+ ? (0, find_matching_projects_1.findMatchingProjects)(positivePatterns.concat(negativePatterns), projects).filter((x) => x !== source)
95
+ : [];
96
+ // Expands negative patterns to equal project names
97
+ const alwaysIgnoredDeps = (0, find_matching_projects_1.findMatchingProjects)(negativePatterns.map((x) => x.slice(1)), projects);
98
+ // We return the matching deps, but keep the negative patterns in the list
99
+ // so that they can be processed later by implicit-project-dependencies.ts
100
+ // This is what allows using a negative implicit dep to remove a dependency
101
+ // detected by createDependencies.
102
+ return deps.concat(alwaysIgnoredDeps.map((x) => '!' + x));
85
103
  }
86
104
  exports.normalizeImplicitDependencies = normalizeImplicitDependencies;
@@ -245,7 +245,15 @@ plugins) {
245
245
  e
246
246
  : // This represents a single plugin erroring out with a hard error.
247
247
  new error_types_1.AggregateCreateNodesError([[null, e]], []);
248
- errorBodyLines.push(...error.errors.map(([file, e]) => ` - ${file}: ${e.message}`));
248
+ const innerErrors = error.errors;
249
+ for (const [file, e] of innerErrors) {
250
+ if (file) {
251
+ errorBodyLines.push(` - ${file}: ${e.message}`);
252
+ }
253
+ else {
254
+ errorBodyLines.push(` - ${e.message}`);
255
+ }
256
+ }
249
257
  error.message = errorBodyLines.join('\n');
250
258
  // This represents a single plugin erroring out with a hard error.
251
259
  errors.push(error);
@@ -9,12 +9,12 @@ import { LoadedNxPlugin } from '../plugins/internal-api';
9
9
  * @param nxJson
10
10
  */
11
11
  export declare function retrieveWorkspaceFiles(workspaceRoot: string, projectRootMap: Record<string, string>): Promise<{
12
- allWorkspaceFiles: import("../file-utils").FileData[];
12
+ allWorkspaceFiles: import("nx/src/devkit-exports").FileData[];
13
13
  fileMap: {
14
14
  projectFileMap: ProjectFiles;
15
- nonProjectFiles: import("../../native").FileData[];
15
+ nonProjectFiles: import("nx/src/native").FileData[];
16
16
  };
17
- rustReferences: import("../../native").NxWorkspaceFilesExternals;
17
+ rustReferences: import("nx/src/native").NxWorkspaceFilesExternals;
18
18
  }>;
19
19
  /**
20
20
  * Walk through the workspace and return `ProjectConfigurations`. Only use this if the projectFileMap is not needed.
@@ -64,7 +64,26 @@ class ProcessTasks {
64
64
  ? overrides
65
65
  : { __overrides_unparsed__: [] };
66
66
  if (dependencyConfig.projects) {
67
- this.processTasksForMatchingProjects(dependencyConfig, projectUsedToDeriveDependencies, configuration, task, taskOverrides, overrides);
67
+ /** LERNA SUPPORT START - Remove in v20 */
68
+ // Lerna uses `dependencies` in `prepNxOptions`, so we need to maintain
69
+ // support for it until lerna can be updated to use the syntax.
70
+ //
71
+ // This should have been removed in v17, but the updates to lerna had not
72
+ // been made yet.
73
+ //
74
+ // TODO(@agentender): Remove this part in v20
75
+ if (typeof dependencyConfig.projects === 'string') {
76
+ if (dependencyConfig.projects === 'self') {
77
+ this.processTasksForSingleProject(task, task.target.project, dependencyConfig, configuration, taskOverrides, overrides);
78
+ continue;
79
+ }
80
+ else if (dependencyConfig.projects === 'dependencies') {
81
+ this.processTasksForDependencies(projectUsedToDeriveDependencies, dependencyConfig, configuration, task, taskOverrides, overrides);
82
+ continue;
83
+ }
84
+ }
85
+ /** LERNA SUPPORT END - Remove in v17 */
86
+ this.processTasksForMatchingProjects(dependencyConfig, configuration, task, taskOverrides, overrides);
68
87
  }
69
88
  else if (dependencyConfig.dependencies) {
70
89
  this.processTasksForDependencies(projectUsedToDeriveDependencies, dependencyConfig, configuration, task, taskOverrides, overrides);
@@ -74,41 +93,21 @@ class ProcessTasks {
74
93
  }
75
94
  }
76
95
  }
77
- processTasksForMatchingProjects(dependencyConfig, projectUsedToDeriveDependencies, configuration, task, taskOverrides, overrides) {
96
+ processTasksForMatchingProjects(dependencyConfig, configuration, task, taskOverrides, overrides) {
78
97
  const targetProjectSpecifiers = typeof dependencyConfig.projects === 'string'
79
98
  ? [dependencyConfig.projects]
80
99
  : dependencyConfig.projects;
81
- for (const projectSpecifier of targetProjectSpecifiers) {
82
- // Lerna uses `dependencies` in `prepNxOptions`, so we need to maintain
83
- // support for it until lerna can be updated to use the syntax.
84
- // TODO(@agentender): Remove this part in v17
85
- if (projectSpecifier === 'dependencies' &&
86
- !this.projectGraph.nodes[projectSpecifier]) {
87
- this.processTasksForDependencies(projectUsedToDeriveDependencies, dependencyConfig, configuration, task, taskOverrides, overrides);
88
- }
89
- else {
90
- // Since we need to maintain support for dependencies, it is more coherent
91
- // that we also support self.
92
- // TODO(@agentender): Remove this part in v17
93
- const matchingProjects =
94
- /** LERNA SUPPORT START - Remove in v17 */
95
- projectSpecifier === 'self' &&
96
- !this.projectGraph.nodes[projectSpecifier]
97
- ? [task.target.project]
98
- : /** LERNA SUPPORT END */
99
- (0, find_matching_projects_1.findMatchingProjects)([projectSpecifier], this.projectGraph.nodes);
100
- if (matchingProjects.length === 0) {
101
- output_1.output.warn({
102
- title: `\`dependsOn\` is misconfigured for ${task.target.project}:${task.target.target}`,
103
- bodyLines: [
104
- `Project pattern "${projectSpecifier}" does not match any projects.`,
105
- ],
106
- });
107
- }
108
- for (const projectName of matchingProjects) {
109
- this.processTasksForSingleProject(task, projectName, dependencyConfig, configuration, taskOverrides, overrides);
110
- }
111
- }
100
+ const matchingProjects = (0, find_matching_projects_1.findMatchingProjects)(targetProjectSpecifiers, this.projectGraph.nodes);
101
+ if (matchingProjects.length === 0) {
102
+ output_1.output.warn({
103
+ title: `\`dependsOn\` is misconfigured for ${task.target.project}:${task.target.target}`,
104
+ bodyLines: [
105
+ `Project patterns "${targetProjectSpecifiers}" does not match any projects.`,
106
+ ],
107
+ });
108
+ }
109
+ for (const projectName of matchingProjects) {
110
+ this.processTasksForSingleProject(task, projectName, dependencyConfig, configuration, taskOverrides, overrides);
112
111
  }
113
112
  }
114
113
  processTasksForSingleProject(task, projectName, dependencyConfig, configuration, taskOverrides, overrides) {
@@ -31,7 +31,7 @@ declare const messageOptions: {
31
31
  }];
32
32
  };
33
33
  export type MessageKey = keyof typeof messageOptions;
34
- export type MessageData = typeof messageOptions[MessageKey][number];
34
+ export type MessageData = (typeof messageOptions)[MessageKey][number];
35
35
  export declare class PromptMessages {
36
36
  private selectedMessages;
37
37
  getPrompt(key: MessageKey): MessageData;
@@ -22,6 +22,15 @@ function findMatchingProjects(patterns = [], projects) {
22
22
  }
23
23
  const projectNames = Object.keys(projects);
24
24
  const matchedProjects = new Set();
25
+ // If the first pattern is an exclude pattern,
26
+ // we add a wildcard pattern at the first to select
27
+ // all projects, except the ones that match the exclude pattern.
28
+ // e.g. ['!tag:someTag', 'project2'] will match all projects except
29
+ // the ones with the tag 'someTag', and also match the project 'project2',
30
+ // regardless of its tags.
31
+ if (isExcludePattern(patterns[0])) {
32
+ patterns.unshift('*');
33
+ }
25
34
  for (const stringPattern of patterns) {
26
35
  if (!stringPattern.length) {
27
36
  continue;
@@ -139,8 +148,11 @@ function addMatchingProjectsByTag(projectNames, projects, pattern, matchedProjec
139
148
  }
140
149
  }
141
150
  }
151
+ function isExcludePattern(pattern) {
152
+ return pattern.startsWith('!');
153
+ }
142
154
  function parseStringPattern(pattern, projects) {
143
- const isExclude = pattern.startsWith('!');
155
+ const isExclude = isExcludePattern(pattern);
144
156
  // Support for things like: `!{type}:value`
145
157
  if (isExclude) {
146
158
  pattern = pattern.substring(1);