nx 19.2.0 → 19.2.2
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.
- package/bin/init-local.js +0 -3
- package/bin/post-install.js +1 -3
- package/migrations.json +6 -0
- package/package.json +12 -12
- package/src/command-line/init/implementation/dot-nx/add-nx-scripts.js +38 -13
- package/src/command-line/init/implementation/utils.js +15 -2
- package/src/command-line/release/version.d.ts +1 -1
- package/src/command-line/yargs-utils/shared-options.d.ts +1 -1
- package/src/core/graph/main.js +1 -1
- package/src/daemon/client/client.js +0 -2
- package/src/executors/run-commands/run-commands.impl.d.ts +1 -1
- package/src/executors/run-commands/run-commands.impl.js +37 -11
- package/src/hasher/file-hasher.js +1 -1
- package/src/plugins/js/project-graph/build-dependencies/target-project-locator.js +3 -1
- package/src/plugins/package-json-workspaces/create-nodes.js +10 -1
- package/src/project-graph/error-types.js +10 -0
- package/src/project-graph/plugins/internal-api.js +1 -1
- package/src/project-graph/plugins/isolation/plugin-pool.js +2 -5
- package/src/project-graph/utils/normalize-project-nodes.js +24 -6
- package/src/project-graph/utils/project-configuration-utils.js +9 -1
- package/src/project-graph/utils/retrieve-workspace-files.d.ts +3 -3
- package/src/tasks-runner/create-task-graph.js +32 -33
- package/src/utils/ab-testing.d.ts +1 -1
- package/src/utils/find-matching-projects.js +13 -1
@@ -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
|
}
|
@@ -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
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
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.
|
314
|
+
const filterdParsedOptions = filterPropKeysFromUnParsedOptions(opts.__unparsed__, opts.parsedArgs);
|
310
315
|
if (filterdParsedOptions.length > 0) {
|
311
|
-
args += ` ${filterdParsedOptions
|
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__,
|
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
|
-
|
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
|
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
|
-
|
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
|
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
|
-
|
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
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
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
|
-
|
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("
|
12
|
+
allWorkspaceFiles: import("nx/src/devkit-exports").FileData[];
|
13
13
|
fileMap: {
|
14
14
|
projectFileMap: ProjectFiles;
|
15
|
-
nonProjectFiles: import("
|
15
|
+
nonProjectFiles: import("nx/src/native").FileData[];
|
16
16
|
};
|
17
|
-
rustReferences: import("
|
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
|
-
|
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,
|
96
|
+
processTasksForMatchingProjects(dependencyConfig, configuration, task, taskOverrides, overrides) {
|
78
97
|
const targetProjectSpecifiers = typeof dependencyConfig.projects === 'string'
|
79
98
|
? [dependencyConfig.projects]
|
80
99
|
: dependencyConfig.projects;
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
}
|
89
|
-
|
90
|
-
|
91
|
-
|
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
|
155
|
+
const isExclude = isExcludePattern(pattern);
|
144
156
|
// Support for things like: `!{type}:value`
|
145
157
|
if (isExclude) {
|
146
158
|
pattern = pattern.substring(1);
|