feat(schematics): upgrade to angular 7

This commit is contained in:
--get 2018-10-11 16:39:24 -04:00 committed by Victor Savkin
parent cd43c6d9ba
commit 67c4bdf4af
15 changed files with 2560 additions and 1143 deletions

View File

@ -1,4 +1,5 @@
tmp
/test
/build
node_modules
/package.json

View File

@ -7,7 +7,8 @@ import {
updateFile,
readJson,
readFile,
runCommand
runCommand,
runCLIAsync
} from '../utils';
describe('Nrwl Convert to Nx Workspace', () => {
@ -147,6 +148,13 @@ describe('Nrwl Convert to Nx Workspace', () => {
with: 'apps/proj/src/environments/environment.prod.ts'
}
],
budgets: [
{
maximumError: '5mb',
maximumWarning: '2mb',
type: 'initial'
}
],
optimization: true,
outputHashing: 'all',
sourceMap: false,
@ -294,6 +302,16 @@ describe('Nrwl Convert to Nx Workspace', () => {
outputPath: 'dist/apps/proj-server',
main: 'apps/proj/src/main.server.ts',
tsConfig: 'apps/proj/tsconfig.server.json'
},
configurations: {
production: {
fileReplacements: [
{
replace: 'src/environments/environment.ts',
with: 'src/environments/environment.prod.ts'
}
]
}
}
});
@ -316,7 +334,7 @@ describe('Nrwl Convert to Nx Workspace', () => {
runCLI('add @nrwl/schematics');
});
it('should handle workspaces with no e2e project', () => {
it('should handle workspaces with no e2e project', async () => {
// create a new AngularCLI app
runNgNew();
@ -327,7 +345,7 @@ describe('Nrwl Convert to Nx Workspace', () => {
updateFile('angular.json', JSON.stringify(existingAngularJson, null, 2));
// Add @nrwl/schematics
const result = runCLI(
const result = await runCLIAsync(
'add @nrwl/schematics --npmScope projscope --skip-install'
);
@ -337,7 +355,7 @@ describe('Nrwl Convert to Nx Workspace', () => {
'apps/proj/src/app/app.module.ts'
);
expect(result).toContain(
expect(result.stderr).toContain(
'No e2e project was migrated because there was none declared in angular.json'
);
});

View File

@ -5,10 +5,13 @@ import * as path from 'path';
const projectName: string = 'proj';
export function runNgNew(command?: string, silent?: boolean): string {
return execSync(`../node_modules/.bin/ng new proj ${command}`, {
cwd: `./tmp`,
...(silent ? { stdio: ['ignore', 'ignore', 'ignore'] } : {})
}).toString();
return execSync(
`../node_modules/.bin/ng new proj --no-interactive ${command}`,
{
cwd: `./tmp`,
...(silent ? { stdio: ['ignore', 'ignore', 'ignore'] } : {})
}
).toString();
}
export function newProject(): void {
@ -56,6 +59,10 @@ export function copyMissingPackages(): void {
'yargs-parser'
];
modulesToCopy.forEach(m => copyNodeModule(projectName, m));
execSync('rm -rf tmp/proj/node_modules/.bin/webpack');
execSync(
`cp -a node_modules/.bin/webpack tmp/proj/node_modules/.bin/webpack`
);
const libIndex = `./tmp/${projectName}/node_modules/@schematics/angular/library/index.js`;
const content = readFileSync(libIndex).toString();
@ -128,11 +135,11 @@ export function runCLI(
}
export function newApp(name: string): string {
return runCLI(`generate app ${name}`);
return runCLI(`generate app --no-interactive ${name}`);
}
export function newLib(name: string): string {
return runCLI(`generate lib ${name}`);
return runCLI(`generate lib --no-interactive ${name}`);
}
export function newModule(name: string): string {

View File

@ -9,36 +9,36 @@
"commit": "git-cz",
"checkcommit": "./scripts/commit-lint.js",
"e2e": "./scripts/e2e.sh",
"format": "prettier \"./**/*.{ts,js,json,css,md}\" \"!./**/{__name__,__directory__}/**\" --write",
"format": "prettier \"**/*.{ts,js,json,css,md}\" \"!**/{__name__,__directory__}/**\" --write",
"linknpm": "./scripts/link.sh",
"nx-release": "./scripts/nx-release.js",
"copy": "./scripts/copy.sh",
"test:schematics": "yarn linknpm fast && ./scripts/test_schematics.sh",
"test:nx": "yarn linknpm fast && ./scripts/test_nx.sh",
"test": "yarn linknpm fast && ./scripts/test_nx.sh && ./scripts/test_schematics.sh",
"checkformat": "prettier \"./**/*.{ts,js,json,css,md}\" \"!./**/{__name__,__directory__}/**\" --list-different"
"checkformat": "prettier \"**/*.{ts,js,json,css,md}\" \"!**/{__name__,__directory__}/**\" --list-different"
},
"devDependencies": {
"@angular-devkit/architect": "0.8.4",
"@angular-devkit/build-angular": "0.8.4",
"@angular-devkit/build-webpack": "0.8.4",
"@angular-devkit/core": "0.8.4",
"@angular-devkit/schematics": "0.8.4",
"@angular/cli": "6.2.4",
"@angular/common": "6.1.8",
"@angular/compiler": "6.1.8",
"@angular/compiler-cli": "6.1.8",
"@angular/core": "6.1.8",
"@angular/platform-browser": "6.1.8",
"@angular/platform-browser-dynamic": "6.1.8",
"@angular/router": "6.1.8",
"@angular/upgrade": "6.1.8",
"@angular-devkit/architect": "0.10.1",
"@angular-devkit/build-angular": "0.10.1",
"@angular-devkit/build-webpack": "0.10.1",
"@angular-devkit/core": "7.0.1",
"@angular-devkit/schematics": "7.0.1",
"@angular/cli": "7.0.1",
"@angular/common": "7.0.0",
"@angular/compiler": "7.0.0",
"@angular/compiler-cli": "7.0.0",
"@angular/core": "7.0.0",
"@angular/platform-browser": "7.0.0",
"@angular/platform-browser-dynamic": "7.0.0",
"@angular/router": "7.0.0",
"@angular/upgrade": "7.0.0",
"@ngrx/effects": "6.1.0",
"@ngrx/router-store": "6.1.0",
"@ngrx/schematics": "6.1.0",
"@ngrx/store": "6.1.0",
"@ngrx/store-devtools": "6.1.0",
"@schematics/angular": "0.8.4",
"@schematics/angular": "7.0.1",
"@types/jasmine": "~2.8.6",
"@types/jasminewd2": "~2.0.3",
"@types/jest": "^23.3.2",
@ -68,21 +68,23 @@
"karma-webpack": "2.0.4",
"license-webpack-plugin": "^1.4.0",
"lint-staged": "^7.2.2",
"ng-packagr": "3.0.6",
"ng-packagr": "4.3.1",
"npm-run-all": "4.1.2",
"opn": "^5.3.0",
"precise-commits": "1.0.2",
"prettier": "1.10.2",
"release-it": "^7.4.0",
"rxjs": "6.2.2",
"rxjs": "6.3.3",
"semver": "5.4.1",
"strip-json-comments": "2.0.1",
"tmp": "0.0.33",
"ts-loader": "^5.2.1",
"tsickle": "^0.33.0",
"tslib": "^1.9.3",
"tslint": "5.11.0",
"typescript": "~2.9.2",
"typescript": "~3.1.3",
"viz.js": "^1.8.1",
"webpack": "4.9.2",
"webpack": "4.19.1",
"webpack-node-externals": "^1.7.2",
"yargs": "^11.0.0",
"yargs-parser": "10.0.0",
@ -102,7 +104,7 @@
}
},
"lint-staged": {
"./**/*.{ts,js,json,css,md}": [
"**/*.{ts,js,json,css,md}": [
"prettier --write",
"git add"
]

View File

@ -24,13 +24,13 @@
"homepage": "https://github.com/nrwl/nx#readme",
"builders": "./src/builders.json",
"dependencies": {
"@angular-devkit/architect": "~0.8.0",
"@angular-devkit/build-webpack": "~0.8.0",
"@angular-devkit/architect": "~0.10.1",
"@angular-devkit/build-webpack": "~0.10.1",
"fork-ts-checker-webpack-plugin": "0.4.9",
"license-webpack-plugin": "^1.4.0",
"rxjs": "6.2.2",
"rxjs": "6.3.3",
"ts-loader": "5.2.1",
"webpack": "4.9.2",
"webpack": "4.19.1",
"webpack-node-externals": "1.7.2"
}
}

View File

@ -29,6 +29,11 @@
"version": "6.4.0",
"description": "Update to @angular/cli@6.2.3",
"factory": "./update-6-4-0/update-6-4-0"
},
"update-7.0.0": {
"version": "7.0.0",
"description": "Update to @angular v7",
"factory": "./update-7-0-0/update-7-0-0"
}
}
}

View File

@ -0,0 +1,37 @@
import { Tree } from '@angular-devkit/schematics';
import { SchematicTestRunner } from '@angular-devkit/schematics/testing';
import { serializeJson } from '../../src/utils/fileutils';
import * as path from 'path';
describe('Update 7.0.0', () => {
let initialTree: Tree;
let schematicRunner: SchematicTestRunner;
beforeEach(() => {
initialTree = Tree.empty();
initialTree.create(
'package.json',
serializeJson({
devDependencies: {
codelyzer: '~4.2.1'
}
})
);
schematicRunner = new SchematicTestRunner(
'@nrwl/schematics',
path.join(__dirname, '../migrations.json')
);
});
it('should update codeylzer', async () => {
const result = await schematicRunner
.runSchematicAsync('update-7.0.0', {}, initialTree)
.toPromise();
const codelyzerVersion = JSON.parse(result.readContent('package.json'))
.devDependencies.codelyzer;
expect(codelyzerVersion).toEqual('~4.5.0');
});
});

View File

@ -0,0 +1,26 @@
import { Rule, externalSchematic, chain } from '@angular-devkit/schematics';
import { updateJsonInTree } from '@nrwl/schematics/src/utils/ast-utils';
export default function(): Rule {
return chain([
updateJsonInTree('package.json', json => {
json.devDependencies = json.devDependencies || {};
json.devDependencies = {
...json.devDependencies,
codelyzer: '~4.5.0'
};
return json;
}),
externalSchematic('@schematics/update', 'update', {
packages: ['@angular/core'],
from: '6.1.0',
to: '7.0.0'
}),
externalSchematic('@schematics/update', 'update', {
packages: ['@angular/cli'],
from: '6.2.0',
to: '7.0.1'
})
]);
}

View File

@ -4,7 +4,7 @@ import { Tree, VirtualTree } from '@angular-devkit/schematics';
import { createEmptyWorkspace } from '../../utils/testing-utils';
import { getFileContent } from '@schematics/angular/utility/test';
import * as stripJsonComments from 'strip-json-comments';
import { readJsonInTree } from '../../utils/ast-utils';
import { readJsonInTree, updateJsonInTree } from '../../utils/ast-utils';
describe('app', () => {
const schematicRunner = new SchematicTestRunner(
@ -30,7 +30,7 @@ describe('app', () => {
expect(angularJson.projects['my-app'].root).toEqual('apps/my-app/');
expect(angularJson.projects['my-app-e2e'].root).toEqual(
'apps/my-app-e2e/'
'apps/my-app-e2e'
);
});
@ -127,6 +127,27 @@ describe('app', () => {
expect(myAppPrefix).toEqual('custom');
expect(appE2eSpec).toContain('Welcome to my-app!');
});
it('should work if the new project root is changed', async () => {
appTree = await schematicRunner
.callRule(
updateJsonInTree('/angular.json', json => ({
...json,
newProjectRoot: 'newProjectRoot'
})),
appTree
)
.toPromise();
const result = schematicRunner.runSchematic(
'app',
{ name: 'myApp' },
appTree
);
expect(result.exists('apps/my-app/src/main.ts')).toEqual(true);
expect(result.exists('apps/my-app-e2e/protractor.conf.js')).toEqual(true);
});
});
describe('nested', () => {
@ -142,7 +163,7 @@ describe('app', () => {
'apps/my-dir/my-app/'
);
expect(angularJson.projects['my-dir-my-app-e2e'].root).toEqual(
'apps/my-dir/my-app-e2e/'
'apps/my-dir/my-app-e2e'
);
});

View File

@ -17,7 +17,8 @@ import {
getDecoratorPropertyValueNode,
insert,
replaceNodeValue,
updateJsonInTree
updateJsonInTree,
readJsonInTree
} from '../../utils/ast-utils';
import { toFileName } from '../../utils/name-utils';
import { offsetFromRoot } from '@nrwl/schematics/src/utils/common';
@ -265,12 +266,17 @@ function updateE2eProject(options: NormalizedSchema): Rule {
return chain([
updateJsonInTree(getWorkspacePath(host), json => {
const project = json.projects[options.e2eProjectName];
const fixedProject = replaceAppNameWithPath(
project,
options.e2eProjectName,
project.root = options.e2eProjectRoot;
project.architect.e2e.options.protractorConfig = `${
options.e2eProjectRoot
);
json.projects[options.e2eProjectName] = fixedProject;
}/protractor.conf.js`;
project.architect.lint.options.tsConfig = `${
options.e2eProjectRoot
}/tsconfig.e2e.json`;
json.projects[options.e2eProjectName] = project;
return json;
}),
updateJsonInTree(`${options.e2eProjectRoot}/tsconfig.e2e.json`, json => {
@ -292,6 +298,17 @@ function updateE2eProject(options: NormalizedSchema): Rule {
export default function(schema: Schema): Rule {
return (host: Tree, context: SchematicContext) => {
const options = normalizeOptions(host, schema);
// Determine the roots where @schematics/angular will place the projects
// This is not where the projects actually end up
const angularJson = readJsonInTree(host, getWorkspacePath(host));
const appProjectRoot = angularJson.newProjectRoot
? `${angularJson.newProjectRoot}/${options.name}`
: options.name;
const e2eProjectRoot = angularJson.newProjectRoot
? `${angularJson.newProjectRoot}/${options.e2eProjectName}`
: 'e2e';
return chain([
externalSchematic('@schematics/angular', 'application', {
name: options.name,
@ -306,10 +323,10 @@ export default function(schema: Schema): Rule {
excludeUnnecessaryFiles(),
move(options.e2eProjectName, options.e2eProjectRoot),
move(e2eProjectRoot, options.e2eProjectRoot),
updateE2eProject(options),
move(options.name, options.appProjectRoot),
move(appProjectRoot, options.appProjectRoot),
updateProject(options),
updateComponentTemplate(options),

View File

@ -55,7 +55,7 @@
"@types/jasmine": "~2.8.6",
"@types/jasminewd2": "~2.0.3",
"@types/node": "~8.9.4",
"codelyzer": "~4.2.1",
"codelyzer": "~4.5.0",
"jasmine-core": "~2.99.1",
"jasmine-spec-reporter": "~4.2.1",
"karma": "~3.0.0",

View File

@ -5,10 +5,12 @@
"properties": {
"name": {
"type": "string",
"description": "Library name"
"description": "Library name",
"$default": {
"$source": "argv",
"index": 0
}
}
},
"required": [
"name"
]
"required": ["name"]
}

View File

@ -1,48 +1,23 @@
import { execSync } from 'child_process';
import * as fs from 'fs';
import { readFileSync, statSync, writeFileSync } from 'fs';
import { readFileSync, writeFileSync, statSync } from 'fs';
import * as path from 'path';
import { copySync, removeSync } from 'fs-extra';
import {
FileSystemEngineHost,
NodeModulesEngineHost,
validateOptionsWithSchema
} from '@angular-devkit/schematics/tools';
import { BuiltinTaskExecutor } from '@angular-devkit/schematics/tasks/node';
import {
CollectionDescription,
EngineHost,
RuleFactory,
Schematic,
SchematicDescription,
SchematicEngine,
Tree,
DryRunSink,
TypedSchematicContext,
HostSink,
HostTree
} from '@angular-devkit/schematics';
import { of } from 'rxjs';
import { concat, concatMap, ignoreElements, map } from 'rxjs/operators';
import { Url } from 'url';
import { NodeWorkflow } from '@angular-devkit/schematics/tools';
import { UnsuccessfulWorkflowExecution } from '@angular-devkit/schematics';
import * as yargsParser from 'yargs-parser';
import { CoreSchemaRegistry } from '@angular-devkit/core/src/json/schema';
import { standardFormats } from '@angular-devkit/schematics/src/formats';
import * as appRoot from 'app-root-path';
import { virtualFs, normalize } from '@angular-devkit/core';
import { NodeJsSyncHost } from '@angular-devkit/core/node';
import { virtualFs, normalize, JsonObject } from '@angular-devkit/core';
import { NodeJsSyncHost, createConsoleLogger } from '@angular-devkit/core/node';
const rootDirectory = appRoot.path;
export function workspaceSchematic(args: string[]) {
const outDir = compileTools();
const schematicName = args[0];
const { schematic, host, engine } = prepareExecutionContext(
outDir,
schematicName
);
executeSchematic(schematicName, parseOptions(args), schematic, host, engine);
const workflow = createWorkflow();
executeSchematic(schematicName, parseOptions(args), workflow, outDir);
}
// compile tools
@ -112,32 +87,25 @@ function schematicsDir() {
return path.join('tools', 'schematics');
}
// prepareExecutionContext
function prepareExecutionContext(outDir: string, schematicName: string) {
const engineHost = new EngineHostHandlingWorkspaceSchematics(outDir);
const engine = new SchematicEngine(engineHost);
const schematic = engine
.createCollection('workspace-schematics')
.createSchematic(schematicName);
const host = new virtualFs.ScopedHost(
new NodeJsSyncHost(),
normalize(rootDirectory)
);
return { schematic, host, engine };
function createWorkflow() {
const root = normalize(rootDirectory);
const host = new virtualFs.ScopedHost(new NodeJsSyncHost(), root);
return new NodeWorkflow(host, {
packageManager: fileExists('yarn.lock') ? 'yarn' : 'npm',
root
});
}
// execute schematic
function executeSchematic(
async function executeSchematic(
schematicName: string,
options: { [p: string]: any },
schematic: Schematic<any, any>,
host: virtualFs.Host,
engine: SchematicEngine<any, object>
workflow: NodeWorkflow,
outDir: string
) {
const dryRunSink = new DryRunSink(host, true);
let error = false;
dryRunSink.reporter.subscribe((event: any) => {
let nothingDone = true;
workflow.reporter.subscribe((event: any) => {
nothingDone = false;
const eventPath = event.path.startsWith('/')
? event.path.substr(1)
: event.path;
@ -148,7 +116,6 @@ function executeSchematic(
? 'already exists'
: 'does not exist.';
console.error(`error! ${eventPath} ${desc}.`);
error = true;
break;
case 'update':
console.log(`update ${eventPath} (${event.content.length} bytes)`);
@ -168,132 +135,42 @@ function executeSchematic(
}
});
const fsSink = new HostSink(host, true);
const args = options._.slice(1);
workflow.registry.addSmartDefaultProvider('argv', (schema: JsonObject) => {
if ('index' in schema) {
return args[+schema.index];
} else {
return args;
}
});
delete options._;
const logger = createConsoleLogger(true, process.stdout, process.stderr);
schematic
.call(options, of(new HostTree(host)))
.pipe(
map((tree: any) => Tree.optimize(tree)) as any,
concatMap((tree: any) =>
dryRunSink
.commit(tree)
.pipe(ignoreElements() as any, concat(of(tree)) as any)
) as any,
concatMap(
(tree: any) =>
error
? of(tree)
: fsSink
.commit(tree)
.pipe(ignoreElements() as any, concat(of(tree)) as any)
) as any,
concatMap(() => (error ? [] : engine.executePostTasks())) as any
)
.subscribe(
() => {},
e => {
console.error(`Error occurred while executing '${schematicName}':`);
console.error(e);
},
() => {
console.log(`'${schematicName}' completed.`);
}
);
}
try {
await workflow
.execute({
collection: path.join(outDir, 'workspace-schematics.json'),
schematic: schematicName,
options: options,
logger: logger
})
.toPromise();
/**
* It uses FileSystemEngineHost for collection named "workspace-tools" and NodeModulesEngineHost
* for everything else.
*/
class EngineHostHandlingWorkspaceSchematics implements EngineHost<any, any> {
readonly toolsHost: FileSystemEngineHost;
readonly defaultHost: NodeModulesEngineHost;
transformContext(): void {}
constructor(outDir: string) {
const transforms = validateOptionsWithSchema(
new CoreSchemaRegistry(standardFormats)
);
this.toolsHost = new FileSystemEngineHost(outDir);
this.toolsHost.registerOptionsTransform(transforms);
this.defaultHost = new NodeModulesEngineHost();
this.defaultHost.registerOptionsTransform(transforms);
this.defaultHost.registerTaskExecutor(BuiltinTaskExecutor.NodePackage, {
rootDirectory,
packageManager: fileExists('yarn.lock') ? 'yarn' : 'npm'
});
}
createCollectionDescription(name: string): CollectionDescription<any> {
return this.hostFor(name).createCollectionDescription(name);
}
createSchematicDescription(
name: string,
collection: CollectionDescription<any>
): SchematicDescription<any, any> | null {
return this.hostFor(collection.name).createSchematicDescription(
name,
collection
);
}
getSchematicRuleFactory<OptionT extends object>(
schematic: SchematicDescription<any, any>,
collection: CollectionDescription<any>
): RuleFactory<OptionT> {
return this.hostFor(collection.name).getSchematicRuleFactory(
schematic,
collection
);
}
createSourceFromUrl(url: Url, context: any): any {
return this.hostFor(context.schematic.collection.name).createSourceFromUrl(
url
);
}
transformOptions<OptionT extends object>(
schematic: SchematicDescription<any, any>,
options: OptionT
): any {
return this.hostFor(schematic.collection.name).transformOptions(
schematic,
options
);
}
listSchematics(collection: any): string[] {
return this.listSchematicNames(collection.description);
}
listSchematicNames(collection: CollectionDescription<any>): string[] {
return this.hostFor(collection.name).listSchematicNames(collection);
}
createTaskExecutor(name: string): any {
return this.defaultHost.createTaskExecutor(name);
}
hasTaskExecutor(name: string): boolean {
return this.defaultHost.hasTaskExecutor(name);
}
private hostFor(name: string) {
return name === 'workspace-schematics' ? this.toolsHost : this.defaultHost;
if (nothingDone) {
logger.info('Nothing to be done.');
}
} catch (err) {
if (err instanceof UnsuccessfulWorkflowExecution) {
// "See above" because we already printed the error.
logger.fatal('The Schematic workflow failed. See above.');
} else {
logger.fatal(err.stack || err.message);
}
}
}
function parseOptions(args: string[]): { [k: string]: any } {
const parsed = yargsParser(args);
if (parsed._ && parsed._.length > 1) {
parsed.name = parsed._[1];
}
delete parsed._;
return parsed;
return yargsParser(args);
}
function exists(file: string): boolean {

View File

@ -1,6 +1,6 @@
export const angularCliVersion = '6.2.4';
export const angularVersion = '^6.1.0';
export const angularDevkitVersion = '~0.8.0';
export const angularCliVersion = '7.0.1';
export const angularVersion = '^7.0.0';
export const angularDevkitVersion = '~0.10.0';
export const angularJsVersion = '1.6.6';
export const ngrxVersion = '6.1.0';
export const ngrxStoreFreezeVersion = '0.2.4';
@ -8,8 +8,8 @@ export const nxVersion = '*';
export const schematicsVersion = '*';
export const latestMigration = '20180507-create-nx-json';
export const prettierVersion = '1.13.7';
export const typescriptVersion = '~2.9.2';
export const rxjsVersion = '^6.0.0';
export const typescriptVersion = '~3.1.3';
export const rxjsVersion = '~6.3.3';
export const jestVersion = '^23.0.0';
export const jestPresetAngularVersion = '6.0.1';
export const jasmineMarblesVersion = '0.3.1';

3230
yarn.lock

File diff suppressed because it is too large Load Diff