feat(core): remove readFile argument from createProjectGraph (#5206)

* feat(core): deprecate creating a project graph from a host

* feat(core): remove readFile argument from createProjectGraph
This commit is contained in:
Jason Jean 2021-04-06 13:44:09 -04:00 committed by GitHub
parent 4dac0a2122
commit bfb194843f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 384 additions and 244 deletions

View File

@ -6,6 +6,16 @@ import {
} from '@nrwl/workspace';
import { callRule, createEmptyWorkspace } from '@nrwl/workspace/testing';
import { runMigration } from '../../utils/testing';
import {
DependencyType,
ProjectGraph,
} from '@nrwl/workspace/src/core/project-graph';
let projectGraph: ProjectGraph;
jest.mock('@nrwl/workspace/src/core/project-graph', () => ({
...jest.requireActual('@nrwl/workspace/src/core/project-graph'),
createProjectGraph: jest.fn().mockImplementation(() => projectGraph),
}));
describe('add-template-support-and-presets-to-eslint', () => {
describe('tslint-only workspace', () => {
@ -71,6 +81,66 @@ describe('add-template-support-and-presets-to-eslint', () => {
}),
tree
);
projectGraph = {
nodes: {
app1: {
name: 'app1',
type: 'app',
data: {
files: [],
root: 'apps/app1',
},
},
app2: {
name: 'app2',
type: 'app',
data: {
files: [],
root: 'apps/app2',
},
},
lib1: {
name: 'lib1',
type: 'app',
data: {
files: [],
root: 'apps/lib1',
},
},
'npm:@angular/core': {
name: 'npm:@angular/core',
type: 'npm',
data: {
files: [],
},
},
},
dependencies: {
app1: [
{
type: DependencyType.static,
source: 'app1',
target: 'npm:@angular/core',
},
],
app2: [
{
type: DependencyType.static,
source: 'app2',
target: 'npm:@angular/core',
},
],
lib1: [
{
type: DependencyType.static,
source: 'lib1',
target: 'npm:@angular/core',
},
],
'npm:@angular/core': [],
},
};
});
it('should do nothing', async () => {

View File

@ -11,7 +11,7 @@ import {
} from '@nrwl/workspace';
import { join } from 'path';
import { offsetFromRoot } from '@nrwl/devkit';
import { getFullProjectGraphFromHost } from '@nrwl/workspace/src/utils/ast-utils';
import { createProjectGraph } from '@nrwl/workspace/src/core/project-graph';
/**
* It was decided with Jason that we would do a simple replacement in this migration
@ -40,7 +40,7 @@ function addHTMLPatternToBuilderConfig(
}
function updateProjectESLintConfigsAndBuilders(host: Tree): Rule {
const graph = getFullProjectGraphFromHost(host);
const graph = createProjectGraph(undefined, undefined, undefined, false);
/**
* Make sure user is already using ESLint and is up to date with

View File

@ -13,7 +13,7 @@ import enforceModuleBoundaries, {
import { TargetProjectLocator } from '@nrwl/workspace/src/core/target-project-locator';
import { readFileSync } from 'fs';
jest.mock('fs', () => require('memfs').fs);
jest.mock('@nrwl/workspace/src/utils/app-root', () => ({
jest.mock('../../../workspace/src/utilities/app-root', () => ({
appRootPath: '/root',
}));
@ -1166,8 +1166,7 @@ function runRule(
(global as any).npmScope = 'mycompany';
(global as any).projectGraph = projectGraph;
(global as any).targetProjectLocator = new TargetProjectLocator(
projectGraph.nodes,
(path) => readFileSync(join('/root', path)).toString()
projectGraph.nodes
);
const config = {

View File

@ -3,6 +3,16 @@ import { SchematicTestRunner } from '@angular-devkit/schematics/testing';
import * as path from 'path';
import { createEmptyWorkspace } from '@nrwl/workspace/testing';
import { createApp, createLib, createWebApp } from '../utils/testing';
import {
DependencyType,
ProjectGraph,
} from '@nrwl/workspace/src/core/project-graph';
let projectGraph: ProjectGraph;
jest.mock('@nrwl/workspace/src/core/project-graph', () => ({
...jest.requireActual('@nrwl/workspace/src/core/project-graph'),
createProjectGraph: jest.fn().mockImplementation(() => projectGraph),
}));
describe('Migrate babel setup', () => {
let tree: Tree;
@ -24,6 +34,51 @@ describe('Migrate babel setup', () => {
},
})
);
projectGraph = {
nodes: {
demo: {
name: 'demo',
type: 'app',
data: {
root: 'apps/demo',
files: [],
},
},
ui: {
name: 'ui',
type: 'lib',
data: {
root: 'libs/ui',
files: [],
},
},
'npm:react': {
name: 'npm:react',
type: 'npm',
data: {
files: [],
packageName: 'react',
},
},
},
dependencies: {
demo: [
{
type: DependencyType.static,
source: 'demo',
target: 'npm:react',
},
],
ui: [
{
type: DependencyType.static,
source: 'ui',
target: 'npm:react',
},
],
'npm:react': [],
},
};
});
it(`should create .babelrc for projects without them`, async () => {
@ -54,6 +109,8 @@ describe('Migrate babel setup', () => {
it(`should not migrate non-React projects`, async () => {
tree = await createWebApp(tree, 'demo');
projectGraph.dependencies.demo = [];
tree = await schematicRunner
.runSchematicAsync('babelrc-9.4.0', {}, tree)
.toPromise();

View File

@ -4,13 +4,13 @@ import {
SchematicContext,
Tree,
} from '@angular-devkit/schematics';
import { getFullProjectGraphFromHost } from '@nrwl/workspace/src/utils/ast-utils';
import {
stripIndent,
stripIndents,
} from '@angular-devkit/core/src/utils/literals';
import { initRootBabelConfig } from '../utils/rules';
import { addDepsToPackageJson, formatFiles } from '@nrwl/workspace';
import { createProjectGraph } from '@nrwl/workspace/src/core/project-graph';
let addedEmotionPreset = false;
@ -25,7 +25,12 @@ export default function update(): Rule {
return (host: Tree, context: SchematicContext) => {
const updates = [];
const conflicts: Array<[string, string]> = [];
const projectGraph = getFullProjectGraphFromHost(host);
const projectGraph = createProjectGraph(
undefined,
undefined,
undefined,
false
);
if (host.exists('/babel.config.json')) {
context.logger.info(
`

View File

@ -1,6 +1,16 @@
import { createTreeWithEmptyWorkspace } from '@nrwl/devkit/testing';
import { readJson, Tree } from '@nrwl/devkit';
import { createBabelrcForWorkspaceLibs } from './create-babelrc-for-workspace-libs';
import {
DependencyType,
ProjectGraph,
} from '@nrwl/workspace/src/core/project-graph';
let projectGraph: ProjectGraph;
jest.mock('@nrwl/workspace/src/core/project-graph', () => ({
...jest.requireActual('@nrwl/workspace/src/core/project-graph'),
createProjectGraph: jest.fn().mockImplementation(() => projectGraph),
}));
describe('Create missing .babelrc files', () => {
let tree: Tree;
@ -53,6 +63,52 @@ describe('Create missing .babelrc files', () => {
);
tree.write('apps/webapp/index.ts', `import '@proj/weblib';`);
projectGraph = {
nodes: {
webapp: {
name: 'webapp',
type: 'app',
data: {
files: [],
root: 'apps/webapp',
},
},
nodeapp: {
name: 'nodeapp',
type: 'app',
data: {
files: [],
root: 'apps/nodeapp',
},
},
weblib: {
name: 'weblib',
type: 'lib',
data: {
files: [],
root: 'libs/weblib',
},
},
nodelib: {
name: 'nodelib',
type: 'lib',
data: {
files: [],
root: 'libs/nodelib',
},
},
},
dependencies: {
webapp: [
{
type: DependencyType.static,
source: 'webapp',
target: 'weblib',
},
],
},
};
await createBabelrcForWorkspaceLibs(tree);
expect(readJson(tree, 'libs/weblib/.babelrc')).toMatchObject({

View File

@ -1,11 +1,15 @@
import { formatFiles, getProjects, Tree } from '@nrwl/devkit';
import { reverse } from '@nrwl/workspace/src/core/project-graph';
import { createProjectGraphFromTree } from '@nrwl/workspace/src/utilities/create-project-graph-from-tree';
import { hasDependentAppUsingWebBuild } from '@nrwl/web/src/migrations/update-11-5-2/utils';
import {
createProjectGraph,
reverse,
} from '@nrwl/workspace/src/core/project-graph';
import { hasDependentAppUsingWebBuild } from './utils';
export async function createBabelrcForWorkspaceLibs(host: Tree) {
const projects = getProjects(host);
const graph = reverse(createProjectGraphFromTree(host));
const graph = reverse(
createProjectGraph(undefined, undefined, undefined, false)
);
for (const [name, p] of projects.entries()) {
if (!hasDependentAppUsingWebBuild(name, graph, projects)) {

View File

@ -3,13 +3,11 @@ import {
ProjectGraphContext,
ProjectGraphNodeRecords,
} from '../project-graph-models';
import { FileRead } from '../../file-utils';
export interface BuildDependencies {
(
ctx: ProjectGraphContext,
nodes: ProjectGraphNodeRecords,
addDependency: AddProjectDependency,
fileRead: FileRead
addDependency: AddProjectDependency
): void;
}

View File

@ -1,5 +1,5 @@
import { buildExplicitPackageJsonDependencies } from '@nrwl/workspace/src/core/project-graph/build-dependencies/explicit-package-json-dependencies';
import { fs, vol } from 'memfs';
import { vol } from 'memfs';
import {
AddProjectDependency,
DependencyType,
@ -8,7 +8,6 @@ import {
} from '../project-graph-models';
import { createProjectFileMap } from '../../file-graph';
import { readWorkspaceFiles } from '../../file-utils';
import { appRootPath } from '../../../utilities/app-root';
jest.mock('../../../utilities/app-root', () => ({
appRootPath: '/root',
@ -107,9 +106,7 @@ describe('explicit package json dependencies', () => {
}
);
buildExplicitPackageJsonDependencies(ctx, projects, addDependency, (s) => {
return fs.readFileSync(`${appRootPath}/${s}`).toString();
});
buildExplicitPackageJsonDependencies(ctx, projects, addDependency);
expect(dependencyMap).toEqual({
proj: [

View File

@ -4,20 +4,19 @@ import {
ProjectGraphContext,
ProjectGraphNodeRecords,
} from '../project-graph-models';
import { FileRead } from '../../file-utils';
import { defaultFileRead } from '../../file-utils';
import { parseJsonWithComments } from '@nrwl/workspace/src/utilities/fileutils';
import { joinPathFragments } from '@nrwl/devkit';
export function buildExplicitPackageJsonDependencies(
ctx: ProjectGraphContext,
nodes: ProjectGraphNodeRecords,
addDependency: AddProjectDependency,
fileRead: FileRead
addDependency: AddProjectDependency
) {
Object.keys(ctx.fileMap).forEach((source) => {
Object.values(ctx.fileMap[source]).forEach((f) => {
if (isPackageJsonAtProjectRoot(nodes, f.file)) {
processPackageJson(source, f.file, nodes, addDependency, fileRead);
processPackageJson(source, f.file, nodes, addDependency);
}
});
});
@ -38,11 +37,10 @@ function processPackageJson(
sourceProject: string,
fileName: string,
nodes: ProjectGraphNodeRecords,
addDependency: AddProjectDependency,
fileRead: FileRead
addDependency: AddProjectDependency
) {
try {
const deps = readDeps(parseJsonWithComments(fileRead(fileName)));
const deps = readDeps(parseJsonWithComments(defaultFileRead(fileName)));
deps.forEach((d) => {
// package.json refers to another project in the monorepo
if (nodes[d]) {

View File

@ -196,9 +196,7 @@ describe('explicit project dependencies', () => {
}
);
buildExplicitTypeScriptDependencies(ctx, projects, addDependency, (s) => {
return fs.readFileSync(`${appRootPath}/${s}`).toString();
});
buildExplicitTypeScriptDependencies(ctx, projects, addDependency);
expect(dependencyMap).toEqual({
proj1234: [

View File

@ -6,16 +6,14 @@ import {
} from '../project-graph-models';
import { TypeScriptImportLocator } from './typescript-import-locator';
import { TargetProjectLocator } from '../../target-project-locator';
import { FileRead } from '../../file-utils';
export function buildExplicitTypeScriptDependencies(
ctx: ProjectGraphContext,
nodes: ProjectGraphNodeRecords,
addDependency: AddProjectDependency,
fileRead: FileRead
addDependency: AddProjectDependency
) {
const importLocator = new TypeScriptImportLocator(fileRead);
const targetProjectLocator = new TargetProjectLocator(nodes, fileRead);
const importLocator = new TypeScriptImportLocator();
const targetProjectLocator = new TargetProjectLocator(nodes);
Object.keys(ctx.fileMap).forEach((source) => {
Object.values(ctx.fileMap[source]).forEach((f) => {
importLocator.fromFile(

View File

@ -2,14 +2,14 @@ import type * as ts from 'typescript';
import * as path from 'path';
import { DependencyType } from '../project-graph-models';
import { stripSourceCode } from '../../../utilities/strip-source-code';
import { FileRead } from '../../file-utils';
import { defaultFileRead } from '../../file-utils';
let tsModule: any;
export class TypeScriptImportLocator {
private readonly scanner: ts.Scanner;
constructor(private readonly fileRead: FileRead) {
constructor() {
tsModule = require('typescript');
this.scanner = tsModule.createScanner(tsModule.ScriptTarget.Latest, false);
}
@ -31,7 +31,7 @@ export class TypeScriptImportLocator {
) {
return;
}
const content = this.fileRead(filePath);
const content = defaultFileRead(filePath);
const strippedContent = stripSourceCode(this.scanner, content);
if (strippedContent !== '') {
const tsFile = tsModule.createSourceFile(

View File

@ -1,6 +1,5 @@
import { AddProjectNode, ProjectGraphContext } from '../project-graph-models';
import { FileRead } from '../../file-utils';
export interface BuildNodes {
(ctx: ProjectGraphContext, addNode: AddProjectNode, fileRead: FileRead): void;
(ctx: ProjectGraphContext, addNode: AddProjectNode): void;
}

View File

@ -1,13 +1,14 @@
import * as stripJsonComments from 'strip-json-comments';
import { ProjectGraphContext, AddProjectNode } from '../project-graph-models';
import { FileRead } from '../../file-utils';
import { AddProjectNode, ProjectGraphContext } from '../project-graph-models';
import { defaultFileRead } from '../../file-utils';
export function buildNpmPackageNodes(
ctx: ProjectGraphContext,
addNode: AddProjectNode,
fileRead: FileRead
addNode: AddProjectNode
) {
const packageJson = JSON.parse(stripJsonComments(fileRead('package.json')));
const packageJson = JSON.parse(
stripJsonComments(defaultFileRead('package.json'))
);
const deps = {
...packageJson.dependencies,
...packageJson.devDependencies,

View File

@ -1,9 +1,11 @@
import { AddProjectNode, ProjectGraphContext } from '../project-graph-models';
import { FileRead } from '../../file-utils';
import { defaultFileRead } from '../../file-utils';
function convertNpmScriptsToTargets(projectRoot: string, fileRead: FileRead) {
function convertNpmScriptsToTargets(projectRoot: string) {
try {
const packageJsonString = fileRead(`${projectRoot}/package.json`);
const packageJsonString = defaultFileRead(
`${projectRoot}/package.json`
).toString();
const parsedPackagedJson = JSON.parse(packageJsonString);
const res = {};
// handle no scripts
@ -21,52 +23,53 @@ function convertNpmScriptsToTargets(projectRoot: string, fileRead: FileRead) {
}
}
export function buildWorkspaceProjectNodes(fileRead: FileRead) {
return (ctx: ProjectGraphContext, addNode: AddProjectNode) => {
const toAdd = [];
export function buildWorkspaceProjectNodes(
ctx: ProjectGraphContext,
addNode: AddProjectNode
) {
const toAdd = [];
Object.keys(ctx.fileMap).forEach((key) => {
const p = ctx.workspaceJson.projects[key];
if (!p.targets) {
p.targets = convertNpmScriptsToTargets(p.root, fileRead);
}
Object.keys(ctx.fileMap).forEach((key) => {
const p = ctx.workspaceJson.projects[key];
if (!p.targets) {
p.targets = convertNpmScriptsToTargets(p.root);
}
// TODO, types and projectType should allign
const projectType =
p.projectType === 'application'
? key.endsWith('-e2e')
? 'e2e'
: 'app'
: 'lib';
const tags =
ctx.nxJson.projects && ctx.nxJson.projects[key]
? ctx.nxJson.projects[key].tags || []
: [];
// TODO, types and projectType should allign
const projectType =
p.projectType === 'application'
? key.endsWith('-e2e')
? 'e2e'
: 'app'
: 'lib';
const tags =
ctx.nxJson.projects && ctx.nxJson.projects[key]
? ctx.nxJson.projects[key].tags || []
: [];
toAdd.push({
name: key,
type: projectType,
data: {
...p,
tags,
files: ctx.fileMap[key],
},
});
toAdd.push({
name: key,
type: projectType,
data: {
...p,
tags,
files: ctx.fileMap[key],
},
});
});
// Sort by root directory length (do we need this?)
toAdd.sort((a, b) => {
if (!a.data.root) return -1;
if (!b.data.root) return -1;
return a.data.root.length > b.data.root.length ? -1 : 1;
});
// Sort by root directory length (do we need this?)
toAdd.sort((a, b) => {
if (!a.data.root) return -1;
if (!b.data.root) return -1;
return a.data.root.length > b.data.root.length ? -1 : 1;
});
toAdd.forEach((n) => {
addNode({
name: n.name,
type: n.type,
data: n.data,
});
toAdd.forEach((n) => {
addNode({
name: n.name,
type: n.type,
data: n.data,
});
};
});
}

View File

@ -1,9 +1,7 @@
import { assertWorkspaceValidity } from '../assert-workspace-validity';
import { createProjectFileMap, ProjectFileMap } from '../file-graph';
import {
defaultFileRead,
FileData,
FileRead,
filesChanged,
readNxJson,
readWorkspaceFiles,
@ -37,7 +35,6 @@ export function createProjectGraph(
workspaceJson = readWorkspaceJson(),
nxJson = readNxJson(),
workspaceFiles = readWorkspaceFiles(),
fileRead: FileRead = defaultFileRead,
cache: false | ProjectGraphCache = readCache(),
shouldCache: boolean = true
): ProjectGraph {
@ -63,7 +60,6 @@ export function createProjectGraph(
};
const projectGraph = buildProjectGraph(
ctx,
fileRead,
diff.partiallyConstructedProjectGraph
);
if (shouldCache) {
@ -76,7 +72,7 @@ export function createProjectGraph(
nxJson: normalizedNxJson,
fileMap: projectFileMap,
};
const projectGraph = buildProjectGraph(ctx, fileRead, null);
const projectGraph = buildProjectGraph(ctx, null);
if (shouldCache) {
writeCache(rootFiles, projectGraph);
}
@ -97,13 +93,12 @@ function buildProjectGraph(
workspaceJson: any;
fileMap: ProjectFileMap;
},
fileRead: FileRead,
projectGraph: ProjectGraph
) {
performance.mark('build project graph:start');
const builder = new ProjectGraphBuilder(projectGraph);
const buildNodesFns: BuildNodes[] = [
buildWorkspaceProjectNodes(fileRead),
buildWorkspaceProjectNodes,
buildNpmPackageNodes,
];
const buildDependenciesFns: BuildDependencies[] = [
@ -111,9 +106,9 @@ function buildProjectGraph(
buildImplicitProjectDependencies,
buildExplicitPackageJsonDependencies,
];
buildNodesFns.forEach((f) => f(ctx, builder.addNode.bind(builder), fileRead));
buildNodesFns.forEach((f) => f(ctx, builder.addNode.bind(builder)));
buildDependenciesFns.forEach((f) =>
f(ctx, builder.nodes, builder.addDependency.bind(builder), fileRead)
f(ctx, builder.nodes, builder.addDependency.bind(builder))
);
const r = builder.build();
performance.mark('build project graph:end');

View File

@ -1,5 +1,5 @@
import { resolveModuleByImport } from '../utilities/typescript';
import { defaultFileRead, FileRead, normalizedProjectRoot } from './file-utils';
import { defaultFileRead, normalizedProjectRoot } from './file-utils';
import {
ProjectGraphNode,
ProjectGraphNodeRecords,
@ -31,15 +31,12 @@ export class TargetProjectLocator {
private npmProjects = this.sortedProjects.filter(isNpmProject);
private tsConfigPath = this.getRootTsConfigPath();
private absTsConfigPath = join(appRootPath, this.tsConfigPath);
private paths = parseJsonWithComments(this.fileRead(this.tsConfigPath))
private paths = parseJsonWithComments(defaultFileRead(this.tsConfigPath))
?.compilerOptions?.paths;
private typescriptResolutionCache = new Map<string, string | null>();
private npmResolutionCache = new Map<string, string | null>();
constructor(
private nodes: ProjectGraphNodeRecords,
private fileRead: FileRead = defaultFileRead
) {}
constructor(private nodes: ProjectGraphNodeRecords) {}
/**
* Find a project based on its import
@ -132,7 +129,7 @@ export class TargetProjectLocator {
private getRootTsConfigPath() {
try {
this.fileRead('tsconfig.base.json');
defaultFileRead('tsconfig.base.json');
return 'tsconfig.base.json';
} catch (e) {
return 'tsconfig.json';

View File

@ -7,6 +7,12 @@ import { Schema } from '../schema';
import { checkDependencies } from './check-dependencies';
import { createTreeWithEmptyWorkspace } from '@nrwl/devkit/testing';
import { libraryGenerator } from '../../library/library';
import { DependencyType, ProjectGraph } from '../../../core/project-graph';
let projectGraph: ProjectGraph;
jest.mock('../../../core/project-graph', () => ({
...jest.requireActual('../../../core/project-graph'),
createProjectGraph: jest.fn().mockImplementation(() => projectGraph),
}));
describe('checkDependencies', () => {
let tree: Tree;
@ -27,6 +33,36 @@ describe('checkDependencies', () => {
await libraryGenerator(tree, {
name: 'my-source',
});
projectGraph = {
nodes: {
'my-source': {
name: 'my-source',
type: 'lib',
data: {
files: [],
root: 'libs/my-source',
},
},
'my-dependent': {
name: 'my-dependent',
type: 'lib',
data: {
files: [],
root: 'libs/my-dependent',
},
},
},
dependencies: {
'my-source': [
{
type: DependencyType.static,
source: 'my-dependent',
target: 'my-source',
},
],
},
};
});
describe('static dependencies', () => {
@ -90,6 +126,10 @@ describe('checkDependencies', () => {
});
it('should not error if there are no dependents', async () => {
projectGraph = {
nodes: projectGraph.nodes,
dependencies: {},
};
expect(() => {
checkDependencies(tree, schema);
}).not.toThrow();

View File

@ -1,37 +1,39 @@
import { Tree } from '@nrwl/devkit';
import {
createProjectGraph,
onlyWorkspaceProjects,
ProjectGraph,
reverse,
} from '../../../core/project-graph';
import { Schema } from '../schema';
import { createProjectGraphFromTree } from '../../../utilities/create-project-graph-from-tree';
/**
* Check whether the project to be removed is depended on by another project
*
* Throws an error if the project is in use, unless the `--forceRemove` option is used.
*/
export function checkDependencies(tree: Tree, schema: Schema) {
export function checkDependencies(_, schema: Schema) {
if (schema.forceRemove) {
return;
}
const graph: ProjectGraph = createProjectGraphFromTree(tree);
const graph: ProjectGraph = createProjectGraph(
undefined,
undefined,
undefined,
false
);
const reverseGraph = onlyWorkspaceProjects(reverse(graph));
const deps = reverseGraph.dependencies[schema.projectName] || [];
if (deps.length === 0) {
return;
if (deps.length > 0) {
throw new Error(
`${
schema.projectName
} is still depended on by the following projects:\n${deps
.map((x) => x.target)
.join('\n')}`
);
}
throw new Error(
`${
schema.projectName
} is still depended on by the following projects:\n${deps
.map((x) => x.target)
.join('\n')}`
);
}

View File

@ -1068,9 +1068,7 @@ function runRule(
`${process.cwd()}/proj`,
'mycompany',
projectGraph,
new TargetProjectLocator(projectGraph.nodes, (path) =>
readFileSync(join('/root', path)).toString()
)
new TargetProjectLocator(projectGraph.nodes)
);
return rule.apply(sourceFile);
}

View File

@ -1,37 +1,10 @@
import {
getWorkspacePath,
readJson,
Tree,
visitNotIgnoredFiles,
} from '@nrwl/devkit';
import { Tree } from '@nrwl/devkit';
import { createProjectGraph } from '../core/project-graph/project-graph';
import { FileData } from '../core/file-utils';
import { extname } from 'path';
// TODO(v13): remove this deprecated method
/**
* @deprecated This method is deprecated and is synonymous to {@link createProjectGraph}()
*/
export function createProjectGraphFromTree(tree: Tree) {
const workspaceJson = readJson(tree, getWorkspacePath(tree));
const nxJson = readJson(tree, 'nx.json');
const files: FileData[] = [];
visitNotIgnoredFiles(tree, '', (file) => {
files.push({
file,
ext: extname(file),
hash: '',
});
});
const readFile = (path) => {
return tree.read(path).toString('utf-8');
};
return createProjectGraph(
workspaceJson,
nxJson,
files,
readFile,
false,
false
);
return createProjectGraph(undefined, undefined, undefined, false);
}

View File

@ -26,7 +26,7 @@ import {
onlyWorkspaceProjects,
ProjectGraph,
} from '../core/project-graph';
import { FileData, FileRead } from '../core/file-utils';
import { FileData } from '../core/file-utils';
import { extname, join, normalize, Path } from '@angular-devkit/core';
import { NxJson, NxJsonProjectConfig } from '../core/shared-interfaces';
import { addInstallTask } from './rules/add-install-task';
@ -357,57 +357,27 @@ export function readJsonInTree<T = any>(host: Tree, path: string): T {
}
}
// TODO(v13): remove this deprecated method
/**
* @deprecated This method is deprecated and is synonymous to {@link onlyWorkspaceProjects}({@link createProjectGraph}())
* Method for utilizing the project graph in schematics
*/
export function getProjectGraphFromHost(host: Tree): ProjectGraph {
return onlyWorkspaceProjects(getFullProjectGraphFromHost(host));
return onlyWorkspaceProjects(createProjectGraph());
}
// TODO(v13): remove this deprecated method
/**
* @deprecated This method is deprecated and is synonymous to {@link createProjectGraph}()
*/
export function getFullProjectGraphFromHost(host: Tree): ProjectGraph {
const workspaceJson = readJsonInTree(host, getWorkspacePath(host));
const nxJson = readJsonInTree<NxJson>(host, '/nx.json');
const fileRead: FileRead = (f: string) => {
try {
return host.read(f).toString();
} catch (e) {
throw new Error(`${f} does not exist`);
}
};
const workspaceFiles: FileData[] = [];
workspaceFiles.push(
...allFilesInDirInHost(host, normalize(''), { recursive: false }).map((f) =>
getFileDataInHost(host, f)
)
);
workspaceFiles.push(
...allFilesInDirInHost(host, normalize('tools')).map((f) =>
getFileDataInHost(host, f)
)
);
// Add files for workspace projects
Object.keys(workspaceJson.projects).forEach((projectName) => {
const project = workspaceJson.projects[projectName];
workspaceFiles.push(
...allFilesInDirInHost(host, normalize(project.root)).map((f) =>
getFileDataInHost(host, f)
)
);
});
return createProjectGraph(
workspaceJson,
nxJson,
workspaceFiles,
fileRead,
false
);
return createProjectGraph(undefined, undefined, undefined, false);
}
// TODO(v13): remove this deprecated method
/**
* @deprecated This method is deprecated
*/
export function getFileDataInHost(host: Tree, path: Path): FileData {
return {
file: path,

View File

@ -1,70 +1,49 @@
import * as ts from 'typescript';
import { SchematicContext, Tree } from '@angular-devkit/schematics';
import { getWorkspace } from '@nrwl/workspace';
import {
getFullProjectGraphFromHost,
chain,
SchematicContext,
Tree,
Rule,
} from '@angular-devkit/schematics';
import { getWorkspace, visitNotIgnoredFiles } from '@nrwl/workspace';
import {
findNodes,
insert,
ReplaceChange,
} from '@nrwl/workspace/src/utils/ast-utils';
import { normalize } from '@angular-devkit/core';
export interface PackageNameMapping {
[packageName: string]: string;
}
const getProjectNamesWithDepsToRename = (
packageNameMapping: PackageNameMapping,
tree: Tree
) => {
const packagesToRename = Object.entries(packageNameMapping);
const projectGraph = getFullProjectGraphFromHost(tree);
return Object.entries(projectGraph.dependencies)
.filter(([, deps]) =>
deps.some(
(dep) =>
dep.type === 'static' &&
packagesToRename.some(
([packageName]) => packageName === dep.target.replace('npm:', '')
)
)
)
.map(([projectName]) => projectName);
};
/**
* Updates all the imports found in the workspace
*
* @param packageNameMapping The packageNameMapping provided to the schematic
*/
export function renamePackageImports(packageNameMapping: PackageNameMapping) {
return async (tree: Tree, _context: SchematicContext): Promise<void> => {
export function renamePackageImports(
packageNameMapping: PackageNameMapping
): Rule {
return async (tree: Tree, _context: SchematicContext) => {
const workspace = await getWorkspace(tree);
const projectNamesThatImportAPackageToRename = getProjectNamesWithDepsToRename(
packageNameMapping,
tree
);
const projectsThatImportPackage = [...workspace.projects].filter(([name]) =>
projectNamesThatImportAPackageToRename.includes(name)
);
projectsThatImportPackage
.map(([, definition]) => tree.getDir(definition.root))
.forEach((projectDir) => {
projectDir.visit((file) => {
// only look at .(j|t)s(x) files
const rules = [];
workspace.projects.forEach((project) => {
rules.push(
visitNotIgnoredFiles((file) => {
if (!/([jt])sx?$/.test(file)) {
return;
}
// if it doesn't contain at least 1 reference to the packages to be renamed bail out
const contents = tree.read(file).toString('utf-8');
if (
!Object.keys(packageNameMapping).some((packageName) =>
contents.includes(packageName)
)
) {
const fileIncludesPackageToRename = Object.keys(
packageNameMapping
).some((packageName) => {
return contents.includes(packageName);
});
if (!fileIncludesPackageToRename) {
return;
}
@ -107,7 +86,10 @@ export function renamePackageImports(packageNameMapping: PackageNameMapping) {
// update the file in the tree
insert(tree, file, changes);
}
});
});
}, normalize(project.root))
);
});
return chain(rules);
};
}