feat(core): show dep types in dep graph (#2760) (#8132)

This commit is contained in:
MaximSagan 2022-01-19 04:20:09 +11:00 committed by GitHub
parent be8ce09ddb
commit 31bb2f3626
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
27 changed files with 342 additions and 113 deletions

View File

@ -1,5 +1,9 @@
// nx-ignore-next-line
import type { ProjectGraphDependency, ProjectGraphNode } from '@nrwl/devkit';
import {
DependencyType,
ProjectGraphDependency,
ProjectGraphNode,
} from '@nrwl/devkit';
import { depGraphMachine } from './dep-graph.machine';
import { interpret } from 'xstate';
@ -51,38 +55,38 @@ export const mockProjects: ProjectGraphNode[] = [
export const mockDependencies: Record<string, ProjectGraphDependency[]> = {
app1: [
{
type: 'static',
type: DependencyType.static,
source: 'app1',
target: 'auth-lib',
},
{
type: 'static',
type: DependencyType.static,
source: 'app1',
target: 'feature-lib1',
},
],
app2: [
{
type: 'static',
type: DependencyType.static,
source: 'app2',
target: 'auth-lib',
},
{
type: 'static',
type: DependencyType.static,
source: 'app2',
target: 'feature-lib2',
},
],
'feature-lib1': [
{
type: 'static',
type: DependencyType.static,
source: 'feature-lib1',
target: 'ui-lib',
},
],
'feature-lib2': [
{
type: 'static',
type: DependencyType.static,
source: 'feature-lib2',
target: 'ui-lib',
},

View File

@ -33,7 +33,7 @@ export class MockProjectGraphService implements ProjectGraphService {
{
source: 'existing-app-1',
target: 'existing-lib-1',
type: 'statis',
type: 'static' as any,
},
],
'existing-lib-1': [],
@ -82,7 +82,7 @@ export class MockProjectGraphService implements ProjectGraphService {
{
source: newProject.name,
target: targetDependency.name,
type: 'static',
type: 'static' as any,
},
];

View File

@ -41,9 +41,22 @@ const dynamicEdges: Stylesheet = {
},
};
const typeOnlyEdges: Stylesheet = {
selector: 'edge.typeOnly',
style: {
label: 'type only',
'font-size': '16px',
'edge-text-rotation': 'autorotate',
'curve-style': 'unbundled-bezier',
'line-dash-pattern': [5, 5],
'line-style': 'dashed',
},
};
export const edgeStyles: Stylesheet[] = [
allEdges,
affectedEdges,
implicitEdges,
dynamicEdges,
typeOnlyEdges,
];

View File

@ -185,7 +185,7 @@
{
"source": "shared-product-data",
"target": "shared-product-types",
"type": "static"
"type": "typeOnly"
}
],
"products-home-page": [

View File

@ -36,6 +36,7 @@ It only uses language primitives and immutable objects
- [FileData](../../generated/nx-devkit/index#filedata)
- [ProjectFileMap](../../generated/nx-devkit/index#projectfilemap)
- [ProjectGraph](../../generated/nx-devkit/index#projectgraph)
- [ProjectGraphBuilderExplicitDependency](../../generated/nx-devkit/index#projectgraphbuilderexplicitdependency)
- [ProjectGraphDependency](../../generated/nx-devkit/index#projectgraphdependency)
- [ProjectGraphExternalNode](../../generated/nx-devkit/index#projectgraphexternalnode)
- [ProjectGraphProcessorContext](../../generated/nx-devkit/index#projectgraphprocessorcontext)
@ -210,6 +211,12 @@ A plugin for Nx
---
### ProjectGraphBuilderExplicitDependency
**ProjectGraphBuilderExplicitDependency**: `Object`
---
### ProjectGraphDependency
**ProjectGraphDependency**: `Object`

View File

@ -273,7 +273,7 @@ describe('dep-graph', () => {
target: mylib,
type: 'static',
},
{ source: myapp, target: mylib2, type: 'static' },
{ source: myapp, target: mylib2, type: 'dynamic' },
],
[myappE2e]: [
{

View File

@ -36,6 +36,7 @@ It only uses language primitives and immutable objects
- [FileData](../../generated/nx-devkit/index#filedata)
- [ProjectFileMap](../../generated/nx-devkit/index#projectfilemap)
- [ProjectGraph](../../generated/nx-devkit/index#projectgraph)
- [ProjectGraphBuilderExplicitDependency](../../generated/nx-devkit/index#projectgraphbuilderexplicitdependency)
- [ProjectGraphDependency](../../generated/nx-devkit/index#projectgraphdependency)
- [ProjectGraphExternalNode](../../generated/nx-devkit/index#projectgraphexternalnode)
- [ProjectGraphProcessorContext](../../generated/nx-devkit/index#projectgraphprocessorcontext)
@ -210,6 +211,12 @@ A plugin for Nx
---
### ProjectGraphBuilderExplicitDependency
**ProjectGraphBuilderExplicitDependency**: `Object`
---
### ProjectGraphDependency
**ProjectGraphDependency**: `Object`

View File

@ -144,6 +144,7 @@ export type {
ProjectFileMap,
FileData,
ProjectGraph,
ProjectGraphBuilderExplicitDependency,
ProjectGraphDependency,
ProjectGraphNode,
ProjectGraphProjectNode,

View File

@ -1,3 +1,4 @@
import { DependencyType } from './interfaces';
import { ProjectGraphBuilder } from './project-graph-builder';
describe('ProjectGraphBuilder', () => {
@ -96,21 +97,77 @@ describe('ProjectGraphBuilder', () => {
});
});
it(`should use implicit dep when both implicit and explicit deps are available`, () => {
// don't include duplicates
builder.addImplicitDependency('source', 'target');
builder.addExplicitDependency('source', 'source/index.ts', 'target');
describe('dependency type priority', () => {
it(`should use implicit dep when both implicit and explicit deps are available`, () => {
// don't include duplicates
builder.addImplicitDependency('source', 'target');
builder.addExplicitDependency('source', 'source/index.ts', 'target');
const graph = builder.getUpdatedProjectGraph();
expect(graph.dependencies).toEqual({
source: [
{
source: 'source',
target: 'target',
type: 'implicit',
},
],
target: [],
const graph = builder.getUpdatedProjectGraph();
expect(graph.dependencies).toEqual({
source: [
{
source: 'source',
target: 'target',
type: 'implicit',
},
],
target: [],
});
});
it(`should use explicit deps in priority order "static > dynamic"`, () => {
builder.addExplicitDependency(
'source',
'source/index.ts',
'target',
DependencyType.dynamic
);
builder.addExplicitDependency(
'source',
'source/index.ts',
'target',
DependencyType.static
);
const graph = builder.getUpdatedProjectGraph();
expect(graph.dependencies).toEqual({
source: [
{
source: 'source',
target: 'target',
type: DependencyType.static,
},
],
target: [],
});
});
it(`should use explicit deps in priority order "dynamic > type-only"`, () => {
builder.addExplicitDependency(
'source',
'source/index.ts',
'target',
DependencyType.dynamic
);
builder.addExplicitDependency(
'source',
'source/second.ts',
'target',
DependencyType.typeOnly
);
const graph = builder.getUpdatedProjectGraph();
expect(graph.dependencies).toEqual({
source: [
{
source: 'source',
target: 'target',
type: DependencyType.dynamic,
},
],
target: [],
});
});
});

View File

@ -1,4 +1,5 @@
import type {
FileData,
ProjectGraph,
ProjectGraphDependency,
ProjectGraphExternalNode,
@ -110,7 +111,8 @@ export class ProjectGraphBuilder {
addExplicitDependency(
sourceProjectName: string,
sourceProjectFile: string,
targetProjectName: string
targetProjectName: string,
dependencyType: DependencyType = DependencyType.static // TODO: Make this argument required
): void {
if (sourceProjectName === targetProjectName) {
return;
@ -127,9 +129,8 @@ export class ProjectGraphBuilder {
throw new Error(`Target project does not exist: ${targetProjectName}`);
}
const fileData = source.data.files.find(
(f) => f.file === sourceProjectFile
);
const files = source.data.files as FileData[];
const fileData = files.find((f) => f.file === sourceProjectFile);
if (!fileData) {
throw new Error(
`Source project ${sourceProjectName} does not have a file: ${sourceProjectFile}`
@ -140,8 +141,19 @@ export class ProjectGraphBuilder {
fileData.deps = [];
}
if (!fileData.deps.find((t) => t === targetProjectName)) {
fileData.deps.push(targetProjectName);
const existingFileDep = fileData.deps.find(
(t) => t.projectName === targetProjectName
);
if (existingFileDep) {
existingFileDep.dependencyType = this.getHigherPriorityDepType(
existingFileDep.dependencyType,
dependencyType
);
} else {
fileData.deps.push({
projectName: targetProjectName,
dependencyType,
});
}
}
@ -153,54 +165,76 @@ export class ProjectGraphBuilder {
}
getUpdatedProjectGraph(): ProjectGraph {
const isRemoved = (sourceProject: string, targetProject: string) =>
this.removedEdges[sourceProject] &&
this.removedEdges[sourceProject].has(targetProject);
for (const sourceProject of Object.keys(this.graph.nodes)) {
const alreadySetTargetProjects =
this.calculateAlreadySetTargetDeps(sourceProject);
this.graph.dependencies[sourceProject] = [
...alreadySetTargetProjects.values(),
];
const sourceProjectDepMap = new Map<string, ProjectGraphDependency>(
this.graph.dependencies[sourceProject]
.map((dep) => [dep.target, dep] as const)
.filter(([targetProject]) => !isRemoved(sourceProject, targetProject))
);
const fileDeps = this.calculateTargetDepsFromFiles(sourceProject);
for (const targetProject of fileDeps) {
if (!alreadySetTargetProjects.has(targetProject)) {
if (
!this.removedEdges[sourceProject] ||
!this.removedEdges[sourceProject].has(targetProject)
) {
this.graph.dependencies[sourceProject].push({
source: sourceProject,
target: targetProject,
type: DependencyType.static,
});
}
for (const [targetProject, targetProjectDepType] of fileDeps) {
if (sourceProjectDepMap.has(targetProject)) {
const existingDep = sourceProjectDepMap.get(targetProject);
existingDep.type = this.getHigherPriorityDepType(
existingDep.type,
targetProjectDepType
);
} else if (!isRemoved(sourceProject, targetProject)) {
sourceProjectDepMap.set(targetProject, {
source: sourceProject,
target: targetProject,
type: targetProjectDepType,
});
}
}
this.graph.dependencies[sourceProject] = [
...sourceProjectDepMap.values(),
];
}
return this.graph;
}
private calculateTargetDepsFromFiles(sourceProject: string) {
const fileDeps = new Set<string>();
const files = this.graph.nodes[sourceProject].data.files;
const fileDeps = new Map<string, DependencyType>();
const files: FileData[] = this.graph.nodes[sourceProject].data.files;
if (!files) return fileDeps;
for (let f of files) {
if (f.deps) {
for (let p of f.deps) {
fileDeps.add(p);
if (fileDeps.has(p.projectName)) {
const existingDepType = fileDeps.get(p.projectName);
const priorityDepType = this.getHigherPriorityDepType(
p.dependencyType,
existingDepType
);
fileDeps.set(p.projectName, priorityDepType);
} else {
fileDeps.set(p.projectName, p.dependencyType);
}
}
}
}
return fileDeps;
}
private calculateAlreadySetTargetDeps(sourceProject: string) {
const alreadySetTargetProjects = new Map<string, ProjectGraphDependency>();
const removed = this.removedEdges[sourceProject];
for (const d of this.graph.dependencies[sourceProject]) {
if (!removed || !removed.has(d.target)) {
alreadySetTargetProjects.set(d.target, d);
private getHigherPriorityDepType(
depTypeA: DependencyType,
depTypeB: DependencyType
): DependencyType {
for (const priorityDepType of [
DependencyType.implicit,
DependencyType.static,
DependencyType.dynamic,
DependencyType.typeOnly,
]) {
if (depTypeA === priorityDepType || depTypeB === priorityDepType) {
return priorityDepType;
}
}
return alreadySetTargetProjects;
}
}

View File

@ -1661,7 +1661,16 @@ linter.defineParser('@typescript-eslint/parser', parser);
linter.defineRule(enforceModuleBoundariesRuleName, enforceModuleBoundaries);
function createFile(f: string, deps?: string[]): FileData {
return { file: f, hash: '', ...(deps && { deps }) };
return {
file: f,
hash: '',
...(deps && {
deps: deps.map((dep) => ({
projectName: dep,
dependencyType: DependencyType.static,
})),
}),
};
}
function runRule(

View File

@ -1,4 +1,4 @@
import { ExecutorContext } from '@nrwl/devkit';
import { DependencyType, ExecutorContext } from '@nrwl/devkit';
import { join } from 'path';
import { mocked } from 'ts-jest/utils';
@ -309,7 +309,7 @@ describe('NodePackageBuilder', () => {
dependencies: {
nodelib: [
{
type: ProjectType.lib,
type: DependencyType.static,
target: 'nodelib-child',
source: null,
},

View File

@ -1,5 +1,5 @@
import { findAllNpmDependencies } from './find-all-npm-dependencies';
import { ProjectGraph } from '@nrwl/devkit';
import { DependencyType, ProjectGraph } from '@nrwl/devkit';
test('findAllNpmDependencies', () => {
const graph: ProjectGraph = {
@ -61,26 +61,34 @@ test('findAllNpmDependencies', () => {
},
dependencies: {
myapp: [
{ type: 'static', source: 'myapp', target: 'lib1' },
{ type: 'static', source: 'myapp', target: 'lib2' },
{ type: DependencyType.static, source: 'myapp', target: 'lib1' },
{ type: DependencyType.static, source: 'myapp', target: 'lib2' },
{
type: 'static',
type: DependencyType.static,
source: 'myapp',
target: 'npm:react-native-image-picker',
},
{
type: 'static',
type: DependencyType.static,
source: 'myapp',
target: 'npm:@nrwl/react-native',
},
],
lib1: [
{ type: 'static', source: 'lib1', target: 'lib2' },
{ type: 'static', source: 'lib3', target: 'npm:react-native-snackbar' },
{ type: DependencyType.static, source: 'lib1', target: 'lib2' },
{
type: DependencyType.static,
source: 'lib3',
target: 'npm:react-native-snackbar',
},
],
lib2: [{ type: 'static', source: 'lib2', target: 'lib3' }],
lib2: [{ type: DependencyType.static, source: 'lib2', target: 'lib3' }],
lib3: [
{ type: 'static', source: 'lib3', target: 'npm:react-native-dialog' },
{
type: DependencyType.static,
source: 'lib3',
target: 'npm:react-native-dialog',
},
],
},
};

View File

@ -11,7 +11,19 @@ export interface FileData {
hash: string;
/** @deprecated this field will be removed in v13. Use {@link path.extname} to parse extension */
ext?: string;
deps?: string[];
deps?: FileDependency[];
}
export interface FileDependency {
projectName: string;
dependencyType: DependencyType;
}
export interface ProjectGraphBuilderExplicitDependency {
sourceProjectName: string;
targetProjectName: string;
sourceProjectFile: string;
dependencyType: DependencyType;
}
/**
@ -49,6 +61,10 @@ export enum DependencyType {
* Implicit dependencies are inferred
*/
implicit = 'implicit',
/**
* Type-only dependencies are those of the form `import type ...`
*/
typeOnly = 'typeOnly',
}
/**
@ -108,7 +124,7 @@ export interface ProjectGraphExternalNode {
* A dependency between two projects
*/
export interface ProjectGraphDependency {
type: DependencyType | string;
type: DependencyType;
/**
* The project being imported by the other
*/

View File

@ -246,7 +246,9 @@ describe('Hasher', () => {
},
},
dependencies: {
parent: [{ source: 'parent', target: 'child', type: 'static' }],
parent: [
{ source: 'parent', target: 'child', type: DependencyType.static },
],
},
},
{} as any,
@ -343,8 +345,12 @@ describe('Hasher', () => {
},
},
dependencies: {
parent: [{ source: 'parent', target: 'child', type: 'static' }],
child: [{ source: 'child', target: 'parent', type: 'static' }],
parent: [
{ source: 'parent', target: 'child', type: DependencyType.static },
],
child: [
{ source: 'child', target: 'parent', type: DependencyType.static },
],
},
},
{} as any,

View File

@ -1,4 +1,9 @@
import { ProjectFileMap, ProjectGraph, Workspace } from '@nrwl/devkit';
import {
ProjectFileMap,
ProjectGraph,
ProjectGraphBuilderExplicitDependency,
Workspace,
} from '@nrwl/devkit';
import { buildExplicitTypeScriptDependencies } from './explicit-project-dependencies';
import { buildExplicitPackageJsonDependencies } from './explicit-package-json-dependencies';
@ -10,7 +15,7 @@ export function buildExplicitTypescriptAndPackageJsonDependencies(
workspace: Workspace,
projectGraph: ProjectGraph,
filesToProcess: ProjectFileMap
) {
): ProjectGraphBuilderExplicitDependency[] {
let res = [];
if (
jsPluginConfig.analyzeSourceFiles === undefined ||

View File

@ -2,6 +2,7 @@ import { buildExplicitPackageJsonDependencies } from './explicit-package-json-de
import { vol } from 'memfs';
import { ProjectGraphNode } from '../project-graph-models';
import {
DependencyType,
ProjectGraphBuilder,
ProjectGraphProcessorContext,
} from '@nrwl/devkit';
@ -125,16 +126,19 @@ describe('explicit package json dependencies', () => {
sourceProjectName: 'proj',
targetProjectName: 'proj2',
sourceProjectFile: 'libs/proj/package.json',
dependencyType: DependencyType.static,
},
{
sourceProjectFile: 'libs/proj/package.json',
sourceProjectName: 'proj',
targetProjectName: 'npm:external',
dependencyType: DependencyType.static,
},
{
sourceProjectName: 'proj',
targetProjectName: 'proj3',
sourceProjectFile: 'libs/proj/package.json',
dependencyType: DependencyType.static,
},
]);
});

View File

@ -1,9 +1,11 @@
import { ProjectGraph, ProjectGraphNodeRecords } from '../project-graph-models';
import { defaultFileRead } from '../../file-utils';
import {
DependencyType,
joinPathFragments,
parseJson,
ProjectFileMap,
ProjectGraphBuilderExplicitDependency,
Workspace,
} from '@nrwl/devkit';
import { join } from 'path';
@ -12,8 +14,8 @@ export function buildExplicitPackageJsonDependencies(
workspace: Workspace,
graph: ProjectGraph,
filesToProcess: ProjectFileMap
) {
const res = [] as any;
): ProjectGraphBuilderExplicitDependency[] {
const res: ProjectGraphBuilderExplicitDependency[] = [];
let packageNameMap = undefined;
Object.keys(filesToProcess).forEach((source) => {
Object.values(filesToProcess[source]).forEach((f) => {
@ -55,7 +57,7 @@ function processPackageJson(
sourceProject: string,
fileName: string,
graph: ProjectGraph,
collectedDeps: any[],
collectedDeps: ProjectGraphBuilderExplicitDependency[],
packageNameMap: { [packageName: string]: string }
) {
try {
@ -68,12 +70,14 @@ function processPackageJson(
sourceProjectName: sourceProject,
targetProjectName: packageNameMap[d],
sourceProjectFile: fileName,
dependencyType: DependencyType.static,
});
} else if (graph.externalNodes[`npm:${d}`]) {
collectedDeps.push({
sourceProjectName: sourceProject,
targetProjectName: `npm:${d}`,
sourceProjectFile: fileName,
dependencyType: DependencyType.static,
});
}
});

View File

@ -9,6 +9,7 @@ import { vol } from 'memfs';
import { ProjectGraphNode } from '../project-graph-models';
import { buildExplicitTypeScriptDependencies } from './explicit-project-dependencies';
import {
DependencyType,
ProjectGraphBuilder,
ProjectGraphProcessorContext,
} from '@nrwl/devkit';
@ -201,21 +202,25 @@ describe('explicit project dependencies', () => {
sourceProjectFile: 'libs/proj1234/index.ts',
sourceProjectName: 'proj1234',
targetProjectName: 'proj1234-child',
dependencyType: DependencyType.static,
},
{
sourceProjectFile: 'libs/proj/index.ts',
sourceProjectName: 'proj',
targetProjectName: 'proj2',
dependencyType: DependencyType.static,
},
{
sourceProjectFile: 'libs/proj/index.ts',
sourceProjectName: 'proj',
targetProjectName: 'proj3a',
dependencyType: DependencyType.dynamic,
},
{
sourceProjectFile: 'libs/proj/index.ts',
sourceProjectName: 'proj',
targetProjectName: 'proj4ab',
dependencyType: DependencyType.dynamic,
},
]);
});

View File

@ -3,8 +3,7 @@ import { TypeScriptImportLocator } from './typescript-import-locator';
import { TargetProjectLocator } from '../../target-project-locator';
import {
ProjectFileMap,
ProjectGraphBuilder,
ProjectGraphProcessorContext,
ProjectGraphBuilderExplicitDependency,
Workspace,
} from '@nrwl/devkit';
@ -12,13 +11,13 @@ export function buildExplicitTypeScriptDependencies(
workspace: Workspace,
graph: ProjectGraph,
filesToProcess: ProjectFileMap
) {
): ProjectGraphBuilderExplicitDependency[] {
const importLocator = new TypeScriptImportLocator();
const targetProjectLocator = new TargetProjectLocator(
graph.nodes,
graph.externalNodes
);
const res = [] as any;
const res: ProjectGraphBuilderExplicitDependency[] = [];
Object.keys(filesToProcess).forEach((source) => {
Object.values(filesToProcess[source]).forEach((f) => {
importLocator.fromFile(
@ -34,6 +33,7 @@ export function buildExplicitTypeScriptDependencies(
sourceProjectName: source,
targetProjectName: target,
sourceProjectFile: f.file,
dependencyType: type,
});
}
}

View File

@ -4,7 +4,7 @@ import { DependencyType } from '@nrwl/devkit';
import { stripSourceCode } from '../../../utilities/strip-source-code';
import { defaultFileRead } from '../../file-utils';
let tsModule: any;
let tsModule: typeof ts;
export class TypeScriptImportLocator {
private readonly scanner: ts.Scanner;
@ -46,7 +46,7 @@ export class TypeScriptImportLocator {
fromNode(
filePath: string,
node: any,
node: ts.Node,
visitor: (
importExpr: string,
filePath: string,
@ -59,7 +59,15 @@ export class TypeScriptImportLocator {
) {
if (!this.ignoreStatement(node)) {
const imp = this.getStringLiteralValue(node.moduleSpecifier);
visitor(imp, filePath, DependencyType.static);
const isTypeOnly =
(tsModule.isImportDeclaration(node) &&
node.importClause?.isTypeOnly) ||
(tsModule.isExportDeclaration(node) && node.isTypeOnly);
visitor(
imp,
filePath,
isTypeOnly ? DependencyType.typeOnly : DependencyType.static
);
}
return; // stop traversing downwards
}

View File

@ -72,6 +72,12 @@ describe('project graph', () => {
projectType: 'library',
targets: {},
},
'types-lib': {
root: 'libs/types-lib',
sourceRoot: 'libs/types-lib',
projectType: 'library',
targets: {},
},
api: {
root: 'apps/api/',
sourceRoot: 'apps/api/src',
@ -98,6 +104,7 @@ describe('project graph', () => {
'@nrwl/shared-util-data': ['libs/shared/util/data/src/index.ts'],
'@nrwl/ui': ['libs/ui/src/index.ts'],
'@nrwl/lazy-lib': ['libs/lazy-lib/src/index.ts'],
'@nrwl/types-lib': ['libs/types-lib/src/index.ts'],
},
},
};
@ -108,6 +115,7 @@ describe('project graph', () => {
'./apps/demo/src/index.ts': stripIndents`
import * as ui from '@nrwl/ui';
import * as data from '@nrwl/shared-util-data;
import type { MyType } from '@nrwl/types-lib;
const s = { loadChildren: '@nrwl/lazy-lib#LAZY' }
`,
'./apps/demo-e2e/src/integration/app.spec.ts': stripIndents`
@ -126,6 +134,9 @@ describe('project graph', () => {
'./libs/lazy-lib/src/index.ts': stripIndents`
export const LAZY = 'lazy lib';
`,
'./libs/types-lib/src/index.ts': stripIndents`
export type MyType = {};
`,
'./package.json': JSON.stringify(packageJson),
'./nx.json': JSON.stringify(nxJson),
'./workspace.json': JSON.stringify(workspaceJson),
@ -142,7 +153,9 @@ describe('project graph', () => {
fail('Invalid tsconfigs should cause project graph to throw error');
} catch (e) {
expect(e.message).toMatchInlineSnapshot(
`"InvalidSymbol in /root/tsconfig.base.json at position 247"`
`"InvalidSymbol in /root/tsconfig.base.json at position ${
JSON.stringify(tsConfigJson).length
}"`
);
}
});
@ -176,23 +189,35 @@ describe('project graph', () => {
},
});
expect(graph.dependencies).toEqual({
api: [{ source: 'api', target: 'npm:express', type: 'static' }],
api: [
{ source: 'api', target: 'npm:express', type: DependencyType.static },
],
demo: [
{ source: 'demo', target: 'api', type: 'implicit' },
{ source: 'demo', target: 'api', type: DependencyType.implicit },
{
source: 'demo',
target: 'ui',
type: 'static',
type: DependencyType.static,
},
{
source: 'demo',
target: 'shared-util-data',
type: DependencyType.static,
},
{
source: 'demo',
target: 'types-lib',
type: DependencyType.typeOnly,
},
{ source: 'demo', target: 'shared-util-data', type: 'static' },
{
source: 'demo',
target: 'lazy-lib',
type: 'static',
type: DependencyType.dynamic,
},
],
'demo-e2e': [],
'lazy-lib': [],
'types-lib': [],
'shared-util': [
{ source: 'shared-util', target: 'npm:happy-nrwl', type: 'static' },
],
@ -202,7 +227,7 @@ describe('project graph', () => {
{
source: 'ui',
target: 'lazy-lib',
type: 'static',
type: 'dynamic',
},
],
});
@ -244,7 +269,7 @@ describe('project graph', () => {
target: 'shared-util',
},
{
type: DependencyType.static,
type: DependencyType.dynamic,
source: 'ui',
target: 'lazy-lib',
},

View File

@ -8,6 +8,7 @@ import {
ProjectFileMap,
ProjectGraph,
ProjectGraphBuilder,
ProjectGraphBuilderExplicitDependency,
ProjectGraphProcessorContext,
readJsonFile,
WorkspaceJsonConfiguration,
@ -280,7 +281,8 @@ function buildExplicitDependenciesWithoutWorkers(
builder.addExplicitDependency(
r.sourceProjectName,
r.sourceProjectFile,
r.targetProjectName
r.targetProjectName,
r.dependencyType
);
});
}
@ -306,13 +308,16 @@ function buildExplicitDependenciesUsingWorkers(
return new Promise((res, reject) => {
for (let w of workers) {
w.on('message', (explicitDependencies) => {
explicitDependencies.forEach((r) => {
builder.addExplicitDependency(
r.sourceProjectName,
r.sourceProjectFile,
r.targetProjectName
);
});
explicitDependencies.forEach(
(r: ProjectGraphBuilderExplicitDependency) => {
builder.addExplicitDependency(
r.sourceProjectName,
r.sourceProjectFile,
r.targetProjectName,
r.dependencyType
);
}
);
if (bins.length > 0) {
w.postMessage({ filesToProcess: bins.shift() });
}

View File

@ -1170,7 +1170,16 @@ Circular file chain:
});
function createFile(f: string, deps?: string[]): FileData {
return { file: f, hash: '', ...(deps && { deps }) };
return {
file: f,
hash: '',
...(deps && {
deps: deps.map((dep) => ({
projectName: dep,
dependencyType: DependencyType.static,
})),
}),
};
}
function runRule(

View File

@ -1,5 +1,5 @@
import { PackageJson } from '@nrwl/tao/src/shared/package-json';
import { ProjectGraph } from '../core/project-graph';
import { DependencyType, ProjectGraph } from '../core/project-graph';
import {
getProjectNameFromDirPath,
getSourceDirOfDependentProjects,
@ -52,17 +52,17 @@ describe('project graph utils', () => {
dependencies: {
'demo-app': [
{
type: 'static',
type: DependencyType.static,
source: 'demo-app',
target: 'ui',
},
{
type: 'static',
type: DependencyType.static,
source: 'demo-app',
target: 'npm:chalk',
},
{
type: 'static',
type: DependencyType.static,
source: 'demo-app',
target: 'core',
},

View File

@ -116,17 +116,19 @@ export function checkCircularPath(
export function findFilesInCircularPath(
circularPath: ProjectGraphNode[]
): Array<string[]> {
const filePathChain = [];
const filePathChain: string[][] = [];
for (let i = 0; i < circularPath.length - 1; i++) {
const next = circularPath[i + 1].name;
const files: FileData[] = circularPath[i].data.files;
const files: Record<string, FileData> = circularPath[i].data.files;
filePathChain.push(
Object.keys(files)
.filter(
(key) => files[key].deps && files[key].deps.indexOf(next) !== -1
(key) =>
files[key].deps &&
files[key].deps.some((dep) => dep.projectName === next)
)
.map((key) => files[key].file)
.map((key) => (files[key] as FileData).file)
);
}

View File

@ -254,7 +254,7 @@ export function mapProjectGraphFiles<T>(
projectGraph.nodes as Record<string, ProjectGraphProjectNode>
).forEach(([name, node]) => {
const files: Record<string, FileData> = {};
node.data.files.forEach(({ file, hash, deps }) => {
node.data.files.forEach(({ file, hash, deps }: FileData) => {
files[removeExt(file)] = { file, hash, ...(deps && { deps }) };
});
const data = { ...node.data, files };