chore(core): reconcile functions to find project of a path (#13364)
This commit is contained in:
parent
0e54262ca2
commit
6d35fd4d85
@ -21,8 +21,7 @@ import {
|
||||
stripIndents,
|
||||
workspaceRoot,
|
||||
} from '@nrwl/devkit';
|
||||
import { createProjectFileMappings } from 'nx/src/utils/target-project-locator';
|
||||
import { lstatSync, mkdirSync, writeFileSync } from 'fs';
|
||||
import { existsSync, lstatSync, mkdirSync, writeFileSync } from 'fs';
|
||||
import { dirname, join, relative } from 'path';
|
||||
import type { BrowserBuilderSchema } from '../src/builders/webpack-browser/webpack-browser.impl';
|
||||
|
||||
@ -74,7 +73,7 @@ ${e.stack ? e.stack : e}`
|
||||
const buildTarget = getBuildableTarget(ctContext);
|
||||
|
||||
if (!buildTarget.project && !graph.nodes?.[buildTarget.project]?.data) {
|
||||
throw new Error(stripIndents`Unable to find project configuration for build target.
|
||||
throw new Error(stripIndents`Unable to find project configuration for build target.
|
||||
Project Name? ${buildTarget.project}
|
||||
Has project config? ${!!graph.nodes?.[buildTarget.project]?.data}`);
|
||||
}
|
||||
@ -279,16 +278,22 @@ function withSchemaDefaults(options: any): BrowserBuilderSchema {
|
||||
* this file should get cleaned up via the cypress executor
|
||||
*/
|
||||
function getTempStylesForTailwind(ctExecutorContext: ExecutorContext) {
|
||||
const mappedGraphFiles = createProjectFileMappings(
|
||||
ctExecutorContext.projectGraph.nodes
|
||||
);
|
||||
const ctProjectConfig = ctExecutorContext.projectGraph.nodes[
|
||||
ctExecutorContext.projectName
|
||||
].data as ProjectConfiguration;
|
||||
// angular only supports `tailwind.config.{js,cjs}`
|
||||
const ctProjectTailwindConfig = join(ctProjectConfig.root, 'tailwind.config');
|
||||
const isTailWindInCtProject = !!mappedGraphFiles[ctProjectTailwindConfig];
|
||||
const isTailWindInRoot = !!mappedGraphFiles['tailwind.config'];
|
||||
const ctProjectTailwindConfig = join(
|
||||
ctExecutorContext.root,
|
||||
ctProjectConfig.root,
|
||||
'tailwind.config'
|
||||
);
|
||||
const isTailWindInCtProject =
|
||||
existsSync(ctProjectTailwindConfig + '.js') ||
|
||||
existsSync(ctProjectTailwindConfig + '.cjs');
|
||||
const rootTailwindPath = join(ctExecutorContext.root, 'tailwind.config');
|
||||
const isTailWindInRoot =
|
||||
existsSync(rootTailwindPath + '.js') ||
|
||||
existsSync(rootTailwindPath + '.cjs');
|
||||
|
||||
if (isTailWindInRoot || isTailWindInCtProject) {
|
||||
const pathToStyle = getTempTailwindPath(ctExecutorContext);
|
||||
|
||||
@ -1,27 +1,25 @@
|
||||
import type { Tree } from '@nrwl/devkit';
|
||||
import {
|
||||
joinPathFragments,
|
||||
readCachedProjectGraph,
|
||||
readProjectConfiguration,
|
||||
readWorkspaceConfiguration,
|
||||
} from '@nrwl/devkit';
|
||||
import type { NormalizedSchema, Schema } from '../schema';
|
||||
import { getProjectNameFromDirPath } from 'nx/src/utils/project-graph-utils';
|
||||
|
||||
function getProjectFromPath(path: string) {
|
||||
try {
|
||||
return getProjectNameFromDirPath(path);
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
import {
|
||||
createProjectRootMappings,
|
||||
findProjectForPath,
|
||||
} from 'nx/src/project-graph/utils/find-project-for-path';
|
||||
|
||||
export function normalizeOptions(
|
||||
tree: Tree,
|
||||
options: Schema
|
||||
): NormalizedSchema {
|
||||
const projectGraph = readCachedProjectGraph();
|
||||
const projectRootMappings = createProjectRootMappings(projectGraph.nodes);
|
||||
const project =
|
||||
options.project ??
|
||||
getProjectFromPath(options.path) ??
|
||||
findProjectForPath(options.path, projectRootMappings) ??
|
||||
readWorkspaceConfiguration(tree).defaultProject;
|
||||
const { projectType, root, sourceRoot } = readProjectConfiguration(
|
||||
tree,
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
import type { Tree } from '@nrwl/devkit';
|
||||
import { getSourceNodes } from '@nrwl/workspace/src/utilities/typescript';
|
||||
import { findNodes } from 'nx/src/utils/typescript';
|
||||
|
||||
import { getSourceNodes } from '@nrwl/workspace/src/utilities/typescript';
|
||||
import type { PropertyDeclaration } from 'typescript';
|
||||
import { SyntaxKind } from 'typescript';
|
||||
import { getTsSourceFile } from '../../../utils/nx-devkit/ast-utils';
|
||||
|
||||
@ -9,9 +9,12 @@ import {
|
||||
workspaceRoot,
|
||||
} from '@nrwl/devkit';
|
||||
import { readProjectsConfigurationFromProjectGraph } from 'nx/src/project-graph/project-graph';
|
||||
import { createProjectFileMappings } from 'nx/src/utils/target-project-locator';
|
||||
import { dirname, extname, join, relative } from 'path';
|
||||
import { lstatSync } from 'fs';
|
||||
import {
|
||||
createProjectRootMappings,
|
||||
findProjectForPath,
|
||||
} from 'nx/src/project-graph/utils/find-project-for-path';
|
||||
|
||||
interface BaseCypressPreset {
|
||||
videosFolder: string;
|
||||
@ -90,16 +93,18 @@ export function getProjectConfigByPath(
|
||||
: configFileFromWorkspaceRoot
|
||||
);
|
||||
|
||||
const mappedGraphFiles = createProjectFileMappings(graph.nodes);
|
||||
const componentTestingProjectName =
|
||||
mappedGraphFiles[normalizedPathFromWorkspaceRoot];
|
||||
const projectRootMappings = createProjectRootMappings(graph.nodes);
|
||||
const componentTestingProjectName = findProjectForPath(
|
||||
normalizedPathFromWorkspaceRoot,
|
||||
projectRootMappings
|
||||
);
|
||||
if (
|
||||
!componentTestingProjectName ||
|
||||
!graph.nodes[componentTestingProjectName]?.data
|
||||
) {
|
||||
throw new Error(
|
||||
stripIndents`Unable to find the project configuration that includes ${normalizedPathFromWorkspaceRoot}.
|
||||
Found project name? ${componentTestingProjectName}.
|
||||
stripIndents`Unable to find the project configuration that includes ${normalizedPathFromWorkspaceRoot}.
|
||||
Found project name? ${componentTestingProjectName}.
|
||||
Graph has data? ${!!graph.nodes[componentTestingProjectName]?.data}`
|
||||
);
|
||||
}
|
||||
|
||||
@ -3,6 +3,7 @@
|
||||
"rules": {
|
||||
"no-restricted-imports": [
|
||||
"error",
|
||||
"@nrwl/workspace",
|
||||
"@angular-devkit/core",
|
||||
"@angular-devkit/architect",
|
||||
"@angular-devkit/schematics"
|
||||
|
||||
@ -3,13 +3,11 @@ import { DependencyType } from '@nrwl/devkit';
|
||||
import * as parser from '@typescript-eslint/parser';
|
||||
import { TSESLint } from '@typescript-eslint/utils';
|
||||
import { vol } from 'memfs';
|
||||
import {
|
||||
TargetProjectLocator,
|
||||
createProjectFileMappings,
|
||||
} from 'nx/src/utils/target-project-locator';
|
||||
import { TargetProjectLocator } from 'nx/src/utils/target-project-locator';
|
||||
import enforceModuleBoundaries, {
|
||||
RULE_NAME as enforceModuleBoundariesRuleName,
|
||||
} from './enforce-module-boundaries';
|
||||
} from '../../src/rules/enforce-module-boundaries';
|
||||
import { createProjectRootMappings } from 'nx/src/project-graph/utils/find-project-for-path';
|
||||
|
||||
jest.mock('fs', () => require('memfs').fs);
|
||||
|
||||
@ -1015,7 +1013,7 @@ Violation detected in:
|
||||
tags: [],
|
||||
implicitDependencies: [],
|
||||
architect: {},
|
||||
files: [createFile(`libs/other/src/index.ts`)],
|
||||
files: [createFile(`libs/other/index.ts`)],
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -1033,7 +1031,6 @@ Violation detected in:
|
||||
if (importKind === 'type') {
|
||||
expect(failures.length).toEqual(0);
|
||||
} else {
|
||||
expect(failures.length).toEqual(1);
|
||||
expect(failures[0].message).toEqual(
|
||||
'Imports of lazy-loaded libraries are forbidden'
|
||||
);
|
||||
@ -1145,10 +1142,7 @@ Violation detected in:
|
||||
tags: [],
|
||||
implicitDependencies: [],
|
||||
architect: {},
|
||||
files: [
|
||||
createFile(`libs/mylib/src/main.ts`),
|
||||
createFile(`libs/mylib/src/index.ts`),
|
||||
],
|
||||
files: [createFile(`libs/mylib/src/main.ts`)],
|
||||
},
|
||||
},
|
||||
anotherlibName: {
|
||||
@ -1272,10 +1266,7 @@ Violation detected in:
|
||||
tags: [],
|
||||
implicitDependencies: [],
|
||||
architect: {},
|
||||
files: [
|
||||
createFile(`libs/mylib/src/main.ts`, ['anotherlibName']),
|
||||
createFile(`libs/mylib/src/index.ts`),
|
||||
],
|
||||
files: [createFile(`libs/mylib/src/main.ts`, ['anotherlibName'])],
|
||||
},
|
||||
},
|
||||
anotherlibName: {
|
||||
@ -1370,7 +1361,6 @@ Circular file chain:
|
||||
architect: {},
|
||||
files: [
|
||||
createFile(`libs/badcirclelib/src/main.ts`, ['anotherlibName']),
|
||||
createFile(`libs/badcirclelib/src/index.ts`),
|
||||
],
|
||||
},
|
||||
},
|
||||
@ -1894,7 +1884,7 @@ function runRule(
|
||||
): TSESLint.Linter.LintMessage[] {
|
||||
(global as any).projectPath = `${process.cwd()}/proj`;
|
||||
(global as any).projectGraph = projectGraph;
|
||||
(global as any).projectGraphFileMappings = createProjectFileMappings(
|
||||
(global as any).projectRootMappings = createProjectRootMappings(
|
||||
projectGraph.nodes
|
||||
);
|
||||
(global as any).targetProjectLocator = new TargetProjectLocator(
|
||||
|
||||
@ -15,9 +15,8 @@ import {
|
||||
findConstraintsFor,
|
||||
findDependenciesWithTags,
|
||||
findProjectUsingImport,
|
||||
findSourceProject,
|
||||
findProject,
|
||||
findTransitiveExternalDependencies,
|
||||
findTargetProject,
|
||||
getSourceFilePath,
|
||||
getTargetProjectBasedOnRelativeImport,
|
||||
groupImports,
|
||||
@ -154,8 +153,7 @@ export default createESLintRule<Options, MessageIds>({
|
||||
);
|
||||
const fileName = normalizePath(context.getFilename());
|
||||
|
||||
const { projectGraph, projectGraphFileMappings } =
|
||||
readProjectGraph(RULE_NAME);
|
||||
const { projectGraph, projectRootMappings } = readProjectGraph(RULE_NAME);
|
||||
|
||||
if (!projectGraph) {
|
||||
return {};
|
||||
@ -199,9 +197,9 @@ export default createESLintRule<Options, MessageIds>({
|
||||
|
||||
const sourceFilePath = getSourceFilePath(fileName, projectPath);
|
||||
|
||||
const sourceProject = findSourceProject(
|
||||
const sourceProject = findProject(
|
||||
projectGraph,
|
||||
projectGraphFileMappings,
|
||||
projectRootMappings,
|
||||
sourceFilePath
|
||||
);
|
||||
// If source is not part of an nx workspace, return.
|
||||
@ -215,17 +213,13 @@ export default createESLintRule<Options, MessageIds>({
|
||||
let targetProject: ProjectGraphProjectNode | ProjectGraphExternalNode;
|
||||
|
||||
if (isAbsoluteImportIntoAnotherProj) {
|
||||
targetProject = findTargetProject(
|
||||
projectGraph,
|
||||
projectGraphFileMappings,
|
||||
imp
|
||||
);
|
||||
targetProject = findProject(projectGraph, projectRootMappings, imp);
|
||||
} else {
|
||||
targetProject = getTargetProjectBasedOnRelativeImport(
|
||||
imp,
|
||||
projectPath,
|
||||
projectGraph,
|
||||
projectGraphFileMappings,
|
||||
projectRootMappings,
|
||||
sourceFilePath
|
||||
);
|
||||
}
|
||||
|
||||
@ -6,10 +6,7 @@ import {
|
||||
readJsonFile,
|
||||
workspaceRoot,
|
||||
} from '@nrwl/devkit';
|
||||
import {
|
||||
findSourceProject,
|
||||
getSourceFilePath,
|
||||
} from '../utils/runtime-lint-utils';
|
||||
import { findProject, getSourceFilePath } from '../utils/runtime-lint-utils';
|
||||
import { existsSync } from 'fs';
|
||||
import { registerTsProject } from 'nx/src/utils/register';
|
||||
import * as path from 'path';
|
||||
@ -87,17 +84,16 @@ export default createESLintRule<Options, MessageIds>({
|
||||
return {};
|
||||
}
|
||||
|
||||
const { projectGraph, projectGraphFileMappings } =
|
||||
readProjectGraph(RULE_NAME);
|
||||
const { projectGraph, projectRootMappings } = readProjectGraph(RULE_NAME);
|
||||
|
||||
const sourceFilePath = getSourceFilePath(
|
||||
context.getFilename(),
|
||||
workspaceRoot
|
||||
);
|
||||
|
||||
const sourceProject = findSourceProject(
|
||||
const sourceProject = findProject(
|
||||
projectGraph,
|
||||
projectGraphFileMappings,
|
||||
projectRootMappings,
|
||||
sourceFilePath
|
||||
);
|
||||
// If source is not part of an nx workspace, return.
|
||||
|
||||
@ -1,18 +1,17 @@
|
||||
import { ProjectGraph, readCachedProjectGraph, readNxJson } from '@nrwl/devkit';
|
||||
import { createProjectFileMappings } from 'nx/src/utils/target-project-locator';
|
||||
import { isTerminalRun } from './runtime-lint-utils';
|
||||
import * as chalk from 'chalk';
|
||||
import {
|
||||
createProjectRootMappings,
|
||||
ProjectRootMappings,
|
||||
} from 'nx/src/project-graph/utils/find-project-for-path';
|
||||
|
||||
export function ensureGlobalProjectGraph(ruleName: string) {
|
||||
/**
|
||||
* Only reuse graph when running from terminal
|
||||
* Enforce every IDE change to get a fresh nxdeps.json
|
||||
*/
|
||||
if (
|
||||
!(global as any).projectGraph ||
|
||||
!(global as any).projectGraphFileMappings ||
|
||||
!isTerminalRun()
|
||||
) {
|
||||
if (!(global as any).projectGraph || !isTerminalRun()) {
|
||||
const nxJson = readNxJson();
|
||||
(global as any).workspaceLayout = nxJson.workspaceLayout;
|
||||
|
||||
@ -22,7 +21,7 @@ export function ensureGlobalProjectGraph(ruleName: string) {
|
||||
*/
|
||||
try {
|
||||
(global as any).projectGraph = readCachedProjectGraph();
|
||||
(global as any).projectGraphFileMappings = createProjectFileMappings(
|
||||
(global as any).projectRootMappings = createProjectRootMappings(
|
||||
(global as any).projectGraph.nodes
|
||||
);
|
||||
} catch {
|
||||
@ -38,11 +37,11 @@ export function ensureGlobalProjectGraph(ruleName: string) {
|
||||
|
||||
export function readProjectGraph(ruleName: string): {
|
||||
projectGraph: ProjectGraph;
|
||||
projectGraphFileMappings: Record<string, string>;
|
||||
projectRootMappings: ProjectRootMappings;
|
||||
} {
|
||||
ensureGlobalProjectGraph(ruleName);
|
||||
return {
|
||||
projectGraph: (global as any).projectGraph,
|
||||
projectGraphFileMappings: (global as any).projectGraphFileMappings,
|
||||
projectRootMappings: (global as any).projectRootMappings,
|
||||
};
|
||||
}
|
||||
|
||||
@ -1,23 +1,24 @@
|
||||
import * as path from 'path';
|
||||
import { join } from 'path';
|
||||
import {
|
||||
DependencyType,
|
||||
joinPathFragments,
|
||||
normalizePath,
|
||||
parseJson,
|
||||
ProjectGraph,
|
||||
ProjectGraphDependency,
|
||||
ProjectGraphProjectNode,
|
||||
normalizePath,
|
||||
DependencyType,
|
||||
parseJson,
|
||||
ProjectGraphExternalNode,
|
||||
joinPathFragments,
|
||||
ProjectGraphProjectNode,
|
||||
workspaceRoot,
|
||||
} from '@nrwl/devkit';
|
||||
import { join } from 'path';
|
||||
import { getPath, pathExists } from './graph-utils';
|
||||
import { existsSync } from 'fs';
|
||||
import { readFileIfExisting } from 'nx/src/project-graph/file-utils';
|
||||
import { TargetProjectLocator } from 'nx/src/utils/target-project-locator';
|
||||
import {
|
||||
TargetProjectLocator,
|
||||
removeExt,
|
||||
} from 'nx/src/utils/target-project-locator';
|
||||
findProjectForPath,
|
||||
ProjectRootMappings,
|
||||
} from 'nx/src/project-graph/utils/find-project-for-path';
|
||||
|
||||
export type Deps = { [projectName: string]: ProjectGraphDependency[] };
|
||||
export type DepConstraint = {
|
||||
@ -99,7 +100,7 @@ export function getTargetProjectBasedOnRelativeImport(
|
||||
imp: string,
|
||||
projectPath: string,
|
||||
projectGraph: ProjectGraph,
|
||||
projectGraphFileMappings: Record<string, string>,
|
||||
projectRootMappings: ProjectRootMappings,
|
||||
sourceFilePath: string
|
||||
): ProjectGraphProjectNode<any> | undefined {
|
||||
if (!isRelative(imp)) {
|
||||
@ -111,55 +112,17 @@ export function getTargetProjectBasedOnRelativeImport(
|
||||
projectPath.length + 1
|
||||
);
|
||||
|
||||
return findTargetProject(projectGraph, projectGraphFileMappings, targetFile);
|
||||
return findProject(projectGraph, projectRootMappings, targetFile);
|
||||
}
|
||||
|
||||
function findProjectUsingFile(
|
||||
export function findProject(
|
||||
projectGraph: ProjectGraph,
|
||||
projectGraphFileMappings: Record<string, string>,
|
||||
file: string
|
||||
): ProjectGraphProjectNode {
|
||||
return projectGraph.nodes[projectGraphFileMappings[file]];
|
||||
}
|
||||
|
||||
export function findSourceProject(
|
||||
projectGraph: ProjectGraph,
|
||||
projectGraphFileMappings: Record<string, string>,
|
||||
projectRootMappings: ProjectRootMappings,
|
||||
sourceFilePath: string
|
||||
) {
|
||||
const targetFile = removeExt(sourceFilePath);
|
||||
return findProjectUsingFile(
|
||||
projectGraph,
|
||||
projectGraphFileMappings,
|
||||
targetFile
|
||||
);
|
||||
}
|
||||
|
||||
export function findTargetProject(
|
||||
projectGraph: ProjectGraph,
|
||||
projectGraphFileMappings: Record<string, string>,
|
||||
targetFile: string
|
||||
) {
|
||||
let targetProject = findProjectUsingFile(
|
||||
projectGraph,
|
||||
projectGraphFileMappings,
|
||||
targetFile
|
||||
);
|
||||
if (!targetProject) {
|
||||
targetProject = findProjectUsingFile(
|
||||
projectGraph,
|
||||
projectGraphFileMappings,
|
||||
normalizePath(path.join(targetFile, 'index'))
|
||||
);
|
||||
}
|
||||
if (!targetProject) {
|
||||
targetProject = findProjectUsingFile(
|
||||
projectGraph,
|
||||
projectGraphFileMappings,
|
||||
normalizePath(path.join(targetFile, 'src', 'index'))
|
||||
);
|
||||
}
|
||||
return targetProject;
|
||||
return projectGraph.nodes[
|
||||
findProjectForPath(sourceFilePath, projectRootMappings)
|
||||
];
|
||||
}
|
||||
|
||||
export function isAbsoluteImportIntoAnotherProject(
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import * as ts from 'typescript';
|
||||
import { findNodes, InsertChange, ReplaceChange } from '@nrwl/workspace';
|
||||
import { InsertChange, ReplaceChange } from '@nrwl/workspace';
|
||||
import { findNodes } from 'nx/src/utils/typescript';
|
||||
import { Tree } from '@angular-devkit/schematics';
|
||||
import { stripJsonComments } from '@nrwl/devkit';
|
||||
import { Config } from '@jest/types';
|
||||
|
||||
@ -22,9 +22,9 @@ describe('getTouchedProjects', () => {
|
||||
it('should return a list of projects for the given changes', () => {
|
||||
const fileChanges = getFileChanges(['libs/a/index.ts', 'libs/b/index.ts']);
|
||||
const projects = {
|
||||
a: { root: 'libs/a', files: [{ file: 'libs/a/index.ts' }] },
|
||||
b: { root: 'libs/b', files: [{ file: 'libs/b/index.ts' }] },
|
||||
c: { root: 'libs/c', files: [{ file: 'libs/c/index.ts' }] },
|
||||
a: { root: 'libs/a' },
|
||||
b: { root: 'libs/b' },
|
||||
c: { root: 'libs/c' },
|
||||
};
|
||||
expect(
|
||||
getTouchedProjects(fileChanges, buildProjectGraphNodes(projects))
|
||||
@ -34,9 +34,9 @@ describe('getTouchedProjects', () => {
|
||||
it('should return projects with the root matching a whole directory name in the file path', () => {
|
||||
const fileChanges = getFileChanges(['libs/a-b/index.ts']);
|
||||
const projects = {
|
||||
a: { root: 'libs/a', files: [{ file: 'libs/a/index.ts' }] },
|
||||
abc: { root: 'libs/a-b-c', files: [{ file: 'libs/a-b-c/index.ts' }] },
|
||||
ab: { root: 'libs/a-b', files: [{ file: 'libs/a-b/index.ts' }] },
|
||||
a: { root: 'libs/a' },
|
||||
abc: { root: 'libs/a-b-c' },
|
||||
ab: { root: 'libs/a-b' },
|
||||
};
|
||||
expect(
|
||||
getTouchedProjects(fileChanges, buildProjectGraphNodes(projects))
|
||||
@ -46,9 +46,9 @@ describe('getTouchedProjects', () => {
|
||||
it('should return projects with the root matching a whole directory name in the file path', () => {
|
||||
const fileChanges = getFileChanges(['libs/a-b/index.ts']);
|
||||
const projects = {
|
||||
aaaaa: { root: 'libs/a', files: [{ file: 'libs/a/index.ts' }] },
|
||||
abc: { root: 'libs/a-b-c', files: [{ file: 'libs/a-b-c/index.ts' }] },
|
||||
ab: { root: 'libs/a-b', files: [{ file: 'libs/a-b/index.ts' }] },
|
||||
aaaaa: { root: 'libs/a' },
|
||||
abc: { root: 'libs/a-b-c' },
|
||||
ab: { root: 'libs/a-b' },
|
||||
};
|
||||
expect(
|
||||
getTouchedProjects(fileChanges, buildProjectGraphNodes(projects))
|
||||
@ -58,8 +58,8 @@ describe('getTouchedProjects', () => {
|
||||
it('should return the most qualifying match with the file path', () => {
|
||||
const fileChanges = getFileChanges(['libs/a/b/index.ts']);
|
||||
const projects = {
|
||||
aaaaa: { root: 'libs/a', files: [{ file: 'libs/a/index.ts' }] },
|
||||
ab: { root: 'libs/a/b', files: [{ file: 'libs/a/b/index.ts' }] },
|
||||
aaaaa: { root: 'libs/a' },
|
||||
ab: { root: 'libs/a/b' },
|
||||
};
|
||||
expect(
|
||||
getTouchedProjects(fileChanges, buildProjectGraphNodes(projects))
|
||||
@ -69,8 +69,8 @@ describe('getTouchedProjects', () => {
|
||||
it('should not return parent project if nested project is touched', () => {
|
||||
const fileChanges = getFileChanges(['libs/a/b/index.ts']);
|
||||
const projects = {
|
||||
a: { root: 'libs/a', files: [{ file: 'libs/a/index.ts' }] },
|
||||
b: { root: 'libs/a/b', files: [{ file: 'libs/a/b/index.ts' }] },
|
||||
a: { root: 'libs/a' },
|
||||
b: { root: 'libs/a/b' },
|
||||
};
|
||||
expect(
|
||||
getTouchedProjects(fileChanges, buildProjectGraphNodes(projects))
|
||||
|
||||
@ -2,23 +2,24 @@ import * as minimatch from 'minimatch';
|
||||
import { TouchedProjectLocator } from '../affected-project-graph-models';
|
||||
import { NxJsonConfiguration } from '../../../config/nx-json';
|
||||
import { ProjectGraphProjectNode } from '../../../config/project-graph';
|
||||
import { createProjectFileMappings } from '../../../utils/target-project-locator';
|
||||
import {
|
||||
createProjectRootMappings,
|
||||
findProjectForPath,
|
||||
} from '../../utils/find-project-for-path';
|
||||
|
||||
export const getTouchedProjects: TouchedProjectLocator = (
|
||||
touchedFiles,
|
||||
projectGraphNodes
|
||||
): string[] => {
|
||||
const allProjectFiles = createProjectFileMappings(projectGraphNodes);
|
||||
const affected = [];
|
||||
const projectRootMap = createProjectRootMappings(projectGraphNodes);
|
||||
|
||||
touchedFiles.forEach((f) => {
|
||||
const matchingProject = allProjectFiles[f.file];
|
||||
return touchedFiles.reduce((affected, f) => {
|
||||
const matchingProject = findProjectForPath(f.file, projectRootMap);
|
||||
if (matchingProject) {
|
||||
affected.push(matchingProject);
|
||||
}
|
||||
});
|
||||
|
||||
return affected;
|
||||
return affected;
|
||||
}, []);
|
||||
};
|
||||
|
||||
export const getImplicitlyTouchedProjects: TouchedProjectLocator = (
|
||||
|
||||
@ -1,57 +1,23 @@
|
||||
import { dirname } from 'path';
|
||||
import { FileData, ProjectFileMap } from '../config/project-graph';
|
||||
|
||||
function createProjectRootMappings(
|
||||
workspaceJson: any,
|
||||
projectFileMap: ProjectFileMap
|
||||
) {
|
||||
const projectRootMappings = new Map();
|
||||
for (const projectName of Object.keys(workspaceJson.projects)) {
|
||||
if (!projectFileMap[projectName]) {
|
||||
projectFileMap[projectName] = [];
|
||||
}
|
||||
const root =
|
||||
workspaceJson.projects[projectName].root === ''
|
||||
? '.'
|
||||
: workspaceJson.projects[projectName].root;
|
||||
projectRootMappings.set(
|
||||
root.endsWith('/') ? root.substring(0, root.length - 1) : root,
|
||||
projectFileMap[projectName]
|
||||
);
|
||||
}
|
||||
return projectRootMappings;
|
||||
}
|
||||
|
||||
function findMatchingProjectFiles(
|
||||
projectRootMappings: Map<string, FileData[]>,
|
||||
file: string
|
||||
) {
|
||||
let currentPath = file;
|
||||
do {
|
||||
currentPath = dirname(currentPath);
|
||||
const p = projectRootMappings.get(currentPath);
|
||||
if (p) {
|
||||
return p;
|
||||
}
|
||||
} while (currentPath != dirname(currentPath));
|
||||
|
||||
return null;
|
||||
}
|
||||
import {
|
||||
createProjectRootMappingsFromProjectConfigurations,
|
||||
findProjectForPath,
|
||||
} from './utils/find-project-for-path';
|
||||
|
||||
export function createProjectFileMap(
|
||||
workspaceJson: any,
|
||||
allWorkspaceFiles: FileData[]
|
||||
): { projectFileMap: ProjectFileMap; allWorkspaceFiles: FileData[] } {
|
||||
const projectFileMap: ProjectFileMap = {};
|
||||
const projectRootMappings = createProjectRootMappings(
|
||||
workspaceJson,
|
||||
projectFileMap
|
||||
);
|
||||
const projectRootMappings =
|
||||
createProjectRootMappingsFromProjectConfigurations(workspaceJson.projects);
|
||||
|
||||
for (const projectName of Object.keys(workspaceJson.projects)) {
|
||||
projectFileMap[projectName] ??= [];
|
||||
}
|
||||
for (const f of allWorkspaceFiles) {
|
||||
const matchingProjectFiles = findMatchingProjectFiles(
|
||||
projectRootMappings,
|
||||
f.file
|
||||
);
|
||||
const matchingProjectFiles =
|
||||
projectFileMap[findProjectForPath(f.file, projectRootMappings)];
|
||||
if (matchingProjectFiles) {
|
||||
matchingProjectFiles.push(f);
|
||||
}
|
||||
@ -66,16 +32,12 @@ export function updateProjectFileMap(
|
||||
updatedFiles: Map<string, string>,
|
||||
deletedFiles: string[]
|
||||
): { projectFileMap: ProjectFileMap; allWorkspaceFiles: FileData[] } {
|
||||
const projectRootMappings = createProjectRootMappings(
|
||||
workspaceJson,
|
||||
projectFileMap
|
||||
);
|
||||
const projectRootMappings =
|
||||
createProjectRootMappingsFromProjectConfigurations(workspaceJson.projects);
|
||||
|
||||
for (const f of updatedFiles.keys()) {
|
||||
const matchingProjectFiles = findMatchingProjectFiles(
|
||||
projectRootMappings,
|
||||
f
|
||||
);
|
||||
const matchingProjectFiles =
|
||||
projectFileMap[findProjectForPath(f, projectRootMappings)] ?? [];
|
||||
if (matchingProjectFiles) {
|
||||
const fileData: FileData = matchingProjectFiles.find((t) => t.file === f);
|
||||
if (fileData) {
|
||||
@ -100,10 +62,8 @@ export function updateProjectFileMap(
|
||||
}
|
||||
|
||||
for (const f of deletedFiles) {
|
||||
const matchingProjectFiles = findMatchingProjectFiles(
|
||||
projectRootMappings,
|
||||
f
|
||||
);
|
||||
const matchingProjectFiles =
|
||||
projectFileMap[findProjectForPath(f, projectRootMappings)] ?? [];
|
||||
if (matchingProjectFiles) {
|
||||
const index = matchingProjectFiles.findIndex((t) => t.file === f);
|
||||
if (index > -1) {
|
||||
|
||||
@ -0,0 +1,85 @@
|
||||
import {
|
||||
createProjectRootMappings,
|
||||
findProjectForPath,
|
||||
} from './find-project-for-path';
|
||||
import { ProjectGraph } from 'nx/src/config/project-graph';
|
||||
|
||||
describe('get project utils', () => {
|
||||
let projectGraph: ProjectGraph;
|
||||
let projectRootMappings: Map<string, string>;
|
||||
describe('findProject', () => {
|
||||
beforeEach(() => {
|
||||
projectGraph = {
|
||||
nodes: {
|
||||
'demo-app': {
|
||||
name: 'demo-app',
|
||||
type: 'app',
|
||||
data: {
|
||||
root: 'apps/demo-app',
|
||||
},
|
||||
},
|
||||
ui: {
|
||||
name: 'ui',
|
||||
type: 'lib',
|
||||
data: {
|
||||
root: 'libs/ui',
|
||||
sourceRoot: 'libs/ui/src',
|
||||
projectType: 'library',
|
||||
targets: {},
|
||||
},
|
||||
},
|
||||
core: {
|
||||
name: 'core',
|
||||
type: 'lib',
|
||||
data: {
|
||||
root: 'libs/core',
|
||||
sourceRoot: 'libs/core/src',
|
||||
projectType: 'library',
|
||||
targets: {},
|
||||
},
|
||||
},
|
||||
'implicit-lib': {
|
||||
name: 'implicit-lib',
|
||||
type: 'lib',
|
||||
data: {},
|
||||
},
|
||||
},
|
||||
dependencies: {
|
||||
'demo-app': [
|
||||
{
|
||||
type: 'static',
|
||||
source: 'demo-app',
|
||||
target: 'ui',
|
||||
},
|
||||
{
|
||||
type: 'static',
|
||||
source: 'demo-app',
|
||||
target: 'npm:chalk',
|
||||
},
|
||||
{
|
||||
type: 'static',
|
||||
source: 'demo-app',
|
||||
target: 'core',
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
projectRootMappings = createProjectRootMappings(projectGraph.nodes);
|
||||
});
|
||||
|
||||
it('should find the project given a file within its src root', () => {
|
||||
expect(findProjectForPath('apps/demo-app', projectRootMappings)).toEqual(
|
||||
'demo-app'
|
||||
);
|
||||
|
||||
expect(
|
||||
findProjectForPath('apps/demo-app/src', projectRootMappings)
|
||||
).toEqual('demo-app');
|
||||
|
||||
expect(
|
||||
findProjectForPath('apps/demo-app/src/subdir/bla', projectRootMappings)
|
||||
).toEqual('demo-app');
|
||||
});
|
||||
});
|
||||
});
|
||||
64
packages/nx/src/project-graph/utils/find-project-for-path.ts
Normal file
64
packages/nx/src/project-graph/utils/find-project-for-path.ts
Normal file
@ -0,0 +1,64 @@
|
||||
import { dirname } from 'path';
|
||||
import { ProjectGraphProjectNode } from '../../config/project-graph';
|
||||
import { ProjectConfiguration } from '../../config/workspace-json-project-json';
|
||||
|
||||
export type ProjectRootMappings = Map<string, string>;
|
||||
|
||||
/**
|
||||
* This creates a map of project roots to project names to easily look up the project of a file
|
||||
* @param projects This is the map of project configurations commonly found in "workspace.json"
|
||||
*/
|
||||
export function createProjectRootMappingsFromProjectConfigurations(
|
||||
projects: Record<string, ProjectConfiguration>
|
||||
) {
|
||||
const projectRootMappings: ProjectRootMappings = new Map();
|
||||
for (const projectName of Object.keys(projects)) {
|
||||
const root = projects[projectName].root;
|
||||
projectRootMappings.set(normalizeProjectRoot(root), projectName);
|
||||
}
|
||||
return projectRootMappings;
|
||||
}
|
||||
|
||||
/**
|
||||
* This creates a map of project roots to project names to easily look up the project of a file
|
||||
* @param nodes This is the nodes from the project graph
|
||||
*/
|
||||
export function createProjectRootMappings(
|
||||
nodes: Record<string, ProjectGraphProjectNode>
|
||||
): ProjectRootMappings {
|
||||
const projectRootMappings = new Map<string, string>();
|
||||
for (const projectName of Object.keys(nodes)) {
|
||||
let root = nodes[projectName].data.root;
|
||||
|
||||
projectRootMappings.set(normalizeProjectRoot(root), projectName);
|
||||
}
|
||||
return projectRootMappings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Locates a project in projectRootMap based on a file within it
|
||||
* @param filePath path that is inside of projectName. This should be relative from the workspace root
|
||||
* @param projectRootMap Map<projectRoot, projectName> Use {@link createProjectRootMappings} to create this
|
||||
*/
|
||||
export function findProjectForPath(
|
||||
filePath: string,
|
||||
projectRootMap: ProjectRootMappings
|
||||
): string | null {
|
||||
let currentPath = filePath;
|
||||
for (
|
||||
;
|
||||
currentPath != dirname(currentPath);
|
||||
currentPath = dirname(currentPath)
|
||||
) {
|
||||
const p = projectRootMap.get(currentPath);
|
||||
if (p) {
|
||||
return p;
|
||||
}
|
||||
}
|
||||
return projectRootMap.get(currentPath);
|
||||
}
|
||||
|
||||
function normalizeProjectRoot(root: string) {
|
||||
root = root === '' ? '.' : root;
|
||||
return root && root.endsWith('/') ? root.substring(0, root.length - 1) : root;
|
||||
}
|
||||
@ -13,11 +13,14 @@ import {
|
||||
import { registerTsProject } from './register';
|
||||
import {
|
||||
ProjectConfiguration,
|
||||
TargetConfiguration,
|
||||
ProjectsConfigurations,
|
||||
TargetConfiguration,
|
||||
} from '../config/workspace-json-project-json';
|
||||
import { findMatchingProjectForPath } from './target-project-locator';
|
||||
import { logger } from './logger';
|
||||
import {
|
||||
createProjectRootMappingsFromProjectConfigurations,
|
||||
findProjectForPath,
|
||||
} from '../project-graph/utils/find-project-for-path';
|
||||
|
||||
export type ProjectTargetConfigurator = (
|
||||
file: string
|
||||
@ -192,15 +195,13 @@ function findNxProjectForImportPath(
|
||||
): string | null {
|
||||
const tsConfigPaths: Record<string, string[]> = readTsConfigPaths(root);
|
||||
const possiblePaths = tsConfigPaths[importPath]?.map((p) =>
|
||||
path.resolve(root, p)
|
||||
path.relative(root, path.join(root, p))
|
||||
);
|
||||
if (possiblePaths?.length) {
|
||||
const projectRootMappings = buildProjectRootMap(workspace.projects, root);
|
||||
const projectRootMappings =
|
||||
createProjectRootMappingsFromProjectConfigurations(workspace.projects);
|
||||
for (const tsConfigPath of possiblePaths) {
|
||||
const nxProject = findMatchingProjectForPath(
|
||||
tsConfigPath,
|
||||
projectRootMappings
|
||||
);
|
||||
const nxProject = findProjectForPath(tsConfigPath, projectRootMappings);
|
||||
if (nxProject) {
|
||||
return nxProject;
|
||||
}
|
||||
@ -218,16 +219,6 @@ function findNxProjectForImportPath(
|
||||
}
|
||||
}
|
||||
|
||||
function buildProjectRootMap(
|
||||
projects: Record<string, ProjectConfiguration>,
|
||||
root: string
|
||||
) {
|
||||
return Object.entries(projects).reduce((m, [project, config]) => {
|
||||
m.set(path.resolve(root, config.root), project);
|
||||
return m;
|
||||
}, new Map<string, string>());
|
||||
}
|
||||
|
||||
let tsconfigPaths: Record<string, string[]>;
|
||||
function readTsConfigPaths(root: string = workspaceRoot) {
|
||||
if (!tsconfigPaths) {
|
||||
|
||||
@ -13,7 +13,6 @@ jest.mock('nx/src/utils/fileutils', () => ({
|
||||
import { PackageJson } from './package-json';
|
||||
import { ProjectGraph } from '../config/project-graph';
|
||||
import {
|
||||
getProjectNameFromDirPath,
|
||||
getSourceDirOfDependentProjects,
|
||||
mergeNpmScriptsWithTargets,
|
||||
} from './project-graph-utils';
|
||||
@ -116,26 +115,6 @@ describe('project graph utils', () => {
|
||||
expect(warnings).toContain('implicit-lib');
|
||||
});
|
||||
});
|
||||
|
||||
it('should find the project given a file within its src root', () => {
|
||||
expect(getProjectNameFromDirPath('apps/demo-app', projGraph)).toEqual(
|
||||
'demo-app'
|
||||
);
|
||||
|
||||
expect(getProjectNameFromDirPath('apps/demo-app/src', projGraph)).toEqual(
|
||||
'demo-app'
|
||||
);
|
||||
|
||||
expect(
|
||||
getProjectNameFromDirPath('apps/demo-app/src/subdir/bla', projGraph)
|
||||
).toEqual('demo-app');
|
||||
});
|
||||
|
||||
it('should throw an error if the project name has not been found', () => {
|
||||
expect(() => {
|
||||
getProjectNameFromDirPath('apps/demo-app-unknown');
|
||||
}).toThrowError();
|
||||
});
|
||||
});
|
||||
|
||||
describe('mergeNpmScriptsWithTargets', () => {
|
||||
|
||||
@ -1,8 +1,7 @@
|
||||
import { buildTargetFromScript, PackageJson } from './package-json';
|
||||
import { join, relative } from 'path';
|
||||
import { join } from 'path';
|
||||
import { ProjectGraph, ProjectGraphProjectNode } from '../config/project-graph';
|
||||
import { readJsonFile } from './fileutils';
|
||||
import { normalizePath } from './path';
|
||||
import { readCachedProjectGraph } from '../project-graph/project-graph';
|
||||
import { TargetConfiguration } from '../config/workspace-json-project-json';
|
||||
|
||||
@ -74,38 +73,6 @@ export function getSourceDirOfDependentProjects(
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the project node name by a file that lives within it's src root
|
||||
* @param projRelativeDirPath directory path relative to the workspace root
|
||||
* @param projectGraph
|
||||
*/
|
||||
export function getProjectNameFromDirPath(
|
||||
projRelativeDirPath: string,
|
||||
projectGraph = readCachedProjectGraph()
|
||||
) {
|
||||
let parentNodeName = null;
|
||||
for (const [nodeName, node] of Object.entries(projectGraph.nodes)) {
|
||||
const normalizedRootPath = normalizePath(node.data.root);
|
||||
const normalizedProjRelPath = normalizePath(projRelativeDirPath);
|
||||
|
||||
const relativePath = relative(normalizedRootPath, normalizedProjRelPath);
|
||||
const isMatch = relativePath && !relativePath.startsWith('..');
|
||||
|
||||
if (isMatch || normalizedRootPath === normalizedProjRelPath) {
|
||||
parentNodeName = nodeName;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!parentNodeName) {
|
||||
throw new Error(
|
||||
`Could not find any project containing the file "${projRelativeDirPath}" among it's project files`
|
||||
);
|
||||
}
|
||||
|
||||
return parentNodeName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find all internal project dependencies.
|
||||
* All the external (npm) dependencies will be filtered out
|
||||
|
||||
@ -75,15 +75,17 @@ describe('findTargetProjectWithImport', () => {
|
||||
...nxJson,
|
||||
} as any,
|
||||
fileMap: {
|
||||
rootProj: [
|
||||
{
|
||||
file: 'index.ts',
|
||||
hash: 'some-hash',
|
||||
},
|
||||
],
|
||||
proj: [
|
||||
{
|
||||
file: 'libs/proj/index.ts',
|
||||
hash: 'some-hash',
|
||||
},
|
||||
{
|
||||
file: 'libs/proj/class.ts',
|
||||
hash: 'some-hash',
|
||||
},
|
||||
],
|
||||
proj2: [
|
||||
{
|
||||
@ -151,12 +153,20 @@ describe('findTargetProjectWithImport', () => {
|
||||
} as any;
|
||||
|
||||
projects = {
|
||||
rootProj: {
|
||||
name: 'rootProj',
|
||||
type: 'lib',
|
||||
data: {
|
||||
root: '.',
|
||||
files: [],
|
||||
},
|
||||
},
|
||||
proj3a: {
|
||||
name: 'proj3a',
|
||||
type: 'lib',
|
||||
data: {
|
||||
root: 'libs/proj3a',
|
||||
files: ctx.fileMap['proj3a'],
|
||||
files: [],
|
||||
},
|
||||
},
|
||||
proj2: {
|
||||
@ -164,7 +174,7 @@ describe('findTargetProjectWithImport', () => {
|
||||
type: 'lib',
|
||||
data: {
|
||||
root: 'libs/proj2',
|
||||
files: ctx.fileMap['proj2'],
|
||||
files: [],
|
||||
},
|
||||
},
|
||||
proj: {
|
||||
@ -172,7 +182,7 @@ describe('findTargetProjectWithImport', () => {
|
||||
type: 'lib',
|
||||
data: {
|
||||
root: 'libs/proj',
|
||||
files: ctx.fileMap['proj'],
|
||||
files: [],
|
||||
},
|
||||
},
|
||||
proj1234: {
|
||||
@ -180,7 +190,7 @@ describe('findTargetProjectWithImport', () => {
|
||||
type: 'lib',
|
||||
data: {
|
||||
root: 'libs/proj1234',
|
||||
files: ctx.fileMap['proj1234'],
|
||||
files: [],
|
||||
},
|
||||
},
|
||||
proj123: {
|
||||
@ -188,7 +198,7 @@ describe('findTargetProjectWithImport', () => {
|
||||
type: 'lib',
|
||||
data: {
|
||||
root: 'libs/proj123',
|
||||
files: ctx.fileMap['proj123'],
|
||||
files: [],
|
||||
},
|
||||
},
|
||||
proj4ab: {
|
||||
@ -196,7 +206,7 @@ describe('findTargetProjectWithImport', () => {
|
||||
type: 'lib',
|
||||
data: {
|
||||
root: 'libs/proj4ab',
|
||||
files: ctx.fileMap['proj4ab'],
|
||||
files: [],
|
||||
},
|
||||
},
|
||||
proj5: {
|
||||
@ -204,7 +214,7 @@ describe('findTargetProjectWithImport', () => {
|
||||
type: 'lib',
|
||||
data: {
|
||||
root: 'libs/proj5',
|
||||
files: ctx.fileMap['proj5'],
|
||||
files: [],
|
||||
},
|
||||
},
|
||||
proj6: {
|
||||
@ -212,7 +222,7 @@ describe('findTargetProjectWithImport', () => {
|
||||
type: 'lib',
|
||||
data: {
|
||||
root: 'libs/proj6',
|
||||
files: ctx.fileMap['proj6'],
|
||||
files: [],
|
||||
},
|
||||
},
|
||||
proj7: {
|
||||
@ -220,7 +230,7 @@ describe('findTargetProjectWithImport', () => {
|
||||
type: 'lib',
|
||||
data: {
|
||||
root: 'libs/proj7',
|
||||
files: ctx.fileMap['proj7'],
|
||||
files: [],
|
||||
},
|
||||
},
|
||||
'proj1234-child': {
|
||||
@ -228,7 +238,7 @@ describe('findTargetProjectWithImport', () => {
|
||||
type: 'lib',
|
||||
data: {
|
||||
root: 'libs/proj1234-child',
|
||||
files: ctx.fileMap['proj1234-child'],
|
||||
files: [],
|
||||
},
|
||||
},
|
||||
};
|
||||
@ -319,11 +329,16 @@ describe('findTargetProjectWithImport', () => {
|
||||
'../proj/../index.ts',
|
||||
'libs/proj/src/index.ts'
|
||||
);
|
||||
const res5 = targetProjectLocator.findProjectWithImport(
|
||||
'../../../index.ts',
|
||||
'libs/proj/src/index.ts'
|
||||
);
|
||||
|
||||
expect(res1).toEqual('proj');
|
||||
expect(res2).toEqual('proj');
|
||||
expect(res3).toEqual('proj2');
|
||||
expect(res4).toEqual('proj');
|
||||
expect(res5).toEqual('rootProj');
|
||||
});
|
||||
|
||||
it('should be able to resolve a module by using tsConfig paths', () => {
|
||||
@ -512,10 +527,6 @@ describe('findTargetProjectWithImport (without tsconfig.json)', () => {
|
||||
file: 'libs/proj/index.ts',
|
||||
hash: 'some-hash',
|
||||
},
|
||||
{
|
||||
file: 'libs/proj/class.ts',
|
||||
hash: 'some-hash',
|
||||
},
|
||||
],
|
||||
proj2: [
|
||||
{
|
||||
@ -588,7 +599,7 @@ describe('findTargetProjectWithImport (without tsconfig.json)', () => {
|
||||
type: 'lib',
|
||||
data: {
|
||||
root: 'libs/proj3a',
|
||||
files: ctx.fileMap.proj3a,
|
||||
files: [],
|
||||
},
|
||||
},
|
||||
proj2: {
|
||||
@ -596,7 +607,7 @@ describe('findTargetProjectWithImport (without tsconfig.json)', () => {
|
||||
type: 'lib',
|
||||
data: {
|
||||
root: 'libs/proj2',
|
||||
files: ctx.fileMap.proj2,
|
||||
files: [],
|
||||
},
|
||||
},
|
||||
proj: {
|
||||
@ -604,7 +615,7 @@ describe('findTargetProjectWithImport (without tsconfig.json)', () => {
|
||||
type: 'lib',
|
||||
data: {
|
||||
root: 'libs/proj',
|
||||
files: ctx.fileMap.proj,
|
||||
files: [],
|
||||
},
|
||||
},
|
||||
proj1234: {
|
||||
@ -612,7 +623,7 @@ describe('findTargetProjectWithImport (without tsconfig.json)', () => {
|
||||
type: 'lib',
|
||||
data: {
|
||||
root: 'libs/proj1234',
|
||||
files: ctx.fileMap.proj1234,
|
||||
files: [],
|
||||
},
|
||||
},
|
||||
proj123: {
|
||||
@ -620,7 +631,7 @@ describe('findTargetProjectWithImport (without tsconfig.json)', () => {
|
||||
type: 'lib',
|
||||
data: {
|
||||
root: 'libs/proj123',
|
||||
files: ctx.fileMap.proj123,
|
||||
files: [],
|
||||
},
|
||||
},
|
||||
proj4ab: {
|
||||
@ -628,7 +639,7 @@ describe('findTargetProjectWithImport (without tsconfig.json)', () => {
|
||||
type: 'lib',
|
||||
data: {
|
||||
root: 'libs/proj4ab',
|
||||
files: ctx.fileMap.proj4ab,
|
||||
files: [],
|
||||
},
|
||||
},
|
||||
proj5: {
|
||||
@ -636,7 +647,7 @@ describe('findTargetProjectWithImport (without tsconfig.json)', () => {
|
||||
type: 'lib',
|
||||
data: {
|
||||
root: 'libs/proj5',
|
||||
files: ctx.fileMap.proj5,
|
||||
files: [],
|
||||
},
|
||||
},
|
||||
proj6: {
|
||||
@ -644,7 +655,7 @@ describe('findTargetProjectWithImport (without tsconfig.json)', () => {
|
||||
type: 'lib',
|
||||
data: {
|
||||
root: 'libs/proj6',
|
||||
files: ctx.fileMap.proj6,
|
||||
files: [],
|
||||
},
|
||||
},
|
||||
proj7: {
|
||||
@ -652,7 +663,7 @@ describe('findTargetProjectWithImport (without tsconfig.json)', () => {
|
||||
type: 'lib',
|
||||
data: {
|
||||
root: 'libs/proj7',
|
||||
files: ctx.fileMap.proj7,
|
||||
files: [],
|
||||
},
|
||||
},
|
||||
'proj1234-child': {
|
||||
@ -660,7 +671,7 @@ describe('findTargetProjectWithImport (without tsconfig.json)', () => {
|
||||
type: 'lib',
|
||||
data: {
|
||||
root: 'libs/proj1234-child',
|
||||
files: ctx.fileMap['proj1234-child'],
|
||||
files: [],
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@ -6,9 +6,13 @@ import {
|
||||
ProjectGraphExternalNode,
|
||||
ProjectGraphProjectNode,
|
||||
} from '../config/project-graph';
|
||||
import {
|
||||
createProjectRootMappings,
|
||||
findProjectForPath,
|
||||
} from '../project-graph/utils/find-project-for-path';
|
||||
|
||||
export class TargetProjectLocator {
|
||||
private allProjectsFiles = createProjectFileMappings(this.nodes);
|
||||
private projectRootMappings = createProjectRootMappings(this.nodes);
|
||||
private npmProjects = filterRootExternalDependencies(this.externalNodes);
|
||||
private tsConfig = this.getRootTsConfig();
|
||||
private paths = this.tsConfig.config?.compilerOptions?.paths;
|
||||
@ -145,19 +149,10 @@ export class TargetProjectLocator {
|
||||
const normalizedResolvedModule = resolvedModule.startsWith('./')
|
||||
? resolvedModule.substring(2)
|
||||
: resolvedModule;
|
||||
|
||||
// for wildcard paths, we need to find file that starts with resolvedModule
|
||||
if (normalizedResolvedModule.endsWith('*')) {
|
||||
const matchingFile = Object.keys(this.allProjectsFiles).find((f) =>
|
||||
f.startsWith(normalizedResolvedModule.slice(0, -1))
|
||||
);
|
||||
return matchingFile && this.allProjectsFiles[matchingFile];
|
||||
}
|
||||
|
||||
return (
|
||||
this.allProjectsFiles[normalizedResolvedModule] ||
|
||||
this.allProjectsFiles[`${normalizedResolvedModule}/index`]
|
||||
const importedProject = this.findMatchingProjectFiles(
|
||||
normalizedResolvedModule
|
||||
);
|
||||
return importedProject ? importedProject.name : void 0;
|
||||
}
|
||||
|
||||
private getAbsolutePath(path: string) {
|
||||
@ -183,8 +178,8 @@ export class TargetProjectLocator {
|
||||
}
|
||||
|
||||
private findMatchingProjectFiles(file: string) {
|
||||
const project = this.allProjectsFiles[file];
|
||||
return project && this.nodes[project];
|
||||
const project = findProjectForPath(file, this.projectRootMappings);
|
||||
return this.nodes[project];
|
||||
}
|
||||
}
|
||||
|
||||
@ -206,78 +201,3 @@ function filterRootExternalDependencies(
|
||||
}
|
||||
return nodes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated This function will be removed in v16. Use {@link createProjectFileMappings} instead.
|
||||
*
|
||||
* Mapps the project root paths to the project name
|
||||
* @param nodes
|
||||
* @returns
|
||||
*/
|
||||
export function createProjectRootMappings(
|
||||
nodes: Record<string, ProjectGraphProjectNode>
|
||||
): Map<string, string> {
|
||||
const projectRootMappings = new Map<string, string>();
|
||||
for (const projectName of Object.keys(nodes)) {
|
||||
const root = nodes[projectName].data.root;
|
||||
projectRootMappings.set(
|
||||
root && root.endsWith('/') ? root.substring(0, root.length - 1) : root,
|
||||
projectName
|
||||
);
|
||||
}
|
||||
return projectRootMappings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Strips the file extension from the file path
|
||||
* @param file
|
||||
* @returns
|
||||
*/
|
||||
export function removeExt(file: string): string {
|
||||
return file.replace(/(?<!(^|\/))\.[^/.]+$/, '');
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps the file paths to the project name, both with and without the file extension
|
||||
* apps/myapp/src/main.ts -> { 'apps/myapp/src/main': 'myapp', 'apps/myapp/src/main.ts': 'myapp' }
|
||||
* @param projectGraph
|
||||
* @returns
|
||||
*/
|
||||
export function createProjectFileMappings(
|
||||
nodes: Record<string, ProjectGraphProjectNode>
|
||||
): Record<string, string> {
|
||||
const result: Record<string, string> = {};
|
||||
Object.entries(nodes).forEach(([name, node]) => {
|
||||
node.data.files.forEach(({ file }) => {
|
||||
const fileName = removeExt(file);
|
||||
result[fileName] = name;
|
||||
result[file] = name;
|
||||
});
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated This function will be removed in v16. Use {@link createProjectFileMappings} instead.
|
||||
*
|
||||
* Locates a project in projectRootMap based on a file within it
|
||||
* @param filePath path that is inside of projectName
|
||||
* @param projectRootMap Map<projectRoot, projectName>
|
||||
*/
|
||||
export function findMatchingProjectForPath(
|
||||
filePath: string,
|
||||
projectRootMap: Map<string, string>
|
||||
): string | null {
|
||||
for (
|
||||
let currentPath = filePath;
|
||||
currentPath != dirname(currentPath);
|
||||
currentPath = dirname(currentPath)
|
||||
) {
|
||||
const p = projectRootMap.get(currentPath);
|
||||
if (p) {
|
||||
return p;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -2,6 +2,7 @@ import { workspaceRoot } from './workspace-root';
|
||||
import { existsSync } from 'fs';
|
||||
import { dirname, join } from 'path';
|
||||
import type * as ts from 'typescript';
|
||||
import type { Node, SyntaxKind } from 'typescript';
|
||||
|
||||
const normalizedAppRoot = workspaceRoot.replace(/\\/g, '/');
|
||||
|
||||
@ -106,15 +107,15 @@ export function getRootTsConfigPath(): string | null {
|
||||
}
|
||||
|
||||
export function findNodes(
|
||||
node: ts.Node,
|
||||
kind: ts.SyntaxKind | ts.SyntaxKind[],
|
||||
node: Node,
|
||||
kind: SyntaxKind | SyntaxKind[],
|
||||
max = Infinity
|
||||
): ts.Node[] {
|
||||
): Node[] {
|
||||
if (!node || max == 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const arr: ts.Node[] = [];
|
||||
const arr: Node[] = [];
|
||||
const hasMatch = Array.isArray(kind)
|
||||
? kind.includes(node.kind)
|
||||
: node.kind === kind;
|
||||
|
||||
@ -58,9 +58,9 @@ function reactWebpack5Check(options: CommonNxStorybookConfig) {
|
||||
It looks like you use Webpack 5 but your Storybook setup is not configured to leverage that
|
||||
and thus falls back to Webpack 4.
|
||||
Make sure you upgrade your Storybook config to use Webpack 5.
|
||||
|
||||
|
||||
- https://gist.github.com/shilman/8856ea1786dcd247139b47b270912324#upgrade
|
||||
|
||||
|
||||
`);
|
||||
}
|
||||
}
|
||||
|
||||
@ -21,7 +21,6 @@ export {
|
||||
readPackageJson,
|
||||
} from 'nx/src/project-graph/file-utils';
|
||||
export { ProjectGraphCache } from 'nx/src/project-graph/nx-deps-cache';
|
||||
export { findNodes } from 'nx/src/utils/typescript';
|
||||
export {
|
||||
readJsonInTree,
|
||||
updateJsonInTree,
|
||||
@ -35,6 +34,7 @@ export {
|
||||
getProjectConfig,
|
||||
addParameterToConstructor,
|
||||
createOrUpdate,
|
||||
findNodes, // TODO(v16): remove this
|
||||
updatePackageJsonDependencies,
|
||||
readWorkspace,
|
||||
renameSyncInTree,
|
||||
|
||||
@ -2,12 +2,13 @@ import { joinPathFragments, logger } from '@nrwl/devkit';
|
||||
import { workspaceRoot } from 'nx/src/utils/workspace-root';
|
||||
import { dirname, join, relative, resolve } from 'path';
|
||||
import { readCachedProjectGraph } from 'nx/src/project-graph/project-graph';
|
||||
import {
|
||||
getProjectNameFromDirPath,
|
||||
getSourceDirOfDependentProjects,
|
||||
} from 'nx/src/utils/project-graph-utils';
|
||||
import { getSourceDirOfDependentProjects } from 'nx/src/utils/project-graph-utils';
|
||||
import { existsSync, lstatSync, readdirSync, readFileSync } from 'fs';
|
||||
import ignore, { Ignore } from 'ignore';
|
||||
import {
|
||||
createProjectRootMappings,
|
||||
findProjectForPath,
|
||||
} from 'nx/src/project-graph/utils/find-project-for-path';
|
||||
|
||||
function configureIgnore() {
|
||||
let ig: Ignore;
|
||||
@ -31,14 +32,21 @@ export function createGlobPatternsForDependencies(
|
||||
let ig = configureIgnore();
|
||||
const filenameRelativeToWorkspaceRoot = relative(workspaceRoot, dirPath);
|
||||
const projectGraph = readCachedProjectGraph();
|
||||
const projectRootMappings = createProjectRootMappings(projectGraph.nodes);
|
||||
|
||||
// find the project
|
||||
let projectName;
|
||||
try {
|
||||
projectName = getProjectNameFromDirPath(
|
||||
projectName = findProjectForPath(
|
||||
filenameRelativeToWorkspaceRoot,
|
||||
projectGraph
|
||||
projectRootMappings
|
||||
);
|
||||
|
||||
if (!projectName) {
|
||||
throw new Error(
|
||||
`Could not find any project containing the file "${filenameRelativeToWorkspaceRoot}" among it's project files`
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
throw new Error(
|
||||
`createGlobPatternsForDependencies: Error when trying to determine main project.\n${e?.message}`
|
||||
|
||||
@ -5,6 +5,7 @@ import { dirname, join } from 'path';
|
||||
import type * as ts from 'typescript';
|
||||
export { compileTypeScript } from './typescript/compilation';
|
||||
export type { TypeScriptCompilationOptions } from './typescript/compilation';
|
||||
export { findNodes } from './typescript/find-nodes'; // TODO(v16): remove this
|
||||
export { getSourceNodes } from './typescript/get-source-nodes';
|
||||
|
||||
const normalizedAppRoot = workspaceRoot.replace(/\\/g, '/');
|
||||
|
||||
16
packages/workspace/src/utilities/typescript/find-nodes.ts
Normal file
16
packages/workspace/src/utilities/typescript/find-nodes.ts
Normal file
@ -0,0 +1,16 @@
|
||||
import { findNodes as _findNodes } from 'nx/src/utils/typescript';
|
||||
import type { Node, SyntaxKind } from 'typescript';
|
||||
|
||||
// TODO(v16): This should be removed.
|
||||
|
||||
/**
|
||||
* @deprecated This function is deprecated and no longer supported.
|
||||
*/
|
||||
export function findNodes(
|
||||
node: Node,
|
||||
kind: SyntaxKind | SyntaxKind[],
|
||||
max = Infinity
|
||||
) {
|
||||
console.warn('"findNodes" is deprecated and no longer supported.');
|
||||
return _findNodes(node, kind, max);
|
||||
}
|
||||
@ -18,10 +18,15 @@ import {
|
||||
Tree,
|
||||
} from '@angular-devkit/schematics';
|
||||
import * as ts from 'typescript';
|
||||
import { parseJson, serializeJson, FileData } from '@nrwl/devkit';
|
||||
import {
|
||||
parseJson,
|
||||
ProjectConfiguration,
|
||||
serializeJson,
|
||||
FileData,
|
||||
} from '@nrwl/devkit';
|
||||
import { getWorkspacePath } from './cli-config-utils';
|
||||
import { extname, join, normalize, Path } from '@angular-devkit/core';
|
||||
import type { NxJsonConfiguration } from '@nrwl/devkit';
|
||||
import type { NxJsonConfiguration, ProjectsConfigurations } from '@nrwl/devkit';
|
||||
import { addInstallTask } from './rules/add-install-task';
|
||||
import { findNodes } from 'nx/src/utils/typescript';
|
||||
import { getSourceNodes } from '../utilities/typescript/get-source-nodes';
|
||||
@ -66,6 +71,7 @@ export function sortObjectByKeys(obj: unknown) {
|
||||
}, {});
|
||||
}
|
||||
|
||||
export { findNodes } from '../utilities/typescript/find-nodes'; // TODO(v16): Remove this
|
||||
export { getSourceNodes } from '../utilities/typescript/get-source-nodes';
|
||||
|
||||
export interface Change {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user