nx 19.2.0-canary.20240530-316dcb9 → 19.2.0-canary.20240601-6f22300

Sign up to get free protection for your applications and to get access to all the features.
Files changed (74) hide show
  1. package/package.json +12 -12
  2. package/src/command-line/deprecated/command-objects.js +2 -1
  3. package/src/command-line/examples.js +59 -5
  4. package/src/command-line/graph/command-object.d.ts +25 -2
  5. package/src/command-line/graph/command-object.js +57 -3
  6. package/src/command-line/graph/graph.js +2 -1
  7. package/src/command-line/init/implementation/react/index.js +1 -1
  8. package/src/command-line/init/implementation/react/rename-js-to-jsx.d.ts +1 -1
  9. package/src/command-line/init/implementation/react/rename-js-to-jsx.js +2 -2
  10. package/src/command-line/init/init-v2.js +1 -1
  11. package/src/command-line/nx-commands.js +1 -1
  12. package/src/command-line/yargs-utils/shared-options.d.ts +0 -21
  13. package/src/command-line/yargs-utils/shared-options.js +2 -52
  14. package/src/daemon/client/client.d.ts +6 -0
  15. package/src/daemon/client/client.js +45 -2
  16. package/src/daemon/is-on-daemon.d.ts +1 -0
  17. package/src/daemon/is-on-daemon.js +7 -0
  18. package/src/daemon/message-types/get-context-file-data.d.ts +5 -0
  19. package/src/daemon/message-types/get-context-file-data.js +11 -0
  20. package/src/daemon/message-types/get-files-in-directory.d.ts +6 -0
  21. package/src/daemon/message-types/get-files-in-directory.js +11 -0
  22. package/src/daemon/message-types/get-nx-workspace-files.d.ts +6 -0
  23. package/src/daemon/message-types/get-nx-workspace-files.js +11 -0
  24. package/src/daemon/message-types/glob.d.ts +7 -0
  25. package/src/daemon/message-types/glob.js +11 -0
  26. package/src/daemon/message-types/hash-glob.d.ts +7 -0
  27. package/src/daemon/message-types/hash-glob.js +11 -0
  28. package/src/daemon/message-types/update-context-files.d.ts +7 -0
  29. package/src/daemon/message-types/update-context-files.js +11 -0
  30. package/src/daemon/server/handle-context-file-data.d.ts +2 -0
  31. package/src/daemon/server/handle-context-file-data.js +13 -0
  32. package/src/daemon/server/handle-get-files-in-directory.d.ts +2 -0
  33. package/src/daemon/server/handle-get-files-in-directory.js +13 -0
  34. package/src/daemon/server/handle-glob.d.ts +2 -0
  35. package/src/daemon/server/handle-glob.js +13 -0
  36. package/src/daemon/server/handle-hash-glob.d.ts +2 -0
  37. package/src/daemon/server/handle-hash-glob.js +13 -0
  38. package/src/daemon/server/handle-nx-workspace-files.d.ts +2 -0
  39. package/src/daemon/server/handle-nx-workspace-files.js +13 -0
  40. package/src/daemon/server/server.js +39 -9
  41. package/src/devkit-exports.d.ts +4 -2
  42. package/src/devkit-exports.js +6 -1
  43. package/src/generators/utils/glob.d.ts +11 -0
  44. package/src/generators/utils/glob.js +20 -3
  45. package/src/generators/utils/project-configuration.js +1 -1
  46. package/src/plugins/js/project-graph/build-dependencies/build-dependencies.js +16 -3
  47. package/src/plugins/js/project-graph/build-dependencies/explicit-package-json-dependencies.d.ts +2 -1
  48. package/src/plugins/js/project-graph/build-dependencies/explicit-package-json-dependencies.js +40 -27
  49. package/src/plugins/js/project-graph/build-dependencies/explicit-project-dependencies.d.ts +2 -1
  50. package/src/plugins/js/project-graph/build-dependencies/explicit-project-dependencies.js +14 -19
  51. package/src/plugins/js/project-graph/build-dependencies/target-project-locator.d.ts +32 -5
  52. package/src/plugins/js/project-graph/build-dependencies/target-project-locator.js +146 -47
  53. package/src/plugins/js/utils/resolve-relative-to-dir.d.ts +5 -0
  54. package/src/plugins/js/utils/resolve-relative-to-dir.js +18 -0
  55. package/src/project-graph/error-types.d.ts +37 -19
  56. package/src/project-graph/error-types.js +31 -19
  57. package/src/project-graph/file-map-utils.js +1 -8
  58. package/src/project-graph/file-utils.js +33 -34
  59. package/src/project-graph/plugins/index.d.ts +1 -0
  60. package/src/project-graph/plugins/index.js +3 -1
  61. package/src/project-graph/plugins/internal-api.d.ts +2 -2
  62. package/src/project-graph/plugins/internal-api.js +33 -3
  63. package/src/project-graph/plugins/isolation/plugin-pool.js +10 -9
  64. package/src/project-graph/plugins/public-api.d.ts +34 -3
  65. package/src/project-graph/plugins/utils.d.ts +4 -3
  66. package/src/project-graph/plugins/utils.js +13 -26
  67. package/src/project-graph/utils/project-configuration-utils.js +89 -74
  68. package/src/project-graph/utils/retrieve-workspace-files.d.ts +4 -4
  69. package/src/project-graph/utils/retrieve-workspace-files.js +6 -6
  70. package/src/utils/all-file-data.js +1 -7
  71. package/src/utils/workspace-context.d.ts +13 -5
  72. package/src/utils/workspace-context.js +45 -14
  73. package/src/daemon/server/handle-request-file-data.d.ts +0 -4
  74. package/src/daemon/server/handle-request-file-data.js +0 -13
