feat(core): add full dependency information to project graph file dependencies (#14893)
This commit is contained in:
parent
00828fd615
commit
8d4855de61
@ -48,7 +48,6 @@ It only uses language primitives and immutable objects
|
|||||||
- [ProjectGraphExternalNode](../../devkit/documents/nrwl_devkit#projectgraphexternalnode)
|
- [ProjectGraphExternalNode](../../devkit/documents/nrwl_devkit#projectgraphexternalnode)
|
||||||
- [ProjectGraphProcessorContext](../../devkit/documents/nrwl_devkit#projectgraphprocessorcontext)
|
- [ProjectGraphProcessorContext](../../devkit/documents/nrwl_devkit#projectgraphprocessorcontext)
|
||||||
- [ProjectGraphProjectNode](../../devkit/documents/nrwl_devkit#projectgraphprojectnode)
|
- [ProjectGraphProjectNode](../../devkit/documents/nrwl_devkit#projectgraphprojectnode)
|
||||||
- [ProjectGraphV4](../../devkit/documents/nrwl_devkit#projectgraphv4)
|
|
||||||
|
|
||||||
### Tree Interfaces
|
### Tree Interfaces
|
||||||
|
|
||||||
@ -301,18 +300,6 @@ A plugin for Nx
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### ProjectGraphV4
|
|
||||||
|
|
||||||
• **ProjectGraphV4**<`T`\>: `Object`
|
|
||||||
|
|
||||||
#### Type parameters
|
|
||||||
|
|
||||||
| Name | Type |
|
|
||||||
| :--- | :---- |
|
|
||||||
| `T` | `any` |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Tree Interfaces
|
## Tree Interfaces
|
||||||
|
|
||||||
### FileChange
|
### FileChange
|
||||||
|
|||||||
@ -48,7 +48,6 @@ It only uses language primitives and immutable objects
|
|||||||
- [ProjectGraphExternalNode](../../devkit/documents/nrwl_devkit#projectgraphexternalnode)
|
- [ProjectGraphExternalNode](../../devkit/documents/nrwl_devkit#projectgraphexternalnode)
|
||||||
- [ProjectGraphProcessorContext](../../devkit/documents/nrwl_devkit#projectgraphprocessorcontext)
|
- [ProjectGraphProcessorContext](../../devkit/documents/nrwl_devkit#projectgraphprocessorcontext)
|
||||||
- [ProjectGraphProjectNode](../../devkit/documents/nrwl_devkit#projectgraphprojectnode)
|
- [ProjectGraphProjectNode](../../devkit/documents/nrwl_devkit#projectgraphprojectnode)
|
||||||
- [ProjectGraphV4](../../devkit/documents/nrwl_devkit#projectgraphv4)
|
|
||||||
|
|
||||||
### Tree Interfaces
|
### Tree Interfaces
|
||||||
|
|
||||||
@ -301,18 +300,6 @@ A plugin for Nx
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### ProjectGraphV4
|
|
||||||
|
|
||||||
• **ProjectGraphV4**<`T`\>: `Object`
|
|
||||||
|
|
||||||
#### Type parameters
|
|
||||||
|
|
||||||
| Name | Type |
|
|
||||||
| :--- | :---- |
|
|
||||||
| `T` | `any` |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Tree Interfaces
|
## Tree Interfaces
|
||||||
|
|
||||||
### FileChange
|
### FileChange
|
||||||
|
|||||||
@ -48,7 +48,6 @@ It only uses language primitives and immutable objects
|
|||||||
- [ProjectGraphExternalNode](../../devkit/documents/nrwl_devkit#projectgraphexternalnode)
|
- [ProjectGraphExternalNode](../../devkit/documents/nrwl_devkit#projectgraphexternalnode)
|
||||||
- [ProjectGraphProcessorContext](../../devkit/documents/nrwl_devkit#projectgraphprocessorcontext)
|
- [ProjectGraphProcessorContext](../../devkit/documents/nrwl_devkit#projectgraphprocessorcontext)
|
||||||
- [ProjectGraphProjectNode](../../devkit/documents/nrwl_devkit#projectgraphprojectnode)
|
- [ProjectGraphProjectNode](../../devkit/documents/nrwl_devkit#projectgraphprojectnode)
|
||||||
- [ProjectGraphV4](../../devkit/documents/nrwl_devkit#projectgraphv4)
|
|
||||||
|
|
||||||
### Tree Interfaces
|
### Tree Interfaces
|
||||||
|
|
||||||
@ -301,18 +300,6 @@ A plugin for Nx
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### ProjectGraphV4
|
|
||||||
|
|
||||||
• **ProjectGraphV4**<`T`\>: `Object`
|
|
||||||
|
|
||||||
#### Type parameters
|
|
||||||
|
|
||||||
| Name | Type |
|
|
||||||
| :--- | :---- |
|
|
||||||
| `T` | `any` |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Tree Interfaces
|
## Tree Interfaces
|
||||||
|
|
||||||
### FileChange
|
### FileChange
|
||||||
|
|||||||
@ -86,11 +86,9 @@ You can create 2 types of dependencies.
|
|||||||
|
|
||||||
### Implicit Dependencies
|
### Implicit Dependencies
|
||||||
|
|
||||||
An implicit dependency is not associated with any file, and can be crated as follows:
|
An implicit dependency is not associated with any file, and can be created as follows:
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
import { DependencyType } from '@nrwl/devkit';
|
|
||||||
|
|
||||||
// Add a new edge
|
// Add a new edge
|
||||||
builder.addImplicitDependency('existing-project', 'new-project');
|
builder.addImplicitDependency('existing-project', 'new-project');
|
||||||
```
|
```
|
||||||
@ -101,23 +99,37 @@ Even though the plugin is written in JavaScript, resolving dependencies of diffe
|
|||||||
|
|
||||||
Because an implicit dependency is not associated with any file, Nx doesn't know when it might change, so it will be recomputed every time.
|
Because an implicit dependency is not associated with any file, Nx doesn't know when it might change, so it will be recomputed every time.
|
||||||
|
|
||||||
## Explicit Dependencies
|
## Static Dependencies
|
||||||
|
|
||||||
Nx knows what files have changed since the last invocation. Only those files will be present in the provided `filesToProcess`. You can associate a dependency with a particular file (e.g., if that file contains an import).
|
Nx knows what files have changed since the last invocation. Only those files will be present in the provided `filesToProcess`. You can associate a dependency with a particular file (e.g., if that file contains an import).
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// Add a new edge
|
||||||
|
builder.addStaticDependency(
|
||||||
|
'existing-project',
|
||||||
|
'new-project',
|
||||||
|
'libs/existing-project/src/index.ts'
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
If a file hasn't changed since the last invocation, it doesn't need to be reanalyzed. Nx knows what dependencies are associated with what files, so it will reuse this information for the files that haven't changed.
|
||||||
|
|
||||||
|
## Dynamic Dependencies
|
||||||
|
|
||||||
|
Dynamic dependencies are a special type of explicit dependencies. In contrast to standard `explicit` dependencies, they are only imported in the runtime under specific conditions.
|
||||||
|
A typical example would be lazy-loaded routes. Having separation between these two allows us to identify situations where static import breaks the lazy-loading.
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
import { DependencyType } from '@nrwl/devkit';
|
import { DependencyType } from '@nrwl/devkit';
|
||||||
|
|
||||||
// Add a new edge
|
// Add a new edge
|
||||||
builder.addExplicitDependency(
|
builder.addDynamicDependency(
|
||||||
'existing-project',
|
'existing-project',
|
||||||
'libs/existing-project/src/index.ts',
|
'lazy-route',
|
||||||
'new-project'
|
'libs/existing-project/src/router-setup.ts'
|
||||||
);
|
);
|
||||||
```
|
```
|
||||||
|
|
||||||
If a file hasn't changed since the last invocation, it doesn't need to be reanalyzed. Nx knows what dependencies are associated with what files, so it will reuse this information for the files that haven't changed.
|
|
||||||
|
|
||||||
## Visualizing the Project Graph
|
## Visualizing the Project Graph
|
||||||
|
|
||||||
You can then visualize the project graph as described [here](/core-features/explore-graph). However, there is a cache that Nx uses to avoid recalculating the project graph as much as possible. As you develop your project graph plugin, it might be a good idea to set the following environment variable to disable the project graph cache: `NX_CACHE_PROJECT_GRAPH=false`.
|
You can then visualize the project graph as described [here](/core-features/explore-graph). However, there is a cache that Nx uses to avoid recalculating the project graph as much as possible. As you develop your project graph plugin, it might be a good idea to set the following environment variable to disable the project graph cache: `NX_CACHE_PROJECT_GRAPH=false`.
|
||||||
|
|||||||
@ -472,7 +472,7 @@ describe('Nx Affected and Graph Tests', () => {
|
|||||||
target: mylib,
|
target: mylib,
|
||||||
type: 'static',
|
type: 'static',
|
||||||
},
|
},
|
||||||
{ source: myapp, target: mylib2, type: 'static' },
|
{ source: myapp, target: mylib2, type: 'dynamic' },
|
||||||
],
|
],
|
||||||
[myappE2e]: [
|
[myappE2e]: [
|
||||||
{
|
{
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
// nx-ignore-next-line
|
// nx-ignore-next-line
|
||||||
import type {
|
import type {
|
||||||
|
DependencyType,
|
||||||
ProjectGraphDependency,
|
ProjectGraphDependency,
|
||||||
ProjectGraphProjectNode,
|
ProjectGraphProjectNode,
|
||||||
} from '@nrwl/devkit';
|
} from '@nrwl/devkit';
|
||||||
@ -28,7 +29,13 @@ export class MockProjectGraphService implements ProjectGraphService {
|
|||||||
{
|
{
|
||||||
file: 'some/file.ts',
|
file: 'some/file.ts',
|
||||||
hash: 'ecccd8481d2e5eae0e59928be1bc4c2d071729d7',
|
hash: 'ecccd8481d2e5eae0e59928be1bc4c2d071729d7',
|
||||||
deps: ['existing-lib-1'],
|
dependencies: [
|
||||||
|
{
|
||||||
|
target: 'existing-lib-1',
|
||||||
|
source: 'existing-app-1',
|
||||||
|
type: 'static' as DependencyType,
|
||||||
|
},
|
||||||
|
],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|||||||
@ -281,7 +281,9 @@ export class RenderGraph {
|
|||||||
.source()
|
.source()
|
||||||
.data('files')
|
.data('files')
|
||||||
?.filter(
|
?.filter(
|
||||||
(file) => file.deps && file.deps.includes(edge.target().id())
|
(file) =>
|
||||||
|
file.dependencies &&
|
||||||
|
file.dependencies.find((d) => d.target === edge.target().id())
|
||||||
)
|
)
|
||||||
.map((file) => {
|
.map((file) => {
|
||||||
return {
|
return {
|
||||||
|
|||||||
@ -1,6 +1,13 @@
|
|||||||
jest.mock('@nrwl/devkit');
|
jest.mock('@nrwl/devkit');
|
||||||
jest.mock('@nrwl/devkit');
|
jest.mock('@nrwl/devkit');
|
||||||
jest.mock('@nrwl/workspace/src/utilities/buildable-libs-utils');
|
jest.mock('@nrwl/workspace/src/utilities/buildable-libs-utils');
|
||||||
|
// nested code imports graph from the repo, which might have innacurate graph version
|
||||||
|
jest.mock('nx/src/project-graph/project-graph', () => ({
|
||||||
|
...jest.requireActual<any>('nx/src/project-graph/project-graph'),
|
||||||
|
readCachedProjectGraph: jest
|
||||||
|
.fn()
|
||||||
|
.mockImplementation(async () => ({ nodes: {}, dependencies: {} })),
|
||||||
|
}));
|
||||||
|
|
||||||
import type { ExecutorContext, Target } from '@nrwl/devkit';
|
import type { ExecutorContext, Target } from '@nrwl/devkit';
|
||||||
import * as devkit from '@nrwl/devkit';
|
import * as devkit from '@nrwl/devkit';
|
||||||
|
|||||||
@ -22,6 +22,14 @@ jest.mock('@nrwl/devkit', () => ({
|
|||||||
.fn()
|
.fn()
|
||||||
.mockImplementation(async () => projectGraph),
|
.mockImplementation(async () => projectGraph),
|
||||||
}));
|
}));
|
||||||
|
// nested code imports graph from the repo, which might have innacurate graph version
|
||||||
|
jest.mock('nx/src/project-graph/project-graph', () => ({
|
||||||
|
...jest.requireActual<any>('nx/src/project-graph/project-graph'),
|
||||||
|
readCachedProjectGraph: jest
|
||||||
|
.fn()
|
||||||
|
.mockImplementation(async () => projectGraph),
|
||||||
|
}));
|
||||||
|
|
||||||
describe('Cypress Component Testing Configuration', () => {
|
describe('Cypress Component Testing Configuration', () => {
|
||||||
let tree: Tree;
|
let tree: Tree;
|
||||||
let mockedInstalledCypressVersion: jest.Mock<
|
let mockedInstalledCypressVersion: jest.Mock<
|
||||||
|
|||||||
@ -11,6 +11,14 @@ import { storybookConfigurationGenerator } from './storybook-configuration';
|
|||||||
// need to mock cypress otherwise it'll use the nx installed version from package.json
|
// need to mock cypress otherwise it'll use the nx installed version from package.json
|
||||||
// which is v9 while we are testing for the new v10 version
|
// which is v9 while we are testing for the new v10 version
|
||||||
jest.mock('@nrwl/cypress/src/utils/cypress-version');
|
jest.mock('@nrwl/cypress/src/utils/cypress-version');
|
||||||
|
// nested code imports graph from the repo, which might have innacurate graph version
|
||||||
|
jest.mock('nx/src/project-graph/project-graph', () => ({
|
||||||
|
...jest.requireActual<any>('nx/src/project-graph/project-graph'),
|
||||||
|
createProjectGraphAsync: jest
|
||||||
|
.fn()
|
||||||
|
.mockImplementation(async () => ({ nodes: {}, dependencies: {} })),
|
||||||
|
}));
|
||||||
|
|
||||||
function listFiles(tree: Tree): string[] {
|
function listFiles(tree: Tree): string[] {
|
||||||
const files = new Set<string>();
|
const files = new Set<string>();
|
||||||
tree.listChanges().forEach((change) => {
|
tree.listChanges().forEach((change) => {
|
||||||
|
|||||||
@ -12,7 +12,16 @@ import {
|
|||||||
} from './update-cy-mount-usage';
|
} from './update-cy-mount-usage';
|
||||||
import { libraryGenerator } from '@nrwl/workspace';
|
import { libraryGenerator } from '@nrwl/workspace';
|
||||||
import { cypressComponentProject } from '../../generators/cypress-component-project/cypress-component-project';
|
import { cypressComponentProject } from '../../generators/cypress-component-project/cypress-component-project';
|
||||||
|
|
||||||
jest.mock('../../utils/cypress-version');
|
jest.mock('../../utils/cypress-version');
|
||||||
|
// nested code imports graph from the repo, which might have innacurate graph version
|
||||||
|
jest.mock('nx/src/project-graph/project-graph', () => ({
|
||||||
|
...jest.requireActual<any>('nx/src/project-graph/project-graph'),
|
||||||
|
createProjectGraphAsync: jest
|
||||||
|
.fn()
|
||||||
|
.mockImplementation(async () => ({ nodes: {}, dependencies: {} })),
|
||||||
|
}));
|
||||||
|
|
||||||
describe('update cy.mount usage', () => {
|
describe('update cy.mount usage', () => {
|
||||||
let tree: Tree;
|
let tree: Tree;
|
||||||
let mockedInstalledCypressVersion: jest.Mock<
|
let mockedInstalledCypressVersion: jest.Mock<
|
||||||
|
|||||||
@ -172,7 +172,6 @@ export type {
|
|||||||
ProjectFileMap,
|
ProjectFileMap,
|
||||||
FileData,
|
FileData,
|
||||||
ProjectGraph,
|
ProjectGraph,
|
||||||
ProjectGraphV4,
|
|
||||||
ProjectGraphDependency,
|
ProjectGraphDependency,
|
||||||
ProjectGraphNode,
|
ProjectGraphNode,
|
||||||
ProjectGraphProjectNode,
|
ProjectGraphProjectNode,
|
||||||
|
|||||||
@ -1,6 +1,10 @@
|
|||||||
import 'nx/src/utils/testing/mock-fs';
|
import 'nx/src/utils/testing/mock-fs';
|
||||||
|
|
||||||
import type { FileData, ProjectGraph } from '@nrwl/devkit';
|
import type {
|
||||||
|
FileData,
|
||||||
|
ProjectGraph,
|
||||||
|
ProjectGraphDependency,
|
||||||
|
} from '@nrwl/devkit';
|
||||||
import { DependencyType } from '@nrwl/devkit';
|
import { DependencyType } from '@nrwl/devkit';
|
||||||
import * as parser from '@typescript-eslint/parser';
|
import * as parser from '@typescript-eslint/parser';
|
||||||
import { TSESLint } from '@typescript-eslint/utils';
|
import { TSESLint } from '@typescript-eslint/utils';
|
||||||
@ -284,7 +288,13 @@ describe('Enforce Module Boundaries (eslint)', () => {
|
|||||||
implicitDependencies: [],
|
implicitDependencies: [],
|
||||||
targets: {},
|
targets: {},
|
||||||
files: [
|
files: [
|
||||||
createFile(`libs/dependsOnPrivate/src/index.ts`, ['privateName']),
|
createFile(`libs/dependsOnPrivate/src/index.ts`, [
|
||||||
|
{
|
||||||
|
source: 'dependsOnPrivateName',
|
||||||
|
type: 'static',
|
||||||
|
target: 'privateName',
|
||||||
|
},
|
||||||
|
]),
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -298,7 +308,11 @@ describe('Enforce Module Boundaries (eslint)', () => {
|
|||||||
targets: {},
|
targets: {},
|
||||||
files: [
|
files: [
|
||||||
createFile(`libs/dependsOnPrivate2/src/index.ts`, [
|
createFile(`libs/dependsOnPrivate2/src/index.ts`, [
|
||||||
'privateName',
|
{
|
||||||
|
source: 'dependsOnPrivateName2',
|
||||||
|
type: 'static',
|
||||||
|
target: 'privateName',
|
||||||
|
},
|
||||||
]),
|
]),
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
@ -313,8 +327,12 @@ describe('Enforce Module Boundaries (eslint)', () => {
|
|||||||
targets: {},
|
targets: {},
|
||||||
files: [
|
files: [
|
||||||
createFile(`libs/private/src/index.ts`, [
|
createFile(`libs/private/src/index.ts`, [
|
||||||
'untaggedName',
|
{
|
||||||
'taggedName',
|
source: 'privateName',
|
||||||
|
type: 'static',
|
||||||
|
target: 'untaggedName',
|
||||||
|
},
|
||||||
|
{ source: 'privateName', type: 'static', target: 'taggedName' },
|
||||||
]),
|
]),
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
@ -1419,7 +1437,15 @@ Violation detected in:
|
|||||||
tags: [],
|
tags: [],
|
||||||
implicitDependencies: [],
|
implicitDependencies: [],
|
||||||
targets: {},
|
targets: {},
|
||||||
files: [createFile(`libs/mylib/src/main.ts`, ['anotherlibName'])],
|
files: [
|
||||||
|
createFile(`libs/mylib/src/main.ts`, [
|
||||||
|
{
|
||||||
|
source: 'mylibName',
|
||||||
|
type: 'static',
|
||||||
|
target: 'anotherlibName',
|
||||||
|
},
|
||||||
|
]),
|
||||||
|
],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
anotherlibName: {
|
anotherlibName: {
|
||||||
@ -1430,7 +1456,15 @@ Violation detected in:
|
|||||||
tags: [],
|
tags: [],
|
||||||
implicitDependencies: [],
|
implicitDependencies: [],
|
||||||
targets: {},
|
targets: {},
|
||||||
files: [createFile(`libs/anotherlib/src/main.ts`, ['mylibName'])],
|
files: [
|
||||||
|
createFile(`libs/anotherlib/src/main.ts`, [
|
||||||
|
{
|
||||||
|
source: 'anotherlibName',
|
||||||
|
type: 'static',
|
||||||
|
target: 'mylibName',
|
||||||
|
},
|
||||||
|
]),
|
||||||
|
],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
myappName: {
|
myappName: {
|
||||||
@ -1486,7 +1520,13 @@ Circular file chain:
|
|||||||
implicitDependencies: [],
|
implicitDependencies: [],
|
||||||
targets: {},
|
targets: {},
|
||||||
files: [
|
files: [
|
||||||
createFile(`libs/mylib/src/main.ts`, ['badcirclelibName']),
|
createFile(`libs/mylib/src/main.ts`, [
|
||||||
|
{
|
||||||
|
source: 'badcirclelibName',
|
||||||
|
type: 'static',
|
||||||
|
target: 'mylibName',
|
||||||
|
},
|
||||||
|
]),
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -1499,8 +1539,20 @@ Circular file chain:
|
|||||||
implicitDependencies: [],
|
implicitDependencies: [],
|
||||||
targets: {},
|
targets: {},
|
||||||
files: [
|
files: [
|
||||||
createFile(`libs/anotherlib/src/main.ts`, ['mylibName']),
|
createFile(`libs/anotherlib/src/main.ts`, [
|
||||||
createFile(`libs/anotherlib/src/index.ts`, ['mylibName']),
|
{
|
||||||
|
source: 'anotherlibName',
|
||||||
|
type: 'static',
|
||||||
|
target: 'mylibName',
|
||||||
|
},
|
||||||
|
]),
|
||||||
|
createFile(`libs/anotherlib/src/index.ts`, [
|
||||||
|
{
|
||||||
|
source: 'anotherlibName',
|
||||||
|
type: 'static',
|
||||||
|
target: 'mylibName',
|
||||||
|
},
|
||||||
|
]),
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -1513,7 +1565,13 @@ Circular file chain:
|
|||||||
implicitDependencies: [],
|
implicitDependencies: [],
|
||||||
targets: {},
|
targets: {},
|
||||||
files: [
|
files: [
|
||||||
createFile(`libs/badcirclelib/src/main.ts`, ['anotherlibName']),
|
createFile(`libs/badcirclelib/src/main.ts`, [
|
||||||
|
{
|
||||||
|
source: 'badcirclelibName',
|
||||||
|
type: 'static',
|
||||||
|
target: 'anotherlibName',
|
||||||
|
},
|
||||||
|
]),
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -2025,8 +2083,11 @@ const baseConfig = {
|
|||||||
linter.defineParser('@typescript-eslint/parser', parser);
|
linter.defineParser('@typescript-eslint/parser', parser);
|
||||||
linter.defineRule(enforceModuleBoundariesRuleName, enforceModuleBoundaries);
|
linter.defineRule(enforceModuleBoundariesRuleName, enforceModuleBoundaries);
|
||||||
|
|
||||||
function createFile(f: string, deps?: string[]): FileData {
|
function createFile(
|
||||||
return { file: f, hash: '', ...(deps && { deps }) };
|
f: string,
|
||||||
|
dependencies?: ProjectGraphDependency[]
|
||||||
|
): FileData {
|
||||||
|
return { file: f, hash: '', ...(dependencies && { dependencies }) };
|
||||||
}
|
}
|
||||||
|
|
||||||
function runRule(
|
function runRule(
|
||||||
|
|||||||
@ -146,7 +146,9 @@ export function findFilesInCircularPath(
|
|||||||
filePathChain.push(
|
filePathChain.push(
|
||||||
Object.keys(files)
|
Object.keys(files)
|
||||||
.filter(
|
.filter(
|
||||||
(key) => files[key].deps && files[key].deps.indexOf(next) !== -1
|
(key) =>
|
||||||
|
files[key].dependencies &&
|
||||||
|
files[key].dependencies.find((d) => d.target === next)
|
||||||
)
|
)
|
||||||
.map((key) => files[key].file)
|
.map((key) => files[key].file)
|
||||||
);
|
);
|
||||||
|
|||||||
@ -11,7 +11,9 @@ import { NxJsonConfiguration } from './nx-json';
|
|||||||
export interface FileData {
|
export interface FileData {
|
||||||
file: string;
|
file: string;
|
||||||
hash: string;
|
hash: string;
|
||||||
|
/** @deprecated this field will be removed in v17. Use {@link dependencies} instead */
|
||||||
deps?: string[];
|
deps?: string[];
|
||||||
|
dependencies?: ProjectGraphDependency[];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -33,15 +35,6 @@ export interface ProjectGraph {
|
|||||||
version?: string;
|
version?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @deprecated this type will be removed in v16. Use {@link ProjectGraph} instead */
|
|
||||||
export interface ProjectGraphV4<T = any> {
|
|
||||||
nodes: Record<string, ProjectGraphNode>;
|
|
||||||
dependencies: Record<string, ProjectGraphDependency[]>;
|
|
||||||
// this is optional otherwise it might break folks who use project graph creation
|
|
||||||
allWorkspaceFiles?: FileData[];
|
|
||||||
version?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Type of dependency between projects
|
* Type of dependency between projects
|
||||||
*/
|
*/
|
||||||
@ -125,7 +118,7 @@ export interface ProjectGraphDependency {
|
|||||||
export interface ProjectGraphProcessorContext {
|
export interface ProjectGraphProcessorContext {
|
||||||
/**
|
/**
|
||||||
* Workspace information such as projects and configuration
|
* Workspace information such as projects and configuration
|
||||||
* @deprecated use projectsConfigurations or nxJsonConfiguration
|
* @deprecated use {@link projectsConfigurations} or {@link nxJsonConfiguration} instead
|
||||||
*/
|
*/
|
||||||
workspace: Workspace;
|
workspace: Workspace;
|
||||||
|
|
||||||
|
|||||||
@ -132,7 +132,6 @@ export type {
|
|||||||
ProjectFileMap,
|
ProjectFileMap,
|
||||||
FileData,
|
FileData,
|
||||||
ProjectGraph,
|
ProjectGraph,
|
||||||
ProjectGraphV4,
|
|
||||||
ProjectGraphDependency,
|
ProjectGraphDependency,
|
||||||
ProjectGraphNode,
|
ProjectGraphNode,
|
||||||
ProjectGraphProjectNode,
|
ProjectGraphProjectNode,
|
||||||
|
|||||||
@ -285,6 +285,8 @@ class TaskHasher {
|
|||||||
visited: string[]
|
visited: string[]
|
||||||
) {
|
) {
|
||||||
const projectGraphDeps = this.projectGraph.dependencies[projectName] ?? [];
|
const projectGraphDeps = this.projectGraph.dependencies[projectName] ?? [];
|
||||||
|
// we don't want random order of dependencies to change the hash
|
||||||
|
projectGraphDeps.sort((a, b) => a.target.localeCompare(b.target));
|
||||||
|
|
||||||
const self = await this.hashSelfInputs(projectName, selfInputs);
|
const self = await this.hashSelfInputs(projectName, selfInputs);
|
||||||
const deps = await this.hashDepsInputs(
|
const deps = await this.hashDepsInputs(
|
||||||
|
|||||||
@ -230,7 +230,7 @@ function addDependencies(
|
|||||||
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) {
|
||||||
builder.addExternalNodeDependency(sourceName, target.name);
|
builder.addStaticDependency(sourceName, target.name);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -291,7 +291,7 @@ function addV1NodeDependencies(
|
|||||||
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) {
|
||||||
builder.addExternalNodeDependency(source, target.name);
|
builder.addStaticDependency(source, target.name);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -317,7 +317,7 @@ function addV1NodeDependencies(
|
|||||||
) {
|
) {
|
||||||
const target = findTarget(path, keyMap, depName, depSpec);
|
const target = findTarget(path, keyMap, depName, depSpec);
|
||||||
if (target) {
|
if (target) {
|
||||||
builder.addExternalNodeDependency(node.name, target.name);
|
builder.addStaticDependency(node.name, target.name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@ -122,7 +122,7 @@ function addDependencies(
|
|||||||
builder.graph.externalNodes[`npm:${name}@${version}`] ||
|
builder.graph.externalNodes[`npm:${name}@${version}`] ||
|
||||||
builder.graph.externalNodes[`npm:${name}`];
|
builder.graph.externalNodes[`npm:${name}`];
|
||||||
if (target) {
|
if (target) {
|
||||||
builder.addExternalNodeDependency(node.name, target.name);
|
builder.addStaticDependency(node.name, target.name);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@ -111,7 +111,7 @@ function traverseNode(
|
|||||||
graph.dependencies[node.name]?.forEach((dep) => {
|
graph.dependencies[node.name]?.forEach((dep) => {
|
||||||
const depNode = graph.externalNodes[dep.target];
|
const depNode = graph.externalNodes[dep.target];
|
||||||
traverseNode(graph, builder, depNode);
|
traverseNode(graph, builder, depNode);
|
||||||
builder.addExternalNodeDependency(node.name, dep.target);
|
builder.addStaticDependency(node.name, dep.target);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -184,11 +184,9 @@ function switchNodeToHoisted(
|
|||||||
builder.addExternalNode(node);
|
builder.addExternalNode(node);
|
||||||
|
|
||||||
targets.forEach((target) => {
|
targets.forEach((target) => {
|
||||||
builder.addExternalNodeDependency(node.name, target);
|
builder.addStaticDependency(node.name, target);
|
||||||
});
|
});
|
||||||
sources.forEach((source) =>
|
sources.forEach((source) => builder.addStaticDependency(source, node.name));
|
||||||
builder.addExternalNodeDependency(source, node.name)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// BFS to find the shortest path to a dependency specified in package.json
|
// BFS to find the shortest path to a dependency specified in package.json
|
||||||
|
|||||||
@ -168,7 +168,7 @@ function addDependencies(
|
|||||||
keyMap.get(`${name}@npm:${versionRange}`) ||
|
keyMap.get(`${name}@npm:${versionRange}`) ||
|
||||||
keyMap.get(`${name}@${versionRange}`);
|
keyMap.get(`${name}@${versionRange}`);
|
||||||
if (target) {
|
if (target) {
|
||||||
builder.addExternalNodeDependency(node.name, target.name);
|
builder.addStaticDependency(node.name, target.name);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,7 @@
|
|||||||
import { buildExplicitTypeScriptDependencies } from './explicit-project-dependencies';
|
import {
|
||||||
|
buildExplicitTypeScriptDependencies,
|
||||||
|
ExplicitDependency,
|
||||||
|
} from './explicit-project-dependencies';
|
||||||
import { buildExplicitPackageJsonDependencies } from './explicit-package-json-dependencies';
|
import { buildExplicitPackageJsonDependencies } from './explicit-package-json-dependencies';
|
||||||
import { ProjectFileMap, ProjectGraph } from '../../config/project-graph';
|
import { ProjectFileMap, ProjectGraph } from '../../config/project-graph';
|
||||||
import { ProjectsConfigurations } from '../../config/workspace-json-project-json';
|
import { ProjectsConfigurations } from '../../config/workspace-json-project-json';
|
||||||
@ -14,7 +17,7 @@ export function buildExplicitTypescriptAndPackageJsonDependencies(
|
|||||||
projectGraph: ProjectGraph,
|
projectGraph: ProjectGraph,
|
||||||
filesToProcess: ProjectFileMap
|
filesToProcess: ProjectFileMap
|
||||||
) {
|
) {
|
||||||
let res = [];
|
let res: ExplicitDependency[] = [];
|
||||||
if (
|
if (
|
||||||
jsPluginConfig.analyzeSourceFiles === undefined ||
|
jsPluginConfig.analyzeSourceFiles === undefined ||
|
||||||
jsPluginConfig.analyzeSourceFiles === true
|
jsPluginConfig.analyzeSourceFiles === true
|
||||||
|
|||||||
@ -5,6 +5,7 @@ import { parseJson } from '../../utils/json';
|
|||||||
import { getImportPath, joinPathFragments } from '../../utils/path';
|
import { getImportPath, joinPathFragments } from '../../utils/path';
|
||||||
import { ProjectsConfigurations } from '../../config/workspace-json-project-json';
|
import { ProjectsConfigurations } from '../../config/workspace-json-project-json';
|
||||||
import { NxJsonConfiguration } from '../../config/nx-json';
|
import { NxJsonConfiguration } from '../../config/nx-json';
|
||||||
|
import { ExplicitDependency } from './explicit-project-dependencies';
|
||||||
|
|
||||||
class ProjectGraphNodeRecords {}
|
class ProjectGraphNodeRecords {}
|
||||||
|
|
||||||
@ -74,7 +75,7 @@ function processPackageJson(
|
|||||||
sourceProject: string,
|
sourceProject: string,
|
||||||
fileName: string,
|
fileName: string,
|
||||||
graph: ProjectGraph,
|
graph: ProjectGraph,
|
||||||
collectedDeps: any[],
|
collectedDeps: ExplicitDependency[],
|
||||||
packageNameMap: { [packageName: string]: string }
|
packageNameMap: { [packageName: string]: string }
|
||||||
) {
|
) {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@ -46,21 +46,25 @@ describe('explicit project dependencies', () => {
|
|||||||
sourceProjectName,
|
sourceProjectName,
|
||||||
sourceProjectFile: 'libs/proj/index.ts',
|
sourceProjectFile: 'libs/proj/index.ts',
|
||||||
targetProjectName: 'proj2',
|
targetProjectName: 'proj2',
|
||||||
|
type: 'static',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
sourceProjectName,
|
sourceProjectName,
|
||||||
sourceProjectFile: 'libs/proj/index.ts',
|
sourceProjectFile: 'libs/proj/index.ts',
|
||||||
targetProjectName: 'proj3a',
|
targetProjectName: 'proj3a',
|
||||||
|
type: 'dynamic',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
sourceProjectName,
|
sourceProjectName,
|
||||||
sourceProjectFile: 'libs/proj/index.ts',
|
sourceProjectFile: 'libs/proj/index.ts',
|
||||||
targetProjectName: 'proj4ab',
|
targetProjectName: 'proj4ab',
|
||||||
|
type: 'static',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
sourceProjectName,
|
sourceProjectName,
|
||||||
sourceProjectFile: 'libs/proj/index.ts',
|
sourceProjectFile: 'libs/proj/index.ts',
|
||||||
targetProjectName: 'npm:npm-package',
|
targetProjectName: 'npm:npm-package',
|
||||||
|
type: 'static',
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
@ -91,16 +95,19 @@ describe('explicit project dependencies', () => {
|
|||||||
sourceProjectName,
|
sourceProjectName,
|
||||||
sourceProjectFile: 'libs/proj/index.ts',
|
sourceProjectFile: 'libs/proj/index.ts',
|
||||||
targetProjectName: 'proj2',
|
targetProjectName: 'proj2',
|
||||||
|
type: 'static',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
sourceProjectName,
|
sourceProjectName,
|
||||||
sourceProjectFile: 'libs/proj/index.ts',
|
sourceProjectFile: 'libs/proj/index.ts',
|
||||||
targetProjectName: 'proj3a',
|
targetProjectName: 'proj3a',
|
||||||
|
type: 'static',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
sourceProjectName,
|
sourceProjectName,
|
||||||
sourceProjectFile: 'libs/proj/index.ts',
|
sourceProjectFile: 'libs/proj/index.ts',
|
||||||
targetProjectName: 'proj4ab',
|
targetProjectName: 'proj4ab',
|
||||||
|
type: 'static',
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
@ -131,16 +138,19 @@ describe('explicit project dependencies', () => {
|
|||||||
sourceProjectName,
|
sourceProjectName,
|
||||||
sourceProjectFile: 'libs/proj/index.ts',
|
sourceProjectFile: 'libs/proj/index.ts',
|
||||||
targetProjectName: 'proj2',
|
targetProjectName: 'proj2',
|
||||||
|
type: 'static',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
sourceProjectName,
|
sourceProjectName,
|
||||||
sourceProjectFile: 'libs/proj/index.ts',
|
sourceProjectFile: 'libs/proj/index.ts',
|
||||||
targetProjectName: 'proj3a',
|
targetProjectName: 'proj3a',
|
||||||
|
type: 'static',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
sourceProjectName,
|
sourceProjectName,
|
||||||
sourceProjectFile: 'libs/proj/index.ts',
|
sourceProjectFile: 'libs/proj/index.ts',
|
||||||
targetProjectName: 'proj4ab',
|
targetProjectName: 'proj4ab',
|
||||||
|
type: 'static',
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
@ -168,7 +178,7 @@ describe('explicit project dependencies', () => {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'libs/proj/component.tsx',
|
path: 'libs/proj/component.tsx',
|
||||||
content: `
|
content: `
|
||||||
export function App() {
|
export function App() {
|
||||||
import('@proj/my-second-proj')
|
import('@proj/my-second-proj')
|
||||||
return (
|
return (
|
||||||
@ -192,16 +202,19 @@ describe('explicit project dependencies', () => {
|
|||||||
sourceProjectName,
|
sourceProjectName,
|
||||||
sourceProjectFile: 'libs/proj/component.tsx',
|
sourceProjectFile: 'libs/proj/component.tsx',
|
||||||
targetProjectName: 'proj2',
|
targetProjectName: 'proj2',
|
||||||
|
type: 'dynamic',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
sourceProjectName,
|
sourceProjectName,
|
||||||
sourceProjectFile: 'libs/proj/nested-dynamic-import.ts',
|
sourceProjectFile: 'libs/proj/nested-dynamic-import.ts',
|
||||||
targetProjectName: 'proj3a',
|
targetProjectName: 'proj3a',
|
||||||
|
type: 'dynamic',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
sourceProjectName,
|
sourceProjectName,
|
||||||
sourceProjectFile: 'libs/proj/nested-require.ts',
|
sourceProjectFile: 'libs/proj/nested-require.ts',
|
||||||
targetProjectName: 'proj4ab',
|
targetProjectName: 'proj4ab',
|
||||||
|
type: 'static',
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
@ -236,11 +249,13 @@ describe('explicit project dependencies', () => {
|
|||||||
sourceProjectName,
|
sourceProjectName,
|
||||||
sourceProjectFile: 'libs/proj/absolute-path.ts',
|
sourceProjectFile: 'libs/proj/absolute-path.ts',
|
||||||
targetProjectName: 'proj3a',
|
targetProjectName: 'proj3a',
|
||||||
|
type: 'dynamic',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
sourceProjectName,
|
sourceProjectName,
|
||||||
sourceProjectFile: 'libs/proj/relative-path.ts',
|
sourceProjectFile: 'libs/proj/relative-path.ts',
|
||||||
targetProjectName: 'proj4ab',
|
targetProjectName: 'proj4ab',
|
||||||
|
type: 'dynamic',
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
@ -313,15 +328,15 @@ describe('explicit project dependencies', () => {
|
|||||||
{
|
{
|
||||||
path: 'libs/proj/comments-with-excess-whitespace.ts',
|
path: 'libs/proj/comments-with-excess-whitespace.ts',
|
||||||
content: `
|
content: `
|
||||||
/*
|
/*
|
||||||
nx-ignore-next-line
|
nx-ignore-next-line
|
||||||
|
|
||||||
*/
|
*/
|
||||||
require('@proj/proj4ab');
|
require('@proj/proj4ab');
|
||||||
// nx-ignore-next-line
|
// nx-ignore-next-line
|
||||||
import('@proj/proj4ab');
|
import('@proj/proj4ab');
|
||||||
/*
|
/*
|
||||||
|
|
||||||
nx-ignore-next-line */
|
nx-ignore-next-line */
|
||||||
import { foo } from '@proj/proj4ab';
|
import { foo } from '@proj/proj4ab';
|
||||||
`,
|
`,
|
||||||
@ -464,11 +479,13 @@ describe('explicit project dependencies', () => {
|
|||||||
sourceProjectName,
|
sourceProjectName,
|
||||||
sourceProjectFile: 'libs/proj/file-1.ts',
|
sourceProjectFile: 'libs/proj/file-1.ts',
|
||||||
targetProjectName: 'proj4ab',
|
targetProjectName: 'proj4ab',
|
||||||
|
type: 'dynamic',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
sourceProjectName,
|
sourceProjectName,
|
||||||
sourceProjectFile: 'libs/proj/file-2.ts',
|
sourceProjectFile: 'libs/proj/file-2.ts',
|
||||||
targetProjectName: 'proj3a',
|
targetProjectName: 'proj3a',
|
||||||
|
type: 'dynamic',
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|||||||
@ -6,6 +6,13 @@ import {
|
|||||||
ProjectGraph,
|
ProjectGraph,
|
||||||
} from '../../config/project-graph';
|
} from '../../config/project-graph';
|
||||||
|
|
||||||
|
export type ExplicitDependency = {
|
||||||
|
sourceProjectName: string;
|
||||||
|
targetProjectName: string;
|
||||||
|
sourceProjectFile: string;
|
||||||
|
type?: DependencyType.static | DependencyType.dynamic;
|
||||||
|
};
|
||||||
|
|
||||||
export function buildExplicitTypeScriptDependencies(
|
export function buildExplicitTypeScriptDependencies(
|
||||||
graph: ProjectGraph,
|
graph: ProjectGraph,
|
||||||
filesToProcess: ProjectFileMap
|
filesToProcess: ProjectFileMap
|
||||||
@ -19,12 +26,16 @@ export function buildExplicitTypeScriptDependencies(
|
|||||||
graph.nodes as any,
|
graph.nodes as any,
|
||||||
graph.externalNodes
|
graph.externalNodes
|
||||||
);
|
);
|
||||||
const res = [] as any;
|
const res: ExplicitDependency[] = [];
|
||||||
Object.keys(filesToProcess).forEach((source) => {
|
Object.keys(filesToProcess).forEach((source) => {
|
||||||
Object.values(filesToProcess[source]).forEach((f) => {
|
Object.values(filesToProcess[source]).forEach((f) => {
|
||||||
importLocator.fromFile(
|
importLocator.fromFile(
|
||||||
f.file,
|
f.file,
|
||||||
(importExpr: string, filePath: string, type: DependencyType) => {
|
(
|
||||||
|
importExpr: string,
|
||||||
|
filePath: string,
|
||||||
|
type: DependencyType.static | DependencyType.dynamic
|
||||||
|
) => {
|
||||||
const target = targetProjectLocator.findProjectWithImport(
|
const target = targetProjectLocator.findProjectWithImport(
|
||||||
importExpr,
|
importExpr,
|
||||||
f.file
|
f.file
|
||||||
@ -39,6 +50,7 @@ export function buildExplicitTypeScriptDependencies(
|
|||||||
sourceProjectName: source,
|
sourceProjectName: source,
|
||||||
targetProjectName: target,
|
targetProjectName: target,
|
||||||
sourceProjectFile: f.file,
|
sourceProjectFile: f.file,
|
||||||
|
type,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -243,7 +243,6 @@ describe('project graph', () => {
|
|||||||
expect(graph.dependencies).toEqual({
|
expect(graph.dependencies).toEqual({
|
||||||
api: [{ source: 'api', target: 'npm:express', type: 'static' }],
|
api: [{ source: 'api', target: 'npm:express', type: 'static' }],
|
||||||
demo: [
|
demo: [
|
||||||
{ source: 'demo', target: 'api', type: 'implicit' },
|
|
||||||
{
|
{
|
||||||
source: 'demo',
|
source: 'demo',
|
||||||
target: 'ui',
|
target: 'ui',
|
||||||
@ -253,8 +252,9 @@ describe('project graph', () => {
|
|||||||
{
|
{
|
||||||
source: 'demo',
|
source: 'demo',
|
||||||
target: 'lazy-lib',
|
target: 'lazy-lib',
|
||||||
type: 'static',
|
type: 'dynamic',
|
||||||
},
|
},
|
||||||
|
{ source: 'demo', target: 'api', type: 'implicit' },
|
||||||
],
|
],
|
||||||
'demo-e2e': [],
|
'demo-e2e': [],
|
||||||
'lazy-lib': [],
|
'lazy-lib': [],
|
||||||
@ -267,7 +267,7 @@ describe('project graph', () => {
|
|||||||
{
|
{
|
||||||
source: 'ui',
|
source: 'ui',
|
||||||
target: 'lazy-lib',
|
target: 'lazy-lib',
|
||||||
type: 'static',
|
type: 'dynamic',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
@ -295,7 +295,7 @@ describe('project graph', () => {
|
|||||||
target: 'shared-util',
|
target: 'shared-util',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: DependencyType.static,
|
type: DependencyType.dynamic,
|
||||||
source: 'ui',
|
source: 'ui',
|
||||||
target: 'lazy-lib',
|
target: 'lazy-lib',
|
||||||
},
|
},
|
||||||
|
|||||||
@ -11,7 +11,10 @@ import {
|
|||||||
shouldRecomputeWholeGraph,
|
shouldRecomputeWholeGraph,
|
||||||
writeCache,
|
writeCache,
|
||||||
} from './nx-deps-cache';
|
} from './nx-deps-cache';
|
||||||
import { buildImplicitProjectDependencies } from './build-dependencies';
|
import {
|
||||||
|
buildImplicitProjectDependencies,
|
||||||
|
ExplicitDependency,
|
||||||
|
} from './build-dependencies';
|
||||||
import { buildWorkspaceProjectNodes } from './build-nodes';
|
import { buildWorkspaceProjectNodes } from './build-nodes';
|
||||||
import * as os from 'os';
|
import * as os from 'os';
|
||||||
import { buildExplicitTypescriptAndPackageJsonDependencies } from './build-dependencies/build-explicit-typescript-and-package-json-dependencies';
|
import { buildExplicitTypescriptAndPackageJsonDependencies } from './build-dependencies/build-explicit-typescript-and-package-json-dependencies';
|
||||||
@ -75,7 +78,7 @@ export async function buildProjectGraphUsingProjectFileMap(
|
|||||||
projectGraphCache: ProjectGraphCache;
|
projectGraphCache: ProjectGraphCache;
|
||||||
}> {
|
}> {
|
||||||
const nxJson = readNxJson();
|
const nxJson = readNxJson();
|
||||||
const projectGraphVersion = '5.0';
|
const projectGraphVersion = '5.1';
|
||||||
assertWorkspaceValidity(projectsConfigurations, nxJson);
|
assertWorkspaceValidity(projectsConfigurations, nxJson);
|
||||||
const packageJsonDeps = readCombinedDeps();
|
const packageJsonDeps = readCombinedDeps();
|
||||||
const rootTsConfig = readRootTsConfig();
|
const rootTsConfig = readRootTsConfig();
|
||||||
@ -203,8 +206,8 @@ async function buildProjectGraphUsingContext(
|
|||||||
for (const proj of Object.keys(cachedFileData)) {
|
for (const proj of Object.keys(cachedFileData)) {
|
||||||
for (const f of updatedBuilder.graph.nodes[proj].data.files) {
|
for (const f of updatedBuilder.graph.nodes[proj].data.files) {
|
||||||
const cached = cachedFileData[proj][f.file];
|
const cached = cachedFileData[proj][f.file];
|
||||||
if (cached && cached.deps) {
|
if (cached && cached.dependencies) {
|
||||||
f.deps = [...cached.deps];
|
f.dependencies = [...cached.dependencies];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -346,11 +349,19 @@ function buildExplicitDependenciesWithoutWorkers(
|
|||||||
builder.graph,
|
builder.graph,
|
||||||
ctx.filesToProcess
|
ctx.filesToProcess
|
||||||
).forEach((r) => {
|
).forEach((r) => {
|
||||||
builder.addExplicitDependency(
|
if (r.type === 'static') {
|
||||||
r.sourceProjectName,
|
builder.addStaticDependency(
|
||||||
r.sourceProjectFile,
|
r.sourceProjectName,
|
||||||
r.targetProjectName
|
r.targetProjectName,
|
||||||
);
|
r.sourceProjectFile
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
builder.addDynamicDependency(
|
||||||
|
r.sourceProjectName,
|
||||||
|
r.targetProjectName,
|
||||||
|
r.sourceProjectFile
|
||||||
|
);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -378,12 +389,20 @@ function buildExplicitDependenciesUsingWorkers(
|
|||||||
return new Promise((res, reject) => {
|
return new Promise((res, reject) => {
|
||||||
for (let w of workers) {
|
for (let w of workers) {
|
||||||
w.on('message', (explicitDependencies) => {
|
w.on('message', (explicitDependencies) => {
|
||||||
explicitDependencies.forEach((r) => {
|
explicitDependencies.forEach((r: ExplicitDependency) => {
|
||||||
builder.addExplicitDependency(
|
if (r.type === 'static') {
|
||||||
r.sourceProjectName,
|
builder.addStaticDependency(
|
||||||
r.sourceProjectFile,
|
r.sourceProjectName,
|
||||||
r.targetProjectName
|
r.targetProjectName,
|
||||||
);
|
r.sourceProjectFile
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
builder.addDynamicDependency(
|
||||||
|
r.sourceProjectName,
|
||||||
|
r.targetProjectName,
|
||||||
|
r.sourceProjectFile
|
||||||
|
);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
if (bins.length > 0) {
|
if (bins.length > 0) {
|
||||||
w.postMessage({ filesToProcess: bins.shift() });
|
w.postMessage({ filesToProcess: bins.shift() });
|
||||||
|
|||||||
@ -13,7 +13,7 @@ describe('nx deps utils', () => {
|
|||||||
it('should be false when nothing changes', () => {
|
it('should be false when nothing changes', () => {
|
||||||
expect(
|
expect(
|
||||||
shouldRecomputeWholeGraph(
|
shouldRecomputeWholeGraph(
|
||||||
createCache({ version: '5.0' }),
|
createCache({ version: '5.1' }),
|
||||||
createPackageJsonDeps({}),
|
createPackageJsonDeps({}),
|
||||||
createWorkspaceJson({}),
|
createWorkspaceJson({}),
|
||||||
createNxJson({}),
|
createNxJson({}),
|
||||||
@ -318,7 +318,7 @@ describe('nx deps utils', () => {
|
|||||||
|
|
||||||
function createCache(p: Partial<ProjectGraphCache>): ProjectGraphCache {
|
function createCache(p: Partial<ProjectGraphCache>): ProjectGraphCache {
|
||||||
const defaults: ProjectGraphCache = {
|
const defaults: ProjectGraphCache = {
|
||||||
version: '5.0',
|
version: '5.1',
|
||||||
deps: {
|
deps: {
|
||||||
'@nrwl/workspace': '12.0.0',
|
'@nrwl/workspace': '12.0.0',
|
||||||
plugin: '1.0.0',
|
plugin: '1.0.0',
|
||||||
|
|||||||
@ -94,7 +94,7 @@ export function createCache(
|
|||||||
version: packageJsonDeps[p],
|
version: packageJsonDeps[p],
|
||||||
}));
|
}));
|
||||||
const newValue: ProjectGraphCache = {
|
const newValue: ProjectGraphCache = {
|
||||||
version: projectGraph.version || '5.0',
|
version: projectGraph.version || '5.1',
|
||||||
deps: packageJsonDeps,
|
deps: packageJsonDeps,
|
||||||
lockFileHash,
|
lockFileHash,
|
||||||
// compilerOptions may not exist, especially for repos converted through add-nx-to-monorepo
|
// compilerOptions may not exist, especially for repos converted through add-nx-to-monorepo
|
||||||
@ -149,7 +149,7 @@ export function shouldRecomputeWholeGraph(
|
|||||||
nxJson: NxJsonConfiguration,
|
nxJson: NxJsonConfiguration,
|
||||||
tsConfig: { compilerOptions: { paths: { [k: string]: any } } }
|
tsConfig: { compilerOptions: { paths: { [k: string]: any } } }
|
||||||
): boolean {
|
): boolean {
|
||||||
if (cache.version !== '5.0') {
|
if (cache.version !== '5.1') {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (cache.deps['@nrwl/workspace'] !== packageJsonDeps['@nrwl/workspace']) {
|
if (cache.deps['@nrwl/workspace'] !== packageJsonDeps['@nrwl/workspace']) {
|
||||||
|
|||||||
@ -25,7 +25,7 @@ describe('ProjectGraphBuilder', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it(`should add an implicit dependency`, () => {
|
it(`should add a dependency`, () => {
|
||||||
expect(() =>
|
expect(() =>
|
||||||
builder.addImplicitDependency('invalid-source', 'target')
|
builder.addImplicitDependency('invalid-source', 'target')
|
||||||
).toThrowError();
|
).toThrowError();
|
||||||
@ -34,11 +34,40 @@ describe('ProjectGraphBuilder', () => {
|
|||||||
).toThrowError();
|
).toThrowError();
|
||||||
|
|
||||||
// ignore the self deps
|
// ignore the self deps
|
||||||
builder.addImplicitDependency('source', 'source');
|
builder.addDynamicDependency('source', 'source', 'source/index.ts');
|
||||||
|
|
||||||
// don't include duplicates
|
// don't include duplicates of the same type
|
||||||
builder.addImplicitDependency('source', 'target');
|
builder.addImplicitDependency('source', 'target');
|
||||||
builder.addImplicitDependency('source', 'target');
|
builder.addImplicitDependency('source', 'target');
|
||||||
|
builder.addStaticDependency('source', 'target', 'source/index.ts');
|
||||||
|
builder.addDynamicDependency('source', 'target', 'source/index.ts');
|
||||||
|
builder.addStaticDependency('source', 'target', 'source/index.ts');
|
||||||
|
|
||||||
|
const graph = builder.getUpdatedProjectGraph();
|
||||||
|
expect(graph.dependencies).toEqual({
|
||||||
|
source: [
|
||||||
|
{
|
||||||
|
source: 'source',
|
||||||
|
target: 'target',
|
||||||
|
type: 'implicit',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
source: 'source',
|
||||||
|
target: 'target',
|
||||||
|
type: 'static',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
source: 'source',
|
||||||
|
target: 'target',
|
||||||
|
type: 'dynamic',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
target: [],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it(`should add an implicit dependency`, () => {
|
||||||
|
builder.addImplicitDependency('source', 'target');
|
||||||
|
|
||||||
const graph = builder.getUpdatedProjectGraph();
|
const graph = builder.getUpdatedProjectGraph();
|
||||||
expect(graph.dependencies).toEqual({
|
expect(graph.dependencies).toEqual({
|
||||||
@ -96,10 +125,10 @@ describe('ProjectGraphBuilder', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it(`should use implicit dep when both implicit and explicit deps are available`, () => {
|
it(`should use both deps when both implicit and explicit deps are available`, () => {
|
||||||
// don't include duplicates
|
// don't include duplicates
|
||||||
builder.addImplicitDependency('source', 'target');
|
builder.addImplicitDependency('source', 'target');
|
||||||
builder.addExplicitDependency('source', 'source/index.ts', 'target');
|
builder.addStaticDependency('source', 'target', 'source/index.ts');
|
||||||
|
|
||||||
const graph = builder.getUpdatedProjectGraph();
|
const graph = builder.getUpdatedProjectGraph();
|
||||||
expect(graph.dependencies).toEqual({
|
expect(graph.dependencies).toEqual({
|
||||||
@ -109,6 +138,11 @@ describe('ProjectGraphBuilder', () => {
|
|||||||
target: 'target',
|
target: 'target',
|
||||||
type: 'implicit',
|
type: 'implicit',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
source: 'source',
|
||||||
|
target: 'target',
|
||||||
|
type: 'static',
|
||||||
|
},
|
||||||
],
|
],
|
||||||
target: [],
|
target: [],
|
||||||
});
|
});
|
||||||
@ -121,7 +155,7 @@ describe('ProjectGraphBuilder', () => {
|
|||||||
data: {} as any,
|
data: {} as any,
|
||||||
});
|
});
|
||||||
builder.addImplicitDependency('source', 'target');
|
builder.addImplicitDependency('source', 'target');
|
||||||
builder.addExplicitDependency('source', 'source/index.ts', 'target');
|
builder.addStaticDependency('source', 'target', 'source/index.ts');
|
||||||
builder.addImplicitDependency('source', 'target2');
|
builder.addImplicitDependency('source', 'target2');
|
||||||
builder.removeDependency('source', 'target');
|
builder.removeDependency('source', 'target');
|
||||||
|
|
||||||
|
|||||||
@ -44,7 +44,6 @@ export class ProjectGraphBuilder {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.graph.nodes[node.name] = node;
|
this.graph.nodes[node.name] = node;
|
||||||
this.graph.dependencies[node.name] = [];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -72,29 +71,61 @@ export class ProjectGraphBuilder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds a dependency from source project to target project
|
* Adds static dependency from source project to target project
|
||||||
|
*/
|
||||||
|
addStaticDependency(
|
||||||
|
sourceProjectName: string,
|
||||||
|
targetProjectName: string,
|
||||||
|
sourceProjectFile?: string
|
||||||
|
): void {
|
||||||
|
if (this.graph.nodes[sourceProjectName] && !sourceProjectFile) {
|
||||||
|
throw new Error(`Source project file is required`);
|
||||||
|
}
|
||||||
|
this.addDependency(
|
||||||
|
sourceProjectName,
|
||||||
|
targetProjectName,
|
||||||
|
DependencyType.static,
|
||||||
|
sourceProjectFile
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds dynamic dependency from source project to target project
|
||||||
|
*/
|
||||||
|
addDynamicDependency(
|
||||||
|
sourceProjectName: string,
|
||||||
|
targetProjectName: string,
|
||||||
|
sourceProjectFile: string
|
||||||
|
): void {
|
||||||
|
if (this.graph.externalNodes[sourceProjectName]) {
|
||||||
|
throw new Error(`External projects can't have "dynamic" dependencies`);
|
||||||
|
}
|
||||||
|
if (!sourceProjectFile) {
|
||||||
|
throw new Error(`Source project file is required`);
|
||||||
|
}
|
||||||
|
this.addDependency(
|
||||||
|
sourceProjectName,
|
||||||
|
targetProjectName,
|
||||||
|
DependencyType.dynamic,
|
||||||
|
sourceProjectFile
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds implicit dependency from source project to target project
|
||||||
*/
|
*/
|
||||||
addImplicitDependency(
|
addImplicitDependency(
|
||||||
sourceProjectName: string,
|
sourceProjectName: string,
|
||||||
targetProjectName: string
|
targetProjectName: string
|
||||||
): void {
|
): void {
|
||||||
if (sourceProjectName === targetProjectName) {
|
if (this.graph.externalNodes[sourceProjectName]) {
|
||||||
return;
|
throw new Error(`External projects can't have "implicit" dependencies`);
|
||||||
}
|
}
|
||||||
if (!this.graph.nodes[sourceProjectName]) {
|
this.addDependency(
|
||||||
throw new Error(`Source project does not exist: ${sourceProjectName}`);
|
sourceProjectName,
|
||||||
}
|
targetProjectName,
|
||||||
if (
|
DependencyType.implicit
|
||||||
!this.graph.nodes[targetProjectName] &&
|
);
|
||||||
!this.graph.externalNodes[targetProjectName]
|
|
||||||
) {
|
|
||||||
throw new Error(`Target project does not exist: ${targetProjectName}`);
|
|
||||||
}
|
|
||||||
this.graph.dependencies[sourceProjectName].push({
|
|
||||||
source: sourceProjectName,
|
|
||||||
target: targetProjectName,
|
|
||||||
type: DependencyType.implicit,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -124,6 +155,7 @@ export class ProjectGraphBuilder {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Add an explicit dependency from a file in source project to target project
|
* Add an explicit dependency from a file in source project to target project
|
||||||
|
* @deprecated this method will be removed in v17. Use {@link addStaticDependency} or {@link addDynamicDependency} instead
|
||||||
*/
|
*/
|
||||||
addExplicitDependency(
|
addExplicitDependency(
|
||||||
sourceProjectName: string,
|
sourceProjectName: string,
|
||||||
@ -154,46 +186,14 @@ export class ProjectGraphBuilder {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!fileData.deps) {
|
if (!fileData.dependencies) {
|
||||||
fileData.deps = [];
|
fileData.dependencies = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!fileData.deps.find((t) => t === targetProjectName)) {
|
if (!fileData.dependencies.find((t) => t.target === targetProjectName)) {
|
||||||
fileData.deps.push(targetProjectName);
|
fileData.dependencies.push({
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add an explicit dependency from a file in source project to target project
|
|
||||||
*/
|
|
||||||
addExternalNodeDependency(
|
|
||||||
sourceProjectName: string,
|
|
||||||
targetProjectName: string
|
|
||||||
): void {
|
|
||||||
if (sourceProjectName === targetProjectName) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const source = this.graph.externalNodes[sourceProjectName];
|
|
||||||
if (!source) {
|
|
||||||
throw new Error(`Source project does not exist: ${sourceProjectName}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!this.graph.externalNodes[targetProjectName]) {
|
|
||||||
throw new Error(`Target project does not exist: ${targetProjectName}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!this.graph.dependencies[sourceProjectName]) {
|
|
||||||
this.graph.dependencies[sourceProjectName] = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
!this.graph.dependencies[sourceProjectName].some(
|
|
||||||
(d) => d.target === targetProjectName
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
this.graph.dependencies[sourceProjectName].push({
|
|
||||||
source: sourceProjectName,
|
|
||||||
target: targetProjectName,
|
target: targetProjectName,
|
||||||
|
source: sourceProjectName,
|
||||||
type: DependencyType.static,
|
type: DependencyType.static,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -212,20 +212,25 @@ export class ProjectGraphBuilder {
|
|||||||
this.calculateAlreadySetTargetDeps(sourceProject);
|
this.calculateAlreadySetTargetDeps(sourceProject);
|
||||||
this.graph.dependencies[sourceProject] = [
|
this.graph.dependencies[sourceProject] = [
|
||||||
...alreadySetTargetProjects.values(),
|
...alreadySetTargetProjects.values(),
|
||||||
];
|
].flatMap((depsMap) => [...depsMap.values()]);
|
||||||
|
|
||||||
const fileDeps = this.calculateTargetDepsFromFiles(sourceProject);
|
const fileDeps = this.calculateTargetDepsFromFiles(sourceProject);
|
||||||
for (const targetProject of fileDeps) {
|
for (const [targetProject, types] of fileDeps.entries()) {
|
||||||
if (!alreadySetTargetProjects.has(targetProject)) {
|
for (const type of types.values()) {
|
||||||
if (
|
if (
|
||||||
!this.removedEdges[sourceProject] ||
|
!alreadySetTargetProjects.has(targetProject) ||
|
||||||
!this.removedEdges[sourceProject].has(targetProject)
|
!alreadySetTargetProjects.get(targetProject).has(type)
|
||||||
) {
|
) {
|
||||||
this.graph.dependencies[sourceProject].push({
|
if (
|
||||||
source: sourceProject,
|
!this.removedEdges[sourceProject] ||
|
||||||
target: targetProject,
|
!this.removedEdges[sourceProject].has(targetProject)
|
||||||
type: DependencyType.static,
|
) {
|
||||||
});
|
this.graph.dependencies[sourceProject].push({
|
||||||
|
source: sourceProject,
|
||||||
|
target: targetProject,
|
||||||
|
type,
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -233,6 +238,78 @@ export class ProjectGraphBuilder {
|
|||||||
return this.graph;
|
return this.graph;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private addDependency(
|
||||||
|
sourceProjectName: string,
|
||||||
|
targetProjectName: string,
|
||||||
|
type: DependencyType,
|
||||||
|
sourceProjectFile?: string
|
||||||
|
): void {
|
||||||
|
if (sourceProjectName === targetProjectName) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
!this.graph.nodes[sourceProjectName] &&
|
||||||
|
!this.graph.externalNodes[sourceProjectName]
|
||||||
|
) {
|
||||||
|
throw new Error(`Source project does not exist: ${sourceProjectName}`);
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
!this.graph.nodes[targetProjectName] &&
|
||||||
|
!this.graph.externalNodes[targetProjectName]
|
||||||
|
) {
|
||||||
|
throw new Error(`Target project does not exist: ${targetProjectName}`);
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
this.graph.externalNodes[sourceProjectName] &&
|
||||||
|
this.graph.nodes[targetProjectName]
|
||||||
|
) {
|
||||||
|
throw new Error(`External projects can't depend on internal projects`);
|
||||||
|
}
|
||||||
|
if (!this.graph.dependencies[sourceProjectName]) {
|
||||||
|
this.graph.dependencies[sourceProjectName] = [];
|
||||||
|
}
|
||||||
|
// do not add duplicate
|
||||||
|
if (
|
||||||
|
this.graph.dependencies[sourceProjectName].find(
|
||||||
|
(d) => d.target === targetProjectName && d.type === type
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const dependency = {
|
||||||
|
source: sourceProjectName,
|
||||||
|
target: targetProjectName,
|
||||||
|
type,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (sourceProjectFile) {
|
||||||
|
const source = this.graph.nodes[sourceProjectName];
|
||||||
|
if (!source) {
|
||||||
|
throw new Error(
|
||||||
|
`Source project is not a project node: ${sourceProjectName}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
const fileData = source.data.files.find(
|
||||||
|
(f) => f.file === sourceProjectFile
|
||||||
|
);
|
||||||
|
if (!fileData) {
|
||||||
|
throw new Error(
|
||||||
|
`Source project ${sourceProjectName} does not have a file: ${sourceProjectFile}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!fileData.dependencies) {
|
||||||
|
fileData.dependencies = [];
|
||||||
|
}
|
||||||
|
if (!fileData.dependencies.find((t) => t.target === targetProjectName)) {
|
||||||
|
fileData.dependencies.push(dependency);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.graph.dependencies[sourceProjectName].push(dependency);
|
||||||
|
}
|
||||||
|
|
||||||
private removeDependenciesWithNode(name: string) {
|
private removeDependenciesWithNode(name: string) {
|
||||||
// remove all source dependencies
|
// remove all source dependencies
|
||||||
delete this.graph.dependencies[name];
|
delete this.graph.dependencies[name];
|
||||||
@ -254,26 +331,45 @@ export class ProjectGraphBuilder {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private calculateTargetDepsFromFiles(sourceProject: string) {
|
private calculateTargetDepsFromFiles(
|
||||||
const fileDeps = new Set<string>();
|
sourceProject: string
|
||||||
|
): Map<string, Set<DependencyType | string>> {
|
||||||
|
const fileDeps = new Map<string, Set<DependencyType | string>>();
|
||||||
const files = this.graph.nodes[sourceProject].data.files;
|
const files = this.graph.nodes[sourceProject].data.files;
|
||||||
if (!files) return fileDeps;
|
if (!files) {
|
||||||
|
return fileDeps;
|
||||||
|
}
|
||||||
for (let f of files) {
|
for (let f of files) {
|
||||||
if (f.deps) {
|
if (f.dependencies) {
|
||||||
for (let p of f.deps) {
|
for (let d of f.dependencies) {
|
||||||
fileDeps.add(p);
|
if (!fileDeps.has(d.target)) {
|
||||||
|
fileDeps.set(d.target, new Set([d.type]));
|
||||||
|
} else {
|
||||||
|
fileDeps.get(d.target).add(d.type);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return fileDeps;
|
return fileDeps;
|
||||||
}
|
}
|
||||||
|
|
||||||
private calculateAlreadySetTargetDeps(sourceProject: string) {
|
private calculateAlreadySetTargetDeps(
|
||||||
const alreadySetTargetProjects = new Map<string, ProjectGraphDependency>();
|
sourceProject: string
|
||||||
const removed = this.removedEdges[sourceProject];
|
): Map<string, Map<DependencyType | string, ProjectGraphDependency>> {
|
||||||
for (const d of this.graph.dependencies[sourceProject]) {
|
const alreadySetTargetProjects = new Map<
|
||||||
if (!removed || !removed.has(d.target)) {
|
string,
|
||||||
alreadySetTargetProjects.set(d.target, d);
|
Map<DependencyType | string, ProjectGraphDependency>
|
||||||
|
>();
|
||||||
|
if (this.graph.dependencies[sourceProject]) {
|
||||||
|
const removed = this.removedEdges[sourceProject];
|
||||||
|
for (const d of this.graph.dependencies[sourceProject]) {
|
||||||
|
if (!removed || !removed.has(d.target)) {
|
||||||
|
if (!alreadySetTargetProjects.has(d.target)) {
|
||||||
|
alreadySetTargetProjects.set(d.target, new Map([[d.type, d]]));
|
||||||
|
} else {
|
||||||
|
alreadySetTargetProjects.get(d.target).set(d.type, d);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return alreadySetTargetProjects;
|
return alreadySetTargetProjects;
|
||||||
|
|||||||
@ -3,7 +3,7 @@ import { buildProjectGraph } from './build-project-graph';
|
|||||||
import { output } from '../utils/output';
|
import { output } from '../utils/output';
|
||||||
import { defaultFileHasher } from '../hasher/file-hasher';
|
import { defaultFileHasher } from '../hasher/file-hasher';
|
||||||
import { markDaemonAsDisabled, writeDaemonLogs } from '../daemon/tmp-dir';
|
import { markDaemonAsDisabled, writeDaemonLogs } from '../daemon/tmp-dir';
|
||||||
import { ProjectGraph, ProjectGraphV4 } from '../config/project-graph';
|
import { ProjectGraph } from '../config/project-graph';
|
||||||
import { stripIndents } from '../utils/strip-indents';
|
import { stripIndents } from '../utils/strip-indents';
|
||||||
import {
|
import {
|
||||||
ProjectConfiguration,
|
ProjectConfiguration,
|
||||||
@ -47,7 +47,7 @@ export function readCachedProjectGraph(): ProjectGraph {
|
|||||||
|
|
||||||
return projectGraphAdapter(
|
return projectGraphAdapter(
|
||||||
projectGraph.version,
|
projectGraph.version,
|
||||||
'5.0',
|
'5.1',
|
||||||
projectGraph
|
projectGraph
|
||||||
) as ProjectGraph;
|
) as ProjectGraph;
|
||||||
}
|
}
|
||||||
@ -179,43 +179,38 @@ export function projectGraphAdapter(
|
|||||||
sourceVersion: string,
|
sourceVersion: string,
|
||||||
targetVersion: string,
|
targetVersion: string,
|
||||||
projectGraph: ProjectGraph
|
projectGraph: ProjectGraph
|
||||||
): ProjectGraph | ProjectGraphV4 {
|
): ProjectGraph {
|
||||||
if (sourceVersion === targetVersion) {
|
if (sourceVersion === targetVersion) {
|
||||||
return projectGraph;
|
return projectGraph;
|
||||||
}
|
}
|
||||||
if (+sourceVersion >= 5 && targetVersion === '4.0') {
|
if (+sourceVersion > 5 && +targetVersion === 5) {
|
||||||
return projectGraphCompat5to4(projectGraph as ProjectGraph);
|
return projectGraphCompatFileDependencies(projectGraph as ProjectGraph);
|
||||||
}
|
}
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Invalid source or target versions. Source: ${sourceVersion}, Target: ${targetVersion}.
|
`Invalid source or target versions. Source: ${sourceVersion}, Target: ${targetVersion}.
|
||||||
|
|
||||||
Only backwards compatibility between "5.0" and "4.0" is supported.
|
Only backwards compatibility between "5.1" and "5.0" is supported.
|
||||||
This error can be caused by "@nrwl/..." packages getting out of sync or outdated project graph cache.
|
This error can be caused by "@nrwl/..." packages getting out of sync or outdated project graph cache.
|
||||||
Check the versions running "nx report" and/or remove your "nxdeps.json" file (in node_modules/.cache/nx folder).
|
Check the versions running "nx report" and/or remove your "nxdeps.json" file (in node_modules/.cache/nx folder).
|
||||||
`
|
`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
function projectGraphCompatFileDependencies(
|
||||||
* Backwards compatibility adapter for project Nodes v4 to v5
|
projectGraph: ProjectGraph
|
||||||
* @param {ProjectGraph} projectGraph
|
): ProjectGraph {
|
||||||
* @returns {ProjectGraph}
|
Object.values(projectGraph.nodes).forEach(({ data }) => {
|
||||||
*/
|
if (data.files) {
|
||||||
function projectGraphCompat5to4(projectGraph: ProjectGraph): ProjectGraphV4 {
|
data.files = data.files.map(({ file, hash, dependencies }) => ({
|
||||||
const { externalNodes, ...rest } = projectGraph;
|
file,
|
||||||
return {
|
hash,
|
||||||
...rest,
|
// map dependencies to array of targets
|
||||||
nodes: {
|
...(dependencies &&
|
||||||
...projectGraph.nodes,
|
dependencies.length && {
|
||||||
...externalNodes,
|
deps: [...new Set(dependencies.map((d) => d.target))],
|
||||||
},
|
}),
|
||||||
dependencies: {
|
}));
|
||||||
...projectGraph.dependencies,
|
}
|
||||||
...Object.keys(externalNodes).reduce(
|
});
|
||||||
(acc, key) => ({ ...acc, [`npm:${key}`]: [] }),
|
return projectGraph;
|
||||||
{}
|
|
||||||
),
|
|
||||||
},
|
|
||||||
version: '4.0',
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,6 +8,14 @@ import applicationGenerator from '../application/application';
|
|||||||
import componentGenerator from '../component/component';
|
import componentGenerator from '../component/component';
|
||||||
import storybookConfigurationGenerator from './configuration';
|
import storybookConfigurationGenerator from './configuration';
|
||||||
|
|
||||||
|
// nested code imports graph from the repo, which might have innacurate graph version
|
||||||
|
jest.mock('nx/src/project-graph/project-graph', () => ({
|
||||||
|
...jest.requireActual<any>('nx/src/project-graph/project-graph'),
|
||||||
|
createProjectGraphAsync: jest
|
||||||
|
.fn()
|
||||||
|
.mockImplementation(async () => ({ nodes: {}, dependencies: {} })),
|
||||||
|
}));
|
||||||
|
|
||||||
describe('react-native:storybook-configuration', () => {
|
describe('react-native:storybook-configuration', () => {
|
||||||
let appTree;
|
let appTree;
|
||||||
|
|
||||||
|
|||||||
@ -22,6 +22,14 @@ jest.mock('@nrwl/devkit', () => ({
|
|||||||
.mockImplementation(async () => projectGraph),
|
.mockImplementation(async () => projectGraph),
|
||||||
}));
|
}));
|
||||||
jest.mock('@nrwl/cypress/src/utils/cypress-version');
|
jest.mock('@nrwl/cypress/src/utils/cypress-version');
|
||||||
|
// nested code imports graph from the repo, which might have innacurate graph version
|
||||||
|
jest.mock('nx/src/project-graph/project-graph', () => ({
|
||||||
|
...jest.requireActual<any>('nx/src/project-graph/project-graph'),
|
||||||
|
readCachedProjectGraph: jest
|
||||||
|
.fn()
|
||||||
|
.mockImplementation(async () => projectGraph),
|
||||||
|
}));
|
||||||
|
|
||||||
describe('React:CypressComponentTestConfiguration', () => {
|
describe('React:CypressComponentTestConfiguration', () => {
|
||||||
let tree: Tree;
|
let tree: Tree;
|
||||||
let mockedAssertCypressVersion: jest.Mock<
|
let mockedAssertCypressVersion: jest.Mock<
|
||||||
|
|||||||
@ -9,6 +9,14 @@ import storybookConfigurationGenerator from './configuration';
|
|||||||
// need to mock cypress otherwise it'll use the nx installed version from package.json
|
// need to mock cypress otherwise it'll use the nx installed version from package.json
|
||||||
// which is v9 while we are testing for the new v10 version
|
// which is v9 while we are testing for the new v10 version
|
||||||
jest.mock('@nrwl/cypress/src/utils/cypress-version');
|
jest.mock('@nrwl/cypress/src/utils/cypress-version');
|
||||||
|
// nested code imports graph from the repo, which might have innacurate graph version
|
||||||
|
jest.mock('nx/src/project-graph/project-graph', () => ({
|
||||||
|
...jest.requireActual<any>('nx/src/project-graph/project-graph'),
|
||||||
|
createProjectGraphAsync: jest
|
||||||
|
.fn()
|
||||||
|
.mockImplementation(async () => ({ nodes: {}, dependencies: {} })),
|
||||||
|
}));
|
||||||
|
|
||||||
describe('react:storybook-configuration', () => {
|
describe('react:storybook-configuration', () => {
|
||||||
let appTree;
|
let appTree;
|
||||||
let mockedInstalledCypressVersion: jest.Mock<
|
let mockedInstalledCypressVersion: jest.Mock<
|
||||||
|
|||||||
@ -10,6 +10,14 @@ import { createTreeWithEmptyWorkspace } from '@nrwl/devkit/testing';
|
|||||||
import configurationGenerator from './configuration';
|
import configurationGenerator from './configuration';
|
||||||
import * as workspaceConfiguration from './test-configs/root-workspace-configuration.json';
|
import * as workspaceConfiguration from './test-configs/root-workspace-configuration.json';
|
||||||
|
|
||||||
|
// nested code imports graph from the repo, which might have innacurate graph version
|
||||||
|
jest.mock('nx/src/project-graph/project-graph', () => ({
|
||||||
|
...jest.requireActual<any>('nx/src/project-graph/project-graph'),
|
||||||
|
createProjectGraphAsync: jest
|
||||||
|
.fn()
|
||||||
|
.mockImplementation(async () => ({ nodes: {}, dependencies: {} })),
|
||||||
|
}));
|
||||||
|
|
||||||
describe('@nrwl/storybook:configuration for workspaces with Root project', () => {
|
describe('@nrwl/storybook:configuration for workspaces with Root project', () => {
|
||||||
describe('basic functionalities', () => {
|
describe('basic functionalities', () => {
|
||||||
let tree: Tree;
|
let tree: Tree;
|
||||||
|
|||||||
@ -18,6 +18,14 @@ import { storybook7Version } from '../../utils/versions';
|
|||||||
import configurationGenerator from './configuration';
|
import configurationGenerator from './configuration';
|
||||||
import * as variousProjects from './test-configs/various-projects.json';
|
import * as variousProjects from './test-configs/various-projects.json';
|
||||||
|
|
||||||
|
// nested code imports graph from the repo, which might have innacurate graph version
|
||||||
|
jest.mock('nx/src/project-graph/project-graph', () => ({
|
||||||
|
...jest.requireActual<any>('nx/src/project-graph/project-graph'),
|
||||||
|
createProjectGraphAsync: jest
|
||||||
|
.fn()
|
||||||
|
.mockImplementation(async () => ({ nodes: {}, dependencies: {} })),
|
||||||
|
}));
|
||||||
|
|
||||||
describe('@nrwl/storybook:configuration for Storybook v7', () => {
|
describe('@nrwl/storybook:configuration for Storybook v7', () => {
|
||||||
describe('basic functionalities', () => {
|
describe('basic functionalities', () => {
|
||||||
let tree: Tree;
|
let tree: Tree;
|
||||||
|
|||||||
@ -15,6 +15,14 @@ import { TsConfig } from '../../utils/utilities';
|
|||||||
import configurationGenerator from './configuration';
|
import configurationGenerator from './configuration';
|
||||||
import * as workspaceConfiguration from './test-configs/workspace-conifiguration.json';
|
import * as workspaceConfiguration from './test-configs/workspace-conifiguration.json';
|
||||||
|
|
||||||
|
// nested code imports graph from the repo, which might have innacurate graph version
|
||||||
|
jest.mock('nx/src/project-graph/project-graph', () => ({
|
||||||
|
...jest.requireActual<any>('nx/src/project-graph/project-graph'),
|
||||||
|
createProjectGraphAsync: jest
|
||||||
|
.fn()
|
||||||
|
.mockImplementation(async () => ({ nodes: {}, dependencies: {} })),
|
||||||
|
}));
|
||||||
|
|
||||||
describe('@nrwl/storybook:configuration', () => {
|
describe('@nrwl/storybook:configuration', () => {
|
||||||
describe('basic functionalities', () => {
|
describe('basic functionalities', () => {
|
||||||
let tree: Tree;
|
let tree: Tree;
|
||||||
|
|||||||
@ -14,6 +14,14 @@ import {
|
|||||||
} from '../../../utils/testing';
|
} from '../../../utils/testing';
|
||||||
import { migrateDefaultsGenerator } from './migrate-defaults-5-to-6';
|
import { migrateDefaultsGenerator } from './migrate-defaults-5-to-6';
|
||||||
|
|
||||||
|
// nested code imports graph from the repo, which might have innacurate graph version
|
||||||
|
jest.mock('nx/src/project-graph/project-graph', () => ({
|
||||||
|
...jest.requireActual<any>('nx/src/project-graph/project-graph'),
|
||||||
|
createProjectGraphAsync: jest
|
||||||
|
.fn()
|
||||||
|
.mockImplementation(async () => ({ nodes: {}, dependencies: {} })),
|
||||||
|
}));
|
||||||
|
|
||||||
describe('migrate-defaults-5-to-6 Generator', () => {
|
describe('migrate-defaults-5-to-6 Generator', () => {
|
||||||
let appTree: Tree;
|
let appTree: Tree;
|
||||||
|
|
||||||
|
|||||||
@ -17,6 +17,14 @@ import {
|
|||||||
} from '@nrwl/devkit/ngcli-adapter';
|
} from '@nrwl/devkit/ngcli-adapter';
|
||||||
import { getTsSourceFile } from '@nrwl/storybook/src/utils/utilities';
|
import { getTsSourceFile } from '@nrwl/storybook/src/utils/utilities';
|
||||||
|
|
||||||
|
// nested code imports graph from the repo, which might have innacurate graph version
|
||||||
|
jest.mock('nx/src/project-graph/project-graph', () => ({
|
||||||
|
...jest.requireActual<any>('nx/src/project-graph/project-graph'),
|
||||||
|
createProjectGraphAsync: jest
|
||||||
|
.fn()
|
||||||
|
.mockImplementation(async () => ({ nodes: {}, dependencies: {} })),
|
||||||
|
}));
|
||||||
|
|
||||||
const componentSchematic = wrapAngularDevkitSchematic(
|
const componentSchematic = wrapAngularDevkitSchematic(
|
||||||
'@schematics/angular',
|
'@schematics/angular',
|
||||||
'component'
|
'component'
|
||||||
|
|||||||
@ -11,6 +11,14 @@ import {
|
|||||||
import { nxVersion, storybookVersion } from './versions';
|
import { nxVersion, storybookVersion } from './versions';
|
||||||
import * as targetVariations from './test-configs/different-target-variations.json';
|
import * as targetVariations from './test-configs/different-target-variations.json';
|
||||||
|
|
||||||
|
// nested code imports graph from the repo, which might have innacurate graph version
|
||||||
|
jest.mock('nx/src/project-graph/project-graph', () => ({
|
||||||
|
...jest.requireActual<any>('nx/src/project-graph/project-graph'),
|
||||||
|
createProjectGraphAsync: jest
|
||||||
|
.fn()
|
||||||
|
.mockImplementation(async () => ({ nodes: {}, dependencies: {} })),
|
||||||
|
}));
|
||||||
|
|
||||||
const componentSchematic = wrapAngularDevkitSchematic(
|
const componentSchematic = wrapAngularDevkitSchematic(
|
||||||
'@schematics/angular',
|
'@schematics/angular',
|
||||||
'component'
|
'component'
|
||||||
@ -66,14 +74,14 @@ describe('testing utilities', () => {
|
|||||||
`
|
`
|
||||||
import { Story, Meta } from '@storybook/react';
|
import { Story, Meta } from '@storybook/react';
|
||||||
import { Button } from './button';
|
import { Button } from './button';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
component: Button,
|
component: Button,
|
||||||
title: 'Button',
|
title: 'Button',
|
||||||
} as Meta;
|
} as Meta;
|
||||||
|
|
||||||
const Template: Story = (args) => <Button {...args} />;
|
const Template: Story = (args) => <Button {...args} />;
|
||||||
|
|
||||||
export const Primary = Template.bind({});
|
export const Primary = Template.bind({});
|
||||||
Primary.args = {};
|
Primary.args = {};
|
||||||
`
|
`
|
||||||
@ -83,7 +91,7 @@ describe('testing utilities', () => {
|
|||||||
`test-ui-lib/src/lib/button/button.component.other.ts`,
|
`test-ui-lib/src/lib/button/button.component.other.ts`,
|
||||||
`
|
`
|
||||||
import { Button } from './button';
|
import { Button } from './button';
|
||||||
|
|
||||||
// test test
|
// test test
|
||||||
`
|
`
|
||||||
);
|
);
|
||||||
@ -92,7 +100,7 @@ describe('testing utilities', () => {
|
|||||||
`test-ui-lib/src/lib/button/button.component.react-native.ts`,
|
`test-ui-lib/src/lib/button/button.component.react-native.ts`,
|
||||||
`
|
`
|
||||||
import { storiesOf } from '@storybook/react-native';
|
import { storiesOf } from '@storybook/react-native';
|
||||||
|
|
||||||
// test test
|
// test test
|
||||||
`
|
`
|
||||||
);
|
);
|
||||||
@ -101,7 +109,7 @@ describe('testing utilities', () => {
|
|||||||
`test-ui-lib/src/lib/button/button.component.new-syntax.ts`,
|
`test-ui-lib/src/lib/button/button.component.new-syntax.ts`,
|
||||||
`
|
`
|
||||||
import { ComponentStory } from '@storybook/react';
|
import { ComponentStory } from '@storybook/react';
|
||||||
|
|
||||||
// test test
|
// test test
|
||||||
`
|
`
|
||||||
);
|
);
|
||||||
|
|||||||
@ -3,6 +3,14 @@ import { createTreeWithEmptyWorkspace } from '@nrwl/devkit/testing';
|
|||||||
import { Schema } from '../schema';
|
import { Schema } from '../schema';
|
||||||
import { checkTargets } from './check-targets';
|
import { checkTargets } from './check-targets';
|
||||||
|
|
||||||
|
// nested code imports graph from the repo, which might have innacurate graph version
|
||||||
|
jest.mock('nx/src/project-graph/project-graph', () => ({
|
||||||
|
...jest.requireActual<any>('nx/src/project-graph/project-graph'),
|
||||||
|
createProjectGraphAsync: jest
|
||||||
|
.fn()
|
||||||
|
.mockImplementation(async () => ({ nodes: {}, dependencies: {} })),
|
||||||
|
}));
|
||||||
|
|
||||||
describe('checkTargets', () => {
|
describe('checkTargets', () => {
|
||||||
let tree: Tree;
|
let tree: Tree;
|
||||||
let schema: Schema;
|
let schema: Schema;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user