parent
be8ce09ddb
commit
31bb2f3626
@ -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',
|
||||
},
|
||||
|
||||
@ -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,
|
||||
},
|
||||
];
|
||||
|
||||
|
||||
@ -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,
|
||||
];
|
||||
|
||||
@ -185,7 +185,7 @@
|
||||
{
|
||||
"source": "shared-product-data",
|
||||
"target": "shared-product-types",
|
||||
"type": "static"
|
||||
"type": "typeOnly"
|
||||
}
|
||||
],
|
||||
"products-home-page": [
|
||||
|
||||
@ -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`
|
||||
|
||||
@ -273,7 +273,7 @@ describe('dep-graph', () => {
|
||||
target: mylib,
|
||||
type: 'static',
|
||||
},
|
||||
{ source: myapp, target: mylib2, type: 'static' },
|
||||
{ source: myapp, target: mylib2, type: 'dynamic' },
|
||||
],
|
||||
[myappE2e]: [
|
||||
{
|
||||
|
||||
@ -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`
|
||||
|
||||
@ -144,6 +144,7 @@ export type {
|
||||
ProjectFileMap,
|
||||
FileData,
|
||||
ProjectGraph,
|
||||
ProjectGraphBuilderExplicitDependency,
|
||||
ProjectGraphDependency,
|
||||
ProjectGraphNode,
|
||||
ProjectGraphProjectNode,
|
||||
|
||||
@ -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: [],
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -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(
|
||||
|
||||
@ -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,
|
||||
},
|
||||
|
||||
@ -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',
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
@ -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
|
||||
*/
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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 ||
|
||||
|
||||
@ -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,
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
@ -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,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
@ -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,
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
@ -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,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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',
|
||||
},
|
||||
|
||||
@ -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() });
|
||||
}
|
||||
|
||||
@ -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(
|
||||
|
||||
@ -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',
|
||||
},
|
||||
|
||||
@ -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)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@ -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 };
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user