nx 20.4.0-rc.0 → 20.4.1

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 (41) hide show
  1. package/package.json +11 -11
  2. package/plugins/package-json.js +2 -1
  3. package/schemas/nx-schema.json +7 -0
  4. package/src/command-line/migrate/migrate.js +21 -18
  5. package/src/config/schema-utils.js +4 -4
  6. package/src/config/workspace-json-project-json.d.ts +2 -0
  7. package/src/daemon/server/project-graph-incremental-recomputation.js +1 -1
  8. package/src/devkit-exports.d.ts +1 -1
  9. package/src/devkit-exports.js +2 -1
  10. package/src/executors/run-commands/run-commands.impl.d.ts +1 -0
  11. package/src/executors/run-commands/run-commands.impl.js +10 -3
  12. package/src/executors/run-commands/schema.json +14 -0
  13. package/src/native/index.d.ts +9 -0
  14. package/src/native/native-bindings.js +1 -0
  15. package/src/native/nx.wasi-browser.js +11 -8
  16. package/src/native/nx.wasi.cjs +11 -8
  17. package/src/native/nx.wasm32-wasi.wasm +0 -0
  18. package/src/native/tests/__fixtures__/file-lock.fixture.js +20 -0
  19. package/src/plugins/js/project-graph/build-dependencies/target-project-locator.d.ts +2 -1
  20. package/src/plugins/js/project-graph/build-dependencies/target-project-locator.js +11 -3
  21. package/src/plugins/js/utils/packages.d.ts +6 -1
  22. package/src/plugins/js/utils/packages.js +59 -12
  23. package/src/plugins/js/utils/register.d.ts +1 -1
  24. package/src/plugins/js/utils/register.js +1 -1
  25. package/src/plugins/js/utils/typescript.d.ts +1 -0
  26. package/src/plugins/js/utils/typescript.js +1 -0
  27. package/src/plugins/package-json/create-nodes.d.ts +3 -3
  28. package/src/plugins/package-json/create-nodes.js +8 -12
  29. package/src/project-graph/error-types.d.ts +6 -2
  30. package/src/project-graph/error-types.js +7 -1
  31. package/src/project-graph/file-utils.js +2 -1
  32. package/src/project-graph/nx-deps-cache.d.ts +6 -2
  33. package/src/project-graph/nx-deps-cache.js +66 -10
  34. package/src/project-graph/plugins/loaded-nx-plugin.js +6 -10
  35. package/src/project-graph/plugins/resolve-plugin.js +11 -2
  36. package/src/project-graph/plugins/transpiler.js +4 -4
  37. package/src/project-graph/project-graph.d.ts +3 -1
  38. package/src/project-graph/project-graph.js +75 -14
  39. package/src/tasks-runner/cache.js +3 -2
  40. package/src/utils/package-json.d.ts +1 -1
  41. package/src/utils/package-json.js +4 -2
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nx",
3
- "version": "20.4.0-rc.0",
3
+ "version": "20.4.1",
4
4
  "private": false,
5
5
  "description": "The core Nx plugin contains the core functionality of Nx like the project graph, nx commands and task orchestration.",
6
6
  "repository": {
@@ -82,16 +82,16 @@
82
82
  }
83
83
  },
84
84
  "optionalDependencies": {
85
- "@nx/nx-darwin-arm64": "20.4.0-rc.0",
86
- "@nx/nx-darwin-x64": "20.4.0-rc.0",
87
- "@nx/nx-freebsd-x64": "20.4.0-rc.0",
88
- "@nx/nx-linux-arm-gnueabihf": "20.4.0-rc.0",
89
- "@nx/nx-linux-arm64-gnu": "20.4.0-rc.0",
90
- "@nx/nx-linux-arm64-musl": "20.4.0-rc.0",
91
- "@nx/nx-linux-x64-gnu": "20.4.0-rc.0",
92
- "@nx/nx-linux-x64-musl": "20.4.0-rc.0",
93
- "@nx/nx-win32-arm64-msvc": "20.4.0-rc.0",
94
- "@nx/nx-win32-x64-msvc": "20.4.0-rc.0"
85
+ "@nx/nx-darwin-arm64": "20.4.1",
86
+ "@nx/nx-darwin-x64": "20.4.1",
87
+ "@nx/nx-freebsd-x64": "20.4.1",
88
+ "@nx/nx-linux-arm-gnueabihf": "20.4.1",
89
+ "@nx/nx-linux-arm64-gnu": "20.4.1",
90
+ "@nx/nx-linux-arm64-musl": "20.4.1",
91
+ "@nx/nx-linux-x64-gnu": "20.4.1",
92
+ "@nx/nx-linux-x64-musl": "20.4.1",
93
+ "@nx/nx-win32-arm64-msvc": "20.4.1",
94
+ "@nx/nx-win32-x64-msvc": "20.4.1"
95
95
  },
