nx 19.2.0-beta.0 → 19.2.0-beta.2

Sign up to get free protection for your applications and to get access to all the features.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nx",
3
- "version": "19.2.0-beta.0",
3
+ "version": "19.2.0-beta.2",
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": {
@@ -69,7 +69,7 @@
69
69
  "yargs-parser": "21.1.1",
70
70
  "node-machine-id": "1.1.12",
71
71
  "ora": "5.3.0",
72
- "@nrwl/tao": "19.2.0-beta.0"
72
+ "@nrwl/tao": "19.2.0-beta.2"
73
73
  },
74
74
  "peerDependencies": {
75
75
  "@swc-node/register": "^1.8.0",
@@ -84,16 +84,16 @@
84
84
  }
85
85
  },
86
86
  "optionalDependencies": {
87
- "@nx/nx-darwin-x64": "19.2.0-beta.0",
88
- "@nx/nx-darwin-arm64": "19.2.0-beta.0",
89
- "@nx/nx-linux-x64-gnu": "19.2.0-beta.0",
90
- "@nx/nx-linux-x64-musl": "19.2.0-beta.0",
91
- "@nx/nx-win32-x64-msvc": "19.2.0-beta.0",
92
- "@nx/nx-linux-arm64-gnu": "19.2.0-beta.0",
93
- "@nx/nx-linux-arm64-musl": "19.2.0-beta.0",
94
- "@nx/nx-linux-arm-gnueabihf": "19.2.0-beta.0",
95
- "@nx/nx-win32-arm64-msvc": "19.2.0-beta.0",
96
- "@nx/nx-freebsd-x64": "19.2.0-beta.0"
87
+ "@nx/nx-darwin-x64": "19.2.0-beta.2",
88
+ "@nx/nx-darwin-arm64": "19.2.0-beta.2",
89
+ "@nx/nx-linux-x64-gnu": "19.2.0-beta.2",
90
+ "@nx/nx-linux-x64-musl": "19.2.0-beta.2",
91
+ "@nx/nx-win32-x64-msvc": "19.2.0-beta.2",
92
+ "@nx/nx-linux-arm64-gnu": "19.2.0-beta.2",
93
+ "@nx/nx-linux-arm64-musl": "19.2.0-beta.2",
94
+ "@nx/nx-linux-arm-gnueabihf": "19.2.0-beta.2",
95
+ "@nx/nx-win32-arm64-msvc": "19.2.0-beta.2",
96
+ "@nx/nx-freebsd-x64": "19.2.0-beta.2"
97
97
  },
