nx 20.3.0-canary.20241213-0d6667d → 20.3.0-canary.20241217-ee4de0b

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 (35) hide show
  1. package/package.json +12 -11
  2. package/src/adapter/ngcli-adapter.js +7 -7
  3. package/src/command-line/add/add.js +21 -45
  4. package/src/command-line/generate/generator-utils.js +2 -2
  5. package/src/command-line/import/import.js +60 -34
  6. package/src/command-line/init/configure-plugins.d.ts +35 -0
  7. package/src/command-line/init/configure-plugins.js +189 -0
  8. package/src/command-line/init/init-v2.d.ts +1 -2
  9. package/src/command-line/init/init-v2.js +3 -18
  10. package/src/command-line/run/executor-utils.js +4 -4
  11. package/src/config/schema-utils.d.ts +4 -3
  12. package/src/config/schema-utils.js +71 -4
  13. package/src/config/workspace-json-project-json.d.ts +5 -0
  14. package/src/daemon/client/client.js +9 -0
  15. package/src/native/nx.wasm32-wasi.wasm +0 -0
  16. package/src/plugins/js/project-graph/build-dependencies/explicit-package-json-dependencies.d.ts +2 -2
  17. package/src/plugins/js/project-graph/build-dependencies/explicit-package-json-dependencies.js +6 -20
  18. package/src/plugins/js/project-graph/build-dependencies/target-project-locator.d.ts +3 -1
  19. package/src/plugins/js/project-graph/build-dependencies/target-project-locator.js +12 -1
  20. package/src/plugins/js/utils/packages.d.ts +3 -0
  21. package/src/plugins/js/utils/packages.js +25 -0
  22. package/src/plugins/package-json/create-nodes.js +6 -0
  23. package/src/project-graph/build-project-graph.js +57 -1
  24. package/src/project-graph/plugins/internal-api.d.ts +4 -3
  25. package/src/project-graph/plugins/internal-api.js +2 -2
  26. package/src/project-graph/plugins/isolation/messaging.d.ts +2 -2
  27. package/src/project-graph/plugins/loader.js +13 -6
  28. package/src/project-graph/utils/project-configuration-utils.js +31 -1
  29. package/src/tasks-runner/forked-process-task-runner.js +30 -8
  30. package/src/utils/delayed-spinner.d.ts +40 -0
  31. package/src/utils/delayed-spinner.js +58 -0
  32. package/src/utils/package-json.d.ts +1 -0
  33. package/src/utils/package-json.js +5 -1
  34. package/src/command-line/import/utils/needs-install.d.ts +0 -3
  35. package/src/command-line/import/utils/needs-install.js +0 -31
@@ -1,15 +1,16 @@
1
+ import type { ProjectConfiguration } from './workspace-json-project-json';
1
2
  /**
2
3
  * This function is used to get the implementation factory of an executor or generator.
3
4
  * @param implementation path to the implementation
4
5
  * @param directory path to the directory
5
6
  * @returns a function that returns the implementation
6
7
  */
7
- export declare function getImplementationFactory<T>(implementation: string, directory: string): () => T;
8
+ export declare function getImplementationFactory<T>(implementation: string, directory: string, packageName: string, projects: Record<string, ProjectConfiguration>): () => T;
8
9
  /**
9
10
  * This function is used to resolve the implementation of an executor or generator.
10
11
  * @param implementationModulePath
11
12
  * @param directory
12
13
  * @returns path to the implementation
13
14
  */
14
- export declare function resolveImplementation(implementationModulePath: string, directory: string): string;
15
- export declare function resolveSchema(schemaPath: string, directory: string): string;
15
+ export declare function resolveImplementation(implementationModulePath: string, directory: string, packageName: string, projects: Record<string, ProjectConfiguration>): string;
16
+ export declare function resolveSchema(schemaPath: string, directory: string, packageName: string, projects: Record<string, ProjectConfiguration>): string;
@@ -5,17 +5,20 @@ exports.resolveImplementation = resolveImplementation;
5
5
  exports.resolveSchema = resolveSchema;
6
6
  const fs_1 = require("fs");
7
7
  const path_1 = require("path");
8
+ const resolve_exports_1 = require("resolve.exports");
9
+ const packages_1 = require("../plugins/js/utils/packages");
8
10
  const plugins_1 = require("../project-graph/plugins");
11
+ const path_2 = require("../utils/path");
9
12
  /**
10
13
  * This function is used to get the implementation factory of an executor or generator.
11
14
  * @param implementation path to the implementation
12
15
  * @param directory path to the directory
13
16
  * @returns a function that returns the implementation
14
17
  */
