@angular-devkit/build-angular 13.1.0-next.1 → 13.1.0

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.
@@ -9,6 +9,7 @@
9
9
  Object.defineProperty(exports, "__esModule", { value: true });
10
10
  exports.augmentIndexHtml = void 0;
11
11
  const crypto_1 = require("crypto");
12
+ const load_esm_1 = require("../load-esm");
12
13
  const html_rewriting_stream_1 = require("./html-rewriting-stream");
13
14
  /*
14
15
  * Helper function used by the IndexHtmlWebpackPlugin.
@@ -18,6 +19,8 @@ const html_rewriting_stream_1 = require("./html-rewriting-stream");
18
19
  */
19
20
  async function augmentIndexHtml(params) {
20
21
  const { loadOutputFile, files, entrypoints, sri, deployUrl = '', lang, baseHref, html } = params;
22
+ const warnings = [];
23
+ const errors = [];
21
24
  let { crossOrigin = 'none' } = params;
22
25
  if (sri && crossOrigin === 'none') {
23
26
  crossOrigin = 'anonymous';
@@ -72,6 +75,7 @@ async function augmentIndexHtml(params) {
72
75
  }
73
76
  linkTags.push(`<link ${attrs.join(' ')}>`);
74
77
  }
78
+ const dir = lang ? await getLanguageDirection(lang, warnings) : undefined;
75
79
  const { rewriter, transformedContent } = await (0, html_rewriting_stream_1.htmlRewritingStream)(html);
76
80
  const baseTagExists = html.includes('<base');
77
81
  rewriter
@@ -82,6 +86,9 @@ async function augmentIndexHtml(params) {
82
86
  if (isString(lang)) {
83
87
  updateAttribute(tag, 'lang', lang);
84
88
  }
89
+ if (dir) {
90
+ updateAttribute(tag, 'dir', dir);
91
+ }
85
92
  break;
86
93
  case 'head':
87
94
  // Base href should be added before any link, meta tags
@@ -119,11 +126,14 @@ async function augmentIndexHtml(params) {
119
126
  rewriter.emitEndTag(tag);
120
127
  });
121
128
  const content = await transformedContent;
122
- if (linkTags.length || scriptTags.length) {
123
- // In case no body/head tags are not present (dotnet partial templates)
124
- return linkTags.join('') + scriptTags.join('') + content;
125
- }
126
- return content;
129
+ return {
130
+ content: linkTags.length || scriptTags.length
131
+ ? // In case no body/head tags are not present (dotnet partial templates)
132
+ linkTags.join('') + scriptTags.join('') + content
133
+ : content,
134
+ warnings,
135
+ errors,
136
+ };
127
137
  }
128
138
  exports.augmentIndexHtml = augmentIndexHtml;
