@angular-devkit/build-angular 0.900.0-rc.7 → 0.900.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,34 +1,34 @@
1
1
  {
2
2
  "name": "@angular-devkit/build-angular",
3
- "version": "0.900.0-rc.7",
3
+ "version": "0.900.1",
4
4
  "description": "Angular Webpack Build Facade",
5
5
  "experimental": true,
6
6
  "main": "src/index.js",
7
7
  "typings": "src/index.d.ts",
8
8
  "builders": "builders.json",
9
9
  "dependencies": {
10
- "@angular-devkit/architect": "0.900.0-rc.7",
11
- "@angular-devkit/build-optimizer": "0.900.0-rc.7",
12
- "@angular-devkit/build-webpack": "0.900.0-rc.7",
13
- "@angular-devkit/core": "9.0.0-rc.7",
14
- "@babel/core": "7.7.2",
15
- "@babel/generator": "7.7.2",
16
- "@babel/preset-env": "7.7.1",
17
- "@ngtools/webpack": "9.0.0-rc.7",
10
+ "@angular-devkit/architect": "0.900.1",
11
+ "@angular-devkit/build-optimizer": "0.900.1",
12
+ "@angular-devkit/build-webpack": "0.900.1",
13
+ "@angular-devkit/core": "9.0.1",
14
+ "@babel/core": "7.7.7",
15
+ "@babel/generator": "7.7.7",
16
+ "@babel/preset-env": "7.7.7",
17
+ "@ngtools/webpack": "9.0.1",
18
18
  "ajv": "6.10.2",
19
19
  "autoprefixer": "9.7.1",
20
20
  "babel-loader": "8.0.6",
21
- "browserslist": "4.7.2",
21
+ "browserslist": "4.8.3",
22
22
  "cacache": "13.0.1",
23
- "caniuse-lite": "1.0.30001006",
23
+ "caniuse-lite": "1.0.30001020",
24
+ "cssnano": "4.1.10",
24
25
  "circular-dependency-plugin": "5.2.0",
25
- "clean-css": "4.2.1",
26
+ "coverage-istanbul-loader": "2.0.3",
26
27
  "copy-webpack-plugin": "5.1.1",
27
- "core-js": "3.3.6",
28
+ "core-js": "3.6.0",
28
29
  "file-loader": "4.2.0",
29
30
  "find-cache-dir": "3.0.0",
30
31
  "glob": "7.1.5",
31
- "istanbul-instrumenter-loader": "3.0.1",
32
32
  "jest-worker": "24.9.0",
33
33
  "karma-source-map-support": "1.4.0",
34
34
  "less": "3.10.3",
@@ -58,9 +58,9 @@
58
58
  "style-loader": "1.0.0",
59
59
  "stylus": "0.54.7",
60
60
  "stylus-loader": "3.0.2",
61
- "tree-kill": "1.2.1",
62
- "terser": "4.4.2",
63
- "terser-webpack-plugin": "2.2.1",
61
+ "tree-kill": "1.2.2",
62
+ "terser": "4.5.1",
63
+ "terser-webpack-plugin": "2.3.3",
64
64
  "webpack": "4.41.2",
65
65
  "webpack-dev-middleware": "3.7.2",
66
66
  "webpack-dev-server": "3.9.0",
@@ -71,7 +71,7 @@
71
71
  },
72
72
  "peerDependencies": {
73
73
  "@angular/compiler-cli": ">=9.0.0-beta < 10",
74
- "typescript": ">=3.6 < 3.7"
74
+ "typescript": ">=3.6 < 3.8"
75
75
  },
76
76
  "peerDependenciesMeta": {
77
77
  "@angular/localize": {
@@ -92,7 +92,6 @@
92
92
  "engines": {
93
93
  "node": ">= 10.13.0",
94
94
  "npm": ">= 6.11.0",
95
- "pnpm": ">= 3.2.0",
96
95
  "yarn": ">= 1.13.0"
97
96
  },
98
97
  "author": "Angular Authors",
@@ -7,7 +7,7 @@
7
7
  */
8
8
  import { logging } from '@angular-devkit/core';
9
9
  import { ParsedConfiguration } from '@angular/compiler-cli';
10
- import { AssetPatternClass, Budget, ExtraEntryPoint, I18NMissingTranslation, Localize, OptimizationClass, SourceMapClass } from '../../browser/schema';
10
+ import { AssetPatternClass, Budget, CrossOrigin, ExtraEntryPoint, I18NMissingTranslation, Localize, OptimizationClass, SourceMapClass } from '../../browser/schema';
11
11
  import { NormalizedFileReplacement } from '../../utils/normalize-file-replacements';
12
12
  export interface BuildOptions {
13
13
  optimization: OptimizationClass;
@@ -46,6 +46,7 @@ export interface BuildOptions {
46
46
  showCircularDependencies?: boolean;
47
47
  buildOptimizer?: boolean;
48
48
  namedChunks?: boolean;
49
+ crossOrigin?: CrossOrigin;
49
50
  subresourceIntegrity?: boolean;
50
51
  serviceWorker?: boolean;
51
52
  webWorkerTsConfig?: string;
@@ -91,7 +91,10 @@ import 'core-js/modules/es.parse-float';
91
91
  import 'core-js/es/number';
92
92
  import 'core-js/es/math';
93
93
  import 'core-js/es/date';
94
- import 'core-js/es/regexp';
94
+
95
+ import 'core-js/modules/es.regexp.constructor';
96
+ import 'core-js/modules/es.regexp.to-string';
97
+ import 'core-js/modules/es.regexp.flags';
95
98
 
96
99
  import 'core-js/modules/es.map';
97
100
  import 'core-js/modules/es.weak-map';
@@ -12,38 +12,46 @@ const utils_1 = require("./utils");
12
12
  const SubresourceIntegrityPlugin = require('webpack-subresource-integrity');
13
13
  function getBrowserConfig(wco) {
14
14
  const { buildOptions } = wco;
15
+ const { crossOrigin = 'none', subresourceIntegrity, evalSourceMap, extractLicenses, vendorChunk, commonChunk, styles, } = buildOptions;
15
16
  const extraPlugins = [];
16
17
  let isEval = false;
17
18
  const { styles: stylesOptimization, scripts: scriptsOptimization } = buildOptions.optimization;
18
19
  const { styles: stylesSourceMap, scripts: scriptsSourceMap, hidden: hiddenSourceMap, } = buildOptions.sourceMap;
19
20
  // See https://webpack.js.org/configuration/devtool/ for sourcemap types.
20
21
  if ((stylesSourceMap || scriptsSourceMap) &&
21
- buildOptions.evalSourceMap &&
22
+ evalSourceMap &&
22
23
  !stylesOptimization &&
23
24
  !scriptsOptimization) {
24
25
  // Produce eval sourcemaps for development with serve, which are faster.
25
26
  isEval = true;
26
27
  }
27
- if (buildOptions.subresourceIntegrity) {
28
+ if (subresourceIntegrity) {
28
29
  extraPlugins.push(new SubresourceIntegrityPlugin({
29
30
  hashFuncNames: ['sha384'],
30
31
  }));
31
32
  }
32
- if (buildOptions.extractLicenses) {
33
+ if (extractLicenses) {
33
34
  extraPlugins.push(new license_webpack_plugin_1.LicenseWebpackPlugin({
34
35
  stats: {
35
36
  warnings: false,
36
37
  errors: false,
37
38
  },
38
39
  perChunkOutput: false,
39
- outputFilename: `3rdpartylicenses.txt`,
40
+ outputFilename: '3rdpartylicenses.txt',
40
41
  }));
41
42
  }
42
43
  if (!isEval && (scriptsSourceMap || stylesSourceMap)) {
43
44
  extraPlugins.push(utils_1.getSourceMapDevTool(scriptsSourceMap, stylesSourceMap, wco.differentialLoadingMode ? true : hiddenSourceMap));
44
45
  }
45
- const globalStylesBundleNames = utils_1.normalizeExtraEntryPoints(buildOptions.styles, 'styles')
46
+ const globalStylesBundleNames = utils_1.normalizeExtraEntryPoints(styles, 'styles')
46
47
  .map(style => style.bundleName);
48
+ let crossOriginLoading = false;
49
+ if (subresourceIntegrity && crossOrigin === 'none') {
50
+ crossOriginLoading = 'anonymous';
51
+ }
52
+ else if (crossOrigin !== 'none') {
53
+ crossOriginLoading = crossOrigin;
54
+ }
47
55
  return {
48
56
  devtool: isEval ? 'eval' : false,
49
57
  resolve: {
@@ -53,19 +61,19 @@ function getBrowserConfig(wco) {
53
61
  ],
54
62
  },
55
63
  output: {
56
- crossOriginLoading: buildOptions.subresourceIntegrity ? 'anonymous' : false,
64
+ crossOriginLoading,
57
65
  },
58
66
  optimization: {
59
67
  runtimeChunk: 'single',
60
68
  splitChunks: {
61
69
  maxAsyncRequests: Infinity,
62
70
  cacheGroups: {
63
- default: !!buildOptions.commonChunk && {
71
+ default: !!commonChunk && {
64
72
  chunks: 'async',
65
73
  minChunks: 2,
66
74
  priority: 10,
67
75
  },
68
- common: !!buildOptions.commonChunk && {
76
+ common: !!commonChunk && {
69
77
  name: 'common',
70
78
  chunks: 'async',
71
79
  minChunks: 2,
@@ -73,7 +81,7 @@ function getBrowserConfig(wco) {
73
81
  priority: 5,
74
82
  },
75
83
  vendors: false,
76
- vendor: !!buildOptions.vendorChunk && {
84
+ vendor: !!vendorChunk && {
77
85
  name: 'vendor',
78
86
  chunks: 'initial',
79
87
  enforce: true,
@@ -10,6 +10,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
10
10
  const build_optimizer_1 = require("@angular-devkit/build-optimizer");
11
11
  const core_1 = require("@angular-devkit/core");
12
12
  const CopyWebpackPlugin = require("copy-webpack-plugin");
13
+ const fs_1 = require("fs");
13
14
  const path = require("path");
14
15
  const typescript_1 = require("typescript");
15
16
  const webpack_1 = require("webpack");
@@ -18,8 +19,8 @@ const utils_1 = require("../../../utils");
18
19
  const cache_path_1 = require("../../../utils/cache-path");
19
20
  const environment_options_1 = require("../../../utils/environment-options");
20
21
  const bundle_budget_1 = require("../../plugins/bundle-budget");
21
- const cleancss_webpack_plugin_1 = require("../../plugins/cleancss-webpack-plugin");
22
22
  const named_chunks_plugin_1 = require("../../plugins/named-chunks-plugin");
23
+ const optimize_css_webpack_plugin_1 = require("../../plugins/optimize-css-webpack-plugin");
23
24
  const scripts_webpack_plugin_1 = require("../../plugins/scripts-webpack-plugin");
24
25
  const webpack_2 = require("../../plugins/webpack");
25
26
  const find_up_1 = require("../../utilities/find-up");
@@ -32,10 +33,6 @@ function getCommonConfig(wco) {
32
33
  const { root, projectRoot, buildOptions, tsConfig } = wco;
33
34
  const { styles: stylesOptimization, scripts: scriptsOptimization } = buildOptions.optimization;
34
35
  const { styles: stylesSourceMap, scripts: scriptsSourceMap, vendor: vendorSourceMap, } = buildOptions.sourceMap;
35
- const nodeModules = find_up_1.findUp('node_modules', projectRoot);
36
- if (!nodeModules) {
37
- throw new Error('Cannot locate node_modules directory.');
38
- }
39
36
  const extraPlugins = [];
40
37
  const extraRules = [];
41
38
  const entryPoints = {};
@@ -158,21 +155,24 @@ function getCommonConfig(wco) {
158
155
  }
159
156
  // process global scripts
160
157
  const globalScriptsByBundleName = utils_2.normalizeExtraEntryPoints(buildOptions.scripts, 'scripts').reduce((prev, curr) => {
161
- const bundleName = curr.bundleName;
162
- const resolvedPath = path.resolve(root, curr.input);
158
+ const { bundleName, inject, input } = curr;
159
+ const resolvedPath = path.resolve(root, input);
160
+ if (!fs_1.existsSync(resolvedPath)) {
161
+ throw new Error(`Script file ${input} does not exist.`);
162
+ }
163
163
  const existingEntry = prev.find(el => el.bundleName === bundleName);
164
164
  if (existingEntry) {
165
- if (existingEntry.inject && !curr.inject) {
165
+ if (existingEntry.inject && !inject) {
166
166
  // All entries have to be lazy for the bundle to be lazy.
167
- throw new Error(`The ${curr.bundleName} bundle is mixing injected and non-injected scripts.`);
167
+ throw new Error(`The ${bundleName} bundle is mixing injected and non-injected scripts.`);
168
168
  }
169
169
  existingEntry.paths.push(resolvedPath);
170
170
  }
171
171
  else {
172
172
  prev.push({
173
173
  bundleName,
174
+ inject,
174
175
  paths: [resolvedPath],
175
- inject: curr.inject,
176
176
  });
177
177
  }
178
178
  return prev;
@@ -274,12 +274,12 @@ function getCommonConfig(wco) {
274
274
  ? 'rxjs/_esm2015/path-mapping'
275
275
  : 'rxjs/_esm5/path-mapping';
276
276
  const rxPaths = require(require.resolve(rxjsPathMappingImport, { paths: [projectRoot] }));
277
- alias = rxPaths(nodeModules);
277
+ alias = rxPaths();
278
278
  }
279
279
  catch (_a) { }
280
280
  const extraMinimizers = [];
281
281
  if (stylesOptimization) {
282
- extraMinimizers.push(new cleancss_webpack_plugin_1.CleanCssWebpackPlugin({
282
+ extraMinimizers.push(new optimize_css_webpack_plugin_1.OptimizeCssWebpackPlugin({
283
283
  sourceMap: stylesSourceMap,
284
284
  // component styles retain their original file name
285
285
  test: file => /\.(?:css|scss|sass|less|styl)$/.test(file),
@@ -313,31 +313,34 @@ function getCommonConfig(wco) {
313
313
  safari10: true,
314
314
  output: {
315
315
  ecma: terserEcma,
316
+ // For differential loading, this is handled in the bundle processing.
317
+ // This should also work with just true but the experimental rollup support breaks without this check.
318
+ ascii_only: !differentialLoadingMode,
316
319
  // default behavior (undefined value) is to keep only important comments (licenses, etc.)
317
320
  comments: !buildOptions.extractLicenses && undefined,
318
321
  webkit: true,
322
+ beautify: environment_options_1.shouldBeautify,
319
323
  },
320
324
  // On server, we don't want to compress anything. We still set the ngDevMode = false for it
321
325
  // to remove dev code, and ngI18nClosureMode to remove Closure compiler i18n code
322
- compress: buildOptions.platform == 'server'
323
- ? {
324
- ecma: terserEcma,
325
- global_defs: angularGlobalDefinitions,
326
- keep_fnames: true,
327
- }
328
- : {
329
- ecma: terserEcma,
330
- pure_getters: buildOptions.buildOptimizer,
331
- // PURE comments work best with 3 passes.
332
- // See https://github.com/webpack/webpack/issues/2899#issuecomment-317425926.
333
- passes: buildOptions.buildOptimizer ? 3 : 1,
334
- global_defs: angularGlobalDefinitions,
335
- },
326
+ compress: environment_options_1.allowMinify &&
327
+ (buildOptions.platform == 'server'
328
+ ? {
329
+ ecma: terserEcma,
330
+ global_defs: angularGlobalDefinitions,
331
+ keep_fnames: true,
332
+ }
333
+ : {
334
+ ecma: terserEcma,
335
+ pure_getters: buildOptions.buildOptimizer,
336
+ // PURE comments work best with 3 passes.
337
+ // See https://github.com/webpack/webpack/issues/2899#issuecomment-317425926.
338
+ passes: buildOptions.buildOptimizer ? 3 : 1,
339
+ global_defs: angularGlobalDefinitions,
340
+ }),
336
341
  // We also want to avoid mangling on server.
337
342
  // Name mangling is handled within the browser builder
338
- mangle: !environment_options_1.manglingDisabled &&
339
- buildOptions.platform !== 'server' &&
340
- !differentialLoadingMode,
343
+ mangle: environment_options_1.allowMangle && buildOptions.platform !== 'server' && !differentialLoadingMode,
341
344
  };
342
345
  extraMinimizers.push(new TerserPlugin({
343
346
  sourceMap: scriptsSourceMap,
@@ -357,7 +360,7 @@ function getCommonConfig(wco) {
357
360
  chunkFilter: (chunk) => globalScriptsByBundleName.some(s => s.bundleName === chunk.name),
358
361
  terserOptions: {
359
362
  ...terserOptions,
360
- compress: {
363
+ compress: environment_options_1.allowMinify && {
361
364
  ...terserOptions.compress,
362
365
  ecma: 5,
363
366
  },
@@ -365,7 +368,7 @@ function getCommonConfig(wco) {
365
368
  ...terserOptions.output,
366
369
  ecma: 5,
367
370
  },
368
- mangle: !environment_options_1.manglingDisabled && buildOptions.platform !== 'server',
371
+ mangle: environment_options_1.allowMangle && buildOptions.platform !== 'server',
369
372
  },
370
373
  }));
371
374
  }
@@ -14,7 +14,6 @@ function getTestConfig(wco) {
14
14
  const { root, buildOptions, sourceRoot: include } = wco;
15
15
  const extraRules = [];
16
16
  const extraPlugins = [];
17
- // if (buildOptions.codeCoverage && CliConfig.fromProject()) {
18
17
  if (buildOptions.codeCoverage) {
19
18
  const codeCoverageExclude = buildOptions.codeCoverageExclude;
20
19
  const exclude = [
@@ -31,7 +30,7 @@ function getTestConfig(wco) {
31
30
  }
32
31
  extraRules.push({
33
32
  test: /\.(jsx?|tsx?)$/,
34
- loader: require.resolve('istanbul-instrumenter-loader'),
33
+ loader: require.resolve('coverage-istanbul-loader'),
35
34
  options: { esModules: true },
36
35
  enforce: 'post',
37
36
  exclude,
@@ -92,9 +92,12 @@ function getAotConfig(wco, i18nExtract = false) {
92
92
  });
93
93
  }
94
94
  const test = /(?:\.ngfactory\.js|\.ngstyle\.js|\.tsx?)$/;
95
+ const optimize = wco.buildOptions.optimization.scripts;
95
96
  return {
96
97
  module: { rules: [{ test, use: loaders }] },
97
- plugins: [_createAotPlugin(wco, { tsConfigPath }, i18nExtract)]
98
+ plugins: [
99
+ _createAotPlugin(wco, { tsConfigPath, emitClassMetadata: !optimize, emitNgModuleScope: !optimize }, i18nExtract),
100
+ ],
98
101
  };
99
102
  }
100
103
  exports.getAotConfig = getAotConfig;
@@ -1,10 +1,10 @@
1
1
  import { Compiler } from 'webpack';
2
- export interface CleanCssWebpackPluginOptions {
2
+ export interface OptimizeCssWebpackPluginOptions {
3
3
  sourceMap: boolean;
4
4
  test: (file: string) => boolean;
5
5
  }
6
- export declare class CleanCssWebpackPlugin {
6
+ export declare class OptimizeCssWebpackPlugin {
7
7
  private readonly _options;
8
- constructor(options: Partial<CleanCssWebpackPluginOptions>);
8
+ constructor(options: Partial<OptimizeCssWebpackPluginOptions>);
9
9
  apply(compiler: Compiler): void;
10
10
  }
@@ -7,14 +7,14 @@ Object.defineProperty(exports, "__esModule", { value: true });
7
7
  * Use of this source code is governed by an MIT-style license that can be
8
8
  * found in the LICENSE file at https://angular.io/license
9
9
  */
10
- const CleanCSS = require("clean-css");
10
+ const cssNano = require("cssnano");
11
11
  const webpack_sources_1 = require("webpack-sources");
12
12
  function hook(compiler, action) {
13
- compiler.hooks.compilation.tap('cleancss-webpack-plugin', (compilation) => {
14
- compilation.hooks.optimizeChunkAssets.tapPromise('cleancss-webpack-plugin', chunks => action(compilation, chunks));
13
+ compiler.hooks.compilation.tap('optimize-css-webpack-plugin', (compilation) => {
14
+ compilation.hooks.optimizeChunkAssets.tapPromise('optimize-css-webpack-plugin', chunks => action(compilation, chunks));
15
15
  });
16
16
  }
17
- class CleanCssWebpackPlugin {
17
+ class OptimizeCssWebpackPlugin {
18
18
  constructor(options) {
19
19
  this._options = {
20
20
  sourceMap: false,
@@ -24,20 +24,6 @@ class CleanCssWebpackPlugin {
24
24
  }
25
25
  apply(compiler) {
26
26
  hook(compiler, (compilation, chunks) => {
27
- const cleancss = new CleanCSS({
28
- compatibility: 'ie9',
29
- level: {
30
- 2: {
31
- skipProperties: [
32
- 'transition',
33
- 'font',
34
- ],
35
- },
36
- },
37
- inline: false,
38
- returnPromise: true,
39
- sourceMap: this._options.sourceMap,
40
- });
41
27
  const files = [...compilation.additionalChunkAssets];
42
28
  chunks.forEach(chunk => {
43
29
  if (chunk.files && chunk.files.length > 0) {
@@ -65,28 +51,32 @@ class CleanCssWebpackPlugin {
65
51
  if (content.length === 0) {
66
52
  return;
67
53
  }
68
- const output = await cleancss.minify(content, map);
69
- let hasWarnings = false;
70
- if (output.warnings && output.warnings.length > 0) {
71
- compilation.warnings.push(...output.warnings);
72
- hasWarnings = true;
73
- }
74
- if (output.errors && output.errors.length > 0) {
75
- output.errors.forEach((error) => compilation.errors.push(new Error(error)));
76
- return;
77
- }
78
- // generally means invalid syntax so bail
79
- if (hasWarnings && output.stats.minifiedSize === 0) {
80
- return;
54
+ const cssNanoOptions = {
55
+ preset: 'default',
56
+ };
57
+ const postCssOptions = {
58
+ from: file,
59
+ map: map && { annotation: false, prev: map },
60
+ };
61
+ const output = await new Promise((resolve, reject) => {
62
+ // the last parameter is not in the typings
63
+ // tslint:disable-next-line: no-any
64
+ cssNano.process(content, postCssOptions, cssNanoOptions)
65
+ .then(resolve)
66
+ .catch(reject);
67
+ });
68
+ const warnings = output.warnings();
69
+ if (warnings.length) {
70
+ compilation.warnings.push(...warnings.map(({ text }) => text));
81
71
  }
82
72
  let newSource;
83
- if (output.sourceMap) {
84
- newSource = new webpack_sources_1.SourceMapSource(output.styles, file,
73
+ if (output.map) {
74
+ newSource = new webpack_sources_1.SourceMapSource(output.css, file,
85
75
  // tslint:disable-next-line: no-any
86
- output.sourceMap.toString(), content, map);
76
+ output.map.toString(), content, map);
87
77
  }
88
78
  else {
89
- newSource = new webpack_sources_1.RawSource(output.styles);
79
+ newSource = new webpack_sources_1.RawSource(output.css);
90
80
  }
91
81
  compilation.assets[file] = newSource;
92
82
  });
@@ -94,4 +84,4 @@ class CleanCssWebpackPlugin {
94
84
  });
95
85
  }
96
86
  }
97
- exports.CleanCssWebpackPlugin = CleanCssWebpackPlugin;
87
+ exports.OptimizeCssWebpackPlugin = OptimizeCssWebpackPlugin;
@@ -6,7 +6,7 @@
6
6
  * found in the LICENSE file at https://angular.io/license
7
7
  */
8
8
  export { AnyComponentStyleBudgetChecker } from './any-component-style-budget-checker';
9
- export { CleanCssWebpackPlugin, CleanCssWebpackPluginOptions } from './cleancss-webpack-plugin';
9
+ export { OptimizeCssWebpackPlugin, OptimizeCssWebpackPluginOptions } from './optimize-css-webpack-plugin';
10
10
  export { BundleBudgetPlugin, BundleBudgetPluginOptions } from './bundle-budget';
11
11
  export { ScriptsWebpackPlugin, ScriptsWebpackPluginOptions } from './scripts-webpack-plugin';
12
12
  export { SuppressExtractedTextChunksWebpackPlugin } from './suppress-entry-chunks-webpack-plugin';
@@ -10,8 +10,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
10
10
  // Exports the webpack plugins we use internally.
11
11
  var any_component_style_budget_checker_1 = require("./any-component-style-budget-checker");
12
12
  exports.AnyComponentStyleBudgetChecker = any_component_style_budget_checker_1.AnyComponentStyleBudgetChecker;
13
- var cleancss_webpack_plugin_1 = require("./cleancss-webpack-plugin");
14
- exports.CleanCssWebpackPlugin = cleancss_webpack_plugin_1.CleanCssWebpackPlugin;
13
+ var optimize_css_webpack_plugin_1 = require("./optimize-css-webpack-plugin");
14
+ exports.OptimizeCssWebpackPlugin = optimize_css_webpack_plugin_1.OptimizeCssWebpackPlugin;
15
15
  var bundle_budget_1 = require("./bundle-budget");
16
16
  exports.BundleBudgetPlugin = bundle_budget_1.BundleBudgetPlugin;
17
17
  var scripts_webpack_plugin_1 = require("./scripts-webpack-plugin");
@@ -233,12 +233,6 @@ function buildWebpackBrowser(options, context, transforms = {}) {
233
233
  if (!es5Polyfills) {
234
234
  moduleFiles.push(file);
235
235
  }
236
- // If not optimizing then ES2015 polyfills do not need processing
237
- // Unlike other module scripts, it is never downleveled
238
- const es2015Polyfills = file.file.startsWith('polyfills-es20');
239
- if (!actionOptions.optimize && es2015Polyfills) {
240
- continue;
241
- }
242
236
  // Retrieve the content/map for the file
243
237
  // NOTE: Additional future optimizations will read directly from memory
244
238
  // tslint:disable-next-line: no-non-null-assertion
@@ -258,6 +252,7 @@ function buildWebpackBrowser(options, context, transforms = {}) {
258
252
  fs.unlinkSync(filename);
259
253
  filename = filename.replace(/\-es20\d{2}/, '');
260
254
  }
255
+ const es2015Polyfills = file.file.startsWith('polyfills-es20');
261
256
  // Record the bundle processing action
262
257
  // The runtime chunk gets special processing for lazy loaded files
263
258
  actions.push({
@@ -307,6 +302,7 @@ function buildWebpackBrowser(options, context, transforms = {}) {
307
302
  const runtimeOptions = {
308
303
  ...processRuntimeAction,
309
304
  runtimeData: processResults,
305
+ supportedBrowsers: buildBrowserFeatures.supportedBrowsers,
310
306
  };
311
307
  processResults.push(await Promise.resolve().then(() => require('../utils/process-bundle')).then(m => m.process(runtimeOptions)));
312
308
  }
@@ -254,6 +254,7 @@ async function setupLocalize(i18n, browserOptions, webpackConfig) {
254
254
  loader: require.resolve('babel-loader'),
255
255
  options: {
256
256
  babelrc: false,
257
+ configFile: false,
257
258
  compact: false,
258
259
  cacheCompression: false,
259
260
  cacheDirectory: cache_path_1.findCachePath('babel-loader'),
@@ -27,21 +27,14 @@ function runProtractor(root, options) {
27
27
  async function updateWebdriver() {
28
28
  // The webdriver-manager update command can only be accessed via a deep import.
29
29
  const webdriverDeepImport = 'webdriver-manager/built/lib/cmds/update';
30
- const importOptions = [
31
- // When using npm, webdriver is within protractor/node_modules.
32
- `protractor/node_modules/${webdriverDeepImport}`,
33
- // When using yarn, webdriver is found as a root module.
34
- webdriverDeepImport,
35
- ];
36
30
  let path;
37
- for (const importOption of importOptions) {
38
- try {
39
- path = require.resolve(importOption);
40
- }
41
- catch (error) {
42
- if (error.code !== 'MODULE_NOT_FOUND') {
43
- throw error;
44
- }
31
+ try {
32
+ const protractorPath = require.resolve('protractor');
33
+ path = require.resolve(webdriverDeepImport, { paths: [protractorPath] });
34
+ }
35
+ catch (error) {
36
+ if (error.code !== 'MODULE_NOT_FOUND') {
37
+ throw error;
45
38
  }
46
39
  }
47
40
  if (!path) {
@@ -13,6 +13,6 @@ export declare class BundleActionCache {
13
13
  static copyEntryContent(entry: CacheEntry | string, dest: fs.PathLike): void;
14
14
  generateBaseCacheKey(content: string): string;
15
15
  generateCacheKeys(action: ProcessBundleOptions): string[];
16
- getCacheEntries(cacheKeys: (string | null)[]): Promise<(CacheEntry | null)[] | false>;
16
+ getCacheEntries(cacheKeys: (string | undefined)[]): Promise<(CacheEntry | null)[] | false>;
17
17
  getCachedBundleResult(action: ProcessBundleOptions): Promise<ProcessBundleResult | null>;
18
18
  }
@@ -35,32 +35,36 @@ class BundleActionCache {
35
35
  .update(content)
36
36
  .digest('base64');
37
37
  let baseCacheKey = `${packageVersion}|${content.length}|${algorithm}-${codeHash}`;
38
- if (environment_options_1.manglingDisabled) {
38
+ if (!environment_options_1.allowMangle) {
39
39
  baseCacheKey += '|MD';
40
40
  }
41
41
  return baseCacheKey;
42
42
  }
43
43
  generateCacheKeys(action) {
44
- const baseCacheKey = this.generateBaseCacheKey(action.code);
45
- // Postfix added to sourcemap cache keys when vendor sourcemaps are present
44
+ // Postfix added to sourcemap cache keys when vendor, hidden sourcemaps are present
46
45
  // Allows non-destructive caching of both variants
47
- const SourceMapVendorPostfix = !!action.sourceMaps && action.vendorSourceMaps ? '|vendor' : '';
46
+ const sourceMapVendorPostfix = action.sourceMaps && action.vendorSourceMaps ? '|vendor' : '';
47
+ // sourceMappingURL is added at the very end which causes the code to be the same when sourcemaps are enabled/disabled
48
+ // When using hiddenSourceMaps we can omit the postfix since sourceMappingURL will not be added.
49
+ // When having sourcemaps a hashed file and non hashed file can have the same content. But the sourceMappingURL will differ.
50
+ const sourceMapPostFix = action.sourceMaps && !action.hiddenSourceMaps ? `|sourcemap|${action.filename}` : '';
51
+ const baseCacheKey = this.generateBaseCacheKey(action.code);
48
52
  // Determine cache entries required based on build settings
49
53
  const cacheKeys = [];
50
54
  // If optimizing and the original is not ignored, add original as required
51
- if ((action.optimize || action.optimizeOnly) && !action.ignoreOriginal) {
52
- cacheKeys[0 /* OriginalCode */] = baseCacheKey + '|orig';
55
+ if (!action.ignoreOriginal) {
56
+ cacheKeys[0 /* OriginalCode */] = baseCacheKey + sourceMapPostFix + '|orig';
53
57
  // If sourcemaps are enabled, add original sourcemap as required
54
58
  if (action.sourceMaps) {
55
- cacheKeys[1 /* OriginalMap */] = baseCacheKey + SourceMapVendorPostfix + '|orig-map';
59
+ cacheKeys[1 /* OriginalMap */] = baseCacheKey + sourceMapVendorPostfix + '|orig-map';
56
60
  }
57
61
  }
58
62
  // If not only optimizing, add downlevel as required
59
63
  if (!action.optimizeOnly) {
60
- cacheKeys[2 /* DownlevelCode */] = baseCacheKey + '|dl';
64
+ cacheKeys[2 /* DownlevelCode */] = baseCacheKey + sourceMapPostFix + '|dl';
61
65
  // If sourcemaps are enabled, add downlevel sourcemap as required
62
66
  if (action.sourceMaps) {
63
- cacheKeys[3 /* DownlevelMap */] = baseCacheKey + SourceMapVendorPostfix + '|dl-map';
67
+ cacheKeys[3 /* DownlevelMap */] = baseCacheKey + sourceMapVendorPostfix + '|dl-map';
64
68
  }
65
69
  }
66
70
  return cacheKeys;
@@ -9,8 +9,8 @@ import * as ts from 'typescript';
9
9
  export declare class BuildBrowserFeatures {
10
10
  private projectRoot;
11
11
  private scriptTarget;
12
- private readonly _supportedBrowsers;
13
12
  private readonly _es6TargetOrLater;
13
+ readonly supportedBrowsers: string[];
14
14
  constructor(projectRoot: string, scriptTarget: ts.ScriptTarget);
15
15
  /**
16
16
  * True, when one or more browsers requires ES5
@@ -14,7 +14,7 @@ class BuildBrowserFeatures {
14
14
  constructor(projectRoot, scriptTarget) {
15
15
  this.projectRoot = projectRoot;
16
16
  this.scriptTarget = scriptTarget;
17
- this._supportedBrowsers = browserslist(undefined, { path: this.projectRoot });
17
+ this.supportedBrowsers = browserslist(undefined, { path: this.projectRoot });
18
18
  this._es6TargetOrLater = this.scriptTarget > ts.ScriptTarget.ES5;
19
19
  }
20
20
  /**
@@ -44,7 +44,7 @@ class BuildBrowserFeatures {
44
44
  'safari 10.1',
45
45
  'ios_saf 10.3',
46
46
  ];
47
- return this._supportedBrowsers.some(browser => safariBrowsers.includes(browser));
47
+ return this.supportedBrowsers.some(browser => safariBrowsers.includes(browser));
48
48
  }
49
49
  /**
50
50
  * True, when a browser feature is supported partially or fully.
@@ -59,7 +59,7 @@ class BuildBrowserFeatures {
59
59
  'a',
60
60
  ];
61
61
  const data = caniuse_lite_1.feature(caniuse_lite_1.features[featureId]);
62
- return !this._supportedBrowsers
62
+ return !this.supportedBrowsers
63
63
  .some(browser => {
64
64
  const [agentId, version] = browser.split(' ');
65
65
  const browserData = data.stats[agentId];
@@ -1,3 +1,5 @@
1
- export declare const manglingDisabled: boolean;
1
+ export declare const allowMangle: boolean;
2
+ export declare const shouldBeautify: boolean;
3
+ export declare const allowMinify: boolean;
2
4
  export declare const cachingDisabled: boolean;
3
5
  export declare const cachingBasePath: string | null;
@@ -8,14 +8,57 @@ Object.defineProperty(exports, "__esModule", { value: true });
8
8
  * found in the LICENSE file at https://angular.io/license
9
9
  */
10
10
  const path = require("path");
11
+ function isDisabled(variable) {
12
+ return variable === '0' || variable.toLowerCase() === 'false';
13
+ }
14
+ function isEnabled(variable) {
15
+ return variable === '1' || variable.toLowerCase() === 'true';
16
+ }
17
+ function isPresent(variable) {
18
+ return typeof variable === 'string' && variable !== '';
19
+ }
20
+ const debugOptimizeVariable = process.env['NG_BUILD_DEBUG_OPTIMIZE'];
21
+ const debugOptimize = (() => {
22
+ if (!isPresent(debugOptimizeVariable) || isDisabled(debugOptimizeVariable)) {
23
+ return {
24
+ mangle: true,
25
+ minify: true,
26
+ beautify: false,
27
+ };
28
+ }
29
+ const debugValue = {
30
+ mangle: false,
31
+ minify: false,
32
+ beautify: true,
33
+ };
34
+ if (isEnabled(debugOptimizeVariable)) {
35
+ return debugValue;
36
+ }
37
+ for (const part of debugOptimizeVariable.split(',')) {
38
+ switch (part.trim().toLowerCase()) {
39
+ case 'mangle':
40
+ debugValue.mangle = true;
41
+ break;
42
+ case 'minify':
43
+ debugValue.minify = true;
44
+ break;
45
+ case 'beautify':
46
+ debugValue.beautify = true;
47
+ break;
48
+ }
49
+ }
50
+ return debugValue;
51
+ })();
11
52
  const mangleVariable = process.env['NG_BUILD_MANGLE'];
12
- exports.manglingDisabled = !!mangleVariable && (mangleVariable === '0' || mangleVariable.toLowerCase() === 'false');
53
+ exports.allowMangle = isPresent(mangleVariable)
54
+ ? !isDisabled(mangleVariable)
55
+ : debugOptimize.mangle;
56
+ exports.shouldBeautify = debugOptimize.beautify;
57
+ exports.allowMinify = debugOptimize.minify;
13
58
  const cacheVariable = process.env['NG_BUILD_CACHE'];
14
- exports.cachingDisabled = !!cacheVariable && (cacheVariable === '0' || cacheVariable.toLowerCase() === 'false');
59
+ exports.cachingDisabled = isPresent(cacheVariable) && isDisabled(cacheVariable);
15
60
  exports.cachingBasePath = (() => {
16
- if (exports.cachingDisabled ||
17
- !cacheVariable ||
18
- (cacheVariable === '1' || cacheVariable.toLowerCase() === 'true')) {
61
+ if (exports.cachingDisabled || !isPresent(cacheVariable) || isEnabled(cacheVariable)) {
19
62
  return null;
20
63
  }
21
64
  if (!path.isAbsolute(cacheVariable)) {
@@ -11,10 +11,11 @@ export interface ProcessBundleOptions {
11
11
  optimize?: boolean;
12
12
  optimizeOnly?: boolean;
13
13
  ignoreOriginal?: boolean;
14
- cacheKeys?: (string | null)[];
14
+ cacheKeys?: (string | undefined)[];
15
15
  integrityAlgorithm?: 'sha256' | 'sha384' | 'sha512';
16
16
  runtimeData?: ProcessBundleResult[];
17
17
  replacements?: [string, string][];
18
+ supportedBrowsers?: string[] | Record<string, string>;
18
19
  }
19
20
  export interface ProcessBundleResult {
20
21
  name: string;
@@ -18,6 +18,8 @@ const webpack_sources_1 = require("webpack-sources");
18
18
  const environment_options_1 = require("./environment-options");
19
19
  const cacache = require('cacache');
20
20
  const deserialize = v8.deserialize;
21
+ // If code size is larger than 500KB, consider lower fidelity but faster sourcemap merge
22
+ const FAST_SOURCEMAP_THRESHOLD = 500 * 1024;
21
23
  let cachePath;
22
24
  let i18n;
23
25
  function setup(data) {
@@ -30,7 +32,7 @@ function setup(data) {
30
32
  exports.setup = setup;
31
33
  async function cachePut(content, key, integrity) {
32
34
  if (cachePath && key) {
33
- await cacache.put(cachePath, key, content, {
35
+ await cacache.put(cachePath, key || null, content, {
34
36
  metadata: { integrity },
35
37
  });
36
38
  }
@@ -52,11 +54,6 @@ async function process(options) {
52
54
  const filename = path.basename(options.filename);
53
55
  const downlevelFilename = filename.replace(/\-es20\d{2}/, '-es5');
54
56
  const downlevel = !options.optimizeOnly;
55
- // if code size is larger than 500kB, manually handle sourcemaps with newer source-map package.
56
- // babel currently uses an older version that still supports sync calls
57
- const codeSize = Buffer.byteLength(options.code);
58
- const mapSize = options.map ? Buffer.byteLength(options.map) : 0;
59
- const manualSourceMaps = codeSize >= 500 * 1024 || mapSize >= 500 * 1024;
60
57
  const sourceCode = options.code;
61
58
  const sourceMap = options.map ? JSON.parse(options.map) : undefined;
62
59
  let downlevelCode;
@@ -64,12 +61,18 @@ async function process(options) {
64
61
  if (downlevel) {
65
62
  // Downlevel the bundle
66
63
  const transformResult = await core_1.transformAsync(sourceCode, {
67
- filename: options.filename,
68
- inputSourceMap: manualSourceMaps ? undefined : sourceMap,
64
+ filename,
65
+ // using false ensures that babel will NOT search and process sourcemap comments (large memory usage)
66
+ // The types do not include the false option even though it is valid
67
+ // tslint:disable-next-line: no-any
68
+ inputSourceMap: false,
69
69
  babelrc: false,
70
+ configFile: false,
70
71
  presets: [[
71
72
  require.resolve('@babel/preset-env'),
72
73
  {
74
+ // browserslist-compatible query or object of minimum environment versions to support
75
+ targets: options.supportedBrowsers,
73
76
  // modules aren't needed since the bundles use webpack's custom module loading
74
77
  modules: false,
75
78
  // 'transform-typeof-symbol' generates slower code
@@ -77,61 +80,46 @@ async function process(options) {
77
80
  },
78
81
  ]],
79
82
  plugins: options.replacements ? [createReplacePlugin(options.replacements)] : [],
80
- minified: options.optimize,
81
- // `false` ensures it is disabled and prevents large file warnings
82
- compact: options.optimize || false,
83
+ minified: environment_options_1.allowMinify && !!options.optimize,
84
+ compact: !environment_options_1.shouldBeautify && !!options.optimize,
83
85
  sourceMaps: !!sourceMap,
84
86
  });
85
87
  if (!transformResult || !transformResult.code) {
86
88
  throw new Error(`Unknown error occurred processing bundle for "${options.filename}".`);
87
89
  }
88
90
  downlevelCode = transformResult.code;
89
- if (manualSourceMaps && sourceMap && transformResult.map) {
90
- downlevelMap = await mergeSourceMapsFast(sourceMap, transformResult.map);
91
- }
92
- else {
93
- // undefined is needed here to normalize the property type
94
- downlevelMap = transformResult.map || undefined;
95
- }
96
- }
97
- if (options.optimize) {
98
- if (downlevelCode) {
99
- const minifyResult = terserMangle(downlevelCode, {
100
- filename: downlevelFilename,
101
- map: downlevelMap,
102
- compress: true,
103
- });
104
- downlevelCode = minifyResult.code;
105
- downlevelMap = minifyResult.map;
106
- }
107
- if (!options.ignoreOriginal) {
108
- result.original = await mangleOriginal(options);
91
+ if (sourceMap && transformResult.map) {
92
+ // String length is used as an estimate for byte length
93
+ const fastSourceMaps = sourceCode.length > FAST_SOURCEMAP_THRESHOLD;
94
+ downlevelMap = await mergeSourceMaps(sourceCode, sourceMap, downlevelCode, transformResult.map, filename,
95
+ // When not optimizing, the sourcemaps are significantly less complex
96
+ // and can use the higher fidelity merge
97
+ !!options.optimize && fastSourceMaps);
109
98
  }
110
99
  }
111
100
  if (downlevelCode) {
112
- const downlevelPath = path.join(basePath, downlevelFilename);
113
- let mapContent;
114
- if (downlevelMap) {
115
- if (!options.hiddenSourceMaps) {
116
- downlevelCode += `\n//# sourceMappingURL=${downlevelFilename}.map`;
117
- }
118
- mapContent = JSON.stringify(downlevelMap);
119
- await cachePut(mapContent, options.cacheKeys[3 /* DownlevelMap */]);
120
- fs.writeFileSync(downlevelPath + '.map', mapContent);
121
- }
122
- result.downlevel = createFileEntry(path.join(basePath, downlevelFilename), downlevelCode, mapContent, options.integrityAlgorithm);
123
- await cachePut(downlevelCode, options.cacheKeys[2 /* DownlevelCode */], result.downlevel.integrity);
124
- fs.writeFileSync(downlevelPath, downlevelCode);
101
+ result.downlevel = await processBundle({
102
+ ...options,
103
+ code: downlevelCode,
104
+ map: downlevelMap,
105
+ filename: path.join(basePath, downlevelFilename),
106
+ isOriginal: false,
107
+ });
125
108
  }
126
- // If original was not processed, add info
127
109
  if (!result.original && !options.ignoreOriginal) {
128
- result.original = createFileEntry(options.filename, options.code, options.map, options.integrityAlgorithm);
110
+ result.original = await processBundle({
111
+ ...options,
112
+ isOriginal: true,
113
+ });
129
114
  }
130
115
  return result;
131
116
  }
132
117
  exports.process = process;
133
- function mergeSourceMaps(inputCode, inputSourceMap, resultCode, resultSourceMap, filename) {
134
- // More accurate but significantly more costly
118
+ async function mergeSourceMaps(inputCode, inputSourceMap, resultCode, resultSourceMap, filename, fast = false) {
119
+ if (fast) {
120
+ return mergeSourceMapsFast(inputSourceMap, resultSourceMap);
121
+ }
122
+ // SourceMapSource produces high-quality sourcemaps
135
123
  // The last argument is not yet in the typings
136
124
  // tslint:disable-next-line: no-any
137
125
  return new webpack_sources_1.SourceMapSource(resultCode, filename, resultSourceMap, inputCode, inputSourceMap, true).map();
@@ -180,45 +168,58 @@ async function mergeSourceMapsFast(first, second) {
180
168
  }
181
169
  return map;
182
170
  }
183
- async function mangleOriginal(options) {
184
- const result = terserMangle(options.code, {
185
- filename: path.basename(options.filename),
186
- map: options.map ? JSON.parse(options.map) : undefined,
187
- ecma: 6,
188
- });
171
+ async function processBundle(options) {
172
+ const { optimize, isOriginal, code, map, filename: filepath, hiddenSourceMaps, cacheKeys = [], integrityAlgorithm, } = options;
173
+ const rawMap = typeof map === 'string' ? JSON.parse(map) : map;
174
+ const filename = path.basename(filepath);
175
+ let result;
176
+ if (rawMap) {
177
+ rawMap.file = filename;
178
+ }
179
+ if (optimize) {
180
+ result = await terserMangle(code, {
181
+ filename,
182
+ map: rawMap,
183
+ compress: !isOriginal,
184
+ ecma: isOriginal ? 6 : 5,
185
+ });
186
+ }
187
+ else {
188
+ result = {
189
+ map: rawMap,
190
+ code,
191
+ };
192
+ }
189
193
  let mapContent;
190
194
  if (result.map) {
191
- if (!options.hiddenSourceMaps) {
192
- result.code += `\n//# sourceMappingURL=${path.basename(options.filename)}.map`;
195
+ if (!hiddenSourceMaps) {
196
+ result.code += `\n//# sourceMappingURL=${filename}.map`;
193
197
  }
194
198
  mapContent = JSON.stringify(result.map);
195
- await cachePut(mapContent, (options.cacheKeys && options.cacheKeys[1 /* OriginalMap */]) || null);
196
- fs.writeFileSync(options.filename + '.map', mapContent);
199
+ await cachePut(mapContent, cacheKeys[isOriginal ? 1 /* OriginalMap */ : 3 /* DownlevelMap */]);
200
+ fs.writeFileSync(filepath + '.map', mapContent);
197
201
  }
198
- const fileResult = createFileEntry(options.filename, result.code, mapContent, options.integrityAlgorithm);
199
- await cachePut(result.code, (options.cacheKeys && options.cacheKeys[0 /* OriginalCode */]) || null, fileResult.integrity);
200
- fs.writeFileSync(options.filename, result.code);
202
+ const fileResult = createFileEntry(filepath, result.code, mapContent, integrityAlgorithm);
203
+ await cachePut(result.code, cacheKeys[isOriginal ? 0 /* OriginalCode */ : 2 /* DownlevelCode */], fileResult.integrity);
204
+ fs.writeFileSync(filepath, result.code);
201
205
  return fileResult;
202
206
  }
203
- function terserMangle(code, options = {}) {
207
+ async function terserMangle(code, options = {}) {
204
208
  // Note: Investigate converting the AST instead of re-parsing
205
209
  // estree -> terser is already supported; need babel -> estree/terser
206
210
  // Mangle downlevel code
207
- const minifyOutput = terser_1.minify(code, {
208
- compress: options.compress || false,
211
+ const minifyOutput = terser_1.minify(options.filename ? { [options.filename]: code } : code, {
212
+ compress: environment_options_1.allowMinify && !!options.compress,
209
213
  ecma: options.ecma || 5,
210
- mangle: !environment_options_1.manglingDisabled,
214
+ mangle: environment_options_1.allowMangle,
211
215
  safari10: true,
212
216
  output: {
213
217
  ascii_only: true,
214
218
  webkit: true,
219
+ beautify: environment_options_1.shouldBeautify,
215
220
  },
216
221
  sourceMap: !!options.map &&
217
222
  {
218
- filename: options.filename,
219
- // terser uses an old version of the sourcemap typings
220
- // tslint:disable-next-line: no-any
221
- content: options.map,
222
223
  asObject: true,
223
224
  },
224
225
  });
@@ -226,7 +227,12 @@ function terserMangle(code, options = {}) {
226
227
  throw minifyOutput.error;
227
228
  }
228
229
  // tslint:disable-next-line: no-non-null-assertion
229
- return { code: minifyOutput.code, map: minifyOutput.map };
230
+ const outputCode = minifyOutput.code;
231
+ let outputMap;
232
+ if (options.map && minifyOutput.map) {
233
+ outputMap = await mergeSourceMaps(code, options.map, outputCode, minifyOutput.map, options.filename || '0', code.length > FAST_SOURCEMAP_THRESHOLD);
234
+ }
235
+ return { code: outputCode, map: outputMap };
230
236
  }
231
237
  function createFileEntry(filename, code, map, integrityAlgorithm) {
232
238
  return {
@@ -271,42 +277,19 @@ async function processRuntime(options) {
271
277
  // Adjust lazy loaded scripts to point to the proper variant
272
278
  // Extra spacing is intentional to align source line positions
273
279
  downlevelCode = downlevelCode.replace(/"\-es20\d{2}\./, ' "-es5.');
274
- const downlevelFilePath = options.filename.replace(/\-es20\d{2}/, '-es5');
275
- let downlevelMap;
276
- let result;
277
- if (options.optimize) {
278
- const minifiyResults = terserMangle(downlevelCode, {
279
- filename: path.basename(downlevelFilePath),
280
- map: options.map === undefined ? undefined : JSON.parse(options.map),
281
- });
282
- downlevelCode = minifiyResults.code;
283
- downlevelMap = JSON.stringify(minifiyResults.map);
284
- result = {
285
- original: await mangleOriginal({ ...options, code: originalCode }),
286
- downlevel: createFileEntry(downlevelFilePath, downlevelCode, downlevelMap, options.integrityAlgorithm),
287
- };
288
- }
289
- else {
290
- if (options.map) {
291
- const rawMap = JSON.parse(options.map);
292
- rawMap.file = path.basename(downlevelFilePath);
293
- downlevelMap = JSON.stringify(rawMap);
294
- }
295
- result = {
296
- original: createFileEntry(options.filename, originalCode, options.map, options.integrityAlgorithm),
297
- downlevel: createFileEntry(downlevelFilePath, downlevelCode, downlevelMap, options.integrityAlgorithm),
298
- };
299
- }
300
- if (downlevelMap) {
301
- await cachePut(downlevelMap, (options.cacheKeys && options.cacheKeys[3 /* DownlevelMap */]) || null);
302
- fs.writeFileSync(downlevelFilePath + '.map', downlevelMap);
303
- if (!options.hiddenSourceMaps) {
304
- downlevelCode += `\n//# sourceMappingURL=${path.basename(downlevelFilePath)}.map`;
305
- }
306
- }
307
- await cachePut(downlevelCode, (options.cacheKeys && options.cacheKeys[2 /* DownlevelCode */]) || null);
308
- fs.writeFileSync(downlevelFilePath, downlevelCode);
309
- return result;
280
+ return {
281
+ original: await processBundle({
282
+ ...options,
283
+ code: originalCode,
284
+ isOriginal: true,
285
+ }),
286
+ downlevel: await processBundle({
287
+ ...options,
288
+ code: downlevelCode,
289
+ filename: options.filename.replace(/\-es20\d{2}/, '-es5'),
290
+ isOriginal: false,
291
+ }),
292
+ };
310
293
  }
311
294
  function createReplacePlugin(replacements) {
312
295
  return {
@@ -345,7 +328,8 @@ async function inlineLocales(options) {
345
328
  if (positions.length === 0 && !options.setLocale) {
346
329
  return inlineCopyOnly(options);
347
330
  }
348
- let content = new MagicString(options.code);
331
+ // tslint:disable-next-line: no-any
332
+ let content = new MagicString(options.code, { filename: options.filename });
349
333
  const inputMap = options.map && JSON.parse(options.map);
350
334
  let contentClone;
351
335
  for (const locale of i18n.inlineLocales) {
@@ -365,7 +349,7 @@ async function inlineLocales(options) {
365
349
  // If locale data is provided, load it and prepend to file
366
350
  const localeDataPath = i18n.locales[locale] && i18n.locales[locale].dataPath;
367
351
  if (localeDataPath) {
368
- const localDataContent = loadLocaleData(localeDataPath, true);
352
+ const localDataContent = await loadLocaleData(localeDataPath, true);
369
353
  // The semicolon ensures that there is no syntax error between statements
370
354
  content.prepend(localDataContent + ';');
371
355
  }
@@ -375,7 +359,7 @@ async function inlineLocales(options) {
375
359
  fs.writeFileSync(outputPath, output);
376
360
  if (inputMap) {
377
361
  const contentMap = content.generateMap();
378
- const outputMap = mergeSourceMaps(options.code, inputMap, output, contentMap, options.filename);
362
+ const outputMap = mergeSourceMaps(options.code, inputMap, output, contentMap, options.filename, options.code.length > FAST_SOURCEMAP_THRESHOLD);
379
363
  fs.writeFileSync(outputPath + '.map', JSON.stringify(outputMap));
380
364
  }
381
365
  if (contentClone) {
@@ -406,7 +390,9 @@ utils) {
406
390
  try {
407
391
  ast = core_1.parseSync(options.code, {
408
392
  babelrc: false,
393
+ configFile: false,
409
394
  sourceType: 'script',
395
+ filename: options.filename,
410
396
  });
411
397
  }
412
398
  catch (error) {
@@ -464,12 +450,12 @@ utils) {
464
450
  }
465
451
  return positions;
466
452
  }
467
- function loadLocaleData(path, optimize) {
453
+ async function loadLocaleData(path, optimize) {
468
454
  // The path is validated during option processing before the build starts
469
455
  const content = fs.readFileSync(path, 'utf8');
470
456
  // NOTE: This can be removed once the locale data files are preprocessed in the framework
471
457
  if (optimize) {
472
- const result = terserMangle(content, {
458
+ const result = await terserMangle(content, {
473
459
  compress: true,
474
460
  ecma: 5,
475
461
  });
@@ -94,6 +94,27 @@ async function generateI18nBrowserWebpackConfigFromContext(options, context, web
94
94
  config.resolve.alias = {};
95
95
  }
96
96
  config.resolve.alias['@angular/localize/init'] = require.resolve('./empty.js');
97
+ // Update file hashes to include translation file content
98
+ const i18nHash = Object.values(i18n.locales).reduce((data, locale) => data + (locale.integrity || ''), '');
99
+ if (!config.plugins) {
100
+ config.plugins = [];
101
+ }
102
+ config.plugins.push({
103
+ apply(compiler) {
104
+ compiler.hooks.compilation.tap('build-angular', compilation => {
105
+ // Webpack typings do not contain template hashForChunk hook
106
+ // tslint:disable-next-line: no-any
107
+ compilation.mainTemplate.hooks.hashForChunk.tap('build-angular', (hash) => {
108
+ hash.update('$localize' + i18nHash);
109
+ });
110
+ // Webpack typings do not contain hooks property
111
+ // tslint:disable-next-line: no-any
112
+ compilation.chunkTemplate.hooks.hashForChunk.tap('build-angular', (hash) => {
113
+ hash.update('$localize' + i18nHash);
114
+ });
115
+ });
116
+ },
117
+ });
97
118
  }
98
119
  return { ...result, i18n };
99
120
  }