96
96
  "nx-migrations": {
97
97
  "migrations": "./migrations.json",
@@ -25,7 +25,8 @@ const plugin = {
25
25
  '*/**/package.json',
26
26
  (configFiles, options, context) => {
27
27
  const cache = readPackageJsonConfigurationCache();
28
- const result = (0, plugins_1.createNodesFromFiles)((f) => (0, package_json_1.createNodeFromPackageJson)(f, workspace_root_1.workspaceRoot, cache), configFiles, options, context);
28
+ const isInPackageJsonWorkspaces = (0, package_json_1.buildPackageJsonWorkspacesMatcher)(context.workspaceRoot, (f) => (0, fileutils_1.readJsonFile)((0, path_1.join)(context.workspaceRoot, f)));
29
+ const result = (0, plugins_1.createNodesFromFiles)((packageJsonPath) => (0, package_json_1.createNodeFromPackageJson)(packageJsonPath, workspace_root_1.workspaceRoot, cache, isInPackageJsonWorkspaces(packageJsonPath)), configFiles, options, context);
29
30
  writeCache(cache);
30
31
  return result;
31
32
  },
@@ -569,6 +569,13 @@
569
569
  "cache": {
570
570
  "type": "boolean",
571
571
  "description": "Specifies if the given target should be cacheable"
572
+ },
573
+ "syncGenerators": {
574
+ "type": "array",
575
+ "items": {
576
+ "type": "string"
577
+ },
578
+ "description": "List of generators to run before the target to ensure the workspace is up to date"
572
579
  }
573
580
  },
574
581
  "additionalProperties": false
@@ -118,10 +118,10 @@ class Migrator {
118
118
  const packagesToCheck = await this.populatePackageJsonUpdatesAndGetPackagesToCheck(targetPackage, target);
119
119
  for (const packageToCheck of packagesToCheck) {
120
120
  const filteredUpdates = {};
121
- for (const packageUpdate of packageToCheck.updates) {
121
+ for (const [packageUpdateKey, packageUpdate] of Object.entries(packageToCheck.updates)) {
122
122
  if (this.areRequirementsMet(packageUpdate.requires) &&
123
123
  (!this.interactive ||
124
- (await this.runPackageJsonUpdatesConfirmationPrompt(packageUpdate)))) {
124
+ (await this.runPackageJsonUpdatesConfirmationPrompt(packageUpdate, packageUpdateKey, packageToCheck.package)))) {
125
125
  Object.entries(packageUpdate.packages).forEach(([name, update]) => {
126
126
  filteredUpdates[name] = update;
127
127
  this.packageUpdates[name] = update;
@@ -166,15 +166,15 @@ class Migrator {
166
166
  addToPackageJson: target.addToPackageJson || false,
167
167
  });
168
168
  const { packageJsonUpdates, packageGroupOrder } = this.getPackageJsonUpdatesFromMigrationConfig(targetPackage, targetVersion, migrationConfig);
169
- if (!packageJsonUpdates.length) {
169
+ if (!Object.keys(packageJsonUpdates).length) {
170
170
  return [];
171
171
  }
172
- const shouldCheckUpdates = packageJsonUpdates.some((packageJsonUpdate) => (this.interactive && packageJsonUpdate['x-prompt']) ||
172
+ const shouldCheckUpdates = Object.values(packageJsonUpdates).some((packageJsonUpdate) => (this.interactive && packageJsonUpdate['x-prompt']) ||
173
173
  Object.keys(packageJsonUpdate.requires ?? {}).length);
174
174
  if (shouldCheckUpdates) {
175
175
  return [{ package: targetPackage, updates: packageJsonUpdates }];
176
176
  }
177
- const packageUpdatesToApply = packageJsonUpdates.reduce((m, c) => ({ ...m, ...c.packages }), {});
177
+ const packageUpdatesToApply = Object.values(packageJsonUpdates).reduce((m, c) => ({ ...m, ...c.packages }), {});
178
178
  return (await Promise.all(Object.entries(packageUpdatesToApply).map(([packageName, packageUpdate]) => this.populatePackageJsonUpdatesAndGetPackagesToCheck(packageName, packageUpdate))))
179
179
  .filter((pkgs) => pkgs.length)
180
180
  .flat()
@@ -185,7 +185,7 @@ class Migrator {
185
185
  const packageGroupOrder = this.getPackageJsonUpdatesFromPackageGroup(packageName, targetVersion, migrationConfig);
186
186
  if (!migrationConfig.packageJsonUpdates ||
187
187
  !this.getPkgVersion(packageName)) {
188
- return { packageJsonUpdates: [], packageGroupOrder };
188
+ return { packageJsonUpdates: {}, packageGroupOrder };
189
189
  }
190
190
  const packageJsonUpdates = this.filterPackageJsonUpdates(migrationConfig.packageJsonUpdates, packageName, targetVersion);
191
191
  return { packageJsonUpdates, packageGroupOrder };
@@ -228,8 +228,8 @@ class Migrator {
228
228
  return packageGroupOrder;
229
229
  }
230
230
  filterPackageJsonUpdates(packageJsonUpdates, packageName, targetVersion) {
231
- const filteredPackageJsonUpdates = [];
232
- for (const packageJsonUpdate of Object.values(packageJsonUpdates)) {
231
+ const filteredPackageJsonUpdates = {};
232
+ for (const [packageJsonUpdateKey, packageJsonUpdate] of Object.entries(packageJsonUpdates)) {
233
233
  if (!packageJsonUpdate.packages ||
234
234
  this.lt(packageJsonUpdate.version, this.getPkgVersion(packageName)) ||
235
235
  this.gt(packageJsonUpdate.version, targetVersion)) {
@@ -254,7 +254,7 @@ class Migrator {
254
254
  }
255
255
  if (Object.keys(filtered).length) {
256
256
  packageJsonUpdate.packages = filtered;
257
- filteredPackageJsonUpdates.push(packageJsonUpdate);
257
+ filteredPackageJsonUpdates[packageJsonUpdateKey] = packageJsonUpdate;
258
258
  }
259
259
  }
260
260
  return filteredPackageJsonUpdates;
@@ -313,7 +313,7 @@ class Migrator {
313
313
  includePrerelease: true,
314
314
  }));
315
315
  }
316
- async runPackageJsonUpdatesConfirmationPrompt(packageUpdate) {
316
+ async runPackageJsonUpdatesConfirmationPrompt(packageUpdate, packageUpdateKey, packageName) {
317
317
  if (!packageUpdate['x-prompt']) {
318
318
  return Promise.resolve(true);
319
319
  }
@@ -322,14 +322,17 @@ class Migrator {
322
322
  // a same prompt was already answered, skip
323
323
  return Promise.resolve(false);
324
324
  }
325
- return await (0, enquirer_1.prompt)([
326
- {
327
- name: 'shouldApply',
328
- type: 'confirm',
329
- message: packageUpdate['x-prompt'],
330
- initial: true,
331
- },
332
- ]).then(({ shouldApply }) => {
325
+ const promptConfig = {
326
+ name: 'shouldApply',
327
+ type: 'confirm',
328
+ message: packageUpdate['x-prompt'],
329
+ initial: true,
330
+ };
331
+ if (packageName.startsWith('@nx/')) {
332
+ // @ts-expect-error -- enquirer types aren't correct, footer does exist
333
+ promptConfig.footer = () => chalk.dim(` View migration details at https://nx.dev/nx-api/${packageName.replace('@nx/', '')}#${packageUpdateKey.replace(/[-\.]/g, '')}packageupdates`);
334
+ }
335
+ return await (0, enquirer_1.prompt)([promptConfig]).then(({ shouldApply }) => {
333
336
  this.promptAnswers[promptKey] = shouldApply;
334
337
  if (!shouldApply &&
335
338
  (!this.minVersionWithSkippedUpdates ||
@@ -79,11 +79,11 @@ function resolveSchema(schemaPath, directory, packageName, projects) {
79
79
  paths: [directory],
80
80
  });
81
81
  }
82
- let packageEntryPointsToProjectMap;
82
+ let packageToProjectMap;
83
83
  function tryResolveFromSource(path, directory, packageName, projects) {
84
- packageEntryPointsToProjectMap ??=
85
- (0, packages_1.getPackageEntryPointsToProjectMap)(projects);
86
- const localProject = packageEntryPointsToProjectMap[packageName];
84
+ packageToProjectMap ??=
85
+ (0, packages_1.getWorkspacePackagesMetadata)(projects).packageToProjectMap;
86
+ const localProject = packageToProjectMap[packageName];
87
87
  if (!localProject) {
88
88
  // it doesn't match any of the package names from the local projects
89
89
  return null;
@@ -126,6 +126,8 @@ export interface ProjectMetadata {
126
126
  js?: {
127
127
  packageName: string;
128
128
  packageExports?: PackageJson['exports'];
129
+ packageMain?: string;
130
+ isInPackageManagerWorkspaces?: boolean;
129
131
  };
130
132
  }
131
133
  export interface TargetMetadata {
@@ -189,6 +189,7 @@ async function processFilesAndCreateAndSerializeProjectGraph(plugins) {
189
189
  };
190
190
  }
191
191
  }
192
+ (0, nx_deps_cache_1.writeCache)(g.projectFileMapCache, g.projectGraph, projectConfigurationsResult.sourceMaps, errors);
192
193
  if (errors.length > 0) {
193
194
  return {
194
195
  error: new error_types_1.DaemonProjectGraphError(errors, g.projectGraph, projectConfigurationsResult.sourceMaps),
@@ -202,7 +203,6 @@ async function processFilesAndCreateAndSerializeProjectGraph(plugins) {
202
203
  };
203
204
  }
204
205
  else {
205
- (0, nx_deps_cache_1.writeCache)(g.projectFileMapCache, g.projectGraph);
206
206
  return g;
207
207
  }
208
208
  }
@@ -16,7 +16,7 @@ export type { WorkspaceJsonConfiguration, ProjectsConfigurations, TargetDependen
16
16
  export type { Generator, GeneratorCallback, PromiseExecutor, AsyncIteratorExecutor, Executor, ExecutorContext, TaskGraphExecutor, GeneratorsJson, ExecutorsJson, MigrationsJson, CustomHasher, HasherContext, } from './config/misc-interfaces';
17
17
  export { workspaceLayout } from './config/configuration';
18
18
  export type { NxPlugin, NxPluginV2, CreateNodes, CreateNodesFunction, CreateNodesResult, CreateNodesContext, CreateNodesContextV2, CreateNodesFunctionV2, CreateNodesResultV2, CreateNodesV2, CreateDependencies, CreateDependenciesContext, CreateMetadata, CreateMetadataContext, ProjectsMetadata, PreTasksExecution, PreTasksExecutionContext, PostTasksExecution, PostTasksExecutionContext, } from './project-graph/plugins';
19
- export { AggregateCreateNodesError } from './project-graph/error-types';
19
+ export { AggregateCreateNodesError, StaleProjectGraphCacheError, } from './project-graph/error-types';
20
20
  export { createNodesFromFiles } from './project-graph/plugins';
21
21
  /**
22
22
  * @category Tasks
@@ -4,11 +4,12 @@
4
4
  * Try hard to not add to this API to reduce the surface area we need to maintain.
5
5
  */
6
6
  Object.defineProperty(exports, "__esModule", { value: true });
7
- exports.isDaemonEnabled = exports.createProjectFileMapUsingProjectGraph = exports.cacheDir = exports.hashArray = exports.defaultTasksRunner = exports.getOutputsForTargetAndConfiguration = exports.readProjectsConfigurationFromProjectGraph = exports.readCachedProjectGraph = exports.createProjectGraphAsync = exports.reverse = exports.workspaceRoot = exports.normalizePath = exports.joinPathFragments = exports.stripIndents = exports.writeJsonFile = exports.readJsonFile = exports.stripJsonComments = exports.serializeJson = exports.parseJson = exports.updateJson = exports.writeJson = exports.readJson = exports.validateDependency = exports.DependencyType = exports.updateNxJson = exports.readNxJson = exports.globAsync = exports.glob = exports.getProjects = exports.updateProjectConfiguration = exports.removeProjectConfiguration = exports.readProjectConfiguration = exports.addProjectConfiguration = exports.runExecutor = exports.isWorkspacesEnabled = exports.getPackageManagerVersion = exports.detectPackageManager = exports.getPackageManagerCommand = exports.output = exports.logger = exports.createNodesFromFiles = exports.AggregateCreateNodesError = exports.workspaceLayout = void 0;
7
+ exports.isDaemonEnabled = exports.createProjectFileMapUsingProjectGraph = exports.cacheDir = exports.hashArray = exports.defaultTasksRunner = exports.getOutputsForTargetAndConfiguration = exports.readProjectsConfigurationFromProjectGraph = exports.readCachedProjectGraph = exports.createProjectGraphAsync = exports.reverse = exports.workspaceRoot = exports.normalizePath = exports.joinPathFragments = exports.stripIndents = exports.writeJsonFile = exports.readJsonFile = exports.stripJsonComments = exports.serializeJson = exports.parseJson = exports.updateJson = exports.writeJson = exports.readJson = exports.validateDependency = exports.DependencyType = exports.updateNxJson = exports.readNxJson = exports.globAsync = exports.glob = exports.getProjects = exports.updateProjectConfiguration = exports.removeProjectConfiguration = exports.readProjectConfiguration = exports.addProjectConfiguration = exports.runExecutor = exports.isWorkspacesEnabled = exports.getPackageManagerVersion = exports.detectPackageManager = exports.getPackageManagerCommand = exports.output = exports.logger = exports.createNodesFromFiles = exports.StaleProjectGraphCacheError = exports.AggregateCreateNodesError = exports.workspaceLayout = void 0;
8
8
  var configuration_1 = require("./config/configuration");
9
9
  Object.defineProperty(exports, "workspaceLayout", { enumerable: true, get: function () { return configuration_1.workspaceLayout; } });
10
10
  var error_types_1 = require("./project-graph/error-types");
11
11
  Object.defineProperty(exports, "AggregateCreateNodesError", { enumerable: true, get: function () { return error_types_1.AggregateCreateNodesError; } });
12
+ Object.defineProperty(exports, "StaleProjectGraphCacheError", { enumerable: true, get: function () { return error_types_1.StaleProjectGraphCacheError; } });
12
13
  var plugins_1 = require("./project-graph/plugins");
13
14
  Object.defineProperty(exports, "createNodesFromFiles", { enumerable: true, get: function () { return plugins_1.createNodesFromFiles; } });
14
15
  /**
@@ -14,6 +14,7 @@ export interface RunCommandsOptions extends Json {
14
14
  */
15
15
  description?: string;
16
16
  prefix?: string;
17
+ prefixColor?: string;
17
18
  color?: string;
18
19
  bgColor?: string;
19
20
  } | string)[];
@@ -46,9 +46,9 @@ async function default_1(options, context) {
46
46
  if (normalized.readyWhenStatus.length && !normalized.parallel) {
47
47
  throw new Error('ERROR: Bad executor config for run-commands - "readyWhen" can only be used when "parallel=true".');
48
48
  }
49
- if (options.commands.find((c) => c.prefix || c.color || c.bgColor) &&
49
+ if (options.commands.find((c) => c.prefix || c.prefixColor || c.color || c.bgColor) &&
50
50
  !options.parallel) {
51
- throw new Error('ERROR: Bad executor config for run-commands - "prefix", "color" and "bgColor" can only be set when "parallel=true".');
51
+ throw new Error('ERROR: Bad executor config for run-commands - "prefix", "prefixColor", "color" and "bgColor" can only be set when "parallel=true".');
52
52
  }
53
53
  try {
54
54
  const result = options.parallel
@@ -263,7 +263,14 @@ function addColorAndPrefix(out, config) {
263
263
  if (config.prefix) {
264
264
  out = out
265
265
  .split('\n')
266
- .map((l) => l.trim().length > 0 ? `${chalk.bold(config.prefix)} ${l}` : l)
266
+ .map((l) => {
267
+ let prefixText = config.prefix;
268
+ if (config.prefixColor && chalk[config.prefixColor]) {
269
+ prefixText = chalk[config.prefixColor](prefixText);
270
+ }
271
+ prefixText = chalk.bold(prefixText);
272
+ return l.trim().length > 0 ? `${prefixText} ${l}` : l;
273
+ })
267
274
  .join('\n');
268
275
  }
269
276
  if (config.color && chalk[config.color]) {
@@ -40,6 +40,20 @@
40
40
  "type": "string",
41
41
  "description": "Prefix in front of every line out of the output"
42
42
  },
43
+ "prefixColor": {
44
+ "type": "string",
45
+ "description": "Color of the prefix",
46
+ "enum": [
47
+ "black",
48
+ "red",
49
+ "green",
50
+ "yellow",
51
+ "blue",
52
+ "magenta",
53
+ "cyan",
54
+ "white"
55
+ ]
56
+ },
43
57
  "color": {
44
58
  "type": "string",
45
59
  "description": "Color of the output",
@@ -13,6 +13,15 @@ export declare class ChildProcess {
13
13
  onOutput(callback: (message: string) => void): void
14
14
  }
15
15
 
16
+ export declare class FileLock {
17
+ locked: boolean
18
+ constructor(lockFilePath: string)
19
+ unlock(): void
20
+ check(): boolean
21
+ wait(): Promise<void>
22
+ lock(): void
23
+ }
24
+
16
25
  export declare class HashPlanner {
17
26
  constructor(nxJson: NxJson, projectGraph: ExternalObject<ProjectGraph>)
18
27
  getPlans(taskIds: Array<string>, taskGraph: TaskGraph): Record<string, string[]>
@@ -362,6 +362,7 @@ if (!nativeBinding) {
362
362
  }
363
363
 
364
364
  module.exports.ChildProcess = nativeBinding.ChildProcess
365
+ module.exports.FileLock = nativeBinding.FileLock
365
366
  module.exports.HashPlanner = nativeBinding.HashPlanner
366
367
  module.exports.ImportResult = nativeBinding.ImportResult
367
368
  module.exports.NxCache = nativeBinding.NxCache
@@ -85,15 +85,18 @@ function __napi_rs_initialize_modules(__napiInstance) {
85
85
  __napiInstance.exports['__napi_register__ExternalDependenciesInput_struct_36']?.()
86
86
  __napiInstance.exports['__napi_register__DepsOutputsInput_struct_37']?.()
87
87
  __napiInstance.exports['__napi_register__NxJson_struct_38']?.()
88
- __napiInstance.exports['__napi_register__WorkspaceContext_struct_39']?.()
89
- __napiInstance.exports['__napi_register__WorkspaceContext_impl_48']?.()
90
- __napiInstance.exports['__napi_register__WorkspaceErrors_49']?.()
91
- __napiInstance.exports['__napi_register__NxWorkspaceFiles_struct_50']?.()
92
- __napiInstance.exports['__napi_register__NxWorkspaceFilesExternals_struct_51']?.()
93
- __napiInstance.exports['__napi_register__UpdatedWorkspaceFiles_struct_52']?.()
94
- __napiInstance.exports['__napi_register__FileMap_struct_53']?.()
95
- __napiInstance.exports['__napi_register____test_only_transfer_file_map_54']?.()
88
+ __napiInstance.exports['__napi_register__FileLock_struct_39']?.()
89
+ __napiInstance.exports['__napi_register__FileLock_impl_41']?.()
90
+ __napiInstance.exports['__napi_register__WorkspaceContext_struct_42']?.()
91
+ __napiInstance.exports['__napi_register__WorkspaceContext_impl_51']?.()
92
+ __napiInstance.exports['__napi_register__WorkspaceErrors_52']?.()
93
+ __napiInstance.exports['__napi_register__NxWorkspaceFiles_struct_53']?.()
94
+ __napiInstance.exports['__napi_register__NxWorkspaceFilesExternals_struct_54']?.()
95
+ __napiInstance.exports['__napi_register__UpdatedWorkspaceFiles_struct_55']?.()
96
+ __napiInstance.exports['__napi_register__FileMap_struct_56']?.()
97
+ __napiInstance.exports['__napi_register____test_only_transfer_file_map_57']?.()
96
98
  }
99
+ export const FileLock = __napiModule.exports.FileLock
97
100
  export const HashPlanner = __napiModule.exports.HashPlanner
98
101
  export const ImportResult = __napiModule.exports.ImportResult
99
102
  export const TaskHasher = __napiModule.exports.TaskHasher
@@ -116,15 +116,18 @@ function __napi_rs_initialize_modules(__napiInstance) {
116
116
  __napiInstance.exports['__napi_register__ExternalDependenciesInput_struct_36']?.()
117
117
  __napiInstance.exports['__napi_register__DepsOutputsInput_struct_37']?.()
118
118
  __napiInstance.exports['__napi_register__NxJson_struct_38']?.()
119
- __napiInstance.exports['__napi_register__WorkspaceContext_struct_39']?.()
120
- __napiInstance.exports['__napi_register__WorkspaceContext_impl_48']?.()
121
- __napiInstance.exports['__napi_register__WorkspaceErrors_49']?.()
122
- __napiInstance.exports['__napi_register__NxWorkspaceFiles_struct_50']?.()
123
- __napiInstance.exports['__napi_register__NxWorkspaceFilesExternals_struct_51']?.()
124
- __napiInstance.exports['__napi_register__UpdatedWorkspaceFiles_struct_52']?.()
125
- __napiInstance.exports['__napi_register__FileMap_struct_53']?.()
126
- __napiInstance.exports['__napi_register____test_only_transfer_file_map_54']?.()
119
+ __napiInstance.exports['__napi_register__FileLock_struct_39']?.()
120
+ __napiInstance.exports['__napi_register__FileLock_impl_41']?.()
121
+ __napiInstance.exports['__napi_register__WorkspaceContext_struct_42']?.()
122
+ __napiInstance.exports['__napi_register__WorkspaceContext_impl_51']?.()
123
+ __napiInstance.exports['__napi_register__WorkspaceErrors_52']?.()
124
+ __napiInstance.exports['__napi_register__NxWorkspaceFiles_struct_53']?.()
125
+ __napiInstance.exports['__napi_register__NxWorkspaceFilesExternals_struct_54']?.()
126
+ __napiInstance.exports['__napi_register__UpdatedWorkspaceFiles_struct_55']?.()
127
+ __napiInstance.exports['__napi_register__FileMap_struct_56']?.()
128
+ __napiInstance.exports['__napi_register____test_only_transfer_file_map_57']?.()
127
129
  }
130
+ module.exports.FileLock = __napiModule.exports.FileLock
128
131
  module.exports.HashPlanner = __napiModule.exports.HashPlanner
129
132
  module.exports.ImportResult = __napiModule.exports.ImportResult
130
133
  module.exports.TaskHasher = __napiModule.exports.TaskHasher
Binary file
@@ -0,0 +1,20 @@
1
+ const { FileLock } = require('../../native-bindings.js');
2
+ const ora = require('ora');
3
+ const tmp = require('os').tmpdir();
4
+
5
+ (async () => {
6
+ const lock = new FileLock(
7
+ require('path').join(tmp, 'nx-unit-tests', 'file-lock-fixture')
8
+ );
9
+ if (lock.locked) {
10
+ const s = ora('Waiting for lock').start();
11
+ await lock.wait();
12
+ s.stop();
13
+ console.log('waited for lock');
14
+ } else {
15
+ await lock.lock();
16
+ await new Promise((resolve) => setTimeout(resolve, 5000));
17
+ console.log('ran with lock');
18
+ await lock.unlock();
19
+ }
20
+ })();
@@ -15,7 +15,7 @@ export declare class TargetProjectLocator {
15
15
  private tsConfig;
16
16
  private paths;
17
17
  private typescriptResolutionCache;
18
- private packageEntryPointsToProjectMap;
18
+ private packagesMetadata;
19
19
  constructor(nodes: Record<string, ProjectGraphProjectNode>, externalNodes?: Record<string, ProjectGraphExternalNode>, npmResolutionCache?: NpmResolutionCache);
20
20
  /**
21
21
  * Resolve any workspace or external project that matches the given import expression,
@@ -39,6 +39,7 @@ export declare class TargetProjectLocator {
39
39
  * @returns
40
40
  */
41
41
  findPaths(normalizedImportExpr: string): string[] | undefined;
42
+ findImportInWorkspaceProjects(importPath: string): string | null;
42
43
  findDependencyInWorkspaceProjects(dep: string): string | null;
43
44
  private resolveImportWithTypescript;
44
45
  private resolveImportWithRequire;
@@ -101,7 +101,7 @@ class TargetProjectLocator {
101
101
  catch { }
102
102
  // fall back to see if it's a locally linked workspace project where the
103
103
  // output might not exist yet
104
- const localProject = this.findDependencyInWorkspaceProjects(importExpr);
104
+ const localProject = this.findImportInWorkspaceProjects(importExpr);
105
105
  if (localProject) {
106
106
  return localProject;
107
107
  }
@@ -189,9 +189,17 @@ class TargetProjectLocator {
189
189
  }
190
190
  return undefined;
191
191
  }
192
+ findImportInWorkspaceProjects(importPath) {
193
+ this.packagesMetadata ??= (0, packages_1.getWorkspacePackagesMetadata)(this.nodes);
194
+ if (this.packagesMetadata.entryPointsToProjectMap[importPath]) {
195
+ return this.packagesMetadata.entryPointsToProjectMap[importPath].name;
196
+ }
197
+ const project = (0, packages_1.matchImportToWildcardEntryPointsToProjectMap)(this.packagesMetadata.wildcardEntryPointsToProjectMap, importPath);
198
+ return project?.name;
199
+ }
192
200
  findDependencyInWorkspaceProjects(dep) {
193
- this.packageEntryPointsToProjectMap ??= (0, packages_1.getPackageEntryPointsToProjectMap)(this.nodes);
194
- return this.packageEntryPointsToProjectMap[dep]?.name ?? null;
201
+ this.packagesMetadata ??= (0, packages_1.getWorkspacePackagesMetadata)(this.nodes);
202
+ return this.packagesMetadata.packageToProjectMap[dep]?.name;
195
203
  }
196
204
  resolveImportWithTypescript(normalizedImportExpr, filePath) {
197
205
  let resolvedModule;
@@ -1,3 +1,8 @@
1
1
  import type { ProjectGraphProjectNode } from '../../../config/project-graph';
2
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>;
3
+ export declare function getWorkspacePackagesMetadata<T extends ProjectGraphProjectNode | ProjectConfiguration>(projects: Record<string, T>): {
4
+ entryPointsToProjectMap: Record<string, T>;
5
+ wildcardEntryPointsToProjectMap: Record<string, T>;
6
+ packageToProjectMap: Record<string, T>;
7
+ };
8
+ export declare function matchImportToWildcardEntryPointsToProjectMap<T extends ProjectGraphProjectNode | ProjectConfiguration>(wildcardEntryPointsToProjectMap: Record<string, T>, importPath: string): T | null;
@@ -1,25 +1,72 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.getPackageEntryPointsToProjectMap = getPackageEntryPointsToProjectMap;
3
+ exports.getWorkspacePackagesMetadata = getWorkspacePackagesMetadata;
4
+ exports.matchImportToWildcardEntryPointsToProjectMap = matchImportToWildcardEntryPointsToProjectMap;
5
+ const minimatch_1 = require("minimatch");
4
6
  const posix_1 = require("node:path/posix");
5
- function getPackageEntryPointsToProjectMap(projects) {
6
- const result = {};
7
+ function getWorkspacePackagesMetadata(projects) {
8
+ const entryPointsToProjectMap = {};
9
+ const wildcardEntryPointsToProjectMap = {};
10
+ const packageToProjectMap = {};
7
11
  for (const project of Object.values(projects)) {
8
12
  const metadata = 'data' in project ? project.data.metadata : project.metadata;
9
13
  if (!metadata?.js) {
10
14
  continue;
11
15
  }
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;
16
+ const { packageName, packageExports, packageMain, isInPackageManagerWorkspaces, } = metadata.js;
17
+ packageToProjectMap[packageName] = project;
18
+ if (!isInPackageManagerWorkspaces) {
19
+ // it is not included in the package manager workspaces config, so we
20
+ // skip it since the exports information wouldn't be used by the Node.js
21
+ // resolution
22
+ continue;
17
23
  }
18
- else {
19
- for (const entryPoint of Object.keys(packageExports)) {
20
- result[(0, posix_1.join)(packageName, entryPoint)] = project;
24
+ if (packageExports) {
25
+ if (typeof packageExports === 'string') {
26
+ // it points to a file, which would be the equivalent of an '.' export,
27
+ // in which case the package name is the entry point
28
+ entryPointsToProjectMap[packageName] = project;
29
+ }
30
+ else {
31
+ for (const entryPoint of Object.keys(packageExports)) {
32
+ if (packageExports[entryPoint] === null) {
33
+ // if the entry point is restricted, we skip it
34
+ continue;
35
+ }
36
+ if (entryPoint.startsWith('.')) {
37
+ // it is a relative subpath export
38
+ if (entryPoint.includes('*')) {
39
+ wildcardEntryPointsToProjectMap[(0, posix_1.join)(packageName, entryPoint)] =
40
+ project;
41
+ }
42
+ else {
43
+ entryPointsToProjectMap[(0, posix_1.join)(packageName, entryPoint)] = project;
44
+ }
45
+ }
46
+ else {
47
+ // it's a conditional export, so we use the package name as the entry point
48
+ // https://nodejs.org/api/packages.html#conditional-exports
49
+ entryPointsToProjectMap[packageName] = project;
50
+ }
51
+ }
21
52
  }
22
53
  }
54
+ else if (packageMain) {
55
+ // if there is no exports, but there is a main, the package name is the
56
+ // entry point
57
+ entryPointsToProjectMap[packageName] = project;
58
+ }
59
+ }
60
+ return {
61
+ entryPointsToProjectMap,
62
+ wildcardEntryPointsToProjectMap,
63
+ packageToProjectMap,
64
+ };
65
+ }
66
+ function matchImportToWildcardEntryPointsToProjectMap(wildcardEntryPointsToProjectMap, importPath) {
67
+ if (!Object.keys(wildcardEntryPointsToProjectMap).length) {
68
+ return null;
23
69
  }
24
- return result;
70
+ const matchingPair = Object.entries(wildcardEntryPointsToProjectMap).find(([key]) => (0, minimatch_1.minimatch)(importPath, key));
71
+ return matchingPair?.[1];
25
72
  }
@@ -33,7 +33,7 @@ export declare function getTranspiler(compilerOptions: CompilerOptions, tsConfig
33
33
  *
34
34
  * @returns cleanup method
35
35
  */
36
- export declare function registerTranspiler(compilerOptions: CompilerOptions, tsConfigRaw?: unknown): () => void;
36
+ export declare function registerTranspiler(compilerOptions: CompilerOptions): () => void;
37
37
  /**
38
38
  * @param tsConfigPath Adds the paths from a tsconfig file into node resolutions
39
39
  * @returns cleanup function
@@ -203,7 +203,7 @@ function getTranspiler(compilerOptions, tsConfigRaw) {
203
203
  *
204
204
  * @returns cleanup method
205
205
  */
206
- function registerTranspiler(compilerOptions, tsConfigRaw) {
206
+ function registerTranspiler(compilerOptions) {
207
207
  // Function to register transpiler that returns cleanup function
208
208
  const transpiler = getTranspiler(compilerOptions);
209
209
  if (!transpiler) {
@@ -1,6 +1,7 @@
1
1
  import type * as ts from 'typescript';
2
2
  import type { Node, SyntaxKind } from 'typescript';
3
3
  export declare function readTsConfig(tsConfigPath: string): ts.ParsedCommandLine;
4
+ export declare function readTsConfigOptions(tsConfigPath: string): ts.CompilerOptions;
4
5
  /**
5
6
  * Find a module based on its import
6
7
  *
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.readTsConfig = readTsConfig;
4
+ exports.readTsConfigOptions = readTsConfigOptions;
4
5
  exports.resolveModuleByImport = resolveModuleByImport;
5
6
  exports.getRootTsConfigFileName = getRootTsConfigFileName;
6
7
  exports.getRootTsConfigPath = getRootTsConfigPath;
@@ -4,13 +4,13 @@ import { PackageJson } from '../../utils/package-json';
4
4
  import { CreateNodesV2 } from '../../project-graph/plugins';
5
5
  import { PackageJsonConfigurationCache } from '../../../plugins/package-json';
6
6
  export declare const createNodesV2: CreateNodesV2;
7
- export declare function buildPackageJsonWorkspacesMatcher(workspaceRoot: string, readJson: (string: any) => any): (p: string) => boolean;
8
- export declare function createNodeFromPackageJson(pkgJsonPath: string, workspaceRoot: string, cache: PackageJsonConfigurationCache): {
7
+ export declare function buildPackageJsonWorkspacesMatcher(workspaceRoot: string, readJson: (path: string) => any): (p: string) => boolean;
8
+ export declare function createNodeFromPackageJson(pkgJsonPath: string, workspaceRoot: string, cache: PackageJsonConfigurationCache, isInPackageManagerWorkspaces: boolean): {
9
9
  projects: {
10
10
  [x: string]: ProjectConfiguration;
11
11
  };
12
12
  };
13
- export declare function buildProjectConfigurationFromPackageJson(packageJson: PackageJson, workspaceRoot: string, packageJsonPath: string, nxJson: NxJsonConfiguration): ProjectConfiguration & {
13
+ export declare function buildProjectConfigurationFromPackageJson(packageJson: PackageJson, workspaceRoot: string, packageJsonPath: string, nxJson: NxJsonConfiguration, isInPackageManagerWorkspaces: boolean): ProjectConfiguration & {
14
14
  name: string;
15
15
  };
16
16
  /**
@@ -31,12 +31,13 @@ exports.createNodesV2 = [
31
31
  };
32
32
  const cache = (0, package_json_2.readPackageJsonConfigurationCache)();
33
33
  return (0, plugins_1.createNodesFromFiles)((packageJsonPath, options, context) => {
34
- if (!isInPackageJsonWorkspaces(packageJsonPath) &&
34
+ const isInPackageManagerWorkspaces = isInPackageJsonWorkspaces(packageJsonPath);
35
+ if (!isInPackageManagerWorkspaces &&
35
36
  !isNextToProjectJson(packageJsonPath)) {
36
37
  // Skip if package.json is not part of the package.json workspaces and not next to a project.json.
37
38
  return null;
38
39
  }
39
- return createNodeFromPackageJson(packageJsonPath, context.workspaceRoot, cache);
40
+ return createNodeFromPackageJson(packageJsonPath, context.workspaceRoot, cache, isInPackageManagerWorkspaces);
40
41
  }, packageJsons, _, context);
41
42
  },
42
43
  ];
@@ -78,18 +79,13 @@ function buildPackageJsonWorkspacesMatcher(workspaceRoot, readJson) {
78
79
  */
79
80
  negativePatterns.every((negative) => (0, minimatch_1.minimatch)(p, negative));
80
81
  }
81
- function createNodeFromPackageJson(pkgJsonPath, workspaceRoot, cache) {
82
+ function createNodeFromPackageJson(pkgJsonPath, workspaceRoot, cache, isInPackageManagerWorkspaces) {
82
83
  const json = (0, fileutils_1.readJsonFile)((0, node_path_1.join)(workspaceRoot, pkgJsonPath));
83
84
  const projectRoot = (0, node_path_1.dirname)(pkgJsonPath);
84
85
  const hash = (0, file_hasher_1.hashObject)({
85
86
  ...json,
86
87
  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,
88
+ isInPackageManagerWorkspaces,
93
89
  });
94
90
  const cached = cache[hash];
95
91
  if (cached) {
@@ -99,7 +95,7 @@ function createNodeFromPackageJson(pkgJsonPath, workspaceRoot, cache) {
99
95
  },
100
96
  };
101
97
  }
102
- const project = buildProjectConfigurationFromPackageJson(json, workspaceRoot, pkgJsonPath, (0, nx_json_1.readNxJson)(workspaceRoot));
98
+ const project = buildProjectConfigurationFromPackageJson(json, workspaceRoot, pkgJsonPath, (0, nx_json_1.readNxJson)(workspaceRoot), isInPackageManagerWorkspaces);
103
99
  cache[hash] = project;
104
100
  return {
105
101
  projects: {
@@ -107,7 +103,7 @@ function createNodeFromPackageJson(pkgJsonPath, workspaceRoot, cache) {
107
103
  },
108
104
  };
109
105
  }
110
- function buildProjectConfigurationFromPackageJson(packageJson, workspaceRoot, packageJsonPath, nxJson) {
106
+ function buildProjectConfigurationFromPackageJson(packageJson, workspaceRoot, packageJsonPath, nxJson, isInPackageManagerWorkspaces) {
111
107
  const normalizedPath = packageJsonPath.split('\\').join('/');
112
108
  const projectRoot = (0, node_path_1.dirname)(normalizedPath);
113
109
  const siblingProjectJson = tryReadJson((0, node_path_1.join)(workspaceRoot, projectRoot, 'project.json'));
@@ -135,7 +131,7 @@ function buildProjectConfigurationFromPackageJson(packageJson, workspaceRoot, pa
135
131
  ...packageJson.nx,
136
132
  targets: (0, package_json_1.readTargetsFromPackageJson)(packageJson, nxJson),
137
133
  tags: (0, package_json_1.getTagsFromPackageJson)(packageJson),
138
- metadata: (0, package_json_1.getMetadataFromPackageJson)(packageJson),
134
+ metadata: (0, package_json_1.getMetadataFromPackageJson)(packageJson, isInPackageManagerWorkspaces),
139
135
  };
140
136
  if (nxJson?.workspaceLayout?.appsDir != nxJson?.workspaceLayout?.libsDir &&
141
137
  nxJson?.workspaceLayout?.appsDir &&
@@ -2,10 +2,14 @@ import { ConfigurationResult, ConfigurationSourceMaps } from './utils/project-co
2
2
  import { ProjectConfiguration } from '../config/workspace-json-project-json';
3
3
  import { ProjectGraph } from '../config/project-graph';
4
4
  import { CreateNodesFunctionV2 } from './plugins/public-api';
5
+ export type ProjectGraphErrorTypes = AggregateCreateNodesError | MergeNodesError | CreateMetadataError | ProjectsWithNoNameError | MultipleProjectsWithSameNameError | ProcessDependenciesError | WorkspaceValidityError;
6
+ export declare class StaleProjectGraphCacheError extends Error {
7
+ constructor();
8
+ }
5
9
  export declare class ProjectGraphError extends Error {
6
10
  #private;
7
11
  private readonly errors;
8
- constructor(errors: Array<AggregateCreateNodesError | MergeNodesError | ProjectsWithNoNameError | MultipleProjectsWithSameNameError | ProcessDependenciesError | CreateMetadataError | WorkspaceValidityError>, partialProjectGraph: ProjectGraph, partialSourceMaps: ConfigurationSourceMaps);
12
+ constructor(errors: Array<ProjectGraphErrorTypes>, partialProjectGraph: ProjectGraph, partialSourceMaps: ConfigurationSourceMaps | null);
9
13
  /**
10
14
  * The daemon cannot throw errors which contain methods as they are not serializable.
11
15
  *
@@ -19,7 +23,7 @@ export declare class ProjectGraphError extends Error {
19
23
  */
20
24
  getPartialProjectGraph(): ProjectGraph;
21
25
  getPartialSourcemaps(): ConfigurationSourceMaps;
22
- getErrors(): (AggregateCreateNodesError | MergeNodesError | ProjectsWithNoNameError | MultipleProjectsWithSameNameError | ProcessDependenciesError | CreateMetadataError | WorkspaceValidityError)[];
26
+ getErrors(): ProjectGraphErrorTypes[];
23
27
  }
24
28
  export declare class MultipleProjectsWithSameNameError extends Error {
25
29
  conflicts: Map<string, string[]>;
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
2
  var _ProjectGraphError_partialProjectGraph, _ProjectGraphError_partialSourceMaps;
3
3
  Object.defineProperty(exports, "__esModule", { value: true });
4
- exports.LoadPluginError = exports.DaemonProjectGraphError = exports.AggregateProjectGraphError = exports.WorkspaceValidityError = exports.ProcessDependenciesError = exports.CreateMetadataError = exports.MergeNodesError = exports.AggregateCreateNodesError = exports.ProjectConfigurationsError = exports.ProjectWithNoNameError = exports.ProjectsWithNoNameError = exports.ProjectWithExistingNameError = exports.MultipleProjectsWithSameNameError = exports.ProjectGraphError = void 0;
4
+ exports.LoadPluginError = exports.DaemonProjectGraphError = exports.AggregateProjectGraphError = exports.WorkspaceValidityError = exports.ProcessDependenciesError = exports.CreateMetadataError = exports.MergeNodesError = exports.AggregateCreateNodesError = exports.ProjectConfigurationsError = exports.ProjectWithNoNameError = exports.ProjectsWithNoNameError = exports.ProjectWithExistingNameError = exports.MultipleProjectsWithSameNameError = exports.ProjectGraphError = exports.StaleProjectGraphCacheError = void 0;
5
5
  exports.isProjectWithExistingNameError = isProjectWithExistingNameError;
6
6
  exports.isMultipleProjectsWithSameNameError = isMultipleProjectsWithSameNameError;
7
7
  exports.isProjectsWithNoNameError = isProjectsWithNoNameError;
@@ -14,6 +14,12 @@ exports.isCreateMetadataError = isCreateMetadataError;
14
14
  exports.isAggregateCreateNodesError = isAggregateCreateNodesError;
15
15
  exports.isMergeNodesError = isMergeNodesError;
16
16
  const tslib_1 = require("tslib");
17
+ class StaleProjectGraphCacheError extends Error {
18
+ constructor() {
19
+ super('The project graph cache was stale. Ensure that it has been recently created before using `readCachedProjectGraph`.');
20
+ }
21
+ }
22
+ exports.StaleProjectGraphCacheError = StaleProjectGraphCacheError;
17
23
  class ProjectGraphError extends Error {
18
24
  constructor(errors, partialProjectGraph, partialSourceMaps) {
19
25
  const messageFragments = ['Failed to process project graph.'];
@@ -154,6 +154,7 @@ function getProjectsSync(root, nxJson) {
154
154
  ...(0, package_json_1.getGlobPatternsFromPackageManagerWorkspaces)(root, fileutils_1.readJsonFile),
155
155
  ];
156
156
  const projectFiles = (0, workspace_context_1.globWithWorkspaceContextSync)(root, patterns);
157
+ const isInPackageJsonWorkspaces = (0, package_json_1.buildPackageJsonWorkspacesMatcher)(root, (f) => (0, fileutils_1.readJsonFile)((0, path_1.join)(root, f)));
157
158
  const rootMap = {};
158
159
  for (const projectFile of projectFiles) {
159
160
  if ((0, path_1.basename)(projectFile) === 'project.json') {
@@ -163,7 +164,7 @@ function getProjectsSync(root, nxJson) {
163
164
  }
164
165
  else if ((0, path_1.basename)(projectFile) === 'package.json') {
165
166
  const packageJson = (0, fileutils_1.readJsonFile)(projectFile);
166
- const config = (0, package_json_1.buildProjectConfigurationFromPackageJson)(packageJson, root, projectFile, nxJson);
167
+ const config = (0, package_json_1.buildProjectConfigurationFromPackageJson)(packageJson, root, projectFile, nxJson, isInPackageJsonWorkspaces(projectFile));
167
168
  if (!rootMap[config.root]) {
168
169
  (0, project_configuration_utils_1.mergeProjectConfigurationIntoRootMap)(rootMap,
169
170
  // Inferred targets, tags, etc don't show up when running generators
@@ -1,6 +1,8 @@
1
1
  import { NxJsonConfiguration } from '../config/nx-json';
2
2
  import { FileData, FileMap, ProjectGraph } from '../config/project-graph';
3
3
  import { ProjectConfiguration } from '../config/workspace-json-project-json';
4
+ import { ConfigurationSourceMaps } from './utils/project-configuration-utils';
5
+ import { ProjectGraphErrorTypes } from './error-types';
4
6
  export interface FileMapCache {
5
7
  version: string;
6
8
  nxVersion: string;
@@ -11,9 +13,11 @@ export interface FileMapCache {
11
13
  }
12
14
  export declare const nxProjectGraph: string;
13
15
  export declare const nxFileMap: string;
16
+ export declare const nxSourceMaps: string;
14
17
  export declare function ensureCacheDirectory(): void;
15
18
  export declare function readFileMapCache(): null | FileMapCache;
16
- export declare function readProjectGraphCache(): null | ProjectGraph;
19
+ export declare function readProjectGraphCache(minimumComputedAt?: number): null | ProjectGraph;
20
+ export declare function readSourceMapsCache(): null | ConfigurationSourceMaps;
17
21
  export declare function createProjectFileMapCache(nxJson: NxJsonConfiguration<'*' | string[]>, packageJsonDeps: Record<string, string>, fileMap: FileMap, tsConfig: {
18
22
  compilerOptions?: {
19
23
  paths?: {
@@ -21,7 +25,7 @@ export declare function createProjectFileMapCache(nxJson: NxJsonConfiguration<'*
21
25
  };
22
26
  };
23
27
  }): FileMapCache;
24
- export declare function writeCache(cache: FileMapCache, projectGraph: ProjectGraph): void;
28
+ export declare function writeCache(cache: FileMapCache, projectGraph: ProjectGraph, sourceMaps: ConfigurationSourceMaps, errors: ProjectGraphErrorTypes[]): void;
25
29
  export declare function shouldRecomputeWholeGraph(cache: FileMapCache, packageJsonDeps: Record<string, string>, projects: Record<string, ProjectConfiguration>, nxJson: NxJsonConfiguration, tsConfig: {
26
30
  compilerOptions: {
27
31
  paths: {
@@ -1,9 +1,10 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.nxFileMap = exports.nxProjectGraph = void 0;
3
+ exports.nxSourceMaps = exports.nxFileMap = exports.nxProjectGraph = void 0;
4
4
  exports.ensureCacheDirectory = ensureCacheDirectory;
5
5
  exports.readFileMapCache = readFileMapCache;
6
6
  exports.readProjectGraphCache = readProjectGraphCache;
7
+ exports.readSourceMapsCache = readSourceMapsCache;
7
8
  exports.createProjectFileMapCache = createProjectFileMapCache;
8
9
  exports.writeCache = writeCache;
9
10
  exports.shouldRecomputeWholeGraph = shouldRecomputeWholeGraph;
@@ -14,8 +15,10 @@ const perf_hooks_1 = require("perf_hooks");
14
15
  const cache_directory_1 = require("../utils/cache-directory");
15
16
  const fileutils_1 = require("../utils/fileutils");
16
17
  const versions_1 = require("../utils/versions");
18
+ const error_types_1 = require("./error-types");
17
19
  exports.nxProjectGraph = (0, path_1.join)(cache_directory_1.workspaceDataDirectory, 'project-graph.json');
18
20
  exports.nxFileMap = (0, path_1.join)(cache_directory_1.workspaceDataDirectory, 'file-map.json');
21
+ exports.nxSourceMaps = (0, path_1.join)(cache_directory_1.workspaceDataDirectory, 'source-maps.json');
19
22
  function ensureCacheDirectory() {
20
23
  try {
21
24
  if (!(0, node_fs_1.existsSync)(cache_directory_1.workspaceDataDirectory)) {
@@ -55,21 +58,62 @@ function readFileMapCache() {
55
58
  perf_hooks_1.performance.measure('read cache', 'read cache:start', 'read cache:end');
56
59
  return data ?? null;
57
60
  }
58
- function readProjectGraphCache() {
61
+ function readProjectGraphCache(minimumComputedAt) {
59
62
  perf_hooks_1.performance.mark('read project-graph:start');
60
63
  ensureCacheDirectory();
61
- let data = null;
62
64
  try {
63
65
  if ((0, fileutils_1.fileExists)(exports.nxProjectGraph)) {
64
- data = (0, fileutils_1.readJsonFile)(exports.nxProjectGraph);
66
+ const { computedAt, errors, ...projectGraphCache } = (0, fileutils_1.readJsonFile)(exports.nxProjectGraph);
67
+ if (minimumComputedAt &&
68
+ (!computedAt || computedAt < minimumComputedAt)) {
69
+ throw new error_types_1.StaleProjectGraphCacheError();
70
+ }
71
+ if (errors && errors.length > 0) {
72
+ if (!minimumComputedAt) {
73
+ // If you didn't pass minimum computed at, we do not know if
74
+ // the errors on the cached graph would be relevant to what you
75
+ // are running. Prior to adding error handling here, the graph
76
+ // would not have been written to the cache. As such, this matches
77
+ // existing behavior of the public API.
78
+ return null;
79
+ }
80
+ throw new error_types_1.ProjectGraphError(errors, projectGraphCache, readSourceMapsCache());
81
+ }
82
+ return projectGraphCache;
83
+ }
84
+ else {
85
+ return null;
65
86
  }
66
87
  }
67
88
  catch (error) {
89
+ if (error instanceof error_types_1.StaleProjectGraphCacheError ||
90
+ error instanceof error_types_1.ProjectGraphError) {
91
+ throw error;
92
+ }
68
93
  console.log(`Error reading '${exports.nxProjectGraph}'. Continue the process without the cache.`);
69
94
  console.log(error);
95
+ return null;
96
+ }
97
+ finally {
98
+ perf_hooks_1.performance.mark('read project-graph:end');
99
+ perf_hooks_1.performance.measure('read cache', 'read project-graph:start', 'read project-graph:end');
70
100
  }
71
- perf_hooks_1.performance.mark('read project-graph:end');
72
- perf_hooks_1.performance.measure('read cache', 'read project-graph:start', 'read project-graph:end');
101
+ }
102
+ function readSourceMapsCache() {
103
+ perf_hooks_1.performance.mark('read source-maps:start');
104
+ ensureCacheDirectory();
105
+ let data = null;
106
+ try {
107
+ if ((0, fileutils_1.fileExists)(exports.nxSourceMaps)) {
108
+ data = (0, fileutils_1.readJsonFile)(exports.nxSourceMaps);
109
+ }
110
+ }
111
+ catch (error) {
112
+ console.log(`Error reading '${exports.nxSourceMaps}'. Continue the process without the cache.`);
113
+ console.log(error);
114
+ }
115
+ perf_hooks_1.performance.mark('read source-maps:end');
116
+ perf_hooks_1.performance.measure('read cache', 'read source-maps:start', 'read source-maps:end');
73
117
  return data ?? null;
74
118
  }
75
119
  function createProjectFileMapCache(nxJson, packageJsonDeps, fileMap, tsConfig) {
@@ -85,7 +129,7 @@ function createProjectFileMapCache(nxJson, packageJsonDeps, fileMap, tsConfig) {
85
129
  };
86
130
  return newValue;
87
131
  }
88
- function writeCache(cache, projectGraph) {
132
+ function writeCache(cache, projectGraph, sourceMaps, errors) {
89
133
  perf_hooks_1.performance.mark('write cache:start');
90
134
  let retry = 1;
91
135
  let done = false;
@@ -98,11 +142,23 @@ function writeCache(cache, projectGraph) {
98
142
  const unique = (Math.random().toString(16) + '0000000').slice(2, 10);
99
143
  const tmpProjectGraphPath = `${exports.nxProjectGraph}~${unique}`;
100
144
  const tmpFileMapPath = `${exports.nxFileMap}~${unique}`;
145
+ const tmpSourceMapPath = `${exports.nxSourceMaps}~${unique}`;
101
146
  try {
102
- (0, fileutils_1.writeJsonFile)(tmpProjectGraphPath, projectGraph);
147
+ (0, fileutils_1.writeJsonFile)(tmpProjectGraphPath, {
148
+ ...projectGraph,
149
+ errors,
150
+ computedAt: Date.now(),
151
+ });
103
152
  (0, node_fs_1.renameSync)(tmpProjectGraphPath, exports.nxProjectGraph);
104
- (0, fileutils_1.writeJsonFile)(tmpFileMapPath, cache);
105
- (0, node_fs_1.renameSync)(tmpFileMapPath, exports.nxFileMap);
153
+ (0, fileutils_1.writeJsonFile)(tmpSourceMapPath, sourceMaps);
154
+ (0, node_fs_1.renameSync)(tmpSourceMapPath, exports.nxSourceMaps);
155
+ // only write the file map if there are no errors
156
+ // if there were errors, the errors make the filemap invalid
157
+ // TODO: We should be able to keep the valid part of the filemap if the errors being thrown told us which parts of the filemap were invalid
158
+ if (errors.length === 0) {
159
+ (0, fileutils_1.writeJsonFile)(tmpFileMapPath, cache);
160
+ (0, node_fs_1.renameSync)(tmpFileMapPath, exports.nxFileMap);
161
+ }
106
162
  done = true;
107
163
  }
108
164
  catch (err) {
@@ -57,27 +57,23 @@ class LoadedNxPlugin {
57
57
  if (plugin.preTasksExecution) {
58
58
  this.preTasksExecution = async (context) => {
59
59
  const updates = {};
60
- let revokeFn;
60
+ let originalEnv = process.env;
61
61
  if ((0, enabled_1.isIsolationEnabled)() || (0, client_1.isDaemonEnabled)()) {
62
- const { proxy, revoke } = Proxy.revocable(process.env, {
62
+ process.env = new Proxy(originalEnv, {
63
63
  set: (target, key, value) => {
64
64
  target[key] = value;
65
65
  updates[key] = value;
66
66
  return true;
67
67
  },
68
68
  });
69
- process.env = proxy;
70
- revokeFn = revoke;
71
69
  }
72
70
  await plugin.preTasksExecution(this.options, context);
73
- if (revokeFn) {
74
- revokeFn();
75
- }
71
+ process.env = originalEnv;
76
72
  return updates;
77
73
  };
78
- if (plugin.postTasksExecution) {
79
- this.postTasksExecution = async (context) => plugin.postTasksExecution(this.options, context);
80
- }
74
+ }
75
+ if (plugin.postTasksExecution) {
76
+ this.postTasksExecution = async (context) => plugin.postTasksExecution(this.options, context);
81
77
  }
82
78
  }
83
79
  }
@@ -83,6 +83,7 @@ function lookupLocalPlugin(importPath, projects, root = workspace_root_1.workspa
83
83
  return { path: path.join(root, projectConfig.root), projectConfig };
84
84
  }
85
85
  let packageEntryPointsToProjectMap;
86
+ let wildcardEntryPointsToProjectMap;
86
87
  function findNxProjectForImportPath(importPath, projects, root = workspace_root_1.workspaceRoot) {
87
88
  const tsConfigPaths = readTsConfigPaths(root);
88
89
  const possibleTsPaths = tsConfigPaths[importPath]?.map((p) => (0, path_1.normalizePath)(path.relative(root, path.join(root, p)))) ?? [];
@@ -101,11 +102,19 @@ function findNxProjectForImportPath(importPath, projects, root = workspace_root_
101
102
  }
102
103
  }
103
104
  }
104
- packageEntryPointsToProjectMap ??=
105
- (0, packages_1.getPackageEntryPointsToProjectMap)(projects);
105
+ if (!packageEntryPointsToProjectMap && !wildcardEntryPointsToProjectMap) {
106
+ ({
107
+ entryPointsToProjectMap: packageEntryPointsToProjectMap,
108
+ wildcardEntryPointsToProjectMap,
109
+ } = (0, packages_1.getWorkspacePackagesMetadata)(projects));
110
+ }
106
111
  if (packageEntryPointsToProjectMap[importPath]) {
107
112
  return packageEntryPointsToProjectMap[importPath];
108
113
  }
114
+ const project = (0, packages_1.matchImportToWildcardEntryPointsToProjectMap)(wildcardEntryPointsToProjectMap, importPath);
115
+ if (project) {
116
+ return project;
117
+ }
109
118
  logger_1.logger.verbose('Unable to find local plugin', possibleTsPaths, projectRootMappings);
110
119
  throw new Error('Unable to resolve local plugin with import path ' + importPath);
111
120
  }
@@ -23,16 +23,16 @@ function registerPluginTSTranspiler() {
23
23
  if (!tsConfigName) {
24
24
  return;
25
25
  }
26
- const tsConfig = tsConfigName
27
- ? (0, typescript_1.readTsConfig)(tsConfigName)
26
+ const tsConfigOptions = tsConfigName
27
+ ? (0, typescript_1.readTsConfigOptions)(tsConfigName)
28
28
  : {};
29
29
  const cleanupFns = [
30
30
  (0, register_1.registerTsConfigPaths)(tsConfigName),
31
31
  (0, register_1.registerTranspiler)({
32
32
  experimentalDecorators: true,
33
33
  emitDecoratorMetadata: true,
34
- ...tsConfig.options,
35
- }, tsConfig.raw),
34
+ ...tsConfigOptions,
35
+ }),
36
36
  ];
37
37
  exports.unregisterPluginTSTranspiler = () => {
38
38
  cleanupFns.forEach((fn) => fn?.());
@@ -2,9 +2,11 @@ import { ProjectGraph } from '../config/project-graph';
2
2
  import { ProjectConfiguration, ProjectsConfigurations } from '../config/workspace-json-project-json';
3
3
  /**
4
4
  * Synchronously reads the latest cached copy of the workspace's ProjectGraph.
5
+ *
6
+ * @param {number} [minimumComputedAt] - The minimum timestamp that the cached ProjectGraph must have been computed at.
5
7
  * @throws {Error} if there is no cached ProjectGraph to read from
6
8
  */
7
- export declare function readCachedProjectGraph(): ProjectGraph;
9
+ export declare function readCachedProjectGraph(minimumComputedAt?: number): ProjectGraph;
8
10
  export declare function readCachedProjectConfiguration(projectName: string): ProjectConfiguration;
9
11
  /**
10
12
  * Get the {@link ProjectsConfigurations} from the {@link ProjectGraph}
@@ -21,12 +21,18 @@ const nx_deps_cache_1 = require("./nx-deps-cache");
21
21
  const retrieve_workspace_files_1 = require("./utils/retrieve-workspace-files");
22
22
  const get_plugins_1 = require("./plugins/get-plugins");
23
23
  const logger_1 = require("../utils/logger");
24
+ const native_1 = require("../native");
25
+ const path_1 = require("path");
26
+ const cache_directory_1 = require("../utils/cache-directory");
27
+ const delayed_spinner_1 = require("../utils/delayed-spinner");
24
28
  /**
25
29
  * Synchronously reads the latest cached copy of the workspace's ProjectGraph.
30
+ *
31
+ * @param {number} [minimumComputedAt] - The minimum timestamp that the cached ProjectGraph must have been computed at.
26
32
  * @throws {Error} if there is no cached ProjectGraph to read from
27
33
  */
28
- function readCachedProjectGraph() {
29
- const projectGraphCache = (0, nx_deps_cache_1.readProjectGraphCache)();
34
+ function readCachedProjectGraph(minimumComputedAt) {
35
+ const projectGraphCache = (0, nx_deps_cache_1.readProjectGraphCache)(minimumComputedAt);
30
36
  if (!projectGraphCache) {
31
37
  const angularSpecificError = (0, fileutils_1.fileExists)(`${workspace_root_1.workspaceRoot}/angular.json`)
32
38
  ? (0, strip_indents_1.stripIndents) `
@@ -120,13 +126,13 @@ async function buildProjectGraphAndSourceMapsWithoutDaemon() {
120
126
  ...(projectConfigurationsError?.errors ?? []),
121
127
  ...(projectGraphError?.errors ?? []),
122
128
  ];
129
+ if (cacheEnabled) {
130
+ (0, nx_deps_cache_1.writeCache)(projectFileMapCache, projectGraph, sourceMaps, errors);
131
+ }
123
132
  if (errors.length > 0) {
124
133
  throw new error_types_1.ProjectGraphError(errors, projectGraph, sourceMaps);
125
134
  }
126
135
  else {
127
- if (cacheEnabled) {
128
- (0, nx_deps_cache_1.writeCache)(projectFileMapCache, projectGraph);
129
- }
130
136
  return { projectGraph, sourceMaps };
131
137
  }
132
138
  }
@@ -143,7 +149,7 @@ function handleProjectGraphError(opts, e) {
143
149
  bodyLines: bodyLines,
144
150
  });
145
151
  }
146
- else {
152
+ else if (typeof e.message === 'string') {
147
153
  const lines = e.message.split('\n');
148
154
  output_1.output.error({
149
155
  title: lines[0],
@@ -153,12 +159,25 @@ function handleProjectGraphError(opts, e) {
153
159
  console.error(e);
154
160
  }
155
161
  }
162
+ else {
163
+ console.error(e);
164
+ }
156
165
  process.exit(1);
157
166
  }
158
167
  else {
159
168
  throw e;
160
169
  }
161
170
  }
171
+ async function readCachedGraphAndHydrateFileMap(minimumComputedAt) {
172
+ const graph = readCachedProjectGraph(minimumComputedAt);
173
+ const projectRootMap = Object.fromEntries(Object.entries(graph.nodes).map(([project, { data }]) => [
174
+ data.root,
175
+ project,
176
+ ]));
177
+ const { allWorkspaceFiles, fileMap, rustReferences } = await (0, retrieve_workspace_files_1.retrieveWorkspaceFiles)(workspace_root_1.workspaceRoot, projectRootMap);
178
+ (0, build_project_graph_1.hydrateFileMap)(fileMap, allWorkspaceFiles, rustReferences);
179
+ return graph;
180
+ }
162
181
  /**
163
182
  * Computes and returns a ProjectGraph.
164
183
  *
@@ -186,17 +205,14 @@ async function createProjectGraphAsync(opts = {
186
205
  }) {
187
206
  if (process.env.NX_FORCE_REUSE_CACHED_GRAPH === 'true') {
188
207
  try {
189
- const graph = readCachedProjectGraph();
190
- const projectRootMap = Object.fromEntries(Object.entries(graph.nodes).map(([project, { data }]) => [
191
- data.root,
192
- project,
193
- ]));
194
- const { allWorkspaceFiles, fileMap, rustReferences } = await (0, retrieve_workspace_files_1.retrieveWorkspaceFiles)(workspace_root_1.workspaceRoot, projectRootMap);
195
- (0, build_project_graph_1.hydrateFileMap)(fileMap, allWorkspaceFiles, rustReferences);
196
- return graph;
197
208
  // If no cached graph is found, we will fall through to the normal flow
209
+ const graph = await readCachedGraphAndHydrateFileMap();
210
+ return graph;
198
211
  }
199
212
  catch (e) {
213
+ if (e instanceof error_types_1.ProjectGraphError) {
214
+ throw e;
215
+ }
200
216
  logger_1.logger.verbose('Unable to use cached project graph', e);
201
217
  }
202
218
  }
@@ -209,6 +225,48 @@ async function createProjectGraphAndSourceMapsAsync(opts = {
209
225
  }) {
210
226
  perf_hooks_1.performance.mark('create-project-graph-async:start');
211
227
  if (!client_1.daemonClient.enabled()) {
228
+ const lock = !native_1.IS_WASM
229
+ ? new native_1.FileLock((0, path_1.join)(cache_directory_1.workspaceDataDirectory, 'project-graph.lock'))
230
+ : null;
231
+ let locked = lock?.locked;
232
+ while (locked) {
233
+ logger_1.logger.verbose('Waiting for graph construction in another process to complete');
234
+ const spinner = new delayed_spinner_1.DelayedSpinner('Waiting for graph construction in another process to complete');
235
+ const start = Date.now();
236
+ await lock.wait();
237
+ spinner.cleanup();
238
+ // Note: This will currently throw if any of the caches are missing...
239
+ // It would be nice if one of the processes that was waiting for the lock
240
+ // could pick up the slack and build the graph if it's missing, but
241
+ // we wouldn't want either of the below to happen:
242
+ // - All of the waiting processes to build the graph
243
+ // - Even one of the processes building the graph on a legitimate error
244
+ try {
245
+ // Ensuring that computedAt was after this process started
246
+ // waiting for the graph to complete, means that the graph
247
+ // was computed by the process was already working.
248
+ const graph = await readCachedGraphAndHydrateFileMap(start);
249
+ const sourceMaps = (0, nx_deps_cache_1.readSourceMapsCache)();
250
+ if (!sourceMaps) {
251
+ throw new Error('The project graph was computed in another process, but the source maps are missing.');
252
+ }
253
+ return {
254
+ projectGraph: graph,
255
+ sourceMaps,
256
+ };
257
+ }
258
+ catch (e) {
259
+ // If the error is that the cached graph is stale after unlock,
260
+ // the process that was working on the graph must have been canceled,
261
+ // so we will fall through to the normal flow to ensure
262
+ // its created by one of the processes that was waiting
263
+ if (!(e instanceof error_types_1.StaleProjectGraphCacheError)) {
264
+ throw e;
265
+ }
266
+ }
267
+ locked = lock.check();
268
+ }
269
+ lock?.lock();
212
270
  try {
213
271
  const res = await buildProjectGraphAndSourceMapsWithoutDaemon();
214
272
  perf_hooks_1.performance.measure('create-project-graph-async >> retrieve-project-configurations', 'retrieve-project-configurations:start', 'retrieve-project-configurations:end');
@@ -221,6 +279,9 @@ async function createProjectGraphAndSourceMapsAsync(opts = {
221
279
  catch (e) {
222
280
  handleProjectGraphError(opts, e);
223
281
  }
282
+ finally {
283
+ lock.unlock();
284
+ }
224
285
  }
225
286
  else {
226
287
  try {
@@ -437,18 +437,19 @@ function tryAndRetry(fn) {
437
437
  let attempts = 0;
438
438
  // Generate a random number between 2 and 4 to raise to the power of attempts
439
439
  const baseExponent = Math.random() * 2 + 2;
440
+ const baseTimeout = 15;
440
441
  const _try = async () => {
441
442
  try {
442
443
  attempts++;
443
444
  return await fn();
444
445
  }
445
446
  catch (e) {
446
- // Max time is 5 * 4^3 = 20480ms
447
+ // Max time is 15 * (4 + 4² + 4³ + 4⁴ + 4⁵) = 20460ms
447
448
  if (attempts === 6) {
448
449
  // After enough attempts, throw the error
449
450
  throw e;
450
451
  }
451
- await new Promise((res) => setTimeout(res, baseExponent ** attempts));
452
+ await new Promise((res) => setTimeout(res, baseTimeout * baseExponent ** attempts));
452
453
  return await _try();
453
454
  }
454
455
  };
@@ -72,7 +72,7 @@ export declare function readNxMigrateConfig(json: Partial<PackageJson>): NxMigra
72
72
  packageGroup?: ArrayPackageGroup;
73
73
  };
74
74
  export declare function buildTargetFromScript(script: string, scripts: Record<string, string>, packageManagerCommand: PackageManagerCommands): TargetConfiguration;
75
- export declare function getMetadataFromPackageJson(packageJson: PackageJson): ProjectMetadata;
75
+ export declare function getMetadataFromPackageJson(packageJson: PackageJson, isInPackageManagerWorkspaces: boolean): ProjectMetadata;
76
76
  export declare function getTagsFromPackageJson(packageJson: PackageJson): string[];
77
77
  export declare function readTargetsFromPackageJson(packageJson: PackageJson, nxJson: NxJsonConfiguration): Record<string, TargetConfiguration<any>>;
78
78
  /**
@@ -57,8 +57,8 @@ function buildTargetFromScript(script, scripts = {}, packageManagerCommand) {
57
57
  };
58
58
  }
59
59
  let packageManagerCommand;
60
- function getMetadataFromPackageJson(packageJson) {
61
- const { scripts, nx, description, name, exports } = packageJson;
60
+ function getMetadataFromPackageJson(packageJson, isInPackageManagerWorkspaces) {
61
+ const { scripts, nx, description, name, exports, main } = packageJson;
62
62
  const includedScripts = nx?.includedScripts || Object.keys(scripts ?? {});
63
63
  return {
64
64
  targetGroups: {
@@ -68,6 +68,8 @@ function getMetadataFromPackageJson(packageJson) {
68
68
  js: {
69
69
  packageName: name,
70
70
  packageExports: exports,
71
+ packageMain: main,
72
+ isInPackageManagerWorkspaces,
71
73
  },
72
74
  };
73
75
  }