@@ -1,36 +1,65 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.TargetProjectLocator = void 0;
4
- const typescript_1 = require("../../utils/typescript");
3
+ exports.TargetProjectLocator = exports.isBuiltinModuleImport = void 0;
4
+ const node_module_1 = require("node:module");
5
+ const node_path_1 = require("node:path");
6
+ const find_project_for_path_1 = require("../../../../project-graph/utils/find-project-for-path");
5
7
  const fileutils_1 = require("../../../../utils/fileutils");
6
- const path_1 = require("path");
7
8
  const workspace_root_1 = require("../../../../utils/workspace-root");
8
- const module_1 = require("module");
9
- const find_project_for_path_1 = require("../../../../project-graph/utils/find-project-for-path");
9
+ const resolve_relative_to_dir_1 = require("../../utils/resolve-relative-to-dir");
10
+ const typescript_1 = require("../../utils/typescript");
11
+ /**
12
+ * Use a shared cache to avoid repeated npm package resolution work within the TargetProjectLocator.
13
+ */
14
+ const defaultNpmResolutionCache = new Map();
10
15
  const builtInModuleSet = new Set([
11
- ...module_1.builtinModules,
12
- ...module_1.builtinModules.map((x) => `node:${x}`),
16
+ ...node_module_1.builtinModules,
17
+ ...node_module_1.builtinModules.map((x) => `node:${x}`),
13
18
  ]);
