feat(schematics): implement convert-to-workspace schematic

This commit is contained in:
vsavkin 2017-09-09 17:08:49 -04:00
parent 4da767ed82
commit b3b49e17a2
26 changed files with 317 additions and 33 deletions

View File

@ -0,0 +1,66 @@
import {checkFilesExists, cleanup, newApp, readFile, runCLI, runSchematic, updateFile} from '../utils';
describe('Nrwl Convert to Nx Workspace', () => {
beforeEach(cleanup);
fit('should generate a workspace', () => {
newApp('new proj --skip-install');
// update package.json
const packageJson = JSON.parse(readFile('proj/package.json'));
packageJson.description = "some description";
packageJson.dependencies['@ngrx/store'] = "4.0.3";
packageJson.devDependencies['@ngrx/router-store'] = "4.0.3";
updateFile('proj/package.json', JSON.stringify(packageJson, null, 2));
// update tsconfig.json
const tsconfigJson = JSON.parse(readFile('proj/tsconfig.json'));
tsconfigJson.compilerOptions.paths = {
'a': ['b']
};
updateFile('proj/tsconfig.json', JSON.stringify(tsconfigJson, null, 2));
// run the command
runSchematic('@nrwl/schematics:convert-to-workspace', { projectName: 'proj' });
// check that files have been moved!
checkFilesExists('proj/apps/proj/src/main.ts', 'proj/apps/proj/src/app/app.module.ts');
// check that package.json got merged
const updatedPackageJson = JSON.parse(readFile('proj/package.json'));
expect(updatedPackageJson.description).toEqual('some description');
expect(updatedPackageJson.dependencies['@ngrx/store']).toEqual('4.0.3');
expect(updatedPackageJson.devDependencies['@ngrx/router-store']).toEqual('4.0.3');
expect(updatedPackageJson.devDependencies['@nrwl/schematics']).toBeDefined();
expect(updatedPackageJson.dependencies['@nrwl/nx']).toBeDefined();
// check if angular-cli.json get merged
const updatedAngularCLIJson = JSON.parse(readFile('proj/.angular-cli.json'));
expect(updatedAngularCLIJson.apps[0].root).toEqual('apps/proj/src');
expect(updatedAngularCLIJson.apps[0].outDir).toEqual('dist/apps/proj');
expect(updatedAngularCLIJson.apps[0].test).toEqual('../../../test.js');
expect(updatedAngularCLIJson.apps[0].tsconfig).toEqual('../../../tsconfig.app.json');
expect(updatedAngularCLIJson.apps[0].testTsconfig).toEqual('../../../tsconfig.spec.json');
// check if tsconfig.json get merged
const updatedTsConfig = JSON.parse(readFile('proj/tsconfig.json'));
expect(updatedTsConfig.compilerOptions.paths).toEqual({
"a": ["b"],
"*": [
"*",
"libs/*",
"apps/*"
]
});
});
it('should build and test', () => {
newApp('new proj');
runSchematic('@nrwl/schematics:convert-to-workspace', { projectName: 'proj' });
expect(runCLI('build', { projectName: 'proj' })).toContain('{main} main.bundle.js');
expect(runCLI('test --single-run', { projectName: 'proj' })).toContain('Executed 3 of 3 SUCCESS');
});
});

View File

@ -140,7 +140,31 @@ import { MyLib } from 'mylib';
And then run `ng build --app=myapp`, it will build `mylib` and include it in the app's bundle. And then run `ng build --app=myapp`, it will build `mylib` and include it in the app's bundle.
#### Covert and Existing App into a Nx Workspace
If you have an existing CLI project, you can convert it into an Nx Workspace like this.
First, make sure you have `@nrwl/schematics` installed. Either run:
```
yarn global add @nrwl/schematics
```
Or run the following in the project dir:
```
yarn add @nrwl/schematics
```
Now, run:
```
schematics @nrwl/schematics:convert-to-workspace
```
* Your project files will be moved under:`apps/projectName`
* Some files have moved to the root: tsconfigs, test.js, so all apps and libs share them.
* `package.json` and `.angular-cli.json` will be updated
### NgRx ### NgRx

View File

