forked from angular/angular-cli
-
Notifications
You must be signed in to change notification settings - Fork 0
/
drop-es6-polyfills.ts
173 lines (151 loc) · 5.37 KB
/
drop-es6-polyfills.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
/**
* @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 {
MergeStrategy,
Rule,
Tree,
apply,
chain,
filter,
mergeWith,
move,
url,
} from '@angular-devkit/schematics';
import { createHash } from 'crypto';
import * as ts from '../../third_party/github.com/Microsoft/TypeScript/lib/typescript';
import { getWorkspace } from '../../utility/workspace';
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,
'core-js/es6/set': true,
'core-js/es6/weak-map': true,
};
const header = `/**
* 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
*/
/***************************************************************************************************
* BROWSER POLYFILLS
*/
`;
const applicationPolyfillsHeader = 'APPLICATION IMPORTS';
const browserPolyfillsHeader = 'BROWSER POLYFILLS';
const knownPolyfillHashes = [
'3dba718d7afe009e112e10d69073d2a2', // 6.0 - unmodified
'fccdb76b06ea636933f8b99b1c8d9725', // 6.0 - all core-js uncommented
'97e16639be1de06153695f5fefde745d', // 7.0 - unmodified
'd6c13d6dcf94ff3749283f33dd0d4864', // 7.0 - all core-js uncommented
'79bf0fd46c215e5f4145e15641c325f3', // 7.2 - unmodified
'6fe8080c7e38ee0ce677fdbc3884377a', // 7.2 - all core-js uncommented
'8e7f6abb3d2dca03b4dbb300e400a880', // 7.3 - unmodified
];
function dropES2015PolyfillsFromFile(polyfillPath: string): Rule {
return (tree: Tree) => {
const source = tree.read(polyfillPath);
if (!source) {
return;
}
const content = source.toString();
// Check if file is unmodified, if so then replace and return
const hash = createHash('md5');
// normalize line endings to increase hash match chances
hash.update(content.replace(/\r\n|\r/g, '\n'));
const digest = hash.digest('hex');
if (knownPolyfillHashes.includes(digest)) {
// Replace with new project polyfills file
// This removes the need to parse and also updates all included comments
// mergeWith overwrite doesn't work so clear out existing file
tree.delete(polyfillPath);
return mergeWith(
apply(url('../../application/files/src'), [
filter(path => path === '/polyfills.ts.template'),
move('/polyfills.ts.template', polyfillPath),
]),
MergeStrategy.Overwrite,
);
}
if (!content.includes('core-js')) {
// no action required if no mention of core-js
return;
}
const sourceFile = ts.createSourceFile(polyfillPath,
content.replace(/^\uFEFF/, ''),
ts.ScriptTarget.Latest,
true,
);
const imports = sourceFile.statements
.filter(s => s.kind === ts.SyntaxKind.ImportDeclaration) as ts.ImportDeclaration[];
if (imports.length === 0) { return; }
// Start the update of the file.
const recorder = tree.beginUpdate(polyfillPath);
const applicationPolyfillsStart = content.indexOf(applicationPolyfillsHeader);
const browserPolyfillsStart = content.indexOf(browserPolyfillsHeader);
let addHeader = false;
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());
if (i.getFullStart() <= browserPolyfillsStart) {
addHeader = true;
}
}
}
// We've removed the header since it's part of the JSDoc of the nodes we dropped
if (addHeader) {
recorder.insertLeft(0, header);
}
tree.commitUpdate(recorder);
};
}
/**
* Drop ES2015 polyfills from all application projects
*/
export function dropES2015Polyfills(): Rule {
return async (tree) => {
let workspace;
try {
workspace = await getWorkspace(tree);
} catch {
return;
}
const rules = [];
for (const [, project] of workspace.projects) {
if (project.extensions.projectType !== 'application') {
continue;
}
const buildTarget = project.targets.get('build');
const polyfills = buildTarget?.options?.polyfills;
if (polyfills && typeof polyfills === 'string') {
rules.push(dropES2015PolyfillsFromFile(polyfills));
}
}
return chain(rules);
};
}