diff --git a/packages/workspace/src/core/project-graph/build-dependencies/import-project-root.spec.ts b/packages/workspace/src/core/project-graph/build-dependencies/import-project-root.spec.ts new file mode 100644 index 0000000000..0866baa149 --- /dev/null +++ b/packages/workspace/src/core/project-graph/build-dependencies/import-project-root.spec.ts @@ -0,0 +1,143 @@ +import { buildImportsToProjectRoots } from './import-project-root'; +import { + AddProjectDependency, + DependencyType, + ProjectGraphContext, + ProjectGraphNode +} from '../../project-graph'; + +import { fs, vol } from 'memfs'; +import { join } from 'path'; + +describe('buildImportsToProjectRoots', () => { + let addDependency: AddProjectDependency; + let ctx: ProjectGraphContext; + let projects: Record; + let fileRead: (path: string) => string; + let fsJson; + beforeEach(() => { + addDependency = jest.fn(); + fileRead = p => fs.readFileSync(join('/root', p)).toString(); + const workspaceJson = { + projects: { + proj1: {} + } + }; + const nxJson = { + npmScope: 'proj', + projects: { + proj1: {} + } + }; + fsJson = { + 'workspace.json': JSON.stringify(workspaceJson), + 'nx.json': JSON.stringify(nxJson), + 'libs/proj1/index.ts': `import '@proj/proj2'; + import('@proj/proj3'); + const a = { loadChildren: '@proj/proj4#a' };`, + 'libs/proj2/index.ts': `export const a = 0;`, + 'libs/proj3/index.ts': `export const a = 0;`, + 'libs/proj4/index.ts': `export const a = 0;` + }; + vol.fromJSON(fsJson, '/root'); + + ctx = { + workspaceJson, + nxJson, + fileMap: { + proj1: [ + { + file: 'libs/proj1/index.ts', + mtime: 0, + ext: '.ts' + } + ], + proj2: [ + { + file: 'libs/proj2/index.ts', + mtime: 0, + ext: '.ts' + } + ], + proj3: [ + { + file: 'libs/proj3/index.ts', + mtime: 0, + ext: '.ts' + } + ], + proj4: [ + { + file: 'libs/proj4/index.ts', + mtime: 0, + ext: '.ts' + } + ] + } + }; + projects = { + proj1: { + name: 'proj1', + type: 'lib', + data: { + root: 'libs/proj1', + files: [] + } + }, + proj2: { + name: 'proj2', + type: 'lib', + data: { + root: 'libs/proj2', + files: [] + } + }, + proj3: { + name: 'proj3', + type: 'lib', + data: { + root: 'libs/proj3', + files: [] + } + }, + proj4: { + name: 'proj4', + type: 'lib', + data: { + root: 'libs/proj4', + files: [] + } + } + }; + }); + + it('should draw static dependencies from imports', () => { + buildImportsToProjectRoots(ctx, projects, addDependency, fileRead); + + expect(addDependency).toHaveBeenCalledWith( + DependencyType.static, + 'proj1', + 'proj2' + ); + }); + + it('should draw dynamic dependencies from import()', () => { + buildImportsToProjectRoots(ctx, projects, addDependency, fileRead); + + expect(addDependency).toHaveBeenCalledWith( + DependencyType.dynamic, + 'proj1', + 'proj3' + ); + }); + + it('should draw dynamic dependencies from loadChildren', () => { + buildImportsToProjectRoots(ctx, projects, addDependency, fileRead); + + expect(addDependency).toHaveBeenCalledWith( + DependencyType.dynamic, + 'proj1', + 'proj4' + ); + }); +}); diff --git a/packages/workspace/src/core/project-graph/build-dependencies/import-project-root.ts b/packages/workspace/src/core/project-graph/build-dependencies/import-project-root.ts new file mode 100644 index 0000000000..9b11d6cfac --- /dev/null +++ b/packages/workspace/src/core/project-graph/build-dependencies/import-project-root.ts @@ -0,0 +1,39 @@ +import { + AddProjectDependency, + DependencyType, + ProjectGraphContext, + ProjectGraphNode, + ProjectGraphNodeRecords +} from '../project-graph-models'; +import { TypeScriptImportLocator } from './typescript-import-locator'; +import { normalizedProjectRoot } from '../../file-utils'; + +export function buildImportsToProjectRoots( + ctx: ProjectGraphContext, + nodes: ProjectGraphNodeRecords, + addDependency: AddProjectDependency, + fileRead: (s: string) => string +) { + const importLocator = new TypeScriptImportLocator(fileRead); + + Object.keys(ctx.fileMap).forEach(source => { + Object.values(ctx.fileMap[source]).forEach(f => { + importLocator.fromFile( + f.file, + (importExpr: string, filePath: string, type: DependencyType) => { + const targetProjectName = Object.keys(nodes).find(projectName => { + const p = nodes[projectName]; + const normalizedRoot = normalizedProjectRoot(p); + const normalizedImportExpr = importExpr.split('#')[0]; + const projectImport = `@${ctx.nxJson.npmScope}/${normalizedRoot}`; + return normalizedImportExpr.startsWith(projectImport); + }); + + if (targetProjectName) { + addDependency(type, source, targetProjectName); + } + } + ); + }); + }); +} diff --git a/packages/workspace/src/core/project-graph/build-dependencies/index.ts b/packages/workspace/src/core/project-graph/build-dependencies/index.ts index 7bfc04761e..8602df93df 100644 --- a/packages/workspace/src/core/project-graph/build-dependencies/index.ts +++ b/packages/workspace/src/core/project-graph/build-dependencies/index.ts @@ -1,4 +1,5 @@ export * from './build-dependencies'; +export * from './import-project-root'; export * from './implicit-project-dependencies'; export * from './explicit-project-dependencies'; export * from './explicit-npm-dependencies'; diff --git a/packages/workspace/src/core/project-graph/project-graph.ts b/packages/workspace/src/core/project-graph/project-graph.ts index 0a1b439a7f..8b04d2a1ef 100644 --- a/packages/workspace/src/core/project-graph/project-graph.ts +++ b/packages/workspace/src/core/project-graph/project-graph.ts @@ -25,7 +25,8 @@ import { BuildDependencies, buildExplicitNpmDependencies, buildExplicitTypeScriptDependencies, - buildImplicitProjectDependencies + buildImplicitProjectDependencies, + buildImportsToProjectRoots } from './build-dependencies'; import { assertWorkspaceValidity } from '../assert-workspace-validity'; import { normalizeNxJson } from '../normalize-nx-json';