feat(core): format workspace.json based on version field

This commit is contained in:
victor savkin 2020-12-03 12:14:39 -05:00 committed by Victor Savkin
parent 776930ce61
commit a24fb961d8
28 changed files with 164 additions and 120 deletions

View File

@ -96,7 +96,6 @@ Object {
"prefix": "proj",
"projectType": "application",
"root": "apps/my-dir/my-app",
"schematics": Object {},
"sourceRoot": "apps/my-dir/my-app/src",
}
`;
@ -232,7 +231,6 @@ Object {
"prefix": "proj",
"projectType": "application",
"root": "apps/my-app",
"schematics": Object {},
"sourceRoot": "apps/my-app/src",
}
`;

View File

@ -309,21 +309,6 @@ describe('app', () => {
true
);
});
it('should set it as default', async () => {
const result = await runSchematic(
'app',
{ name: 'myApp', style: 'scss' },
appTree
);
const workspaceJson = readJsonInTree(result, 'workspace.json');
expect(workspaceJson.projects['my-app'].schematics).toEqual({
'@schematics/angular:component': {
style: 'scss',
},
});
});
});
describe('--linter', () => {

View File

@ -484,6 +484,7 @@ function updateProject(options: NormalizedSchema): Rule {
options.name,
options.appProjectRoot
);
delete fixedProject.schematics;
delete fixedProject.architect.test;
@ -803,10 +804,6 @@ export default function (schema: Schema): Rule {
skipInstall: true,
skipPackageJson: false,
}),
// TODO: Remove this after Angular 10.1.0
(host) => {
host.delete('tsconfig.json');
},
addSchematicFiles(appProjectRoot, options),
options.e2eTestRunner === 'protractor'
? move(e2eProjectRoot, options.e2eProjectRoot)

View File

@ -130,15 +130,7 @@ export function updateProject(options: NormalizedSchema): Rule {
options.projectRoot
);
fixedProject.schematics = fixedProject.schematics || {};
if (options.style !== 'css') {
fixedProject.schematics = {
...fixedProject.schematics,
'@schematics/angular:component': {
style: options.style,
},
};
}
delete fixedProject.schematics;
if (!options.publishable && !options.buildable) {
delete fixedProject.architect.build;

View File

@ -1026,24 +1026,6 @@ describe('lib', () => {
});
});
describe('--style scss', () => {
it('should set it as default', async () => {
const result = await runSchematic(
'lib',
{ name: 'myLib', style: 'scss' },
appTree
);
const workspaceJson = readJsonInTree(result, 'workspace.json');
expect(workspaceJson.projects['my-lib'].schematics).toEqual({
'@schematics/angular:component': {
style: 'scss',
},
});
});
});
describe('--unit-test-runner karma', () => {
it('should generate karma configuration', async () => {
const resultTree = await runSchematic(

View File

@ -9,15 +9,15 @@ export {
NxJsonProjectConfiguration,
} from '@nrwl/tao/src/shared/nx';
export { TargetContext } from '@nrwl/tao/src/commands/run';
export { formatFiles } from './src/schematics/format-files';
export { formatFiles } from './src/generators/format-files';
export { readJson } from './src/utils/read-json';
export { generateFiles } from './src/schematics/generate-files';
export { generateFiles } from './src/generators/generate-files';
export { installPackagesTask } from './src/tasks/install-packages-task';
export {
addProjectConfiguration,
readProjectConfiguration,
updateProjectConfiguration,
} from './src/schematics/project-configuration';
} from './src/generators/project-configuration';
export { names } from './src/utils/names';
export { getWorkspaceLayout } from './src/utils/get-workspace-layout';
export { offsetFromRoot } from './src/utils/offset-from-root';

View File

@ -1,6 +1,8 @@
import { Tree } from '@nrwl/tao/src/shared/tree';
import * as path from 'path';
import type * as Prettier from 'prettier';
import { getWorkspacePath } from '../utils/get-workspace-layout';
import { Workspaces } from '@nrwl/tao/src/shared/workspace';
let prettier: typeof Prettier;
try {
@ -12,6 +14,8 @@ try {
* @param host - the file system tree
*/
export async function formatFiles(host: Tree) {
updateWorkspaceJsonToMatchFormatVersion(host);
if (!prettier) return;
const files = new Set(
@ -41,8 +45,23 @@ export async function formatFiles(host: Tree) {
prettier.format(file.content.toString(), options)
);
} catch (e) {
console.warn(`Could not format ${file.path} because ${e.message}`);
console.warn(`Could not format ${file.path}. Error: "${e.message}"`);
}
})
);
}
function updateWorkspaceJsonToMatchFormatVersion(host: Tree) {
const ws = new Workspaces();
const path = getWorkspacePath(host);
try {
const workspaceJson = JSON.parse(host.read(path).toString());
const reformatted = ws.reformattedWorkspaceJsonOrNull(workspaceJson);
if (reformatted) {
host.write(path, JSON.stringify(reformatted, null, 2));
}
} catch (e) {
console.error(`Failed to format: ${path}`);
console.error(e);
}
}

View File

@ -9,6 +9,7 @@ import {
NxJsonConfiguration,
NxJsonProjectConfiguration,
} from '@nrwl/tao/src/shared/nx';
import { getWorkspacePath } from '../utils/get-workspace-layout';
/**
* Adds project configuration to the Nx workspace.
@ -140,8 +141,3 @@ function addProjectToNxJson(
};
host.write('nx.json', JSON.stringify(nxJson));
}
function getWorkspacePath(host: Tree) {
const possibleFiles = ['/workspace.json', '/angular.json', '/.angular.json'];
return possibleFiles.filter((path) => host.exists(path))[0];
}

View File

@ -6,10 +6,7 @@ import { FsTree } from '@nrwl/tao/src/shared/tree';
export function createTreeWithEmptyWorkspace() {
const tree = new FsTree('/virtual', false);
tree.write(
'/workspace.json',
JSON.stringify({ version: 1, projects: {}, newProjectRoot: '' })
);
tree.write('/workspace.json', JSON.stringify({ version: 1, projects: {} }));
tree.write(
'/package.json',
JSON.stringify({

View File

@ -21,3 +21,8 @@ export function getWorkspaceLayout(
const npmScope = nxJson.npmScope;
return { ...layout, npmScope };
}
export function getWorkspacePath(host: Tree) {
const possibleFiles = ['/workspace.json', '/angular.json'];
return possibleFiles.filter((path) => host.exists(path))[0];
}

View File

@ -62,7 +62,6 @@ export function addProject(options: NormalizedSchema): Rule {
root: options.appProjectRoot,
sourceRoot: options.appProjectRoot,
projectType: 'application',
schematics: {},
architect,
};

View File

@ -96,7 +96,6 @@ function updateWorkspaceJson(options: NormalizedSchema): Rule {
sourceRoot: join(options.appProjectRoot, 'src'),
projectType: 'application',
prefix: options.name,
schematics: {},
architect: <any>{},
};

View File

@ -1,5 +1,5 @@
import { createTreeWithEmptyWorkspace } from '@nrwl/devkit/testing';
import { Tree } from '@nrwl/devkit';
import { Tree, readProjectConfiguration } from '@nrwl/devkit';
import generator from './generator';
import { <%= className %>GeneratorSchema } from './schema';
@ -14,27 +14,7 @@ describe('<%= name %> generator', () => {
it('should run successfully', async () => {
generator(appTree, options);
const workspace = JSON.parse(appTree.read('workspace.json').toString());
expect(workspace.projects['test']).toMatchInlineSnapshot(`
Object {
"projectType": "library",
"root": "libs/test",
"sourceRoot": "libs/test/src",
"targets": Object {
"build": Object {
"executor": "<%= npmPackageName %>:build",
},
},
}
`);
const nxjson = JSON.parse(appTree.read('nx.json').toString());
expect(nxjson.projects).toMatchInlineSnapshot(`
Object {
"test": Object {
"tags": Array [],
},
}
`);
const config = readProjectConfiguration(appTree, 'test');
expect(config).toBeDefined();
})
});

View File

@ -85,7 +85,6 @@ export function addProject(options: NormalizedSchema): Rule {
root: options.appProjectRoot,
sourceRoot: join(options.appProjectRoot, 'src'),
projectType: 'application',
schematics: {},
architect,
};

View File

@ -182,7 +182,6 @@ function addProject(options: NormalizedSchema): Rule {
root: options.projectRoot,
sourceRoot: join(normalize(options.projectRoot), 'src'),
projectType: 'library',
schematics: {},
architect,
};
return json;

View File

@ -269,9 +269,10 @@ class NxScopedHost extends virtualFs.ScopedHost<any> {
map((r) => {
try {
const w = JSON.parse(Buffer.from(r).toString());
return Buffer.from(
JSON.stringify(new Workspaces().toOldFormat(w), null, 2)
);
const formatted = new Workspaces().toOldFormatOrNull(w);
return formatted
? Buffer.from(JSON.stringify(formatted, null, 2))
: r;
} catch (e) {
return r;
}
@ -294,12 +295,15 @@ class NxScopedHost extends virtualFs.ScopedHost<any> {
if (newFormat) {
try {
const w = JSON.parse(Buffer.from(content).toString());
return super.write(
path,
Buffer.from(
JSON.stringify(new Workspaces().toNewFormat(w), null, 2)
)
);
const formatted = new Workspaces().toNewFormatOrNull(w);
if (formatted) {
return super.write(
path,
Buffer.from(JSON.stringify(formatted, null, 2))
);
} else {
return super.write(path, content);
}
} catch (e) {
return super.write(path, content);
}
@ -328,7 +332,9 @@ class NxScopedHost extends virtualFs.ScopedHost<any> {
switchMap((isAngularJson) => {
return super
.read((isAngularJson ? '/angular.json' : '/workspace.json') as any)
.pipe(map((r) => !!JSON.parse(Buffer.from(r).toString()).generators));
.pipe(
map((r) => JSON.parse(Buffer.from(r).toString()).version === 2)
);
})
);
}

View File

@ -112,17 +112,31 @@ export class Workspaces {
return this.toNewFormat(w);
}
reformattedWorkspaceJsonOrNull(w: any) {
return w.version === 2
? this.toNewFormatOrNull(w)
: this.toOldFormatOrNull(w);
}
toNewFormat(w: any) {
const f = this.toNewFormatOrNull(w);
return f ? f : w;
}
toNewFormatOrNull(w: any) {
let formatted = false;
Object.values(w.projects || {}).forEach((project: any) => {
if (project.architect) {
project.targets = project.architect;
delete project.architect;
formatted = true;
}
Object.values(project.targets || {}).forEach((target: any) => {
if (target.builder) {
target.executor = target.builder;
delete target.builder;
formatted = true;
}
});
});
@ -130,21 +144,28 @@ export class Workspaces {
if (w.schematics) {
w.generators = w.schematics;
delete w.schematics;
formatted = true;
}
return w;
if (formatted) {
w.version = 2;
}
return formatted ? w : null;
}
toOldFormat(w: any) {
toOldFormatOrNull(w: any) {
let formatted = false;
Object.values(w.projects || {}).forEach((project: any) => {
if (project.targets) {
project.architect = project.targets;
delete project.targets;
formatted = true;
}
Object.values(project.architect || {}).forEach((target: any) => {
if (target.executor) {
target.builder = target.executor;
delete target.execuctor;
delete target.executor;
formatted = true;
}
});
});
@ -152,8 +173,12 @@ export class Workspaces {
if (w.generators) {
w.schematics = w.generators;
delete w.generators;
formatted = true;
}
return w;
if (formatted) {
w.version = 1;
}
return formatted ? w : null;
}
isNxExecutor(nodeModule: string, executor: string) {

View File

@ -140,7 +140,6 @@ function addProject(options: NormalizedSchema): Rule {
root: options.appProjectRoot,
sourceRoot: join(normalize(options.appProjectRoot), 'src'),
projectType: 'application',
schematics: {},
architect,
};

View File

@ -8,9 +8,15 @@ import {
onlyWorkspaceProjects,
} from '../core/project-graph';
import { filterAffected } from '../core/affected-project-graph';
import { calculateFileChanges } from '../core/file-utils';
import { calculateFileChanges, readWorkspaceJson } from '../core/file-utils';
import * as yargs from 'yargs';
import { NxArgs, splitArgsIntoNxArgsAndOverrides } from './utils';
import {
workspaceConfigName,
Workspaces,
} from '@nrwl/tao/src/shared/workspace';
import { appRootPath } from '@nrwl/workspace/src/utils/app-root';
import { readFileSync, writeFileSync } from 'fs-extra';
const PRETTIER_EXTENSIONS = [
'ts',
@ -40,6 +46,7 @@ export function format(command: 'check' | 'write', args: yargs.Arguments) {
switch (command) {
case 'write':
updateWorkspaceJsonToMatchFormatVersion();
chunkList.forEach((chunk) => write(chunk));
break;
case 'check':
@ -124,3 +131,22 @@ function prettierPath() {
);
return path.join(basePath, 'bin-prettier.js');
}
function updateWorkspaceJsonToMatchFormatVersion() {
const ws = new Workspaces();
try {
const workspaceJson = JSON.parse(
readFileSync(workspaceConfigName(appRootPath)).toString()
);
const reformatted = ws.reformattedWorkspaceJsonOrNull(workspaceJson);
if (reformatted) {
writeFileSync(
workspaceConfigName(appRootPath),
JSON.stringify(reformatted, null, 2)
);
}
} catch (e) {
console.error(`Failed to format: ${path}`);
console.error(e);
}
}

View File

@ -5,7 +5,7 @@ import { runMigration } from '../../utils/testing';
function createAngularCLIPoweredWorkspace() {
const tree = createEmptyWorkspace(Tree.empty());
tree.delete('workspace.json');
tree.create('angular.json', '');
tree.create('angular.json', '{}');
return tree;
}

View File

@ -53,7 +53,6 @@ function addProject(options: NormalizedSchema): Rule {
root: options.projectRoot,
sourceRoot: join(normalize(options.projectRoot), 'src'),
projectType: 'library',
schematics: {},
architect,
};
return json;

View File

@ -141,7 +141,7 @@ export default function (options: Schema): Rule {
addCloudDependencies(options),
move('/', options.directory),
addTasks(options),
formatFiles(),
formatFiles({ skipFormat: false }, options.directory),
])(Tree.empty(), context);
};
}

View File

@ -159,7 +159,10 @@ describe('addDepsToPackageJson', () => {
})
);
const testRunner = new SchematicTestRunner('@nrwl/jest', null);
const testRunner = new SchematicTestRunner(
'@nrwl/jest',
join(__dirname, '../../../jest/collection.json')
);
await testRunner
.callRule(() => {
@ -188,7 +191,10 @@ describe('addDepsToPackageJson', () => {
})
);
const testRunner = new SchematicTestRunner('@nrwl/jest', null);
const testRunner = new SchematicTestRunner(
'@nrwl/jest',
join(__dirname, '../../../jest/collection.json')
);
await testRunner
.callRule(() => {

View File

@ -367,15 +367,15 @@ export function insert(host: Tree, modulePath: string, changes: Change[]) {
}
// sort changes so that the highest pos goes first
const orderedChanges = changes.sort((a, b) => b.order - a.order);
const orderedChanges = changes.sort((a, b) => b.order - a.order) as any;
const recorder = host.beginUpdate(modulePath);
for (const change of orderedChanges) {
if (change instanceof InsertChange) {
if (change.type == 'insert') {
recorder.insertLeft(change.pos, change.toAdd);
} else if (change instanceof RemoveChange) {
} else if (change.type == 'remove') {
recorder.remove(change.pos - 1, change.toRemove.length + 1);
} else if (change instanceof ReplaceChange) {
} else if (change.type == 'replace') {
recorder.remove(change.pos, change.oldText.length);
recorder.insertLeft(change.pos, change.newText);
} else if (change.type === 'noop') {

View File

@ -3,7 +3,7 @@ import { readJsonInTree } from './ast-utils';
import { NxJson } from '@nrwl/workspace/src/core/shared-interfaces';
export function getWorkspacePath(host: Tree) {
const possibleFiles = ['/workspace.json', '/angular.json', '/.angular.json'];
const possibleFiles = ['/workspace.json', '/angular.json'];
return possibleFiles.filter((path) => host.exists(path))[0];
}

View File

@ -6,22 +6,32 @@ import {
SchematicContext,
Tree,
} from '@angular-devkit/schematics';
let prettier;
try {
prettier = require('prettier');
} catch (e) {}
import { from } from 'rxjs';
import { filter, map, mergeMap } from 'rxjs/operators';
import * as path from 'path';
import { appRootPath } from '../app-root';
import { Workspaces } from '@nrwl/tao/src/shared/workspace';
let prettier;
try {
prettier = require('prettier');
} catch (e) {}
export function formatFiles(
options: { skipFormat: boolean } = { skipFormat: false }
options: { skipFormat: boolean } = { skipFormat: false },
directory: string = ''
): Rule {
if (options.skipFormat || !prettier) {
if (options.skipFormat) {
return noop();
}
return (host: Tree, context: SchematicContext) => {
updateWorkspaceJsonToMatchFormatVersion(host, directory);
if (!prettier) {
return host;
}
const files = new Set(
host.actions
.filter((action) => action.kind !== 'd' && action.kind !== 'r')
@ -64,3 +74,27 @@ export function formatFiles(
);
};
}
function updateWorkspaceJsonToMatchFormatVersion(
host: Tree,
directory: string
) {
const ws = new Workspaces();
const possibleFiles = [
`${directory}/workspace.json`,
`${directory}/angular.json`,
];
const path = possibleFiles.filter((path) => host.exists(path))[0];
try {
if (path) {
const workspaceJson = JSON.parse(host.read(path).toString());
const reformatted = ws.reformattedWorkspaceJsonOrNull(workspaceJson);
if (reformatted) {
host.overwrite(path, JSON.stringify(reformatted, null, 2));
}
}
} catch (e) {
console.error(`Failed to format: ${path}`);
console.error(e);
}
}

View File

@ -50,6 +50,8 @@ module.exports = function (path, options) {
}
// Try to use the defaultResolver
try {
if (path.startsWith('@nrwl/')) throw new Error('custom resolution');
if (path.indexOf('@nrwl/workspace') > -1) {
throw 'Reference to local Nx package found. Use local version instead.';
}