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