220 lines
7.2 KiB
TypeScript

import {
apply,
branchAndMerge,
chain,
externalSchematic,
mergeWith,
move,
Rule,
template,
Tree,
url,
schematic
} from '@angular-devkit/schematics';
import { Schema } from './schema';
import * as path from 'path';
import { angularCliVersion, ngrxVersion, nxVersion, prettierVersion, schematicsVersion } from '../utility/lib-versions';
import * as fs from 'fs';
import { join } from 'path';
import { serializeJson, updateJsonFile } from '../utility/fileutils';
import { toFileName } from '@nrwl/schematics';
function updatePackageJson() {
return (host: Tree) => {
if (!host.exists('package.json')) {
throw new Error('Cannot find package.json');
}
const packageJson = JSON.parse(host.read('package.json')!.toString('utf-8'));
if (!packageJson.devDependencies) {
packageJson.devDependencies = {};
}
if (!packageJson.dependencies) {
packageJson.dependencies = {};
}
if (!packageJson.scripts) {
packageJson.scripts = {};
}
if (!packageJson.dependencies['@nrwl/nx']) {
packageJson.dependencies['@nrwl/nx'] = nxVersion;
}
if (!packageJson.dependencies['@ngrx/store']) {
packageJson.dependencies['@ngrx/store'] = ngrxVersion;
}
if (!packageJson.dependencies['@ngrx/router-store']) {
packageJson.dependencies['@ngrx/router-store'] = ngrxVersion;
}
if (!packageJson.dependencies['@ngrx/effects']) {
packageJson.dependencies['@ngrx/effects'] = ngrxVersion;
}
if (!packageJson.dependencies['@ngrx/store-devtools']) {
packageJson.dependencies['@ngrx/store-devtools'] = ngrxVersion;
}
if (!packageJson.devDependencies['@nrwl/schematics']) {
packageJson.devDependencies['@nrwl/schematics'] = schematicsVersion;
}
if (!packageJson.dependencies['@angular/cli']) {
packageJson.dependencies['@angular/cli'] = angularCliVersion;
}
if (!packageJson.devDependencies['prettier']) {
packageJson.devDependencies['prettier'] = prettierVersion;
}
packageJson.scripts['format'] = `prettier --single-quote --print-width 120 --write '{apps,libs}/**/*.ts'`;
host.overwrite('package.json', serializeJson(packageJson));
return host;
};
}
function updateAngularCLIJson(options: Schema) {
return (host: Tree) => {
if (!host.exists('.angular-cli.json')) {
throw new Error('Cannot find .angular-cli.json');
}
const angularCliJson = JSON.parse(host.read('.angular-cli.json')!.toString('utf-8'));
angularCliJson.project.npmScope = npmScope(options);
if (angularCliJson.apps.length !== 1) {
throw new Error('Can only convert projects with one app');
}
angularCliJson.lint = [
{ project: './tsconfig.app.json' },
{ project: './tsconfig.spec.json' },
{ project: './tsconfig.e2e.json' }
];
const app = angularCliJson.apps[0];
app.root = path.join('apps', options.name, app.root);
app.outDir = path.join('dist', 'apps', options.name);
app.test = '../../../test.js';
app.tsconfig = '../../../tsconfig.app.json';
app.testTsconfig = '../../../tsconfig.spec.json';
app.scripts = app.scripts.map(p => path.join('../../', p));
if (!angularCliJson.defaults) {
angularCliJson.defaults = {};
}
if (!angularCliJson.defaults.schematics) {
angularCliJson.defaults.schematics = {};
}
angularCliJson.defaults.schematics['collection'] = '@nrwl/schematics';
angularCliJson.defaults.schematics['postGenerate'] = 'npm run format';
angularCliJson.defaults.schematics['newProject'] = ['app', 'lib'];
host.overwrite('.angular-cli.json', serializeJson(angularCliJson));
return host;
};
}
function updateTsConfigsJson(options: Schema) {
return (host: Tree) => {
updateJsonFile('tsconfig.json', json => setUpCompilerOptions(json, npmScope(options)));
updateJsonFile('tsconfig.app.json', json => {
json['extends'] = './tsconfig.json';
if (!json.exclude) json.exclude = [];
json.exclude = dedup(json.exclude.concat(['**/*.spec.ts', '**/*.e2e-spec.ts', 'node_modules', 'tmp']));
setUpCompilerOptions(json, npmScope(options));
});
updateJsonFile('tsconfig.spec.json', json => {
json['extends'] = './tsconfig.json';
if (!json.exclude) json.exclude = [];
json.files = ['test.js'];
json.exclude = dedup(json.exclude.concat(['node_modules', 'tmp']));
setUpCompilerOptions(json, npmScope(options));
});
updateJsonFile('tsconfig.e2e.json', json => {
json['extends'] = './tsconfig.json';
if (!json.exclude) json.exclude = [];
json.exclude = dedup(json.exclude.concat(['**/*.spec.ts', 'node_modules', 'tmp']));
setUpCompilerOptions(json, npmScope(options));
});
return host;
};
}
function updateTsLintJson(options: Schema) {
return (host: Tree) => {
updateJsonFile('tslint.json', json => {
['no-trailing-whitespace', 'one-line', 'quotemark', 'typedef-whitespace', 'whitespace'].forEach(key => {
json[key] = undefined;
});
json['nx-enforce-module-boundaries'] = [true, { npmScope: npmScope(options), lazyLoad: [] }];
});
return host;
};
}
function npmScope(options: Schema): string {
return options && options.npmScope ? options.npmScope : options.name;
}
function updateProtractorConf() {
return (host: Tree) => {
if (!host.exists('protractor.conf.js')) {
throw new Error('Cannot find protractor.conf.js');
}
const protractorConf = host.read('protractor.conf.js')!.toString('utf-8');
const updatedConf = protractorConf
.replace(`./e2e/**/*.e2e-spec.ts`, `./apps/**/*.e2e-spec.ts`)
.replace(`e2e/tsconfig.e2e.json`, `./tsconfig.e2e.json`);
host.overwrite('protractor.conf.js', updatedConf);
return host;
};
}
function setUpCompilerOptions(tsconfig: any, npmScope: string): void {
if (!tsconfig.compilerOptions.paths) {
tsconfig.compilerOptions.paths = {};
}
tsconfig.compilerOptions.baseUrl = '.';
tsconfig.compilerOptions.paths[`@${npmScope}/*`] = ['libs/*'];
}
function moveFiles(options: Schema) {
return (host: Tree) => {
const angularCliJson = JSON.parse(host.read('.angular-cli.json')!.toString('utf-8'));
const app = angularCliJson.apps[0];
fs.mkdirSync('apps');
fs.mkdirSync('libs');
fs.unlinkSync(path.join(app.root, app.test));
fs.mkdirSync(path.join('apps', options.name));
fs.renameSync(path.join(app.root, app.tsconfig), 'tsconfig.app.json');
fs.renameSync(path.join(app.root, app.testTsconfig), 'tsconfig.spec.json');
fs.renameSync(path.join('e2e', 'tsconfig.e2e.json'), 'tsconfig.e2e.json');
fs.renameSync(app.root, join('apps', options.name, app.root));
fs.renameSync('e2e', join('apps', options.name, 'e2e'));
return host;
};
}
function dedup(array: any[]): any[] {
const res = [];
array.forEach(a => {
if (res.indexOf(a) === -1) {
res.push(a);
}
});
return res;
}
export default function(schema: Schema): Rule {
const options = { ...schema, name: toFileName(schema.name) };
return chain([
moveFiles(options),
branchAndMerge(chain([mergeWith(apply(url('./files'), []))])),
updatePackageJson(),
updateAngularCLIJson(options),
updateTsConfigsJson(options),
updateProtractorConf(),
updateTsLintJson(options)
]);
}