Skip to content

Commit b84cd23

Browse files
committed
fix(@angular-devkit/build-angular): fully optimize script bundles with bundle downleveling
This also allows terser to perform ECMA 6 level compress optimizations on the actual application bundles (non-script bundles) which can provide for further size improvements. Fixes angular#15507
1 parent 87d2223 commit b84cd23

File tree

1 file changed

+55
-35
lines changed
  • packages/angular_devkit/build_angular/src/angular-cli-files/models/webpack-configs

1 file changed

+55
-35
lines changed

packages/angular_devkit/build_angular/src/angular-cli-files/models/webpack-configs/common.ts

Lines changed: 55 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import {
1818
Configuration,
1919
ContextReplacementPlugin,
2020
HashedModuleIdsPlugin,
21+
compilation,
2122
debug,
2223
} from 'webpack';
2324
import { RawSource } from 'webpack-sources';
@@ -146,34 +147,34 @@ export function getCommonConfig(wco: WebpackConfigOptions): Configuration {
146147
const hashFormat = getOutputHashFormat(buildOptions.outputHashing || 'none');
147148

148149
// process global scripts
149-
if (buildOptions.scripts.length > 0) {
150-
const globalScriptsByBundleName = normalizeExtraEntryPoints(
151-
buildOptions.scripts,
152-
'scripts',
153-
).reduce((prev: { bundleName: string; paths: string[]; inject: boolean }[], curr) => {
154-
const bundleName = curr.bundleName;
155-
const resolvedPath = path.resolve(root, curr.input);
156-
const existingEntry = prev.find(el => el.bundleName === bundleName);
157-
if (existingEntry) {
158-
if (existingEntry.inject && !curr.inject) {
159-
// All entries have to be lazy for the bundle to be lazy.
160-
throw new Error(
161-
`The ${curr.bundleName} bundle is mixing injected and non-injected scripts.`,
162-
);
163-
}
164-
165-
existingEntry.paths.push(resolvedPath);
166-
} else {
167-
prev.push({
168-
bundleName,
169-
paths: [resolvedPath],
170-
inject: curr.inject,
171-
});
150+
const globalScriptsByBundleName = normalizeExtraEntryPoints(
151+
buildOptions.scripts,
152+
'scripts',
153+
).reduce((prev: { bundleName: string; paths: string[]; inject: boolean }[], curr) => {
154+
const bundleName = curr.bundleName;
155+
const resolvedPath = path.resolve(root, curr.input);
156+
const existingEntry = prev.find(el => el.bundleName === bundleName);
157+
if (existingEntry) {
158+
if (existingEntry.inject && !curr.inject) {
159+
// All entries have to be lazy for the bundle to be lazy.
160+
throw new Error(
161+
`The ${curr.bundleName} bundle is mixing injected and non-injected scripts.`,
162+
);
172163
}
173164

174-
return prev;
175-
}, []);
165+
existingEntry.paths.push(resolvedPath);
166+
} else {
167+
prev.push({
168+
bundleName,
169+
paths: [resolvedPath],
170+
inject: curr.inject,
171+
});
172+
}
173+
174+
return prev;
175+
}, []);
176176

177+
if (globalScriptsByBundleName.length > 0) {
177178
// Add a new asset for each entry.
178179
globalScriptsByBundleName.forEach(script => {
179180
// Lazy scripts don't get a hash, otherwise they can't be loaded by name.
@@ -321,8 +322,8 @@ export function getCommonConfig(wco: WebpackConfigOptions): Configuration {
321322

322323
if (buildOptions.aot) {
323324
// Also try to load AOT-only global definitions.
324-
const GLOBAL_DEFS_FOR_TERSER_WITH_AOT =
325-
require('@angular/compiler-cli').GLOBAL_DEFS_FOR_TERSER_WITH_AOT;
325+
const GLOBAL_DEFS_FOR_TERSER_WITH_AOT = require('@angular/compiler-cli')
326+
.GLOBAL_DEFS_FOR_TERSER_WITH_AOT;
326327
if (GLOBAL_DEFS_FOR_TERSER_WITH_AOT) {
327328
angularGlobalDefinitions = {
328329
...angularGlobalDefinitions,
@@ -332,17 +333,10 @@ export function getCommonConfig(wco: WebpackConfigOptions): Configuration {
332333
}
333334

334335
const terserOptions = {
335-
// Use 5 if using bundle downleveling to ensure script bundles do not use ES2015+ features
336-
// Script bundles are shared for differential loading
337-
// Bundle processing will use the ES2015+ optimizations on the ES2015 bundles
338-
ecma:
339-
wco.supportES2015 &&
340-
(!differentialLoadingNeeded || (differentialLoadingNeeded && fullDifferential))
341-
? 6
342-
: 5,
343336
warnings: !!buildOptions.verbose,
344337
safari10: true,
345338
output: {
339+
ecma: wco.supportES2015 ? 6 : 5,
346340
comments: false,
347341
webkit: true,
348342
},
@@ -351,10 +345,12 @@ export function getCommonConfig(wco: WebpackConfigOptions): Configuration {
351345
compress:
352346
buildOptions.platform == 'server'
353347
? {
348+
ecma: wco.supportES2015 ? 6 : 5,
354349
global_defs: angularGlobalDefinitions,
355350
keep_fnames: true,
356351
}
357352
: {
353+
ecma: wco.supportES2015 ? 6 : 5,
358354
pure_getters: buildOptions.buildOptimizer,
359355
// PURE comments work best with 3 passes.
360356
// See https://github.com/webpack/webpack/issues/2899#issuecomment-317425926.
@@ -375,8 +371,32 @@ export function getCommonConfig(wco: WebpackConfigOptions): Configuration {
375371
parallel: true,
376372
cache: true,
377373
extractComments: false,
374+
chunkFilter: (chunk: compilation.Chunk) =>
375+
!globalScriptsByBundleName.some(s => s.bundleName === chunk.name),
378376
terserOptions,
379377
}),
378+
// Script bundles are fully optimized here in one step since they are never downleveled.
379+
// They are shared between ES2015 & ES5 outputs so must support ES5.
380+
new TerserPlugin({
381+
sourceMap: scriptsSourceMap,
382+
parallel: true,
383+
cache: true,
384+
extractComments: false,
385+
chunkFilter: (chunk: compilation.Chunk) =>
386+
globalScriptsByBundleName.some(s => s.bundleName === chunk.name),
387+
terserOptions: {
388+
...terserOptions,
389+
compress: {
390+
...terserOptions.compress,
391+
ecma: 5,
392+
},
393+
output: {
394+
...terserOptions.output,
395+
ecma: 5,
396+
},
397+
mangle: !manglingDisabled && buildOptions.platform !== 'server',
398+
},
399+
}),
380400
);
381401
}
382402

0 commit comments

Comments
 (0)