129
139
  function generateSriAttributes(content) {
@@ -144,3 +154,26 @@ function updateAttribute(tag, name, value) {
144
154
  function isString(value) {
145
155
  return typeof value === 'string';
146
156
  }
157
+ async function getLanguageDirection(locale, warnings) {
158
+ const dir = await getLanguageDirectionFromLocales(locale);
159
+ if (!dir) {
160
+ warnings.push(`Locale data for '${locale}' cannot be found. 'dir' attribute will not be set for this locale.`);
161
+ }
162
+ return dir;
163
+ }
164
+ async function getLanguageDirectionFromLocales(locale) {
165
+ try {
166
+ const localeData = (await (0, load_esm_1.loadEsmModule)(`@angular/common/locales/${locale}`)).default;
167
+ const dir = localeData[localeData.length - 2];
168
+ return isString(dir) ? dir : undefined;
169
+ }
170
+ catch {
171
+ // In some cases certain locales might map to files which are named only with language id.
172
+ // Example: `en-US` -> `en`.
173
+ const [languageId] = locale.split('-', 1);
174
+ if (languageId !== locale) {
175
+ return getLanguageDirectionFromLocales(languageId);
176
+ }
177
+ }
178
+ return undefined;
179
+ }
@@ -41,10 +41,7 @@ async function readTsconfig(tsconfigPath, workspaceRoot) {
41
41
  // Load ESM `@angular/compiler-cli` using the TypeScript dynamic import workaround.
42
42
  // Once TypeScript provides support for keeping the dynamic import this workaround can be
43
43
  // changed to a direct dynamic import.
44
- const compilerCliModule = await (0, load_esm_1.loadEsmModule)('@angular/compiler-cli');
45
- // If it is not ESM then the functions needed will be stored in the `default` property.
46
- // TODO_ESM: This can be removed once `@angular/compiler-cli` is ESM only.
47
- const { formatDiagnostics, readConfiguration } = (compilerCliModule.readConfiguration ? compilerCliModule : compilerCliModule.default);
44
+ const { formatDiagnostics, readConfiguration } = await (0, load_esm_1.loadEsmModule)('@angular/compiler-cli');
48
45
  const configResult = readConfiguration(tsConfigFullPath);
49
46
  if (configResult.errors && configResult.errors.length) {
50
47
  throw new Error(formatDiagnostics(configResult.errors));
@@ -39,13 +39,15 @@ const webpack_subresource_integrity_1 = require("webpack-subresource-integrity")
39
39
  const environment_options_1 = require("../../utils/environment-options");
40
40
  const load_esm_1 = require("../../utils/load-esm");
41
41
  const plugins_1 = require("../plugins");
42
+ const named_chunks_plugin_1 = require("../plugins/named-chunks-plugin");
42
43
  const progress_plugin_1 = require("../plugins/progress-plugin");
44
+ const transfer_size_plugin_1 = require("../plugins/transfer-size-plugin");
43
45
  const typescript_2 = require("../plugins/typescript");
44
46
  const helpers_1 = require("../utils/helpers");
45
47
  // eslint-disable-next-line max-lines-per-function
46
48
  async function getCommonConfig(wco) {
47
49
  var _a, _b;
48
- const { root, projectRoot, buildOptions, tsConfig, projectName, sourceRoot, tsConfigPath } = wco;
50
+ const { root, projectRoot, buildOptions, tsConfig, projectName, sourceRoot, tsConfigPath, scriptTarget, } = wco;
49
51
  const { cache, codeCoverage, crossOrigin = 'none', platform = 'browser', aot = true, codeCoverageExclude = [], main, polyfills, sourceMap: { styles: stylesSourceMap, scripts: scriptsSourceMap, vendor: vendorSourceMap, hidden: hiddenSourceMap, }, optimization: { styles: stylesOptimization, scripts: scriptsOptimization }, commonChunk, vendorChunk, subresourceIntegrity, verbose, poll, webWorkerTsConfig, externalDependencies = [], allowedCommonJsDependencies, bundleDependencies, } = buildOptions;
50
52
  const isPlatformServer = buildOptions.platform === 'server';
51
53
  const extraPlugins = [];
@@ -54,10 +56,7 @@ async function getCommonConfig(wco) {
54
56
  // Load ESM `@angular/compiler-cli` using the TypeScript dynamic import workaround.
55
57
  // Once TypeScript provides support for keeping the dynamic import this workaround can be
56
58
  // changed to a direct dynamic import.
57
- const compilerCliModule = await (0, load_esm_1.loadEsmModule)('@angular/compiler-cli');
58
- // If it is not ESM then the values needed will be stored in the `default` property.
59
- // TODO_ESM: This can be removed once `@angular/compiler-cli` is ESM only.
60
- const { GLOBAL_DEFS_FOR_TERSER, GLOBAL_DEFS_FOR_TERSER_WITH_AOT, VERSION: NG_VERSION, } = (compilerCliModule.GLOBAL_DEFS_FOR_TERSER ? compilerCliModule : compilerCliModule.default);
59
+ const { GLOBAL_DEFS_FOR_TERSER, GLOBAL_DEFS_FOR_TERSER_WITH_AOT, VERSION: NG_VERSION, } = await (0, load_esm_1.loadEsmModule)('@angular/compiler-cli');
61
60
  // determine hashing format
62
61
  const hashFormat = (0, helpers_1.getOutputHashFormat)(buildOptions.outputHashing || 'none');
63
62
  if (buildOptions.progress) {
@@ -91,11 +90,6 @@ async function getCommonConfig(wco) {
91
90
  }
92
91
  }
93
92
  }
94
- if (environment_options_1.profilingEnabled) {
95
- extraPlugins.push(new webpack_2.debug.ProfilingPlugin({
96
- outputPath: path.resolve(root, 'chrome-profiler-events.json'),
97
- }));
98
- }
99
93
  if (allowedCommonJsDependencies) {
100
94
  // When this is not defined it means the builder doesn't support showing common js usages.
101
95
  // When it does it will be an array.
@@ -188,8 +182,10 @@ async function getCommonConfig(wco) {
188
182
  }
189
183
  if (main || polyfills) {
190
184
  extraRules.push({
191
- test: /\.[cm]?[tj]sx?$/,
185
+ test: tsConfig.options.allowJs ? /\.[cm]?[tj]sx?$/ : /\.[cm]?tsx?$/,
192
186
  loader: webpack_1.AngularWebpackLoaderPath,
187
+ // The below are known paths that are not part of the TypeScript compilation even when allowJs is enabled.
188
+ exclude: [/[/\\](?:css-loader|mini-css-extract-plugin|webpack-dev-server|webpack)[/\\]/],
193
189
  });
194
190
  extraPlugins.push((0, typescript_2.createIvyPlugin)(wco, aot, tsConfigPath));
195
191
  }
@@ -201,12 +197,15 @@ async function getCommonConfig(wco) {
201
197
  extraMinimizers.push(new plugins_1.JavaScriptOptimizerPlugin({
202
198
  define: buildOptions.aot ? GLOBAL_DEFS_FOR_TERSER_WITH_AOT : GLOBAL_DEFS_FOR_TERSER,
203
199
  sourcemap: scriptsSourceMap,
204
- target: wco.scriptTarget,
200
+ target: scriptTarget,
205
201
  keepNames: !environment_options_1.allowMangle || isPlatformServer,
206
202
  removeLicenses: buildOptions.extractLicenses,
207
203
  advanced: buildOptions.buildOptimizer,
208
204
  }));
209
205
  }
206
+ if (platform === 'browser' && (scriptsOptimization || stylesOptimization.minify)) {
207
+ extraMinimizers.push(new transfer_size_plugin_1.TransferSizePlugin());
208
+ }
210
209
  const externals = [...externalDependencies];
211
210
  if (isPlatformServer && !bundleDependencies) {
212
211
  externals.push(({ context, request }, callback) => (0, helpers_1.externalizePackages)(context !== null && context !== void 0 ? context : wco.projectRoot, request, callback));
@@ -223,7 +222,7 @@ async function getCommonConfig(wco) {
223
222
  devtool: false,
224
223
  target: [
225
224
  isPlatformServer ? 'node' : 'web',
226
- tsConfig.options.target === typescript_1.ScriptTarget.ES5 ? 'es5' : 'es2015',
225
+ scriptTarget === typescript_1.ScriptTarget.ES5 ? 'es5' : 'es2015',
227
226
  ],
228
227
  profile: buildOptions.statsJson,
229
228
  resolve: {
@@ -231,10 +230,7 @@ async function getCommonConfig(wco) {
231
230
  extensions: ['.ts', '.tsx', '.mjs', '.js'],
232
231
  symlinks: !buildOptions.preserveSymlinks,
233
232
  modules: [tsConfig.options.baseUrl || projectRoot, 'node_modules'],
234
- mainFields: isPlatformServer
235
- ? ['es2015', 'module', 'main']
236
- : ['es2020', 'es2015', 'browser', 'module', 'main'],
237
- conditionNames: isPlatformServer ? ['es2015', '...'] : ['es2020', 'es2015', '...'],
233
+ ...(0, helpers_1.getMainFieldsAndConditionNames)(scriptTarget, isPlatformServer),
238
234
  },
239
235
  resolveLoader: {
240
236
  symlinks: !buildOptions.preserveSymlinks,
@@ -258,7 +254,7 @@ async function getCommonConfig(wco) {
258
254
  watch: buildOptions.watch,
259
255
  watchOptions: {
260
256
  poll,
261
- ignored: poll === undefined ? undefined : 'node_modules/**',
257
+ ignored: poll === undefined ? undefined : '**/node_modules/**',
262
258
  },
263
259
  performance: {
264
260
  hints: false,
@@ -284,6 +280,12 @@ async function getCommonConfig(wco) {
284
280
  }
285
281
  : undefined,
286
282
  rules: [
283
+ {
284
+ test: /\.?(svg|html)$/,
285
+ // Only process HTML and SVG which are known Angular component resources.
286
+ resourceQuery: /\?ngResource/,
287
+ type: 'asset/source',
288
+ },
287
289
  {
288
290
  // Mark files inside `rxjs/add` as containing side effects.
289
291
  // If this is fixed upstream and the fixed version becomes the minimum
@@ -301,7 +303,7 @@ async function getCommonConfig(wco) {
301
303
  loader: require.resolve('../../babel/webpack-loader'),
302
304
  options: {
303
305
  cacheDirectory: (cache.enabled && path.join(cache.path, 'babel-webpack')) || false,
304
- scriptTarget: wco.scriptTarget,
306
+ scriptTarget,
305
307
  aot: buildOptions.aot,
306
308
  optimize: buildOptions.buildOptimizer,
307
309
  instrumentCode: codeCoverage
@@ -318,6 +320,7 @@ async function getCommonConfig(wco) {
318
320
  ],
319
321
  },
320
322
  experiments: {
323
+ backCompat: false,
321
324
  syncWebAssembly: true,
322
325
  asyncWebAssembly: true,
323
326
  },
@@ -357,7 +360,7 @@ async function getCommonConfig(wco) {
357
360
  },
358
361
  },
359
362
  },
360
- plugins: [new plugins_1.DedupeModuleResolvePlugin({ verbose }), ...extraPlugins],
363
+ plugins: [new named_chunks_plugin_1.NamedChunksPlugin(), new plugins_1.DedupeModuleResolvePlugin({ verbose }), ...extraPlugins],
361
364
  node: false,
362
365
  };
363
366
  }
@@ -30,7 +30,7 @@ exports.buildServePath = exports.getDevServerConfig = void 0;
30
30
  const core_1 = require("@angular-devkit/core");
31
31
  const fs_1 = require("fs");
32
32
  const path_1 = require("path");
33
- const url = __importStar(require("url"));
33
+ const url_1 = require("url");
34
34
  const load_esm_1 = require("../../utils/load-esm");
35
35
  const webpack_browser_config_1 = require("../../utils/webpack-browser-config");
36
36
  const hmr_loader_1 = require("../plugins/hmr/hmr-loader");
@@ -78,7 +78,7 @@ async function getDevServerConfig(wco) {
78
78
  rewrites: [
79
79
  {
80
80
  from: new RegExp(`^(?!${servePath})/.*`),
81
- to: (context) => url.format(context.parsedUrl),
81
+ to: (context) => context.parsedUrl.href,
82
82
  },
83
83
  ],
84
84
  },
@@ -89,7 +89,7 @@ async function getDevServerConfig(wco) {
89
89
  },
90
90
  compress: false,
91
91
  static: false,
92
- https: getSslConfig(root, wco.buildOptions),
92
+ server: getServerConfig(root, wco.buildOptions),
93
93
  allowedHosts: getAllowedHostsConfig(wco.buildOptions),
94
94
  devMiddleware: {
95
95
  publicPath: servePath,
@@ -139,15 +139,20 @@ exports.buildServePath = buildServePath;
139
139
  * Private method to enhance a webpack config with SSL configuration.
140
140
  * @private
141
141
  */
142
- function getSslConfig(root, options) {
142
+ function getServerConfig(root, options) {
143
143
  const { ssl, sslCert, sslKey } = options;
144
- if (ssl && sslCert && sslKey) {
145
- return {
146
- key: (0, path_1.resolve)(root, sslKey),
147
- cert: (0, path_1.resolve)(root, sslCert),
148
- };
144
+ if (!ssl) {
145
+ return 'http';
149
146
  }
150
- return ssl;
147
+ return {
148
+ type: 'https',
149
+ options: sslCert && sslKey
150
+ ? {
151
+ key: (0, path_1.resolve)(root, sslKey),
152
+ cert: (0, path_1.resolve)(root, sslCert),
153
+ }
154
+ : undefined,
155
+ };
151
156
  }
152
157
  /**
153
158
  * Private method to enhance a webpack config with Proxy configuration.
@@ -158,21 +163,71 @@ async function addProxyConfig(root, proxyConfig) {
158
163
  return undefined;
159
164
  }
160
165
  const proxyPath = (0, path_1.resolve)(root, proxyConfig);
161
- if ((0, fs_1.existsSync)(proxyPath)) {
162
- try {
163
- return require(proxyPath);
166
+ if (!(0, fs_1.existsSync)(proxyPath)) {
167
+ throw new Error(`Proxy configuration file ${proxyPath} does not exist.`);
168
+ }
169
+ switch ((0, path_1.extname)(proxyPath)) {
170
+ case '.json': {
171
+ const content = await fs_1.promises.readFile(proxyPath, 'utf-8');
172
+ const { parse, printParseErrorCode } = await Promise.resolve().then(() => __importStar(require('jsonc-parser')));
173
+ const parseErrors = [];
174
+ const proxyConfiguration = parse(content, parseErrors, { allowTrailingComma: true });
175
+ if (parseErrors.length > 0) {
176
+ let errorMessage = `Proxy configuration file ${proxyPath} contains parse errors:`;
177
+ for (const parseError of parseErrors) {
178
+ const { line, column } = getJsonErrorLineColumn(parseError.offset, content);
179
+ errorMessage += `\n[${line}, ${column}] ${printParseErrorCode(parseError.error)}`;
180
+ }
181
+ throw new Error(errorMessage);
182
+ }
183
+ return proxyConfiguration;
164
184
  }
165
- catch (e) {
166
- if (e.code === 'ERR_REQUIRE_ESM') {
167
- // Load the ESM configuration file using the TypeScript dynamic import workaround.
168
- // Once TypeScript provides support for keeping the dynamic import this workaround can be
169
- // changed to a direct dynamic import.
170
- return (await (0, load_esm_1.loadEsmModule)(url.pathToFileURL(proxyPath))).default;
185
+ case '.mjs':
186
+ // Load the ESM configuration file using the TypeScript dynamic import workaround.
187
+ // Once TypeScript provides support for keeping the dynamic import this workaround can be
188
+ // changed to a direct dynamic import.
189
+ return (await (0, load_esm_1.loadEsmModule)((0, url_1.pathToFileURL)(proxyPath))).default;
190
+ case '.cjs':
191
+ return require(proxyPath);
192
+ default:
193
+ // The file could be either CommonJS or ESM.
194
+ // CommonJS is tried first then ESM if loading fails.
195
+ try {
196
+ return require(proxyPath);
171
197
  }
172
- throw e;
198
+ catch (e) {
199
+ if (e.code === 'ERR_REQUIRE_ESM') {
200
+ // Load the ESM configuration file using the TypeScript dynamic import workaround.
201
+ // Once TypeScript provides support for keeping the dynamic import this workaround can be
202
+ // changed to a direct dynamic import.
203
+ return (await (0, load_esm_1.loadEsmModule)((0, url_1.pathToFileURL)(proxyPath))).default;
204
+ }
205
+ throw e;
206
+ }
207
+ }
208
+ }
209
+ /**
210
+ * Calculates the line and column for an error offset in the content of a JSON file.
211
+ * @param location The offset error location from the beginning of the content.
212
+ * @param content The full content of the file containing the error.
213
+ * @returns An object containing the line and column
214
+ */
215
+ function getJsonErrorLineColumn(offset, content) {
216
+ if (offset === 0) {
217
+ return { line: 1, column: 1 };
218
+ }
219
+ let line = 0;
220
+ let position = 0;
221
+ // eslint-disable-next-line no-constant-condition
222
+ while (true) {
223
+ ++line;
224
+ const nextNewline = content.indexOf('\n', position);
225
+ if (nextNewline === -1 || nextNewline > offset) {
226
+ break;
173
227
  }
228
+ position = nextNewline + 1;
174
229
  }
175
- throw new Error('Proxy config file ' + proxyPath + ' does not exist.');
230
+ return { line, column: offset - position + 1 };
176
231
  }
177
232
  /**
178
233
  * Find the default server path. We don't want to expose baseHref and deployUrl as arguments, only
@@ -219,10 +274,8 @@ function getAllowedHostsConfig(options) {
219
274
  function getPublicHostOptions(options, webSocketPath) {
220
275
  let publicHost = options.publicHost;
221
276
  if (publicHost) {
222
- if (!/^\w+:\/\//.test(publicHost)) {
223
- publicHost = `https://${publicHost}`;
224
- }
225
- publicHost = url.parse(publicHost).host;
277
+ const hostWithProtocol = !/^\w+:\/\//.test(publicHost) ? `https://${publicHost}` : publicHost;
278
+ publicHost = new url_1.URL(hostWithProtocol).host;
226
279
  }
227
280
  return `auto://${publicHost || '0.0.0.0:0'}${webSocketPath}`;
228
281
  }
@@ -91,14 +91,16 @@ function getStylesConfig(wco) {
91
91
  wco.logger.warn('Stylus usage is deprecated and will be removed in a future major version. ' +
92
92
  'To opt-out of the deprecated behaviour, please migrate to another stylesheet language.');
93
93
  }
94
- const sassImplementation = new sass_service_1.SassWorkerImplementation();
95
- extraPlugins.push({
96
- apply(compiler) {
97
- compiler.hooks.shutdown.tap('sass-worker', () => {
98
- sassImplementation === null || sassImplementation === void 0 ? void 0 : sassImplementation.close();
99
- });
100
- },
101
- });
94
+ const sassImplementation = getSassImplementation();
95
+ if (sassImplementation instanceof sass_service_1.SassWorkerImplementation) {
96
+ extraPlugins.push({
97
+ apply(compiler) {
98
+ compiler.hooks.shutdown.tap('sass-worker', () => {
99
+ sassImplementation === null || sassImplementation === void 0 ? void 0 : sassImplementation.close();
100
+ });
101
+ },
102
+ });
103
+ }
102
104
  const assetNameTemplate = (0, helpers_1.assetNameTemplateFactory)(hashFormat);
103
105
  const extraPostcssPlugins = [];
104
106
  // Attempt to setup Tailwind CSS
@@ -340,14 +342,14 @@ function getStylesConfig(wco) {
340
342
  oneOf: [
341
343
  // Component styles are all styles except defined global styles
342
344
  {
343
- exclude: globalStylePaths,
344
345
  use: componentStyleLoaders,
346
+ resourceQuery: /\?ngResource/,
345
347
  type: 'asset/source',
346
348
  },
347
349
  // Global styles are only defined global styles
348
350
  {
349
- include: globalStylePaths,
350
351
  use: globalStyleLoaders,
352
+ resourceQuery: { not: [/\?ngResource/] },
351
353
  },
352
354
  ],
353
355
  },
@@ -368,3 +370,12 @@ function getStylesConfig(wco) {
368
370
  };
369
371
  }
370
372
  exports.getStylesConfig = getStylesConfig;
373
+ function getSassImplementation() {
374
+ const { webcontainer } = process.versions;
375
+ // When `webcontainer` is a truthy it means that we are running in a StackBlitz webcontainer.
376
+ // `SassWorkerImplementation` uses `receiveMessageOnPort` Node.js `worker_thread` API to ensure sync behavior which is ~2x faster.
377
+ // However, it is non trivial to support this in a webcontainer and while slower we choose to use `dart-sass`
378
+ // which in Webpack uses the slower async path.
379
+ // We should periodically check with StackBlitz folks (Mark Whitfeld / Dominic Elm) to determine if this workaround is still needed.
380
+ return webcontainer ? require('sass') : new sass_service_1.SassWorkerImplementation();
381
+ }
@@ -0,0 +1,17 @@
1
+ /**
2
+ * @license
3
+ * Copyright Google LLC All Rights Reserved.
4
+ *
5
+ * Use of this source code is governed by an MIT-style license that can be
6
+ * found in the LICENSE file at https://angular.io/license
7
+ */
8
+ import { Compiler } from 'webpack';
9
+ /**
10
+ * Webpack will not populate the chunk `name` property unless `webpackChunkName` magic comment is used.
11
+ * This however will also effect the filename which is not desired when using `deterministic` chunkIds.
12
+ * This plugin will populate the chunk `name` which is mainly used so that users can set bundle budgets on lazy chunks.
13
+ */
14
+ export declare class NamedChunksPlugin {
15
+ apply(compiler: Compiler): void;
16
+ private generateName;
17
+ }
@@ -0,0 +1,49 @@
1
+ "use strict";
2
+ /**
3
+ * @license
4
+ * Copyright Google LLC All Rights Reserved.
5
+ *
6
+ * Use of this source code is governed by an MIT-style license that can be
7
+ * found in the LICENSE file at https://angular.io/license
8
+ */
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ exports.NamedChunksPlugin = void 0;
11
+ const webpack_1 = require("webpack");
12
+ // `ImportDependency` is not part of Webpack's depenencies typings.
13
+ const ImportDependency = require('webpack/lib/dependencies/ImportDependency');
14
+ const PLUGIN_NAME = 'named-chunks-plugin';
15
+ /**
16
+ * Webpack will not populate the chunk `name` property unless `webpackChunkName` magic comment is used.
17
+ * This however will also effect the filename which is not desired when using `deterministic` chunkIds.
18
+ * This plugin will populate the chunk `name` which is mainly used so that users can set bundle budgets on lazy chunks.
19
+ */
20
+ class NamedChunksPlugin {
21
+ apply(compiler) {
22
+ compiler.hooks.compilation.tap(PLUGIN_NAME, (compilation) => {
23
+ compilation.hooks.chunkAsset.tap(PLUGIN_NAME, (chunk) => {
24
+ if (chunk.name) {
25
+ return;
26
+ }
27
+ const name = this.generateName(chunk);
28
+ if (name) {
29
+ chunk.name = name;
30
+ }
31
+ });
32
+ });
33
+ }
34
+ generateName(chunk) {
35
+ for (const group of chunk.groupsIterable) {
36
+ const [block] = group.getBlocks();
37
+ if (!(block instanceof webpack_1.AsyncDependenciesBlock)) {
38
+ continue;
39
+ }
40
+ for (const dependency of block.dependencies) {
41
+ if (dependency instanceof ImportDependency) {
42
+ return webpack_1.Template.toPath(dependency.request);
43
+ }
44
+ }
45
+ }
46
+ return undefined;
47
+ }
48
+ }
49
+ exports.NamedChunksPlugin = NamedChunksPlugin;
@@ -0,0 +1,12 @@
1
+ /**
2
+ * @license
3
+ * Copyright Google LLC All Rights Reserved.
4
+ *
5
+ * Use of this source code is governed by an MIT-style license that can be
6
+ * found in the LICENSE file at https://angular.io/license
7
+ */
8
+ import { Compiler } from 'webpack';
9
+ export declare class TransferSizePlugin {
10
+ constructor();
11
+ apply(compiler: Compiler): void;
12
+ }
@@ -0,0 +1,47 @@
1
+ "use strict";
2
+ /**
3
+ * @license
4
+ * Copyright Google LLC All Rights Reserved.
5
+ *
6
+ * Use of this source code is governed by an MIT-style license that can be
7
+ * found in the LICENSE file at https://angular.io/license
8
+ */
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ exports.TransferSizePlugin = void 0;
11
+ const util_1 = require("util");
12
+ const zlib_1 = require("zlib");
13
+ const brotliCompressAsync = (0, util_1.promisify)(zlib_1.brotliCompress);
14
+ const PLUGIN_NAME = 'angular-transfer-size-estimator';
15
+ class TransferSizePlugin {
16
+ constructor() { }
17
+ apply(compiler) {
18
+ compiler.hooks.thisCompilation.tap(PLUGIN_NAME, (compilation) => {
19
+ compilation.hooks.processAssets.tapPromise({
20
+ name: PLUGIN_NAME,
21
+ stage: compiler.webpack.Compilation.PROCESS_ASSETS_STAGE_ANALYSE,
22
+ }, async (compilationAssets) => {
23
+ const actions = [];
24
+ for (const assetName of Object.keys(compilationAssets)) {
25
+ if (!assetName.endsWith('.js') && !assetName.endsWith('.css')) {
26
+ continue;
27
+ }
28
+ const scriptAsset = compilation.getAsset(assetName);
29
+ if (!scriptAsset || scriptAsset.source.size() <= 0) {
30
+ continue;
31
+ }
32
+ actions.push(brotliCompressAsync(scriptAsset.source.source())
33
+ .then((result) => {
34
+ compilation.updateAsset(assetName, (s) => s, {
35
+ estimatedTransferSize: result.length,
36
+ });
37
+ })
38
+ .catch((error) => {
39
+ compilation.warnings.push(new compilation.compiler.webpack.WebpackError(`Unable to calculate estimated transfer size for '${assetName}'. Reason: ${error.message}`));
40
+ }));
41
+ }
42
+ await Promise.all(actions);
43
+ });
44
+ });
45
+ }
46
+ }
47
+ exports.TransferSizePlugin = TransferSizePlugin;
@@ -6,6 +6,7 @@
6
6
  * found in the LICENSE file at https://angular.io/license
7
7
  */
8
8
  import type { ObjectPattern } from 'copy-webpack-plugin';
9
+ import { ScriptTarget } from 'typescript';
9
10
  import type { Configuration, WebpackOptionsNormalized } from 'webpack';
10
11
  import { AssetPatternClass, ExtraEntryPoint, ExtraEntryPointClass } from '../../builders/browser/schema';
11
12
  import { WebpackConfigOptions } from '../../utils/build-options';
@@ -30,4 +31,5 @@ export declare function assetPatterns(root: string, assets: AssetPatternClass[])
30
31
  export declare function externalizePackages(context: string, request: string | undefined, callback: (error?: Error, result?: string) => void): void;
31
32
  declare type WebpackStatsOptions = Exclude<Configuration['stats'], string | boolean>;
32
33
  export declare function getStatsOptions(verbose?: boolean): WebpackStatsOptions;
34
+ export declare function getMainFieldsAndConditionNames(target: ScriptTarget, platformServer: boolean): Pick<WebpackOptionsNormalized['resolve'], 'mainFields' | 'conditionNames'>;
33
35
  export {};
@@ -29,11 +29,12 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
29
29
  return (mod && mod.__esModule) ? mod : { "default": mod };
30
30
  };
31
31
  Object.defineProperty(exports, "__esModule", { value: true });
32
- exports.getStatsOptions = exports.externalizePackages = exports.assetPatterns = exports.globalScriptsByBundleName = exports.getCacheSettings = exports.getInstrumentationExcludedPaths = exports.assetNameTemplateFactory = exports.normalizeExtraEntryPoints = exports.getOutputHashFormat = void 0;
32
+ exports.getMainFieldsAndConditionNames = exports.getStatsOptions = exports.externalizePackages = exports.assetPatterns = exports.globalScriptsByBundleName = exports.getCacheSettings = exports.getInstrumentationExcludedPaths = exports.assetNameTemplateFactory = exports.normalizeExtraEntryPoints = exports.getOutputHashFormat = void 0;
33
33
  const crypto_1 = require("crypto");
34
34
  const fs_1 = require("fs");
35
35
  const glob_1 = __importDefault(require("glob"));
36
36
  const path = __importStar(require("path"));
37
+ const typescript_1 = require("typescript");
37
38
  function getOutputHashFormat(option, length = 20) {
38
39
  const hashFormats = {
39
40
  none: { chunk: '', extract: '', file: '', script: '' },
@@ -265,3 +266,18 @@ function getStatsOptions(verbose = false) {
265
266
  : webpackOutputOptions;
266
267
  }
267
268
  exports.getStatsOptions = getStatsOptions;
269
+ function getMainFieldsAndConditionNames(target, platformServer) {
270
+ const mainFields = platformServer
271
+ ? ['es2015', 'module', 'main']
272
+ : ['es2015', 'browser', 'module', 'main'];
273
+ const conditionNames = ['es2015', '...'];
274
+ if (target >= typescript_1.ScriptTarget.ES2020) {
275
+ mainFields.unshift('es2020');
276
+ conditionNames.unshift('es2020');
277
+ }
278
+ return {
279
+ mainFields,
280
+ conditionNames,
281
+ };
282
+ }
283
+ exports.getMainFieldsAndConditionNames = getMainFieldsAndConditionNames;
@@ -9,14 +9,21 @@ import { WebpackLoggingCallback } from '@angular-devkit/build-webpack';
9
9
  import { logging } from '@angular-devkit/core';
10
10
  import { Configuration, StatsCompilation } from 'webpack';
11
11
  import { Schema as BrowserBuilderOptions } from '../../builders/browser/schema';
12
+ import { BudgetCalculatorResult } from '../../utils/bundle-calculator';
12
13
  export declare function formatSize(size: number): string;
13
- export declare type BundleStatsData = [files: string, names: string, size: number | string];
14
+ export declare type BundleStatsData = [
15
+ files: string,
16
+ names: string,
17
+ rawSize: number | string,
18
+ estimatedTransferSize: number | string
19
+ ];
14
20
  export interface BundleStats {
15
21
  initial: boolean;
16
22
  stats: BundleStatsData;
17
23
  }
18
24
  export declare function generateBundleStats(info: {
19
- size?: number;
25
+ rawSize?: number;
26
+ estimatedTransferSize?: number;
20
27
  files?: string[];
21
28
  names?: string[];
22
29
  initial?: boolean;
@@ -27,4 +34,4 @@ export declare function statsErrorsToString(json: StatsCompilation, statsConfig:
27
34
  export declare function statsHasErrors(json: StatsCompilation): boolean;
28
35
  export declare function statsHasWarnings(json: StatsCompilation): boolean;
29
36
  export declare function createWebpackLoggingCallback(options: BrowserBuilderOptions, logger: logging.LoggerApi): WebpackLoggingCallback;
30
- export declare function webpackStatsLogger(logger: logging.LoggerApi, json: StatsCompilation, config: Configuration, bundleStats?: BundleStats[]): void;
37
+ export declare function webpackStatsLogger(logger: logging.LoggerApi, json: StatsCompilation, config: Configuration, budgetFailures?: BudgetCalculatorResult[]): void;