19
+ function isBuiltinModuleImport(importExpr) {
20
+ const packageName = parsePackageNameFromImportExpression(importExpr);
21
+ return builtInModuleSet.has(packageName);
22
+ }
23
+ exports.isBuiltinModuleImport = isBuiltinModuleImport;
14
24
  class TargetProjectLocator {
15
- constructor(nodes, externalNodes) {
25
+ constructor(nodes, externalNodes = {}, npmResolutionCache = defaultNpmResolutionCache) {
16
26
  this.nodes = nodes;
17
27
  this.externalNodes = externalNodes;
28
+ this.npmResolutionCache = npmResolutionCache;
18
29
  this.projectRootMappings = (0, find_project_for_path_1.createProjectRootMappings)(this.nodes);
19
- this.npmProjects = filterRootExternalDependencies(this.externalNodes);
20
30
  this.tsConfig = this.getRootTsConfig();
21
31
  this.paths = this.tsConfig.config?.compilerOptions?.paths;
22
32
  this.typescriptResolutionCache = new Map();
23
- this.npmResolutionCache = new Map();
33
+ /**
34
+ * Only the npm external nodes should be included.
35
+ *
36
+ * Unlike the raw externalNodes, ensure that there is always copy of the node where the version
37
+ * is set in the key for optimal lookup.
38
+ */
39
+ this.npmProjects = Object.values(this.externalNodes).reduce((acc, node) => {
40
+ if (node.type === 'npm') {
41
+ const keyWithVersion = `npm:${node.data.packageName}@${node.data.version}`;
42
+ if (!acc[node.name]) {
43
+ acc[node.name] = node;
44
+ }
45
+ // The node.name may have already contained the version
46
+ if (!acc[keyWithVersion]) {
47
+ acc[keyWithVersion] = node;
48
+ }
49
+ }
50
+ return acc;
51
+ }, {});
24
52
  }
25
53
  /**
26
- * Find a project based on its import
54
+ * Resolve any workspace or external project that matches the given import expression,
55
+ * originating from the given filePath.
27
56
  *
28
57
  * @param importExpr
29
58
  * @param filePath
30
59
  */
31
- findProjectWithImport(importExpr, filePath) {
60
+ findProjectFromImport(importExpr, filePath) {
32
61
  if ((0, fileutils_1.isRelativePath)(importExpr)) {
33
- const resolvedModule = path_1.posix.join((0, path_1.dirname)(filePath), importExpr);
62
+ const resolvedModule = node_path_1.posix.join((0, node_path_1.dirname)(filePath), importExpr);
34
63
  return this.findProjectOfResolvedModule(resolvedModule);
35
64
  }
36
65
  // find project using tsconfig paths
@@ -39,7 +68,7 @@ class TargetProjectLocator {
39
68
  const [path, paths] = results;
40
69
  for (let p of paths) {
41
70
  const r = p.endsWith('/*')
42
- ? (0, path_1.join)((0, path_1.dirname)(p), (0, path_1.relative)(path.replace(/\*$/, ''), importExpr))
71
+ ? (0, node_path_1.join)((0, node_path_1.dirname)(p), (0, node_path_1.relative)(path.replace(/\*$/, ''), importExpr))
43
72
  : p;
44
73
  const maybeResolvedProject = this.findProjectOfResolvedModule(r);
45
74
  if (maybeResolvedProject) {
@@ -47,14 +76,14 @@ class TargetProjectLocator {
47
76
  }
48
77
  }
49
78
  }
50
- if (builtInModuleSet.has(importExpr)) {
79
+ if (isBuiltinModuleImport(importExpr)) {
51
80
  this.npmResolutionCache.set(importExpr, null);
52
81
  return null;
53
82
  }
54
83
  // try to find npm package before using expensive typescript resolution
55
- const npmProject = this.findNpmPackage(importExpr);
56
- if (npmProject) {
57
- return npmProject;
84
+ const externalProject = this.findNpmProjectFromImport(importExpr, filePath);
85
+ if (externalProject) {
86
+ return externalProject;
58
87
  }
59
88
  if (this.tsConfig.config) {
60
89
  // TODO(meeroslav): this block is probably obsolete
@@ -74,6 +103,55 @@ class TargetProjectLocator {
74
103
  this.npmResolutionCache.set(importExpr, null);
75
104
  return null;
76
105
  }
106
+ /**
107
+ * Resolve any external project that matches the given import expression,
108
+ * relative to the given file path.
109
+ *
110
+ * @param importExpr
111
+ * @param projectRoot
112
+ */
113
+ findNpmProjectFromImport(importExpr, fromFilePath) {
114
+ const packageName = parsePackageNameFromImportExpression(importExpr);
115
+ let fullFilePath = fromFilePath;
116
+ let workspaceRelativeFilePath = fromFilePath;
117
+ if (fromFilePath.startsWith(workspace_root_1.workspaceRoot)) {
118
+ workspaceRelativeFilePath = fromFilePath.replace(workspace_root_1.workspaceRoot, '');
119
+ }
120
+ else {
121
+ fullFilePath = (0, node_path_1.join)(workspace_root_1.workspaceRoot, fromFilePath);
122
+ }
123
+ const fullDirPath = (0, node_path_1.dirname)(fullFilePath);
124
+ const workspaceRelativeDirPath = (0, node_path_1.dirname)(workspaceRelativeFilePath);
125
+ const npmImportForProject = `${packageName}__${workspaceRelativeDirPath}`;
126
+ if (this.npmResolutionCache.has(npmImportForProject)) {
127
+ return this.npmResolutionCache.get(npmImportForProject);
128
+ }
129
+ try {
130
+ // package.json refers to an external package, we do not match against the version found in there, we instead try and resolve the relevant package how node would
131
+ const externalPackageJson = this.readPackageJson(packageName, fullDirPath);
132
+ // The external package.json path might be not be resolvable, e.g. if a reference has been added to a project package.json, but the install command has not been run yet.
133
+ if (!externalPackageJson) {
134
+ // Try and fall back to resolving an external node from the graph by name
135
+ const externalNode = this.npmProjects[`npm:${packageName}`];
136
+ const externalNodeName = externalNode?.name || null;
137
+ this.npmResolutionCache.set(npmImportForProject, externalNodeName);
138
+ return externalNodeName;
139
+ }
140
+ const npmProjectKey = `npm:${externalPackageJson.name}@${externalPackageJson.version}`;
141
+ const matchingExternalNode = this.npmProjects[npmProjectKey];
142
+ if (!matchingExternalNode) {
143
+ return null;
144
+ }
145
+ this.npmResolutionCache.set(npmImportForProject, matchingExternalNode.name);
146
+ return matchingExternalNode.name;
147
+ }
148
+ catch (e) {
149
+ if (process.env.NX_VERBOSE_LOGGING === 'true') {
150
+ console.error(e);
151
+ }
152
+ return null;
153
+ }
154
+ }
77
155
  /**
78
156
  * Return file paths matching the import relative to the repo root
79
157
  * @param normalizedImportExpr
@@ -113,23 +191,10 @@ class TargetProjectLocator {
113
191
  return;
114
192
  }
115
193
  resolveImportWithRequire(normalizedImportExpr, filePath) {
116
- return path_1.posix.relative(workspace_root_1.workspaceRoot, require.resolve(normalizedImportExpr, {
117
- paths: [(0, path_1.dirname)(filePath)],
194
+ return node_path_1.posix.relative(workspace_root_1.workspaceRoot, require.resolve(normalizedImportExpr, {
195
+ paths: [(0, node_path_1.dirname)(filePath)],
118
196
  }));
119
197
  }
120
- findNpmPackage(npmImport) {
121
- if (this.npmResolutionCache.has(npmImport)) {
122
- return this.npmResolutionCache.get(npmImport);
123
- }
124
- else {
125
- const pkg = this.npmProjects.find((pkg) => npmImport === pkg.data.packageName ||
126
- npmImport.startsWith(`${pkg.data.packageName}/`));
127
- if (pkg) {
128
- this.npmResolutionCache.set(npmImport, pkg.name);
129
- return pkg.name;
130
- }
131
- }
132
- }
133
198
  findProjectOfResolvedModule(resolvedModule) {
134
199
  if (resolvedModule.startsWith('node_modules/') ||
135
200
  resolvedModule.includes('/node_modules/')) {
@@ -142,7 +207,7 @@ class TargetProjectLocator {
142
207
  return importedProject ? importedProject.name : void 0;
143
208
  }
144
209
  getAbsolutePath(path) {
145
- return (0, path_1.join)(workspace_root_1.workspaceRoot, path);
210
+ return (0, node_path_1.join)(workspace_root_1.workspaceRoot, path);
146
211
  }
147
212
  getRootTsConfig() {
148
213
  const path = (0, typescript_1.getRootTsConfigFileName)();
@@ -164,20 +229,54 @@ class TargetProjectLocator {
164
229
  const project = (0, find_project_for_path_1.findProjectForPath)(file, this.projectRootMappings);
165
230
  return this.nodes[project];
166
231
  }
232
+ /**
233
+ * In many cases the package.json will be directly resolvable, so we try that first.
234
+ * If, however, package exports are used and the package.json is not defined, we will
235
+ * need to resolve the main entry point of the package and traverse upwards to find the
236
+ * package.json.
237
+ *
238
+ * In some cases, such as when multiple module formats are published, the resolved package.json
239
+ * might only contain the "type" field - no "name" or "version", so in such cases we keep traversing
240
+ * until we find a package.json that contains the "name" and "version" fields.
241
+ */
242
+ readPackageJson(packageName, relativeToDir) {
243
+ // The package.json is directly resolvable
244
+ const packageJsonPath = (0, resolve_relative_to_dir_1.resolveRelativeToDir)((0, node_path_1.join)(packageName, 'package.json'), relativeToDir);
245
+ if (packageJsonPath) {
246
+ return (0, fileutils_1.readJsonFile)(packageJsonPath);
247
+ }
248
+ try {
249
+ // Resolve the main entry point of the package
250
+ const mainPath = (0, resolve_relative_to_dir_1.resolveRelativeToDir)(packageName, relativeToDir);
251
+ let dir = (0, node_path_1.dirname)(mainPath);
252
+ while (dir !== (0, node_path_1.parse)(dir).root) {
253
+ const packageJsonPath = (0, node_path_1.join)(dir, 'package.json');
254
+ try {
255
+ const parsedPackageJson = (0, fileutils_1.readJsonFile)(packageJsonPath);
256
+ // Ensure the package.json contains the "name" and "version" fields
257
+ if (parsedPackageJson.name && parsedPackageJson.version) {
258
+ return parsedPackageJson;
259
+ }
260
+ }
261
+ catch {
262
+ // Package.json doesn't exist, keep traversing
263
+ }
264
+ dir = (0, node_path_1.dirname)(dir);
265
+ }
266
+ return null;
267
+ }
268
+ catch {
269
+ return null;
270
+ }
271
+ }
167
272
  }
168
273
  exports.TargetProjectLocator = TargetProjectLocator;
169
- // matches `npm:@scope/name`, `npm:name` but not `npm:@scope/name@version` and `npm:name@version`
170
- const ROOT_VERSION_PACKAGE_NAME_REGEX = /^npm:(?!.+@.+)/;
171
- function filterRootExternalDependencies(externalNodes) {
172
- if (!externalNodes) {
173
- return [];
174
- }
175
- const keys = Object.keys(externalNodes);
176
- const nodes = [];
177
- for (let i = 0; i < keys.length; i++) {
178
- if (keys[i].match(ROOT_VERSION_PACKAGE_NAME_REGEX)) {
179
- nodes.push(externalNodes[keys[i]]);
180
- }
274
+ function parsePackageNameFromImportExpression(importExpression) {
275
+ // Check if the package is scoped
276
+ if (importExpression.startsWith('@')) {
277
+ // For scoped packages, the package name is up to the second '/'
278
+ return importExpression.split('/').slice(0, 2).join('/');
181
279
  }
182
- return nodes;
280
+ // For unscoped packages, the package name is up to the first '/'
281
+ return importExpression.split('/')[0];
183
282
  }
@@ -0,0 +1,5 @@
1
+ /**
2
+ * NOTE: This function is in its own file because it is not possible to mock
3
+ * require.resolve directly in jest https://github.com/jestjs/jest/issues/9543
4
+ */
5
+ export declare function resolveRelativeToDir(path: string, relativeToDir: any): string;
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.resolveRelativeToDir = void 0;
4
+ /**
5
+ * NOTE: This function is in its own file because it is not possible to mock
6
+ * require.resolve directly in jest https://github.com/jestjs/jest/issues/9543
7
+ */
8
+ function resolveRelativeToDir(path, relativeToDir) {
9
+ try {
10
+ return require.resolve(path, {
11
+ paths: [relativeToDir],
12
+ });
13
+ }
14
+ catch {
15
+ return null;
16
+ }
17
+ }
18
+ exports.resolveRelativeToDir = resolveRelativeToDir;
@@ -1,10 +1,10 @@
1
- import { CreateNodesResultWithContext } from './plugins/internal-api';
2
1
  import { ConfigurationResult, ConfigurationSourceMaps } from './utils/project-configuration-utils';
3
2
  import { ProjectConfiguration } from '../config/workspace-json-project-json';
4
3
  import { ProjectGraph } from '../config/project-graph';
4
+ import { CreateNodesFunctionV2 } from './plugins';
5
5
  export declare class ProjectGraphError extends Error {
6
6
  #private;
7
- constructor(errors: Array<CreateNodesError | MergeNodesError | ProjectsWithNoNameError | MultipleProjectsWithSameNameError | ProcessDependenciesError | ProcessProjectGraphError | CreateMetadataError | WorkspaceValidityError>, partialProjectGraph: ProjectGraph, partialSourceMaps: ConfigurationSourceMaps);
7
+ constructor(errors: Array<AggregateCreateNodesError | MergeNodesError | ProjectsWithNoNameError | MultipleProjectsWithSameNameError | ProcessDependenciesError | ProcessProjectGraphError | CreateMetadataError | WorkspaceValidityError>, partialProjectGraph: ProjectGraph, partialSourceMaps: ConfigurationSourceMaps);
8
8
  /**
9
9
  * The daemon cannot throw errors which contain methods as they are not serializable.
10
10
  *
@@ -18,7 +18,7 @@ export declare class ProjectGraphError extends Error {
18
18
  */
19
19
  getPartialProjectGraph(): ProjectGraph;
20
20
  getPartialSourcemaps(): ConfigurationSourceMaps;
21
- getErrors(): (CreateNodesError | MergeNodesError | ProjectsWithNoNameError | MultipleProjectsWithSameNameError | CreateMetadataError | ProcessDependenciesError | ProcessProjectGraphError | WorkspaceValidityError)[];
21
+ getErrors(): (AggregateCreateNodesError | MergeNodesError | CreateMetadataError | ProjectsWithNoNameError | MultipleProjectsWithSameNameError | ProcessDependenciesError | ProcessProjectGraphError | WorkspaceValidityError)[];
22
22
  }
23
23
  export declare class MultipleProjectsWithSameNameError extends Error {
24
24
  conflicts: Map<string, string[]>;
@@ -44,25 +44,44 @@ export declare class ProjectWithNoNameError extends Error {
44
44
  }
45
45
  export declare function isProjectWithNoNameError(e: unknown): e is ProjectWithNoNameError;
46
46
  export declare class ProjectConfigurationsError extends Error {
47
- readonly errors: Array<MergeNodesError | CreateNodesError | ProjectsWithNoNameError | MultipleProjectsWithSameNameError>;
47
+ readonly errors: Array<MergeNodesError | AggregateCreateNodesError | ProjectsWithNoNameError | MultipleProjectsWithSameNameError>;
48
48
  readonly partialProjectConfigurationsResult: ConfigurationResult;
49
- constructor(errors: Array<MergeNodesError | CreateNodesError | ProjectsWithNoNameError | MultipleProjectsWithSameNameError>, partialProjectConfigurationsResult: ConfigurationResult);
49
+ constructor(errors: Array<MergeNodesError | AggregateCreateNodesError | ProjectsWithNoNameError | MultipleProjectsWithSameNameError>, partialProjectConfigurationsResult: ConfigurationResult);
50
50
  }
51
51
  export declare function isProjectConfigurationsError(e: unknown): e is ProjectConfigurationsError;
52
- export declare class CreateNodesError extends Error {
53
- file: string;
54
- pluginName: string;
55
- constructor({ file, pluginName, error, }: {
56
- file: string;
57
- pluginName: string;
58
- error: Error;
59
- });
60
- }
52
+ /**
53
+ * This error should be thrown when a `createNodesV2` function hits a recoverable error.
54
+ * It allows Nx to recieve partial results and continue processing for better UX.
55
+ */
61
56
  export declare class AggregateCreateNodesError extends Error {
62
- readonly pluginName: string;
63
- readonly errors: Array<CreateNodesError>;
64
- readonly partialResults: Array<CreateNodesResultWithContext>;
65
- constructor(pluginName: string, errors: Array<CreateNodesError>, partialResults: Array<CreateNodesResultWithContext>);
57
+ readonly errors: Array<[file: string | null, error: Error]>;
58
+ readonly partialResults: Awaited<ReturnType<CreateNodesFunctionV2>>;
59
+ /**
60
+ * Throwing this error from a `createNodesV2` function will allow Nx to continue processing and recieve partial results from your plugin.
61
+ * @example
62
+ * export async function createNodesV2(
63
+ * files: string[],
64
+ * ) {
65
+ * const partialResults = [];
66
+ * const errors = [];
67
+ * await Promise.all(files.map(async (file) => {
68
+ * try {
69
+ * const result = await createNodes(file);
70
+ * partialResults.push(result);
71
+ * } catch (e) {
72
+ * errors.push([file, e]);
73
+ * }
74
+ * }));
75
+ * if (errors.length > 0) {
76
+ * throw new AggregateCreateNodesError(errors, partialResults);
77
+ * }
78
+ * return partialResults;
79
+ * }
80
+ *
81
+ * @param errors An array of tuples that represent errors encountered when processing a given file. An example entry might look like ['path/to/project.json', [Error: 'Invalid JSON. Unexpected token 'a' in JSON at position 0]]
82
+ * @param partialResults The partial results of the `createNodesV2` function. This should be the results for each file that didn't encounter an issue.
83
+ */
84
+ constructor(errors: Array<[file: string | null, error: Error]>, partialResults: Awaited<ReturnType<CreateNodesFunctionV2>>);
66
85
  }
67
86
  export declare class MergeNodesError extends Error {
68
87
  file: string;
@@ -102,7 +121,6 @@ export declare class AggregateProjectGraphError extends Error {
102
121
  }
103
122
  export declare function isAggregateProjectGraphError(e: unknown): e is AggregateProjectGraphError;
104
123
  export declare function isCreateMetadataError(e: unknown): e is CreateMetadataError;
105
- export declare function isCreateNodesError(e: unknown): e is CreateNodesError;
106
124
  export declare function isAggregateCreateNodesError(e: unknown): e is AggregateCreateNodesError;
107
125
  export declare function isMergeNodesError(e: unknown): e is MergeNodesError;
108
126
  export declare class DaemonProjectGraphError extends Error {
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
2
  var _ProjectGraphError_errors, _ProjectGraphError_partialProjectGraph, _ProjectGraphError_partialSourceMaps;
3
3
  Object.defineProperty(exports, "__esModule", { value: true });
4
- exports.LoadPluginError = exports.DaemonProjectGraphError = exports.isMergeNodesError = exports.isAggregateCreateNodesError = exports.isCreateNodesError = exports.isCreateMetadataError = exports.isAggregateProjectGraphError = exports.AggregateProjectGraphError = exports.ProcessProjectGraphError = exports.isWorkspaceValidityError = exports.WorkspaceValidityError = exports.ProcessDependenciesError = exports.CreateMetadataError = exports.MergeNodesError = exports.AggregateCreateNodesError = exports.CreateNodesError = exports.isProjectConfigurationsError = exports.ProjectConfigurationsError = exports.isProjectWithNoNameError = exports.ProjectWithNoNameError = exports.isProjectsWithNoNameError = exports.ProjectsWithNoNameError = exports.isMultipleProjectsWithSameNameError = exports.isProjectWithExistingNameError = exports.ProjectWithExistingNameError = exports.MultipleProjectsWithSameNameError = exports.ProjectGraphError = void 0;
4
+ exports.LoadPluginError = exports.DaemonProjectGraphError = exports.isMergeNodesError = exports.isAggregateCreateNodesError = exports.isCreateMetadataError = exports.isAggregateProjectGraphError = exports.AggregateProjectGraphError = exports.ProcessProjectGraphError = exports.isWorkspaceValidityError = exports.WorkspaceValidityError = exports.ProcessDependenciesError = exports.CreateMetadataError = exports.MergeNodesError = exports.AggregateCreateNodesError = exports.isProjectConfigurationsError = exports.ProjectConfigurationsError = exports.isProjectWithNoNameError = exports.ProjectWithNoNameError = exports.isProjectsWithNoNameError = exports.ProjectsWithNoNameError = exports.isMultipleProjectsWithSameNameError = exports.isProjectWithExistingNameError = exports.ProjectWithExistingNameError = exports.MultipleProjectsWithSameNameError = exports.ProjectGraphError = void 0;
5
5
  const tslib_1 = require("tslib");
6
6
  class ProjectGraphError extends Error {
7
7
  constructor(errors, partialProjectGraph, partialSourceMaps) {
@@ -126,21 +126,38 @@ function isProjectConfigurationsError(e) {
126
126
  e?.name === ProjectConfigurationsError.name));
127
127
  }
128
128
  exports.isProjectConfigurationsError = isProjectConfigurationsError;
129
- class CreateNodesError extends Error {
130
- constructor({ file, pluginName, error, }) {
131
- const msg = `The "${pluginName}" plugin threw an error while creating nodes from ${file}:`;
132
- super(msg, { cause: error });
133
- this.name = this.constructor.name;
134
- this.file = file;
135
- this.pluginName = pluginName;
136
- this.stack = `${this.message}\n ${error.stack.split('\n').join('\n ')}`;
137
- }
138
- }
139
- exports.CreateNodesError = CreateNodesError;
129
+ /**
130
+ * This error should be thrown when a `createNodesV2` function hits a recoverable error.
131
+ * It allows Nx to recieve partial results and continue processing for better UX.
132
+ */
140
133
  class AggregateCreateNodesError extends Error {
141
- constructor(pluginName, errors, partialResults) {
134
+ /**
135
+ * Throwing this error from a `createNodesV2` function will allow Nx to continue processing and recieve partial results from your plugin.
136
+ * @example
137
+ * export async function createNodesV2(
138
+ * files: string[],
139
+ * ) {
140
+ * const partialResults = [];
141
+ * const errors = [];
142
+ * await Promise.all(files.map(async (file) => {
143
+ * try {
144
+ * const result = await createNodes(file);
145
+ * partialResults.push(result);
146
+ * } catch (e) {
147
+ * errors.push([file, e]);
148
+ * }
149
+ * }));
150
+ * if (errors.length > 0) {
151
+ * throw new AggregateCreateNodesError(errors, partialResults);
152
+ * }
153
+ * return partialResults;
154
+ * }
155
+ *
156
+ * @param errors An array of tuples that represent errors encountered when processing a given file. An example entry might look like ['path/to/project.json', [Error: 'Invalid JSON. Unexpected token 'a' in JSON at position 0]]
157
+ * @param partialResults The partial results of the `createNodesV2` function. This should be the results for each file that didn't encounter an issue.
158
+ */
159
+ constructor(errors, partialResults) {
142
160
  super('Failed to create nodes');
143
- this.pluginName = pluginName;
144
161
  this.errors = errors;
145
162
  this.partialResults = partialResults;
146
163
  this.name = this.constructor.name;
@@ -230,11 +247,6 @@ function isCreateMetadataError(e) {
230
247
  e?.name === CreateMetadataError.name));
231
248
  }
232
249
  exports.isCreateMetadataError = isCreateMetadataError;
233
- function isCreateNodesError(e) {
234
- return (e instanceof CreateNodesError ||
235
- (typeof e === 'object' && 'name' in e && e?.name === CreateNodesError.name));
236
- }
237
- exports.isCreateNodesError = isCreateNodesError;
238
250
  function isAggregateCreateNodesError(e) {
239
251
  return (e instanceof AggregateCreateNodesError ||
240
252
  (typeof e === 'object' &&
@@ -1,7 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.updateFileMap = exports.createFileMap = exports.createFileMapUsingProjectGraph = exports.createProjectFileMapUsingProjectGraph = void 0;
4
- const client_1 = require("../daemon/client/client");
5
4
  const workspace_context_1 = require("../utils/workspace-context");
6
5
  const workspace_root_1 = require("../utils/workspace-root");
7
6
  const project_graph_1 = require("./project-graph");
@@ -14,13 +13,7 @@ exports.createProjectFileMapUsingProjectGraph = createProjectFileMapUsingProject
14
13
  // TODO: refactor this to pull straight from the rust context instead of creating the file map in JS
15
14
  async function createFileMapUsingProjectGraph(graph) {
16
15
  const configs = (0, project_graph_1.readProjectsConfigurationFromProjectGraph)(graph);
17
- let files;
18
- if (client_1.daemonClient.enabled()) {
19
- files = await client_1.daemonClient.getAllFileData();
20
- }
21
- else {
22
- files = (0, workspace_context_1.getAllFileDataInContext)(workspace_root_1.workspaceRoot);
23
- }
16
+ let files = await (0, workspace_context_1.getAllFileDataInContext)(workspace_root_1.workspaceRoot);
24
17
  return createFileMap(configs, files);
25
18
  }
26
19
  exports.createFileMapUsingProjectGraph = createFileMapUsingProjectGraph;
@@ -11,11 +11,10 @@ const json_diff_1 = require("../utils/json-diff");
11
11
  const project_graph_1 = require("./project-graph");
12
12
  const angular_json_1 = require("../adapter/angular-json");
13
13
  const ignore_1 = require("../utils/ignore");
14
- const retrieve_workspace_files_1 = require("./utils/retrieve-workspace-files");
15
14
  const project_configuration_utils_1 = require("./utils/project-configuration-utils");
16
- const nx_plugin_deprecated_1 = require("../utils/nx-plugin.deprecated");
17
- const minimatch_1 = require("minimatch");
18
- const package_json_next_to_project_json_1 = require("../plugins/project-json/build-nodes/package-json-next-to-project-json");
15
+ const package_json_workspaces_1 = require("../plugins/package-json-workspaces");
16
+ const workspace_context_1 = require("../utils/workspace-context");
17
+ const project_json_1 = require("../plugins/project-json/build-nodes/project-json");
19
18
  class WholeFileChange {
20
19
  constructor() {
21
20
  this.type = 'WholeFileChange';
@@ -115,7 +114,7 @@ function readWorkspaceConfig(opts) {
115
114
  catch {
116
115
  configuration = {
117
116
  version: 2,
118
- projects: getProjectsSyncNoInference(root, nxJson).projects,
117
+ projects: getProjectsSync(root, nxJson),
119
118
  };
120
119
  }
121
120
  if (opts.format === 'angularCli') {
@@ -142,38 +141,38 @@ exports.readPackageJson = readPackageJson;
142
141
  /**
143
142
  * TODO(v20): Remove this function.
144
143
  */
145
- function getProjectsSyncNoInference(root, nxJson) {
146
- const allConfigFiles = (0, retrieve_workspace_files_1.retrieveProjectConfigurationPaths)(root, (0, nx_plugin_deprecated_1.getDefaultPluginsSync)(root));
147
- const plugins = [
148
- package_json_next_to_project_json_1.PackageJsonProjectsNextToProjectJsonPlugin,
149
- ...(0, nx_plugin_deprecated_1.getDefaultPluginsSync)(root),
144
+ function getProjectsSync(root, nxJson) {
145
+ /**
146
+ * We can't update projects that come from plugins anyways, so we are going
147
+ * to ignore them for now. Plugins should add their own add/create/update methods
148
+ * if they would like to use devkit to update inferred projects.
149
+ */
150
+ const patterns = [
151
+ '**/project.json',
152
+ 'project.json',
153
+ ...(0, package_json_workspaces_1.getGlobPatternsFromPackageManagerWorkspaces)(root, fileutils_1.readJsonFile),
150
154
  ];
151
- const projectRootMap = {};
152
- // We iterate over plugins first - this ensures that plugins specified first take precedence.
153
- for (const plugin of plugins) {
154
- const [pattern, createNodes] = plugin.createNodes ?? [];
155
- if (!pattern) {
156
- continue;
155
+ const projectFiles = (0, workspace_context_1.globWithWorkspaceContextSync)(root, patterns);
156
+ const rootMap = {};
157
+ for (const projectFile of projectFiles) {
158
+ if ((0, path_1.basename)(projectFile) === 'project.json') {
159
+ const json = (0, fileutils_1.readJsonFile)(projectFile);
160
+ const config = (0, project_json_1.buildProjectFromProjectJson)(json, projectFile);
161
+ (0, project_configuration_utils_1.mergeProjectConfigurationIntoRootMap)(rootMap, config, undefined, undefined, true);
157
162
  }
158
- const matchingConfigFiles = allConfigFiles.filter((file) => (0, minimatch_1.minimatch)(file, pattern, { dot: true }));
159
- for (const file of matchingConfigFiles) {
160
- if ((0, minimatch_1.minimatch)(file, pattern, { dot: true })) {
161
- let r = createNodes(file, {}, {
162
- nxJsonConfiguration: nxJson,
163
- workspaceRoot: root,
164
- configFiles: matchingConfigFiles,
165
- });
166
- for (const node in r.projects) {
167
- const project = {
168
- root: node,
169
- ...r.projects[node],
170
- };
171
- (0, project_configuration_utils_1.mergeProjectConfigurationIntoRootMap)(projectRootMap, project);
172
- }
163
+ else if ((0, path_1.basename)(projectFile) === 'package.json') {
164
+ const packageJson = (0, fileutils_1.readJsonFile)(projectFile);
165
+ const config = (0, package_json_workspaces_1.buildProjectConfigurationFromPackageJson)(packageJson, projectFile, nxJson);
166
+ if (!rootMap[config.root]) {
167
+ (0, project_configuration_utils_1.mergeProjectConfigurationIntoRootMap)(rootMap,
168
+ // Inferred targets, tags, etc don't show up when running generators
169
+ // This is to help avoid running into issues when trying to update the workspace
170
+ {
171
+ name: config.name,
172
+ root: config.root,
173
+ }, undefined, undefined, true);
173
174
  }
174
175
  }
175
176
  }
176
- return {
177
- projects: (0, project_configuration_utils_1.readProjectConfigurationsFromRootMap)(projectRootMap),
178
- };
177
+ return (0, project_configuration_utils_1.readProjectConfigurationsFromRootMap)(rootMap);
179
178
  }
@@ -1,2 +1,3 @@
1
1
  export * from './public-api';
2
2
  export { readPluginPackageJson, registerPluginTSTranspiler } from './loader';
3
+ export { createNodesFromFiles } from './utils';
@@ -1,8 +1,10 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.registerPluginTSTranspiler = exports.readPluginPackageJson = void 0;
3
+ exports.createNodesFromFiles = exports.registerPluginTSTranspiler = exports.readPluginPackageJson = void 0;
4
4
  const tslib_1 = require("tslib");
5
5
  tslib_1.__exportStar(require("./public-api"), exports);
6
6
  var loader_1 = require("./loader");
7
7
  Object.defineProperty(exports, "readPluginPackageJson", { enumerable: true, get: function () { return loader_1.readPluginPackageJson; } });
8
8
  Object.defineProperty(exports, "registerPluginTSTranspiler", { enumerable: true, get: function () { return loader_1.registerPluginTSTranspiler; } });
9
+ var utils_1 = require("./utils");
10
+ Object.defineProperty(exports, "createNodesFromFiles", { enumerable: true, get: function () { return utils_1.createNodesFromFiles; } });
@@ -1,12 +1,12 @@
1
1
  import { PluginConfiguration } from '../../config/nx-json';
2
2
  import { NxPluginV1 } from '../../utils/nx-plugin.deprecated';
3
- import { CreateDependencies, CreateDependenciesContext, CreateMetadata, CreateMetadataContext, CreateNodesContext, CreateNodesResult, NxPluginV2 } from './public-api';
3
+ import { CreateDependencies, CreateDependenciesContext, CreateMetadata, CreateMetadataContext, CreateNodesContextV2, CreateNodesResult, NxPluginV2 } from './public-api';
4
4
  import { ProjectGraph, ProjectGraphProcessor } from '../../config/project-graph';
5
5
  export declare class LoadedNxPlugin {
6
6
  readonly name: string;
7
7
  readonly createNodes?: [
8
8
  filePattern: string,
9
- fn: (matchedFiles: string[], context: CreateNodesContext) => Promise<CreateNodesResultWithContext[]>
9
+ fn: (matchedFiles: string[], context: CreateNodesContextV2) => Promise<Array<readonly [plugin: string, file: string, result: CreateNodesResult]>>
10
10
  ];
11
11
  readonly createDependencies?: (context: CreateDependenciesContext) => ReturnType<CreateDependencies>;
12
12
  readonly createMetadata?: (graph: ProjectGraph, context: CreateMetadataContext) => ReturnType<CreateMetadata>;