98
98
  "nx-migrations": {
99
99
  "migrations": "./migrations.json",
@@ -23,3 +23,4 @@ export { retrieveProjectConfigurations } from './project-graph/utils/retrieve-wo
23
23
  export { LoadedNxPlugin } from './project-graph/plugins/internal-api';
24
24
  export * from './project-graph/error-types';
25
25
  export { registerTsProject } from './plugins/js/utils/register';
26
+ export { interpolate } from './tasks-runner/utils';
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.registerTsProject = exports.LoadedNxPlugin = exports.retrieveProjectConfigurations = exports.findProjectForPath = exports.createProjectRootMappingsFromProjectConfigurations = exports.hashWithWorkspaceContext = exports.hashObject = exports.splitByColons = exports.readModulePackageJson = exports.stripIndent = exports.sortObjectByKeys = exports.combineOptionsForExecutor = exports.splitTarget = exports.readProjectConfigurationsFromRootMap = exports.mergeTargetConfigurations = exports.retrieveProjectConfigurationsWithAngularProjects = exports.calculateDefaultProjectName = exports.readNxJsonFromDisk = exports.getExecutorInformation = exports.createTempNpmDirectory = void 0;
3
+ exports.interpolate = exports.registerTsProject = exports.LoadedNxPlugin = exports.retrieveProjectConfigurations = exports.findProjectForPath = exports.createProjectRootMappingsFromProjectConfigurations = exports.hashWithWorkspaceContext = exports.hashObject = exports.splitByColons = exports.readModulePackageJson = exports.stripIndent = exports.sortObjectByKeys = exports.combineOptionsForExecutor = exports.splitTarget = exports.readProjectConfigurationsFromRootMap = exports.mergeTargetConfigurations = exports.retrieveProjectConfigurationsWithAngularProjects = exports.calculateDefaultProjectName = exports.readNxJsonFromDisk = exports.getExecutorInformation = exports.createTempNpmDirectory = void 0;
4
4
  const tslib_1 = require("tslib");
5
5
  /**
6
6
  * Note to developers: STOP! These exports are available via requireNx in @nx/devkit.
@@ -47,3 +47,5 @@ Object.defineProperty(exports, "LoadedNxPlugin", { enumerable: true, get: functi
47
47
  tslib_1.__exportStar(require("./project-graph/error-types"), exports);
48
48
  var register_1 = require("./plugins/js/utils/register");
49
49
  Object.defineProperty(exports, "registerTsProject", { enumerable: true, get: function () { return register_1.registerTsProject; } });
50
+ var utils_1 = require("./tasks-runner/utils");
51
+ Object.defineProperty(exports, "interpolate", { enumerable: true, get: function () { return utils_1.interpolate; } });
@@ -233,7 +233,7 @@ function getNodes(data, keyMap, isV5) {
233
233
  function getHoistedVersion(hoistedDependencies, packageName, isV5) {
234
234
  let version = (0, package_json_1.getHoistedPackageVersion)(packageName);
235
235
  if (!version) {
236
- const key = Object.keys(hoistedDependencies).find((k) => k.startsWith(isV5 ? `/${packageName}/` : `/${packageName}@`));
236
+ const key = Object.keys(hoistedDependencies).find((k) => k.startsWith(`/${packageName}/`));
237
237
  if (key) {
238
238
  version = parseBaseVersion(getVersion(key.slice(1), packageName), isV5);
239
239
  }
@@ -1,12 +1,25 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.buildExplicitDependencies = void 0;
4
- const explicit_project_dependencies_1 = require("./explicit-project-dependencies");
5
4
  const explicit_package_json_dependencies_1 = require("./explicit-package-json-dependencies");
5
+ const explicit_project_dependencies_1 = require("./explicit-project-dependencies");
6
+ const target_project_locator_1 = require("./target-project-locator");
6
7
  function buildExplicitDependencies(jsPluginConfig, ctx) {
7
8
  if (totalNumberOfFilesToProcess(ctx) === 0)
8
9
  return [];
9
10
  let dependencies = [];
11
+ // TODO: TargetProjectLocator is a public API, so we can't change the shape of it
12
+ // We should eventually let it accept Record<string, ProjectConfiguration> s.t. we
13
+ // don't have to reshape the CreateDependenciesContext here.
14
+ const nodes = Object.fromEntries(Object.entries(ctx.projects).map(([key, config]) => [
15
+ key,
16
+ {
17
+ name: key,
18
+ type: null,
19
+ data: config,
20
+ },
21
+ ]));
22
+ const targetProjectLocator = new target_project_locator_1.TargetProjectLocator(nodes, ctx.externalNodes);
10
23
  if (jsPluginConfig.analyzeSourceFiles === undefined ||
11
24
  jsPluginConfig.analyzeSourceFiles === true) {
12
25
  let tsExists = false;
@@ -16,12 +29,12 @@ function buildExplicitDependencies(jsPluginConfig, ctx) {
16
29
  }
17
30
  catch { }
18
31
  if (tsExists) {
19
- dependencies = dependencies.concat((0, explicit_project_dependencies_1.buildExplicitTypeScriptDependencies)(ctx));
32
+ dependencies = dependencies.concat((0, explicit_project_dependencies_1.buildExplicitTypeScriptDependencies)(ctx, targetProjectLocator));
20
33
  }
21
34
  }
22
35
  if (jsPluginConfig.analyzePackageJson === undefined ||
23
36
  jsPluginConfig.analyzePackageJson === true) {
24
- dependencies = dependencies.concat((0, explicit_package_json_dependencies_1.buildExplicitPackageJsonDependencies)(ctx));
37
+ dependencies = dependencies.concat((0, explicit_package_json_dependencies_1.buildExplicitPackageJsonDependencies)(ctx, targetProjectLocator));
25
38
  }
26
39
  return dependencies;
27
40
  }
@@ -1,3 +1,4 @@
1
1
  import { CreateDependenciesContext } from '../../../../project-graph/plugins';
2
2
  import { RawProjectGraphDependency } from '../../../../project-graph/project-graph-builder';
3
- export declare function buildExplicitPackageJsonDependencies(ctx: CreateDependenciesContext): RawProjectGraphDependency[];
3
+ import { TargetProjectLocator } from './target-project-locator';
4
+ export declare function buildExplicitPackageJsonDependencies(ctx: CreateDependenciesContext, targetProjectLocator: TargetProjectLocator): RawProjectGraphDependency[];
@@ -1,13 +1,13 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.buildExplicitPackageJsonDependencies = void 0;
4
- const file_utils_1 = require("../../../../project-graph/file-utils");
5
- const path_1 = require("path");
4
+ const node_path_1 = require("node:path");
6
5
  const project_graph_1 = require("../../../../config/project-graph");
7
- const json_1 = require("../../../../utils/json");
8
- const path_2 = require("../../../../utils/path");
6
+ const file_utils_1 = require("../../../../project-graph/file-utils");
9
7
  const project_graph_builder_1 = require("../../../../project-graph/project-graph-builder");
10
- function buildExplicitPackageJsonDependencies(ctx) {
8
+ const json_1 = require("../../../../utils/json");
9
+ const path_1 = require("../../../../utils/path");
10
+ function buildExplicitPackageJsonDependencies(ctx, targetProjectLocator) {
11
11
  const res = [];
12
12
  let packageNameMap = undefined;
13
13
  const nodes = Object.values(ctx.projects);
@@ -16,7 +16,7 @@ function buildExplicitPackageJsonDependencies(ctx) {
16
16
  if (isPackageJsonAtProjectRoot(nodes, f.file)) {
17
17
  // we only create the package name map once and only if a package.json file changes
18
18
  packageNameMap = packageNameMap || createPackageNameMap(ctx.projects);
19
- processPackageJson(source, f.file, ctx, res, packageNameMap);
19
+ processPackageJson(source, f.file, ctx, targetProjectLocator, res, packageNameMap);
20
20
  }
21
21
  });
22
22
  });
@@ -27,7 +27,7 @@ function createPackageNameMap(projects) {
27
27
  const res = {};
28
28
  for (let projectName of Object.keys(projects)) {
29
29
  try {
30
- const packageJson = (0, json_1.parseJson)((0, file_utils_1.defaultFileRead)((0, path_1.join)(projects[projectName].root, 'package.json')));
30
+ const packageJson = (0, json_1.parseJson)((0, file_utils_1.defaultFileRead)((0, node_path_1.join)(projects[projectName].root, 'package.json')));
31
31
  res[packageJson.name ?? projectName] = projectName;
32
32
  }
33
33
  catch (e) { }
@@ -36,13 +36,12 @@ function createPackageNameMap(projects) {
36
36
  }
37
37
  function isPackageJsonAtProjectRoot(nodes, fileName) {
38
38
  return (fileName.endsWith('package.json') &&
39
- nodes.find((projectNode) => (0, path_2.joinPathFragments)(projectNode.root, 'package.json') === fileName));
39
+ nodes.find((projectNode) => (0, path_1.joinPathFragments)(projectNode.root, 'package.json') === fileName));
40
40
  }
41
- function processPackageJson(sourceProject, fileName, ctx, collectedDeps, packageNameMap) {
41
+ function processPackageJson(sourceProject, fileName, ctx, targetProjectLocator, collectedDeps, packageNameMap) {
42
42
  try {
43
43
  const deps = readDeps((0, json_1.parseJson)((0, file_utils_1.defaultFileRead)(fileName)));
44
- // the name matches the import path
45
- deps.forEach((d) => {
44
+ for (const d of Object.keys(deps)) {
46
45
  // package.json refers to another project in the monorepo
47
46
  if (packageNameMap[d]) {
48
47
  const dependency = {
@@ -53,30 +52,44 @@ function processPackageJson(sourceProject, fileName, ctx, collectedDeps, package
53
52
  };
54
53
  (0, project_graph_builder_1.validateDependency)(dependency, ctx);
55
54
  collectedDeps.push(dependency);
55
+ continue;
56
56
  }
57
- else if (ctx.externalNodes[`npm:${d}`]) {
58
- const dependency = {
59
- source: sourceProject,
60
- target: `npm:${d}`,
61
- sourceFile: fileName,
62
- type: project_graph_1.DependencyType.static,
63
- };
64
- (0, project_graph_builder_1.validateDependency)(dependency, ctx);
65
- collectedDeps.push(dependency);
57
+ const externalNodeName = targetProjectLocator.findNpmProjectFromImport(d, fileName);
58
+ if (!externalNodeName) {
59
+ continue;
66
60
  }
67
- });
61
+ const dependency = {
62
+ source: sourceProject,
63
+ target: externalNodeName,
64
+ sourceFile: fileName,
65
+ type: project_graph_1.DependencyType.static,
66
+ };
67
+ (0, project_graph_builder_1.validateDependency)(dependency, ctx);
68
+ collectedDeps.push(dependency);
69
+ }
68
70
  }
69
71
  catch (e) {
70
72
  if (process.env.NX_VERBOSE_LOGGING === 'true') {
71
- console.log(e);
73
+ console.error(e);
72
74
  }
73
75
  }
74
76
  }
75
77
  function readDeps(packageJson) {
76
- return [
77
- ...Object.keys(packageJson?.dependencies ?? {}),
78
- ...Object.keys(packageJson?.devDependencies ?? {}),
79
- ...Object.keys(packageJson?.peerDependencies ?? {}),
80
- ...Object.keys(packageJson?.optionalDependencies ?? {}),
78
+ const deps = {};
79
+ /**
80
+ * We process dependencies in a rough order of increasing importance such that if a dependency is listed in multiple
81
+ * sections, the version listed under the "most important" one wins, with production dependencies being the most important.
82
+ */
83
+ const depType = [
84
+ 'optionalDependencies',
85
+ 'peerDependencies',
86
+ 'devDependencies',
87
+ 'dependencies',
81
88
  ];
89
+ for (const type of depType) {
90
+ for (const [depName, depVersion] of Object.entries(packageJson[type] || {})) {
91
+ deps[depName] = depVersion;
92
+ }
93
+ }
94
+ return deps;
82
95
  }
