feat(@schematics/angular): drop es6 from modern polyfills

1. Remove imports of es6 polyfills introduced by the CLI.
2. Refactor the migrations for version 8 by moving the codelyzer and
polyfill transforms into different files.

The PR drops all `core-js/es6` polyfills that we've introduced with the
CLI, except the commented ones. We do not remove commented imports,
since they are not part of the internal es6 polyfills.

The migration automatically drops the
associated comments with the removed imports since they are part of the node - under its
`jsDoc` property.
This commit is contained in:
Minko Gechev 2019-03-01 13:56:34 -08:00 committed by Minko Gechev
parent 36d78debdb
commit a355e7d693
6 changed files with 269 additions and 84 deletions

View File

@ -25,7 +25,7 @@ export * from './rules/url';
export * from './tree/delegate';
export * from './tree/empty';
export * from './tree/host-tree';
export {UpdateRecorder} from './tree/interface';
export { UpdateRecorder } from './tree/interface';
export * from './engine/schematic';
export * from './sink/dryrun';
export * from './sink/filesystem';

View File

@ -0,0 +1,94 @@
/**
* @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 { JsonParseMode, parseJsonAst } from '@angular-devkit/core';
import {
Rule,
SchematicContext,
Tree,
} from '@angular-devkit/schematics';
import { NodePackageInstallTask } from '@angular-devkit/schematics/tasks';
import {
NodeDependency,
NodeDependencyType,
addPackageJsonDependency,
} from '../../utility/dependencies';
import { findPropertyInAstObject } from '../../utility/json-utils';
const ruleMapping: {[key: string]: string} = {
'contextual-life-cycle': 'contextual-lifecycle',
'no-conflicting-life-cycle-hooks': 'no-conflicting-lifecycle',
'no-life-cycle-call': 'no-lifecycle-call',
'use-life-cycle-interface': 'use-lifecycle-interface',
'decorator-not-allowed': 'contextual-decorator',
'enforce-component-selector': 'use-component-selector',
'no-output-named-after-standard-event': 'no-output-native',
'use-host-property-decorator': 'no-host-metadata-property',
'use-input-property-decorator': 'no-inputs-metadata-property',
'use-output-property-decorator': 'no-outputs-metadata-property',
'no-queries-parameter': 'no-queries-metadata-property',
'pipe-impure': 'no-pipe-impure',
'use-view-encapsulation': 'use-component-view-encapsulation',
i18n: 'template-i18n',
'banana-in-box': 'template-banana-in-box',
'no-template-call-expression': 'template-no-call-expression',
'templates-no-negated-async': 'template-no-negated-async',
'trackBy-function': 'template-use-track-by-function',
'no-attribute-parameter-decorator': 'no-attribute-decorator',
'max-inline-declarations': 'component-max-inline-declarations',
};
export const updateTsLintConfig = (): Rule => {
return (host: Tree) => {
const tsLintPath = '/tslint.json';
const buffer = host.read(tsLintPath);
if (!buffer) {
return host;
}
const tsCfgAst = parseJsonAst(buffer.toString(), JsonParseMode.Loose);
if (tsCfgAst.kind != 'object') {
return host;
}
const rulesNode = findPropertyInAstObject(tsCfgAst, 'rules');
if (!rulesNode || rulesNode.kind != 'object') {
return host;
}
const recorder = host.beginUpdate(tsLintPath);
rulesNode.properties.forEach(prop => {
const mapping = ruleMapping[prop.key.value];
if (mapping) {
recorder.remove(prop.key.start.offset + 1, prop.key.value.length);
recorder.insertLeft(prop.key.start.offset + 1, mapping);
}
});
host.commitUpdate(recorder);
return host;
};
};
export const updatePackageJson = () => {
return (host: Tree, context: SchematicContext) => {
const dependency: NodeDependency = {
type: NodeDependencyType.Dev,
name: 'codelyzer',
version: '^5.0.0',
overwrite: true,
};
addPackageJsonDependency(host, dependency);
context.addTask(new NodePackageInstallTask());
return host;
};
};

View File

@ -0,0 +1,77 @@
/**
* @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 { Rule, Tree } from '@angular-devkit/schematics';
import * as ts from '../../third_party/github.com/Microsoft/TypeScript/lib/typescript';
const toDrop: {[importName: string]: true} = {
'core-js/es6/symbol': true,
'core-js/es6/object': true,
'core-js/es6/function': true,
'core-js/es6/parse-int': true,
'core-js/es6/parse-float': true,
'core-js/es6/number': true,
'core-js/es6/math': true,
'core-js/es6/string': true,
'core-js/es6/date': true,
'core-js/es6/array': true,
'core-js/es6/regexp': true,
'core-js/es6/map': true,
'zone.js/dist/zone': true,
'core-js/es6/set': true,
};
const header = `/**
*/
/***************************************************************************************************
* BROWSER POLYFILLS
*/
/** IE9, IE10 and IE11 requires all of the following polyfills. **/
// import 'core-js/es6/weak-map';`;
const applicationPolyfillsHeader = 'APPLICATION IMPORTS';
export const dropES2015Polyfills = (): Rule => {
return (tree: Tree) => {
const path = '/polyfills.ts';
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, true);
const imports = sourceFile.statements
.filter(s => s.kind === ts.SyntaxKind.ImportDeclaration) as ts.ImportDeclaration[];
const applicationPolyfillsStart = sourceFile.getText().indexOf(applicationPolyfillsHeader);
if (imports.length === 0) { return; }
for (const i of imports) {
const module = ts.isStringLiteral(i.moduleSpecifier) && i.moduleSpecifier.text;
// We do not want to remove imports which are after the "APPLICATION IMPORTS" header.
if (module && toDrop[module] && applicationPolyfillsStart > i.getFullStart()) {
recorder.remove(i.getFullStart(), i.getFullWidth());
}
}
// We've removed the header since it's part of the JSDoc of the nodes we dropped
// As part of the header, we also add the comment for importing WeakMap since
// it's not part of the internal ES2015 polyfills we provide.
if (sourceFile.getText().indexOf(header) < 0) {
recorder.insertLeft(0, header);
}
tree.commitUpdate(recorder);
};
};

