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
Ƭ **CreateDependencies**: (`context`: [`CreateDependenciesContext`](../../devkit/documents/CreateDependenciesContext)) => [`ProjectGraphDependencyWithFile`](../../devkit/documents/ProjectGraphDependencyWithFile)[] \| `Promise`<[`ProjectGraphDependencyWithFile`](../../devkit/documents/ProjectGraphDependencyWithFile)[]\>
Ƭ **CreateDependencies**: (`context`: [`CreateDependenciesContext`](../../devkit/documents/CreateDependenciesContext)) => [`RawProjectGraphDependency`](../../devkit/documents/RawProjectGraphDependency)[] \| `Promise`<[`RawProjectGraphDependency`](../../devkit/documents/RawProjectGraphDependency)[]\>
#### Type declaration
▸ (`context`): [`ProjectGraphDependencyWithFile`](../../devkit/documents/ProjectGraphDependencyWithFile)[] \| `Promise`<[`ProjectGraphDependencyWithFile`](../../devkit/documents/ProjectGraphDependencyWithFile)[]\>
▸ (`context`): [`RawProjectGraphDependency`](../../devkit/documents/RawProjectGraphDependency)[] \| `Promise`<[`RawProjectGraphDependency`](../../devkit/documents/RawProjectGraphDependency)[]\>
A function which parses files in the workspace to create dependencies in the [ProjectGraph](../../devkit/documents/ProjectGraph)
Use [validateDependency](../../devkit/documents/validateDependency) to validate dependencies
@ -17,4 +17,4 @@ Use [validateDependency](../../devkit/documents/validateDependency) to validate
##### Returns
[`ProjectGraphDependencyWithFile`](../../devkit/documents/ProjectGraphDependencyWithFile)[] \| `Promise`<[`ProjectGraphDependencyWithFile`](../../devkit/documents/ProjectGraphDependencyWithFile)[]\>
[`RawProjectGraphDependency`](../../devkit/documents/RawProjectGraphDependency)[] \| `Promise`<[`RawProjectGraphDependency`](../../devkit/documents/RawProjectGraphDependency)[]\>

View File

