diff --git a/docs/generated/devkit/CreateDependencies.md b/docs/generated/devkit/CreateDependencies.md index 505cf00ed1..3c9938169f 100644 --- a/docs/generated/devkit/CreateDependencies.md +++ b/docs/generated/devkit/CreateDependencies.md @@ -1,10 +1,10 @@ # Type alias: CreateDependencies -Ƭ **CreateDependencies**: (`context`: [`CreateDependenciesContext`](../../devkit/documents/CreateDependenciesContext)) => [`ProjectGraphDependencyWithFile`](../../devkit/documents/ProjectGraphDependencyWithFile)[] \| `Promise`<[`ProjectGraphDependencyWithFile`](../../devkit/documents/ProjectGraphDependencyWithFile)[]\> +Ƭ **CreateDependencies**: (`context`: [`CreateDependenciesContext`](../../devkit/documents/CreateDependenciesContext)) => [`RawProjectGraphDependency`](../../devkit/documents/RawProjectGraphDependency)[] \| `Promise`<[`RawProjectGraphDependency`](../../devkit/documents/RawProjectGraphDependency)[]\> #### Type declaration -▸ (`context`): [`ProjectGraphDependencyWithFile`](../../devkit/documents/ProjectGraphDependencyWithFile)[] \| `Promise`<[`ProjectGraphDependencyWithFile`](../../devkit/documents/ProjectGraphDependencyWithFile)[]\> +▸ (`context`): [`RawProjectGraphDependency`](../../devkit/documents/RawProjectGraphDependency)[] \| `Promise`<[`RawProjectGraphDependency`](../../devkit/documents/RawProjectGraphDependency)[]\> A function which parses files in the workspace to create dependencies in the [ProjectGraph](../../devkit/documents/ProjectGraph) Use [validateDependency](../../devkit/documents/validateDependency) to validate dependencies @@ -17,4 +17,4 @@ Use [validateDependency](../../devkit/documents/validateDependency) to validate ##### Returns -[`ProjectGraphDependencyWithFile`](../../devkit/documents/ProjectGraphDependencyWithFile)[] \| `Promise`<[`ProjectGraphDependencyWithFile`](../../devkit/documents/ProjectGraphDependencyWithFile)[]\> +[`RawProjectGraphDependency`](../../devkit/documents/RawProjectGraphDependency)[] \| `Promise`<[`RawProjectGraphDependency`](../../devkit/documents/RawProjectGraphDependency)[]\> diff --git a/docs/generated/devkit/CreateDependenciesContext.md b/docs/generated/devkit/CreateDependenciesContext.md index 42c55523a1..71595ae3bc 100644 --- a/docs/generated/devkit/CreateDependenciesContext.md +++ b/docs/generated/devkit/CreateDependenciesContext.md @@ -6,14 +6,23 @@ Context for [CreateDependencies](../../devkit/documents/CreateDependencies) ### Properties +- [externalNodes](../../devkit/documents/CreateDependenciesContext#externalnodes): Record<string, ProjectGraphExternalNode> - [fileMap](../../devkit/documents/CreateDependenciesContext#filemap): ProjectFileMap - [filesToProcess](../../devkit/documents/CreateDependenciesContext#filestoprocess): ProjectFileMap -- [graph](../../devkit/documents/CreateDependenciesContext#graph): ProjectGraph - [nxJsonConfiguration](../../devkit/documents/CreateDependenciesContext#nxjsonconfiguration): NxJsonConfiguration<string[] | "\*"> -- [projectsConfigurations](../../devkit/documents/CreateDependenciesContext#projectsconfigurations): ProjectsConfigurations +- [projects](../../devkit/documents/CreateDependenciesContext#projects): Record<string, ProjectConfiguration> +- [workspaceRoot](../../devkit/documents/CreateDependenciesContext#workspaceroot): string ## Properties +### externalNodes + +• `Readonly` **externalNodes**: `Record`<`string`, [`ProjectGraphExternalNode`](../../devkit/documents/ProjectGraphExternalNode)\> + +The external nodes that have been added to the graph. + +--- + ### fileMap • `Readonly` **fileMap**: [`ProjectFileMap`](../../devkit/documents/ProjectFileMap) @@ -30,14 +39,6 @@ Files changes since last invocation --- -### graph - -• `Readonly` **graph**: [`ProjectGraph`](../../devkit/documents/ProjectGraph) - -The current project graph, - ---- - ### nxJsonConfiguration • `Readonly` **nxJsonConfiguration**: [`NxJsonConfiguration`](../../devkit/documents/NxJsonConfiguration)<`string`[] \| `"*"`\> @@ -46,8 +47,14 @@ The `nx.json` configuration from the workspace --- -### projectsConfigurations +### projects -• `Readonly` **projectsConfigurations**: [`ProjectsConfigurations`](../../devkit/documents/ProjectsConfigurations) +• `Readonly` **projects**: `Record`<`string`, [`ProjectConfiguration`](../../devkit/documents/ProjectConfiguration)\> -The configuration of each project in the workspace +The configuration of each project in the workspace. + +--- + +### workspaceRoot + +• `Readonly` **workspaceRoot**: `string` diff --git a/docs/generated/devkit/DynamicDependency.md b/docs/generated/devkit/DynamicDependency.md new file mode 100644 index 0000000000..988c3017ed --- /dev/null +++ b/docs/generated/devkit/DynamicDependency.md @@ -0,0 +1,16 @@ +# Type alias: DynamicDependency + +Ƭ **DynamicDependency**: `Object` + +A dynamic [ProjectGraph](../../devkit/documents/ProjectGraph) dependency between 2 projects + +This type of dependency indicates the source project MAY OR MAY NOT load the target project. + +#### Type declaration + +| Name | Type | Description | +| :----------- | :---------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------- | +| `source` | `string` | The name of a [ProjectGraphProjectNode](../../devkit/documents/ProjectGraphProjectNode) depending on the target project | +| `sourceFile` | `string` | The path of a file (relative from the workspace root) where the dependency is made | +| `target` | `string` | The name of a [ProjectGraphProjectNode](../../devkit/documents/ProjectGraphProjectNode) that the source project depends on | +| `type` | typeof [`dynamic`](../../devkit/documents/DependencyType#dynamic) | - | diff --git a/docs/generated/devkit/ImplicitDependency.md b/docs/generated/devkit/ImplicitDependency.md new file mode 100644 index 0000000000..55b903358f --- /dev/null +++ b/docs/generated/devkit/ImplicitDependency.md @@ -0,0 +1,15 @@ +# Type alias: ImplicitDependency + +Ƭ **ImplicitDependency**: `Object` + +An implicit [ProjectGraph](../../devkit/documents/ProjectGraph) dependency between 2 projects + +This type of dependency indicates a connection without an explicit reference in code + +#### Type declaration + +| Name | Type | Description | +| :------- | :------------------------------------------------------------------ | :------------------------------------------------------------------------------------------------------------------------- | +| `source` | `string` | The name of a [ProjectGraphProjectNode](../../devkit/documents/ProjectGraphProjectNode) depending on the target project | +| `target` | `string` | The name of a [ProjectGraphProjectNode](../../devkit/documents/ProjectGraphProjectNode) that the source project depends on | +| `type` | typeof [`implicit`](../../devkit/documents/DependencyType#implicit) | - | diff --git a/docs/generated/devkit/ProjectGraphDependencyWithFile.md b/docs/generated/devkit/ProjectGraphDependencyWithFile.md deleted file mode 100644 index f8f9411b94..0000000000 --- a/docs/generated/devkit/ProjectGraphDependencyWithFile.md +++ /dev/null @@ -1,45 +0,0 @@ -# Interface: ProjectGraphDependencyWithFile - -A [ProjectGraph](../../devkit/documents/ProjectGraph) dependency between 2 projects -Optional: Specifies a file from where the dependency is made - -## Table of contents - -### Properties - -- [dependencyType](../../devkit/documents/ProjectGraphDependencyWithFile#dependencytype): DependencyType -- [source](../../devkit/documents/ProjectGraphDependencyWithFile#source): string -- [sourceFile](../../devkit/documents/ProjectGraphDependencyWithFile#sourcefile): string -- [target](../../devkit/documents/ProjectGraphDependencyWithFile#target): string - -## Properties - -### dependencyType - -• **dependencyType**: [`DependencyType`](../../devkit/documents/DependencyType) - -The type of dependency - ---- - -### source - -• **source**: `string` - -The name of a [ProjectGraphProjectNode](../../devkit/documents/ProjectGraphProjectNode) or [ProjectGraphExternalNode](../../devkit/documents/ProjectGraphExternalNode) depending on the target project - ---- - -### sourceFile - -• `Optional` **sourceFile**: `string` - -The path of a file (relative from the workspace root) where the dependency is made - ---- - -### target - -• **target**: `string` - -The name of a [ProjectGraphProjectNode](../../devkit/documents/ProjectGraphProjectNode) or [ProjectGraphExternalNode](../../devkit/documents/ProjectGraphExternalNode) that the source project depends on diff --git a/docs/generated/devkit/README.md b/docs/generated/devkit/README.md index a527f441b9..8cb7126335 100644 --- a/docs/generated/devkit/README.md +++ b/docs/generated/devkit/README.md @@ -43,7 +43,6 @@ It only uses language primitives and immutable objects - [ProjectFileMap](../../devkit/documents/ProjectFileMap) - [ProjectGraph](../../devkit/documents/ProjectGraph) - [ProjectGraphDependency](../../devkit/documents/ProjectGraphDependency) -- [ProjectGraphDependencyWithFile](../../devkit/documents/ProjectGraphDependencyWithFile) - [ProjectGraphExternalNode](../../devkit/documents/ProjectGraphExternalNode) - [ProjectGraphProcessorContext](../../devkit/documents/ProjectGraphProcessorContext) - [ProjectGraphProjectNode](../../devkit/documents/ProjectGraphProjectNode) @@ -66,10 +65,12 @@ It only uses language primitives and immutable objects - [CreateNodes](../../devkit/documents/CreateNodes) - [CreateNodesFunction](../../devkit/documents/CreateNodesFunction) - [CustomHasher](../../devkit/documents/CustomHasher) +- [DynamicDependency](../../devkit/documents/DynamicDependency) - [Executor](../../devkit/documents/Executor) - [Generator](../../devkit/documents/Generator) - [GeneratorCallback](../../devkit/documents/GeneratorCallback) - [Hasher](../../devkit/documents/Hasher) +- [ImplicitDependency](../../devkit/documents/ImplicitDependency) - [ImplicitDependencyEntry](../../devkit/documents/ImplicitDependencyEntry) - [NxPlugin](../../devkit/documents/NxPlugin) - [NxPluginV1](../../devkit/documents/NxPluginV1) @@ -78,6 +79,8 @@ It only uses language primitives and immutable objects - [ProjectGraphNode](../../devkit/documents/ProjectGraphNode) - [ProjectTargetConfigurator](../../devkit/documents/ProjectTargetConfigurator) - [ProjectType](../../devkit/documents/ProjectType) +- [RawProjectGraphDependency](../../devkit/documents/RawProjectGraphDependency) +- [StaticDependency](../../devkit/documents/StaticDependency) - [StringChange](../../devkit/documents/StringChange) - [TaskGraphExecutor](../../devkit/documents/TaskGraphExecutor) - [WorkspaceConfiguration](../../devkit/documents/WorkspaceConfiguration) diff --git a/docs/generated/devkit/RawProjectGraphDependency.md b/docs/generated/devkit/RawProjectGraphDependency.md new file mode 100644 index 0000000000..8718ec4bf9 --- /dev/null +++ b/docs/generated/devkit/RawProjectGraphDependency.md @@ -0,0 +1,7 @@ +# Type alias: RawProjectGraphDependency + +Ƭ **RawProjectGraphDependency**: [`ImplicitDependency`](../../devkit/documents/ImplicitDependency) \| [`StaticDependency`](../../devkit/documents/StaticDependency) \| [`DynamicDependency`](../../devkit/documents/DynamicDependency) + +A [ProjectGraph](../../devkit/documents/ProjectGraph) dependency between 2 projects + +See [DynamicDependency](../../devkit/documents/DynamicDependency), [ImplicitDependency](../../devkit/documents/ImplicitDependency), or [StaticDependency](../../devkit/documents/StaticDependency) diff --git a/docs/generated/devkit/StaticDependency.md b/docs/generated/devkit/StaticDependency.md new file mode 100644 index 0000000000..e3216d0c66 --- /dev/null +++ b/docs/generated/devkit/StaticDependency.md @@ -0,0 +1,18 @@ +# Type alias: StaticDependency + +Ƭ **StaticDependency**: `Object` + +A static [ProjectGraph](../../devkit/documents/ProjectGraph) dependency between 2 projects + +This type of dependency indicates the source project ALWAYS load the target project. + +NOTE: StaticDependency#sourceFile MUST be present unless the source is the name of a [ProjectGraphExternalNode](../../devkit/documents/ProjectGraphExternalNode) + +#### Type declaration + +| Name | Type | Description | +| :------------ | :-------------------------------------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `source` | `string` | The name of a [ProjectGraphProjectNode](../../devkit/documents/ProjectGraphProjectNode) or [ProjectGraphExternalNode](../../devkit/documents/ProjectGraphExternalNode) depending on the target project | +| `sourceFile?` | `string` | The path of a file (relative from the workspace root) where the dependency is made | +| `target` | `string` | The name of a [ProjectGraphProjectNode](../../devkit/documents/ProjectGraphProjectNode) or [ProjectGraphExternalNode](../../devkit/documents/ProjectGraphExternalNode) that the source project depends on | +| `type` | typeof [`static`](../../devkit/documents/DependencyType#static) | - | diff --git a/docs/generated/devkit/validateDependency.md b/docs/generated/devkit/validateDependency.md index 84c83fa02c..2f66a9e993 100644 --- a/docs/generated/devkit/validateDependency.md +++ b/docs/generated/devkit/validateDependency.md @@ -1,6 +1,6 @@ # Function: validateDependency -▸ **validateDependency**(`graph`, `dependency`): `void` +▸ **validateDependency**(`dependency`, `ctx`): `void` A function to validate dependencies in a [CreateDependencies](../../devkit/documents/CreateDependencies) function @@ -10,10 +10,10 @@ If the dependency is invalid. #### Parameters -| Name | Type | -| :----------- | :---------------------------------------------------------------------------------------- | -| `graph` | [`ProjectGraph`](../../devkit/documents/ProjectGraph) | -| `dependency` | [`ProjectGraphDependencyWithFile`](../../devkit/documents/ProjectGraphDependencyWithFile) | +| Name | Type | +| :----------- | :------------------------------------------------------------------------------ | +| `dependency` | [`RawProjectGraphDependency`](../../devkit/documents/RawProjectGraphDependency) | +| `ctx` | [`CreateDependenciesContext`](../../devkit/documents/CreateDependenciesContext) | #### Returns diff --git a/docs/generated/packages/devkit/documents/nx_devkit.md b/docs/generated/packages/devkit/documents/nx_devkit.md index a527f441b9..8cb7126335 100644 --- a/docs/generated/packages/devkit/documents/nx_devkit.md +++ b/docs/generated/packages/devkit/documents/nx_devkit.md @@ -43,7 +43,6 @@ It only uses language primitives and immutable objects - [ProjectFileMap](../../devkit/documents/ProjectFileMap) - [ProjectGraph](../../devkit/documents/ProjectGraph) - [ProjectGraphDependency](../../devkit/documents/ProjectGraphDependency) -- [ProjectGraphDependencyWithFile](../../devkit/documents/ProjectGraphDependencyWithFile) - [ProjectGraphExternalNode](../../devkit/documents/ProjectGraphExternalNode) - [ProjectGraphProcessorContext](../../devkit/documents/ProjectGraphProcessorContext) - [ProjectGraphProjectNode](../../devkit/documents/ProjectGraphProjectNode) @@ -66,10 +65,12 @@ It only uses language primitives and immutable objects - [CreateNodes](../../devkit/documents/CreateNodes) - [CreateNodesFunction](../../devkit/documents/CreateNodesFunction) - [CustomHasher](../../devkit/documents/CustomHasher) +- [DynamicDependency](../../devkit/documents/DynamicDependency) - [Executor](../../devkit/documents/Executor) - [Generator](../../devkit/documents/Generator) - [GeneratorCallback](../../devkit/documents/GeneratorCallback) - [Hasher](../../devkit/documents/Hasher) +- [ImplicitDependency](../../devkit/documents/ImplicitDependency) - [ImplicitDependencyEntry](../../devkit/documents/ImplicitDependencyEntry) - [NxPlugin](../../devkit/documents/NxPlugin) - [NxPluginV1](../../devkit/documents/NxPluginV1) @@ -78,6 +79,8 @@ It only uses language primitives and immutable objects - [ProjectGraphNode](../../devkit/documents/ProjectGraphNode) - [ProjectTargetConfigurator](../../devkit/documents/ProjectTargetConfigurator) - [ProjectType](../../devkit/documents/ProjectType) +- [RawProjectGraphDependency](../../devkit/documents/RawProjectGraphDependency) +- [StaticDependency](../../devkit/documents/StaticDependency) - [StringChange](../../devkit/documents/StringChange) - [TaskGraphExecutor](../../devkit/documents/TaskGraphExecutor) - [WorkspaceConfiguration](../../devkit/documents/WorkspaceConfiguration) diff --git a/docs/shared/recipes/plugins/project-graph-plugins.md b/docs/shared/recipes/plugins/project-graph-plugins.md index 0884a98637..d9fe4c4b73 100644 --- a/docs/shared/recipes/plugins/project-graph-plugins.md +++ b/docs/shared/recipes/plugins/project-graph-plugins.md @@ -90,16 +90,14 @@ The shape of the [`createDependencies`](/packages/devkit/documents/CreateDepende ```typescript export type CreateDependencies = ( context: CreateDependenciesContext -) => - | ProjectGraphDependencyWithFile[] - | Promise; +) => CandidateDependency[] | Promise; ``` In the `createDependencies` function, you can analyze the files in the workspace and return a list of dependencies. It's up to the plugin to determine how to analyze the files. This should also be exported from the plugin's entry point, as listed in `nx.json`. -Within the `CreateDependenciesContext`, you have access to the current project graph, the configuration of each project in the workspace, the `nx.json` configuration from the workspace, all files in the workspace, and files that have changed since the last invocation. It's important to utilize the `filesToProcess` parameter, as this will allow Nx to only reanalyze files that have changed since the last invocation, and reuse the information from the previous invocation for files that haven't changed. +Within the `CreateDependenciesContext`, you have access to the graph's external nodes, the configuration of each project in the workspace, the `nx.json` configuration from the workspace, all files in the workspace, and files that have changed since the last invocation. It's important to utilize the `filesToProcess` parameter, as this will allow Nx to only reanalyze files that have changed since the last invocation, and reuse the information from the previous invocation for files that haven't changed. -`@nx/devkit` exports a function called `validateDependency` which can be used to validate a dependency. This function takes in a `ProjectGraphDependencyWithFile` and a `ProjectGraph` and throws an error if the dependency is invalid. This function is called when the returned dependencies are merged with the existing project graph, but may be useful to call within your plugin to validate dependencies before returning them when debugging. +`@nx/devkit` exports a function called `validateDependency` which can be used to validate a dependency. This function takes in a `CandidateDependency` and the `CreateDependenciesContext` and throws an error if the dependency is invalid. This function is called when the returned dependencies are merged with the existing project graph, but may be useful to call within your plugin to validate dependencies before returning them when debugging. The dependencies can be of three types: @@ -184,7 +182,7 @@ export const createNodes: CreateNodes = (ctx) => { dependencyType: DependencyType.static, }; } - validateDependency(ctx.graph, newDependency); + validateDependency(newDependency, ctx); results.push(newDependency); } } diff --git a/packages/nx/src/devkit-exports.ts b/packages/nx/src/devkit-exports.ts index 4b894196bb..e1b81a185e 100644 --- a/packages/nx/src/devkit-exports.ts +++ b/packages/nx/src/devkit-exports.ts @@ -156,7 +156,10 @@ export { DependencyType } from './config/project-graph'; */ export { ProjectGraphBuilder, - ProjectGraphDependencyWithFile, + RawProjectGraphDependency, + DynamicDependency, + ImplicitDependency, + StaticDependency, validateDependency, } from './project-graph/project-graph-builder'; diff --git a/packages/nx/src/plugins/js/index.ts b/packages/nx/src/plugins/js/index.ts index 5898c83bc1..05c1848856 100644 --- a/packages/nx/src/plugins/js/index.ts +++ b/packages/nx/src/plugins/js/index.ts @@ -19,7 +19,7 @@ import { } from './lock-file/lock-file'; import { buildExplicitDependencies } from './project-graph/build-dependencies/build-dependencies'; import { jsPluginConfig } from './utils/config'; -import { ProjectGraphDependencyWithFile } from '../../project-graph/project-graph-builder'; +import { RawProjectGraphDependency } from '../../project-graph/project-graph-builder'; import { hashArray } from '../../hasher/file-hasher'; import { detectPackageManager } from '../../utils/package-manager'; import { workspaceRoot } from '../../utils/workspace-root'; @@ -29,7 +29,7 @@ export const name = 'nx-js-graph-plugin'; interface ParsedLockFile { externalNodes?: ProjectGraph['externalNodes']; - dependencies?: ProjectGraphDependencyWithFile[]; + dependencies?: RawProjectGraphDependency[]; } let parsedLockFile: ParsedLockFile = {}; @@ -80,7 +80,7 @@ export const createDependencies: CreateDependencies = ( const packageManager = detectPackageManager(workspaceRoot); - let lockfileDependencies: ProjectGraphDependencyWithFile[] = []; + let lockfileDependencies: RawProjectGraphDependency[] = []; // lockfile may not exist yet if ( pluginConfig.analyzeLockfile && @@ -98,7 +98,7 @@ export const createDependencies: CreateDependencies = ( packageManager, lockFileContents, lockFileHash, - ctx.graph + ctx ); parsedLockFile.dependencies = lockfileDependencies; diff --git a/packages/nx/src/plugins/js/lock-file/lock-file.ts b/packages/nx/src/plugins/js/lock-file/lock-file.ts index f2ec5549ab..017a92b5f3 100644 --- a/packages/nx/src/plugins/js/lock-file/lock-file.ts +++ b/packages/nx/src/plugins/js/lock-file/lock-file.ts @@ -15,10 +15,7 @@ import { ProjectGraph, ProjectGraphExternalNode, } from '../../../config/project-graph'; -import { - ProjectGraphBuilder, - ProjectGraphDependencyWithFile, -} from '../../../project-graph/project-graph-builder'; +import { RawProjectGraphDependency } from '../../../project-graph/project-graph-builder'; import { PackageJson } from '../../../utils/package-json'; import { output } from '../../../utils/output'; @@ -40,6 +37,7 @@ import { import { pruneProjectGraph } from './project-graph-pruning'; import { normalizePackageJson } from './utils/package-json'; import { readJsonFile } from '../../../utils/fileutils'; +import { CreateDependenciesContext } from '../../../utils/nx-plugin'; const YARN_LOCK_FILE = 'yarn.lock'; const NPM_LOCK_FILE = 'package-lock.json'; @@ -88,17 +86,17 @@ export function getLockFileDependencies( packageManager: PackageManager, contents: string, lockFileHash: string, - projectGraph: ProjectGraph -): ProjectGraphDependencyWithFile[] { + context: CreateDependenciesContext +): RawProjectGraphDependency[] { try { if (packageManager === 'yarn') { - return getYarnLockfileDependencies(contents, lockFileHash, projectGraph); + return getYarnLockfileDependencies(contents, lockFileHash, context); } if (packageManager === 'pnpm') { - return getPnpmLockfileDependencies(contents, lockFileHash, projectGraph); + return getPnpmLockfileDependencies(contents, lockFileHash, context); } if (packageManager === 'npm') { - return getNpmLockfileDependencies(contents, lockFileHash, projectGraph); + return getNpmLockfileDependencies(contents, lockFileHash, context); } } catch (e) { if (!isPostInstallProcess()) { diff --git a/packages/nx/src/plugins/js/lock-file/npm-parser.spec.ts b/packages/nx/src/plugins/js/lock-file/npm-parser.spec.ts index 1de96292f5..240e6e6396 100644 --- a/packages/nx/src/plugins/js/lock-file/npm-parser.spec.ts +++ b/packages/nx/src/plugins/js/lock-file/npm-parser.spec.ts @@ -8,6 +8,7 @@ import { pruneProjectGraph } from './project-graph-pruning'; import { vol } from 'memfs'; import { ProjectGraph } from '../../../config/project-graph'; import { ProjectGraphBuilder } from '../../../project-graph/project-graph-builder'; +import { CreateDependenciesContext } from '../../../utils/nx-plugin'; jest.mock('fs', () => { const memFs = require('memfs').fs; @@ -36,15 +37,23 @@ describe('NPM lock file utility', () => { JSON.stringify(rootLockFile), hash ); - const pg = { - nodes: {}, + const ctx: CreateDependenciesContext = { + projects: {}, + externalNodes, + fileMap: {}, + filesToProcess: {}, + nxJsonConfiguration: null, + workspaceRoot: '/virtual', + }; + const pg: ProjectGraph = { dependencies: {}, + nodes: {}, externalNodes, }; const dependencies = getNpmLockfileDependencies( JSON.stringify(rootLockFile), hash, - pg + ctx ); const builder = new ProjectGraphBuilder(pg); @@ -52,8 +61,8 @@ describe('NPM lock file utility', () => { builder.addDependency( dep.source, dep.target, - dep.dependencyType, - dep.sourceFile + dep.type, + 'sourceFile' in dep ? dep.sourceFile : null ); } graph = builder.getUpdatedProjectGraph(); @@ -84,10 +93,18 @@ describe('NPM lock file utility', () => { dependencies: {}, externalNodes, }; + const ctx: CreateDependenciesContext = { + projects: {}, + externalNodes, + fileMap: {}, + filesToProcess: {}, + nxJsonConfiguration: null, + workspaceRoot: '/virtual', + }; const dependencies = getNpmLockfileDependencies( JSON.stringify(appLockFile), hash, - pg + ctx ); const builder = new ProjectGraphBuilder(pg); @@ -95,8 +112,8 @@ describe('NPM lock file utility', () => { builder.addDependency( dep.source, dep.target, - dep.dependencyType, - dep.sourceFile + dep.type, + 'sourceFile' in dep ? dep.sourceFile : null ); } const appGraph = builder.getUpdatedProjectGraph(); @@ -155,10 +172,18 @@ describe('NPM lock file utility', () => { dependencies: {}, externalNodes, }; + const ctx: CreateDependenciesContext = { + projects: {}, + externalNodes, + fileMap: {}, + filesToProcess: {}, + nxJsonConfiguration: null, + workspaceRoot: '/virtual', + }; const dependencies = getNpmLockfileDependencies( JSON.stringify(rootLockFile), hash, - pg + ctx ); const builder = new ProjectGraphBuilder(pg); @@ -166,8 +191,8 @@ describe('NPM lock file utility', () => { builder.addDependency( dep.source, dep.target, - dep.dependencyType, - dep.sourceFile + dep.type, + 'sourceFile' in dep ? dep.sourceFile : null ); } const graph = builder.getUpdatedProjectGraph(); @@ -237,10 +262,18 @@ describe('NPM lock file utility', () => { dependencies: {}, externalNodes, }; + const ctx: CreateDependenciesContext = { + projects: {}, + externalNodes, + fileMap: {}, + filesToProcess: {}, + nxJsonConfiguration: null, + workspaceRoot: '/virtual', + }; const dependencies = getNpmLockfileDependencies( JSON.stringify(rootV2LockFile), hash, - pg + ctx ); const builder = new ProjectGraphBuilder(pg); @@ -248,8 +281,8 @@ describe('NPM lock file utility', () => { builder.addDependency( dep.source, dep.target, - dep.dependencyType, - dep.sourceFile + dep.type, + 'sourceFile' in dep ? dep.sourceFile : null ); } const graph = builder.getUpdatedProjectGraph(); @@ -359,10 +392,18 @@ describe('NPM lock file utility', () => { dependencies: {}, externalNodes, }; + const ctx: CreateDependenciesContext = { + projects: {}, + externalNodes, + fileMap: {}, + filesToProcess: {}, + nxJsonConfiguration: null, + workspaceRoot: '/virtual', + }; const dependencies = getNpmLockfileDependencies( JSON.stringify(rootV2LockFile), hash, - pg + ctx ); const builder = new ProjectGraphBuilder(pg); @@ -370,8 +411,8 @@ describe('NPM lock file utility', () => { builder.addDependency( dep.source, dep.target, - dep.dependencyType, - dep.sourceFile + dep.type, + 'sourceFile' in dep ? dep.sourceFile : null ); } const graph = builder.getUpdatedProjectGraph(); @@ -470,10 +511,18 @@ describe('NPM lock file utility', () => { dependencies: {}, externalNodes, }; + const ctx: CreateDependenciesContext = { + projects: {}, + externalNodes, + fileMap: {}, + filesToProcess: {}, + nxJsonConfiguration: null, + workspaceRoot: '/virtual', + }; const dependencies = getNpmLockfileDependencies( JSON.stringify(rootLockFile), hash, - pg + ctx ); const builder = new ProjectGraphBuilder(pg); @@ -481,8 +530,8 @@ describe('NPM lock file utility', () => { builder.addDependency( dep.source, dep.target, - dep.dependencyType, - dep.sourceFile + dep.type, + 'sourceFile' in dep ? dep.sourceFile : null ); } const graph = builder.getUpdatedProjectGraph(); @@ -505,10 +554,18 @@ describe('NPM lock file utility', () => { dependencies: {}, externalNodes, }; + const ctx: CreateDependenciesContext = { + projects: {}, + externalNodes, + fileMap: {}, + filesToProcess: {}, + nxJsonConfiguration: null, + workspaceRoot: '/virtual', + }; const dependencies = getNpmLockfileDependencies( JSON.stringify(rootLockFile), hash, - pg + ctx ); const builder = new ProjectGraphBuilder(pg); @@ -516,8 +573,8 @@ describe('NPM lock file utility', () => { builder.addDependency( dep.source, dep.target, - dep.dependencyType, - dep.sourceFile + dep.type, + 'sourceFile' in dep ? dep.sourceFile : null ); } const graph = builder.getUpdatedProjectGraph(); @@ -544,10 +601,18 @@ describe('NPM lock file utility', () => { dependencies: {}, externalNodes, }; + const ctx: CreateDependenciesContext = { + projects: {}, + externalNodes, + fileMap: {}, + filesToProcess: {}, + nxJsonConfiguration: null, + workspaceRoot: '/virtual', + }; const dependencies = getNpmLockfileDependencies( JSON.stringify(lockFile), hash, - pg + ctx ); const builder = new ProjectGraphBuilder(pg); @@ -555,8 +620,8 @@ describe('NPM lock file utility', () => { builder.addDependency( dep.source, dep.target, - dep.dependencyType, - dep.sourceFile + dep.type, + 'sourceFile' in dep ? dep.sourceFile : null ); } const graph = builder.getUpdatedProjectGraph(); @@ -594,10 +659,18 @@ describe('NPM lock file utility', () => { dependencies: {}, externalNodes, }; + const ctx: CreateDependenciesContext = { + projects: {}, + externalNodes, + fileMap: {}, + filesToProcess: {}, + nxJsonConfiguration: null, + workspaceRoot: '/virtual', + }; const dependencies = getNpmLockfileDependencies( JSON.stringify(rootLockFile), hash, - pg + ctx ); const builder = new ProjectGraphBuilder(pg); @@ -605,8 +678,8 @@ describe('NPM lock file utility', () => { builder.addDependency( dep.source, dep.target, - dep.dependencyType, - dep.sourceFile + dep.type, + 'sourceFile' in dep ? dep.sourceFile : null ); } const graph = builder.getUpdatedProjectGraph(); @@ -646,10 +719,18 @@ describe('NPM lock file utility', () => { dependencies: {}, externalNodes, }; + const ctx: CreateDependenciesContext = { + projects: {}, + externalNodes, + fileMap: {}, + filesToProcess: {}, + nxJsonConfiguration: null, + workspaceRoot: '/virtual', + }; const dependencies = getNpmLockfileDependencies( JSON.stringify(rootLockFile), hash, - pg + ctx ); const builder = new ProjectGraphBuilder(pg); @@ -657,8 +738,8 @@ describe('NPM lock file utility', () => { builder.addDependency( dep.source, dep.target, - dep.dependencyType, - dep.sourceFile + dep.type, + 'sourceFile' in dep ? dep.sourceFile : null ); } const graph = builder.getUpdatedProjectGraph(); diff --git a/packages/nx/src/plugins/js/lock-file/npm-parser.ts b/packages/nx/src/plugins/js/lock-file/npm-parser.ts index 0ded7a8e21..c048ce515c 100644 --- a/packages/nx/src/plugins/js/lock-file/npm-parser.ts +++ b/packages/nx/src/plugins/js/lock-file/npm-parser.ts @@ -4,7 +4,7 @@ import { workspaceRoot } from '../../../utils/workspace-root'; import { reverse } from '../../../project-graph/operators'; import { NormalizedPackageJson } from './utils/package-json'; import { - ProjectGraphDependencyWithFile, + RawProjectGraphDependency, validateDependency, } from '../../../project-graph/project-graph-builder'; import { @@ -13,6 +13,7 @@ import { ProjectGraphExternalNode, } from '../../../config/project-graph'; import { hashArray } from '../../../hasher/file-hasher'; +import { CreateDependenciesContext } from '../../../utils/nx-plugin'; /** * NPM @@ -87,14 +88,14 @@ export function getNpmLockfileNodes( export function getNpmLockfileDependencies( lockFileContent: string, lockFileHash: string, - projectGraph: ProjectGraph + ctx: CreateDependenciesContext ) { const data = parsePackageLockFile( lockFileContent, lockFileHash ) as NpmLockFile; - return getDependencies(data, keyMap, projectGraph); + return getDependencies(data, keyMap, ctx); } function getNodes( @@ -246,9 +247,9 @@ function findV3Version(snapshot: NpmDependencyV3, packageName: string): string { function getDependencies( data: NpmLockFile, keyMap: Map, - projectGraph: ProjectGraph -): ProjectGraphDependencyWithFile[] { - const dependencies: ProjectGraphDependencyWithFile[] = []; + ctx: CreateDependenciesContext +): RawProjectGraphDependency[] { + const dependencies: RawProjectGraphDependency[] = []; if (data.lockfileVersion > 1) { Object.entries(data.packages).forEach(([path, snapshot]) => { // we are skipping workspaces packages @@ -265,12 +266,12 @@ function getDependencies( Object.entries(section).forEach(([name, versionRange]) => { const target = findTarget(path, keyMap, name, versionRange); if (target) { - const dep = { + const dep: RawProjectGraphDependency = { source: sourceName, target: target.name, - dependencyType: DependencyType.static, + type: DependencyType.static, }; - validateDependency(projectGraph, dep); + validateDependency(dep, ctx); dependencies.push(dep); } }); @@ -284,7 +285,7 @@ function getDependencies( snapshot, dependencies, keyMap, - projectGraph + ctx ); }); } @@ -326,21 +327,21 @@ function findTarget( function addV1NodeDependencies( path: string, snapshot: NpmDependencyV1, - dependencies: ProjectGraphDependencyWithFile[], + dependencies: RawProjectGraphDependency[], keyMap: Map, - projectGraph: ProjectGraph + ctx: CreateDependenciesContext ) { if (keyMap.has(path) && snapshot.requires) { const source = keyMap.get(path).name; Object.entries(snapshot.requires).forEach(([name, versionRange]) => { const target = findTarget(path, keyMap, name, versionRange); if (target) { - const dep = { + const dep: RawProjectGraphDependency = { source: source, target: target.name, - dependencyType: DependencyType.static, + type: DependencyType.static, }; - validateDependency(projectGraph, dep); + validateDependency(dep, ctx); dependencies.push(dep); } }); @@ -353,7 +354,7 @@ function addV1NodeDependencies( depSnapshot, dependencies, keyMap, - projectGraph + ctx ); }); } @@ -363,12 +364,12 @@ function addV1NodeDependencies( Object.entries(peerDependencies).forEach(([depName, depSpec]) => { const target = findTarget(path, keyMap, depName, depSpec); if (target) { - const dep = { + const dep: RawProjectGraphDependency = { source: node.name, target: target.name, - dependencyType: DependencyType.static, + type: DependencyType.static, }; - validateDependency(projectGraph, dep); + validateDependency(dep, ctx); dependencies.push(dep); } }); diff --git a/packages/nx/src/plugins/js/lock-file/pnpm-parser.spec.ts b/packages/nx/src/plugins/js/lock-file/pnpm-parser.spec.ts index 5443ae8a03..ffbe169289 100644 --- a/packages/nx/src/plugins/js/lock-file/pnpm-parser.spec.ts +++ b/packages/nx/src/plugins/js/lock-file/pnpm-parser.spec.ts @@ -9,8 +9,9 @@ import { vol } from 'memfs'; import { pruneProjectGraph } from './project-graph-pruning'; import { ProjectGraphBuilder, - ProjectGraphDependencyWithFile, + RawProjectGraphDependency, } from '../../../project-graph/project-graph-builder'; +import { CreateDependenciesContext } from '../../../utils/nx-plugin'; jest.mock('fs', () => { const memFs = require('memfs').fs; @@ -127,7 +128,7 @@ describe('pnpm LockFile utility', () => { }); let externalNodes: ProjectGraph['externalNodes']; - let dependencies: ProjectGraphDependencyWithFile[]; + let dependencies: RawProjectGraphDependency[]; let graph: ProjectGraph; let lockFile: string; @@ -147,19 +148,23 @@ describe('pnpm LockFile utility', () => { dependencies: {}, externalNodes, }; - dependencies = getPnpmLockfileDependencies( - lockFile, - lockFileHash, - graph - ); + const ctx: CreateDependenciesContext = { + projects: {}, + externalNodes, + fileMap: {}, + filesToProcess: {}, + nxJsonConfiguration: null, + workspaceRoot: '/virtual', + }; + dependencies = getPnpmLockfileDependencies(lockFile, lockFileHash, ctx); const builder = new ProjectGraphBuilder(graph); for (const dep of dependencies) { builder.addDependency( dep.source, dep.target, - dep.dependencyType, - dep.sourceFile + dep.type, + 'sourceFile' in dep ? dep.sourceFile : null ); } graph = builder.getUpdatedProjectGraph(); @@ -204,19 +209,23 @@ describe('pnpm LockFile utility', () => { dependencies: {}, externalNodes, }; - dependencies = getPnpmLockfileDependencies( - lockFile, - lockFileHash, - graph - ); + const ctx: CreateDependenciesContext = { + projects: {}, + externalNodes, + fileMap: {}, + filesToProcess: {}, + nxJsonConfiguration: null, + workspaceRoot: '/virtual', + }; + dependencies = getPnpmLockfileDependencies(lockFile, lockFileHash, ctx); const builder = new ProjectGraphBuilder(graph); for (const dep of dependencies) { builder.addDependency( dep.source, dep.target, - dep.dependencyType, - dep.sourceFile + dep.type, + 'sourceFile' in dep ? dep.sourceFile : null ); } graph = builder.getUpdatedProjectGraph(); @@ -247,10 +256,18 @@ describe('pnpm LockFile utility', () => { dependencies: {}, externalNodes, }; + const appCtx: CreateDependenciesContext = { + projects: {}, + externalNodes, + fileMap: {}, + filesToProcess: {}, + nxJsonConfiguration: null, + workspaceRoot: '/virtual', + }; const dependencies = getPnpmLockfileDependencies( appLockFile, appLockFileHash, - appGraph + appCtx ); const builder = new ProjectGraphBuilder(appGraph); @@ -258,8 +275,8 @@ describe('pnpm LockFile utility', () => { builder.addDependency( dep.source, dep.target, - dep.dependencyType, - dep.sourceFile + dep.type, + 'sourceFile' in dep ? dep.sourceFile : null ); } appGraph = builder.getUpdatedProjectGraph(); @@ -317,10 +334,18 @@ describe('pnpm LockFile utility', () => { dependencies: {}, externalNodes, }; + const ctx: CreateDependenciesContext = { + projects: {}, + externalNodes, + fileMap: {}, + filesToProcess: {}, + nxJsonConfiguration: null, + workspaceRoot: '/virtual', + }; const dependencies = getPnpmLockfileDependencies( lockFile, lockFileHash, - graph + ctx ); const builder = new ProjectGraphBuilder(graph); @@ -328,8 +353,8 @@ describe('pnpm LockFile utility', () => { builder.addDependency( dep.source, dep.target, - dep.dependencyType, - dep.sourceFile + dep.type, + 'sourceFile' in dep ? dep.sourceFile : null ); } graph = builder.getUpdatedProjectGraph(); @@ -420,11 +445,18 @@ describe('pnpm LockFile utility', () => { dependencies: {}, externalNodes, }; - + const ctx: CreateDependenciesContext = { + projects: {}, + externalNodes, + fileMap: {}, + filesToProcess: {}, + nxJsonConfiguration: null, + workspaceRoot: '/virtual', + }; const dependencies = getPnpmLockfileDependencies( lockFile, lockFileHash, - graph + ctx ); const builder = new ProjectGraphBuilder(graph); @@ -432,8 +464,8 @@ describe('pnpm LockFile utility', () => { builder.addDependency( dep.source, dep.target, - dep.dependencyType, - dep.sourceFile + dep.type, + 'sourceFile' in dep ? dep.sourceFile : null ); } graph = builder.getUpdatedProjectGraph(); @@ -481,10 +513,18 @@ describe('pnpm LockFile utility', () => { dependencies: {}, externalNodes, }; + const ctx: CreateDependenciesContext = { + projects: {}, + externalNodes, + fileMap: {}, + filesToProcess: {}, + nxJsonConfiguration: null, + workspaceRoot: '/virtual', + }; const dependencies = getPnpmLockfileDependencies( lockFile, lockFileHash, - graph + ctx ); const builder = new ProjectGraphBuilder(graph); @@ -492,8 +532,8 @@ describe('pnpm LockFile utility', () => { builder.addDependency( dep.source, dep.target, - dep.dependencyType, - dep.sourceFile + dep.type, + 'sourceFile' in dep ? dep.sourceFile : null ); } graph = builder.getUpdatedProjectGraph(); @@ -528,10 +568,18 @@ describe('pnpm LockFile utility', () => { dependencies: {}, externalNodes, }; + const ctx: CreateDependenciesContext = { + projects: {}, + externalNodes, + fileMap: {}, + filesToProcess: {}, + nxJsonConfiguration: null, + workspaceRoot: '/virtual', + }; const dependencies = getPnpmLockfileDependencies( lockFile, lockFileHash, - graph + ctx ); const builder = new ProjectGraphBuilder(graph); @@ -539,8 +587,8 @@ describe('pnpm LockFile utility', () => { builder.addDependency( dep.source, dep.target, - dep.dependencyType, - dep.sourceFile + dep.type, + 'sourceFile' in dep ? dep.sourceFile : null ); } graph = builder.getUpdatedProjectGraph(); @@ -588,10 +636,18 @@ describe('pnpm LockFile utility', () => { dependencies: {}, externalNodes, }; + const ctx: CreateDependenciesContext = { + projects: {}, + externalNodes, + fileMap: {}, + filesToProcess: {}, + nxJsonConfiguration: null, + workspaceRoot: '/virtual', + }; const dependencies = getPnpmLockfileDependencies( lockFile, lockFileHash, - graph + ctx ); const builder = new ProjectGraphBuilder(graph); @@ -599,8 +655,8 @@ describe('pnpm LockFile utility', () => { builder.addDependency( dep.source, dep.target, - dep.dependencyType, - dep.sourceFile + dep.type, + 'sourceFile' in dep ? dep.sourceFile : null ); } graph = builder.getUpdatedProjectGraph(); @@ -659,10 +715,18 @@ describe('pnpm LockFile utility', () => { dependencies: {}, externalNodes, }; + const ctx: CreateDependenciesContext = { + projects: {}, + externalNodes, + fileMap: {}, + filesToProcess: {}, + nxJsonConfiguration: null, + workspaceRoot: '/virtual', + }; const dependencies = getPnpmLockfileDependencies( lockFile, lockFileHash, - graph + ctx ); const builder = new ProjectGraphBuilder(graph); @@ -670,8 +734,8 @@ describe('pnpm LockFile utility', () => { builder.addDependency( dep.source, dep.target, - dep.dependencyType, - dep.sourceFile + dep.type, + 'sourceFile' in dep ? dep.sourceFile : null ); } graph = builder.getUpdatedProjectGraph(); diff --git a/packages/nx/src/plugins/js/lock-file/pnpm-parser.ts b/packages/nx/src/plugins/js/lock-file/pnpm-parser.ts index 851f5f1241..8f6a6692cc 100644 --- a/packages/nx/src/plugins/js/lock-file/pnpm-parser.ts +++ b/packages/nx/src/plugins/js/lock-file/pnpm-parser.ts @@ -16,7 +16,7 @@ import { } from './utils/package-json'; import { sortObjectByKeys } from '../../../utils/object-sort'; import { - ProjectGraphDependencyWithFile, + RawProjectGraphDependency, validateDependency, } from '../../../project-graph/project-graph-builder'; import { @@ -25,6 +25,7 @@ import { ProjectGraphExternalNode, } from '../../../config/project-graph'; import { hashArray } from '../../../hasher/file-hasher'; +import { CreateDependenciesContext } from '../../../utils/nx-plugin'; // we use key => node map to avoid duplicate work when parsing keys let keyMap = new Map(); @@ -56,12 +57,12 @@ export function getPnpmLockfileNodes( export function getPnpmLockfileDependencies( lockFileContent: string, lockFileHash: string, - projectGraph: ProjectGraph + ctx: CreateDependenciesContext ) { const data = parsePnpmLockFile(lockFileContent, lockFileHash); const isV6 = isV6Lockfile(data); - return getDependencies(data, keyMap, isV6, projectGraph); + return getDependencies(data, keyMap, isV6, ctx); } function getNodes( @@ -157,9 +158,9 @@ function getDependencies( data: Lockfile, keyMap: Map, isV6: boolean, - projectGraph: ProjectGraph -): ProjectGraphDependencyWithFile[] { - const results: ProjectGraphDependencyWithFile[] = []; + ctx: CreateDependenciesContext +): RawProjectGraphDependency[] { + const results: RawProjectGraphDependency[] = []; Object.entries(data.packages).forEach(([key, snapshot]) => { const node = keyMap.get(key); [snapshot.dependencies, snapshot.optionalDependencies].forEach( @@ -171,15 +172,15 @@ function getDependencies( isV6 ); const target = - projectGraph.externalNodes[`npm:${name}@${version}`] || - projectGraph.externalNodes[`npm:${name}`]; + ctx.externalNodes[`npm:${name}@${version}`] || + ctx.externalNodes[`npm:${name}`]; if (target) { - const dep = { + const dep: RawProjectGraphDependency = { source: node.name, target: target.name, - dependencyType: DependencyType.static, + type: DependencyType.static, }; - validateDependency(projectGraph, dep); + validateDependency(dep, ctx); results.push(dep); } }); diff --git a/packages/nx/src/plugins/js/lock-file/yarn-parser.spec.ts b/packages/nx/src/plugins/js/lock-file/yarn-parser.spec.ts index fd5c48faf9..210a94f93c 100644 --- a/packages/nx/src/plugins/js/lock-file/yarn-parser.spec.ts +++ b/packages/nx/src/plugins/js/lock-file/yarn-parser.spec.ts @@ -9,6 +9,7 @@ import { vol } from 'memfs'; import { ProjectGraph } from '../../../config/project-graph'; import { PackageJson } from '../../../utils/package-json'; import { ProjectGraphBuilder } from '../../../project-graph/project-graph-builder'; +import { CreateDependenciesContext } from '../../../utils/nx-plugin'; jest.mock('fs', () => { const memFs = require('memfs').fs; @@ -188,15 +189,23 @@ describe('yarn LockFile utility', () => { dependencies: {}, externalNodes, }; - const dependencies = getYarnLockfileDependencies(lockFile, hash, pg); + const ctx: CreateDependenciesContext = { + projects: {}, + externalNodes, + fileMap: {}, + filesToProcess: {}, + nxJsonConfiguration: null, + workspaceRoot: '/virtual', + }; + const dependencies = getYarnLockfileDependencies(lockFile, hash, ctx); const builder = new ProjectGraphBuilder(pg); for (const dep of dependencies) { builder.addDependency( dep.source, dep.target, - dep.dependencyType, - dep.sourceFile + dep.type, + 'sourceFile' in dep ? dep.sourceFile : null ); } graph = builder.getUpdatedProjectGraph(); @@ -498,15 +507,23 @@ describe('yarn LockFile utility', () => { dependencies: {}, externalNodes, }; - const dependencies = getYarnLockfileDependencies(lockFile, hash, pg); + const ctx: CreateDependenciesContext = { + projects: {}, + externalNodes, + fileMap: {}, + filesToProcess: {}, + nxJsonConfiguration: null, + workspaceRoot: '/virtual', + }; + const dependencies = getYarnLockfileDependencies(lockFile, hash, ctx); const builder = new ProjectGraphBuilder(pg); for (const dep of dependencies) { builder.addDependency( dep.source, dep.target, - dep.dependencyType, - dep.sourceFile + dep.type, + 'sourceFile' in dep ? dep.sourceFile : null ); } const graph = builder.getUpdatedProjectGraph(); @@ -557,15 +574,23 @@ describe('yarn LockFile utility', () => { dependencies: {}, externalNodes, }; - const dependencies = getYarnLockfileDependencies(lockFile, hash, pg); + const ctx: CreateDependenciesContext = { + projects: {}, + externalNodes, + fileMap: {}, + filesToProcess: {}, + nxJsonConfiguration: null, + workspaceRoot: '/virtual', + }; + const dependencies = getYarnLockfileDependencies(lockFile, hash, ctx); const builder = new ProjectGraphBuilder(pg); for (const dep of dependencies) { builder.addDependency( dep.source, dep.target, - dep.dependencyType, - dep.sourceFile + dep.type, + 'sourceFile' in dep ? dep.sourceFile : null ); } const graph = builder.getUpdatedProjectGraph(); @@ -688,15 +713,23 @@ describe('yarn LockFile utility', () => { dependencies: {}, externalNodes, }; - const dependencies = getYarnLockfileDependencies(lockFile, hash, pg); + const ctx: CreateDependenciesContext = { + projects: {}, + externalNodes, + fileMap: {}, + filesToProcess: {}, + nxJsonConfiguration: null, + workspaceRoot: '/virtual', + }; + const dependencies = getYarnLockfileDependencies(lockFile, hash, ctx); const builder = new ProjectGraphBuilder(pg); for (const dep of dependencies) { builder.addDependency( dep.source, dep.target, - dep.dependencyType, - dep.sourceFile + dep.type, + 'sourceFile' in dep ? dep.sourceFile : null ); } const graph = builder.getUpdatedProjectGraph(); @@ -757,15 +790,23 @@ __metadata: dependencies: {}, externalNodes, }; - const dependencies = getYarnLockfileDependencies(lockFile, hash, pg); + const ctx: CreateDependenciesContext = { + projects: {}, + externalNodes, + fileMap: {}, + filesToProcess: {}, + nxJsonConfiguration: null, + workspaceRoot: '/virtual', + }; + const dependencies = getYarnLockfileDependencies(lockFile, hash, ctx); const builder = new ProjectGraphBuilder(pg); for (const dep of dependencies) { builder.addDependency( dep.source, dep.target, - dep.dependencyType, - dep.sourceFile + dep.type, + 'sourceFile' in dep ? dep.sourceFile : null ); } const graph = builder.getUpdatedProjectGraph(); @@ -1223,15 +1264,23 @@ nx-cloud@latest: dependencies: {}, externalNodes, }; - const dependencies = getYarnLockfileDependencies(lockFile, hash, pg); + const ctx: CreateDependenciesContext = { + projects: {}, + externalNodes, + fileMap: {}, + filesToProcess: {}, + nxJsonConfiguration: null, + workspaceRoot: '/virtual', + }; + const dependencies = getYarnLockfileDependencies(lockFile, hash, ctx); const builder = new ProjectGraphBuilder(pg); for (const dep of dependencies) { builder.addDependency( dep.source, dep.target, - dep.dependencyType, - dep.sourceFile + dep.type, + 'sourceFile' in dep ? dep.sourceFile : null ); } const graph = builder.getUpdatedProjectGraph(); @@ -1424,15 +1473,23 @@ nx-cloud@latest: dependencies: {}, externalNodes, }; - const dependencies = getYarnLockfileDependencies(lockFile, hash, pg); + const ctx: CreateDependenciesContext = { + projects: {}, + externalNodes, + fileMap: {}, + filesToProcess: {}, + nxJsonConfiguration: null, + workspaceRoot: '/virtual', + }; + const dependencies = getYarnLockfileDependencies(lockFile, hash, ctx); const builder = new ProjectGraphBuilder(pg); for (const dep of dependencies) { builder.addDependency( dep.source, dep.target, - dep.dependencyType, - dep.sourceFile + dep.type, + 'sourceFile' in dep ? dep.sourceFile : null ); } const graph = builder.getUpdatedProjectGraph(); @@ -1473,15 +1530,23 @@ nx-cloud@latest: dependencies: {}, externalNodes, }; - const dependencies = getYarnLockfileDependencies(lockFile, hash, pg); + const ctx: CreateDependenciesContext = { + projects: {}, + externalNodes, + fileMap: {}, + filesToProcess: {}, + nxJsonConfiguration: null, + workspaceRoot: '/virtual', + }; + const dependencies = getYarnLockfileDependencies(lockFile, hash, ctx); const builder = new ProjectGraphBuilder(pg); for (const dep of dependencies) { builder.addDependency( dep.source, dep.target, - dep.dependencyType, - dep.sourceFile + dep.type, + 'sourceFile' in dep ? dep.sourceFile : null ); } const graph = builder.getUpdatedProjectGraph(); @@ -1608,15 +1673,23 @@ type-fest@^0.20.2: dependencies: {}, externalNodes, }; - const dependencies = getYarnLockfileDependencies(lockFile, hash, pg); + const ctx: CreateDependenciesContext = { + projects: {}, + externalNodes, + fileMap: {}, + filesToProcess: {}, + nxJsonConfiguration: null, + workspaceRoot: '/virtual', + }; + const dependencies = getYarnLockfileDependencies(lockFile, hash, ctx); const builder = new ProjectGraphBuilder(pg); for (const dep of dependencies) { builder.addDependency( dep.source, dep.target, - dep.dependencyType, - dep.sourceFile + dep.type, + 'sourceFile' in dep ? dep.sourceFile : null ); } const graph = builder.getUpdatedProjectGraph(); @@ -1713,15 +1786,23 @@ __metadata: dependencies: {}, externalNodes, }; - const dependencies = getYarnLockfileDependencies(lockFile, hash, pg); + const ctx: CreateDependenciesContext = { + projects: {}, + externalNodes, + fileMap: {}, + filesToProcess: {}, + nxJsonConfiguration: null, + workspaceRoot: '/virtual', + }; + const dependencies = getYarnLockfileDependencies(lockFile, hash, ctx); const builder = new ProjectGraphBuilder(pg); for (const dep of dependencies) { builder.addDependency( dep.source, dep.target, - dep.dependencyType, - dep.sourceFile + dep.type, + 'sourceFile' in dep ? dep.sourceFile : null ); } const graph = builder.getUpdatedProjectGraph(); @@ -1828,15 +1909,23 @@ __metadata: dependencies: {}, externalNodes, }; - const dependencies = getYarnLockfileDependencies(lockFile, hash, pg); + const ctx: CreateDependenciesContext = { + projects: {}, + externalNodes, + fileMap: {}, + filesToProcess: {}, + nxJsonConfiguration: null, + workspaceRoot: '/virtual', + }; + const dependencies = getYarnLockfileDependencies(lockFile, hash, ctx); const builder = new ProjectGraphBuilder(pg); for (const dep of dependencies) { builder.addDependency( dep.source, dep.target, - dep.dependencyType, - dep.sourceFile + dep.type, + 'sourceFile' in dep ? dep.sourceFile : null ); } const graph = builder.getUpdatedProjectGraph(); @@ -2057,15 +2146,23 @@ __metadata: dependencies: {}, externalNodes, }; - const dependencies = getYarnLockfileDependencies(lockFile, hash, pg); + const ctx: CreateDependenciesContext = { + projects: {}, + externalNodes, + fileMap: {}, + filesToProcess: {}, + nxJsonConfiguration: null, + workspaceRoot: '/virtual', + }; + const dependencies = getYarnLockfileDependencies(lockFile, hash, ctx); const builder = new ProjectGraphBuilder(pg); for (const dep of dependencies) { builder.addDependency( dep.source, dep.target, - dep.dependencyType, - dep.sourceFile + dep.type, + 'sourceFile' in dep ? dep.sourceFile : null ); } const graph = builder.getUpdatedProjectGraph(); @@ -2436,15 +2533,23 @@ __metadata: dependencies: {}, externalNodes, }; - const dependencies = getYarnLockfileDependencies(lockFile, hash, pg); + const ctx: CreateDependenciesContext = { + projects: {}, + externalNodes, + fileMap: {}, + filesToProcess: {}, + nxJsonConfiguration: null, + workspaceRoot: '/virtual', + }; + const dependencies = getYarnLockfileDependencies(lockFile, hash, ctx); const builder = new ProjectGraphBuilder(pg); for (const dep of dependencies) { builder.addDependency( dep.source, dep.target, - dep.dependencyType, - dep.sourceFile + dep.type, + 'sourceFile' in dep ? dep.sourceFile : null ); } const graph = builder.getUpdatedProjectGraph(); diff --git a/packages/nx/src/plugins/js/lock-file/yarn-parser.ts b/packages/nx/src/plugins/js/lock-file/yarn-parser.ts index 0fc937c4f8..7098220352 100644 --- a/packages/nx/src/plugins/js/lock-file/yarn-parser.ts +++ b/packages/nx/src/plugins/js/lock-file/yarn-parser.ts @@ -3,7 +3,7 @@ import { NormalizedPackageJson, } from './utils/package-json'; import { - ProjectGraphDependencyWithFile, + RawProjectGraphDependency, validateDependency, } from '../../../project-graph/project-graph-builder'; import { gt, Range, satisfies } from 'semver'; @@ -14,6 +14,7 @@ import { } from '../../../config/project-graph'; import { hashArray } from '../../../hasher/file-hasher'; import { sortObjectByKeys } from '../../../utils/object-sort'; +import { CreateDependenciesContext } from '../../../utils/nx-plugin'; /** * Yarn @@ -79,7 +80,7 @@ export function getYarnLockfileNodes( export function getYarnLockfileDependencies( lockFileContent: string, lockFileHash: string, - projectGraph: ProjectGraph + ctx: CreateDependenciesContext ) { const { __metadata, ...dependencies } = parseLockFile( lockFileContent, @@ -91,7 +92,7 @@ export function getYarnLockfileDependencies( // yarn classic splits keys when parsing so we need to stich them back together const groupedDependencies = groupDependencies(dependencies, isBerry); - return getDependencies(groupedDependencies, keyMap, projectGraph); + return getDependencies(groupedDependencies, keyMap, ctx); } function getPackageNameKeyPairs(keys: string): Map> { @@ -289,9 +290,9 @@ function getHoistedVersion(packageName: string): string { function getDependencies( dependencies: Record, keyMap: Map, - projectGraph: ProjectGraph + ctx: CreateDependenciesContext ) { - const projectGraphDependencies: ProjectGraphDependencyWithFile[] = []; + const projectGraphDependencies: RawProjectGraphDependency[] = []; Object.keys(dependencies).forEach((keys) => { const snapshot = dependencies[keys]; keys.split(', ').forEach((key) => { @@ -305,12 +306,12 @@ function getDependencies( keyMap.get(`${name}@npm:${versionRange}`) || keyMap.get(`${name}@${versionRange}`); if (target) { - const dep = { + const dep: RawProjectGraphDependency = { source: node.name, target: target.name, - dependencyType: DependencyType.static, + type: DependencyType.static, }; - validateDependency(projectGraph, dep); + validateDependency(dep, ctx); projectGraphDependencies.push(dep); } }); diff --git a/packages/nx/src/plugins/js/project-graph/build-dependencies/build-dependencies.ts b/packages/nx/src/plugins/js/project-graph/build-dependencies/build-dependencies.ts index 094f9cce95..d5d7af7a88 100644 --- a/packages/nx/src/plugins/js/project-graph/build-dependencies/build-dependencies.ts +++ b/packages/nx/src/plugins/js/project-graph/build-dependencies/build-dependencies.ts @@ -1,7 +1,7 @@ import { buildExplicitTypeScriptDependencies } from './explicit-project-dependencies'; import { buildExplicitPackageJsonDependencies } from './explicit-package-json-dependencies'; import { CreateDependenciesContext } from '../../../../utils/nx-plugin'; -import { ProjectGraphDependencyWithFile } from '../../../../project-graph/project-graph-builder'; +import { RawProjectGraphDependency } from '../../../../project-graph/project-graph-builder'; export function buildExplicitDependencies( jsPluginConfig: { @@ -9,10 +9,10 @@ export function buildExplicitDependencies( analyzePackageJson?: boolean; }, ctx: CreateDependenciesContext -): ProjectGraphDependencyWithFile[] { +): RawProjectGraphDependency[] { if (totalNumberOfFilesToProcess(ctx) === 0) return []; - let dependencies: ProjectGraphDependencyWithFile[] = []; + let dependencies: RawProjectGraphDependency[] = []; if ( jsPluginConfig.analyzeSourceFiles === undefined || diff --git a/packages/nx/src/plugins/js/project-graph/build-dependencies/explicit-package-json-dependencies.spec.ts b/packages/nx/src/plugins/js/project-graph/build-dependencies/explicit-package-json-dependencies.spec.ts index ff76490fab..50eb25abf9 100644 --- a/packages/nx/src/plugins/js/project-graph/build-dependencies/explicit-package-json-dependencies.spec.ts +++ b/packages/nx/src/plugins/js/project-graph/build-dependencies/explicit-package-json-dependencies.spec.ts @@ -93,10 +93,11 @@ describe('explicit package json dependencies', () => { ctx = { fileMap: projectFileMap, - graph: builder.getUpdatedProjectGraph(), - projectsConfigurations: projectsConfigurations as any, + externalNodes: builder.getUpdatedProjectGraph().externalNodes, + projects: projectsConfigurations.projects, nxJsonConfiguration, filesToProcess: projectFileMap, + workspaceRoot: tempFs.tempDir, }; }); @@ -112,19 +113,19 @@ describe('explicit package json dependencies', () => { source: 'proj', target: 'proj2', sourceFile: 'libs/proj/package.json', - dependencyType: 'static', + type: 'static', }, { sourceFile: 'libs/proj/package.json', source: 'proj', target: 'npm:external', - dependencyType: 'static', + type: 'static', }, { source: 'proj', target: 'proj3', sourceFile: 'libs/proj/package.json', - dependencyType: 'static', + type: 'static', }, ]); }); diff --git a/packages/nx/src/plugins/js/project-graph/build-dependencies/explicit-package-json-dependencies.ts b/packages/nx/src/plugins/js/project-graph/build-dependencies/explicit-package-json-dependencies.ts index 11b2c73116..8f2bc0c95f 100644 --- a/packages/nx/src/plugins/js/project-graph/build-dependencies/explicit-package-json-dependencies.ts +++ b/packages/nx/src/plugins/js/project-graph/build-dependencies/explicit-package-json-dependencies.ts @@ -1,38 +1,34 @@ import { defaultFileRead } from '../../../../project-graph/file-utils'; import { join } from 'path'; -import { - DependencyType, - ProjectGraph, - ProjectGraphProjectNode, -} from '../../../../config/project-graph'; +import { DependencyType } from '../../../../config/project-graph'; import { parseJson } from '../../../../utils/json'; import { joinPathFragments } from '../../../../utils/path'; -import { ProjectsConfigurations } from '../../../../config/workspace-json-project-json'; +import { + ProjectConfiguration, + ProjectsConfigurations, +} from '../../../../config/workspace-json-project-json'; import { NxJsonConfiguration } from '../../../../config/nx-json'; import { PackageJson } from '../../../../utils/package-json'; import { CreateDependenciesContext } from '../../../../utils/nx-plugin'; import { - ProjectGraphDependencyWithFile, + RawProjectGraphDependency, validateDependency, } from '../../../../project-graph/project-graph-builder'; -export function buildExplicitPackageJsonDependencies({ - nxJsonConfiguration, - projectsConfigurations, - graph, - filesToProcess, -}: CreateDependenciesContext): ProjectGraphDependencyWithFile[] { - const res: ProjectGraphDependencyWithFile[] = []; +export function buildExplicitPackageJsonDependencies( + ctx: CreateDependenciesContext +): RawProjectGraphDependency[] { + const res: RawProjectGraphDependency[] = []; let packageNameMap = undefined; - const nodes = Object.values(graph.nodes); - Object.keys(filesToProcess).forEach((source) => { - Object.values(filesToProcess[source]).forEach((f) => { + const nodes = Object.values(ctx.projects); + Object.keys(ctx.filesToProcess).forEach((source) => { + Object.values(ctx.filesToProcess[source]).forEach((f) => { if (isPackageJsonAtProjectRoot(nodes, f.file)) { // we only create the package name map once and only if a package.json file changes packageNameMap = packageNameMap || - createPackageNameMap(nxJsonConfiguration, projectsConfigurations); - processPackageJson(source, f.file, graph, res, packageNameMap); + createPackageNameMap(ctx.nxJsonConfiguration, ctx.projects); + processPackageJson(source, f.file, ctx, res, packageNameMap); } }); }); @@ -41,18 +37,13 @@ export function buildExplicitPackageJsonDependencies({ function createPackageNameMap( nxJsonConfiguration: NxJsonConfiguration, - projectsConfigurations: ProjectsConfigurations + projects: ProjectsConfigurations['projects'] ) { const res = {}; - for (let projectName of Object.keys(projectsConfigurations.projects)) { + for (let projectName of Object.keys(projects)) { try { const packageJson = parseJson( - defaultFileRead( - join( - projectsConfigurations.projects[projectName].root, - 'package.json' - ) - ) + defaultFileRead(join(projects[projectName].root, 'package.json')) ); // TODO(v17): Stop reading nx.json for the npmScope const npmScope = nxJsonConfiguration.npmScope; @@ -68,15 +59,14 @@ function createPackageNameMap( } function isPackageJsonAtProjectRoot( - nodes: ProjectGraphProjectNode[], + nodes: ProjectConfiguration[], fileName: string ) { return ( fileName.endsWith('package.json') && nodes.find( (projectNode) => - (projectNode.type === 'lib' || projectNode.type === 'app') && - joinPathFragments(projectNode.data.root, 'package.json') === fileName + joinPathFragments(projectNode.root, 'package.json') === fileName ) ); } @@ -84,8 +74,8 @@ function isPackageJsonAtProjectRoot( function processPackageJson( sourceProject: string, fileName: string, - graph: ProjectGraph, - collectedDeps: ProjectGraphDependencyWithFile[], + ctx: CreateDependenciesContext, + collectedDeps: RawProjectGraphDependency[], packageNameMap: { [packageName: string]: string } ) { try { @@ -94,22 +84,22 @@ function processPackageJson( deps.forEach((d) => { // package.json refers to another project in the monorepo if (packageNameMap[d]) { - const dependency = { + const dependency: RawProjectGraphDependency = { source: sourceProject, target: packageNameMap[d], sourceFile: fileName, - dependencyType: DependencyType.static, + type: DependencyType.static, }; - validateDependency(graph, dependency); + validateDependency(dependency, ctx); collectedDeps.push(dependency); - } else if (graph.externalNodes[`npm:${d}`]) { - const dependency = { + } else if (ctx.externalNodes[`npm:${d}`]) { + const dependency: RawProjectGraphDependency = { source: sourceProject, target: `npm:${d}`, sourceFile: fileName, - dependencyType: DependencyType.static, + type: DependencyType.static, }; - validateDependency(graph, dependency); + validateDependency(dependency, ctx); collectedDeps.push(dependency); } }); diff --git a/packages/nx/src/plugins/js/project-graph/build-dependencies/explicit-project-dependencies.spec.ts b/packages/nx/src/plugins/js/project-graph/build-dependencies/explicit-project-dependencies.spec.ts index cf7a9bf4d6..ed9b8b196a 100644 --- a/packages/nx/src/plugins/js/project-graph/build-dependencies/explicit-project-dependencies.spec.ts +++ b/packages/nx/src/plugins/js/project-graph/build-dependencies/explicit-project-dependencies.spec.ts @@ -43,25 +43,25 @@ describe('explicit project dependencies', () => { source, sourceFile: 'libs/proj/index.ts', target: 'proj2', - dependencyType: 'static', + type: 'static', }, { source, sourceFile: 'libs/proj/index.ts', target: 'proj4ab', - dependencyType: 'static', + type: 'static', }, { source, sourceFile: 'libs/proj/index.ts', target: 'npm:npm-package', - dependencyType: 'static', + type: 'static', }, { source, sourceFile: 'libs/proj/index.ts', target: 'proj3a', - dependencyType: 'dynamic', + type: 'dynamic', }, ]); }); @@ -89,19 +89,19 @@ describe('explicit project dependencies', () => { source, sourceFile: 'libs/proj/index.ts', target: 'proj2', - dependencyType: 'static', + type: 'static', }, { source, sourceFile: 'libs/proj/index.ts', target: 'proj3a', - dependencyType: 'static', + type: 'static', }, { source, sourceFile: 'libs/proj/index.ts', target: 'proj4ab', - dependencyType: 'static', + type: 'static', }, ]); }); @@ -128,19 +128,19 @@ describe('explicit project dependencies', () => { source, sourceFile: 'libs/proj/index.mts', target: 'proj2', - dependencyType: 'static', + type: 'static', }, { source, sourceFile: 'libs/proj/index.mts', target: 'proj3a', - dependencyType: 'static', + type: 'static', }, { source, sourceFile: 'libs/proj/index.mts', target: 'proj4ab', - dependencyType: 'static', + type: 'static', }, ]); }); @@ -168,19 +168,19 @@ describe('explicit project dependencies', () => { source, sourceFile: 'libs/proj/index.ts', target: 'proj2', - dependencyType: 'static', + type: 'static', }, { source, sourceFile: 'libs/proj/index.ts', target: 'proj3a', - dependencyType: 'static', + type: 'static', }, { source, sourceFile: 'libs/proj/index.ts', target: 'proj4ab', - dependencyType: 'static', + type: 'static', }, ]); }); @@ -229,19 +229,19 @@ describe('explicit project dependencies', () => { source, sourceFile: 'libs/proj/component.tsx', target: 'proj2', - dependencyType: 'dynamic', + type: 'dynamic', }, { source, sourceFile: 'libs/proj/nested-dynamic-import.ts', target: 'proj3a', - dependencyType: 'dynamic', + type: 'dynamic', }, { source, sourceFile: 'libs/proj/nested-require.ts', target: 'proj4ab', - dependencyType: 'static', + type: 'static', }, ]); }); @@ -273,13 +273,13 @@ describe('explicit project dependencies', () => { source, sourceFile: 'libs/proj/absolute-path.ts', target: 'proj3a', - dependencyType: 'dynamic', + type: 'dynamic', }, { source, sourceFile: 'libs/proj/relative-path.ts', target: 'proj4ab', - dependencyType: 'dynamic', + type: 'dynamic', }, ]); }); @@ -563,10 +563,11 @@ async function createContext( await retrieveWorkspaceFiles(tempFs.tempDir, nxJson); return { - graph: builder.getUpdatedProjectGraph(), - projectsConfigurations: projectConfigurations, + externalNodes: builder.getUpdatedProjectGraph().externalNodes, + projects: projectConfigurations.projects, nxJsonConfiguration: nxJson, filesToProcess: projectFileMap, fileMap: projectFileMap, + workspaceRoot: tempFs.tempDir, }; } diff --git a/packages/nx/src/plugins/js/project-graph/build-dependencies/explicit-project-dependencies.ts b/packages/nx/src/plugins/js/project-graph/build-dependencies/explicit-project-dependencies.ts index faf1190b19..9ab63f2043 100644 --- a/packages/nx/src/plugins/js/project-graph/build-dependencies/explicit-project-dependencies.ts +++ b/packages/nx/src/plugins/js/project-graph/build-dependencies/explicit-project-dependencies.ts @@ -1,25 +1,32 @@ import { TargetProjectLocator } from './target-project-locator'; -import { DependencyType, ProjectGraph } from '../../../../config/project-graph'; +import { + DependencyType, + ProjectGraphProjectNode, +} from '../../../../config/project-graph'; import { join, relative } from 'path'; import { workspaceRoot } from '../../../../utils/workspace-root'; import { normalizePath } from '../../../../utils/path'; import { CreateDependenciesContext } from '../../../../utils/nx-plugin'; import { - ProjectGraphDependencyWithFile, + RawProjectGraphDependency, validateDependency, } from '../../../../project-graph/project-graph-builder'; +import { ProjectConfiguration } from '../../../../config/workspace-json-project-json'; -function isRoot(graph: ProjectGraph, projectName: string): boolean { - return graph.nodes[projectName]?.data?.root === '.'; +function isRoot( + projects: Record, + projectName: string +): boolean { + return projects[projectName]?.root === '.'; } function convertImportToDependency( importExpr: string, sourceFile: string, source: string, - dependencyType: ProjectGraphDependencyWithFile['dependencyType'], + type: RawProjectGraphDependency['type'], targetProjectLocator: TargetProjectLocator -): ProjectGraphDependencyWithFile { +): RawProjectGraphDependency { const target = targetProjectLocator.findProjectWithImport(importExpr, sourceFile) ?? `npm:${importExpr}`; @@ -28,19 +35,31 @@ function convertImportToDependency( source, target, sourceFile, - dependencyType, + type, }; } -export function buildExplicitTypeScriptDependencies({ - fileMap, - graph, -}: CreateDependenciesContext): ProjectGraphDependencyWithFile[] { - const targetProjectLocator = new TargetProjectLocator( - graph.nodes as any, - graph.externalNodes +export function buildExplicitTypeScriptDependencies( + ctx: CreateDependenciesContext +): RawProjectGraphDependency[] { + // TODO: TargetProjectLocator is a public API, so we can't change the shape of it + // We should eventually let it accept Record s.t. we + // don't have to reshape the CreateDependenciesContext here. + const nodes: Record = Object.fromEntries( + Object.entries(ctx.projects).map(([key, config]) => [ + key, + { + name: key, + type: null, + data: config, + }, + ]) ); - const res: ProjectGraphDependencyWithFile[] = []; + const targetProjectLocator = new TargetProjectLocator( + nodes, + ctx.externalNodes + ); + const res: RawProjectGraphDependency[] = []; const filesToProcess: Record = {}; @@ -51,7 +70,7 @@ export function buildExplicitTypeScriptDependencies({ moduleExtensions.push('.vue'); } - for (const [project, fileData] of Object.entries(fileMap)) { + for (const [project, fileData] of Object.entries(ctx.fileMap)) { filesToProcess[project] ??= []; for (const { file } of fileData) { if (moduleExtensions.some((ext) => file.endsWith(ext))) { @@ -81,8 +100,8 @@ export function buildExplicitTypeScriptDependencies({ ); // TODO: These edges technically should be allowed but we need to figure out how to separate config files out from root if ( - isRoot(graph, dependency.source) || - !isRoot(graph, dependency.target) + isRoot(ctx.projects, dependency.source) || + !isRoot(ctx.projects, dependency.target) ) { res.push(dependency); } @@ -97,10 +116,10 @@ export function buildExplicitTypeScriptDependencies({ ); // TODO: These edges technically should be allowed but we need to figure out how to separate config files out from root if ( - isRoot(graph, dependency.source) || - !isRoot(graph, dependency.target) + isRoot(ctx.projects, dependency.source) || + !isRoot(ctx.projects, dependency.target) ) { - validateDependency(graph, dependency); + validateDependency(dependency, ctx); res.push(dependency); } } diff --git a/packages/nx/src/project-graph/build-project-graph.ts b/packages/nx/src/project-graph/build-project-graph.ts index d33db7ad05..16c151bbb9 100644 --- a/packages/nx/src/project-graph/build-project-graph.ts +++ b/packages/nx/src/project-graph/build-project-graph.ts @@ -245,15 +245,21 @@ async function updateProjectGraphWithPlugins( if (isNxPluginV2(plugin) && plugin.createDependencies) { const builder = new ProjectGraphBuilder(graph, context.fileMap); const newDependencies = await plugin.createDependencies({ - ...context, - graph, + externalNodes: graph.externalNodes, + fileMap: context.fileMap, + filesToProcess: context.filesToProcess, + nxJsonConfiguration: context.nxJsonConfiguration, + projects: context.projectsConfigurations.projects, + workspaceRoot: workspaceRoot, }); for (const targetProjectDependency of newDependencies) { builder.addDependency( targetProjectDependency.source, targetProjectDependency.target, - targetProjectDependency.dependencyType, - targetProjectDependency.sourceFile + targetProjectDependency.type, + 'sourceFile' in targetProjectDependency + ? targetProjectDependency.sourceFile + : null ); } graph = builder.getUpdatedProjectGraph(); diff --git a/packages/nx/src/project-graph/project-graph-builder.ts b/packages/nx/src/project-graph/project-graph-builder.ts index e5b7ba75ce..fdea1e14d0 100644 --- a/packages/nx/src/project-graph/project-graph-builder.ts +++ b/packages/nx/src/project-graph/project-graph-builder.ts @@ -11,6 +11,8 @@ import { ProjectGraphExternalNode, ProjectGraphProjectNode, } from '../config/project-graph'; +import { ProjectConfiguration } from '../config/workspace-json-project-json'; +import { CreateDependenciesContext } from '../utils/nx-plugin'; import { getProjectFileMap } from './build-project-graph'; /** @@ -245,12 +247,23 @@ export class ProjectGraphBuilder { return; } - validateDependency(this.graph, { - source, - target, - dependencyType: type, - sourceFile, - }); + validateDependency( + { + source, + target, + type, + sourceFile, + }, + { + externalNodes: this.graph.externalNodes, + fileMap: this.fileMap, + // the validators only really care about the keys on this. + projects: this.graph.nodes as any, + filesToProcess: null, + nxJsonConfiguration: null, + workspaceRoot: null, + } + ); if (!this.graph.dependencies[source]) { this.graph.dependencies[source] = []; @@ -260,20 +273,12 @@ export class ProjectGraphBuilder { ); if (sourceFile) { - const sourceProject = this.graph.nodes[source]; - if (!sourceProject) { - throw new Error( - `Source project is not a project node: ${sourceProject}` - ); - } - const fileData = (this.fileMap[source] || []).find( - (f) => f.file === sourceFile + const fileData = getFileData( + source, + sourceFile, + this.graph.nodes, + this.fileMap ); - if (!fileData) { - throw new Error( - `Source project ${source} does not have a file: ${sourceFile}` - ); - } if (!fileData.deps) { fileData.deps = []; @@ -369,80 +374,139 @@ export class ProjectGraphBuilder { } /** - * A {@link ProjectGraph} dependency between 2 projects - * Optional: Specifies a file from where the dependency is made + * A static {@link ProjectGraph} dependency between 2 projects + * + * This type of dependency indicates the source project ALWAYS load the target project. + * + * NOTE: {@link StaticDependency#sourceFile} MUST be present unless the source is the name of a {@link ProjectGraphExternalNode} */ -export interface ProjectGraphDependencyWithFile { +export type StaticDependency = { /** * The name of a {@link ProjectGraphProjectNode} or {@link ProjectGraphExternalNode} depending on the target project */ source: string; + /** * The name of a {@link ProjectGraphProjectNode} or {@link ProjectGraphExternalNode} that the source project depends on */ target: string; + /** * The path of a file (relative from the workspace root) where the dependency is made */ sourceFile?: string; + + type: typeof DependencyType.static; +}; + +/** + * A dynamic {@link ProjectGraph} dependency between 2 projects + * + * This type of dependency indicates the source project MAY OR MAY NOT load the target project. + */ +export type DynamicDependency = { /** - * The type of dependency + * The name of a {@link ProjectGraphProjectNode} depending on the target project */ - dependencyType: DependencyType; -} + source: string; + + /** + * The name of a {@link ProjectGraphProjectNode} that the source project depends on + */ + target: string; + + /** + * The path of a file (relative from the workspace root) where the dependency is made + */ + sourceFile: string; + + type: typeof DependencyType.dynamic; +}; + +/** + * An implicit {@link ProjectGraph} dependency between 2 projects + * + * This type of dependency indicates a connection without an explicit reference in code + */ +export type ImplicitDependency = { + /** + * The name of a {@link ProjectGraphProjectNode} depending on the target project + */ + source: string; + /** + * The name of a {@link ProjectGraphProjectNode} that the source project depends on + */ + target: string; + + type: typeof DependencyType.implicit; +}; + +/** + * A {@link ProjectGraph} dependency between 2 projects + * + * See {@link DynamicDependency}, {@link ImplicitDependency}, or {@link StaticDependency} + */ +export type RawProjectGraphDependency = + | ImplicitDependency + | StaticDependency + | DynamicDependency; /** * A function to validate dependencies in a {@link CreateDependencies} function * @throws If the dependency is invalid. */ export function validateDependency( - graph: ProjectGraph, - dependency: ProjectGraphDependencyWithFile + dependency: RawProjectGraphDependency, + ctx: CreateDependenciesContext ): void { - if (dependency.dependencyType === DependencyType.implicit) { - validateImplicitDependency(graph, dependency); - } else if (dependency.dependencyType === DependencyType.dynamic) { - validateDynamicDependency(graph, dependency); - } else if (dependency.dependencyType === DependencyType.static) { - validateStaticDependency(graph, dependency); + if (dependency.type === DependencyType.implicit) { + validateImplicitDependency(dependency, ctx); + } else if (dependency.type === DependencyType.dynamic) { + validateDynamicDependency(dependency, ctx); + } else if (dependency.type === DependencyType.static) { + validateStaticDependency(dependency, ctx); } - validateCommonDependencyRules(graph, dependency); + validateCommonDependencyRules(dependency, ctx); } function validateCommonDependencyRules( - graph: ProjectGraph, - d: ProjectGraphDependencyWithFile + d: RawProjectGraphDependency, + { externalNodes, projects, fileMap }: CreateDependenciesContext ) { - if (!graph.nodes[d.source] && !graph.externalNodes[d.source]) { + if (!projects[d.source] && !externalNodes[d.source]) { throw new Error(`Source project does not exist: ${d.source}`); } if ( - !graph.nodes[d.target] && - !graph.externalNodes[d.target] && - !d.sourceFile + !projects[d.target] && + !externalNodes[d.target] && + !('sourceFile' in d && d.sourceFile) ) { throw new Error(`Target project does not exist: ${d.target}`); } - if (graph.externalNodes[d.source] && graph.nodes[d.target]) { + if (externalNodes[d.source] && projects[d.target]) { throw new Error(`External projects can't depend on internal projects`); } + if ('sourceFile' in d && d.sourceFile) { + // Throws if source file is not a valid file within the source project. + getFileData(d.source, d.sourceFile, projects, fileMap); + } } function validateImplicitDependency( - graph: ProjectGraph, - d: ProjectGraphDependencyWithFile + d: ImplicitDependency, + { externalNodes }: CreateDependenciesContext ) { - if (graph.externalNodes[d.source]) { + if (externalNodes[d.source]) { throw new Error(`External projects can't have "implicit" dependencies`); } } function validateDynamicDependency( - graph: ProjectGraph, - d: ProjectGraphDependencyWithFile + d: DynamicDependency, + { externalNodes }: CreateDependenciesContext ) { - if (graph.externalNodes[d.source]) { + if (externalNodes[d.source]) { throw new Error(`External projects can't have "dynamic" dependencies`); } // dynamic dependency is always bound to a file @@ -454,12 +518,31 @@ function validateDynamicDependency( } function validateStaticDependency( - graph: ProjectGraph, - d: ProjectGraphDependencyWithFile + d: StaticDependency, + { projects }: CreateDependenciesContext ) { // internal nodes must provide sourceProjectFile when creating static dependency // externalNodes do not have sourceProjectFile - if (graph.nodes[d.source] && !d.sourceFile) { + if (projects[d.source] && !d.sourceFile) { throw new Error(`Source project file is required`); } } + +function getFileData( + source: string, + sourceFile: string, + projects: Record, + fileMap: ProjectFileMap +) { + const sourceProject = projects[source]; + if (!sourceProject) { + throw new Error(`Source project is not a project node: ${sourceProject}`); + } + const fileData = (fileMap[source] || []).find((f) => f.file === sourceFile); + if (!fileData) { + throw new Error( + `Source project ${source} does not have a file: ${sourceFile}` + ); + } + return fileData; +} diff --git a/packages/nx/src/utils/nx-plugin.ts b/packages/nx/src/utils/nx-plugin.ts index 8beadb54df..54ac18e378 100644 --- a/packages/nx/src/utils/nx-plugin.ts +++ b/packages/nx/src/utils/nx-plugin.ts @@ -1,6 +1,7 @@ import { existsSync } from 'fs'; import * as path from 'path'; import { + FileData, ProjectFileMap, ProjectGraph, ProjectGraphExternalNode, @@ -35,7 +36,7 @@ import { NxJsonConfiguration } from '../config/nx-json'; import type * as ts from 'typescript'; import { retrieveProjectConfigurationsWithoutPluginInference } from '../project-graph/utils/retrieve-workspace-files'; import { NxPluginV1 } from './nx-plugin.deprecated'; -import { ProjectGraphDependencyWithFile } from '../project-graph/project-graph-builder'; +import { RawProjectGraphDependency } from '../project-graph/project-graph-builder'; import { combineGlobPatterns } from './globs'; import { NxAngularJsonPlugin, @@ -77,14 +78,14 @@ export type CreateNodes = readonly [ */ export interface CreateDependenciesContext { /** - * The current project graph, + * The external nodes that have been added to the graph. */ - readonly graph: ProjectGraph; + readonly externalNodes: ProjectGraph['externalNodes']; /** - * The configuration of each project in the workspace + * The configuration of each project in the workspace. */ - readonly projectsConfigurations: ProjectsConfigurations; + readonly projects: Record; /** * The `nx.json` configuration from the workspace @@ -100,6 +101,8 @@ export interface CreateDependenciesContext { * Files changes since last invocation */ readonly filesToProcess: ProjectFileMap; + + readonly workspaceRoot: string; } /** @@ -108,9 +111,7 @@ export interface CreateDependenciesContext { */ export type CreateDependencies = ( context: CreateDependenciesContext -) => - | ProjectGraphDependencyWithFile[] - | Promise; +) => RawProjectGraphDependency[] | Promise; /** * A plugin for Nx which creates nodes and dependencies for the {@link ProjectGraph}