@@ -1,3 +1,4 @@
1
1
  import { CreateDependenciesContext } from '../../../../project-graph/plugins';
2
2
  import { RawProjectGraphDependency } from '../../../../project-graph/project-graph-builder';
3
- export declare function buildExplicitTypeScriptDependencies(ctx: CreateDependenciesContext): RawProjectGraphDependency[];
3
+ import { TargetProjectLocator } from './target-project-locator';
4
+ export declare function buildExplicitTypeScriptDependencies(ctx: CreateDependenciesContext, targetProjectLocator: TargetProjectLocator): RawProjectGraphDependency[];
@@ -1,18 +1,19 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.buildExplicitTypeScriptDependencies = void 0;
4
- const target_project_locator_1 = require("./target-project-locator");
5
- const project_graph_1 = require("../../../../config/project-graph");
6
4
  const path_1 = require("path");
7
- const workspace_root_1 = require("../../../../utils/workspace-root");
8
- const path_2 = require("../../../../utils/path");
5
+ const project_graph_1 = require("../../../../config/project-graph");
9
6
  const project_graph_builder_1 = require("../../../../project-graph/project-graph-builder");
7
+ const path_2 = require("../../../../utils/path");
8
+ const workspace_root_1 = require("../../../../utils/workspace-root");
10
9
  function isRoot(projects, projectName) {
11
10
  return projects[projectName]?.root === '.';
12
11
  }