@ -6,14 +6,23 @@ Context for [CreateDependencies](../../devkit/documents/CreateDependencies)
### Properties
- [externalNodes](../../devkit/documents/CreateDependenciesContext#externalnodes): Record&lt;string, ProjectGraphExternalNode&gt;
- [fileMap](../../devkit/documents/CreateDependenciesContext#filemap): ProjectFileMap
- [filesToProcess](../../devkit/documents/CreateDependenciesContext#filestoprocess): ProjectFileMap
- [graph](../../devkit/documents/CreateDependenciesContext#graph): ProjectGraph
- [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
### externalNodes
`Readonly` **externalNodes**: `Record`<`string`, [`ProjectGraphExternalNode`](../../devkit/documents/ProjectGraphExternalNode)\>
The external nodes that have been added to the graph.
---
### fileMap
`Readonly` **fileMap**: [`ProjectFileMap`](../../devkit/documents/ProjectFileMap)
@ -30,14 +39,6 @@ Files changes since last invocation
---
### graph
`Readonly` **graph**: [`ProjectGraph`](../../devkit/documents/ProjectGraph)
The current project graph,
---
### nxJsonConfiguration
`Readonly` **nxJsonConfiguration**: [`NxJsonConfiguration`](../../devkit/documents/NxJsonConfiguration)<`string`[] \| `"*"`\>
@ -46,8 +47,14 @@ The `nx.json` configuration from the workspace
---
### projectsConfigurations
### projects
`Readonly` **projectsConfigurations**: [`ProjectsConfigurations`](../../devkit/documents/ProjectsConfigurations)
`Readonly` **projects**: `Record`<`string`, [`ProjectConfiguration`](../../devkit/documents/ProjectConfiguration)\>
The configuration of each project in the workspace
The configuration of each project in the workspace.
---
### workspaceRoot
`Readonly` **workspaceRoot**: `string`

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

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

View File

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

View File

@ -90,16 +90,14 @@ The shape of the [`createDependencies`](/packages/devkit/documents/CreateDepende
```typescript
export type CreateDependencies = (
context: CreateDependenciesContext
) =>
| ProjectGraphDependencyWithFile[]
| Promise<ProjectGraphDependencyWithFile[]>;
) => CandidateDependency[] | Promise<CandidateDependency[]>;
```
In the `createDependencies` function, you can analyze the files in the workspace and return a list of dependencies. It's up to the plugin to determine how to analyze the files. This should also be exported from the plugin's entry point, as listed in `nx.json`.
Within the `CreateDependenciesContext`, you have access to the current project graph, the configuration of each project in the workspace, the `nx.json` configuration from the workspace, all files in the workspace, and files that have changed since the last invocation. It's important to utilize the `filesToProcess` parameter, as this will allow Nx to only reanalyze files that have changed since the last invocation, and reuse the information from the previous invocation for files that haven't changed.
Within the `CreateDependenciesContext`, you have access to the graph's external nodes, the configuration of each project in the workspace, the `nx.json` configuration from the workspace, all files in the workspace, and files that have changed since the last invocation. It's important to utilize the `filesToProcess` parameter, as this will allow Nx to only reanalyze files that have changed since the last invocation, and reuse the information from the previous invocation for files that haven't changed.
`@nx/devkit` exports a function called `validateDependency` which can be used to validate a dependency. This function takes in a `ProjectGraphDependencyWithFile` and a `ProjectGraph` and throws an error if the dependency is invalid. This function is called when the returned dependencies are merged with the existing project graph, but may be useful to call within your plugin to validate dependencies before returning them when debugging.
`@nx/devkit` exports a function called `validateDependency` which can be used to validate a dependency. This function takes in a `CandidateDependency` and the `CreateDependenciesContext` and throws an error if the dependency is invalid. This function is called when the returned dependencies are merged with the existing project graph, but may be useful to call within your plugin to validate dependencies before returning them when debugging.
The dependencies can be of three types:
@ -184,7 +182,7 @@ export const createNodes: CreateNodes = (ctx) => {
dependencyType: DependencyType.static,
};
}
validateDependency(ctx.graph, newDependency);
validateDependency(newDependency, ctx);
results.push(newDependency);
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -11,6 +11,8 @@ import {
ProjectGraphExternalNode,
ProjectGraphProjectNode,
} from '../config/project-graph';
import { ProjectConfiguration } from '../config/workspace-json-project-json';
import { CreateDependenciesContext } from '../utils/nx-plugin';
import { getProjectFileMap } from './build-project-graph';
/**
@ -245,12 +247,23 @@ export class ProjectGraphBuilder {
return;
}
validateDependency(this.graph, {
source,
target,
dependencyType: type,
sourceFile,
});
validateDependency(
{
source,
target,
type,
sourceFile,
},
{
externalNodes: this.graph.externalNodes,
fileMap: this.fileMap,
// the validators only really care about the keys on this.
projects: this.graph.nodes as any,
filesToProcess: null,
nxJsonConfiguration: null,
workspaceRoot: null,
}
);
if (!this.graph.dependencies[source]) {
this.graph.dependencies[source] = [];
@ -260,20 +273,12 @@ export class ProjectGraphBuilder {
);
if (sourceFile) {
const sourceProject = this.graph.nodes[source];
if (!sourceProject) {
throw new Error(
`Source project is not a project node: ${sourceProject}`
);
}
const fileData = (this.fileMap[source] || []).find(
(f) => f.file === sourceFile
const fileData = getFileData(
source,
sourceFile,
this.graph.nodes,
this.fileMap
);
if (!fileData) {
throw new Error(
`Source project ${source} does not have a file: ${sourceFile}`
);
}
if (!fileData.deps) {
fileData.deps = [];
@ -369,80 +374,139 @@ export class ProjectGraphBuilder {
}
/**
* A {@link ProjectGraph} dependency between 2 projects
* Optional: Specifies a file from where the dependency is made
* A static {@link ProjectGraph} dependency between 2 projects
*
* This type of dependency indicates the source project ALWAYS load the target project.
*
* NOTE: {@link StaticDependency#sourceFile} MUST be present unless the source is the name of a {@link ProjectGraphExternalNode}
*/
export interface ProjectGraphDependencyWithFile {
export type StaticDependency = {
/**
* The name of a {@link ProjectGraphProjectNode} or {@link ProjectGraphExternalNode} depending on the target project
*/
source: string;
/**
* The name of a {@link ProjectGraphProjectNode} or {@link ProjectGraphExternalNode} that the source project depends on
*/
target: string;
/**
* The path of a file (relative from the workspace root) where the dependency is made
*/
sourceFile?: string;
type: typeof DependencyType.static;
};
/**
* A dynamic {@link ProjectGraph} dependency between 2 projects
*
* This type of dependency indicates the source project MAY OR MAY NOT load the target project.
*/
export type DynamicDependency = {
/**
* The type of dependency
* The name of a {@link ProjectGraphProjectNode} depending on the target project
*/
dependencyType: DependencyType;
}
source: string;
/**
* The name of a {@link ProjectGraphProjectNode} that the source project depends on
*/
target: string;
/**
* The path of a file (relative from the workspace root) where the dependency is made
*/
sourceFile: string;
type: typeof DependencyType.dynamic;
};
/**
* An implicit {@link ProjectGraph} dependency between 2 projects
*
* This type of dependency indicates a connection without an explicit reference in code
*/
export type ImplicitDependency = {
/**
* The name of a {@link ProjectGraphProjectNode} depending on the target project
*/
source: string;
/**
* The name of a {@link ProjectGraphProjectNode} that the source project depends on
*/
target: string;
type: typeof DependencyType.implicit;
};
/**
* A {@link ProjectGraph} dependency between 2 projects
*
* See {@link DynamicDependency}, {@link ImplicitDependency}, or {@link StaticDependency}
*/
export type RawProjectGraphDependency =
| ImplicitDependency
| StaticDependency
| DynamicDependency;
/**
* A function to validate dependencies in a {@link CreateDependencies} function
* @throws If the dependency is invalid.
*/
export function validateDependency(
graph: ProjectGraph,
dependency: ProjectGraphDependencyWithFile
dependency: RawProjectGraphDependency,
ctx: CreateDependenciesContext
): void {
if (dependency.dependencyType === DependencyType.implicit) {
validateImplicitDependency(graph, dependency);
} else if (dependency.dependencyType === DependencyType.dynamic) {
validateDynamicDependency(graph, dependency);
} else if (dependency.dependencyType === DependencyType.static) {
validateStaticDependency(graph, dependency);
if (dependency.type === DependencyType.implicit) {
validateImplicitDependency(dependency, ctx);
} else if (dependency.type === DependencyType.dynamic) {
validateDynamicDependency(dependency, ctx);
} else if (dependency.type === DependencyType.static) {
validateStaticDependency(dependency, ctx);
}
validateCommonDependencyRules(graph, dependency);
validateCommonDependencyRules(dependency, ctx);
}
function validateCommonDependencyRules(
graph: ProjectGraph,
d: ProjectGraphDependencyWithFile
d: RawProjectGraphDependency,
{ externalNodes, projects, fileMap }: CreateDependenciesContext
) {
if (!graph.nodes[d.source] && !graph.externalNodes[d.source]) {
if (!projects[d.source] && !externalNodes[d.source]) {
throw new Error(`Source project does not exist: ${d.source}`);
}
if (
!graph.nodes[d.target] &&
!graph.externalNodes[d.target] &&
!d.sourceFile
!projects[d.target] &&
!externalNodes[d.target] &&
!('sourceFile' in d && d.sourceFile)
) {
throw new Error(`Target project does not exist: ${d.target}`);
}
if (graph.externalNodes[d.source] && graph.nodes[d.target]) {
if (externalNodes[d.source] && projects[d.target]) {
throw new Error(`External projects can't depend on internal projects`);
}
if ('sourceFile' in d && d.sourceFile) {
// Throws if source file is not a valid file within the source project.
getFileData(d.source, d.sourceFile, projects, fileMap);
}
}
function validateImplicitDependency(
graph: ProjectGraph,
d: ProjectGraphDependencyWithFile
d: ImplicitDependency,
{ externalNodes }: CreateDependenciesContext
) {
if (graph.externalNodes[d.source]) {
if (externalNodes[d.source]) {
throw new Error(`External projects can't have "implicit" dependencies`);
}
}
function validateDynamicDependency(
graph: ProjectGraph,
d: ProjectGraphDependencyWithFile
d: DynamicDependency,
{ externalNodes }: CreateDependenciesContext
) {
if (graph.externalNodes[d.source]) {
if (externalNodes[d.source]) {
throw new Error(`External projects can't have "dynamic" dependencies`);
}
// dynamic dependency is always bound to a file
@ -454,12 +518,31 @@ function validateDynamicDependency(
}
function validateStaticDependency(
graph: ProjectGraph,
d: ProjectGraphDependencyWithFile
d: StaticDependency,
{ projects }: CreateDependenciesContext
) {
// internal nodes must provide sourceProjectFile when creating static dependency
// externalNodes do not have sourceProjectFile
if (graph.nodes[d.source] && !d.sourceFile) {
if (projects[d.source] && !d.sourceFile) {
throw new Error(`Source project file is required`);
}
}
function getFileData(
source: string,
sourceFile: string,
projects: Record<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 * as path from 'path';
import {
FileData,
ProjectFileMap,
ProjectGraph,
ProjectGraphExternalNode,
@ -35,7 +36,7 @@ import { NxJsonConfiguration } from '../config/nx-json';
import type * as ts from 'typescript';
import { retrieveProjectConfigurationsWithoutPluginInference } from '../project-graph/utils/retrieve-workspace-files';
import { NxPluginV1 } from './nx-plugin.deprecated';
import { ProjectGraphDependencyWithFile } from '../project-graph/project-graph-builder';
import { RawProjectGraphDependency } from '../project-graph/project-graph-builder';
import { combineGlobPatterns } from './globs';
import {
NxAngularJsonPlugin,
@ -77,14 +78,14 @@ export type CreateNodes = readonly [
*/
export interface CreateDependenciesContext {
/**
* The current project graph,
* The external nodes that have been added to the graph.
*/
readonly graph: ProjectGraph;
readonly externalNodes: ProjectGraph['externalNodes'];
/**
* The configuration of each project in the workspace
* The configuration of each project in the workspace.
*/
readonly projectsConfigurations: ProjectsConfigurations;
readonly projects: Record<string, ProjectConfiguration>;
/**
* The `nx.json` configuration from the workspace
@ -100,6 +101,8 @@ export interface CreateDependenciesContext {
* Files changes since last invocation
*/
readonly filesToProcess: ProjectFileMap;
readonly workspaceRoot: string;
}
/**
@ -108,9 +111,7 @@ export interface CreateDependenciesContext {
*/
export type CreateDependencies = (
context: CreateDependenciesContext
) =>
| ProjectGraphDependencyWithFile[]
| Promise<ProjectGraphDependencyWithFile[]>;
) => RawProjectGraphDependency[] | Promise<RawProjectGraphDependency[]>;
/**
* A plugin for Nx which creates nodes and dependencies for the {@link ProjectGraph}