From a0a333be19671e0946398a009b7cc549a24dc5c6 Mon Sep 17 00:00:00 2001 From: Hans Date: Wed, 19 Sep 2018 19:01:13 -0700 Subject: [PATCH 1/2] ci: call main function from schematics bin --- bin/schematics | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/schematics b/bin/schematics index 748599df46c9..ea3f5176befa 100755 --- a/bin/schematics +++ b/bin/schematics @@ -11,4 +11,4 @@ require('../lib/bootstrap-local'); const packages = require('../lib/packages').packages; -require(packages['@angular-devkit/schematics-cli'].bin['schematics']); +require(packages['@angular-devkit/schematics-cli'].bin['schematics']).main({ args: process.argv.slice(2) }); From ef32681ee72c930ce3f0c0b64ddec2aac8ae3778 Mon Sep 17 00:00:00 2001 From: Hans Larsen Date: Mon, 24 Sep 2018 15:05:09 -0400 Subject: [PATCH 2/2] feat(@schematics/angular): new 7.0.0 migration to remove polyfills See PR https://github.com/angular/angular-cli/pull/12346 for more information. This PR migrates current CLI projects to the new polyfills introduced in that PR. --- .../migrations/migration-collection.json | 7 +- .../angular/migrations/update-7/index.ts | 1 + .../migrations/update-7/polyfill-metadata.ts | 136 ++++++++++++++++++ .../update-7/polyfill-metadata_spec.ts | 89 ++++++++++++ 4 files changed, 232 insertions(+), 1 deletion(-) create mode 100644 packages/schematics/angular/migrations/update-7/polyfill-metadata.ts create mode 100644 packages/schematics/angular/migrations/update-7/polyfill-metadata_spec.ts diff --git a/packages/schematics/angular/migrations/migration-collection.json b/packages/schematics/angular/migrations/migration-collection.json index adee986ffabe..fab2b498dcc3 100644 --- a/packages/schematics/angular/migrations/migration-collection.json +++ b/packages/schematics/angular/migrations/migration-collection.json @@ -9,6 +9,11 @@ "version": "7.0.0-beta.0", "factory": "./update-7", "description": "Update an Angular CLI project to version 7." + }, + "migration-03": { + "version": "7.0.0-rc.0", + "factory": "./update-7/index#polyfillMetadataRule", + "description": "Update an Angular CLI project to version 7." } } -} \ No newline at end of file +} diff --git a/packages/schematics/angular/migrations/update-7/index.ts b/packages/schematics/angular/migrations/update-7/index.ts index 626d4a449ef1..682463a0041c 100644 --- a/packages/schematics/angular/migrations/update-7/index.ts +++ b/packages/schematics/angular/migrations/update-7/index.ts @@ -13,6 +13,7 @@ import { } from '../../utility/dependencies'; import { latestVersions } from '../../utility/latest-versions'; +export { polyfillMetadataRule } from './polyfill-metadata'; export default function(): Rule { return (tree, context) => { diff --git a/packages/schematics/angular/migrations/update-7/polyfill-metadata.ts b/packages/schematics/angular/migrations/update-7/polyfill-metadata.ts new file mode 100644 index 000000000000..328457c8b2ff --- /dev/null +++ b/packages/schematics/angular/migrations/update-7/polyfill-metadata.ts @@ -0,0 +1,136 @@ +/** + * @license + * Copyright Google Inc. All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ +import { json } from '@angular-devkit/core'; +import { Rule, Tree, chain, noop } from '@angular-devkit/schematics'; +import * as ts from 'typescript'; + + +/** + * Remove the Reflect import from a polyfill file. + * @param tree The tree to use. + * @param path Path of the polyfill file found. + * @private + */ +function _removeReflectFromPolyfills(tree: Tree, path: string) { + const source = tree.read(path); + if (!source) { + return; + } + + // Start the update of the file. + const recorder = tree.beginUpdate(path); + + const sourceFile = ts.createSourceFile(path, source.toString(), ts.ScriptTarget.Latest); + const imports = ( + sourceFile.statements + .filter(s => s.kind === ts.SyntaxKind.ImportDeclaration) as ts.ImportDeclaration[] + ); + + for (const i of imports) { + const module = i.moduleSpecifier.kind == ts.SyntaxKind.StringLiteral + && (i.moduleSpecifier as ts.StringLiteral).text; + + switch (module) { + case 'core-js/es7/reflect': + recorder.remove(i.getStart(sourceFile), i.getWidth(sourceFile)); + break; + } + } + + tree.commitUpdate(recorder); +} + +/** + * Update a project's target, maybe. Only if it's a builder supported and the options look right. + * This is a rule factory so we return the new rule (or noop if we don't support doing the change). + * @param root The root of the project source. + * @param targetObject The target information. + * @private + */ +function _updateProjectTarget(root: string, targetObject: json.JsonObject): Rule { + // Make sure we're using the correct builder. + if (targetObject.builder !== '@angular-devkit/build-angular:browser' + || !json.isJsonObject(targetObject.options)) { + return noop(); + } + const options = targetObject.options; + if (typeof options.polyfills != 'string') { + return noop(); + } + + const polyfillsToUpdate = [`${root}/${options.polyfills}`]; + const configurations = targetObject.configurations; + if (json.isJsonObject(configurations)) { + for (const configName of Object.keys(configurations)) { + const config = configurations[configName]; + + // Just in case, only do non-AOT configurations. + if (json.isJsonObject(config) + && typeof config.polyfills == 'string' + && config.aot !== true) { + polyfillsToUpdate.push(`${root}/${config.polyfills}`); + } + } + } + + return chain( + polyfillsToUpdate.map(polyfillPath => { + return (tree: Tree) => _removeReflectFromPolyfills(tree, polyfillPath); + }), + ); +} + +/** + * Move the import reflect metadata polyfill from the polyfill file to the dev environment. This is + * not guaranteed to work, but if it doesn't it will result in no changes made. + */ +export function polyfillMetadataRule(): Rule { + return (tree) => { + // Simple. Take the ast of polyfills (if it exists) and find the import metadata. Remove it. + const angularConfigContent = tree.read('angular.json') || tree.read('.angular.json'); + const rules: Rule[] = []; + + if (!angularConfigContent) { + // Is this even an angular project? + return; + } + + const angularJson = json.parseJson(angularConfigContent.toString(), json.JsonParseMode.Loose); + + if (!json.isJsonObject(angularJson) || !json.isJsonObject(angularJson.projects)) { + // If that field isn't there, no use... + return; + } + + // For all projects, for all targets, read the polyfill field, and read the environment. + for (const projectName of Object.keys(angularJson.projects)) { + const project = angularJson.projects[projectName]; + if (!json.isJsonObject(project)) { + continue; + } + if (typeof project.root != 'string') { + continue; + } + + const targets = project.targets || project.architect; + if (!json.isJsonObject(targets)) { + continue; + } + + for (const targetName of Object.keys(targets)) { + const target = targets[targetName]; + if (json.isJsonObject(target)) { + rules.push(_updateProjectTarget(project.root, target)); + } + } + } + + // Remove null or undefined rules. + return chain(rules); + }; +} diff --git a/packages/schematics/angular/migrations/update-7/polyfill-metadata_spec.ts b/packages/schematics/angular/migrations/update-7/polyfill-metadata_spec.ts new file mode 100644 index 000000000000..beed4a5dbb58 --- /dev/null +++ b/packages/schematics/angular/migrations/update-7/polyfill-metadata_spec.ts @@ -0,0 +1,89 @@ +/** + * @license + * Copyright Google Inc. All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ +import { Action, EmptyTree } from '@angular-devkit/schematics'; +import { SchematicTestRunner, UnitTestTree } from '@angular-devkit/schematics/testing'; + +// Randomly import stuff (including es6 and es7 reflects). +const oldPolyfills = ` +/** IE9, IE10 and IE11 requires all of the following polyfills. **/ +// import 'core-js/es6/symbol'; +// import 'core-js/es6/object'; +import 'core-js/es6/function'; +import 'core-js/es6/parse-int'; +// import 'core-js/es6/parse-float'; +import 'core-js/es6/number'; +// import 'core-js/es6/math'; +// import 'core-js/es6/string'; +import 'core-js/es6/date'; +// import 'core-js/es6/array'; +// import 'core-js/es6/regexp'; + +/** IE10 and IE11 requires the following for the Reflect API. */ +import 'core-js/es6/reflect'; + + +/** Evergreen browsers require these. **/ +// Used for reflect-metadata in JIT. If you use AOT (and only Angular decorators), you can remove. +import 'core-js/es7/reflect'; + +import 'web-animations-js'; // Run \`npm install --save web-animations-js\`. + + (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame + (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick + +/*************************************************************************************************** + * Zone JS is required by default for Angular itself. + */ +import 'zone.js/dist/zone'; // Included with Angular CLI. +`; + + +describe('polyfillMetadataRule', () => { + const schematicRunner = new SchematicTestRunner( + 'migrations', + require.resolve('../migration-collection.json'), + ); + + let tree: UnitTestTree; + + beforeEach(async () => { + tree = new UnitTestTree(new EmptyTree()); + tree = await schematicRunner.runExternalSchematicAsync( + require.resolve('../../collection.json'), 'ng-new', + { + name: 'migration-test', + version: '1.2.3', + directory: '.', + }, + tree, + ).toPromise(); + }); + + it('is noop for new projects', async () => { + const mapToIdem = (x: Action) => { + const content = (x.kind == 'o' || x.kind == 'c') ? x.content.toString() : null; + + return { ...x, content, id: -1 }; + }; + + const expected = [...tree.actions.map(mapToIdem)]; + const tree2 = await schematicRunner.runSchematicAsync('migration-03', {}, tree.branch()) + .toPromise(); + + expect(tree2.actions.map(mapToIdem)).toEqual(expected); + }); + + it('should work as expected', async () => { + const polyfillPath = '/src/polyfills.ts'; + tree.overwrite(polyfillPath, oldPolyfills); + const tree2 = await schematicRunner.runSchematicAsync('migration-03', {}, tree.branch()) + .toPromise(); + + expect(tree2.readContent(polyfillPath)).not.toMatch(/import .*es7.*reflect.*;/); + }); +});