View File

@ -0,0 +1,93 @@
/**
* @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
*/
// tslint:disable:no-big-function
import { EmptyTree } from '@angular-devkit/schematics';
import { SchematicTestRunner, UnitTestTree } from '@angular-devkit/schematics/testing';
describe('Migration to version 8', () => {
const schematicRunner = new SchematicTestRunner(
'migrations',
require.resolve('../migration-collection.json'),
);
let tree: UnitTestTree;
const polyfillsPath = '/polyfills.ts';
const defaultOptions = {};
const polyfills = `/**
*/
/***************************************************************************************************
* BROWSER POLYFILLS
*/
/** 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';
import 'core-js/es6/map';
// import 'core-js/es6/weak-map';
import 'core-js/es6/set';
/** IE10 and IE11 requires the following for NgClass support on SVG elements */
// import 'classlist.js'; // Run "npm install --save classlist.js".
/** 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';
/***************************************************************************************************
* Zone JS is required by default for Angular itself.
*/
import 'zone.js/dist/zone'; // Included with Angular CLI.
/***************************************************************************************************
* APPLICATION IMPORTS
*/
`;
const packageJson = {
devDependencies: {
codelyzer: '^4.5.0',
},
};
const packageJsonPath = '/package.json';
describe('Migration to differential polyfill loading', () => {
beforeEach(() => {
tree = new UnitTestTree(new EmptyTree());
tree.create(polyfillsPath, polyfills);
tree.create(packageJsonPath, JSON.stringify(packageJson, null, 2));
});
it('should drop the es6 polyfills', () => {
tree = schematicRunner.runSchematic('migration-07', defaultOptions, tree);
const polyfills = tree.readContent(polyfillsPath);
expect(polyfills).not.toContain('core-js/es6/symbol');
expect(polyfills).not.toContain('core-js/es6/set');
expect(polyfills).not.toContain('zone.js');
expect(polyfills).not.toContain('Zone');
// We don't want to drop this commented import comments
expect(polyfills).toContain('core-js/es6/reflect');
expect(polyfills).toContain('core-js/es7/reflect');
expect(polyfills).toContain('BROWSER POLYFILLS');
expect(polyfills).toContain('core-js/es6/weak-map');
});
});
});