15
- function getImplementationFactory(implementation, directory) {
18
+ function getImplementationFactory(implementation, directory, packageName, projects) {
16
19
  const [implementationModulePath, implementationExportName] = implementation.split('#');
17
20
  return () => {
18
- const modulePath = resolveImplementation(implementationModulePath, directory);
21
+ const modulePath = resolveImplementation(implementationModulePath, directory, packageName, projects);
19
22
  if ((0, path_1.extname)(modulePath) === '.ts') {
20
23
  (0, plugins_1.registerPluginTSTranspiler)();
21
24
  }
@@ -31,8 +34,19 @@ function getImplementationFactory(implementation, directory) {
31
34
  * @param directory
32
35
  * @returns path to the implementation
33
36
  */
34
- function resolveImplementation(implementationModulePath, directory) {
37
+ function resolveImplementation(implementationModulePath, directory, packageName, projects) {
35
38
  const validImplementations = ['', '.js', '.ts'].map((x) => implementationModulePath + x);
39
+ if (!directory.includes('node_modules')) {
40
+ // It might be a local plugin where the implementation path points to the
41
+ // outputs which might not exist or can be stale. We prioritize finding
42
+ // the implementation from the source over the outputs.
43
+ for (const maybeImplementation of validImplementations) {
44
+ const maybeImplementationFromSource = tryResolveFromSource(maybeImplementation, directory, packageName, projects);
45
+ if (maybeImplementationFromSource) {
46
+ return maybeImplementationFromSource;
47
+ }
48
+ }
49
+ }
36
50
  for (const maybeImplementation of validImplementations) {
37
51
  const maybeImplementationPath = (0, path_1.join)(directory, maybeImplementation);
38
52
  if ((0, fs_1.existsSync)(maybeImplementationPath)) {
@@ -47,7 +61,16 @@ function resolveImplementation(implementationModulePath, directory) {
47
61
  }
48
62
  throw new Error(`Could not resolve "${implementationModulePath}" from "${directory}".`);
49
63
  }
50
- function resolveSchema(schemaPath, directory) {
64
+ function resolveSchema(schemaPath, directory, packageName, projects) {
65
+ if (!directory.includes('node_modules')) {
66
+ // It might be a local plugin where the schema path points to the outputs
67
+ // which might not exist or can be stale. We prioritize finding the schema
68
+ // from the source over the outputs.
69
+ const schemaPathFromSource = tryResolveFromSource(schemaPath, directory, packageName, projects);
70
+ if (schemaPathFromSource) {
71
+ return schemaPathFromSource;
72
+ }
73
+ }
51
74
  const maybeSchemaPath = (0, path_1.join)(directory, schemaPath);
52
75
  if ((0, fs_1.existsSync)(maybeSchemaPath)) {
53
76
  return maybeSchemaPath;
@@ -56,3 +79,47 @@ function resolveSchema(schemaPath, directory) {
56
79
  paths: [directory],
57
80
  });
58
81
  }
82
+ let packageEntryPointsToProjectMap;
83
+ function tryResolveFromSource(path, directory, packageName, projects) {
84
+ packageEntryPointsToProjectMap ??=
85
+ (0, packages_1.getPackageEntryPointsToProjectMap)(projects);
86
+ const localProject = packageEntryPointsToProjectMap[packageName];
87
+ if (!localProject) {
88
+ // it doesn't match any of the package names from the local projects
89
+ return null;
90
+ }
91
+ try {
92
+ const fromExports = (0, resolve_exports_1.resolve)({
93
+ name: localProject.metadata.js.packageName,
94
+ exports: localProject.metadata.js.packageExports,
95
+ }, path, { conditions: ['development'] });
96
+ if (fromExports && fromExports.length) {
97
+ for (const exportPath of fromExports) {
98
+ if ((0, fs_1.existsSync)((0, path_1.join)(directory, exportPath))) {
99
+ return (0, path_1.join)(directory, exportPath);
100
+ }
101
+ }
102
+ }
103
+ }
104
+ catch { }
105
+ /**
106
+ * Fall back to try to "guess" the source by checking the path in some common directories:
107
+ * - the root of the project
108
+ * - the src directory
109
+ * - the src/lib directory
110
+ */
111
+ const segments = (0, path_2.normalizePath)(path).replace(/^\.\//, '').split('/');
112
+ for (let i = 1; i < segments.length; i++) {
113
+ const possiblePaths = [
114
+ (0, path_1.join)(directory, ...segments.slice(i)),
115
+ (0, path_1.join)(directory, 'src', ...segments.slice(i)),
116
+ (0, path_1.join)(directory, 'src', 'lib', ...segments.slice(i)),
117
+ ];
118
+ for (const possiblePath of possiblePaths) {
119
+ if ((0, fs_1.existsSync)(possiblePath)) {
120
+ return possiblePath;
121
+ }
122
+ }
123
+ }
124
+ return null;
125
+ }
@@ -1,3 +1,4 @@
1
+ import type { PackageJson } from '../utils/package-json';
1
2
  import type { NxJsonConfiguration, NxReleaseVersionConfiguration } from './nx-json';
2
3
  /**
3
4
  * @deprecated use ProjectsConfigurations or NxJsonConfiguration
@@ -122,6 +123,10 @@ export interface ProjectMetadata {
122
123
  }[];
123
124
  };
124
125
  };
126
+ js?: {
127
+ packageName: string;
128
+ packageExports: undefined | PackageJson['exports'];
129
+ };
125
130
  }
126
131
  export interface TargetMetadata {
127
132
  [k: string]: any;
@@ -30,6 +30,7 @@ const get_sync_generator_changes_1 = require("../message-types/get-sync-generato
30
30
  const get_registered_sync_generators_1 = require("../message-types/get-registered-sync-generators");
31
31
  const update_workspace_context_1 = require("../message-types/update-workspace-context");
32
32
  const flush_sync_generator_changes_to_disk_1 = require("../message-types/flush-sync-generator-changes-to-disk");
33
+ const delayed_spinner_1 = require("../../utils/delayed-spinner");
33
34
  const DAEMON_ENV_SETTINGS = {
34
35
  NX_PROJECT_GLOB_CACHE: 'false',
35
36
  NX_CACHE_PROJECTS_CONFIG: 'false',
@@ -113,6 +114,11 @@ class DaemonClient {
113
114
  return this.sendToDaemonViaQueue({ type: 'REQUEST_SHUTDOWN' });
114
115
  }
115
116
  async getProjectGraphAndSourceMaps() {
117
+ let spinner;
118
+ if (delayed_spinner_1.SHOULD_SHOW_SPINNERS) {
119
+ // If the graph takes a while to load, we want to show a spinner.
120
+ spinner = new delayed_spinner_1.DelayedSpinner('Calculating the project graph on the Nx Daemon', 500).scheduleMessageUpdate('Calculating the project graph on the Nx Daemon is taking longer than expected. Re-run with NX_DAEMON=false to see more details.', 30_000);
121
+ }
116
122
  try {
117
123
  const response = await this.sendToDaemonViaQueue({
118
124
  type: 'REQUEST_PROJECT_GRAPH',
@@ -130,6 +136,9 @@ class DaemonClient {
130
136
  throw e;
131
137
  }
132
138
  }
139
+ finally {
140
+ spinner?.cleanup();
141
+ }
133
142
  }
134
143
  async getAllFileData() {
135
144
  return await this.sendToDaemonViaQueue({ type: 'REQUEST_FILE_DATA' });
Binary file
@@ -1,4 +1,4 @@
1
- import { CreateDependenciesContext } from '../../../../project-graph/plugins';
2
- import { RawProjectGraphDependency } from '../../../../project-graph/project-graph-builder';
1
+ import type { CreateDependenciesContext } from '../../../../project-graph/plugins';
2
+ import { type RawProjectGraphDependency } from '../../../../project-graph/project-graph-builder';
3
3
  import { TargetProjectLocator } from './target-project-locator';
4
4
  export declare function buildExplicitPackageJsonDependencies(ctx: CreateDependenciesContext, targetProjectLocator: TargetProjectLocator): RawProjectGraphDependency[];
@@ -1,7 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.buildExplicitPackageJsonDependencies = buildExplicitPackageJsonDependencies;
4
- const node_path_1 = require("node:path");
5
4
  const project_graph_1 = require("../../../../config/project-graph");
6
5
  const file_utils_1 = require("../../../../project-graph/file-utils");
7
6
  const project_graph_builder_1 = require("../../../../project-graph/project-graph-builder");
@@ -9,43 +8,30 @@ const json_1 = require("../../../../utils/json");
9
8
  const path_1 = require("../../../../utils/path");
10
9
  function buildExplicitPackageJsonDependencies(ctx, targetProjectLocator) {
11
10
  const res = [];
12
- let packageNameMap = undefined;
13
11
  const nodes = Object.values(ctx.projects);
14
12
  Object.keys(ctx.filesToProcess.projectFileMap).forEach((source) => {
15
13
  Object.values(ctx.filesToProcess.projectFileMap[source]).forEach((f) => {
16
14
  if (isPackageJsonAtProjectRoot(nodes, f.file)) {
17
- // we only create the package name map once and only if a package.json file changes
18
- packageNameMap = packageNameMap || createPackageNameMap(ctx.projects);
19
- processPackageJson(source, f.file, ctx, targetProjectLocator, res, packageNameMap);
15
+ processPackageJson(source, f.file, ctx, targetProjectLocator, res);
20
16
  }
21
17
  });
22
18
  });
23
19
  return res;
24
20
  }
25
- function createPackageNameMap(projects) {
26
- const res = {};
27
- for (let projectName of Object.keys(projects)) {
28
- try {
29
- const packageJson = (0, json_1.parseJson)((0, file_utils_1.defaultFileRead)((0, node_path_1.join)(projects[projectName].root, 'package.json')));
30
- res[packageJson.name ?? projectName] = projectName;
31
- }
32
- catch (e) { }
33
- }
34
- return res;
35
- }
36
21
  function isPackageJsonAtProjectRoot(nodes, fileName) {
37
22
  return (fileName.endsWith('package.json') &&
38
23
  nodes.find((projectNode) => (0, path_1.joinPathFragments)(projectNode.root, 'package.json') === fileName));
39
24
  }
40
- function processPackageJson(sourceProject, fileName, ctx, targetProjectLocator, collectedDeps, packageNameMap) {
25
+ function processPackageJson(sourceProject, fileName, ctx, targetProjectLocator, collectedDeps) {
41
26
  try {
42
27
  const deps = readDeps((0, json_1.parseJson)((0, file_utils_1.defaultFileRead)(fileName)));
43
28
  for (const d of Object.keys(deps)) {
44
- // package.json refers to another project in the monorepo
45
- if (packageNameMap[d]) {
29
+ const localProject = targetProjectLocator.findDependencyInWorkspaceProjects(d);
30
+ if (localProject) {
31
+ // package.json refers to another project in the monorepo
46
32
  const dependency = {
47
33
  source: sourceProject,
48
- target: packageNameMap[d],
34
+ target: localProject,
49
35
  sourceFile: fileName,
50
36
  type: project_graph_1.DependencyType.static,
51
37
  };
@@ -1,4 +1,4 @@
1
- import { ProjectGraphExternalNode, ProjectGraphProjectNode } from '../../../../config/project-graph';
1
+ import type { ProjectGraphExternalNode, ProjectGraphProjectNode } from '../../../../config/project-graph';
2
2
  /**
3
3
  * The key is a combination of the package name and the workspace relative directory
4
4
  * containing the file importing it e.g. `lodash__packages/my-lib`, the value is the
@@ -15,6 +15,7 @@ export declare class TargetProjectLocator {
15
15
  private tsConfig;
16
16
  private paths;
17
17
  private typescriptResolutionCache;
18
+ private packageEntryPointsToProjectMap;
18
19
  constructor(nodes: Record<string, ProjectGraphProjectNode>, externalNodes?: Record<string, ProjectGraphExternalNode>, npmResolutionCache?: NpmResolutionCache);
19
20
  /**
20
21
  * Resolve any workspace or external project that matches the given import expression,
@@ -38,6 +39,7 @@ export declare class TargetProjectLocator {
38
39
  * @returns
39
40
  */
40
41
  findPaths(normalizedImportExpr: string): string[] | undefined;
42
+ findDependencyInWorkspaceProjects(dep: string): string | null;
41
43
  private resolveImportWithTypescript;
42
44
  private resolveImportWithRequire;
43
45
  private findProjectOfResolvedModule;
@@ -7,10 +7,11 @@ const node_path_1 = require("node:path");
7
7
  const semver_1 = require("semver");
8
8
  const find_project_for_path_1 = require("../../../../project-graph/utils/find-project-for-path");
9
9
  const fileutils_1 = require("../../../../utils/fileutils");
10
+ const get_package_name_from_import_path_1 = require("../../../../utils/get-package-name-from-import-path");
10
11
  const workspace_root_1 = require("../../../../utils/workspace-root");
12
+ const packages_1 = require("../../utils/packages");
11
13
  const resolve_relative_to_dir_1 = require("../../utils/resolve-relative-to-dir");
12
14
  const typescript_1 = require("../../utils/typescript");
13
- const get_package_name_from_import_path_1 = require("../../../../utils/get-package-name-from-import-path");
14
15
  /**
15
16
  * Use a shared cache to avoid repeated npm package resolution work within the TargetProjectLocator.
16
17
  */
@@ -98,6 +99,12 @@ class TargetProjectLocator {
98
99
  return this.findProjectOfResolvedModule(resolvedModule);
99
100
  }
100
101
  catch { }
102
+ // fall back to see if it's a locally linked workspace project where the
103
+ // output might not exist yet
104
+ const localProject = this.findDependencyInWorkspaceProjects(importExpr);
105
+ if (localProject) {
106
+ return localProject;
107
+ }
101
108
  // nothing found, cache for later
102
109
  this.npmResolutionCache.set(importExpr, null);
103
110
  return null;
@@ -182,6 +189,10 @@ class TargetProjectLocator {
182
189
  }
183
190
  return undefined;
184
191
  }
192
+ findDependencyInWorkspaceProjects(dep) {
193
+ this.packageEntryPointsToProjectMap ??= (0, packages_1.getPackageEntryPointsToProjectMap)(this.nodes);
194
+ return this.packageEntryPointsToProjectMap[dep]?.name ?? null;
195
+ }
185
196
  resolveImportWithTypescript(normalizedImportExpr, filePath) {
186
197
  let resolvedModule;
187
198
  if (this.typescriptResolutionCache.has(normalizedImportExpr)) {
@@ -0,0 +1,3 @@
1
+ import type { ProjectGraphProjectNode } from '../../../config/project-graph';
2
+ import type { ProjectConfiguration } from '../../../config/workspace-json-project-json';
3
+ export declare function getPackageEntryPointsToProjectMap<T extends ProjectGraphProjectNode | ProjectConfiguration>(projects: Record<string, T>): Record<string, T>;
@@ -0,0 +1,25 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getPackageEntryPointsToProjectMap = getPackageEntryPointsToProjectMap;
4
+ const posix_1 = require("node:path/posix");
5
+ function getPackageEntryPointsToProjectMap(projects) {
6
+ const result = {};
7
+ for (const project of Object.values(projects)) {
8
+ const metadata = 'data' in project ? project.data.metadata : project.metadata;
9
+ if (!metadata?.js) {
10
+ continue;
11
+ }
12
+ const { packageName, packageExports } = metadata.js;
13
+ if (!packageExports || typeof packageExports === 'string') {
14
+ // no `exports` or it points to a file, which would be the equivalent of
15
+ // an '.' export, in which case the package name is the entry point
16
+ result[packageName] = project;
17
+ }
18
+ else {
19
+ for (const entryPoint of Object.keys(packageExports)) {
20
+ result[(0, posix_1.join)(packageName, entryPoint)] = project;
21
+ }
22
+ }
23
+ }
24
+ return result;
25
+ }
@@ -84,6 +84,12 @@ function createNodeFromPackageJson(pkgJsonPath, workspaceRoot, cache) {
84
84
  const hash = (0, file_hasher_1.hashObject)({
85
85
  ...json,
86
86
  root: projectRoot,
87
+ /**
88
+ * Increment this number to force processing the package.json again. Do it
89
+ * when the implementation of this plugin is changed and results in different
90
+ * results for the same package.json contents.
91
+ */
92
+ bust: 1,
87
93
  });
88
94
  const cached = cache[hash];
89
95
  if (cached) {
@@ -18,6 +18,7 @@ const configuration_1 = require("../config/configuration");
18
18
  const fs_1 = require("fs");
19
19
  const error_types_1 = require("./error-types");
20
20
  const project_configuration_utils_1 = require("./utils/project-configuration-utils");
21
+ const delayed_spinner_1 = require("../utils/delayed-spinner");
21
22
  let storedFileMap = null;
22
23
  let storedAllWorkspaceFiles = null;
23
24
  let storedRustReferences = null;
@@ -186,11 +187,39 @@ async function updateProjectGraphWithPlugins(context, initProjectGraph, plugins,
186
187
  const builder = new project_graph_builder_1.ProjectGraphBuilder(graph, context.fileMap.projectFileMap, context.fileMap.nonProjectFiles);
187
188
  const createDependencyPlugins = plugins.filter((plugin) => plugin.createDependencies);
188
189
  perf_hooks_1.performance.mark('createDependencies:start');
190
+ let spinner;
191
+ const inProgressPlugins = new Set();
192
+ function updateSpinner() {
193
+ if (!spinner) {
194
+ return;
195
+ }
196
+ if (inProgressPlugins.size === 1) {
197
+ return `Creating project graph dependencies with ${inProgressPlugins.keys()[0]}`;
198
+ }
199
+ else if (process.env.NX_VERBOSE_LOGGING === 'true') {
200
+ return [
201
+ `Creating project graph dependencies with ${inProgressPlugins.size} plugins`,
202
+ ...Array.from(inProgressPlugins).map((p) => ` - ${p}`),
203
+ ].join('\n');
204
+ }
205
+ else {
206
+ return `Creating project graph dependencies with ${inProgressPlugins.size} plugins`;
207
+ }
208
+ }
209
+ if (delayed_spinner_1.SHOULD_SHOW_SPINNERS) {
210
+ spinner = new delayed_spinner_1.DelayedSpinner(`Creating project graph dependencies with ${plugins.length} plugins`);
211
+ }
189
212
  await Promise.all(createDependencyPlugins.map(async (plugin) => {
190
213
  perf_hooks_1.performance.mark(`${plugin.name}:createDependencies - start`);
214
+ inProgressPlugins.add(plugin.name);
191
215
  try {
192
- const dependencies = await plugin.createDependencies({
216
+ const dependencies = await plugin
217
+ .createDependencies({
193
218
  ...context,
219
+ })
220
+ .finally(() => {
221
+ inProgressPlugins.delete(plugin.name);
222
+ updateSpinner();
194
223
  });
195
224
  for (const dep of dependencies) {
196
225
  builder.addDependency(dep.source, dep.target, dep.type, 'sourceFile' in dep ? dep.sourceFile : null);
@@ -206,6 +235,7 @@ async function updateProjectGraphWithPlugins(context, initProjectGraph, plugins,
206
235
  }));
207
236
  perf_hooks_1.performance.mark('createDependencies:end');
208
237
  perf_hooks_1.performance.measure(`createDependencies`, `createDependencies:start`, `createDependencies:end`);
238
+ spinner?.cleanup();
209
239
  const graphWithDeps = builder.getUpdatedProjectGraph();
210
240
  const { errors: metadataErrors, graph: updatedGraph } = await applyProjectMetadata(graphWithDeps, plugins, {
211
241
  nxJsonConfiguration: context.nxJsonConfiguration,
@@ -232,9 +262,32 @@ async function applyProjectMetadata(graph, plugins, context, sourceMap) {
232
262
  const results = [];
233
263
  const errors = [];
234
264
  perf_hooks_1.performance.mark('createMetadata:start');
265
+ let spinner;
266
+ const inProgressPlugins = new Set();
267
+ function updateSpinner() {
268
+ if (!spinner) {
269
+ return;
270
+ }
271
+ if (inProgressPlugins.size === 1) {
272
+ return `Creating project metadata with ${inProgressPlugins.keys()[0]}`;
273
+ }
274
+ else if (process.env.NX_VERBOSE_LOGGING === 'true') {
275
+ return [
276
+ `Creating project metadata with ${inProgressPlugins.size} plugins`,
277
+ ...Array.from(inProgressPlugins).map((p) => ` - ${p}`),
278
+ ].join('\n');
279
+ }
280
+ else {
281
+ return `Creating project metadata with ${inProgressPlugins.size} plugins`;
282
+ }
283
+ }
284
+ if (delayed_spinner_1.SHOULD_SHOW_SPINNERS) {
285
+ spinner = new delayed_spinner_1.DelayedSpinner(`Creating project metadata with ${plugins.length} plugins`);
286
+ }
235
287
  const promises = plugins.map(async (plugin) => {
236
288
  if (plugin.createMetadata) {
237
289
  perf_hooks_1.performance.mark(`${plugin.name}:createMetadata - start`);
290
+ inProgressPlugins.add(plugin.name);
238
291
  try {
239
292
  const metadata = await plugin.createMetadata(graph, context);
240
293
  results.push({ metadata, pluginName: plugin.name });
@@ -243,12 +296,15 @@ async function applyProjectMetadata(graph, plugins, context, sourceMap) {
243
296
  errors.push(new error_types_1.CreateMetadataError(e, plugin.name));
244
297
  }
245
298
  finally {
299
+ inProgressPlugins.delete(plugin.name);
300
+ updateSpinner();
246
301
  perf_hooks_1.performance.mark(`${plugin.name}:createMetadata - end`);
247
302
  perf_hooks_1.performance.measure(`${plugin.name}:createMetadata`, `${plugin.name}:createMetadata - start`, `${plugin.name}:createMetadata - end`);
248
303
  }
249
304
  }
250
305
  });
251
306
  await Promise.all(promises);
307
+ spinner?.cleanup();
252
308
  for (const { metadata: projectsMetadata, pluginName } of results) {
253
309
  for (const project in projectsMetadata) {
254
310
  const projectConfiguration = graph.nodes[project]?.data;
@@ -1,14 +1,15 @@
1
1
  import { PluginConfiguration } from '../../config/nx-json';
2
- import { CreateDependencies, CreateDependenciesContext, CreateMetadata, CreateMetadataContext, CreateNodesContextV2, CreateNodesResult, NxPluginV2 } from './public-api';
2
+ import { CreateDependenciesContext, CreateMetadataContext, CreateNodesContextV2, CreateNodesResult, NxPluginV2, ProjectsMetadata } from './public-api';
3
3
  import { ProjectGraph } from '../../config/project-graph';
4
+ import { RawProjectGraphDependency } from '../project-graph-builder';
4
5
  export declare class LoadedNxPlugin {
5
6
  readonly name: string;
6
7
  readonly createNodes?: [
7
8
  filePattern: string,
8
9
  fn: (matchedFiles: string[], context: CreateNodesContextV2) => Promise<Array<readonly [plugin: string, file: string, result: CreateNodesResult]>>
9
10
  ];
10
- readonly createDependencies?: (context: CreateDependenciesContext) => ReturnType<CreateDependencies>;
11
- readonly createMetadata?: (graph: ProjectGraph, context: CreateMetadataContext) => ReturnType<CreateMetadata>;
11
+ readonly createDependencies?: (context: CreateDependenciesContext) => Promise<RawProjectGraphDependency[]>;
12
+ readonly createMetadata?: (graph: ProjectGraph, context: CreateMetadataContext) => Promise<ProjectsMetadata>;
12
13
  readonly options?: unknown;
13
14
  readonly include?: string[];
14
15
  readonly exclude?: string[];
@@ -57,10 +57,10 @@ class LoadedNxPlugin {
57
57
  };
58
58
  }
59
59
  if (plugin.createDependencies) {
60
- this.createDependencies = (context) => plugin.createDependencies(this.options, context);
60
+ this.createDependencies = async (context) => plugin.createDependencies(this.options, context);
61
61
  }
62
62
  if (plugin.createMetadata) {
63
- this.createMetadata = (graph, context) => plugin.createMetadata(graph, this.options, context);
63
+ this.createMetadata = async (graph, context) => plugin.createMetadata(graph, this.options, context);
64
64
  }
65
65
  }
66
66
  }
@@ -68,7 +68,7 @@ export interface PluginCreateMetadataMessage {
68
68
  export interface PluginCreateDependenciesResult {
69
69
  type: 'createDependenciesResult';
70
70
  payload: {
71
- dependencies: ReturnType<LoadedNxPlugin['createDependencies']>;
71
+ dependencies: Awaited<ReturnType<LoadedNxPlugin['createDependencies']>>;
72
72
  success: true;
73
73
  tx: string;
74
74
  } | {
@@ -80,7 +80,7 @@ export interface PluginCreateDependenciesResult {
80
80
  export interface PluginCreateMetadataResult {
81
81
  type: 'createMetadataResult';
82
82
  payload: {
83
- metadata: ReturnType<LoadedNxPlugin['createMetadata']>;
83
+ metadata: Awaited<ReturnType<LoadedNxPlugin['createMetadata']>>;
84
84
  success: true;
85
85
  tx: string;
86
86
  } | {
@@ -25,6 +25,7 @@ const error_types_1 = require("../error-types");
25
25
  const path = require("node:path/posix");
26
26
  const typescript_1 = require("../../plugins/js/utils/typescript");
27
27
  const load_resolved_plugin_1 = require("./load-resolved-plugin");
28
+ const packages_1 = require("../../plugins/js/utils/packages");
28
29
  function readPluginPackageJson(pluginName, projects, paths = (0, installation_directory_1.getNxRequirePaths)()) {
29
30
  try {
30
31
  const result = (0, package_json_1.readModulePackageJsonWithoutFallbacks)(pluginName, paths);
@@ -89,26 +90,32 @@ function lookupLocalPlugin(importPath, projects, root = workspace_root_1.workspa
89
90
  }
90
91
  return { path: path.join(root, projectConfig.root), projectConfig };
91
92
  }
93
+ let packageEntryPointsToProjectMap;
92
94
  function findNxProjectForImportPath(importPath, projects, root = workspace_root_1.workspaceRoot) {
93
95
  const tsConfigPaths = readTsConfigPaths(root);
94
- const possiblePaths = tsConfigPaths[importPath]?.map((p) => (0, path_1.normalizePath)(path.relative(root, path.join(root, p))));
95
- if (possiblePaths?.length) {
96
- const projectRootMappings = new Map();
96
+ const possibleTsPaths = tsConfigPaths[importPath]?.map((p) => (0, path_1.normalizePath)(path.relative(root, path.join(root, p)))) ?? [];
97
+ const projectRootMappings = new Map();
98
+ if (possibleTsPaths.length) {
97
99
  const projectNameMap = new Map();
98
100
  for (const projectRoot in projects) {
99
101
  const project = projects[projectRoot];
100
102
  projectRootMappings.set(project.root, project.name);
101
103
  projectNameMap.set(project.name, project);
102
104
  }
103
- for (const tsConfigPath of possiblePaths) {
105
+ for (const tsConfigPath of possibleTsPaths) {
104
106
  const nxProject = (0, find_project_for_path_1.findProjectForPath)(tsConfigPath, projectRootMappings);
105
107
  if (nxProject) {
106
108
  return projectNameMap.get(nxProject);
107
109
  }
108
110
  }
109
- logger_1.logger.verbose('Unable to find local plugin', possiblePaths, projectRootMappings);
110
- throw new Error('Unable to resolve local plugin with import path ' + importPath);
111
111
  }
112
+ packageEntryPointsToProjectMap ??=
113
+ (0, packages_1.getPackageEntryPointsToProjectMap)(projects);
114
+ if (packageEntryPointsToProjectMap[importPath]) {
115
+ return packageEntryPointsToProjectMap[importPath];
116
+ }
117
+ logger_1.logger.verbose('Unable to find local plugin', possibleTsPaths, projectRootMappings);
118
+ throw new Error('Unable to resolve local plugin with import path ' + importPath);
112
119
  }
113
120
  let tsconfigPaths;
114
121
  function readTsConfigPaths(root = workspace_root_1.workspaceRoot) {
@@ -20,6 +20,7 @@ const path_1 = require("path");
20
20
  const perf_hooks_1 = require("perf_hooks");
21
21
  const error_types_1 = require("../error-types");
22
22
  const globs_1 = require("../../utils/globs");
23
+ const delayed_spinner_1 = require("../../utils/delayed-spinner");
23
24
  function mergeProjectConfigurationIntoRootMap(projectRootMap, project, configurationSourceMaps, sourceInformation,
24
25
  // This function is used when reading project configuration
25
26
  // in generators, where we don't want to do this.
@@ -224,6 +225,28 @@ function mergeMetadata(sourceMap, sourceInformation, baseSourceMapPath, metadata
224
225
  async function createProjectConfigurations(root = workspace_root_1.workspaceRoot, nxJson, projectFiles, // making this parameter allows devkit to pick up newly created projects
225
226
  plugins) {
226
227
  perf_hooks_1.performance.mark('build-project-configs:start');
228
+ let spinner;
229
+ const inProgressPlugins = new Set();
230
+ function updateSpinner() {
231
+ if (!spinner) {
232
+ return;
233
+ }
234
+ if (inProgressPlugins.size === 1) {
235
+ return `Creating project graph nodes with ${inProgressPlugins.keys()[0]}`;
236
+ }
237
+ else if (process.env.NX_VERBOSE_LOGGING === 'true') {
238
+ return [
239
+ `Creating project graph nodes with ${inProgressPlugins.size} plugins`,
240
+ ...Array.from(inProgressPlugins).map((p) => ` - ${p}`),
241
+ ].join('\n');
242
+ }
243
+ else {
244
+ return `Creating project graph nodes with ${inProgressPlugins.size} plugins`;
245
+ }
246
+ }
247
+ if (delayed_spinner_1.SHOULD_SHOW_SPINNERS) {
248
+ spinner = new delayed_spinner_1.DelayedSpinner(`Creating project graph nodes with ${plugins.length} plugins`);
249
+ }
227
250
  const results = [];
228
251
  const errors = [];
229
252
  // We iterate over plugins first - this ensures that plugins specified first take precedence.
@@ -233,10 +256,12 @@ plugins) {
233
256
  continue;
234
257
  }
235
258
  const matchingConfigFiles = findMatchingConfigFiles(projectFiles, pattern, include, exclude);
259
+ inProgressPlugins.add(pluginName);
236
260
  let r = createNodes(matchingConfigFiles, {
237
261
  nxJsonConfiguration: nxJson,
238
262
  workspaceRoot: root,
239
- }).catch((e) => {
263
+ })
264
+ .catch((e) => {
240
265
  const errorBodyLines = [
241
266
  `An error occurred while processing files for the ${pluginName} plugin.`,
242
267
  ];
@@ -263,10 +288,15 @@ plugins) {
263
288
  errors.push(error);
264
289
  // The plugin didn't return partial results, so we return an empty array.
265
290
  return error.partialResults.map((r) => [pluginName, r[0], r[1]]);
291
+ })
292
+ .finally(() => {
293
+ inProgressPlugins.delete(pluginName);
294
+ updateSpinner();
266
295
  });
267
296
  results.push(r);
268
297
  }
269
298
  return Promise.all(results).then((results) => {
299
+ spinner?.cleanup();
270
300
  const { projectRootMap, externalNodes, rootMap, configurationSourceMaps } = mergeCreateNodesResults(results, nxJson, errors);
271
301
  perf_hooks_1.performance.mark('build-project-configs:end');
272
302
  perf_hooks_1.performance.measure('build-project-configs', 'build-project-configs:start', 'build-project-configs:end');