Doug Parker eac18aed78 feat(@schematics/angular): drop polyfills required only for Internet Explorer now that support has been dropped for it
Removes the `classlist.js` and `web-animations-js` polyfills from any polyfills files in a workspace. Specifically, it looks for `import 'classlist.js';` and `import 'web-animations-js';`, then removes those them along with any preceeding comments (aside from the file overview and browser polyfills header comment).

Previous versions of `ng new` generated a polyfills file with commented out imports for both modules, to avoid including them by default while giving users an easy way to enable them. This migration also looks for these commented out imports and removes them along with associated descriptive comments. This has no effect on the application, but cleans up unnecessary noise from the polyfills file.

BREAKING CHANGE:

`classlist.js` and `web-animations-js` are removed from application polyfills and uninstalled from the package. These were only needed for compatibility with Internet Explorer, which is no longer needed now that Angular only supports evergreen browsers. See: https://angular.io/guide/browser-support.

Add the following to the polyfills file for an app to re-add these packages:

```typescript
import 'classlist.js';
import 'web-animations-js';
```

And then run:

```sh
npm install classlist.js web-animations-js --save
```
2021-10-06 06:11:23 -05:00

660 lines
20 KiB
TypeScript

/**
* @license
* Copyright Google LLC 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 { EmptyTree } from '@angular-devkit/schematics';
import { SchematicTestRunner, UnitTestTree } from '@angular-devkit/schematics/testing';
describe('Migration to remove polyfills specific to Internet Explorer', () => {
const schematicRunner = new SchematicTestRunner(
'migrations',
require.resolve('../migration-collection.json'),
);
let tree: UnitTestTree;
beforeEach(async () => {
tree = await schematicRunner
.runExternalSchematicAsync(
require.resolve('../../collection.json'),
'ng-new',
{
name: 'migration-test',
version: '1.2.3',
directory: '.',
},
new UnitTestTree(new EmptyTree()),
)
.toPromise();
});
it('should remove used `classlist.js` polyfill', async () => {
tree.overwrite(
'src/polyfills.ts',
`
/**
* IE11 requires the following for NgClass support on SVG elements
*/
import 'classlist.js';
// Other stuff.
/***************************************************************************************************
* Zone JS is required by default for Angular itself.
*/
import 'zone.js'; // Included with Angular CLI.
`.trim(),
);
const migrated = await schematicRunner
.runSchematicAsync('drop-ie-polyfills', {}, tree)
.toPromise();
expect(migrated.readContent('src/polyfills.ts').trim()).toBe(
`
// Other stuff.
/***************************************************************************************************
* Zone JS is required by default for Angular itself.
*/
import 'zone.js'; // Included with Angular CLI.
`.trim(),
);
});
it('should remove used `web-animations-js` polyfill', async () => {
tree.overwrite(
'src/polyfills.ts',
`
/**
* Web Animations \`@angular/platform-browser/animations\`
* Only required if AnimationBuilder is used within the application and using IE/Edge or Safari.
* Standard animation support in Angular DOES NOT require any polyfills (as of Angular 6.0).
*/
import 'web-animations-js';
// Other stuff.
/***************************************************************************************************
* Zone JS is required by default for Angular itself.
*/
import 'zone.js'; // Included with Angular CLI.
`.trim(),
);
const migrated = await schematicRunner
.runSchematicAsync('drop-ie-polyfills', {}, tree)
.toPromise();
expect(migrated.readContent('src/polyfills.ts').trim()).toBe(
`
// Other stuff.
/***************************************************************************************************
* Zone JS is required by default for Angular itself.
*/
import 'zone.js'; // Included with Angular CLI.
`.trim(),
);
});
it('should remove unused `classlist.js` polyfill', async () => {
tree.overwrite(
'src/polyfills.ts',
`
/**
* IE11 requires the following for NgClass support on SVG elements
*/
// import 'classlist.js'; // Run \`npm install --save classlist.js\`.
// Other stuff.
/***************************************************************************************************
* Zone JS is required by default for Angular itself.
*/
import 'zone.js'; // Included with Angular CLI.
`.trim(),
);
const migrated = await schematicRunner
.runSchematicAsync('drop-ie-polyfills', {}, tree)
.toPromise();
expect(migrated.readContent('src/polyfills.ts').trim()).toBe(
`
// Other stuff.
/***************************************************************************************************
* Zone JS is required by default for Angular itself.
*/
import 'zone.js'; // Included with Angular CLI.
`.trim(),
);
});
it('should remove unused `web-animations-js` polyfill', async () => {
tree.overwrite(
'src/polyfills.ts',
`
/**
* Web Animations \`@angular/platform-browser/animations\`
* Only required if AnimationBuilder is used within the application and using IE/Edge or Safari.
* Standard animation support in Angular DOES NOT require any polyfills (as of Angular 6.0).
*/
// import 'web-animations-js'; // Run \`npm install --save web-animations-js\`.
// Other stuff.
/***************************************************************************************************
* Zone JS is required by default for Angular itself.
*/
import 'zone.js'; // Included with Angular CLI.
`.trim(),
);
const migrated = await schematicRunner
.runSchematicAsync('drop-ie-polyfills', {}, tree)
.toPromise();
expect(migrated.readContent('src/polyfills.ts').trim()).toBe(
`
// Other stuff.
/***************************************************************************************************
* Zone JS is required by default for Angular itself.
*/
import 'zone.js'; // Included with Angular CLI.
`.trim(),
);
});
it('warns on a polyfill path that does not reference a valid file', async () => {
tree.delete('src/polyfills.ts');
const logs = [] as string[];
schematicRunner.logger.subscribe((log) => logs.push(log.message));
await schematicRunner.runSchematicAsync('drop-ie-polyfills', {}, tree).toPromise();
expect(logs).toEqual([
'Polyfill path from workspace configuration could not be read, does the file exist?',
]);
});
it('handles byte-order-marks (BOMs) in the polyfill file', async () => {
tree.overwrite(
'src/polyfills.ts',
// File with leading BOM (\uFEFF).
`
\uFEFF/**
* IE11 requires the following for NgClass support on SVG elements
*/
import 'classlist.js';
// Other stuff.
/***************************************************************************************************
* Zone JS is required by default for Angular itself.
*/
import 'zone.js'; // Included with Angular CLI.
`.trim(),
);
const migrated = await schematicRunner
.runSchematicAsync('drop-ie-polyfills', {}, tree)
.toPromise();
expect(migrated.readContent('src/polyfills.ts').trim()).toBe(
`
// Other stuff.
/***************************************************************************************************
* Zone JS is required by default for Angular itself.
*/
import 'zone.js'; // Included with Angular CLI.
`.trim(),
);
});
it('handles carriage returns with newlines if present', async () => {
tree.overwrite(
'src/polyfills.ts',
// File each `\r\n` for newline separators.
`
/**\r
* IE11 requires the following for NgClass support on SVG elements\r
*/\r
// import 'classlist.js'; // Run \`npm install --save classlist.js\`.\r
\r
// Other stuff.\r
/***************************************************************************************************\r
* Zone JS is required by default for Angular itself.\r
*/\r
import 'zone.js'; // Included with Angular CLI.\r
`.trim(),
);
const migrated = await schematicRunner
.runSchematicAsync('drop-ie-polyfills', {}, tree)
.toPromise();
expect(migrated.readContent('src/polyfills.ts').trim()).toBe(
`
// Other stuff.\r
/***************************************************************************************************\r
* Zone JS is required by default for Angular itself.\r
*/\r
import 'zone.js'; // Included with Angular CLI.\r
`.trim(),
);
});
it('removes older-style `classlist.js` polyfill comment', async () => {
// Previous Angular versions used a single-line comment, this should still be removed.
tree.overwrite(
'src/polyfills.ts',
`
/** IE11 requires the following for NgClass support on SVG elements */
import 'classlist.js';
// Other stuff.
/***************************************************************************************************
* Zone JS is required by default for Angular itself.
*/
import 'zone.js'; // Included with Angular CLI.
`.trim(),
);
const migrated = await schematicRunner
.runSchematicAsync('drop-ie-polyfills', {}, tree)
.toPromise();
expect(migrated.readContent('src/polyfills.ts').trim()).toBe(
`
// Other stuff.
/***************************************************************************************************
* Zone JS is required by default for Angular itself.
*/
import 'zone.js'; // Included with Angular CLI.
`.trim(),
);
});
it('should remove an unused polyfill from the last item in the file', async () => {
// TypeScript APIs require special-casing any trailing comments in the file, so we need to test
// this explicitly.
tree.overwrite(
'src/polyfills.ts',
`
/***************************************************************************************************
* Zone JS is required by default for Angular itself.
*/
import 'zone.js'; // Included with Angular CLI.
/**
* IE11 requires the following for NgClass support on SVG elements
*/
// import 'classlist.js'; // Run \`npm install --save classlist.js\`.
`.trim(),
);
const migrated = await schematicRunner
.runSchematicAsync('drop-ie-polyfills', {}, tree)
.toPromise();
expect(migrated.readContent('src/polyfills.ts').trim()).toBe(
`
/***************************************************************************************************
* Zone JS is required by default for Angular itself.
*/
import 'zone.js'; // Included with Angular CLI.
`.trim(),
);
});
it('keeps the file overview comment preceeding a used polyfill', async () => {
tree.overwrite(
'src/polyfills.ts',
`
/**
* This file includes polyfills needed by Angular and is loaded before the app.
* You can add your own extra polyfills to this file.
*
* This file is divided into 2 sections:
* 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers.
* 2. Application imports. Files imported after ZoneJS that should be loaded before your main
* file.
*
* The current setup is for so-called "evergreen" browsers; the last versions of browsers that
* automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera),
* Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile.
*
* Learn more in https://angular.io/guide/browser-support
*/
/**
* IE11 requires the following for NgClass support on SVG elements
*/
import 'classlist.js';
`.trim(),
);
const migrated = await schematicRunner
.runSchematicAsync('drop-ie-polyfills', {}, tree)
.toPromise();
expect(migrated.readContent('src/polyfills.ts').trim()).toBe(
`
/**
* This file includes polyfills needed by Angular and is loaded before the app.
* You can add your own extra polyfills to this file.
*
* This file is divided into 2 sections:
* 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers.
* 2. Application imports. Files imported after ZoneJS that should be loaded before your main
* file.
*
* The current setup is for so-called "evergreen" browsers; the last versions of browsers that
* automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera),
* Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile.
*
* Learn more in https://angular.io/guide/browser-support
*/
`.trim(),
);
});
it('keeps the file overview comment preceeding an unused polyfill', async () => {
tree.overwrite(
'src/polyfills.ts',
`
/**
* This file includes polyfills needed by Angular and is loaded before the app.
* You can add your own extra polyfills to this file.
*
* This file is divided into 2 sections:
* 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers.
* 2. Application imports. Files imported after ZoneJS that should be loaded before your main
* file.
*
* The current setup is for so-called "evergreen" browsers; the last versions of browsers that
* automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera),
* Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile.
*
* Learn more in https://angular.io/guide/browser-support
*/
/**
* IE11 requires the following for NgClass support on SVG elements
*/
// import 'classlist.js'; // Run \`npm install --save classlist.js\`.
`.trim(),
);
const migrated = await schematicRunner
.runSchematicAsync('drop-ie-polyfills', {}, tree)
.toPromise();
expect(migrated.readContent('src/polyfills.ts').trim()).toBe(
`
/**
* This file includes polyfills needed by Angular and is loaded before the app.
* You can add your own extra polyfills to this file.
*
* This file is divided into 2 sections:
* 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers.
* 2. Application imports. Files imported after ZoneJS that should be loaded before your main
* file.
*
* The current setup is for so-called "evergreen" browsers; the last versions of browsers that
* automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera),
* Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile.
*
* Learn more in https://angular.io/guide/browser-support
*/
`.trim(),
);
});
it('keeps the BROWSER POLYFILLS comment preceeding a used polyfill', async () => {
tree.overwrite(
'src/polyfills.ts',
`
/***************************************************************************************************
* BROWSER POLYFILLS
*/
/**
* IE11 requires the following for NgClass support on SVG elements
*/
import 'classlist.js';
`.trim(),
);
const migrated = await schematicRunner
.runSchematicAsync('drop-ie-polyfills', {}, tree)
.toPromise();
expect(migrated.readContent('src/polyfills.ts').trim()).toBe(
`
/***************************************************************************************************
* BROWSER POLYFILLS
*/
`.trim(),
);
});
it('keeps the BROWSER POLYFILLS comment preceeding an unused polyfill', async () => {
tree.overwrite(
'src/polyfills.ts',
`
/***************************************************************************************************
* BROWSER POLYFILLS
*/
/**
* IE11 requires the following for NgClass support on SVG elements
*/
// import 'classlist.js'; // Run \`npm install --save classlist.js\`.
`.trim(),
);
const migrated = await schematicRunner
.runSchematicAsync('drop-ie-polyfills', {}, tree)
.toPromise();
expect(migrated.readContent('src/polyfills.ts').trim()).toBe(
`
/***************************************************************************************************
* BROWSER POLYFILLS
*/
`.trim(),
);
});
it('ignores a `package.json` that does not include polyfill dependencies', async () => {
const packageJson = `
{
"name": "ng-new",
"version": "0.0.0",
"dependencies": {
"@angular/core": "^13.0.0"
},
"devDependencies": {
"@angular/cli": "^13.0.0"
}
}
`.trim();
tree.overwrite('package.json', packageJson);
const migrated = await schematicRunner
.runSchematicAsync('drop-ie-polyfills', {}, tree)
.toPromise();
expect(migrated.readContent('package.json')).toBe(packageJson);
// No `npm install` should be scheduled, nothing to remove.
expect(schematicRunner.tasks).toEqual([]);
});
it('uninstalls `classlist.js` and `web-animations-js` packages', async () => {
tree.overwrite(
'package.json',
`
{
"name": "ng-new",
"version": "0.0.0",
"dependencies": {
"@angular/core": "^13.0.0",
"classlist.js": "^1.0.0"
},
"devDependencies": {
"@angular/cli": "^13.0.0",
"web-animations-js": "^1.0.0"
}
}
`.trim(),
);
const migrated = await schematicRunner
.runSchematicAsync('drop-ie-polyfills', {}, tree)
.toPromise();
// Assert `package.json` no longer contains removed dependencies.
expect(migrated.readContent('package.json')).toBe(
`
{
"name": "ng-new",
"version": "0.0.0",
"dependencies": {
"@angular/core": "^13.0.0"
},
"devDependencies": {
"@angular/cli": "^13.0.0"
}
}
`.trim(),
);
// Assert that `npm install` is scheduled.
const taskNames = schematicRunner.tasks.map((task) => task.name);
expect(taskNames).toEqual(['node-package']);
});
it('removes preceeding newline from used polyfill', async () => {
tree.overwrite(
'src/polyfills.ts',
`
/** Some other polyfill. */
import 'some-other-polyfill';
/**
* IE11 requires the following for NgClass support on SVG elements
*/
import 'classlist.js';
// Other stuff.
/***************************************************************************************************
* Zone JS is required by default for Angular itself.
*/
import 'zone.js'; // Included with Angular CLI.
`.trim(),
);
const migrated = await schematicRunner
.runSchematicAsync('drop-ie-polyfills', {}, tree)
.toPromise();
expect(migrated.readContent('src/polyfills.ts').trim()).toBe(
`
/** Some other polyfill. */
import 'some-other-polyfill';
// Other stuff.
/***************************************************************************************************
* Zone JS is required by default for Angular itself.
*/
import 'zone.js'; // Included with Angular CLI.
`.trim(),
);
});
it('removes preceeding newline from unused polyfill', async () => {
tree.overwrite(
'src/polyfills.ts',
`
/** Some other polyfill. */
import 'some-other-polyfill';
/**
* IE11 requires the following for NgClass support on SVG elements
*/
// import 'classlist.js'; // Run \`npm install --save classlist.js\`.
// Other stuff.
/***************************************************************************************************
* Zone JS is required by default for Angular itself.
*/
import 'zone.js'; // Included with Angular CLI.
`.trim(),
);
const migrated = await schematicRunner
.runSchematicAsync('drop-ie-polyfills', {}, tree)
.toPromise();
expect(migrated.readContent('src/polyfills.ts').trim()).toBe(
`
/** Some other polyfill. */
import 'some-other-polyfill';
// Other stuff.
/***************************************************************************************************
* Zone JS is required by default for Angular itself.
*/
import 'zone.js'; // Included with Angular CLI.
`.trim(),
);
});
it('removes newlines between multiple removed polyfills', async () => {
tree.overwrite(
'src/polyfills.ts',
`
/** Some other polyfill. */
import 'some-other-polyfill';
/**
* IE11 requires the following for NgClass support on SVG elements
*/
// import 'classlist.js'; // Run \`npm install --save classlist.js\`.
/**
* Web Animations \`@angular/platform-browser/animations\`
* Only required if AnimationBuilder is used within the application and using IE/Edge or Safari.
* Standard animation support in Angular DOES NOT require any polyfills (as of Angular 6.0).
*/
import 'web-animations-js';
// Other stuff.
/***************************************************************************************************
* Zone JS is required by default for Angular itself.
*/
import 'zone.js'; // Included with Angular CLI.
`.trim(),
);
const migrated = await schematicRunner
.runSchematicAsync('drop-ie-polyfills', {}, tree)
.toPromise();
expect(migrated.readContent('src/polyfills.ts').trim()).toBe(
`
/** Some other polyfill. */
import 'some-other-polyfill';
// Other stuff.
/***************************************************************************************************
* Zone JS is required by default for Angular itself.
*/
import 'zone.js'; // Included with Angular CLI.
`.trim(),
);
});
});