feat(core): migrate create-nx-workspace to devkit
This commit is contained in:
parent
764f580e99
commit
09c78c4750
@ -11,6 +11,7 @@ export {
|
||||
NxJsonProjectConfiguration,
|
||||
} from '@nrwl/tao/src/shared/nx';
|
||||
export { logger } from '@nrwl/tao/src/shared/logger';
|
||||
export { getPackageManagerCommand } from '@nrwl/tao/src/shared/package-manager';
|
||||
export { TargetContext } from '@nrwl/tao/src/commands/run';
|
||||
|
||||
export { formatFiles } from './src/generators/format-files';
|
||||
@ -25,7 +26,10 @@ export { readJson, writeJson, updateJson } from './src/utils/json';
|
||||
export { addDependenciesToPackageJson } from './src/utils/package-json';
|
||||
export { installPackagesTask } from './src/tasks/install-packages-task';
|
||||
export { names } from './src/utils/names';
|
||||
export { getWorkspaceLayout } from './src/utils/get-workspace-layout';
|
||||
export {
|
||||
getWorkspaceLayout,
|
||||
getWorkspacePath,
|
||||
} from './src/utils/get-workspace-layout';
|
||||
export {
|
||||
applyChangesToString,
|
||||
ChangeType,
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import { Tree } from '@nrwl/tao/src/shared/tree';
|
||||
import { execSync } from 'child_process';
|
||||
import { getPackageManagerCommand } from '@nrwl/tao/src/shared/package-manager';
|
||||
import { join } from 'path';
|
||||
|
||||
let storedPackageJsonValue;
|
||||
|
||||
@ -11,14 +12,21 @@ let storedPackageJsonValue;
|
||||
* @param host - the file system tree
|
||||
* @param alwaysRun - always run the command even if `package.json` hasn't changed.
|
||||
*/
|
||||
export function installPackagesTask(host: Tree, alwaysRun: boolean = false) {
|
||||
const packageJsonValue = host.read('package.json').toString();
|
||||
if (host.listChanges().find((f) => f.path === 'package.json') || alwaysRun) {
|
||||
export function installPackagesTask(
|
||||
host: Tree,
|
||||
alwaysRun: boolean = false,
|
||||
cwd: string = ''
|
||||
) {
|
||||
const packageJsonValue = host.read(join(cwd, 'package.json')).toString();
|
||||
if (
|
||||
host.listChanges().find((f) => f.path === join(cwd, 'package.json')) ||
|
||||
alwaysRun
|
||||
) {
|
||||
if (storedPackageJsonValue != packageJsonValue || alwaysRun) {
|
||||
storedPackageJsonValue = host.read('package.json').toString();
|
||||
storedPackageJsonValue = host.read(join(cwd, 'package.json')).toString();
|
||||
const pmc = getPackageManagerCommand();
|
||||
execSync(pmc.install, {
|
||||
cwd: host.root,
|
||||
cwd: join(host.root, cwd),
|
||||
stdio: [0, 1, 2],
|
||||
});
|
||||
}
|
||||
|
||||
@ -179,7 +179,23 @@ export async function taoNew(cwd: string, args: string[], isVerbose = false) {
|
||||
null,
|
||||
null
|
||||
);
|
||||
return (await import('./ngcli-adapter')).invokeNew(
|
||||
|
||||
if (ws.isNxGenerator(opts.collectionName, normalizedGeneratorName)) {
|
||||
const host = new FsTree(cwd, isVerbose);
|
||||
const task = await implementation(host, combinedOpts);
|
||||
const changes = host.listChanges();
|
||||
|
||||
printChanges(changes);
|
||||
if (!opts.dryRun) {
|
||||
flushChanges(cwd, changes);
|
||||
if (task) {
|
||||
await task();
|
||||
}
|
||||
} else {
|
||||
logger.warn(`\nNOTE: The "dryRun" flag means no changes were made.`);
|
||||
}
|
||||
} else {
|
||||
return (await import('./ngcli-adapter')).generate(
|
||||
cwd,
|
||||
{
|
||||
...opts,
|
||||
@ -187,6 +203,7 @@ export async function taoNew(cwd: string, args: string[], isVerbose = false) {
|
||||
},
|
||||
isVerbose
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@ -95,6 +95,42 @@ describe('tree', () => {
|
||||
).toEqual('new child content');
|
||||
});
|
||||
|
||||
it('should normalize paths', () => {
|
||||
tree.write('dir/file1', 'File 1 Contents');
|
||||
tree.write('/dir/file2', 'File 2 Contents');
|
||||
tree.write('./dir/file3', 'File 3 Contents');
|
||||
|
||||
expect(tree.read('dir/file1').toString()).toEqual('File 1 Contents');
|
||||
expect(tree.read('/dir/file1').toString()).toEqual('File 1 Contents');
|
||||
expect(tree.read('./dir/file1').toString()).toEqual('File 1 Contents');
|
||||
|
||||
expect(tree.read('dir/file2').toString()).toEqual('File 2 Contents');
|
||||
expect(tree.read('/dir/file2').toString()).toEqual('File 2 Contents');
|
||||
expect(tree.read('./dir/file2').toString()).toEqual('File 2 Contents');
|
||||
|
||||
expect(tree.read('dir/file3').toString()).toEqual('File 3 Contents');
|
||||
expect(tree.read('/dir/file3').toString()).toEqual('File 3 Contents');
|
||||
expect(tree.read('./dir/file3').toString()).toEqual('File 3 Contents');
|
||||
|
||||
tree.rename('dir/file1', 'dir/file-a');
|
||||
|
||||
expect(tree.read('dir/file-a').toString()).toEqual('File 1 Contents');
|
||||
expect(tree.read('/dir/file-a').toString()).toEqual('File 1 Contents');
|
||||
expect(tree.read('./dir/file-a').toString()).toEqual('File 1 Contents');
|
||||
|
||||
tree.rename('/dir/file2', '/dir/file-b');
|
||||
|
||||
expect(tree.read('dir/file-b').toString()).toEqual('File 2 Contents');
|
||||
expect(tree.read('/dir/file-b').toString()).toEqual('File 2 Contents');
|
||||
expect(tree.read('./dir/file-b').toString()).toEqual('File 2 Contents');
|
||||
|
||||
tree.rename('./dir/file3', './dir/file-c');
|
||||
|
||||
expect(tree.read('dir/file-c').toString()).toEqual('File 3 Contents');
|
||||
expect(tree.read('/dir/file-c').toString()).toEqual('File 3 Contents');
|
||||
expect(tree.read('./dir/file-c').toString()).toEqual('File 3 Contents');
|
||||
});
|
||||
|
||||
it('should be able to delete files', () => {
|
||||
tree.delete('parent/parent-file.txt');
|
||||
tree.write('parent/new-child/new-child-file.txt', 'new child content');
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
import * as path from 'path';
|
||||
import {
|
||||
readdirSync,
|
||||
readFileSync,
|
||||
@ -8,6 +7,7 @@ import {
|
||||
} from 'fs';
|
||||
import { mkdirpSync, rmdirSync } from 'fs-extra';
|
||||
import { logger } from './logger';
|
||||
import { dirname, join, relative } from 'path';
|
||||
const chalk = require('chalk');
|
||||
|
||||
/**
|
||||
@ -88,6 +88,7 @@ export class FsTree implements Tree {
|
||||
constructor(readonly root: string, private readonly isVerbose: boolean) {}
|
||||
|
||||
read(filePath: string): Buffer | null {
|
||||
filePath = this.normalize(filePath);
|
||||
try {
|
||||
if (this.recordedChanges[this.rp(filePath)]) {
|
||||
return this.recordedChanges[this.rp(filePath)].content;
|
||||
@ -103,6 +104,7 @@ export class FsTree implements Tree {
|
||||
}
|
||||
|
||||
write(filePath: string, content: Buffer | string): void {
|
||||
filePath = this.normalize(filePath);
|
||||
try {
|
||||
this.recordedChanges[this.rp(filePath)] = {
|
||||
content: Buffer.from(content),
|
||||
@ -116,10 +118,12 @@ export class FsTree implements Tree {
|
||||
}
|
||||
|
||||
overwrite(filePath: string, content: Buffer | string): void {
|
||||
filePath = this.normalize(filePath);
|
||||
this.write(filePath, content);
|
||||
}
|
||||
|
||||
exists(filePath: string): boolean {
|
||||
filePath = this.normalize(filePath);
|
||||
try {
|
||||
if (this.recordedChanges[this.rp(filePath)]) {
|
||||
return !this.recordedChanges[this.rp(filePath)].isDeleted;
|
||||
@ -134,6 +138,7 @@ export class FsTree implements Tree {
|
||||
}
|
||||
|
||||
delete(filePath: string): void {
|
||||
filePath = this.normalize(filePath);
|
||||
if (this.filesForDir(this.rp(filePath)).length > 0) {
|
||||
this.filesForDir(this.rp(filePath)).forEach(
|
||||
(f) => (this.recordedChanges[f] = { content: null, isDeleted: true })
|
||||
@ -146,12 +151,15 @@ export class FsTree implements Tree {
|
||||
}
|
||||
|
||||
rename(from: string, to: string): void {
|
||||
from = this.normalize(from);
|
||||
to = this.normalize(to);
|
||||
const content = this.read(this.rp(from));
|
||||
this.recordedChanges[this.rp(from)] = { content: null, isDeleted: true };
|
||||
this.recordedChanges[this.rp(to)] = { content: content, isDeleted: false };
|
||||
}
|
||||
|
||||
isFile(filePath: string): boolean {
|
||||
filePath = this.normalize(filePath);
|
||||
try {
|
||||
if (this.recordedChanges[this.rp(filePath)]) {
|
||||
return !this.recordedChanges[this.rp(filePath)].isDeleted;
|
||||
@ -164,11 +172,12 @@ export class FsTree implements Tree {
|
||||
}
|
||||
|
||||
children(dirPath: string): string[] {
|
||||
dirPath = this.normalize(dirPath);
|
||||
let res = this.fsReadDir(dirPath);
|
||||
|
||||
res = [...res, ...this.directChildrenOfDir(this.rp(dirPath))];
|
||||
return res.filter((q) => {
|
||||
const r = this.recordedChanges[path.join(this.rp(dirPath), q)];
|
||||
const r = this.recordedChanges[join(this.rp(dirPath), q)];
|
||||
if (r && r.isDeleted) return false;
|
||||
return true;
|
||||
});
|
||||
@ -200,10 +209,14 @@ export class FsTree implements Tree {
|
||||
return res;
|
||||
}
|
||||
|
||||
private normalize(path: string) {
|
||||
return relative(this.root, join(this.root, path));
|
||||
}
|
||||
|
||||
private fsReadDir(dirPath: string) {
|
||||
if (!this.delegateToFs) return [];
|
||||
try {
|
||||
return readdirSync(path.join(this.root, dirPath));
|
||||
return readdirSync(join(this.root, dirPath));
|
||||
} catch (e) {
|
||||
return [];
|
||||
}
|
||||
@ -211,19 +224,19 @@ export class FsTree implements Tree {
|
||||
|
||||
private fsIsFile(filePath: string) {
|
||||
if (!this.delegateToFs) return false;
|
||||
const stat = statSync(path.join(this.root, filePath));
|
||||
const stat = statSync(join(this.root, filePath));
|
||||
return stat.isFile();
|
||||
}
|
||||
|
||||
private fsReadFile(filePath: string) {
|
||||
if (!this.delegateToFs) return null;
|
||||
return readFileSync(path.join(this.root, filePath));
|
||||
return readFileSync(join(this.root, filePath));
|
||||
}
|
||||
|
||||
private fsExists(filePath: string): boolean {
|
||||
if (!this.delegateToFs) return false;
|
||||
try {
|
||||
const stat = statSync(path.join(this.root, filePath));
|
||||
const stat = statSync(join(this.root, filePath));
|
||||
return stat.isFile() || stat.isDirectory();
|
||||
} catch (e) {
|
||||
return false;
|
||||
@ -258,9 +271,9 @@ export class FsTree implements Tree {
|
||||
|
||||
export function flushChanges(root: string, fileChanges: FileChange[]) {
|
||||
fileChanges.forEach((f) => {
|
||||
const fpath = path.join(root, f.path);
|
||||
const fpath = join(root, f.path);
|
||||
if (f.type === 'CREATE') {
|
||||
mkdirpSync(path.dirname(fpath));
|
||||
mkdirpSync(dirname(fpath));
|
||||
writeFileSync(fpath, f.content);
|
||||
} else if (f.type === 'UPDATE') {
|
||||
writeFileSync(fpath, f.content);
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
"version": "0.1",
|
||||
"schematics": {
|
||||
"workspace": {
|
||||
"factory": "./src/schematics/workspace/workspace",
|
||||
"factory": "./src/schematics/workspace/workspace#workspaceSchematic",
|
||||
"schema": "./src/schematics/workspace/schema.json",
|
||||
"description": "Create an empty workspace",
|
||||
"hidden": true
|
||||
@ -38,7 +38,71 @@
|
||||
},
|
||||
|
||||
"new": {
|
||||
"factory": "./src/schematics/new/new",
|
||||
"factory": "./src/schematics/new/new#newSchematic",
|
||||
"schema": "./src/schematics/new/schema.json",
|
||||
"description": "Create a workspace",
|
||||
"hidden": true
|
||||
},
|
||||
|
||||
"library": {
|
||||
"factory": "./src/schematics/library/library",
|
||||
"schema": "./src/schematics/library/schema.json",
|
||||
"aliases": ["lib"],
|
||||
"description": "Create a library"
|
||||
},
|
||||
|
||||
"workspace-generator": {
|
||||
"factory": "./src/schematics/workspace-generator/workspace-generator",
|
||||
"schema": "./src/schematics/workspace-generator/schema.json",
|
||||
"aliases": ["workspace-schematic"],
|
||||
"description": "Generates a workspace generator"
|
||||
},
|
||||
|
||||
"run-commands": {
|
||||
"factory": "./src/schematics/run-commands/run-commands",
|
||||
"schema": "./src/schematics/run-commands/schema.json",
|
||||
"aliases": ["run-command", "target"],
|
||||
"description": "Generates a target to run any command in the terminal"
|
||||
}
|
||||
},
|
||||
"generators": {
|
||||
"workspace": {
|
||||
"factory": "./src/schematics/workspace/workspace#workspaceGenerator",
|
||||
"schema": "./src/schematics/workspace/schema.json",
|
||||
"description": "Create an empty workspace",
|
||||
"hidden": true
|
||||
},
|
||||
|
||||
"ng-add": {
|
||||
"factory": "./src/schematics/init/init",
|
||||
"schema": "./src/schematics/init/schema.json",
|
||||
"description": "Convert an existing CLI project into an Nx Workspace",
|
||||
"hidden": true
|
||||
},
|
||||
|
||||
"preset": {
|
||||
"factory": "./src/schematics/preset/preset",
|
||||
"schema": "./src/schematics/preset/schema.json",
|
||||
"description": "Create application in an empty workspace",
|
||||
"hidden": true
|
||||
},
|
||||
|
||||
"move": {
|
||||
"factory": "./src/schematics/move/move",
|
||||
"schema": "./src/schematics/move/schema.json",
|
||||
"aliases": ["mv"],
|
||||
"description": "Move an application or library to another folder"
|
||||
},
|
||||
|
||||
"remove": {
|
||||
"factory": "./src/schematics/remove/remove",
|
||||
"schema": "./src/schematics/remove/schema.json",
|
||||
"aliases": ["rm"],
|
||||
"description": "Remove an application or library"
|
||||
},
|
||||
|
||||
"new": {
|
||||
"factory": "./src/schematics/new/new#newGenerator",
|
||||
"schema": "./src/schematics/new/schema.json",
|
||||
"description": "Create a workspace",
|
||||
"hidden": true
|
||||
|
||||
@ -0,0 +1,383 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`new --preset angular should generate an empty nx.json 1`] = `
|
||||
Object {
|
||||
"affected": Object {
|
||||
"defaultBase": "master",
|
||||
},
|
||||
"implicitDependencies": Object {
|
||||
".eslintrc.json": "*",
|
||||
"nx.json": "*",
|
||||
"package.json": Object {
|
||||
"dependencies": "*",
|
||||
"devDependencies": "*",
|
||||
},
|
||||
"tsconfig.base.json": "*",
|
||||
"tslint.json": "*",
|
||||
"workspace.json": "*",
|
||||
},
|
||||
"npmScope": "npmScope",
|
||||
"projects": Object {},
|
||||
"tasksRunnerOptions": Object {
|
||||
"default": Object {
|
||||
"options": Object {
|
||||
"cacheableOperations": Array [
|
||||
"build",
|
||||
"lint",
|
||||
"test",
|
||||
"e2e",
|
||||
],
|
||||
},
|
||||
"runner": "@nrwl/workspace/tasks-runners/default",
|
||||
},
|
||||
},
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`new --preset angular should generate an empty workspace.json 1`] = `
|
||||
Object {
|
||||
"cli": Object {
|
||||
"defaultCollection": "@nrwl/workspace",
|
||||
},
|
||||
"generators": Object {
|
||||
"@nrwl/angular": Object {
|
||||
"application": Object {
|
||||
"linter": "eslint",
|
||||
},
|
||||
"library": Object {
|
||||
"linter": "eslint",
|
||||
},
|
||||
"storybook-configuration": Object {
|
||||
"linter": "eslint",
|
||||
},
|
||||
},
|
||||
},
|
||||
"projects": Object {},
|
||||
"version": 2,
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`new --preset angular should generate necessary npm dependencies 1`] = `
|
||||
Object {
|
||||
"dependencies": Object {
|
||||
"@nrwl/angular": "*",
|
||||
},
|
||||
"devDependencies": Object {
|
||||
"@nrwl/cli": "*",
|
||||
"@nrwl/tao": "*",
|
||||
"@nrwl/workspace": "*",
|
||||
"@types/node": "12.12.38",
|
||||
"dotenv": "6.2.0",
|
||||
"eslint": "7.10.0",
|
||||
"prettier": "2.1.2",
|
||||
"ts-node": "~9.1.1",
|
||||
"tslint": "~6.1.0",
|
||||
"typescript": "~4.0.3",
|
||||
},
|
||||
"license": "MIT",
|
||||
"name": "my-workspace",
|
||||
"private": true,
|
||||
"scripts": Object {
|
||||
"affected": "nx affected",
|
||||
"affected:apps": "nx affected:apps",
|
||||
"affected:build": "nx affected:build",
|
||||
"affected:dep-graph": "nx affected:dep-graph",
|
||||
"affected:e2e": "nx affected:e2e",
|
||||
"affected:libs": "nx affected:libs",
|
||||
"affected:lint": "nx affected:lint",
|
||||
"affected:test": "nx affected:test",
|
||||
"build": "nx build",
|
||||
"dep-graph": "nx dep-graph",
|
||||
"e2e": "nx e2e",
|
||||
"format": "nx format:write",
|
||||
"format:check": "nx format:check",
|
||||
"format:write": "nx format:write",
|
||||
"help": "nx help",
|
||||
"lint": "nx workspace-lint && nx lint",
|
||||
"nx": "nx",
|
||||
"start": "nx serve",
|
||||
"test": "nx test",
|
||||
"update": "nx migrate latest",
|
||||
"workspace-generator": "nx workspace-generator",
|
||||
},
|
||||
"version": "0.0.0",
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`new --preset empty should generate an empty nx.json 1`] = `
|
||||
Object {
|
||||
"affected": Object {
|
||||
"defaultBase": "master",
|
||||
},
|
||||
"implicitDependencies": Object {
|
||||
".eslintrc.json": "*",
|
||||
"nx.json": "*",
|
||||
"package.json": Object {
|
||||
"dependencies": "*",
|
||||
"devDependencies": "*",
|
||||
},
|
||||
"tsconfig.base.json": "*",
|
||||
"tslint.json": "*",
|
||||
"workspace.json": "*",
|
||||
},
|
||||
"npmScope": "npmScope",
|
||||
"projects": Object {},
|
||||
"tasksRunnerOptions": Object {
|
||||
"default": Object {
|
||||
"options": Object {
|
||||
"cacheableOperations": Array [
|
||||
"build",
|
||||
"lint",
|
||||
"test",
|
||||
"e2e",
|
||||
],
|
||||
},
|
||||
"runner": "@nrwl/workspace/tasks-runners/default",
|
||||
},
|
||||
},
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`new --preset empty should generate an empty workspace.json 1`] = `
|
||||
Object {
|
||||
"cli": Object {
|
||||
"defaultCollection": "@nrwl/workspace",
|
||||
},
|
||||
"projects": Object {},
|
||||
"version": 2,
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`new --preset empty should generate an necessary npm dependencies 1`] = `
|
||||
Object {
|
||||
"dependencies": Object {},
|
||||
"devDependencies": Object {
|
||||
"@nrwl/cli": "*",
|
||||
"@nrwl/tao": "*",
|
||||
"@nrwl/workspace": "*",
|
||||
"@types/node": "12.12.38",
|
||||
"dotenv": "6.2.0",
|
||||
"eslint": "7.10.0",
|
||||
"prettier": "2.1.2",
|
||||
"ts-node": "~9.1.1",
|
||||
"tslint": "~6.1.0",
|
||||
"typescript": "~4.0.3",
|
||||
},
|
||||
"license": "MIT",
|
||||
"name": "my-workspace",
|
||||
"private": true,
|
||||
"scripts": Object {
|
||||
"affected": "nx affected",
|
||||
"affected:apps": "nx affected:apps",
|
||||
"affected:build": "nx affected:build",
|
||||
"affected:dep-graph": "nx affected:dep-graph",
|
||||
"affected:e2e": "nx affected:e2e",
|
||||
"affected:libs": "nx affected:libs",
|
||||
"affected:lint": "nx affected:lint",
|
||||
"affected:test": "nx affected:test",
|
||||
"build": "nx build",
|
||||
"dep-graph": "nx dep-graph",
|
||||
"e2e": "nx e2e",
|
||||
"format": "nx format:write",
|
||||
"format:check": "nx format:check",
|
||||
"format:write": "nx format:write",
|
||||
"help": "nx help",
|
||||
"lint": "nx workspace-lint && nx lint",
|
||||
"nx": "nx",
|
||||
"start": "nx serve",
|
||||
"test": "nx test",
|
||||
"update": "nx migrate latest",
|
||||
"workspace-generator": "nx workspace-generator",
|
||||
},
|
||||
"version": "0.0.0",
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`new --preset empty should generate necessary npm dependencies 1`] = `
|
||||
Object {
|
||||
"dependencies": Object {},
|
||||
"devDependencies": Object {
|
||||
"@nrwl/cli": "*",
|
||||
"@nrwl/tao": "*",
|
||||
"@nrwl/workspace": "*",
|
||||
"@types/node": "12.12.38",
|
||||
"dotenv": "6.2.0",
|
||||
"eslint": "7.10.0",
|
||||
"prettier": "2.1.2",
|
||||
"ts-node": "~9.1.1",
|
||||
"tslint": "~6.1.0",
|
||||
"typescript": "~4.0.3",
|
||||
},
|
||||
"license": "MIT",
|
||||
"name": "my-workspace",
|
||||
"private": true,
|
||||
"scripts": Object {
|
||||
"affected": "nx affected",
|
||||
"affected:apps": "nx affected:apps",
|
||||
"affected:build": "nx affected:build",
|
||||
"affected:dep-graph": "nx affected:dep-graph",
|
||||
"affected:e2e": "nx affected:e2e",
|
||||
"affected:libs": "nx affected:libs",
|
||||
"affected:lint": "nx affected:lint",
|
||||
"affected:test": "nx affected:test",
|
||||
"build": "nx build",
|
||||
"dep-graph": "nx dep-graph",
|
||||
"e2e": "nx e2e",
|
||||
"format": "nx format:write",
|
||||
"format:check": "nx format:check",
|
||||
"format:write": "nx format:write",
|
||||
"help": "nx help",
|
||||
"lint": "nx workspace-lint && nx lint",
|
||||
"nx": "nx",
|
||||
"start": "nx serve",
|
||||
"test": "nx test",
|
||||
"update": "nx migrate latest",
|
||||
"workspace-generator": "nx workspace-generator",
|
||||
},
|
||||
"version": "0.0.0",
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`new --preset react should generate an empty nx.json 1`] = `
|
||||
Object {
|
||||
"affected": Object {
|
||||
"defaultBase": "master",
|
||||
},
|
||||
"implicitDependencies": Object {
|
||||
".eslintrc.json": "*",
|
||||
"nx.json": "*",
|
||||
"package.json": Object {
|
||||
"dependencies": "*",
|
||||
"devDependencies": "*",
|
||||
},
|
||||
"tsconfig.base.json": "*",
|
||||
"tslint.json": "*",
|
||||
"workspace.json": "*",
|
||||
},
|
||||
"npmScope": "npmScope",
|
||||
"projects": Object {},
|
||||
"tasksRunnerOptions": Object {
|
||||
"default": Object {
|
||||
"options": Object {
|
||||
"cacheableOperations": Array [
|
||||
"build",
|
||||
"lint",
|
||||
"test",
|
||||
"e2e",
|
||||
],
|
||||
},
|
||||
"runner": "@nrwl/workspace/tasks-runners/default",
|
||||
},
|
||||
},
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`new --preset react should generate an empty workspace.json 1`] = `
|
||||
Object {
|
||||
"cli": Object {
|
||||
"defaultCollection": "@nrwl/workspace",
|
||||
},
|
||||
"projects": Object {},
|
||||
"version": 2,
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`new --preset react should generate necessary npm dependencies 1`] = `
|
||||
Object {
|
||||
"dependencies": Object {},
|
||||
"devDependencies": Object {
|
||||
"@nrwl/cli": "*",
|
||||
"@nrwl/react": "*",
|
||||
"@nrwl/tao": "*",
|
||||
"@nrwl/workspace": "*",
|
||||
"@types/node": "12.12.38",
|
||||
"dotenv": "6.2.0",
|
||||
"eslint": "7.10.0",
|
||||
"prettier": "2.1.2",
|
||||
"ts-node": "~9.1.1",
|
||||
"tslint": "~6.1.0",
|
||||
"typescript": "~4.0.3",
|
||||
},
|
||||
"license": "MIT",
|
||||
"name": "my-workspace",
|
||||
"private": true,
|
||||
"scripts": Object {
|
||||
"affected": "nx affected",
|
||||
"affected:apps": "nx affected:apps",
|
||||
"affected:build": "nx affected:build",
|
||||
"affected:dep-graph": "nx affected:dep-graph",
|
||||
"affected:e2e": "nx affected:e2e",
|
||||
"affected:libs": "nx affected:libs",
|
||||
"affected:lint": "nx affected:lint",
|
||||
"affected:test": "nx affected:test",
|
||||
"build": "nx build",
|
||||
"dep-graph": "nx dep-graph",
|
||||
"e2e": "nx e2e",
|
||||
"format": "nx format:write",
|
||||
"format:check": "nx format:check",
|
||||
"format:write": "nx format:write",
|
||||
"help": "nx help",
|
||||
"lint": "nx workspace-lint && nx lint",
|
||||
"nx": "nx",
|
||||
"start": "nx serve",
|
||||
"test": "nx test",
|
||||
"update": "nx migrate latest",
|
||||
"workspace-generator": "nx workspace-generator",
|
||||
},
|
||||
"version": "0.0.0",
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`new should 1`] = `
|
||||
Object {
|
||||
"cli": Object {
|
||||
"defaultCollection": "@nrwl/workspace",
|
||||
},
|
||||
"projects": Object {},
|
||||
"version": 2,
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`new should generate an empty nx.json 1`] = `
|
||||
Object {
|
||||
"affected": Object {
|
||||
"defaultBase": "master",
|
||||
},
|
||||
"implicitDependencies": Object {
|
||||
".eslintrc.json": "*",
|
||||
"nx.json": "*",
|
||||
"package.json": Object {
|
||||
"dependencies": "*",
|
||||
"devDependencies": "*",
|
||||
},
|
||||
"tsconfig.base.json": "*",
|
||||
"tslint.json": "*",
|
||||
"workspace.json": "*",
|
||||
},
|
||||
"npmScope": "npmScope",
|
||||
"projects": Object {},
|
||||
"tasksRunnerOptions": Object {
|
||||
"default": Object {
|
||||
"options": Object {
|
||||
"cacheableOperations": Array [
|
||||
"build",
|
||||
"lint",
|
||||
"test",
|
||||
"e2e",
|
||||
],
|
||||
},
|
||||
"runner": "@nrwl/workspace/tasks-runners/default",
|
||||
},
|
||||
},
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`new should generate an empty workspace.json 1`] = `
|
||||
Object {
|
||||
"cli": Object {
|
||||
"defaultCollection": "@nrwl/workspace",
|
||||
},
|
||||
"projects": Object {},
|
||||
"version": 2,
|
||||
}
|
||||
`;
|
||||
65
packages/workspace/src/schematics/new/new.spec.ts
Normal file
65
packages/workspace/src/schematics/new/new.spec.ts
Normal file
@ -0,0 +1,65 @@
|
||||
import { createTree } from '@nrwl/devkit/testing';
|
||||
import { readJson, Tree } from '@nrwl/devkit';
|
||||
import { newGenerator, Preset, Schema } from './new';
|
||||
import { Linter } from '../../utils/lint';
|
||||
|
||||
const defaultOptions: Omit<Schema, 'name' | 'directory' | 'appName'> = {
|
||||
cli: 'nx',
|
||||
preset: Preset.Empty,
|
||||
skipInstall: false,
|
||||
skipGit: false,
|
||||
linter: Linter.EsLint,
|
||||
defaultBase: 'master',
|
||||
};
|
||||
|
||||
describe('new', () => {
|
||||
let tree: Tree;
|
||||
|
||||
beforeEach(() => {
|
||||
tree = createTree();
|
||||
});
|
||||
|
||||
it('should generate an empty workspace.json', async () => {
|
||||
await newGenerator(tree, {
|
||||
...defaultOptions,
|
||||
name: 'my-workspace',
|
||||
directory: 'my-workspace',
|
||||
npmScope: 'npmScope',
|
||||
appName: 'app',
|
||||
});
|
||||
expect(readJson(tree, 'my-workspace/workspace.json')).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should generate an empty nx.json', async () => {
|
||||
await newGenerator(tree, {
|
||||
...defaultOptions,
|
||||
name: 'my-workspace',
|
||||
directory: 'my-workspace',
|
||||
npmScope: 'npmScope',
|
||||
appName: 'app',
|
||||
});
|
||||
expect(readJson(tree, 'my-workspace/nx.json')).toMatchSnapshot();
|
||||
});
|
||||
|
||||
describe('--preset', () => {
|
||||
describe.each([[Preset.Empty], [Preset.Angular], [Preset.React]])(
|
||||
'%s',
|
||||
(preset) => {
|
||||
beforeEach(async () => {
|
||||
await newGenerator(tree, {
|
||||
...defaultOptions,
|
||||
name: 'my-workspace',
|
||||
directory: 'my-workspace',
|
||||
npmScope: 'npmScope',
|
||||
appName: 'app',
|
||||
preset,
|
||||
});
|
||||
});
|
||||
|
||||
it('should generate necessary npm dependencies', () => {
|
||||
expect(readJson(tree, 'my-workspace/package.json')).toMatchSnapshot();
|
||||
});
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
@ -1,31 +1,21 @@
|
||||
import {
|
||||
chain,
|
||||
move,
|
||||
noop,
|
||||
Rule,
|
||||
schematic,
|
||||
SchematicContext,
|
||||
Tree,
|
||||
} from '@angular-devkit/schematics';
|
||||
import {
|
||||
NodePackageInstallTask,
|
||||
RepositoryInitializerTask,
|
||||
} from '@angular-devkit/schematics/tasks';
|
||||
formatFiles,
|
||||
updateJson,
|
||||
addDependenciesToPackageJson,
|
||||
installPackagesTask,
|
||||
getWorkspacePath,
|
||||
convertNxGenerator,
|
||||
names,
|
||||
getPackageManagerCommand,
|
||||
} from '@nrwl/devkit';
|
||||
|
||||
import {
|
||||
addDepsToPackageJson,
|
||||
updateWorkspaceInTree,
|
||||
} from '../../utils/ast-utils';
|
||||
|
||||
import { formatFiles } from '../../utils/rules/format-files';
|
||||
|
||||
import { nxVersion } from '../../utils/versions';
|
||||
import * as path from 'path';
|
||||
import { Observable } from 'rxjs';
|
||||
import { spawn } from 'child_process';
|
||||
import { getPackageManagerCommand } from '@nrwl/tao/src/shared/package-manager';
|
||||
import { join } from 'path';
|
||||
import * as yargsParser from 'yargs-parser';
|
||||
import { names } from '@nrwl/devkit';
|
||||
import { spawn, SpawnOptions } from 'child_process';
|
||||
|
||||
import { workspaceGenerator } from '../workspace/workspace';
|
||||
import { nxVersion } from '../../utils/versions';
|
||||
|
||||
export enum Preset {
|
||||
Empty = 'empty',
|
||||
@ -51,34 +41,20 @@ export interface Schema {
|
||||
nxCloud?: boolean;
|
||||
preset: Preset;
|
||||
commit?: { name: string; email: string; message?: string };
|
||||
defaultBase?: string;
|
||||
nxWorkspaceRoot?: string;
|
||||
defaultBase: string;
|
||||
linter: 'tslint' | 'eslint';
|
||||
packageManager?: string;
|
||||
}
|
||||
|
||||
class RunPresetTask {
|
||||
toConfiguration() {
|
||||
return {
|
||||
name: 'RunPreset',
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
function createPresetTaskExecutor(opts: Schema) {
|
||||
function generatePreset(host: Tree, opts: Schema) {
|
||||
const cliCommand = opts.cli === 'angular' ? 'ng' : 'nx';
|
||||
const parsedArgs = yargsParser(process.argv, {
|
||||
boolean: ['interactive'],
|
||||
});
|
||||
|
||||
return {
|
||||
name: 'RunPreset',
|
||||
create: () => {
|
||||
return Promise.resolve(() => {
|
||||
const spawnOptions = {
|
||||
stdio: [process.stdin, process.stdout, process.stderr],
|
||||
shell: true,
|
||||
cwd: path.join(opts.nxWorkspaceRoot || process.cwd(), opts.directory),
|
||||
cwd: join(host.root, opts.directory),
|
||||
};
|
||||
const pmc = getPackageManagerCommand();
|
||||
const executable = `${pmc.exec} ${cliCommand}`;
|
||||
@ -88,30 +64,91 @@ function createPresetTaskExecutor(opts: Schema) {
|
||||
`--name=${opts.appName}`,
|
||||
opts.style ? `--style=${opts.style}` : null,
|
||||
opts.linter ? `--linter=${opts.linter}` : null,
|
||||
opts.npmScope
|
||||
? `--npmScope=${opts.npmScope}`
|
||||
: `--npmScope=${opts.name}`,
|
||||
opts.npmScope ? `--npmScope=${opts.npmScope}` : `--npmScope=${opts.name}`,
|
||||
opts.preset ? `--preset=${opts.preset}` : null,
|
||||
`--cli=${cliCommand}`,
|
||||
parsedArgs.interactive ? '--interactive=true' : '--interactive=false',
|
||||
].filter((e) => !!e);
|
||||
return new Observable((obs) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
spawn(executable, args, spawnOptions).on('close', (code: number) => {
|
||||
if (code === 0) {
|
||||
obs.next();
|
||||
obs.complete();
|
||||
resolve();
|
||||
} else {
|
||||
const message = 'Workspace creation failed, see above.';
|
||||
obs.error(new Error(message));
|
||||
reject(new Error(message));
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export default function (options: Schema): Rule {
|
||||
async function initializeGitRepo(
|
||||
host: Tree,
|
||||
rootDirectory: string,
|
||||
options: Schema
|
||||
) {
|
||||
const execute = (args: ReadonlyArray<string>, ignoreErrorStream = false) => {
|
||||
const outputStream = 'ignore';
|
||||
const errorStream = ignoreErrorStream ? 'ignore' : process.stderr;
|
||||
const spawnOptions: SpawnOptions = {
|
||||
stdio: [process.stdin, outputStream, errorStream],
|
||||
shell: true,
|
||||
cwd: join(host.root, rootDirectory),
|
||||
env: {
|
||||
...process.env,
|
||||
...(options.commit.name
|
||||
? {
|
||||
GIT_AUTHOR_NAME: options.commit.name,
|
||||
GIT_COMMITTER_NAME: options.commit.name,
|
||||
}
|
||||
: {}),
|
||||
...(options.commit.email
|
||||
? {
|
||||
GIT_AUTHOR_EMAIL: options.commit.email,
|
||||
GIT_COMMITTER_EMAIL: options.commit.email,
|
||||
}
|
||||
: {}),
|
||||
},
|
||||
};
|
||||
return new Promise((resolve, reject) => {
|
||||
spawn('git', args, spawnOptions).on('close', (code) => {
|
||||
if (code === 0) {
|
||||
resolve();
|
||||
} else {
|
||||
reject(code);
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
const hasCommand = await execute(['--version']).then(
|
||||
() => true,
|
||||
() => false
|
||||
);
|
||||
if (!hasCommand) {
|
||||
return;
|
||||
}
|
||||
const insideRepo = await execute(
|
||||
['rev-parse', '--is-inside-work-tree'],
|
||||
true
|
||||
).then(
|
||||
() => true,
|
||||
() => false
|
||||
);
|
||||
if (insideRepo) {
|
||||
console.info(
|
||||
`Directory is already under version control. Skipping initialization of git.`
|
||||
);
|
||||
return;
|
||||
}
|
||||
await execute(['init']);
|
||||
await execute(['add', '.']);
|
||||
if (options.commit) {
|
||||
const message = options.commit.message || 'initial commit';
|
||||
await execute(['commit', `-m "${message}"`]);
|
||||
}
|
||||
console.info('Successfully initialized git.');
|
||||
}
|
||||
|
||||
export async function newGenerator(host: Tree, options: Schema) {
|
||||
if (
|
||||
options.skipInstall &&
|
||||
options.preset !== 'empty' &&
|
||||
@ -125,139 +162,97 @@ export default function (options: Schema): Rule {
|
||||
|
||||
options = normalizeOptions(options);
|
||||
|
||||
const layout = options.preset === 'oss' ? 'packages' : 'apps-and-libs';
|
||||
const layout: 'packages' | 'apps-and-libs' =
|
||||
options.preset === 'oss' ? 'packages' : 'apps-and-libs';
|
||||
const workspaceOpts = {
|
||||
...options,
|
||||
layout,
|
||||
preset: undefined,
|
||||
nxCloud: undefined,
|
||||
};
|
||||
return (host: Tree, context: SchematicContext) => {
|
||||
const engineHost = (context.engine.workflow as any).engineHost;
|
||||
engineHost.registerTaskExecutor(createPresetTaskExecutor(options));
|
||||
workspaceGenerator(host, workspaceOpts);
|
||||
|
||||
return chain([
|
||||
schematic('workspace', workspaceOpts),
|
||||
options.cli === 'angular' ? setDefaultPackageManager(options) : noop(),
|
||||
setDefaultLinter(options),
|
||||
addPresetDependencies(options),
|
||||
addCloudDependencies(options),
|
||||
move('/', options.directory),
|
||||
addTasks(options),
|
||||
formatFiles({ skipFormat: false }, options.directory),
|
||||
])(Tree.empty(), context);
|
||||
if (options.cli === 'angular') {
|
||||
setDefaultPackageManager(host, options);
|
||||
}
|
||||
setDefaultLinter(host, options);
|
||||
addPresetDependencies(host, options);
|
||||
addCloudDependencies(host, options);
|
||||
|
||||
await formatFiles(host);
|
||||
host.listChanges().forEach((change) => {
|
||||
if (change.type !== 'DELETE') {
|
||||
host.rename(change.path, join(options.directory, change.path));
|
||||
}
|
||||
});
|
||||
return async () => {
|
||||
installPackagesTask(host, false, options.directory);
|
||||
await generatePreset(host, options);
|
||||
if (!options.skipGit) {
|
||||
await initializeGitRepo(host, options.directory, options);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function addCloudDependencies(options: Schema) {
|
||||
return options.nxCloud
|
||||
? addDepsToPackageJson({}, { '@nrwl/nx-cloud': 'latest' }, false)
|
||||
: noop();
|
||||
export default newGenerator;
|
||||
export const newSchematic = convertNxGenerator(newGenerator);
|
||||
|
||||
function addCloudDependencies(host: Tree, options: Schema) {
|
||||
if (options.nxCloud) {
|
||||
return addDependenciesToPackageJson(
|
||||
host,
|
||||
{},
|
||||
{ '@nrwl/nx-cloud': 'latest' }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function addPresetDependencies(options: Schema) {
|
||||
if (options.preset === 'empty') {
|
||||
return noop();
|
||||
} else if (options.preset === 'web-components') {
|
||||
return addDepsToPackageJson(
|
||||
{},
|
||||
{
|
||||
'@nrwl/web': nxVersion,
|
||||
const presetDependencies: Omit<
|
||||
Record<
|
||||
Preset,
|
||||
{ dependencies: Record<string, string>; dev: Record<string, string> }
|
||||
>,
|
||||
Preset.Empty | Preset.OSS
|
||||
> = {
|
||||
[Preset.WebComponents]: { dependencies: {}, dev: { '@nrwl/web': nxVersion } },
|
||||
[Preset.Angular]: { dependencies: { '@nrwl/angular': nxVersion }, dev: {} },
|
||||
[Preset.AngularWithNest]: {
|
||||
dependencies: { '@nrwl/angular': nxVersion },
|
||||
dev: { '@nrwl/nest': nxVersion },
|
||||
},
|
||||
false
|
||||
);
|
||||
} else if (options.preset === 'angular') {
|
||||
return addDepsToPackageJson(
|
||||
{
|
||||
'@nrwl/angular': nxVersion,
|
||||
},
|
||||
{},
|
||||
false
|
||||
);
|
||||
} else if (options.preset === 'angular-nest') {
|
||||
return addDepsToPackageJson(
|
||||
{
|
||||
'@nrwl/angular': nxVersion,
|
||||
},
|
||||
{
|
||||
'@nrwl/nest': nxVersion,
|
||||
},
|
||||
false
|
||||
);
|
||||
} else if (options.preset === 'react') {
|
||||
return addDepsToPackageJson(
|
||||
{},
|
||||
{
|
||||
[Preset.React]: {
|
||||
dependencies: {},
|
||||
dev: {
|
||||
'@nrwl/react': nxVersion,
|
||||
},
|
||||
false
|
||||
);
|
||||
} else if (options.preset === 'react-express') {
|
||||
return addDepsToPackageJson(
|
||||
{},
|
||||
{
|
||||
},
|
||||
[Preset.ReactWithExpress]: {
|
||||
dependencies: {},
|
||||
dev: {
|
||||
'@nrwl/react': nxVersion,
|
||||
'@nrwl/express': nxVersion,
|
||||
},
|
||||
false
|
||||
);
|
||||
} else if (options.preset === 'next') {
|
||||
return addDepsToPackageJson(
|
||||
{},
|
||||
{
|
||||
'@nrwl/next': nxVersion,
|
||||
},
|
||||
false
|
||||
);
|
||||
} else if (options.preset === 'nest') {
|
||||
return addDepsToPackageJson(
|
||||
{},
|
||||
{
|
||||
[Preset.Nest]: {
|
||||
dependencies: {},
|
||||
dev: {
|
||||
'@nrwl/nest': nxVersion,
|
||||
},
|
||||
false
|
||||
);
|
||||
} else {
|
||||
return noop();
|
||||
}
|
||||
}
|
||||
},
|
||||
[Preset.NextJs]: {
|
||||
dependencies: {},
|
||||
dev: {
|
||||
'@nrwl/next': nxVersion,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
function addTasks(options: Schema) {
|
||||
return (host: Tree, context: SchematicContext) => {
|
||||
let packageTask;
|
||||
let presetInstallTask;
|
||||
if (!options.skipInstall) {
|
||||
packageTask = context.addTask(
|
||||
new NodePackageInstallTask(options.directory)
|
||||
);
|
||||
function addPresetDependencies(host: Tree, options: Schema) {
|
||||
if (options.preset === Preset.Empty || options.preset === Preset.OSS) {
|
||||
return;
|
||||
}
|
||||
if (options.preset !== 'empty') {
|
||||
const createPresetTask = context.addTask(new RunPresetTask(), [
|
||||
packageTask,
|
||||
]);
|
||||
|
||||
presetInstallTask = context.addTask(
|
||||
new NodePackageInstallTask(options.directory),
|
||||
[createPresetTask]
|
||||
);
|
||||
}
|
||||
if (!options.skipGit) {
|
||||
const commit =
|
||||
typeof options.commit == 'object'
|
||||
? options.commit
|
||||
: !!options.commit
|
||||
? {}
|
||||
: false;
|
||||
context.addTask(
|
||||
new RepositoryInitializerTask(options.directory, commit),
|
||||
presetInstallTask
|
||||
? [presetInstallTask]
|
||||
: packageTask
|
||||
? [packageTask]
|
||||
: []
|
||||
);
|
||||
}
|
||||
};
|
||||
const { dependencies, dev } = presetDependencies[options.preset];
|
||||
return addDependenciesToPackageJson(host, dependencies, dev);
|
||||
}
|
||||
|
||||
function normalizeOptions(options: Schema): Schema {
|
||||
@ -269,30 +264,29 @@ function normalizeOptions(options: Schema): Schema {
|
||||
return options;
|
||||
}
|
||||
|
||||
function setDefaultLinter({ linter, preset }: Schema): Rule {
|
||||
function setDefaultLinter(host: Tree, { linter, preset }: Schema) {
|
||||
// Don't do anything if someone doesn't pick angular
|
||||
if (preset === 'angular' || preset === 'angular-nest') {
|
||||
if (preset !== 'angular' && preset !== 'angular-nest') {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (linter) {
|
||||
case 'eslint': {
|
||||
return setESLintDefault();
|
||||
setESLintDefault(host);
|
||||
break;
|
||||
}
|
||||
case 'tslint': {
|
||||
return setTSLintDefault();
|
||||
setTSLintDefault(host);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
return noop();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return noop();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This sets ESLint as the default for any schematics that default to TSLint
|
||||
*/
|
||||
function setESLintDefault() {
|
||||
return updateWorkspaceInTree((json) => {
|
||||
function setESLintDefault(host: Tree) {
|
||||
updateJson(host, getWorkspacePath(host), (json) => {
|
||||
setDefault(json, '@nrwl/angular', 'application', 'linter', 'eslint');
|
||||
setDefault(json, '@nrwl/angular', 'library', 'linter', 'eslint');
|
||||
setDefault(
|
||||
@ -309,8 +303,8 @@ function setESLintDefault() {
|
||||
/**
|
||||
* This sets TSLint as the default for any schematics that default to ESLint
|
||||
*/
|
||||
function setTSLintDefault() {
|
||||
return updateWorkspaceInTree((json) => {
|
||||
function setTSLintDefault(host: Tree) {
|
||||
updateJson(host, getWorkspacePath(host), (json) => {
|
||||
setDefault(json, '@nrwl/workspace', 'library', 'linter', 'tslint');
|
||||
setDefault(json, '@nrwl/cypress', 'cypress-project', 'linter', 'tslint');
|
||||
setDefault(json, '@nrwl/cypress', 'cypress-project', 'linter', 'tslint');
|
||||
@ -325,12 +319,12 @@ function setTSLintDefault() {
|
||||
});
|
||||
}
|
||||
|
||||
function setDefaultPackageManager({ packageManager }: Schema) {
|
||||
function setDefaultPackageManager(host: Tree, { packageManager }: Schema) {
|
||||
if (!packageManager) {
|
||||
return noop();
|
||||
return;
|
||||
}
|
||||
|
||||
return updateWorkspaceInTree((json) => {
|
||||
updateJson(host, getWorkspacePath(host), (json) => {
|
||||
if (!json.cli) {
|
||||
json.cli = {};
|
||||
}
|
||||
|
||||
@ -3,6 +3,7 @@
|
||||
"id": "NxWorkspaceNew",
|
||||
"title": "Create an empty workspace",
|
||||
"type": "object",
|
||||
"cli": "nx",
|
||||
"properties": {
|
||||
"name": {
|
||||
"description": "The name of the workspace.",
|
||||
@ -13,17 +14,17 @@
|
||||
},
|
||||
"x-prompt": "What name would you like to use for the workspace?"
|
||||
},
|
||||
"cli": {
|
||||
"description": "CLI used for generating code and running tasks",
|
||||
"type": "string",
|
||||
"enum": ["nx", "angular"],
|
||||
"default": "nx"
|
||||
},
|
||||
"style": {
|
||||
"description": "The file extension to be used for style files.",
|
||||
"type": "string",
|
||||
"default": "css"
|
||||
},
|
||||
"directory": {
|
||||
"type": "string",
|
||||
"format": "path",
|
||||
"description": "The directory name to create the workspace in.",
|
||||
"default": ""
|
||||
},
|
||||
"npmScope": {
|
||||
"type": "string",
|
||||
"description": "Npm scope for importing libs."
|
||||
@ -82,11 +83,6 @@
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
},
|
||||
"nxWorkspaceRoot": {
|
||||
"type": "string",
|
||||
"description": "Root directory.",
|
||||
"hidden": true
|
||||
},
|
||||
"linter": {
|
||||
"description": "The tool to use for running lint checks.",
|
||||
"type": "string",
|
||||
|
||||
@ -0,0 +1,7 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`workspace should create a prettierrc file 1`] = `
|
||||
"{
|
||||
\\"singleQuote\\": true
|
||||
}"
|
||||
`;
|
||||
@ -1,6 +1,6 @@
|
||||
<% if(cli === 'angular') { %>
|
||||
|
||||
# <%= utils.classify(name) %>
|
||||
# <%= formattedNames.className %>
|
||||
|
||||
This project was generated using [Nx](https://nx.dev).
|
||||
|
||||
@ -92,7 +92,7 @@ Visit the [Nx Documentation](https://nx.dev/angular) to learn more.
|
||||
|
||||
<% } else { %>
|
||||
|
||||
# <%= utils.classify(name) %>
|
||||
# <%= formattedNames.className %>
|
||||
|
||||
This project was generated using [Nx](https://nx.dev).
|
||||
|
||||
|
||||
@ -1 +0,0 @@
|
||||
<%= defaultNrwlPrettierConfig %>
|
||||
@ -1,5 +1,5 @@
|
||||
{
|
||||
"name": "<%= utils.dasherize(name) %>",
|
||||
"name": "<%= formattedNames.fileName %>",
|
||||
"version": "0.0.0",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
export interface Schema {
|
||||
directory: string;
|
||||
name: string;
|
||||
npmScope?: string;
|
||||
skipInstall?: boolean;
|
||||
@ -8,5 +7,5 @@ export interface Schema {
|
||||
commit?: { name: string; email: string; message?: string };
|
||||
cli: 'nx' | 'angular';
|
||||
layout: 'apps-and-libs' | 'packages';
|
||||
defaultBase?: string;
|
||||
defaultBase: string;
|
||||
}
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/schema",
|
||||
"id": "SchematicsNxNgNew",
|
||||
"cli": "nx",
|
||||
"title": "Create an empty workspace",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@ -41,6 +42,7 @@
|
||||
"type": "string",
|
||||
"format": "path",
|
||||
"description": "The directory name to create the workspace in.",
|
||||
"x-deprecated": "This option is no longer used.",
|
||||
"default": ""
|
||||
},
|
||||
"layout": {
|
||||
|
||||
@ -1,16 +1,22 @@
|
||||
import { Tree } from '@angular-devkit/schematics';
|
||||
import { NxJson, readJsonInTree } from '@nrwl/workspace';
|
||||
import { runSchematic } from '../../utils/testing';
|
||||
import { readJson, Tree } from '@nrwl/devkit';
|
||||
import { workspaceGenerator } from './workspace';
|
||||
import { createTree } from '@nrwl/devkit/testing';
|
||||
import { NxJson } from '../../core/shared-interfaces';
|
||||
|
||||
describe('workspace', () => {
|
||||
let projectTree: Tree;
|
||||
describe('@nrwl/workspace:workspace', () => {
|
||||
let tree: Tree;
|
||||
|
||||
beforeEach(() => {
|
||||
projectTree = Tree.empty();
|
||||
tree = createTree();
|
||||
});
|
||||
|
||||
it('should create files', async () => {
|
||||
const tree = await runSchematic('workspace', { name: 'proj' }, projectTree);
|
||||
workspaceGenerator(tree, {
|
||||
name: 'proj',
|
||||
cli: 'nx',
|
||||
layout: 'apps-and-libs',
|
||||
defaultBase: 'main',
|
||||
});
|
||||
expect(tree.exists('/nx.json')).toBe(true);
|
||||
expect(tree.exists('/workspace.json')).toBe(true);
|
||||
expect(tree.exists('/.prettierrc')).toBe(true);
|
||||
@ -18,8 +24,13 @@ describe('workspace', () => {
|
||||
});
|
||||
|
||||
it('should create nx.json', async () => {
|
||||
const tree = await runSchematic('workspace', { name: 'proj' }, projectTree);
|
||||
const nxJson = readJsonInTree<NxJson>(tree, '/nx.json');
|
||||
workspaceGenerator(tree, {
|
||||
name: 'proj',
|
||||
cli: 'nx',
|
||||
layout: 'apps-and-libs',
|
||||
defaultBase: 'master',
|
||||
});
|
||||
const nxJson = readJson<NxJson>(tree, '/nx.json');
|
||||
expect(nxJson).toEqual({
|
||||
npmScope: 'proj',
|
||||
affected: {
|
||||
@ -48,9 +59,24 @@ describe('workspace', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('should create a prettierrc file', async () => {
|
||||
workspaceGenerator(tree, {
|
||||
name: 'proj',
|
||||
cli: 'nx',
|
||||
layout: 'apps-and-libs',
|
||||
defaultBase: 'main',
|
||||
});
|
||||
expect(tree.read('.prettierrc').toString()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should recommend vscode extensions', async () => {
|
||||
const tree = await runSchematic('workspace', { name: 'proj' }, projectTree);
|
||||
const recommendations = readJsonInTree<{ recommendations: string[] }>(
|
||||
workspaceGenerator(tree, {
|
||||
name: 'proj',
|
||||
cli: 'nx',
|
||||
layout: 'apps-and-libs',
|
||||
defaultBase: 'main',
|
||||
});
|
||||
const recommendations = readJson<{ recommendations: string[] }>(
|
||||
tree,
|
||||
'/.vscode/extensions.json'
|
||||
).recommendations;
|
||||
@ -62,12 +88,13 @@ describe('workspace', () => {
|
||||
});
|
||||
|
||||
it('should recommend vscode extensions (angular)', async () => {
|
||||
const tree = await runSchematic(
|
||||
'workspace',
|
||||
{ name: 'proj', cli: 'angular' },
|
||||
projectTree
|
||||
);
|
||||
const recommendations = readJsonInTree<{ recommendations: string[] }>(
|
||||
workspaceGenerator(tree, {
|
||||
name: 'proj',
|
||||
cli: 'angular',
|
||||
layout: 'apps-and-libs',
|
||||
defaultBase: 'main',
|
||||
});
|
||||
const recommendations = readJson<{ recommendations: string[] }>(
|
||||
tree,
|
||||
'/.vscode/extensions.json'
|
||||
).recommendations;
|
||||
@ -81,39 +108,42 @@ describe('workspace', () => {
|
||||
});
|
||||
|
||||
it('should add decorate-angular-cli when used with angular cli', async () => {
|
||||
const tree = await runSchematic(
|
||||
'workspace',
|
||||
{ name: 'proj', cli: 'angular' },
|
||||
projectTree
|
||||
);
|
||||
workspaceGenerator(tree, {
|
||||
name: 'proj',
|
||||
cli: 'angular',
|
||||
layout: 'apps-and-libs',
|
||||
defaultBase: 'main',
|
||||
});
|
||||
expect(tree.exists('/decorate-angular-cli.js')).toBe(true);
|
||||
const packageJson = readJsonInTree(tree, '/package.json');
|
||||
const packageJson = readJson(tree, '/package.json');
|
||||
expect(packageJson.scripts.postinstall).toEqual(
|
||||
'node ./decorate-angular-cli.js'
|
||||
);
|
||||
});
|
||||
|
||||
it('should not add decorate-angular-cli when used with nx cli', async () => {
|
||||
const tree = await runSchematic(
|
||||
'workspace',
|
||||
{ name: 'proj', cli: 'nx' },
|
||||
projectTree
|
||||
);
|
||||
workspaceGenerator(tree, {
|
||||
name: 'proj',
|
||||
cli: 'nx',
|
||||
layout: 'apps-and-libs',
|
||||
defaultBase: 'main',
|
||||
});
|
||||
expect(tree.exists('/decorate-angular-cli.js')).toBe(false);
|
||||
const packageJson = readJsonInTree(tree, '/package.json');
|
||||
const packageJson = readJson(tree, '/package.json');
|
||||
expect(packageJson.scripts.postinstall).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should create a workspace using package layout', async () => {
|
||||
const tree = await runSchematic(
|
||||
'workspace',
|
||||
{ name: 'proj', cli: 'nx', layout: 'packages' },
|
||||
projectTree
|
||||
);
|
||||
workspaceGenerator(tree, {
|
||||
name: 'proj',
|
||||
cli: 'nx',
|
||||
layout: 'packages',
|
||||
defaultBase: 'main',
|
||||
});
|
||||
expect(tree.exists('/packages/.gitkeep')).toBe(true);
|
||||
expect(tree.exists('/apps/.gitkeep')).toBe(false);
|
||||
expect(tree.exists('/libs/.gitkeep')).toBe(false);
|
||||
const nx = readJsonInTree(tree, '/nx.json');
|
||||
const nx = readJson(tree, '/nx.json');
|
||||
expect(nx.workspaceLayout).toEqual({
|
||||
appsDir: 'packages',
|
||||
libsDir: 'packages',
|
||||
|
||||
@ -1,17 +1,12 @@
|
||||
import {
|
||||
apply,
|
||||
branchAndMerge,
|
||||
chain,
|
||||
mergeWith,
|
||||
noop,
|
||||
Rule,
|
||||
SchematicContext,
|
||||
template,
|
||||
generateFiles,
|
||||
Tree,
|
||||
url,
|
||||
} from '@angular-devkit/schematics';
|
||||
updateJson,
|
||||
convertNxGenerator,
|
||||
names,
|
||||
writeJson,
|
||||
} from '@nrwl/devkit';
|
||||
import { Schema } from './schema';
|
||||
import { join, strings } from '@angular-devkit/core';
|
||||
import {
|
||||
angularCliVersion,
|
||||
eslintVersion,
|
||||
@ -21,7 +16,6 @@ import {
|
||||
} from '../../utils/versions';
|
||||
import { readFileSync } from 'fs';
|
||||
import { join as pathJoin } from 'path';
|
||||
import { updateJsonInTree } from '@nrwl/workspace';
|
||||
|
||||
export const DEFAULT_NRWL_PRETTIER_CONFIG = {
|
||||
singleQuote: true,
|
||||
@ -31,11 +25,11 @@ const decorateAngularClI = (host: Tree) => {
|
||||
const decorateCli = readFileSync(
|
||||
pathJoin(__dirname as any, '..', 'utils', 'decorate-angular-cli.js__tmpl__')
|
||||
).toString();
|
||||
host.create('decorate-angular-cli.js', decorateCli);
|
||||
host.write('decorate-angular-cli.js', decorateCli);
|
||||
};
|
||||
|
||||
function setWorkspaceLayoutProperties(options: Schema) {
|
||||
return updateJsonInTree('nx.json', (json) => {
|
||||
function setWorkspaceLayoutProperties(tree: Tree, options: Schema) {
|
||||
updateJson(tree, 'nx.json', (json) => {
|
||||
if (options.layout === 'packages') {
|
||||
json.workspaceLayout = {
|
||||
appsDir: 'packages',
|
||||
@ -46,27 +40,20 @@ function setWorkspaceLayoutProperties(options: Schema) {
|
||||
});
|
||||
}
|
||||
|
||||
function createAppsAndLibsFolders(options: Schema) {
|
||||
return (host: Tree) => {
|
||||
function createAppsAndLibsFolders(host: Tree, options: Schema) {
|
||||
if (options.layout === 'packages') {
|
||||
host.create('packages/.gitkeep', '');
|
||||
host.write('packages/.gitkeep', '');
|
||||
} else {
|
||||
host.create('apps/.gitkeep', '');
|
||||
host.create('libs/.gitkeep', '');
|
||||
host.write('apps/.gitkeep', '');
|
||||
host.write('libs/.gitkeep', '');
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export default function (options: Schema): Rule {
|
||||
if (!options.name) {
|
||||
throw new Error(`Invalid options, "name" is required.`);
|
||||
}
|
||||
|
||||
return (host: Tree, context: SchematicContext) => {
|
||||
function createFiles(host: Tree, options: Schema) {
|
||||
const npmScope = options.npmScope ? options.npmScope : options.name;
|
||||
const templateSource = apply(url('./files'), [
|
||||
template({
|
||||
utils: strings,
|
||||
const formattedNames = names(options.name);
|
||||
generateFiles(host, pathJoin(__dirname, './files'), '', {
|
||||
formattedNames,
|
||||
dot: '.',
|
||||
tmpl: '',
|
||||
workspaceFile: options.cli === 'angular' ? 'angular' : 'workspace',
|
||||
@ -80,22 +67,24 @@ export default function (options: Schema): Rule {
|
||||
...(options as object),
|
||||
nxVersion,
|
||||
npmScope,
|
||||
defaultNrwlPrettierConfig: JSON.stringify(
|
||||
DEFAULT_NRWL_PRETTIER_CONFIG,
|
||||
null,
|
||||
2
|
||||
),
|
||||
}),
|
||||
]);
|
||||
return chain([
|
||||
branchAndMerge(
|
||||
chain([
|
||||
mergeWith(templateSource),
|
||||
options.cli === 'angular' ? decorateAngularClI : noop(),
|
||||
setWorkspaceLayoutProperties(options),
|
||||
createAppsAndLibsFolders(options),
|
||||
])
|
||||
),
|
||||
])(host, context);
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
function createPrettierrc(host: Tree) {
|
||||
writeJson(host, '.prettierrc', DEFAULT_NRWL_PRETTIER_CONFIG);
|
||||
}
|
||||
|
||||
export function workspaceGenerator(host: Tree, options: Schema) {
|
||||
if (!options.name) {
|
||||
throw new Error(`Invalid options, "name" is required.`);
|
||||
}
|
||||
createFiles(host, options);
|
||||
createPrettierrc(host);
|
||||
if (options.cli === 'angular') {
|
||||
decorateAngularClI(host);
|
||||
}
|
||||
setWorkspaceLayoutProperties(host, options);
|
||||
createAppsAndLibsFolders(host, options);
|
||||
}
|
||||
|
||||
export const workspaceSchematic = convertNxGenerator(workspaceGenerator);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user