cleanup(core): adjust create dependencies context (#19070)

Co-authored-by: FrozenPandaz <jasonjean1993@gmail.com>
This commit is contained in:
Craigory Coppola 2023-09-12 09:01:32 -04:00 committed by GitHub
parent 50b62524f5
commit f2e20c81b6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
28 changed files with 762 additions and 385 deletions

View File

@ -1,10 +1,10 @@
# Type alias: CreateDependencies # 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 #### 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) 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 Use [validateDependency](../../devkit/documents/validateDependency) to validate dependencies
@ -17,4 +17,4 @@ Use [validateDependency](../../devkit/documents/validateDependency) to validate
##### Returns ##### Returns
[`ProjectGraphDependencyWithFile`](../../devkit/documents/ProjectGraphDependencyWithFile)[] \| `Promise`<[`ProjectGraphDependencyWithFile`](../../devkit/documents/ProjectGraphDependencyWithFile)[]\> [`RawProjectGraphDependency`](../../devkit/documents/RawProjectGraphDependency)[] \| `Promise`<[`RawProjectGraphDependency`](../../devkit/documents/RawProjectGraphDependency)[]\>

View File

@ -6,14 +6,23 @@ Context for [CreateDependencies](../../devkit/documents/CreateDependencies)
### Properties ### Properties
- [externalNodes](../../devkit/documents/CreateDependenciesContext#externalnodes): Record&lt;string, ProjectGraphExternalNode&gt;
- [fileMap](../../devkit/documents/CreateDependenciesContext#filemap): ProjectFileMap - [fileMap](../../devkit/documents/CreateDependenciesContext#filemap): ProjectFileMap
- [filesToProcess](../../devkit/documents/CreateDependenciesContext#filestoprocess): ProjectFileMap - [filesToProcess](../../devkit/documents/CreateDependenciesContext#filestoprocess): ProjectFileMap
- [graph](../../devkit/documents/CreateDependenciesContext#graph): ProjectGraph
- [nxJsonConfiguration](../../devkit/documents/CreateDependenciesContext#nxjsonconfiguration): NxJsonConfiguration&lt;string[] | &quot;\*&quot;&gt; - [nxJsonConfiguration](../../devkit/documents/CreateDependenciesContext#nxjsonconfiguration): NxJsonConfiguration&lt;string[] | &quot;\*&quot;&gt;
- [projectsConfigurations](../../devkit/documents/CreateDependenciesContext#projectsconfigurations): ProjectsConfigurations - [projects](../../devkit/documents/CreateDependenciesContext#projects): Record&lt;string, ProjectConfiguration&gt;
- [workspaceRoot](../../devkit/documents/CreateDependenciesContext#workspaceroot): string
## Properties ## Properties
### externalNodes
`Readonly` **externalNodes**: `Record`<`string`, [`ProjectGraphExternalNode`](../../devkit/documents/ProjectGraphExternalNode)\>
The external nodes that have been added to the graph.
---
### fileMap ### fileMap
`Readonly` **fileMap**: [`ProjectFileMap`](../../devkit/documents/ProjectFileMap) `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 ### nxJsonConfiguration
`Readonly` **nxJsonConfiguration**: [`NxJsonConfiguration`](../../devkit/documents/NxJsonConfiguration)<`string`[] \| `"*"`\> `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`

View File

@ -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) | - |

View File

@ -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) | - |

View File

@ -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

View File

@ -43,7 +43,6 @@ It only uses language primitives and immutable objects
- [ProjectFileMap](../../devkit/documents/ProjectFileMap) - [ProjectFileMap](../../devkit/documents/ProjectFileMap)
- [ProjectGraph](../../devkit/documents/ProjectGraph) - [ProjectGraph](../../devkit/documents/ProjectGraph)
- [ProjectGraphDependency](../../devkit/documents/ProjectGraphDependency) - [ProjectGraphDependency](../../devkit/documents/ProjectGraphDependency)
- [ProjectGraphDependencyWithFile](../../devkit/documents/ProjectGraphDependencyWithFile)
- [ProjectGraphExternalNode](../../devkit/documents/ProjectGraphExternalNode) - [ProjectGraphExternalNode](../../devkit/documents/ProjectGraphExternalNode)
- [ProjectGraphProcessorContext](../../devkit/documents/ProjectGraphProcessorContext) - [ProjectGraphProcessorContext](../../devkit/documents/ProjectGraphProcessorContext)
- [ProjectGraphProjectNode](../../devkit/documents/ProjectGraphProjectNode) - [ProjectGraphProjectNode](../../devkit/documents/ProjectGraphProjectNode)
@ -66,10 +65,12 @@ It only uses language primitives and immutable objects
- [CreateNodes](../../devkit/documents/CreateNodes) - [CreateNodes](../../devkit/documents/CreateNodes)
- [CreateNodesFunction](../../devkit/documents/CreateNodesFunction) - [CreateNodesFunction](../../devkit/documents/CreateNodesFunction)
- [CustomHasher](../../devkit/documents/CustomHasher) - [CustomHasher](../../devkit/documents/CustomHasher)
- [DynamicDependency](../../devkit/documents/DynamicDependency)
- [Executor](../../devkit/documents/Executor) - [Executor](../../devkit/documents/Executor)
- [Generator](../../devkit/documents/Generator) - [Generator](../../devkit/documents/Generator)
- [GeneratorCallback](../../devkit/documents/GeneratorCallback) - [GeneratorCallback](../../devkit/documents/GeneratorCallback)
- [Hasher](../../devkit/documents/Hasher) - [Hasher](../../devkit/documents/Hasher)
- [ImplicitDependency](../../devkit/documents/ImplicitDependency)
- [ImplicitDependencyEntry](../../devkit/documents/ImplicitDependencyEntry) - [ImplicitDependencyEntry](../../devkit/documents/ImplicitDependencyEntry)
- [NxPlugin](../../devkit/documents/NxPlugin) - [NxPlugin](../../devkit/documents/NxPlugin)
- [NxPluginV1](../../devkit/documents/NxPluginV1) - [NxPluginV1](../../devkit/documents/NxPluginV1)
@ -78,6 +79,8 @@ It only uses language primitives and immutable objects
- [ProjectGraphNode](../../devkit/documents/ProjectGraphNode) - [ProjectGraphNode](../../devkit/documents/ProjectGraphNode)
- [ProjectTargetConfigurator](../../devkit/documents/ProjectTargetConfigurator) - [ProjectTargetConfigurator](../../devkit/documents/ProjectTargetConfigurator)
- [ProjectType](../../devkit/documents/ProjectType) - [ProjectType](../../devkit/documents/ProjectType)
- [RawProjectGraphDependency](../../devkit/documents/RawProjectGraphDependency)
- [StaticDependency](../../devkit/documents/StaticDependency)
- [StringChange](../../devkit/documents/StringChange) - [StringChange](../../devkit/documents/StringChange)
- [TaskGraphExecutor](../../devkit/documents/TaskGraphExecutor) - [TaskGraphExecutor](../../devkit/documents/TaskGraphExecutor)
- [WorkspaceConfiguration](../../devkit/documents/WorkspaceConfiguration) - [WorkspaceConfiguration](../../devkit/documents/WorkspaceConfiguration)

View File

@ -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)

View File

@ -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) | - |

View File

@ -1,6 +1,6 @@
# Function: validateDependency # Function: validateDependency
**validateDependency**(`graph`, `dependency`): `void` **validateDependency**(`dependency`, `ctx`): `void`
A function to validate dependencies in a [CreateDependencies](../../devkit/documents/CreateDependencies) function A function to validate dependencies in a [CreateDependencies](../../devkit/documents/CreateDependencies) function
@ -10,10 +10,10 @@ If the dependency is invalid.
#### Parameters #### Parameters
| Name | Type | | Name | Type |
| :----------- | :---------------------------------------------------------------------------------------- | | :----------- | :------------------------------------------------------------------------------ |
| `graph` | [`ProjectGraph`](../../devkit/documents/ProjectGraph) | | `dependency` | [`RawProjectGraphDependency`](../../devkit/documents/RawProjectGraphDependency) |
| `dependency` | [`ProjectGraphDependencyWithFile`](../../devkit/documents/ProjectGraphDependencyWithFile) | | `ctx` | [`CreateDependenciesContext`](../../devkit/documents/CreateDependenciesContext) |
#### Returns #### Returns

View File

@ -43,7 +43,6 @@ It only uses language primitives and immutable objects
- [ProjectFileMap](../../devkit/documents/ProjectFileMap) - [ProjectFileMap](../../devkit/documents/ProjectFileMap)
- [ProjectGraph](../../devkit/documents/ProjectGraph) - [ProjectGraph](../../devkit/documents/ProjectGraph)
- [ProjectGraphDependency](../../devkit/documents/ProjectGraphDependency) - [ProjectGraphDependency](../../devkit/documents/ProjectGraphDependency)
- [ProjectGraphDependencyWithFile](../../devkit/documents/ProjectGraphDependencyWithFile)
- [ProjectGraphExternalNode](../../devkit/documents/ProjectGraphExternalNode) - [ProjectGraphExternalNode](../../devkit/documents/ProjectGraphExternalNode)
- [ProjectGraphProcessorContext](../../devkit/documents/ProjectGraphProcessorContext) - [ProjectGraphProcessorContext](../../devkit/documents/ProjectGraphProcessorContext)
- [ProjectGraphProjectNode](../../devkit/documents/ProjectGraphProjectNode) - [ProjectGraphProjectNode](../../devkit/documents/ProjectGraphProjectNode)
@ -66,10 +65,12 @@ It only uses language primitives and immutable objects
- [CreateNodes](../../devkit/documents/CreateNodes) - [CreateNodes](../../devkit/documents/CreateNodes)
- [CreateNodesFunction](../../devkit/documents/CreateNodesFunction) - [CreateNodesFunction](../../devkit/documents/CreateNodesFunction)
- [CustomHasher](../../devkit/documents/CustomHasher) - [CustomHasher](../../devkit/documents/CustomHasher)
- [DynamicDependency](../../devkit/documents/DynamicDependency)
- [Executor](../../devkit/documents/Executor) - [Executor](../../devkit/documents/Executor)
- [Generator](../../devkit/documents/Generator) - [Generator](../../devkit/documents/Generator)
- [GeneratorCallback](../../devkit/documents/GeneratorCallback) - [GeneratorCallback](../../devkit/documents/GeneratorCallback)
- [Hasher](../../devkit/documents/Hasher) - [Hasher](../../devkit/documents/Hasher)
- [ImplicitDependency](../../devkit/documents/ImplicitDependency)
- [ImplicitDependencyEntry](../../devkit/documents/ImplicitDependencyEntry) - [ImplicitDependencyEntry](../../devkit/documents/ImplicitDependencyEntry)
- [NxPlugin](../../devkit/documents/NxPlugin) - [NxPlugin](../../devkit/documents/NxPlugin)
- [NxPluginV1](../../devkit/documents/NxPluginV1) - [NxPluginV1](../../devkit/documents/NxPluginV1)
@ -78,6 +79,8 @@ It only uses language primitives and immutable objects
- [ProjectGraphNode](../../devkit/documents/ProjectGraphNode) - [ProjectGraphNode](../../devkit/documents/ProjectGraphNode)
- [ProjectTargetConfigurator](../../devkit/documents/ProjectTargetConfigurator) - [ProjectTargetConfigurator](../../devkit/documents/ProjectTargetConfigurator)
- [ProjectType](../../devkit/documents/ProjectType) - [ProjectType](../../devkit/documents/ProjectType)
- [RawProjectGraphDependency](../../devkit/documents/RawProjectGraphDependency)
- [StaticDependency](../../devkit/documents/StaticDependency)
- [StringChange](../../devkit/documents/StringChange) - [StringChange](../../devkit/documents/StringChange)
- [TaskGraphExecutor](../../devkit/documents/TaskGraphExecutor) - [TaskGraphExecutor](../../devkit/documents/TaskGraphExecutor)
- [WorkspaceConfiguration](../../devkit/documents/WorkspaceConfiguration) - [WorkspaceConfiguration](../../devkit/documents/WorkspaceConfiguration)

View File

@ -90,16 +90,14 @@ The shape of the [`createDependencies`](/packages/devkit/documents/CreateDepende
```typescript ```typescript
export type CreateDependencies = ( export type CreateDependencies = (
context: CreateDependenciesContext context: CreateDependenciesContext
) => ) => CandidateDependency[] | Promise<CandidateDependency[]>;
| ProjectGraphDependencyWithFile[]
| Promise<ProjectGraphDependencyWithFile[]>;
``` ```
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`. 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: The dependencies can be of three types:
@ -184,7 +182,7 @@ export const createNodes: CreateNodes = (ctx) => {
dependencyType: DependencyType.static, dependencyType: DependencyType.static,
}; };
} }
validateDependency(ctx.graph, newDependency); validateDependency(newDependency, ctx);
results.push(newDependency); results.push(newDependency);
} }
} }

View File

@ -156,7 +156,10 @@ export { DependencyType } from './config/project-graph';
*/ */
export { export {
ProjectGraphBuilder, ProjectGraphBuilder,
ProjectGraphDependencyWithFile, RawProjectGraphDependency,
DynamicDependency,
ImplicitDependency,
StaticDependency,
validateDependency, validateDependency,
} from './project-graph/project-graph-builder'; } from './project-graph/project-graph-builder';

View File

@ -19,7 +19,7 @@ import {
} from './lock-file/lock-file'; } from './lock-file/lock-file';
import { buildExplicitDependencies } from './project-graph/build-dependencies/build-dependencies'; import { buildExplicitDependencies } from './project-graph/build-dependencies/build-dependencies';
import { jsPluginConfig } from './utils/config'; 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 { hashArray } from '../../hasher/file-hasher';
import { detectPackageManager } from '../../utils/package-manager'; import { detectPackageManager } from '../../utils/package-manager';
import { workspaceRoot } from '../../utils/workspace-root'; import { workspaceRoot } from '../../utils/workspace-root';
@ -29,7 +29,7 @@ export const name = 'nx-js-graph-plugin';
interface ParsedLockFile { interface ParsedLockFile {
externalNodes?: ProjectGraph['externalNodes']; externalNodes?: ProjectGraph['externalNodes'];
dependencies?: ProjectGraphDependencyWithFile[]; dependencies?: RawProjectGraphDependency[];
} }
let parsedLockFile: ParsedLockFile = {}; let parsedLockFile: ParsedLockFile = {};
@ -80,7 +80,7 @@ export const createDependencies: CreateDependencies = (
const packageManager = detectPackageManager(workspaceRoot); const packageManager = detectPackageManager(workspaceRoot);
let lockfileDependencies: ProjectGraphDependencyWithFile[] = []; let lockfileDependencies: RawProjectGraphDependency[] = [];
// lockfile may not exist yet // lockfile may not exist yet
if ( if (
pluginConfig.analyzeLockfile && pluginConfig.analyzeLockfile &&
@ -98,7 +98,7 @@ export const createDependencies: CreateDependencies = (
packageManager, packageManager,
lockFileContents, lockFileContents,
lockFileHash, lockFileHash,
ctx.graph ctx
); );
parsedLockFile.dependencies = lockfileDependencies; parsedLockFile.dependencies = lockfileDependencies;

View File

@ -15,10 +15,7 @@ import {
ProjectGraph, ProjectGraph,
ProjectGraphExternalNode, ProjectGraphExternalNode,
} from '../../../config/project-graph'; } from '../../../config/project-graph';
import { import { RawProjectGraphDependency } from '../../../project-graph/project-graph-builder';
ProjectGraphBuilder,
ProjectGraphDependencyWithFile,
} from '../../../project-graph/project-graph-builder';
import { PackageJson } from '../../../utils/package-json'; import { PackageJson } from '../../../utils/package-json';
import { output } from '../../../utils/output'; import { output } from '../../../utils/output';
@ -40,6 +37,7 @@ import {
import { pruneProjectGraph } from './project-graph-pruning'; import { pruneProjectGraph } from './project-graph-pruning';
import { normalizePackageJson } from './utils/package-json'; import { normalizePackageJson } from './utils/package-json';
import { readJsonFile } from '../../../utils/fileutils'; import { readJsonFile } from '../../../utils/fileutils';
import { CreateDependenciesContext } from '../../../utils/nx-plugin';
const YARN_LOCK_FILE = 'yarn.lock'; const YARN_LOCK_FILE = 'yarn.lock';
const NPM_LOCK_FILE = 'package-lock.json'; const NPM_LOCK_FILE = 'package-lock.json';
@ -88,17 +86,17 @@ export function getLockFileDependencies(
packageManager: PackageManager, packageManager: PackageManager,
contents: string, contents: string,
lockFileHash: string, lockFileHash: string,
projectGraph: ProjectGraph context: CreateDependenciesContext
): ProjectGraphDependencyWithFile[] { ): RawProjectGraphDependency[] {
try { try {
if (packageManager === 'yarn') { if (packageManager === 'yarn') {
return getYarnLockfileDependencies(contents, lockFileHash, projectGraph); return getYarnLockfileDependencies(contents, lockFileHash, context);
} }
if (packageManager === 'pnpm') { if (packageManager === 'pnpm') {
return getPnpmLockfileDependencies(contents, lockFileHash, projectGraph); return getPnpmLockfileDependencies(contents, lockFileHash, context);
} }
if (packageManager === 'npm') { if (packageManager === 'npm') {
return getNpmLockfileDependencies(contents, lockFileHash, projectGraph); return getNpmLockfileDependencies(contents, lockFileHash, context);
} }
} catch (e) { } catch (e) {
if (!isPostInstallProcess()) { if (!isPostInstallProcess()) {

View File

@ -8,6 +8,7 @@ import { pruneProjectGraph } from './project-graph-pruning';
import { vol } from 'memfs'; import { vol } from 'memfs';
import { ProjectGraph } from '../../../config/project-graph'; import { ProjectGraph } from '../../../config/project-graph';
import { ProjectGraphBuilder } from '../../../project-graph/project-graph-builder'; import { ProjectGraphBuilder } from '../../../project-graph/project-graph-builder';
import { CreateDependenciesContext } from '../../../utils/nx-plugin';
jest.mock('fs', () => { jest.mock('fs', () => {
const memFs = require('memfs').fs; const memFs = require('memfs').fs;
@ -36,15 +37,23 @@ describe('NPM lock file utility', () => {
JSON.stringify(rootLockFile), JSON.stringify(rootLockFile),
hash hash
); );
const pg = { const ctx: CreateDependenciesContext = {
nodes: {}, projects: {},
externalNodes,
fileMap: {},
filesToProcess: {},
nxJsonConfiguration: null,
workspaceRoot: '/virtual',
};
const pg: ProjectGraph = {
dependencies: {}, dependencies: {},
nodes: {},
externalNodes, externalNodes,
}; };
const dependencies = getNpmLockfileDependencies( const dependencies = getNpmLockfileDependencies(
JSON.stringify(rootLockFile), JSON.stringify(rootLockFile),
hash, hash,
pg ctx
); );
const builder = new ProjectGraphBuilder(pg); const builder = new ProjectGraphBuilder(pg);
@ -52,8 +61,8 @@ describe('NPM lock file utility', () => {
builder.addDependency( builder.addDependency(
dep.source, dep.source,
dep.target, dep.target,
dep.dependencyType, dep.type,
dep.sourceFile 'sourceFile' in dep ? dep.sourceFile : null
); );
} }
graph = builder.getUpdatedProjectGraph(); graph = builder.getUpdatedProjectGraph();
@ -84,10 +93,18 @@ describe('NPM lock file utility', () => {
dependencies: {}, dependencies: {},
externalNodes, externalNodes,
}; };
const ctx: CreateDependenciesContext = {
projects: {},
externalNodes,
fileMap: {},
filesToProcess: {},
nxJsonConfiguration: null,
workspaceRoot: '/virtual',
};
const dependencies = getNpmLockfileDependencies( const dependencies = getNpmLockfileDependencies(
JSON.stringify(appLockFile), JSON.stringify(appLockFile),
hash, hash,
pg ctx
); );
const builder = new ProjectGraphBuilder(pg); const builder = new ProjectGraphBuilder(pg);
@ -95,8 +112,8 @@ describe('NPM lock file utility', () => {
builder.addDependency( builder.addDependency(
dep.source, dep.source,
dep.target, dep.target,
dep.dependencyType, dep.type,
dep.sourceFile 'sourceFile' in dep ? dep.sourceFile : null
); );
} }
const appGraph = builder.getUpdatedProjectGraph(); const appGraph = builder.getUpdatedProjectGraph();
@ -155,10 +172,18 @@ describe('NPM lock file utility', () => {
dependencies: {}, dependencies: {},
externalNodes, externalNodes,
}; };
const ctx: CreateDependenciesContext = {
projects: {},
externalNodes,
fileMap: {},
filesToProcess: {},
nxJsonConfiguration: null,
workspaceRoot: '/virtual',
};
const dependencies = getNpmLockfileDependencies( const dependencies = getNpmLockfileDependencies(
JSON.stringify(rootLockFile), JSON.stringify(rootLockFile),
hash, hash,
pg ctx
); );
const builder = new ProjectGraphBuilder(pg); const builder = new ProjectGraphBuilder(pg);
@ -166,8 +191,8 @@ describe('NPM lock file utility', () => {
builder.addDependency( builder.addDependency(
dep.source, dep.source,
dep.target, dep.target,
dep.dependencyType, dep.type,
dep.sourceFile 'sourceFile' in dep ? dep.sourceFile : null
); );
} }
const graph = builder.getUpdatedProjectGraph(); const graph = builder.getUpdatedProjectGraph();
@ -237,10 +262,18 @@ describe('NPM lock file utility', () => {
dependencies: {}, dependencies: {},
externalNodes, externalNodes,
}; };
const ctx: CreateDependenciesContext = {
projects: {},
externalNodes,
fileMap: {},
filesToProcess: {},
nxJsonConfiguration: null,
workspaceRoot: '/virtual',
};
const dependencies = getNpmLockfileDependencies( const dependencies = getNpmLockfileDependencies(
JSON.stringify(rootV2LockFile), JSON.stringify(rootV2LockFile),
hash, hash,
pg ctx
); );
const builder = new ProjectGraphBuilder(pg); const builder = new ProjectGraphBuilder(pg);
@ -248,8 +281,8 @@ describe('NPM lock file utility', () => {
builder.addDependency( builder.addDependency(
dep.source, dep.source,
dep.target, dep.target,
dep.dependencyType, dep.type,
dep.sourceFile 'sourceFile' in dep ? dep.sourceFile : null
); );
} }
const graph = builder.getUpdatedProjectGraph(); const graph = builder.getUpdatedProjectGraph();
@ -359,10 +392,18 @@ describe('NPM lock file utility', () => {
dependencies: {}, dependencies: {},
externalNodes, externalNodes,
}; };
const ctx: CreateDependenciesContext = {
projects: {},
externalNodes,
fileMap: {},
filesToProcess: {},
nxJsonConfiguration: null,
workspaceRoot: '/virtual',
};
const dependencies = getNpmLockfileDependencies( const dependencies = getNpmLockfileDependencies(
JSON.stringify(rootV2LockFile), JSON.stringify(rootV2LockFile),
hash, hash,
pg ctx
); );
const builder = new ProjectGraphBuilder(pg); const builder = new ProjectGraphBuilder(pg);
@ -370,8 +411,8 @@ describe('NPM lock file utility', () => {
builder.addDependency( builder.addDependency(
dep.source, dep.source,
dep.target, dep.target,
dep.dependencyType, dep.type,
dep.sourceFile 'sourceFile' in dep ? dep.sourceFile : null
); );
} }
const graph = builder.getUpdatedProjectGraph(); const graph = builder.getUpdatedProjectGraph();
@ -470,10 +511,18 @@ describe('NPM lock file utility', () => {
dependencies: {}, dependencies: {},
externalNodes, externalNodes,
}; };
const ctx: CreateDependenciesContext = {
projects: {},
externalNodes,
fileMap: {},
filesToProcess: {},
nxJsonConfiguration: null,
workspaceRoot: '/virtual',
};
const dependencies = getNpmLockfileDependencies( const dependencies = getNpmLockfileDependencies(
JSON.stringify(rootLockFile), JSON.stringify(rootLockFile),
hash, hash,
pg ctx
); );
const builder = new ProjectGraphBuilder(pg); const builder = new ProjectGraphBuilder(pg);
@ -481,8 +530,8 @@ describe('NPM lock file utility', () => {
builder.addDependency( builder.addDependency(
dep.source, dep.source,
dep.target, dep.target,
dep.dependencyType, dep.type,
dep.sourceFile 'sourceFile' in dep ? dep.sourceFile : null
); );
} }
const graph = builder.getUpdatedProjectGraph(); const graph = builder.getUpdatedProjectGraph();
@ -505,10 +554,18 @@ describe('NPM lock file utility', () => {
dependencies: {}, dependencies: {},
externalNodes, externalNodes,
}; };
const ctx: CreateDependenciesContext = {
projects: {},
externalNodes,
fileMap: {},
filesToProcess: {},
nxJsonConfiguration: null,
workspaceRoot: '/virtual',
};
const dependencies = getNpmLockfileDependencies( const dependencies = getNpmLockfileDependencies(
JSON.stringify(rootLockFile), JSON.stringify(rootLockFile),
hash, hash,
pg ctx
); );
const builder = new ProjectGraphBuilder(pg); const builder = new ProjectGraphBuilder(pg);
@ -516,8 +573,8 @@ describe('NPM lock file utility', () => {
builder.addDependency( builder.addDependency(
dep.source, dep.source,
dep.target, dep.target,
dep.dependencyType, dep.type,
dep.sourceFile 'sourceFile' in dep ? dep.sourceFile : null
); );
} }
const graph = builder.getUpdatedProjectGraph(); const graph = builder.getUpdatedProjectGraph();
@ -544,10 +601,18 @@ describe('NPM lock file utility', () => {
dependencies: {}, dependencies: {},
externalNodes, externalNodes,
}; };
const ctx: CreateDependenciesContext = {
projects: {},
externalNodes,
fileMap: {},
filesToProcess: {},
nxJsonConfiguration: null,
workspaceRoot: '/virtual',
};
const dependencies = getNpmLockfileDependencies( const dependencies = getNpmLockfileDependencies(
JSON.stringify(lockFile), JSON.stringify(lockFile),
hash, hash,
pg ctx
); );
const builder = new ProjectGraphBuilder(pg); const builder = new ProjectGraphBuilder(pg);
@ -555,8 +620,8 @@ describe('NPM lock file utility', () => {
builder.addDependency( builder.addDependency(
dep.source, dep.source,
dep.target, dep.target,
dep.dependencyType, dep.type,
dep.sourceFile 'sourceFile' in dep ? dep.sourceFile : null
); );
} }
const graph = builder.getUpdatedProjectGraph(); const graph = builder.getUpdatedProjectGraph();
@ -594,10 +659,18 @@ describe('NPM lock file utility', () => {
dependencies: {}, dependencies: {},
externalNodes, externalNodes,
}; };
const ctx: CreateDependenciesContext = {
projects: {},
externalNodes,
fileMap: {},
filesToProcess: {},
nxJsonConfiguration: null,
workspaceRoot: '/virtual',
};
const dependencies = getNpmLockfileDependencies( const dependencies = getNpmLockfileDependencies(
JSON.stringify(rootLockFile), JSON.stringify(rootLockFile),
hash, hash,
pg ctx
); );
const builder = new ProjectGraphBuilder(pg); const builder = new ProjectGraphBuilder(pg);
@ -605,8 +678,8 @@ describe('NPM lock file utility', () => {
builder.addDependency( builder.addDependency(
dep.source, dep.source,
dep.target, dep.target,
dep.dependencyType, dep.type,
dep.sourceFile 'sourceFile' in dep ? dep.sourceFile : null
); );
} }
const graph = builder.getUpdatedProjectGraph(); const graph = builder.getUpdatedProjectGraph();
@ -646,10 +719,18 @@ describe('NPM lock file utility', () => {
dependencies: {}, dependencies: {},
externalNodes, externalNodes,
}; };
const ctx: CreateDependenciesContext = {
projects: {},
externalNodes,
fileMap: {},
filesToProcess: {},
nxJsonConfiguration: null,
workspaceRoot: '/virtual',
};
const dependencies = getNpmLockfileDependencies( const dependencies = getNpmLockfileDependencies(
JSON.stringify(rootLockFile), JSON.stringify(rootLockFile),
hash, hash,
pg ctx
); );
const builder = new ProjectGraphBuilder(pg); const builder = new ProjectGraphBuilder(pg);
@ -657,8 +738,8 @@ describe('NPM lock file utility', () => {
builder.addDependency( builder.addDependency(
dep.source, dep.source,
dep.target, dep.target,
dep.dependencyType, dep.type,
dep.sourceFile 'sourceFile' in dep ? dep.sourceFile : null
); );
} }
const graph = builder.getUpdatedProjectGraph(); const graph = builder.getUpdatedProjectGraph();

View File

@ -4,7 +4,7 @@ import { workspaceRoot } from '../../../utils/workspace-root';
import { reverse } from '../../../project-graph/operators'; import { reverse } from '../../../project-graph/operators';
import { NormalizedPackageJson } from './utils/package-json'; import { NormalizedPackageJson } from './utils/package-json';
import { import {
ProjectGraphDependencyWithFile, RawProjectGraphDependency,
validateDependency, validateDependency,
} from '../../../project-graph/project-graph-builder'; } from '../../../project-graph/project-graph-builder';
import { import {
@ -13,6 +13,7 @@ import {
ProjectGraphExternalNode, ProjectGraphExternalNode,
} from '../../../config/project-graph'; } from '../../../config/project-graph';
import { hashArray } from '../../../hasher/file-hasher'; import { hashArray } from '../../../hasher/file-hasher';
import { CreateDependenciesContext } from '../../../utils/nx-plugin';
/** /**
* NPM * NPM
@ -87,14 +88,14 @@ export function getNpmLockfileNodes(
export function getNpmLockfileDependencies( export function getNpmLockfileDependencies(
lockFileContent: string, lockFileContent: string,
lockFileHash: string, lockFileHash: string,
projectGraph: ProjectGraph ctx: CreateDependenciesContext
) { ) {
const data = parsePackageLockFile( const data = parsePackageLockFile(
lockFileContent, lockFileContent,
lockFileHash lockFileHash
) as NpmLockFile; ) as NpmLockFile;
return getDependencies(data, keyMap, projectGraph); return getDependencies(data, keyMap, ctx);
} }
function getNodes( function getNodes(
@ -246,9 +247,9 @@ function findV3Version(snapshot: NpmDependencyV3, packageName: string): string {
function getDependencies( function getDependencies(
data: NpmLockFile, data: NpmLockFile,
keyMap: Map<string, ProjectGraphExternalNode>, keyMap: Map<string, ProjectGraphExternalNode>,
projectGraph: ProjectGraph ctx: CreateDependenciesContext
): ProjectGraphDependencyWithFile[] { ): RawProjectGraphDependency[] {
const dependencies: ProjectGraphDependencyWithFile[] = []; const dependencies: RawProjectGraphDependency[] = [];
if (data.lockfileVersion > 1) { if (data.lockfileVersion > 1) {
Object.entries(data.packages).forEach(([path, snapshot]) => { Object.entries(data.packages).forEach(([path, snapshot]) => {
// we are skipping workspaces packages // we are skipping workspaces packages
@ -265,12 +266,12 @@ function getDependencies(
Object.entries(section).forEach(([name, versionRange]) => { Object.entries(section).forEach(([name, versionRange]) => {
const target = findTarget(path, keyMap, name, versionRange); const target = findTarget(path, keyMap, name, versionRange);
if (target) { if (target) {
const dep = { const dep: RawProjectGraphDependency = {
source: sourceName, source: sourceName,
target: target.name, target: target.name,
dependencyType: DependencyType.static, type: DependencyType.static,
}; };
validateDependency(projectGraph, dep); validateDependency(dep, ctx);
dependencies.push(dep); dependencies.push(dep);
} }
}); });
@ -284,7 +285,7 @@ function getDependencies(
snapshot, snapshot,
dependencies, dependencies,
keyMap, keyMap,
projectGraph ctx
); );
}); });
} }
@ -326,21 +327,21 @@ function findTarget(
function addV1NodeDependencies( function addV1NodeDependencies(
path: string, path: string,
snapshot: NpmDependencyV1, snapshot: NpmDependencyV1,
dependencies: ProjectGraphDependencyWithFile[], dependencies: RawProjectGraphDependency[],
keyMap: Map<string, ProjectGraphExternalNode>, keyMap: Map<string, ProjectGraphExternalNode>,
projectGraph: ProjectGraph ctx: CreateDependenciesContext
) { ) {
if (keyMap.has(path) && snapshot.requires) { if (keyMap.has(path) && snapshot.requires) {
const source = keyMap.get(path).name; const source = keyMap.get(path).name;
Object.entries(snapshot.requires).forEach(([name, versionRange]) => { Object.entries(snapshot.requires).forEach(([name, versionRange]) => {
const target = findTarget(path, keyMap, name, versionRange); const target = findTarget(path, keyMap, name, versionRange);
if (target) { if (target) {
const dep = { const dep: RawProjectGraphDependency = {
source: source, source: source,
target: target.name, target: target.name,
dependencyType: DependencyType.static, type: DependencyType.static,
}; };
validateDependency(projectGraph, dep); validateDependency(dep, ctx);
dependencies.push(dep); dependencies.push(dep);
} }
}); });
@ -353,7 +354,7 @@ function addV1NodeDependencies(
depSnapshot, depSnapshot,
dependencies, dependencies,
keyMap, keyMap,
projectGraph ctx
); );
}); });
} }
@ -363,12 +364,12 @@ function addV1NodeDependencies(
Object.entries(peerDependencies).forEach(([depName, depSpec]) => { Object.entries(peerDependencies).forEach(([depName, depSpec]) => {
const target = findTarget(path, keyMap, depName, depSpec); const target = findTarget(path, keyMap, depName, depSpec);
if (target) { if (target) {
const dep = { const dep: RawProjectGraphDependency = {
source: node.name, source: node.name,
target: target.name, target: target.name,
dependencyType: DependencyType.static, type: DependencyType.static,
}; };
validateDependency(projectGraph, dep); validateDependency(dep, ctx);
dependencies.push(dep); dependencies.push(dep);
} }
}); });

View File

@ -9,8 +9,9 @@ import { vol } from 'memfs';
import { pruneProjectGraph } from './project-graph-pruning'; import { pruneProjectGraph } from './project-graph-pruning';
import { import {
ProjectGraphBuilder, ProjectGraphBuilder,
ProjectGraphDependencyWithFile, RawProjectGraphDependency,
} from '../../../project-graph/project-graph-builder'; } from '../../../project-graph/project-graph-builder';
import { CreateDependenciesContext } from '../../../utils/nx-plugin';
jest.mock('fs', () => { jest.mock('fs', () => {
const memFs = require('memfs').fs; const memFs = require('memfs').fs;
@ -127,7 +128,7 @@ describe('pnpm LockFile utility', () => {
}); });
let externalNodes: ProjectGraph['externalNodes']; let externalNodes: ProjectGraph['externalNodes'];
let dependencies: ProjectGraphDependencyWithFile[]; let dependencies: RawProjectGraphDependency[];
let graph: ProjectGraph; let graph: ProjectGraph;
let lockFile: string; let lockFile: string;
@ -147,19 +148,23 @@ describe('pnpm LockFile utility', () => {
dependencies: {}, dependencies: {},
externalNodes, externalNodes,
}; };
dependencies = getPnpmLockfileDependencies( const ctx: CreateDependenciesContext = {
lockFile, projects: {},
lockFileHash, externalNodes,
graph fileMap: {},
); filesToProcess: {},
nxJsonConfiguration: null,
workspaceRoot: '/virtual',
};
dependencies = getPnpmLockfileDependencies(lockFile, lockFileHash, ctx);
const builder = new ProjectGraphBuilder(graph); const builder = new ProjectGraphBuilder(graph);
for (const dep of dependencies) { for (const dep of dependencies) {
builder.addDependency( builder.addDependency(
dep.source, dep.source,
dep.target, dep.target,
dep.dependencyType, dep.type,
dep.sourceFile 'sourceFile' in dep ? dep.sourceFile : null
); );
} }
graph = builder.getUpdatedProjectGraph(); graph = builder.getUpdatedProjectGraph();
@ -204,19 +209,23 @@ describe('pnpm LockFile utility', () => {
dependencies: {}, dependencies: {},
externalNodes, externalNodes,
}; };
dependencies = getPnpmLockfileDependencies( const ctx: CreateDependenciesContext = {
lockFile, projects: {},
lockFileHash, externalNodes,
graph fileMap: {},
); filesToProcess: {},
nxJsonConfiguration: null,
workspaceRoot: '/virtual',
};
dependencies = getPnpmLockfileDependencies(lockFile, lockFileHash, ctx);
const builder = new ProjectGraphBuilder(graph); const builder = new ProjectGraphBuilder(graph);
for (const dep of dependencies) { for (const dep of dependencies) {
builder.addDependency( builder.addDependency(
dep.source, dep.source,
dep.target, dep.target,
dep.dependencyType, dep.type,
dep.sourceFile 'sourceFile' in dep ? dep.sourceFile : null
); );
} }
graph = builder.getUpdatedProjectGraph(); graph = builder.getUpdatedProjectGraph();
@ -247,10 +256,18 @@ describe('pnpm LockFile utility', () => {
dependencies: {}, dependencies: {},
externalNodes, externalNodes,
}; };
const appCtx: CreateDependenciesContext = {
projects: {},
externalNodes,
fileMap: {},
filesToProcess: {},
nxJsonConfiguration: null,
workspaceRoot: '/virtual',
};
const dependencies = getPnpmLockfileDependencies( const dependencies = getPnpmLockfileDependencies(
appLockFile, appLockFile,
appLockFileHash, appLockFileHash,
appGraph appCtx
); );
const builder = new ProjectGraphBuilder(appGraph); const builder = new ProjectGraphBuilder(appGraph);
@ -258,8 +275,8 @@ describe('pnpm LockFile utility', () => {
builder.addDependency( builder.addDependency(
dep.source, dep.source,
dep.target, dep.target,
dep.dependencyType, dep.type,
dep.sourceFile 'sourceFile' in dep ? dep.sourceFile : null
); );
} }
appGraph = builder.getUpdatedProjectGraph(); appGraph = builder.getUpdatedProjectGraph();
@ -317,10 +334,18 @@ describe('pnpm LockFile utility', () => {
dependencies: {}, dependencies: {},
externalNodes, externalNodes,
}; };
const ctx: CreateDependenciesContext = {
projects: {},
externalNodes,
fileMap: {},
filesToProcess: {},
nxJsonConfiguration: null,
workspaceRoot: '/virtual',
};
const dependencies = getPnpmLockfileDependencies( const dependencies = getPnpmLockfileDependencies(
lockFile, lockFile,
lockFileHash, lockFileHash,
graph ctx
); );
const builder = new ProjectGraphBuilder(graph); const builder = new ProjectGraphBuilder(graph);
@ -328,8 +353,8 @@ describe('pnpm LockFile utility', () => {
builder.addDependency( builder.addDependency(
dep.source, dep.source,
dep.target, dep.target,
dep.dependencyType, dep.type,
dep.sourceFile 'sourceFile' in dep ? dep.sourceFile : null
); );
} }
graph = builder.getUpdatedProjectGraph(); graph = builder.getUpdatedProjectGraph();
@ -420,11 +445,18 @@ describe('pnpm LockFile utility', () => {
dependencies: {}, dependencies: {},
externalNodes, externalNodes,
}; };
const ctx: CreateDependenciesContext = {
projects: {},
externalNodes,
fileMap: {},
filesToProcess: {},
nxJsonConfiguration: null,
workspaceRoot: '/virtual',
};
const dependencies = getPnpmLockfileDependencies( const dependencies = getPnpmLockfileDependencies(
lockFile, lockFile,
lockFileHash, lockFileHash,
graph ctx
); );
const builder = new ProjectGraphBuilder(graph); const builder = new ProjectGraphBuilder(graph);
@ -432,8 +464,8 @@ describe('pnpm LockFile utility', () => {
builder.addDependency( builder.addDependency(
dep.source, dep.source,
dep.target, dep.target,
dep.dependencyType, dep.type,
dep.sourceFile 'sourceFile' in dep ? dep.sourceFile : null
); );
} }
graph = builder.getUpdatedProjectGraph(); graph = builder.getUpdatedProjectGraph();
@ -481,10 +513,18 @@ describe('pnpm LockFile utility', () => {
dependencies: {}, dependencies: {},
externalNodes, externalNodes,
}; };
const ctx: CreateDependenciesContext = {
projects: {},
externalNodes,
fileMap: {},
filesToProcess: {},
nxJsonConfiguration: null,
workspaceRoot: '/virtual',
};
const dependencies = getPnpmLockfileDependencies( const dependencies = getPnpmLockfileDependencies(
lockFile, lockFile,
lockFileHash, lockFileHash,
graph ctx
); );
const builder = new ProjectGraphBuilder(graph); const builder = new ProjectGraphBuilder(graph);
@ -492,8 +532,8 @@ describe('pnpm LockFile utility', () => {
builder.addDependency( builder.addDependency(
dep.source, dep.source,
dep.target, dep.target,
dep.dependencyType, dep.type,
dep.sourceFile 'sourceFile' in dep ? dep.sourceFile : null
); );
} }
graph = builder.getUpdatedProjectGraph(); graph = builder.getUpdatedProjectGraph();
@ -528,10 +568,18 @@ describe('pnpm LockFile utility', () => {
dependencies: {}, dependencies: {},
externalNodes, externalNodes,
}; };
const ctx: CreateDependenciesContext = {
projects: {},
externalNodes,
fileMap: {},
filesToProcess: {},
nxJsonConfiguration: null,
workspaceRoot: '/virtual',
};
const dependencies = getPnpmLockfileDependencies( const dependencies = getPnpmLockfileDependencies(
lockFile, lockFile,
lockFileHash, lockFileHash,
graph ctx
); );
const builder = new ProjectGraphBuilder(graph); const builder = new ProjectGraphBuilder(graph);
@ -539,8 +587,8 @@ describe('pnpm LockFile utility', () => {
builder.addDependency( builder.addDependency(
dep.source, dep.source,
dep.target, dep.target,
dep.dependencyType, dep.type,
dep.sourceFile 'sourceFile' in dep ? dep.sourceFile : null
); );
} }
graph = builder.getUpdatedProjectGraph(); graph = builder.getUpdatedProjectGraph();
@ -588,10 +636,18 @@ describe('pnpm LockFile utility', () => {
dependencies: {}, dependencies: {},
externalNodes, externalNodes,
}; };
const ctx: CreateDependenciesContext = {
projects: {},
externalNodes,
fileMap: {},
filesToProcess: {},
nxJsonConfiguration: null,
workspaceRoot: '/virtual',
};
const dependencies = getPnpmLockfileDependencies( const dependencies = getPnpmLockfileDependencies(
lockFile, lockFile,
lockFileHash, lockFileHash,
graph ctx
); );
const builder = new ProjectGraphBuilder(graph); const builder = new ProjectGraphBuilder(graph);
@ -599,8 +655,8 @@ describe('pnpm LockFile utility', () => {
builder.addDependency( builder.addDependency(
dep.source, dep.source,
dep.target, dep.target,
dep.dependencyType, dep.type,
dep.sourceFile 'sourceFile' in dep ? dep.sourceFile : null
); );
} }
graph = builder.getUpdatedProjectGraph(); graph = builder.getUpdatedProjectGraph();
@ -659,10 +715,18 @@ describe('pnpm LockFile utility', () => {
dependencies: {}, dependencies: {},
externalNodes, externalNodes,
}; };
const ctx: CreateDependenciesContext = {
projects: {},
externalNodes,
fileMap: {},
filesToProcess: {},
nxJsonConfiguration: null,
workspaceRoot: '/virtual',
};
const dependencies = getPnpmLockfileDependencies( const dependencies = getPnpmLockfileDependencies(
lockFile, lockFile,
lockFileHash, lockFileHash,
graph ctx
); );
const builder = new ProjectGraphBuilder(graph); const builder = new ProjectGraphBuilder(graph);
@ -670,8 +734,8 @@ describe('pnpm LockFile utility', () => {
builder.addDependency( builder.addDependency(
dep.source, dep.source,
dep.target, dep.target,
dep.dependencyType, dep.type,
dep.sourceFile 'sourceFile' in dep ? dep.sourceFile : null
); );
} }
graph = builder.getUpdatedProjectGraph(); graph = builder.getUpdatedProjectGraph();

View File

@ -16,7 +16,7 @@ import {
} from './utils/package-json'; } from './utils/package-json';
import { sortObjectByKeys } from '../../../utils/object-sort'; import { sortObjectByKeys } from '../../../utils/object-sort';
import { import {
ProjectGraphDependencyWithFile, RawProjectGraphDependency,
validateDependency, validateDependency,
} from '../../../project-graph/project-graph-builder'; } from '../../../project-graph/project-graph-builder';
import { import {
@ -25,6 +25,7 @@ import {
ProjectGraphExternalNode, ProjectGraphExternalNode,
} from '../../../config/project-graph'; } from '../../../config/project-graph';
import { hashArray } from '../../../hasher/file-hasher'; import { hashArray } from '../../../hasher/file-hasher';
import { CreateDependenciesContext } from '../../../utils/nx-plugin';
// we use key => node map to avoid duplicate work when parsing keys // we use key => node map to avoid duplicate work when parsing keys
let keyMap = new Map<string, ProjectGraphExternalNode>(); let keyMap = new Map<string, ProjectGraphExternalNode>();
@ -56,12 +57,12 @@ export function getPnpmLockfileNodes(
export function getPnpmLockfileDependencies( export function getPnpmLockfileDependencies(
lockFileContent: string, lockFileContent: string,
lockFileHash: string, lockFileHash: string,
projectGraph: ProjectGraph ctx: CreateDependenciesContext
) { ) {
const data = parsePnpmLockFile(lockFileContent, lockFileHash); const data = parsePnpmLockFile(lockFileContent, lockFileHash);
const isV6 = isV6Lockfile(data); const isV6 = isV6Lockfile(data);
return getDependencies(data, keyMap, isV6, projectGraph); return getDependencies(data, keyMap, isV6, ctx);
} }
function getNodes( function getNodes(
@ -157,9 +158,9 @@ function getDependencies(
data: Lockfile, data: Lockfile,
keyMap: Map<string, ProjectGraphExternalNode>, keyMap: Map<string, ProjectGraphExternalNode>,
isV6: boolean, isV6: boolean,
projectGraph: ProjectGraph ctx: CreateDependenciesContext
): ProjectGraphDependencyWithFile[] { ): RawProjectGraphDependency[] {
const results: ProjectGraphDependencyWithFile[] = []; const results: RawProjectGraphDependency[] = [];
Object.entries(data.packages).forEach(([key, snapshot]) => { Object.entries(data.packages).forEach(([key, snapshot]) => {
const node = keyMap.get(key); const node = keyMap.get(key);
[snapshot.dependencies, snapshot.optionalDependencies].forEach( [snapshot.dependencies, snapshot.optionalDependencies].forEach(
@ -171,15 +172,15 @@ function getDependencies(
isV6 isV6
); );
const target = const target =
projectGraph.externalNodes[`npm:${name}@${version}`] || ctx.externalNodes[`npm:${name}@${version}`] ||
projectGraph.externalNodes[`npm:${name}`]; ctx.externalNodes[`npm:${name}`];
if (target) { if (target) {
const dep = { const dep: RawProjectGraphDependency = {
source: node.name, source: node.name,
target: target.name, target: target.name,
dependencyType: DependencyType.static, type: DependencyType.static,
}; };
validateDependency(projectGraph, dep); validateDependency(dep, ctx);
results.push(dep); results.push(dep);
} }
}); });

View File

@ -9,6 +9,7 @@ import { vol } from 'memfs';
import { ProjectGraph } from '../../../config/project-graph'; import { ProjectGraph } from '../../../config/project-graph';
import { PackageJson } from '../../../utils/package-json'; import { PackageJson } from '../../../utils/package-json';
import { ProjectGraphBuilder } from '../../../project-graph/project-graph-builder'; import { ProjectGraphBuilder } from '../../../project-graph/project-graph-builder';
import { CreateDependenciesContext } from '../../../utils/nx-plugin';
jest.mock('fs', () => { jest.mock('fs', () => {
const memFs = require('memfs').fs; const memFs = require('memfs').fs;
@ -188,15 +189,23 @@ describe('yarn LockFile utility', () => {
dependencies: {}, dependencies: {},
externalNodes, 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); const builder = new ProjectGraphBuilder(pg);
for (const dep of dependencies) { for (const dep of dependencies) {
builder.addDependency( builder.addDependency(
dep.source, dep.source,
dep.target, dep.target,
dep.dependencyType, dep.type,
dep.sourceFile 'sourceFile' in dep ? dep.sourceFile : null
); );
} }
graph = builder.getUpdatedProjectGraph(); graph = builder.getUpdatedProjectGraph();
@ -498,15 +507,23 @@ describe('yarn LockFile utility', () => {
dependencies: {}, dependencies: {},
externalNodes, 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); const builder = new ProjectGraphBuilder(pg);
for (const dep of dependencies) { for (const dep of dependencies) {
builder.addDependency( builder.addDependency(
dep.source, dep.source,
dep.target, dep.target,
dep.dependencyType, dep.type,
dep.sourceFile 'sourceFile' in dep ? dep.sourceFile : null
); );
} }
const graph = builder.getUpdatedProjectGraph(); const graph = builder.getUpdatedProjectGraph();
@ -557,15 +574,23 @@ describe('yarn LockFile utility', () => {
dependencies: {}, dependencies: {},
externalNodes, 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); const builder = new ProjectGraphBuilder(pg);
for (const dep of dependencies) { for (const dep of dependencies) {
builder.addDependency( builder.addDependency(
dep.source, dep.source,
dep.target, dep.target,
dep.dependencyType, dep.type,
dep.sourceFile 'sourceFile' in dep ? dep.sourceFile : null
); );
} }
const graph = builder.getUpdatedProjectGraph(); const graph = builder.getUpdatedProjectGraph();
@ -688,15 +713,23 @@ describe('yarn LockFile utility', () => {
dependencies: {}, dependencies: {},
externalNodes, 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); const builder = new ProjectGraphBuilder(pg);
for (const dep of dependencies) { for (const dep of dependencies) {
builder.addDependency( builder.addDependency(
dep.source, dep.source,
dep.target, dep.target,
dep.dependencyType, dep.type,
dep.sourceFile 'sourceFile' in dep ? dep.sourceFile : null
); );
} }
const graph = builder.getUpdatedProjectGraph(); const graph = builder.getUpdatedProjectGraph();
@ -757,15 +790,23 @@ __metadata:
dependencies: {}, dependencies: {},
externalNodes, 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); const builder = new ProjectGraphBuilder(pg);
for (const dep of dependencies) { for (const dep of dependencies) {
builder.addDependency( builder.addDependency(
dep.source, dep.source,
dep.target, dep.target,
dep.dependencyType, dep.type,
dep.sourceFile 'sourceFile' in dep ? dep.sourceFile : null
); );
} }
const graph = builder.getUpdatedProjectGraph(); const graph = builder.getUpdatedProjectGraph();
@ -1223,15 +1264,23 @@ nx-cloud@latest:
dependencies: {}, dependencies: {},
externalNodes, 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); const builder = new ProjectGraphBuilder(pg);
for (const dep of dependencies) { for (const dep of dependencies) {
builder.addDependency( builder.addDependency(
dep.source, dep.source,
dep.target, dep.target,
dep.dependencyType, dep.type,
dep.sourceFile 'sourceFile' in dep ? dep.sourceFile : null
); );
} }
const graph = builder.getUpdatedProjectGraph(); const graph = builder.getUpdatedProjectGraph();
@ -1424,15 +1473,23 @@ nx-cloud@latest:
dependencies: {}, dependencies: {},
externalNodes, 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); const builder = new ProjectGraphBuilder(pg);
for (const dep of dependencies) { for (const dep of dependencies) {
builder.addDependency( builder.addDependency(
dep.source, dep.source,
dep.target, dep.target,
dep.dependencyType, dep.type,
dep.sourceFile 'sourceFile' in dep ? dep.sourceFile : null
); );
} }
const graph = builder.getUpdatedProjectGraph(); const graph = builder.getUpdatedProjectGraph();
@ -1473,15 +1530,23 @@ nx-cloud@latest:
dependencies: {}, dependencies: {},
externalNodes, 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); const builder = new ProjectGraphBuilder(pg);
for (const dep of dependencies) { for (const dep of dependencies) {
builder.addDependency( builder.addDependency(
dep.source, dep.source,
dep.target, dep.target,
dep.dependencyType, dep.type,
dep.sourceFile 'sourceFile' in dep ? dep.sourceFile : null
); );
} }
const graph = builder.getUpdatedProjectGraph(); const graph = builder.getUpdatedProjectGraph();
@ -1608,15 +1673,23 @@ type-fest@^0.20.2:
dependencies: {}, dependencies: {},
externalNodes, 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); const builder = new ProjectGraphBuilder(pg);
for (const dep of dependencies) { for (const dep of dependencies) {
builder.addDependency( builder.addDependency(
dep.source, dep.source,
dep.target, dep.target,
dep.dependencyType, dep.type,
dep.sourceFile 'sourceFile' in dep ? dep.sourceFile : null
); );
} }
const graph = builder.getUpdatedProjectGraph(); const graph = builder.getUpdatedProjectGraph();
@ -1713,15 +1786,23 @@ __metadata:
dependencies: {}, dependencies: {},
externalNodes, 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); const builder = new ProjectGraphBuilder(pg);
for (const dep of dependencies) { for (const dep of dependencies) {
builder.addDependency( builder.addDependency(
dep.source, dep.source,
dep.target, dep.target,
dep.dependencyType, dep.type,
dep.sourceFile 'sourceFile' in dep ? dep.sourceFile : null
); );
} }
const graph = builder.getUpdatedProjectGraph(); const graph = builder.getUpdatedProjectGraph();
@ -1828,15 +1909,23 @@ __metadata:
dependencies: {}, dependencies: {},
externalNodes, 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); const builder = new ProjectGraphBuilder(pg);
for (const dep of dependencies) { for (const dep of dependencies) {
builder.addDependency( builder.addDependency(
dep.source, dep.source,
dep.target, dep.target,
dep.dependencyType, dep.type,
dep.sourceFile 'sourceFile' in dep ? dep.sourceFile : null
); );
} }
const graph = builder.getUpdatedProjectGraph(); const graph = builder.getUpdatedProjectGraph();
@ -2057,15 +2146,23 @@ __metadata:
dependencies: {}, dependencies: {},
externalNodes, 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); const builder = new ProjectGraphBuilder(pg);
for (const dep of dependencies) { for (const dep of dependencies) {
builder.addDependency( builder.addDependency(
dep.source, dep.source,
dep.target, dep.target,
dep.dependencyType, dep.type,
dep.sourceFile 'sourceFile' in dep ? dep.sourceFile : null
); );
} }
const graph = builder.getUpdatedProjectGraph(); const graph = builder.getUpdatedProjectGraph();
@ -2436,15 +2533,23 @@ __metadata:
dependencies: {}, dependencies: {},
externalNodes, 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); const builder = new ProjectGraphBuilder(pg);
for (const dep of dependencies) { for (const dep of dependencies) {
builder.addDependency( builder.addDependency(
dep.source, dep.source,
dep.target, dep.target,
dep.dependencyType, dep.type,
dep.sourceFile 'sourceFile' in dep ? dep.sourceFile : null
); );
} }
const graph = builder.getUpdatedProjectGraph(); const graph = builder.getUpdatedProjectGraph();

View File

@ -3,7 +3,7 @@ import {
NormalizedPackageJson, NormalizedPackageJson,
} from './utils/package-json'; } from './utils/package-json';
import { import {
ProjectGraphDependencyWithFile, RawProjectGraphDependency,
validateDependency, validateDependency,
} from '../../../project-graph/project-graph-builder'; } from '../../../project-graph/project-graph-builder';
import { gt, Range, satisfies } from 'semver'; import { gt, Range, satisfies } from 'semver';
@ -14,6 +14,7 @@ import {
} from '../../../config/project-graph'; } from '../../../config/project-graph';
import { hashArray } from '../../../hasher/file-hasher'; import { hashArray } from '../../../hasher/file-hasher';
import { sortObjectByKeys } from '../../../utils/object-sort'; import { sortObjectByKeys } from '../../../utils/object-sort';
import { CreateDependenciesContext } from '../../../utils/nx-plugin';
/** /**
* Yarn * Yarn
@ -79,7 +80,7 @@ export function getYarnLockfileNodes(
export function getYarnLockfileDependencies( export function getYarnLockfileDependencies(
lockFileContent: string, lockFileContent: string,
lockFileHash: string, lockFileHash: string,
projectGraph: ProjectGraph ctx: CreateDependenciesContext
) { ) {
const { __metadata, ...dependencies } = parseLockFile( const { __metadata, ...dependencies } = parseLockFile(
lockFileContent, lockFileContent,
@ -91,7 +92,7 @@ export function getYarnLockfileDependencies(
// yarn classic splits keys when parsing so we need to stich them back together // yarn classic splits keys when parsing so we need to stich them back together
const groupedDependencies = groupDependencies(dependencies, isBerry); const groupedDependencies = groupDependencies(dependencies, isBerry);
return getDependencies(groupedDependencies, keyMap, projectGraph); return getDependencies(groupedDependencies, keyMap, ctx);
} }
function getPackageNameKeyPairs(keys: string): Map<string, Set<string>> { function getPackageNameKeyPairs(keys: string): Map<string, Set<string>> {
@ -289,9 +290,9 @@ function getHoistedVersion(packageName: string): string {
function getDependencies( function getDependencies(
dependencies: Record<string, YarnDependency>, dependencies: Record<string, YarnDependency>,
keyMap: Map<string, ProjectGraphExternalNode>, keyMap: Map<string, ProjectGraphExternalNode>,
projectGraph: ProjectGraph ctx: CreateDependenciesContext
) { ) {
const projectGraphDependencies: ProjectGraphDependencyWithFile[] = []; const projectGraphDependencies: RawProjectGraphDependency[] = [];
Object.keys(dependencies).forEach((keys) => { Object.keys(dependencies).forEach((keys) => {
const snapshot = dependencies[keys]; const snapshot = dependencies[keys];
keys.split(', ').forEach((key) => { keys.split(', ').forEach((key) => {
@ -305,12 +306,12 @@ function getDependencies(
keyMap.get(`${name}@npm:${versionRange}`) || keyMap.get(`${name}@npm:${versionRange}`) ||
keyMap.get(`${name}@${versionRange}`); keyMap.get(`${name}@${versionRange}`);
if (target) { if (target) {
const dep = { const dep: RawProjectGraphDependency = {
source: node.name, source: node.name,
target: target.name, target: target.name,
dependencyType: DependencyType.static, type: DependencyType.static,
}; };
validateDependency(projectGraph, dep); validateDependency(dep, ctx);
projectGraphDependencies.push(dep); projectGraphDependencies.push(dep);
} }
}); });

View File

@ -1,7 +1,7 @@
import { buildExplicitTypeScriptDependencies } from './explicit-project-dependencies'; import { buildExplicitTypeScriptDependencies } from './explicit-project-dependencies';
import { buildExplicitPackageJsonDependencies } from './explicit-package-json-dependencies'; import { buildExplicitPackageJsonDependencies } from './explicit-package-json-dependencies';
import { CreateDependenciesContext } from '../../../../utils/nx-plugin'; 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( export function buildExplicitDependencies(
jsPluginConfig: { jsPluginConfig: {
@ -9,10 +9,10 @@ export function buildExplicitDependencies(
analyzePackageJson?: boolean; analyzePackageJson?: boolean;
}, },
ctx: CreateDependenciesContext ctx: CreateDependenciesContext
): ProjectGraphDependencyWithFile[] { ): RawProjectGraphDependency[] {
if (totalNumberOfFilesToProcess(ctx) === 0) return []; if (totalNumberOfFilesToProcess(ctx) === 0) return [];
let dependencies: ProjectGraphDependencyWithFile[] = []; let dependencies: RawProjectGraphDependency[] = [];
if ( if (
jsPluginConfig.analyzeSourceFiles === undefined || jsPluginConfig.analyzeSourceFiles === undefined ||

View File

@ -93,10 +93,11 @@ describe('explicit package json dependencies', () => {
ctx = { ctx = {
fileMap: projectFileMap, fileMap: projectFileMap,
graph: builder.getUpdatedProjectGraph(), externalNodes: builder.getUpdatedProjectGraph().externalNodes,
projectsConfigurations: projectsConfigurations as any, projects: projectsConfigurations.projects,
nxJsonConfiguration, nxJsonConfiguration,
filesToProcess: projectFileMap, filesToProcess: projectFileMap,
workspaceRoot: tempFs.tempDir,
}; };
}); });
@ -112,19 +113,19 @@ describe('explicit package json dependencies', () => {
source: 'proj', source: 'proj',
target: 'proj2', target: 'proj2',
sourceFile: 'libs/proj/package.json', sourceFile: 'libs/proj/package.json',
dependencyType: 'static', type: 'static',
}, },
{ {
sourceFile: 'libs/proj/package.json', sourceFile: 'libs/proj/package.json',
source: 'proj', source: 'proj',
target: 'npm:external', target: 'npm:external',
dependencyType: 'static', type: 'static',
}, },
{ {
source: 'proj', source: 'proj',
target: 'proj3', target: 'proj3',
sourceFile: 'libs/proj/package.json', sourceFile: 'libs/proj/package.json',
dependencyType: 'static', type: 'static',
}, },
]); ]);
}); });

View File

@ -1,38 +1,34 @@
import { defaultFileRead } from '../../../../project-graph/file-utils'; import { defaultFileRead } from '../../../../project-graph/file-utils';
import { join } from 'path'; import { join } from 'path';
import { import { DependencyType } from '../../../../config/project-graph';
DependencyType,
ProjectGraph,
ProjectGraphProjectNode,
} from '../../../../config/project-graph';
import { parseJson } from '../../../../utils/json'; import { parseJson } from '../../../../utils/json';
import { joinPathFragments } from '../../../../utils/path'; 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 { NxJsonConfiguration } from '../../../../config/nx-json';
import { PackageJson } from '../../../../utils/package-json'; import { PackageJson } from '../../../../utils/package-json';
import { CreateDependenciesContext } from '../../../../utils/nx-plugin'; import { CreateDependenciesContext } from '../../../../utils/nx-plugin';
import { import {
ProjectGraphDependencyWithFile, RawProjectGraphDependency,
validateDependency, validateDependency,
} from '../../../../project-graph/project-graph-builder'; } from '../../../../project-graph/project-graph-builder';
export function buildExplicitPackageJsonDependencies({ export function buildExplicitPackageJsonDependencies(
nxJsonConfiguration, ctx: CreateDependenciesContext
projectsConfigurations, ): RawProjectGraphDependency[] {
graph, const res: RawProjectGraphDependency[] = [];
filesToProcess,
}: CreateDependenciesContext): ProjectGraphDependencyWithFile[] {
const res: ProjectGraphDependencyWithFile[] = [];
let packageNameMap = undefined; let packageNameMap = undefined;
const nodes = Object.values(graph.nodes); const nodes = Object.values(ctx.projects);
Object.keys(filesToProcess).forEach((source) => { Object.keys(ctx.filesToProcess).forEach((source) => {
Object.values(filesToProcess[source]).forEach((f) => { Object.values(ctx.filesToProcess[source]).forEach((f) => {
if (isPackageJsonAtProjectRoot(nodes, f.file)) { if (isPackageJsonAtProjectRoot(nodes, f.file)) {
// we only create the package name map once and only if a package.json file changes // we only create the package name map once and only if a package.json file changes
packageNameMap = packageNameMap =
packageNameMap || packageNameMap ||
createPackageNameMap(nxJsonConfiguration, projectsConfigurations); createPackageNameMap(ctx.nxJsonConfiguration, ctx.projects);
processPackageJson(source, f.file, graph, res, packageNameMap); processPackageJson(source, f.file, ctx, res, packageNameMap);
} }
}); });
}); });
@ -41,18 +37,13 @@ export function buildExplicitPackageJsonDependencies({
function createPackageNameMap( function createPackageNameMap(
nxJsonConfiguration: NxJsonConfiguration, nxJsonConfiguration: NxJsonConfiguration,
projectsConfigurations: ProjectsConfigurations projects: ProjectsConfigurations['projects']
) { ) {
const res = {}; const res = {};
for (let projectName of Object.keys(projectsConfigurations.projects)) { for (let projectName of Object.keys(projects)) {
try { try {
const packageJson = parseJson( const packageJson = parseJson(
defaultFileRead( defaultFileRead(join(projects[projectName].root, 'package.json'))
join(
projectsConfigurations.projects[projectName].root,
'package.json'
)
)
); );
// TODO(v17): Stop reading nx.json for the npmScope // TODO(v17): Stop reading nx.json for the npmScope
const npmScope = nxJsonConfiguration.npmScope; const npmScope = nxJsonConfiguration.npmScope;
@ -68,15 +59,14 @@ function createPackageNameMap(
} }
function isPackageJsonAtProjectRoot( function isPackageJsonAtProjectRoot(
nodes: ProjectGraphProjectNode[], nodes: ProjectConfiguration[],
fileName: string fileName: string
) { ) {
return ( return (
fileName.endsWith('package.json') && fileName.endsWith('package.json') &&
nodes.find( nodes.find(
(projectNode) => (projectNode) =>
(projectNode.type === 'lib' || projectNode.type === 'app') && joinPathFragments(projectNode.root, 'package.json') === fileName
joinPathFragments(projectNode.data.root, 'package.json') === fileName
) )
); );
} }
@ -84,8 +74,8 @@ function isPackageJsonAtProjectRoot(
function processPackageJson( function processPackageJson(
sourceProject: string, sourceProject: string,
fileName: string, fileName: string,
graph: ProjectGraph, ctx: CreateDependenciesContext,
collectedDeps: ProjectGraphDependencyWithFile[], collectedDeps: RawProjectGraphDependency[],
packageNameMap: { [packageName: string]: string } packageNameMap: { [packageName: string]: string }
) { ) {
try { try {
@ -94,22 +84,22 @@ function processPackageJson(
deps.forEach((d) => { deps.forEach((d) => {
// package.json refers to another project in the monorepo // package.json refers to another project in the monorepo
if (packageNameMap[d]) { if (packageNameMap[d]) {
const dependency = { const dependency: RawProjectGraphDependency = {
source: sourceProject, source: sourceProject,
target: packageNameMap[d], target: packageNameMap[d],
sourceFile: fileName, sourceFile: fileName,
dependencyType: DependencyType.static, type: DependencyType.static,
}; };
validateDependency(graph, dependency); validateDependency(dependency, ctx);
collectedDeps.push(dependency); collectedDeps.push(dependency);
} else if (graph.externalNodes[`npm:${d}`]) { } else if (ctx.externalNodes[`npm:${d}`]) {
const dependency = { const dependency: RawProjectGraphDependency = {
source: sourceProject, source: sourceProject,
target: `npm:${d}`, target: `npm:${d}`,
sourceFile: fileName, sourceFile: fileName,
dependencyType: DependencyType.static, type: DependencyType.static,
}; };
validateDependency(graph, dependency); validateDependency(dependency, ctx);
collectedDeps.push(dependency); collectedDeps.push(dependency);
} }
}); });

View File

@ -43,25 +43,25 @@ describe('explicit project dependencies', () => {
source, source,
sourceFile: 'libs/proj/index.ts', sourceFile: 'libs/proj/index.ts',
target: 'proj2', target: 'proj2',
dependencyType: 'static', type: 'static',
}, },
{ {
source, source,
sourceFile: 'libs/proj/index.ts', sourceFile: 'libs/proj/index.ts',
target: 'proj4ab', target: 'proj4ab',
dependencyType: 'static', type: 'static',
}, },
{ {
source, source,
sourceFile: 'libs/proj/index.ts', sourceFile: 'libs/proj/index.ts',
target: 'npm:npm-package', target: 'npm:npm-package',
dependencyType: 'static', type: 'static',
}, },
{ {
source, source,
sourceFile: 'libs/proj/index.ts', sourceFile: 'libs/proj/index.ts',
target: 'proj3a', target: 'proj3a',
dependencyType: 'dynamic', type: 'dynamic',
}, },
]); ]);
}); });
@ -89,19 +89,19 @@ describe('explicit project dependencies', () => {
source, source,
sourceFile: 'libs/proj/index.ts', sourceFile: 'libs/proj/index.ts',
target: 'proj2', target: 'proj2',
dependencyType: 'static', type: 'static',
}, },
{ {
source, source,
sourceFile: 'libs/proj/index.ts', sourceFile: 'libs/proj/index.ts',
target: 'proj3a', target: 'proj3a',
dependencyType: 'static', type: 'static',
}, },
{ {
source, source,
sourceFile: 'libs/proj/index.ts', sourceFile: 'libs/proj/index.ts',
target: 'proj4ab', target: 'proj4ab',
dependencyType: 'static', type: 'static',
}, },
]); ]);
}); });
@ -128,19 +128,19 @@ describe('explicit project dependencies', () => {
source, source,
sourceFile: 'libs/proj/index.mts', sourceFile: 'libs/proj/index.mts',
target: 'proj2', target: 'proj2',
dependencyType: 'static', type: 'static',
}, },
{ {
source, source,
sourceFile: 'libs/proj/index.mts', sourceFile: 'libs/proj/index.mts',
target: 'proj3a', target: 'proj3a',
dependencyType: 'static', type: 'static',
}, },
{ {
source, source,
sourceFile: 'libs/proj/index.mts', sourceFile: 'libs/proj/index.mts',
target: 'proj4ab', target: 'proj4ab',
dependencyType: 'static', type: 'static',
}, },
]); ]);
}); });
@ -168,19 +168,19 @@ describe('explicit project dependencies', () => {
source, source,
sourceFile: 'libs/proj/index.ts', sourceFile: 'libs/proj/index.ts',
target: 'proj2', target: 'proj2',
dependencyType: 'static', type: 'static',
}, },
{ {
source, source,
sourceFile: 'libs/proj/index.ts', sourceFile: 'libs/proj/index.ts',
target: 'proj3a', target: 'proj3a',
dependencyType: 'static', type: 'static',
}, },
{ {
source, source,
sourceFile: 'libs/proj/index.ts', sourceFile: 'libs/proj/index.ts',
target: 'proj4ab', target: 'proj4ab',
dependencyType: 'static', type: 'static',
}, },
]); ]);
}); });
@ -229,19 +229,19 @@ describe('explicit project dependencies', () => {
source, source,
sourceFile: 'libs/proj/component.tsx', sourceFile: 'libs/proj/component.tsx',
target: 'proj2', target: 'proj2',
dependencyType: 'dynamic', type: 'dynamic',
}, },
{ {
source, source,
sourceFile: 'libs/proj/nested-dynamic-import.ts', sourceFile: 'libs/proj/nested-dynamic-import.ts',
target: 'proj3a', target: 'proj3a',
dependencyType: 'dynamic', type: 'dynamic',
}, },
{ {
source, source,
sourceFile: 'libs/proj/nested-require.ts', sourceFile: 'libs/proj/nested-require.ts',
target: 'proj4ab', target: 'proj4ab',
dependencyType: 'static', type: 'static',
}, },
]); ]);
}); });
@ -273,13 +273,13 @@ describe('explicit project dependencies', () => {
source, source,
sourceFile: 'libs/proj/absolute-path.ts', sourceFile: 'libs/proj/absolute-path.ts',
target: 'proj3a', target: 'proj3a',
dependencyType: 'dynamic', type: 'dynamic',
}, },
{ {
source, source,
sourceFile: 'libs/proj/relative-path.ts', sourceFile: 'libs/proj/relative-path.ts',
target: 'proj4ab', target: 'proj4ab',
dependencyType: 'dynamic', type: 'dynamic',
}, },
]); ]);
}); });
@ -563,10 +563,11 @@ async function createContext(
await retrieveWorkspaceFiles(tempFs.tempDir, nxJson); await retrieveWorkspaceFiles(tempFs.tempDir, nxJson);
return { return {
graph: builder.getUpdatedProjectGraph(), externalNodes: builder.getUpdatedProjectGraph().externalNodes,
projectsConfigurations: projectConfigurations, projects: projectConfigurations.projects,
nxJsonConfiguration: nxJson, nxJsonConfiguration: nxJson,
filesToProcess: projectFileMap, filesToProcess: projectFileMap,
fileMap: projectFileMap, fileMap: projectFileMap,
workspaceRoot: tempFs.tempDir,
}; };
} }

View File

@ -1,25 +1,32 @@
import { TargetProjectLocator } from './target-project-locator'; 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 { join, relative } from 'path';
import { workspaceRoot } from '../../../../utils/workspace-root'; import { workspaceRoot } from '../../../../utils/workspace-root';
import { normalizePath } from '../../../../utils/path'; import { normalizePath } from '../../../../utils/path';
import { CreateDependenciesContext } from '../../../../utils/nx-plugin'; import { CreateDependenciesContext } from '../../../../utils/nx-plugin';
import { import {
ProjectGraphDependencyWithFile, RawProjectGraphDependency,
validateDependency, validateDependency,
} from '../../../../project-graph/project-graph-builder'; } from '../../../../project-graph/project-graph-builder';
import { ProjectConfiguration } from '../../../../config/workspace-json-project-json';
function isRoot(graph: ProjectGraph, projectName: string): boolean { function isRoot(
return graph.nodes[projectName]?.data?.root === '.'; projects: Record<string, ProjectConfiguration>,
projectName: string
): boolean {
return projects[projectName]?.root === '.';
} }
function convertImportToDependency( function convertImportToDependency(
importExpr: string, importExpr: string,
sourceFile: string, sourceFile: string,
source: string, source: string,
dependencyType: ProjectGraphDependencyWithFile['dependencyType'], type: RawProjectGraphDependency['type'],
targetProjectLocator: TargetProjectLocator targetProjectLocator: TargetProjectLocator
): ProjectGraphDependencyWithFile { ): RawProjectGraphDependency {
const target = const target =
targetProjectLocator.findProjectWithImport(importExpr, sourceFile) ?? targetProjectLocator.findProjectWithImport(importExpr, sourceFile) ??
`npm:${importExpr}`; `npm:${importExpr}`;
@ -28,19 +35,31 @@ function convertImportToDependency(
source, source,
target, target,
sourceFile, sourceFile,
dependencyType, type,
}; };
} }
export function buildExplicitTypeScriptDependencies({ export function buildExplicitTypeScriptDependencies(
fileMap, ctx: CreateDependenciesContext
graph, ): RawProjectGraphDependency[] {
}: CreateDependenciesContext): ProjectGraphDependencyWithFile[] { // TODO: TargetProjectLocator is a public API, so we can't change the shape of it
const targetProjectLocator = new TargetProjectLocator( // We should eventually let it accept Record<string, ProjectConfiguration> s.t. we
graph.nodes as any, // don't have to reshape the CreateDependenciesContext here.
graph.externalNodes const nodes: Record<string, ProjectGraphProjectNode> = 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<string, string[]> = {}; const filesToProcess: Record<string, string[]> = {};
@ -51,7 +70,7 @@ export function buildExplicitTypeScriptDependencies({
moduleExtensions.push('.vue'); moduleExtensions.push('.vue');
} }
for (const [project, fileData] of Object.entries(fileMap)) { for (const [project, fileData] of Object.entries(ctx.fileMap)) {
filesToProcess[project] ??= []; filesToProcess[project] ??= [];
for (const { file } of fileData) { for (const { file } of fileData) {
if (moduleExtensions.some((ext) => file.endsWith(ext))) { 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 // TODO: These edges technically should be allowed but we need to figure out how to separate config files out from root
if ( if (
isRoot(graph, dependency.source) || isRoot(ctx.projects, dependency.source) ||
!isRoot(graph, dependency.target) !isRoot(ctx.projects, dependency.target)
) { ) {
res.push(dependency); 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 // TODO: These edges technically should be allowed but we need to figure out how to separate config files out from root
if ( if (
isRoot(graph, dependency.source) || isRoot(ctx.projects, dependency.source) ||
!isRoot(graph, dependency.target) !isRoot(ctx.projects, dependency.target)
) { ) {
validateDependency(graph, dependency); validateDependency(dependency, ctx);
res.push(dependency); res.push(dependency);
} }
} }

View File

@ -245,15 +245,21 @@ async function updateProjectGraphWithPlugins(
if (isNxPluginV2(plugin) && plugin.createDependencies) { if (isNxPluginV2(plugin) && plugin.createDependencies) {
const builder = new ProjectGraphBuilder(graph, context.fileMap); const builder = new ProjectGraphBuilder(graph, context.fileMap);
const newDependencies = await plugin.createDependencies({ const newDependencies = await plugin.createDependencies({
...context, externalNodes: graph.externalNodes,
graph, fileMap: context.fileMap,
filesToProcess: context.filesToProcess,
nxJsonConfiguration: context.nxJsonConfiguration,
projects: context.projectsConfigurations.projects,
workspaceRoot: workspaceRoot,
}); });
for (const targetProjectDependency of newDependencies) { for (const targetProjectDependency of newDependencies) {
builder.addDependency( builder.addDependency(
targetProjectDependency.source, targetProjectDependency.source,
targetProjectDependency.target, targetProjectDependency.target,
targetProjectDependency.dependencyType, targetProjectDependency.type,
targetProjectDependency.sourceFile 'sourceFile' in targetProjectDependency
? targetProjectDependency.sourceFile
: null
); );
} }
graph = builder.getUpdatedProjectGraph(); graph = builder.getUpdatedProjectGraph();

View File

@ -11,6 +11,8 @@ import {
ProjectGraphExternalNode, ProjectGraphExternalNode,
ProjectGraphProjectNode, ProjectGraphProjectNode,
} from '../config/project-graph'; } from '../config/project-graph';
import { ProjectConfiguration } from '../config/workspace-json-project-json';
import { CreateDependenciesContext } from '../utils/nx-plugin';
import { getProjectFileMap } from './build-project-graph'; import { getProjectFileMap } from './build-project-graph';
/** /**
@ -245,12 +247,23 @@ export class ProjectGraphBuilder {
return; return;
} }
validateDependency(this.graph, { validateDependency(
source, {
target, source,
dependencyType: type, target,
sourceFile, 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]) { if (!this.graph.dependencies[source]) {
this.graph.dependencies[source] = []; this.graph.dependencies[source] = [];
@ -260,20 +273,12 @@ export class ProjectGraphBuilder {
); );
if (sourceFile) { if (sourceFile) {
const sourceProject = this.graph.nodes[source]; const fileData = getFileData(
if (!sourceProject) { source,
throw new Error( sourceFile,
`Source project is not a project node: ${sourceProject}` this.graph.nodes,
); this.fileMap
}
const fileData = (this.fileMap[source] || []).find(
(f) => f.file === sourceFile
); );
if (!fileData) {
throw new Error(
`Source project ${source} does not have a file: ${sourceFile}`
);
}
if (!fileData.deps) { if (!fileData.deps) {
fileData.deps = []; fileData.deps = [];
@ -369,80 +374,139 @@ export class ProjectGraphBuilder {
} }
/** /**
* A {@link ProjectGraph} dependency between 2 projects * A static {@link ProjectGraph} dependency between 2 projects
* Optional: Specifies a file from where the dependency is made *
* 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 * The name of a {@link ProjectGraphProjectNode} or {@link ProjectGraphExternalNode} depending on the target project
*/ */
source: string; source: string;
/** /**
* The name of a {@link ProjectGraphProjectNode} or {@link ProjectGraphExternalNode} that the source project depends on * The name of a {@link ProjectGraphProjectNode} or {@link ProjectGraphExternalNode} that the source project depends on
*/ */
target: string; target: string;
/** /**
* The path of a file (relative from the workspace root) where the dependency is made * The path of a file (relative from the workspace root) where the dependency is made
*/ */
sourceFile?: string; 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 * A function to validate dependencies in a {@link CreateDependencies} function
* @throws If the dependency is invalid. * @throws If the dependency is invalid.
*/ */
export function validateDependency( export function validateDependency(
graph: ProjectGraph, dependency: RawProjectGraphDependency,
dependency: ProjectGraphDependencyWithFile ctx: CreateDependenciesContext
): void { ): void {
if (dependency.dependencyType === DependencyType.implicit) { if (dependency.type === DependencyType.implicit) {
validateImplicitDependency(graph, dependency); validateImplicitDependency(dependency, ctx);
} else if (dependency.dependencyType === DependencyType.dynamic) { } else if (dependency.type === DependencyType.dynamic) {
validateDynamicDependency(graph, dependency); validateDynamicDependency(dependency, ctx);
} else if (dependency.dependencyType === DependencyType.static) { } else if (dependency.type === DependencyType.static) {
validateStaticDependency(graph, dependency); validateStaticDependency(dependency, ctx);
} }
validateCommonDependencyRules(graph, dependency); validateCommonDependencyRules(dependency, ctx);
} }
function validateCommonDependencyRules( function validateCommonDependencyRules(
graph: ProjectGraph, d: RawProjectGraphDependency,
d: ProjectGraphDependencyWithFile { 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}`); throw new Error(`Source project does not exist: ${d.source}`);
} }
if ( if (
!graph.nodes[d.target] && !projects[d.target] &&
!graph.externalNodes[d.target] && !externalNodes[d.target] &&
!d.sourceFile !('sourceFile' in d && d.sourceFile)
) { ) {
throw new Error(`Target project does not exist: ${d.target}`); 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`); 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( function validateImplicitDependency(
graph: ProjectGraph, d: ImplicitDependency,
d: ProjectGraphDependencyWithFile { externalNodes }: CreateDependenciesContext
) { ) {
if (graph.externalNodes[d.source]) { if (externalNodes[d.source]) {
throw new Error(`External projects can't have "implicit" dependencies`); throw new Error(`External projects can't have "implicit" dependencies`);
} }
} }
function validateDynamicDependency( function validateDynamicDependency(
graph: ProjectGraph, d: DynamicDependency,
d: ProjectGraphDependencyWithFile { externalNodes }: CreateDependenciesContext
) { ) {
if (graph.externalNodes[d.source]) { if (externalNodes[d.source]) {
throw new Error(`External projects can't have "dynamic" dependencies`); throw new Error(`External projects can't have "dynamic" dependencies`);
} }
// dynamic dependency is always bound to a file // dynamic dependency is always bound to a file
@ -454,12 +518,31 @@ function validateDynamicDependency(
} }
function validateStaticDependency( function validateStaticDependency(
graph: ProjectGraph, d: StaticDependency,
d: ProjectGraphDependencyWithFile { projects }: CreateDependenciesContext
) { ) {
// internal nodes must provide sourceProjectFile when creating static dependency // internal nodes must provide sourceProjectFile when creating static dependency
// externalNodes do not have sourceProjectFile // 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`); throw new Error(`Source project file is required`);
} }
} }
function getFileData(
source: string,
sourceFile: string,
projects: Record<string, ProjectGraphProjectNode | ProjectConfiguration>,
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;
}

View File

@ -1,6 +1,7 @@
import { existsSync } from 'fs'; import { existsSync } from 'fs';
import * as path from 'path'; import * as path from 'path';
import { import {
FileData,
ProjectFileMap, ProjectFileMap,
ProjectGraph, ProjectGraph,
ProjectGraphExternalNode, ProjectGraphExternalNode,
@ -35,7 +36,7 @@ import { NxJsonConfiguration } from '../config/nx-json';
import type * as ts from 'typescript'; import type * as ts from 'typescript';
import { retrieveProjectConfigurationsWithoutPluginInference } from '../project-graph/utils/retrieve-workspace-files'; import { retrieveProjectConfigurationsWithoutPluginInference } from '../project-graph/utils/retrieve-workspace-files';
import { NxPluginV1 } from './nx-plugin.deprecated'; 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 { combineGlobPatterns } from './globs';
import { import {
NxAngularJsonPlugin, NxAngularJsonPlugin,
@ -77,14 +78,14 @@ export type CreateNodes = readonly [
*/ */
export interface CreateDependenciesContext { 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<string, ProjectConfiguration>;
/** /**
* The `nx.json` configuration from the workspace * The `nx.json` configuration from the workspace
@ -100,6 +101,8 @@ export interface CreateDependenciesContext {
* Files changes since last invocation * Files changes since last invocation
*/ */
readonly filesToProcess: ProjectFileMap; readonly filesToProcess: ProjectFileMap;
readonly workspaceRoot: string;
} }
/** /**
@ -108,9 +111,7 @@ export interface CreateDependenciesContext {
*/ */
export type CreateDependencies = ( export type CreateDependencies = (
context: CreateDependenciesContext context: CreateDependenciesContext
) => ) => RawProjectGraphDependency[] | Promise<RawProjectGraphDependency[]>;
| ProjectGraphDependencyWithFile[]
| Promise<ProjectGraphDependencyWithFile[]>;
/** /**
* A plugin for Nx which creates nodes and dependencies for the {@link ProjectGraph} * A plugin for Nx which creates nodes and dependencies for the {@link ProjectGraph}