@ -24,7 +24,7 @@ function addAppToAngularCliJson(options: Schema): Rule {
return (host: Tree) => { return (host: Tree) => {
const appConfig = { const appConfig = {
"name": options.name, "name": options.name,
"root": `apps/${options.name}/${options.sourceDir}`, "root": path.join('apps', options.name, options.sourceDir),
"outDir": `dist/apps/${options.name}`, "outDir": `dist/apps/${options.name}`,
"assets": [ "assets": [
"assets", "assets",

View File

@ -2,6 +2,12 @@
"name": "nx", "name": "nx",
"version": "0.1", "version": "0.1",
"schematics": { "schematics": {
"convert-to-workspace": {
"factory": "./convert-to-workspace",
"schema": "./convert-to-workspace/schema.json",
"description": "Convert an existing CLI project into an Nx Workspace"
},
"application": { "application": {
"factory": "./workspace", "factory": "./workspace",
"schema": "./workspace/schema.json", "schema": "./workspace/schema.json",

View File

@ -0,0 +1,165 @@
import {
apply, branchAndMerge, chain, externalSchematic, mergeWith, move, Rule, template, Tree,
url,
schematic
} from '@angular-devkit/schematics';
import {Schema} from './schema';
import {names, toFileName} from '@nrwl/schematics';
import * as path from 'path';
import {nxVersion, schematicsVersion} from '../utility/lib-versions';
import * as fs from 'fs';
import {join} from 'path';
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 = {};
}
packageJson.dependencies['@nrwl/nx'] = nxVersion;
packageJson.devDependencies['@nrwl/schematics'] = schematicsVersion;
host.overwrite('package.json', JSON.stringify(packageJson, null, 2));
return host;
};
}
function updateAngularCLIJson() {
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'));
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', angularCliJson.project.name, app.root);
app.outDir = path.join('dist', 'apps', angularCliJson.project.name);
app.test = '../../../test.js';
app.tsconfig = '../../../tsconfig.app.json';
app.testTsconfig = '../../../tsconfig.spec.json';
host.overwrite('.angular-cli.json', JSON.stringify(angularCliJson, null, 2));
return host;
};
}
function updateTsConfigsJson() {
return (host: Tree) => {
const tsconfigJson = JSON.parse(fs.readFileSync('tsconfig.json', 'utf-8'));
if (! tsconfigJson.compilerOptions.paths) {
tsconfigJson.compilerOptions.paths = {};
}
tsconfigJson.compilerOptions.baseUrl = '.';
tsconfigJson.compilerOptions.paths['*'] = [
"*",
"libs/*",
"apps/*"
];
fs.writeFileSync('tsconfig.json', JSON.stringify(tsconfigJson, null, 2));
const tsconfingAppJson = JSON.parse(fs.readFileSync('tsconfig.app.json', 'utf-8'));
tsconfingAppJson['extends'] = './tsconfig.json';
if (!tsconfingAppJson.exclude) {
tsconfingAppJson.exclude = [];
}
tsconfingAppJson.exclude = dedup(tsconfingAppJson.exclude.concat([
"**/*.spec.ts",
"**/*.e2e-spec.ts",
"node_modules",
"tmp"
]));
fs.writeFileSync('tsconfig.app.json', JSON.stringify(tsconfingAppJson, null, 2));
const tsconfingSpecJson = JSON.parse(fs.readFileSync('tsconfig.spec.json', 'utf-8'));
tsconfingSpecJson['extends'] = './tsconfig.json';
if (!tsconfingSpecJson.exclude) {
tsconfingSpecJson.exclude = [];
}
tsconfingSpecJson.files = [
'test.js'
];
tsconfingSpecJson.exclude = dedup(tsconfingSpecJson.exclude.concat([
"node_modules",
"tmp"
]));
fs.writeFileSync('tsconfig.spec.json', JSON.stringify(tsconfingSpecJson, null, 2));
const tsconfingE2eJson = JSON.parse(fs.readFileSync('tsconfig.e2e.json', 'utf-8'));
tsconfingE2eJson['extends'] = './tsconfig.json';
if (!tsconfingE2eJson.exclude) {
tsconfingE2eJson.exclude = [];
}
tsconfingE2eJson.exclude = dedup(tsconfingE2eJson.exclude.concat([
"**/*.spec.ts",
"node_modules",
"tmp"
]));
fs.writeFileSync('tsconfig.e2e.json', JSON.stringify(tsconfingE2eJson, null, 2));
return host;
};
}
function moveFiles() {
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', angularCliJson.project.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', angularCliJson.project.name, app.root));
fs.renameSync('e2e', join('apps', angularCliJson.project.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 (options: Schema): Rule {
return chain([
moveFiles(),
branchAndMerge(chain([
mergeWith( apply(url('./files'), [])),
])),
updatePackageJson(),
updateAngularCLIJson(),
updateTsConfigsJson()
]);
}

View File

@ -0,0 +1,2 @@
export interface Schema {
}

View File

@ -0,0 +1,11 @@
{
"$schema": "http://json-schema.org/schema",
"id": "convert-to-workspace",
"title": "Convert to Nx workspace",
"description": "NOTE: Does not work in the --dry-run mode",
"type": "object",
"properties": {
},
"required": [
]
}

View File

@ -1,2 +1,4 @@
export const angularJsVersion = "1.6.6"; export const angularJsVersion = "1.6.6";
export const ngrxVersion = "4.0.3"; export const ngrxVersion = "4.0.3";
export const nxVersion = "nrwl/nx-build";
export const schematicsVersion = "nrwl/schematics-build";

View File

@ -23,12 +23,12 @@
"@angular/router": "^4.2.4", "@angular/router": "^4.2.4",
"core-js": "^2.4.1", "core-js": "^2.4.1",
"rxjs": "^5.4.2", "rxjs": "^5.4.2",
"zone.js": "^0.8.14" "zone.js": "^0.8.14",
"@nrwl/nx": "nrwl/nx-build"
}, },
"devDependencies": { "devDependencies": {
"@angular/cli": "<%= version %>", "@angular/cli": "<%= version %>",
"@angular/compiler-cli": "^4.2.4", "@angular/compiler-cli": "^4.2.4",
"@nrwl/nx": "nrwl/nx-build",
"@nrwl/schematics": "nrwl/schematics-build", "@nrwl/schematics": "nrwl/schematics-build",
"@angular/language-service": "^4.2.4",<% if (!minimal) { %> "@angular/language-service": "^4.2.4",<% if (!minimal) { %>
"@types/jasmine": "~2.5.53", "@types/jasmine": "~2.5.53",

View File

@ -0,0 +1,30 @@
// This file is required by karma.conf.js and loads recursively all the .spec and framework files
require('zone.js/dist/long-stack-trace-zone');
require('zone.js/dist/proxy.js');
require('zone.js/dist/sync-test');
require('zone.js/dist/jasmine-patch');
require('zone.js/dist/async-test');
require('zone.js/dist/fake-async-test');
const getTestBed = require('@angular/core/testing').getTestBed;
const BrowserDynamicTestingModule = require('@angular/platform-browser-dynamic/testing').BrowserDynamicTestingModule;
const platformBrowserDynamicTesting = require('@angular/platform-browser-dynamic/testing').platformBrowserDynamicTesting;
// Prevent Karma from running prematurely.
__karma__.loaded = function () {};
// First, initialize the Angular testing environment.
getTestBed().initTestEnvironment(
BrowserDynamicTestingModule,
platformBrowserDynamicTesting()
);
// Then we find all the tests.
const contextApps = require.context('./apps', true, /\.spec\.ts$/);
// And load the modules.
contextApps.keys().map(contextApps);
const contextLibs = require.context('./libs', true, /\.spec\.ts$/);
// And load the modules.
contextLibs.keys().map(contextLibs);
// Finally, start Karma to run the tests.
__karma__.start();

View File

@ -2,17 +2,11 @@
"extends": "./tsconfig.json", "extends": "./tsconfig.json",
"compilerOptions": { "compilerOptions": {
"outDir": "./out-tsc/app", "outDir": "./out-tsc/app",
"module": "es2015", "module": "es2015"
"baseUrl": ".",
"paths": {
"*": [
"*",
"libs/*",
"apps/*"
]
}
}, },
"exclude": [ "exclude": [
"**/*.spec.ts",
"**/*.e2e-spec.ts",
"node_modules", "node_modules",
"tmp" "tmp"
] ]

View File

@ -2,23 +2,16 @@
"extends": "./tsconfig.json", "extends": "./tsconfig.json",
"compilerOptions": { "compilerOptions": {
"outDir": "./out-tsc/e2e", "outDir": "./out-tsc/e2e",
"baseUrl": "./",
"module": "commonjs", "module": "commonjs",
"target": "es5", "target": "es5",
"types": [ "types": [
"jasmine", "jasmine",
"jasminewd2", "jasminewd2",
"node" "node"
],
"paths": {
"*": [
"*",
"libs/*",
"apps/*"
] ]
}
}, },
"exclude": [ "exclude": [
"**/*.spec.ts",
"node_modules", "node_modules",
"tmp" "tmp"
] ]

View File

@ -2,20 +2,12 @@
"extends": "./tsconfig.json", "extends": "./tsconfig.json",
"compilerOptions": { "compilerOptions": {
"outDir": "./out-tsc/spec", "outDir": "./out-tsc/spec",
"baseUrl": "./",
"module": "commonjs", "module": "commonjs",
"target": "es5", "target": "es5",
"types": [ "types": [
"jasmine", "jasmine",
"node" "node"
],
"paths": {
"*": [
"*",
"libs/*",
"apps/*"
] ]
}
}, },
"files": [ "files": [
"test.js" "test.js"

View File

@ -13,8 +13,7 @@ export default function (options: Schema): Rule {
return chain([ return chain([
branchAndMerge(chain([ branchAndMerge(chain([
mergeWith(templateSource), mergeWith(templateSource)
move(options.directory)
])) ]))
]); ]);
} }

View File

@ -2,4 +2,4 @@
./scripts/link.sh ./scripts/link.sh
rm -rf tmp rm -rf tmp
jest --maxWorkers=1 ./build/e2e jest --maxWorkers=1 ./build/e2e/schematics/convert-to-workspace.test.js