View File

@ -5,99 +5,20 @@
* 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 { JsonParseMode, parseJsonAst } from '@angular-devkit/core';
import {
Rule,
SchematicContext,
Tree,
chain,
} from '@angular-devkit/schematics';
import { NodePackageInstallTask } from '@angular-devkit/schematics/tasks';
import {
NodeDependency,
NodeDependencyType,
addPackageJsonDependency,
} from '../../utility/dependencies';
import { findPropertyInAstObject } from '../../utility/json-utils';
const ruleMapping: {[key: string]: string} = {
'contextual-life-cycle': 'contextual-lifecycle',
'no-conflicting-life-cycle-hooks': 'no-conflicting-lifecycle',
'no-life-cycle-call': 'no-lifecycle-call',
'use-life-cycle-interface': 'use-lifecycle-interface',
'decorator-not-allowed': 'contextual-decorator',
'enforce-component-selector': 'use-component-selector',
'no-output-named-after-standard-event': 'no-output-native',
'use-host-property-decorator': 'no-host-metadata-property',
'use-input-property-decorator': 'no-inputs-metadata-property',
'use-output-property-decorator': 'no-outputs-metadata-property',
'no-queries-parameter': 'no-queries-metadata-property',
'pipe-impure': 'no-pipe-impure',
'use-view-encapsulation': 'use-component-view-encapsulation',
i18n: 'template-i18n',
'banana-in-box': 'template-banana-in-box',
'no-template-call-expression': 'template-no-call-expression',
'templates-no-negated-async': 'template-no-negated-async',
'trackBy-function': 'template-use-track-by-function',
'no-attribute-parameter-decorator': 'no-attribute-decorator',
'max-inline-declarations': 'component-max-inline-declarations',
};
function updateTsLintConfig(): Rule {
return (host: Tree) => {
const tsLintPath = '/tslint.json';
const buffer = host.read(tsLintPath);
if (!buffer) {
return host;
}
const tsCfgAst = parseJsonAst(buffer.toString(), JsonParseMode.Loose);
if (tsCfgAst.kind != 'object') {
return host;
}
const rulesNode = findPropertyInAstObject(tsCfgAst, 'rules');
if (!rulesNode || rulesNode.kind != 'object') {
return host;
}
const recorder = host.beginUpdate(tsLintPath);
rulesNode.properties.forEach(prop => {
const mapping = ruleMapping[prop.key.value];
if (mapping) {
recorder.remove(prop.key.start.offset + 1, prop.key.value.length);
recorder.insertLeft(prop.key.start.offset + 1, mapping);
}
});
host.commitUpdate(recorder);
return host;
};
}
function updatePackageJson() {
return (host: Tree, context: SchematicContext) => {
const dependency: NodeDependency = {
type: NodeDependencyType.Dev,
name: 'codelyzer',
version: '^5.0.0',
overwrite: true,
};
addPackageJsonDependency(host, dependency);
context.addTask(new NodePackageInstallTask());
return host;
};
}
import { updatePackageJson, updateTsLintConfig } from './codelyzer-5';
import { dropES2015Polyfills } from './drop-es6-polyfills';
export default function(): Rule {
return () => {
return chain([
updateTsLintConfig(),
updatePackageJson(),
dropES2015Polyfills(),
]);
};
}