13
12
  function convertImportToDependency(importExpr, sourceFile, source, type, targetProjectLocator) {
14
- const target = targetProjectLocator.findProjectWithImport(importExpr, sourceFile) ??
15
- `npm:${importExpr}`;
13
+ const target = targetProjectLocator.findProjectFromImport(importExpr, sourceFile);
14
+ if (!target) {
15
+ return;
16
+ }
16
17
  return {
17
18
  source,
18
19
  target,
@@ -20,19 +21,7 @@ function convertImportToDependency(importExpr, sourceFile, source, type, targetP
20
21
  type,
21
22
  };
22
23
  }
23
- function buildExplicitTypeScriptDependencies(ctx) {
24
- // TODO: TargetProjectLocator is a public API, so we can't change the shape of it
25
- // We should eventually let it accept Record<string, ProjectConfiguration> s.t. we
26
- // don't have to reshape the CreateDependenciesContext here.
27
- const nodes = Object.fromEntries(Object.entries(ctx.projects).map(([key, config]) => [
28
- key,
29
- {
30
- name: key,
31
- type: null,
32
- data: config,
33
- },
34
- ]));
35
- const targetProjectLocator = new target_project_locator_1.TargetProjectLocator(nodes, ctx.externalNodes);
24
+ function buildExplicitTypeScriptDependencies(ctx, targetProjectLocator) {
36
25
  const res = [];
37
26
  const filesToProcess = {};
38
27
  const moduleExtensions = [
@@ -63,6 +52,9 @@ function buildExplicitTypeScriptDependencies(ctx) {
63
52
  const normalizedFilePath = (0, path_2.normalizePath)((0, path_1.relative)(workspace_root_1.workspaceRoot, file));
64
53
  for (const importExpr of staticImportExpressions) {
65
54
  const dependency = convertImportToDependency(importExpr, normalizedFilePath, sourceProject, project_graph_1.DependencyType.static, targetProjectLocator);
55
+ if (!dependency) {
56
+ continue;
57
+ }
66
58
  // TODO: These edges technically should be allowed but we need to figure out how to separate config files out from root
67
59
  if (isRoot(ctx.projects, dependency.source) ||
68
60
  !isRoot(ctx.projects, dependency.target)) {
@@ -71,6 +63,9 @@ function buildExplicitTypeScriptDependencies(ctx) {
71
63
  }
72
64
  for (const importExpr of dynamicImportExpressions) {
73
65
  const dependency = convertImportToDependency(importExpr, normalizedFilePath, sourceProject, project_graph_1.DependencyType.dynamic, targetProjectLocator);
66
+ if (!dependency) {
67
+ continue;
68
+ }
74
69
  // TODO: These edges technically should be allowed but we need to figure out how to separate config files out from root
75
70
  if (isRoot(ctx.projects, dependency.source) ||
76
71
  !isRoot(ctx.projects, dependency.target)) {
@@ -1,21 +1,37 @@
1
1
  import { ProjectGraphExternalNode, ProjectGraphProjectNode } from '../../../../config/project-graph';
2
+ /**
3
+ * The key is a combination of the package name and the workspace relative directory
4
+ * containing the file importing it e.g. `lodash__packages/my-lib`, the value is the
5
+ * resolved external node name from the project graph.
6
+ */
7
+ type NpmResolutionCache = Map<string, string>;
8
+ export declare function isBuiltinModuleImport(importExpr: string): boolean;
2
9
  export declare class TargetProjectLocator {
3
10
  private readonly nodes;
4
- private readonly externalNodes;
11
+ readonly externalNodes: Record<string, ProjectGraphExternalNode>;
12
+ private readonly npmResolutionCache;
5
13
  private projectRootMappings;
6
14
  private npmProjects;
7
15
  private tsConfig;
8
16
  private paths;
9
17
  private typescriptResolutionCache;
10
- private npmResolutionCache;
11
- constructor(nodes: Record<string, ProjectGraphProjectNode>, externalNodes: Record<string, ProjectGraphExternalNode>);
18
+ constructor(nodes: Record<string, ProjectGraphProjectNode>, externalNodes?: Record<string, ProjectGraphExternalNode>, npmResolutionCache?: NpmResolutionCache);
12
19
  /**
13
- * Find a project based on its import
20
+ * Resolve any workspace or external project that matches the given import expression,
21
+ * originating from the given filePath.
14
22
  *
15
23
  * @param importExpr
16
24
  * @param filePath
17
25
  */
18
- findProjectWithImport(importExpr: string, filePath: string): string;
26
+ findProjectFromImport(importExpr: string, filePath: string): string;
27
+ /**
28
+ * Resolve any external project that matches the given import expression,
29
+ * relative to the given file path.
30
+ *
31
+ * @param importExpr
32
+ * @param projectRoot
33
+ */
34
+ findNpmProjectFromImport(importExpr: string, fromFilePath: string): string | undefined;
19
35
  /**
20
36
  * Return file paths matching the import relative to the repo root
21
37
  * @param normalizedImportExpr
@@ -24,9 +40,9 @@ export declare class TargetProjectLocator {
24
40
  findPaths(normalizedImportExpr: string): string[] | undefined;
25
41
  private resolveImportWithTypescript;
26
42
  private resolveImportWithRequire;
27
- private findNpmPackage;
28
43
  private findProjectOfResolvedModule;
29
44
  private getAbsolutePath;
30
45
  private getRootTsConfig;
31
46
  private findMatchingProjectFiles;
32
47
  }
48
+ export {};
@@ -1,36 +1,60 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.TargetProjectLocator = void 0;
4
- const typescript_1 = require("../../utils/typescript");
3
+ exports.TargetProjectLocator = exports.isBuiltinModuleImport = void 0;
4
+ const node_module_1 = require("node:module");
5
+ const node_path_1 = require("node:path");
6
+ const find_project_for_path_1 = require("../../../../project-graph/utils/find-project-for-path");
5
7
  const fileutils_1 = require("../../../../utils/fileutils");
6
- const path_1 = require("path");
7
8
  const workspace_root_1 = require("../../../../utils/workspace-root");
8
- const module_1 = require("module");
9
- const find_project_for_path_1 = require("../../../../project-graph/utils/find-project-for-path");
9
+ const find_external_package_json_path_1 = require("../../utils/find-external-package-json-path");
10
+ const typescript_1 = require("../../utils/typescript");
11
+ /**
12
+ * Use a shared cache to avoid repeated npm package resolution work within the TargetProjectLocator.
13
+ */
14
+ const defaultNpmResolutionCache = new Map();
10
15
  const builtInModuleSet = new Set([
11
- ...module_1.builtinModules,
12
- ...module_1.builtinModules.map((x) => `node:${x}`),
16
+ ...node_module_1.builtinModules,
17
+ ...node_module_1.builtinModules.map((x) => `node:${x}`),
13
18
  ]);
19
+ function isBuiltinModuleImport(importExpr) {
20
+ const packageName = parsePackageNameFromImportExpression(importExpr);
21
+ return builtInModuleSet.has(packageName);
22
+ }
23
+ exports.isBuiltinModuleImport = isBuiltinModuleImport;
14
24
  class TargetProjectLocator {
15
- constructor(nodes, externalNodes) {
25
+ constructor(nodes, externalNodes = {}, npmResolutionCache = defaultNpmResolutionCache) {
16
26
  this.nodes = nodes;
17
27
  this.externalNodes = externalNodes;
28
+ this.npmResolutionCache = npmResolutionCache;
18
29
  this.projectRootMappings = (0, find_project_for_path_1.createProjectRootMappings)(this.nodes);
19
- this.npmProjects = filterRootExternalDependencies(this.externalNodes);
20
30
  this.tsConfig = this.getRootTsConfig();
21
31
  this.paths = this.tsConfig.config?.compilerOptions?.paths;
22
32
  this.typescriptResolutionCache = new Map();
23
- this.npmResolutionCache = new Map();
33
+ this.npmProjects = externalNodes;
34
+ /**
35
+ * Only the npm external nodes should be included.
36
+ *
37
+ * Unlike the raw externalNodes, ensure that the version is always set in the key
38
+ * for optimal lookup.
39
+ */
40
+ this.npmProjects = Object.values(externalNodes).reduce((acc, node) => {
41
+ if (node.type === 'npm') {
42
+ const key = `npm:${node.data.packageName}@${node.data.version}`;
43
+ acc[key] = node;
44
+ }
45
+ return acc;
46
+ }, {});
24
47
  }
25
48
  /**
26
- * Find a project based on its import
49
+ * Resolve any workspace or external project that matches the given import expression,
50
+ * originating from the given filePath.
27
51
  *
28
52
  * @param importExpr
29
53
  * @param filePath
30
54
  */
31
- findProjectWithImport(importExpr, filePath) {
55
+ findProjectFromImport(importExpr, filePath) {
32
56
  if ((0, fileutils_1.isRelativePath)(importExpr)) {
33
- const resolvedModule = path_1.posix.join((0, path_1.dirname)(filePath), importExpr);
57
+ const resolvedModule = node_path_1.posix.join((0, node_path_1.dirname)(filePath), importExpr);
34
58
  return this.findProjectOfResolvedModule(resolvedModule);
35
59
  }
36
60
  // find project using tsconfig paths
@@ -39,7 +63,7 @@ class TargetProjectLocator {
39
63
  const [path, paths] = results;
40
64
  for (let p of paths) {
41
65
  const r = p.endsWith('/*')
42
- ? (0, path_1.join)((0, path_1.dirname)(p), (0, path_1.relative)(path.replace(/\*$/, ''), importExpr))
66
+ ? (0, node_path_1.join)((0, node_path_1.dirname)(p), (0, node_path_1.relative)(path.replace(/\*$/, ''), importExpr))
43
67
  : p;
44
68
  const maybeResolvedProject = this.findProjectOfResolvedModule(r);
45
69
  if (maybeResolvedProject) {
@@ -47,14 +71,14 @@ class TargetProjectLocator {
47
71
  }
48
72
  }
49
73
  }
50
- if (builtInModuleSet.has(importExpr)) {
74
+ if (isBuiltinModuleImport(importExpr)) {
51
75
  this.npmResolutionCache.set(importExpr, null);
52
76
  return null;
53
77
  }
54
78
  // try to find npm package before using expensive typescript resolution
55
- const npmProject = this.findNpmPackage(importExpr);
56
- if (npmProject) {
57
- return npmProject;
79
+ const externalProject = this.findNpmProjectFromImport(importExpr, filePath);
80
+ if (externalProject) {
81
+ return externalProject;
58
82
  }
59
83
  if (this.tsConfig.config) {
60
84
  // TODO(meeroslav): this block is probably obsolete
@@ -74,6 +98,52 @@ class TargetProjectLocator {
74
98
  this.npmResolutionCache.set(importExpr, null);
75
99
  return null;
76
100
  }
101
+ /**
102
+ * Resolve any external project that matches the given import expression,
103
+ * relative to the given file path.
104
+ *
105
+ * @param importExpr
106
+ * @param projectRoot
107
+ */
108
+ findNpmProjectFromImport(importExpr, fromFilePath) {
109
+ const packageName = parsePackageNameFromImportExpression(importExpr);
110
+ let fullFilePath = fromFilePath;
111
+ let workspaceRelativeFilePath = fromFilePath;
112
+ if (fromFilePath.startsWith(workspace_root_1.workspaceRoot)) {
113
+ workspaceRelativeFilePath = fromFilePath.replace(workspace_root_1.workspaceRoot, '');
114
+ }
115
+ else {
116
+ fullFilePath = (0, node_path_1.join)(workspace_root_1.workspaceRoot, fromFilePath);
117
+ }
118
+ const fullDirPath = (0, node_path_1.dirname)(fullFilePath);
119
+ const workspaceRelativeDirPath = (0, node_path_1.dirname)(workspaceRelativeFilePath);
120
+ const npmImportForProject = `${packageName}__${workspaceRelativeDirPath}`;
121
+ if (this.npmResolutionCache.has(npmImportForProject)) {
122
+ return this.npmResolutionCache.get(npmImportForProject);
123
+ }
124
+ try {
125
+ // package.json refers to an external package, we do not match against the version found in there, we instead try and resolve the relevant package how node would
126
+ const externalPackageJsonPath = (0, find_external_package_json_path_1.findExternalPackageJsonPath)(packageName, fullDirPath);
127
+ // The external package.json path might be not be resolvable, e.g. if a reference has been added to a project package.json, but the install command has not been run yet.
128
+ if (!externalPackageJsonPath) {
129
+ return undefined;
130
+ }
131
+ const externalPackageJson = (0, fileutils_1.readJsonFile)(externalPackageJsonPath);
132
+ const npmProjectKey = `npm:${externalPackageJson.name}@${externalPackageJson.version}`;
133
+ if (!this.npmProjects[npmProjectKey]) {
134
+ return undefined;
135
+ }
136
+ const matchingExternalNode = this.npmProjects[npmProjectKey];
137
+ this.npmResolutionCache.set(npmImportForProject, matchingExternalNode.name);
138
+ return matchingExternalNode.name;
139
+ }
140
+ catch (e) {
141
+ if (process.env.NX_VERBOSE_LOGGING === 'true') {
142
+ console.error(e);
143
+ }
144
+ return undefined;
145
+ }
146
+ }
77
147
  /**
78
148
  * Return file paths matching the import relative to the repo root
79
149
  * @param normalizedImportExpr
@@ -113,23 +183,10 @@ class TargetProjectLocator {
113
183
  return;
114
184
  }
115
185
  resolveImportWithRequire(normalizedImportExpr, filePath) {
116
- return path_1.posix.relative(workspace_root_1.workspaceRoot, require.resolve(normalizedImportExpr, {
117
- paths: [(0, path_1.dirname)(filePath)],
186
+ return node_path_1.posix.relative(workspace_root_1.workspaceRoot, require.resolve(normalizedImportExpr, {
187
+ paths: [(0, node_path_1.dirname)(filePath)],
118
188
  }));
119
189
  }
120
- findNpmPackage(npmImport) {
121
- if (this.npmResolutionCache.has(npmImport)) {
122
- return this.npmResolutionCache.get(npmImport);
123
- }
124
- else {
125
- const pkg = this.npmProjects.find((pkg) => npmImport === pkg.data.packageName ||
126
- npmImport.startsWith(`${pkg.data.packageName}/`));
127
- if (pkg) {
128
- this.npmResolutionCache.set(npmImport, pkg.name);
129
- return pkg.name;
130
- }
131
- }
132
- }
133
190
  findProjectOfResolvedModule(resolvedModule) {
134
191
  if (resolvedModule.startsWith('node_modules/') ||
135
192
  resolvedModule.includes('/node_modules/')) {
@@ -142,7 +199,7 @@ class TargetProjectLocator {
142
199
  return importedProject ? importedProject.name : void 0;
143
200
  }
144
201
  getAbsolutePath(path) {
145
- return (0, path_1.join)(workspace_root_1.workspaceRoot, path);
202
+ return (0, node_path_1.join)(workspace_root_1.workspaceRoot, path);
146
203
  }
147
204
  getRootTsConfig() {
148
205
  const path = (0, typescript_1.getRootTsConfigFileName)();
@@ -166,18 +223,12 @@ class TargetProjectLocator {
166
223
  }
167
224
  }
168
225
  exports.TargetProjectLocator = TargetProjectLocator;
169
- // matches `npm:@scope/name`, `npm:name` but not `npm:@scope/name@version` and `npm:name@version`
170
- const ROOT_VERSION_PACKAGE_NAME_REGEX = /^npm:(?!.+@.+)/;
171
- function filterRootExternalDependencies(externalNodes) {
172
- if (!externalNodes) {
173
- return [];
174
- }
175
- const keys = Object.keys(externalNodes);
176
- const nodes = [];
177
- for (let i = 0; i < keys.length; i++) {
178
- if (keys[i].match(ROOT_VERSION_PACKAGE_NAME_REGEX)) {
179
- nodes.push(externalNodes[keys[i]]);
180
- }
226
+ function parsePackageNameFromImportExpression(importExpression) {
227
+ // Check if the package is scoped
228
+ if (importExpression.startsWith('@')) {
229
+ // For scoped packages, the package name is up to the second '/'
230
+ return importExpression.split('/').slice(0, 2).join('/');
181
231
  }
182
- return nodes;
232
+ // For unscoped packages, the package name is up to the first '/'
233
+ return importExpression.split('/')[0];
183
234
  }
@@ -0,0 +1,10 @@
1
+ /**
2
+ * In many cases the package.json will be directly resolvable, so we try that first.
3
+ * If, however, package exports are used and the package.json is not defined, we will
4
+ * need to resolve the main entry point of the package and traverse upwards to find the
5
+ * package.json.
6
+ *
7
+ * NOTE: Unit testing this code is currently impractical as it is not possible to mock
8
+ * require.resolve in jest https://github.com/jestjs/jest/issues/9543
9
+ */
10
+ export declare function findExternalPackageJsonPath(packageName: string, relativeToDir: string): string | null;
@@ -0,0 +1,42 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.findExternalPackageJsonPath = void 0;
4
+ const node_fs_1 = require("node:fs");
5
+ const node_path_1 = require("node:path");
6
+ /**
7
+ * In many cases the package.json will be directly resolvable, so we try that first.
8
+ * If, however, package exports are used and the package.json is not defined, we will
9
+ * need to resolve the main entry point of the package and traverse upwards to find the
10
+ * package.json.
11
+ *
12
+ * NOTE: Unit testing this code is currently impractical as it is not possible to mock
13
+ * require.resolve in jest https://github.com/jestjs/jest/issues/9543
14
+ */
15
+ function findExternalPackageJsonPath(packageName, relativeToDir) {
16
+ try {
17
+ return require.resolve((0, node_path_1.join)(packageName, 'package.json'), {
18
+ paths: [relativeToDir],
19
+ });
20
+ }
21
+ catch {
22
+ try {
23
+ // Resolve the main entry point of the package
24
+ const mainPath = require.resolve(packageName, {
25
+ paths: [relativeToDir],
26
+ });
27
+ let dir = (0, node_path_1.dirname)(mainPath);
28
+ while (dir !== (0, node_path_1.parse)(dir).root) {
29
+ const packageJsonPath = (0, node_path_1.join)(dir, 'package.json');
30
+ if ((0, node_fs_1.existsSync)(packageJsonPath)) {
31
+ return packageJsonPath;
32
+ }
33
+ dir = (0, node_path_1.dirname)(dir);
34
+ }
35
+ throw new Error(`Could not find package.json for ${packageName}`);
36
+ }
37
+ catch {
38
+ return null;
39
+ }
40
+ }
41
+ }
42
+ exports.findExternalPackageJsonPath = findExternalPackageJsonPath;
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.unparse = exports.isCacheableTask = exports.shouldStreamOutput = exports.getSerializedArgsForTask = exports.getPrintableCommandArgsForTask = exports.getCliPath = exports.calculateReverseDeps = exports.removeIdsFromGraph = exports.removeTasksFromTaskGraph = exports.getCustomHasher = exports.getExecutorForTask = exports.getExecutorNameForTask = exports.getTargetConfigurationForTask = exports.interpolate = exports.getOutputsForTargetAndConfiguration = exports.transformLegacyOutputs = exports.validateOutputs = exports.getOutputs = exports.expandDependencyConfigSyntaxSugar = exports.getDependencyConfigs = void 0;
4
4
  const output_1 = require("../utils/output");
5
5
  const path_1 = require("path");
6
+ const posix_1 = require("path/posix");
6
7
  const workspace_root_1 = require("../utils/workspace-root");
7
8
  const path_2 = require("../utils/path");
8
9
  const fileutils_1 = require("../utils/fileutils");
@@ -166,18 +167,32 @@ function getOutputsForTargetAndConfiguration(taskTargetOrTask, overridesOrNode,
166
167
  }
167
168
  }
168
169
  exports.getOutputsForTargetAndConfiguration = getOutputsForTargetAndConfiguration;
170
+ /**
171
+ * Matches portions of a string which need to be interpolated.
172
+ * Matches anything within curly braces, excluding the braces.
173
+ */
174
+ const replacementRegex = /{([\s\S]+?)}/g;
169
175
  function interpolate(template, data) {
176
+ // Path is absolute or doesn't need interpolation
177
+ if (template.startsWith('/') || !replacementRegex.test(template)) {
178
+ return template;
179
+ }
170
180
  if (template.includes('{workspaceRoot}', 1)) {
171
181
  throw new Error(`Output '${template}' is invalid. {workspaceRoot} can only be used at the beginning of the expression.`);
172
182
  }
173
183
  if (data.projectRoot == '.' && template.includes('{projectRoot}', 1)) {
174
184
  throw new Error(`Output '${template}' is invalid. When {projectRoot} is '.', it can only be used at the beginning of the expression.`);
175
185
  }
176
- let res = template.replace('{workspaceRoot}/', '');
186
+ const parts = template.split('/').map((s) => _interpolate(s, data));
187
+ return (0, posix_1.join)(...parts).replace('{workspaceRoot}/', '');
188
+ }
189
+ exports.interpolate = interpolate;
190
+ function _interpolate(template, data) {
191
+ let res = template;
177
192
  if (data.projectRoot == '.') {
178
- res = res.replace('{projectRoot}/', '');
193
+ res = res.replace('{projectRoot}', '');
179
194
  }
180
- return res.replace(/{([\s\S]+?)}/g, (match) => {
195
+ return res.replace(replacementRegex, (match) => {
181
196
  let value = data;
182
197
  let path = match.slice(1, -1).trim().split('.');
183
198
  for (let idx = 0; idx < path.length; idx++) {
@@ -189,7 +204,6 @@ function interpolate(template, data) {
189
204
  return value;
190
205
  });
191
206
  }
192
- exports.interpolate = interpolate;
193
207
  function getTargetConfigurationForTask(task, projectGraph) {
194
208
  const project = projectGraph.nodes[task.target.project].data;
195
209
  return project.targets[task.target.target];
@@ -226,7 +226,7 @@ function generateLinkOutput({ pluginName, name, type, }) {
226
226
  !pluginName.startsWith(nrwlPackagePrefix)) {
227
227
  return '';
228
228
  }
229
- const link = `https://nx.dev/packages/${pluginName.substring(pluginName.startsWith(nxPackagePrefix)
229
+ const link = `https://nx.dev/nx-api/${pluginName.substring(pluginName.startsWith(nxPackagePrefix)
230
230
  ? nxPackagePrefix.length
231
231
  : nrwlPackagePrefix.length)}/${type}/${name}`;
232
232
  return `\n\n${chalk.dim('Find more information and examples at:')} ${chalk.bold(link)}`;