@angular-devkit/build-angular 15.0.0-next.4 → 15.0.0-next.5

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.
Files changed (32) hide show
  1. package/package.json +13 -13
  2. package/src/builders/app-shell/index.js +39 -40
  3. package/src/builders/app-shell/render-worker.d.ts +36 -0
  4. package/src/builders/app-shell/render-worker.js +82 -0
  5. package/src/builders/browser/index.d.ts +2 -0
  6. package/src/builders/browser/index.js +38 -19
  7. package/src/builders/browser/schema.json +2 -2
  8. package/src/builders/browser-esbuild/compiler-plugin.d.ts +1 -0
  9. package/src/builders/browser-esbuild/compiler-plugin.js +125 -53
  10. package/src/builders/browser-esbuild/index.js +3 -2
  11. package/src/builders/browser-esbuild/profiling.d.ts +11 -0
  12. package/src/builders/browser-esbuild/profiling.js +64 -0
  13. package/src/builders/browser-esbuild/sass-plugin.js +11 -5
  14. package/src/builders/browser-esbuild/schema.json +2 -2
  15. package/src/builders/dev-server/index.d.ts +2 -0
  16. package/src/builders/dev-server/index.js +10 -7
  17. package/src/builders/karma/find-tests-plugin.js +1 -0
  18. package/src/builders/server/schema.json +1 -1
  19. package/src/utils/environment-options.d.ts +1 -0
  20. package/src/utils/environment-options.js +3 -1
  21. package/src/webpack/configs/common.js +29 -5
  22. package/src/webpack/configs/index.d.ts +0 -1
  23. package/src/webpack/configs/index.js +0 -1
  24. package/src/webpack/configs/styles.js +21 -4
  25. package/src/webpack/plugins/occurrences-plugin.d.ts +18 -0
  26. package/src/webpack/plugins/occurrences-plugin.js +79 -0
  27. package/src/webpack/utils/stats.d.ts +13 -8
  28. package/src/webpack/utils/stats.js +57 -6
  29. package/src/webpack/configs/analytics.d.ts +0 -11
  30. package/src/webpack/configs/analytics.js +0 -27
  31. package/src/webpack/plugins/analytics.d.ts +0 -66
  32. package/src/webpack/plugins/analytics.js +0 -236
@@ -43,6 +43,7 @@ const typescript_1 = __importDefault(require("typescript"));
43
43
  const application_1 = __importDefault(require("../../babel/presets/application"));
44
44
  const webpack_loader_1 = require("../../babel/webpack-loader");
45
45
  const load_esm_1 = require("../../utils/load-esm");
46
+ const profiling_1 = require("./profiling");
46
47
  const stylesheets_1 = require("./stylesheets");
47
48
  /**
48
49
  * Converts TypeScript Diagnostic related information into an esbuild compatible note object.
@@ -113,10 +114,12 @@ class SourceFileCache extends Map {
113
114
  constructor() {
114
115
  super(...arguments);
115
116
  this.modifiedFiles = new Set();
117
+ this.babelFileCache = new Map();
116
118
  }
117
119
  invalidate(files) {
118
120
  this.modifiedFiles.clear();
119
121
  for (let file of files) {
122
+ this.babelFileCache.delete(file);
120
123
  // Normalize separators to allow matching TypeScript Host paths
121
124
  if (USING_WINDOWS) {
122
125
  file = file.replace(WINDOWS_SEP_REGEXP, path.posix.sep);
@@ -136,14 +139,15 @@ function createCompilerPlugin(pluginOptions, styleOptions) {
136
139
  async setup(build) {
137
140
  var _a, _b;
138
141
  var _c;
142
+ let setupWarnings;
139
143
  // This uses a wrapped dynamic import to load `@angular/compiler-cli` which is ESM.
140
144
  // Once TypeScript provides support for retaining dynamic imports this workaround can be dropped.
141
- const compilerCli = await (0, load_esm_1.loadEsmModule)('@angular/compiler-cli');
145
+ const { GLOBAL_DEFS_FOR_TERSER_WITH_AOT, NgtscProgram, OptimizeFor, readConfiguration } = await (0, load_esm_1.loadEsmModule)('@angular/compiler-cli');
142
146
  // Temporary deep import for transformer support
143
147
  const { mergeTransformers, replaceBootstrap, } = require('@ngtools/webpack/src/ivy/transformation');
144
148
  // Setup defines based on the values provided by the Angular compiler-cli
145
149
  (_a = (_c = build.initialOptions).define) !== null && _a !== void 0 ? _a : (_c.define = {});
146
- for (const [key, value] of Object.entries(compilerCli.GLOBAL_DEFS_FOR_TERSER_WITH_AOT)) {
150
+ for (const [key, value] of Object.entries(GLOBAL_DEFS_FOR_TERSER_WITH_AOT)) {
147
151
  if (key in build.initialOptions.define) {
148
152
  // Skip keys that have been manually provided
149
153
  continue;
@@ -154,7 +158,7 @@ function createCompilerPlugin(pluginOptions, styleOptions) {
154
158
  }
155
159
  // The tsconfig is loaded in setup instead of in start to allow the esbuild target build option to be modified.
156
160
  // esbuild build options can only be modified in setup prior to starting the build.
157
- const { options: compilerOptions, rootNames, errors: configurationDiagnostics, } = compilerCli.readConfiguration(pluginOptions.tsconfig, {
161
+ const { options: compilerOptions, rootNames, errors: configurationDiagnostics, } = (0, profiling_1.profileSync)('NG_READ_CONFIG', () => readConfiguration(pluginOptions.tsconfig, {
158
162
  noEmitOnError: false,
159
163
  suppressOutputPathCheck: true,
160
164
  outDir: undefined,
@@ -168,14 +172,24 @@ function createCompilerPlugin(pluginOptions, styleOptions) {
168
172
  allowEmptyCodegenFiles: false,
169
173
  annotationsAs: 'decorators',
170
174
  enableResourceInlining: false,
171
- });
175
+ }));
172
176
  if (compilerOptions.target === undefined || compilerOptions.target < typescript_1.default.ScriptTarget.ES2022) {
173
177
  // If 'useDefineForClassFields' is already defined in the users project leave the value as is.
174
178
  // Otherwise fallback to false due to https://github.com/microsoft/TypeScript/issues/45995
175
179
  // which breaks the deprecated `@Effects` NGRX decorator and potentially other existing code as well.
176
180
  compilerOptions.target = typescript_1.default.ScriptTarget.ES2022;
177
181
  (_b = compilerOptions.useDefineForClassFields) !== null && _b !== void 0 ? _b : (compilerOptions.useDefineForClassFields = false);
178
- // TODO: show warning about this override when we have access to the logger.
182
+ (setupWarnings !== null && setupWarnings !== void 0 ? setupWarnings : (setupWarnings = [])).push({
183
+ text: 'TypeScript compiler options "target" and "useDefineForClassFields" are set to "ES2022" and ' +
184
+ '"false" respectively by the Angular CLI.',
185
+ location: { file: pluginOptions.tsconfig },
186
+ notes: [
187
+ {
188
+ text: `To control ECMA version and features use the Browerslist configuration. ' +
189
+ 'For more information, see https://github.com/browserslist/browserslist#queries'`,
190
+ },
191
+ ],
192
+ });
179
193
  }
180
194
  // The file emitter created during `onStart` that will be used during the build in `onLoad` callbacks for TS files
181
195
  let fileEmitter;
@@ -183,10 +197,16 @@ function createCompilerPlugin(pluginOptions, styleOptions) {
183
197
  let stylesheetResourceFiles;
184
198
  let previousBuilder;
185
199
  let previousAngularProgram;
186
- const babelMemoryCache = new Map();
200
+ const babelDataCache = new Map();
201
+ const diagnosticCache = new WeakMap();
187
202
  build.onStart(async () => {
188
- var _a, _b;
189
- const result = {};
203
+ const result = {
204
+ warnings: setupWarnings,
205
+ };
206
+ // Reset the setup warnings so that they are only shown during the first build.
207
+ setupWarnings = undefined;
208
+ // Reset debug performance tracking
209
+ (0, profiling_1.resetCumulativeDurations)();
190
210
  // Reset stylesheet resource output files
191
211
  stylesheetResourceFiles = [];
192
212
  // Create TypeScript compiler host
@@ -241,15 +261,14 @@ function createCompilerPlugin(pluginOptions, styleOptions) {
241
261
  };
242
262
  }
243
263
  // Create the Angular specific program that contains the Angular compiler
244
- const angularProgram = new compilerCli.NgtscProgram(rootNames, compilerOptions, host, previousAngularProgram);
264
+ const angularProgram = (0, profiling_1.profileSync)('NG_CREATE_PROGRAM', () => new NgtscProgram(rootNames, compilerOptions, host, previousAngularProgram));
245
265
  previousAngularProgram = angularProgram;
246
266
  const angularCompiler = angularProgram.compiler;
247
- const { ignoreForDiagnostics } = angularCompiler;
248
267
  const typeScriptProgram = angularProgram.getTsProgram();
249
268
  augmentProgramWithVersioning(typeScriptProgram);
250
269
  const builder = typescript_1.default.createEmitAndSemanticDiagnosticsBuilderProgram(typeScriptProgram, host, previousBuilder, configurationDiagnostics);
251
270
  previousBuilder = builder;
252
- await angularCompiler.analyzeAsync();
271
+ await (0, profiling_1.profileAsync)('NG_ANALYZE_PROGRAM', () => angularCompiler.analyzeAsync());
253
272
  function* collectDiagnostics() {
254
273
  // Collect program level diagnostics
255
274
  yield* builder.getConfigFileParsingDiagnostics();
@@ -257,32 +276,49 @@ function createCompilerPlugin(pluginOptions, styleOptions) {
257
276
  yield* builder.getOptionsDiagnostics();
258
277
  yield* builder.getGlobalDiagnostics();
259
278
  // Collect source file specific diagnostics
260
- const OptimizeFor = compilerCli.OptimizeFor;
279
+ const affectedFiles = findAffectedFiles(builder, angularCompiler);
280
+ const optimizeFor = affectedFiles.size > 1 ? OptimizeFor.WholeProgram : OptimizeFor.SingleFile;
261
281
  for (const sourceFile of builder.getSourceFiles()) {
262
- if (ignoreForDiagnostics.has(sourceFile)) {
282
+ if (angularCompiler.ignoreForDiagnostics.has(sourceFile)) {
263
283
  continue;
264
284
  }
265
- yield* builder.getSyntacticDiagnostics(sourceFile);
266
- yield* builder.getSemanticDiagnostics(sourceFile);
267
- const angularDiagnostics = angularCompiler.getDiagnosticsForFile(sourceFile, OptimizeFor.WholeProgram);
268
- yield* angularDiagnostics;
285
+ // TypeScript will use cached diagnostics for files that have not been
286
+ // changed or affected for this build when using incremental building.
287
+ yield* (0, profiling_1.profileSync)('NG_DIAGNOSTICS_SYNTACTIC', () => builder.getSyntacticDiagnostics(sourceFile), true);
288
+ yield* (0, profiling_1.profileSync)('NG_DIAGNOSTICS_SEMANTIC', () => builder.getSemanticDiagnostics(sourceFile), true);
289
+ // Only request Angular template diagnostics for affected files to avoid
290
+ // overhead of template diagnostics for unchanged files.
291
+ if (affectedFiles.has(sourceFile)) {
292
+ const angularDiagnostics = (0, profiling_1.profileSync)('NG_DIAGNOSTICS_TEMPLATE', () => angularCompiler.getDiagnosticsForFile(sourceFile, optimizeFor), true);
293
+ diagnosticCache.set(sourceFile, angularDiagnostics);
294
+ yield* angularDiagnostics;
295
+ }
296
+ else {
297
+ const angularDiagnostics = diagnosticCache.get(sourceFile);
298
+ if (angularDiagnostics) {
299
+ yield* angularDiagnostics;
300
+ }
301
+ }
269
302
  }
270
303
  }
271
- for (const diagnostic of collectDiagnostics()) {
272
- const message = convertTypeScriptDiagnostic(diagnostic, host);
273
- if (diagnostic.category === typescript_1.default.DiagnosticCategory.Error) {
274
- ((_a = result.errors) !== null && _a !== void 0 ? _a : (result.errors = [])).push(message);
275
- }
276
- else {
277
- ((_b = result.warnings) !== null && _b !== void 0 ? _b : (result.warnings = [])).push(message);
304
+ (0, profiling_1.profileSync)('NG_DIAGNOSTICS_TOTAL', () => {
305
+ var _a, _b;
306
+ for (const diagnostic of collectDiagnostics()) {
307
+ const message = convertTypeScriptDiagnostic(diagnostic, host);
308
+ if (diagnostic.category === typescript_1.default.DiagnosticCategory.Error) {
309
+ ((_a = result.errors) !== null && _a !== void 0 ? _a : (result.errors = [])).push(message);
310
+ }
311
+ else {
312
+ ((_b = result.warnings) !== null && _b !== void 0 ? _b : (result.warnings = [])).push(message);
313
+ }
278
314
  }
279
- }
315
+ });
280
316
  fileEmitter = createFileEmitter(builder, mergeTransformers(angularCompiler.prepareEmit().transformers, {
281
317
  before: [replaceBootstrap(() => builder.getProgram().getTypeChecker())],
282
- }), (sourceFile) => angularCompiler.incrementalDriver.recordSuccessfulEmit(sourceFile));
318
+ }), (sourceFile) => angularCompiler.incrementalCompilation.recordSuccessfulEmit(sourceFile));
283
319
  return result;
284
320
  });
285
- build.onLoad({ filter: compilerOptions.allowJs ? /\.[cm]?[jt]sx?$/ : /\.[cm]?tsx?$/ }, async (args) => {
321
+ build.onLoad({ filter: compilerOptions.allowJs ? /\.[cm]?[jt]sx?$/ : /\.[cm]?tsx?$/ }, (args) => (0, profiling_1.profileAsync)('NG_EMIT_TS*', async () => {
286
322
  var _a, _b, _c;
287
323
  assert.ok(fileEmitter, 'Invalid plugin execution order');
288
324
  const typescriptResult = await fileEmitter((_b = (_a = pluginOptions.fileReplacements) === null || _a === void 0 ? void 0 : _a[args.path]) !== null && _b !== void 0 ? _b : args.path);
@@ -306,23 +342,44 @@ function createCompilerPlugin(pluginOptions, styleOptions) {
306
342
  ],
307
343
  };
308
344
  }
345
+ const data = (_c = typescriptResult.content) !== null && _c !== void 0 ? _c : '';
346
+ // The pre-transformed data is used as a cache key. Since the cache is memory only,
347
+ // the options cannot change and do not need to be represented in the key. If the
348
+ // cache is later stored to disk, then the options that affect transform output
349
+ // would need to be added to the key as well.
350
+ let contents = babelDataCache.get(data);
351
+ if (contents === undefined) {
352
+ contents = await transformWithBabel(args.path, data, pluginOptions);
353
+ babelDataCache.set(data, contents);
354
+ }
309
355
  return {
310
- contents: await transformWithBabelCached(args.path, (_c = typescriptResult.content) !== null && _c !== void 0 ? _c : '', pluginOptions, babelMemoryCache),
356
+ contents,
311
357
  loader: 'js',
312
358
  };
313
- });
314
- build.onLoad({ filter: /\.[cm]?js$/ }, async (args) => {
315
- const data = await fs_1.promises.readFile(args.path, 'utf-8');
359
+ }, true));
360
+ build.onLoad({ filter: /\.[cm]?js$/ }, (args) => (0, profiling_1.profileAsync)('NG_EMIT_JS*', async () => {
361
+ var _a, _b;
362
+ // The filename is currently used as a cache key. Since the cache is memory only,
363
+ // the options cannot change and do not need to be represented in the key. If the
364
+ // cache is later stored to disk, then the options that affect transform output
365
+ // would need to be added to the key as well as a check for any change of content.
366
+ let contents = (_a = pluginOptions.sourceFileCache) === null || _a === void 0 ? void 0 : _a.babelFileCache.get(args.path);
367
+ if (contents === undefined) {
368
+ const data = await fs_1.promises.readFile(args.path, 'utf-8');
369
+ contents = await transformWithBabel(args.path, data, pluginOptions);
370
+ (_b = pluginOptions.sourceFileCache) === null || _b === void 0 ? void 0 : _b.babelFileCache.set(args.path, contents);
371
+ }
316
372
  return {
317
- contents: await transformWithBabelCached(args.path, data, pluginOptions, babelMemoryCache),
373
+ contents,
318
374
  loader: 'js',
319
375
  };
320
- });
376
+ }, true));
321
377
  build.onEnd((result) => {
322
378
  var _a;
323
379
  if (stylesheetResourceFiles.length) {
324
380
  (_a = result.outputFiles) === null || _a === void 0 ? void 0 : _a.push(...stylesheetResourceFiles);
325
381
  }
382
+ (0, profiling_1.logCumulativeDurations)();
326
383
  });
327
384
  },
328
385
  };
@@ -388,25 +445,40 @@ async function transformWithBabel(filename, data, pluginOptions) {
388
445
  });
389
446
  return (_a = result === null || result === void 0 ? void 0 : result.code) !== null && _a !== void 0 ? _a : data;
390
447
  }
391
- /**
392
- * Transforms JavaScript file data using the babel transforms setup in transformWithBabel. The
393
- * supplied cache will be used to avoid repeating the transforms for data that has previously
394
- * been transformed such as in a previous rebuild cycle.
395
- * @param filename The file path of the data to be transformed.
396
- * @param data The file data that will be transformed.
397
- * @param pluginOptions Compiler plugin options that will be used to control the transformation.
398
- * @param cache A cache of previously transformed data that will be used to avoid repeat transforms.
399
- * @returns A promise containing the transformed data.
400
- */
401
- async function transformWithBabelCached(filename, data, pluginOptions, cache) {
402
- // The pre-transformed data is used as a cache key. Since the cache is memory only,
403
- // the options cannot change and do not need to be represented in the key. If the
404
- // cache is later stored to disk, then the options that affect transform output
405
- // would need to be added to the key as well.
406
- let result = cache.get(data);
407
- if (result === undefined) {
408
- result = await transformWithBabel(filename, data, pluginOptions);
409
- cache.set(data, result);
448
+ function findAffectedFiles(builder, { ignoreForDiagnostics, ignoreForEmit, incrementalCompilation }) {
449
+ const affectedFiles = new Set();
450
+ // eslint-disable-next-line no-constant-condition
451
+ while (true) {
452
+ const result = builder.getSemanticDiagnosticsOfNextAffectedFile(undefined, (sourceFile) => {
453
+ // If the affected file is a TTC shim, add the shim's original source file.
454
+ // This ensures that changes that affect TTC are typechecked even when the changes
455
+ // are otherwise unrelated from a TS perspective and do not result in Ivy codegen changes.
456
+ // For example, changing @Input property types of a directive used in another component's
457
+ // template.
458
+ // A TTC shim is a file that has been ignored for diagnostics and has a filename ending in `.ngtypecheck.ts`.
459
+ if (ignoreForDiagnostics.has(sourceFile) && sourceFile.fileName.endsWith('.ngtypecheck.ts')) {
460
+ // This file name conversion relies on internal compiler logic and should be converted
461
+ // to an official method when available. 15 is length of `.ngtypecheck.ts`
462
+ const originalFilename = sourceFile.fileName.slice(0, -15) + '.ts';
463
+ const originalSourceFile = builder.getSourceFile(originalFilename);
464
+ if (originalSourceFile) {
465
+ affectedFiles.add(originalSourceFile);
466
+ }
467
+ return true;
468
+ }
469
+ return false;
470
+ });
471
+ if (!result) {
472
+ break;
473
+ }
474
+ affectedFiles.add(result.affected);
410
475
  }
411
- return result;
476
+ // A file is also affected if the Angular compiler requires it to be emitted
477
+ for (const sourceFile of builder.getSourceFiles()) {
478
+ if (ignoreForEmit.has(sourceFile) || incrementalCompilation.safeToSkipEmit(sourceFile)) {
479
+ continue;
480
+ }
481
+ affectedFiles.add(sourceFile);
482
+ }
483
+ return affectedFiles;
412
484
  }
@@ -78,7 +78,7 @@ class ExecutionResult {
78
78
  }
79
79
  async function execute(options, context, rebuildState) {
80
80
  var _a, _b, _c, _d;
81
- const startTime = Date.now();
81
+ const startTime = process.hrtime.bigint();
82
82
  const { projectRoot, workspaceRoot, optimizationOptions, outputPath, assets, serviceWorkerOptions, indexHtmlOptions, } = options;
83
83
  const target = (0, esbuild_targets_1.transformSupportedBrowsersToTargets)((0, supported_browsers_1.getSupportedBrowsers)(projectRoot, context.logger));
84
84
  const codeBundleCache = options.watch
@@ -177,7 +177,8 @@ async function execute(options, context, rebuildState) {
177
177
  return new ExecutionResult(false, codeResults.rebuild, codeBundleCache);
178
178
  }
179
179
  }
180
- context.logger.info(`Complete. [${(Date.now() - startTime) / 1000} seconds]`);
180
+ const buildTime = Number(process.hrtime.bigint() - startTime) / 10 ** 9;
181
+ context.logger.info(`Complete. [${buildTime.toFixed(3)} seconds]`);
181
182
  return new ExecutionResult(true, codeResults.rebuild, codeBundleCache);
182
183
  }
183
184
  function createOutputFileFromText(path, text) {
@@ -0,0 +1,11 @@
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
+ export declare function resetCumulativeDurations(): void;
9
+ export declare function logCumulativeDurations(): void;
10
+ export declare function profileAsync<T>(name: string, action: () => Promise<T>, cumulative?: boolean): Promise<T>;
11
+ export declare function profileSync<T>(name: string, action: () => T, cumulative?: boolean): T;
@@ -0,0 +1,64 @@
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.profileSync = exports.profileAsync = exports.logCumulativeDurations = exports.resetCumulativeDurations = void 0;
11
+ const environment_options_1 = require("../../utils/environment-options");
12
+ let cumulativeDurations;
13
+ function resetCumulativeDurations() {
14
+ cumulativeDurations === null || cumulativeDurations === void 0 ? void 0 : cumulativeDurations.clear();
15
+ }
16
+ exports.resetCumulativeDurations = resetCumulativeDurations;
17
+ function logCumulativeDurations() {
18
+ if (!environment_options_1.debugPerformance || !cumulativeDurations) {
19
+ return;
20
+ }
21
+ for (const [name, duration] of cumulativeDurations) {
22
+ // eslint-disable-next-line no-console
23
+ console.log(`DURATION[${name}]: ${duration.toFixed(9)} seconds`);
24
+ }
25
+ }
26
+ exports.logCumulativeDurations = logCumulativeDurations;
27
+ function recordDuration(name, startTime, cumulative) {
28
+ var _a;
29
+ const duration = Number(process.hrtime.bigint() - startTime) / 10 ** 9;
30
+ if (cumulative) {
31
+ cumulativeDurations !== null && cumulativeDurations !== void 0 ? cumulativeDurations : (cumulativeDurations = new Map());
32
+ cumulativeDurations.set(name, ((_a = cumulativeDurations.get(name)) !== null && _a !== void 0 ? _a : 0) + duration);
33
+ }
34
+ else {
35
+ // eslint-disable-next-line no-console
36
+ console.log(`DURATION[${name}]: ${duration.toFixed(9)} seconds`);
37
+ }
38
+ }
39
+ async function profileAsync(name, action, cumulative) {
40
+ if (!environment_options_1.debugPerformance) {
41
+ return action();
42
+ }
43
+ const startTime = process.hrtime.bigint();
44
+ try {
45
+ return await action();
46
+ }
47
+ finally {
48
+ recordDuration(name, startTime, cumulative);
49
+ }
50
+ }
51
+ exports.profileAsync = profileAsync;
52
+ function profileSync(name, action, cumulative) {
53
+ if (!environment_options_1.debugPerformance) {
54
+ return action();
55
+ }
56
+ const startTime = process.hrtime.bigint();
57
+ try {
58
+ return action();
59
+ }
60
+ finally {
61
+ recordDuration(name, startTime, cumulative);
62
+ }
63
+ }
64
+ exports.profileSync = profileSync;
@@ -31,7 +31,8 @@ var __importStar = (this && this.__importStar) || function (mod) {
31
31
  };
32
32
  Object.defineProperty(exports, "__esModule", { value: true });
33
33
  exports.createSassPlugin = void 0;
34
- const url_1 = require("url");
34
+ const node_path_1 = require("node:path");
35
+ const node_url_1 = require("node:url");
35
36
  function createSassPlugin(options) {
36
37
  return {
37
38
  name: 'angular-sass',
@@ -59,14 +60,16 @@ function createSassPlugin(options) {
59
60
  });
60
61
  return {
61
62
  loader: 'css',
62
- contents: sourceMap ? `${css}\n${sourceMapToUrlComment(sourceMap)}` : css,
63
- watchFiles: loadedUrls.map((url) => (0, url_1.fileURLToPath)(url)),
63
+ contents: sourceMap
64
+ ? `${css}\n${sourceMapToUrlComment(sourceMap, (0, node_path_1.dirname)(args.path))}`
65
+ : css,
66
+ watchFiles: loadedUrls.map((url) => (0, node_url_1.fileURLToPath)(url)),
64
67
  warnings,
65
68
  };
66
69
  }
67
70
  catch (error) {
68
71
  if (error instanceof sass.Exception) {
69
- const file = error.span.url ? (0, url_1.fileURLToPath)(error.span.url) : undefined;
72
+ const file = error.span.url ? (0, node_url_1.fileURLToPath)(error.span.url) : undefined;
70
73
  return {
71
74
  loader: 'css',
72
75
  errors: [
@@ -84,7 +87,10 @@ function createSassPlugin(options) {
84
87
  };
85
88
  }
86
89
  exports.createSassPlugin = createSassPlugin;
87
- function sourceMapToUrlComment(sourceMap) {
90
+ function sourceMapToUrlComment(sourceMap, root) {
91
+ // Remove `file` protocol from all sourcemap sources and adjust to be relative to the input file.
92
+ // This allows esbuild to correctly process the paths.
93
+ sourceMap.sources = sourceMap.sources.map((source) => (0, node_path_1.relative)(root, (0, node_url_1.fileURLToPath)(source)));
88
94
  const urlSourceMap = Buffer.from(JSON.stringify(sourceMap), 'utf-8').toString('base64');
89
95
  return `/*# sourceMappingURL=data:application/json;charset=utf-8;base64,${urlSourceMap} */`;
90
96
  }
@@ -141,8 +141,8 @@
141
141
  },
142
142
  "optimization": {
143
143
  "description": "Enables optimization of the build output. Including minification of scripts and styles, tree-shaking, dead-code elimination, inlining of critical CSS and fonts inlining. For more information, see https://angular.io/guide/workspace-config#optimization-configuration.",
144
- "x-user-analytics": 16,
145
144
  "default": true,
145
+ "x-user-analytics": "ep.ng_optimization",
146
146
  "oneOf": [
147
147
  {
148
148
  "type": "object",
@@ -224,7 +224,7 @@
224
224
  "aot": {
225
225
  "type": "boolean",
226
226
  "description": "Build using Ahead of Time compilation.",
227
- "x-user-analytics": 13,
227
+ "x-user-analytics": "ep.ng_aot",
228
228
  "default": true
229
229
  },
230
230
  "sourceMap": {
@@ -12,6 +12,7 @@ import { Observable } from 'rxjs';
12
12
  import webpack from 'webpack';
13
13
  import { ExecutionTransformer } from '../../transforms';
14
14
  import { IndexHtmlTransform } from '../../utils/index-file/index-html-generator';
15
+ import { BuildEventStats } from '../../webpack/utils/stats';
15
16
  import { Schema } from './schema';
16
17
  export declare type DevServerBuilderOptions = Schema;
17
18
  /**
@@ -19,6 +20,7 @@ export declare type DevServerBuilderOptions = Schema;
19
20
  */
20
21
  export declare type DevServerBuilderOutput = DevServerBuildOutput & {
21
22
  baseUrl: string;
23
+ stats: BuildEventStats;
22
24
  };
23
25
  /**
24
26
  * Reusable implementation of the Angular Webpack development server builder.
@@ -132,12 +132,7 @@ function serveWebpackBrowser(options, context, transforms = {}) {
132
132
  ****************************************************************************************
133
133
  `);
134
134
  }
135
- const { config, projectRoot, i18n } = await (0, webpack_browser_config_1.generateI18nBrowserWebpackConfigFromContext)(browserOptions, context, (wco) => [
136
- (0, configs_1.getDevServerConfig)(wco),
137
- (0, configs_1.getCommonConfig)(wco),
138
- (0, configs_1.getStylesConfig)(wco),
139
- (0, configs_1.getAnalyticsConfig)(wco, context),
140
- ], options);
135
+ const { config, projectRoot, i18n } = await (0, webpack_browser_config_1.generateI18nBrowserWebpackConfigFromContext)(browserOptions, context, (wco) => [(0, configs_1.getDevServerConfig)(wco), (0, configs_1.getCommonConfig)(wco), (0, configs_1.getStylesConfig)(wco)], options);
141
136
  if (!config.devServer) {
142
137
  throw new Error('Webpack Dev Server configuration was not set.');
143
138
  }
@@ -207,6 +202,10 @@ function serveWebpackBrowser(options, context, transforms = {}) {
207
202
  webpackDevServerFactory: require('webpack-dev-server'),
208
203
  }).pipe((0, operators_1.concatMap)(async (buildEvent, index) => {
209
204
  var _a, _b;
205
+ const webpackRawStats = buildEvent.webpackStats;
206
+ if (!webpackRawStats) {
207
+ throw new Error('Webpack stats build result is required.');
208
+ }
210
209
  // Resolve serve address.
211
210
  const publicPath = (_b = (_a = webpackConfig.devServer) === null || _a === void 0 ? void 0 : _a.devMiddleware) === null || _b === void 0 ? void 0 : _b.publicPath;
212
211
  const serverAddress = url.format({
@@ -235,7 +234,11 @@ function serveWebpackBrowser(options, context, transforms = {}) {
235
234
  else {
236
235
  logger.info(`\n${color_1.colors.redBright(color_1.colors.symbols.cross)} Failed to compile.`);
237
236
  }
238
- return { ...buildEvent, baseUrl: serverAddress };
237
+ return {
238
+ ...buildEvent,
239
+ baseUrl: serverAddress,
240
+ stats: (0, stats_1.generateBuildEventStats)(webpackRawStats, browserOptions),
241
+ };
239
242
  }));
240
243
  }));
241
244
  }
@@ -117,6 +117,7 @@ async function findMatchingTests(pattern, workspaceRoot, projectSourceRoot) {
117
117
  root: projectSourceRoot,
118
118
  nomount: true,
119
119
  absolute: true,
120
+ ignore: ['**/node_modules/**'],
120
121
  });
121
122
  }
122
123
  async function isDirectory(path) {
@@ -36,8 +36,8 @@
36
36
  },
37
37
  "optimization": {
38
38
  "description": "Enables optimization of the build output. Including minification of scripts and styles, tree-shaking and dead-code elimination. For more information, see https://angular.io/guide/workspace-config#optimization-configuration.",
39
- "x-user-analytics": 16,
40
39
  "default": true,
40
+ "x-user-analytics": "ep.ng_optimization",
41
41
  "oneOf": [
42
42
  {
43
43
  "type": "object",
@@ -10,3 +10,4 @@ export declare const shouldBeautify: boolean;
10
10
  export declare const allowMinify: boolean;
11
11
  export declare const maxWorkers: number;
12
12
  export declare const useLegacySass: boolean;
13
+ export declare const debugPerformance: boolean;
@@ -7,7 +7,7 @@
7
7
  * found in the LICENSE file at https://angular.io/license
8
8
  */
9
9
  Object.defineProperty(exports, "__esModule", { value: true });
10
- exports.useLegacySass = exports.maxWorkers = exports.allowMinify = exports.shouldBeautify = exports.allowMangle = void 0;
10
+ exports.debugPerformance = exports.useLegacySass = exports.maxWorkers = exports.allowMinify = exports.shouldBeautify = exports.allowMangle = void 0;
11
11
  const color_1 = require("./color");
12
12
  function isDisabled(variable) {
13
13
  return variable === '0' || variable.toLowerCase() === 'false';
@@ -77,3 +77,5 @@ exports.useLegacySass = (() => {
77
77
  console.warn(color_1.colors.yellow(`Warning: 'NG_BUILD_LEGACY_SASS' environment variable support will be removed in version 16.`));
78
78
  return isEnabled(legacySassVariable);
79
79
  })();
80
+ const debugPerfVariable = process.env['NG_BUILD_DEBUG_PERF'];
81
+ exports.debugPerformance = isPresent(debugPerfVariable) && isEnabled(debugPerfVariable);
@@ -44,6 +44,7 @@ const load_esm_1 = require("../../utils/load-esm");
44
44
  const plugins_1 = require("../plugins");
45
45
  const devtools_ignore_plugin_1 = require("../plugins/devtools-ignore-plugin");
46
46
  const named_chunks_plugin_1 = require("../plugins/named-chunks-plugin");
47
+ const occurrences_plugin_1 = require("../plugins/occurrences-plugin");
47
48
  const progress_plugin_1 = require("../plugins/progress-plugin");
48
49
  const transfer_size_plugin_1 = require("../plugins/transfer-size-plugin");
49
50
  const typescript_1 = require("../plugins/typescript");
@@ -52,7 +53,7 @@ const helpers_1 = require("../utils/helpers");
52
53
  const VENDORS_TEST = /[\\/]node_modules[\\/]/;
53
54
  // eslint-disable-next-line max-lines-per-function
54
55
  async function getCommonConfig(wco) {
55
- var _a, _b;
56
+ var _a, _b, _c;
56
57
  const { root, projectRoot, buildOptions, tsConfig, projectName, sourceRoot, tsConfigPath } = wco;
57
58
  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, } = buildOptions;
58
59
  const isPlatformServer = buildOptions.platform === 'server';
@@ -68,13 +69,28 @@ async function getCommonConfig(wco) {
68
69
  if (buildOptions.progress) {
69
70
  extraPlugins.push(new progress_plugin_1.ProgressPlugin(platform));
70
71
  }
72
+ const localizePackageInitEntryPoint = '@angular/localize/init';
73
+ const hasLocalizeType = (_a = tsConfig.options.types) === null || _a === void 0 ? void 0 : _a.some((t) => t === '@angular/localize' || t === localizePackageInitEntryPoint);
74
+ if (hasLocalizeType) {
75
+ entryPoints['main'] = [localizePackageInitEntryPoint];
76
+ }
71
77
  if (buildOptions.main) {
72
78
  const mainPath = path.resolve(root, buildOptions.main);
73
- entryPoints['main'] = [mainPath];
79
+ if (Array.isArray(entryPoints['main'])) {
80
+ entryPoints['main'].push(mainPath);
81
+ }
82
+ else {
83
+ entryPoints['main'] = [mainPath];
84
+ }
74
85
  }
75
86
  if (isPlatformServer) {
76
87
  // Fixes Critical dependency: the request of a dependency is an expression
77
88
  extraPlugins.push(new webpack_2.ContextReplacementPlugin(/@?hapi|express[\\/]/));
89
+ if (Array.isArray(entryPoints['main'])) {
90
+ // This import must come before any imports (direct or transitive) that rely on DOM built-ins being
91
+ // available, such as `@angular/elements`.
92
+ entryPoints['main'].unshift('@angular/platform-server/init');
93
+ }
78
94
  }
79
95
  if (polyfills === null || polyfills === void 0 ? void 0 : polyfills.length) {
80
96
  // `zone.js/testing` is a **special** polyfill because when not imported in the main it fails with the below errors:
@@ -253,9 +269,9 @@ async function getCommonConfig(wco) {
253
269
  output: {
254
270
  uniqueName: projectName,
255
271
  hashFunction: 'xxhash64',
256
- clean: (_a = buildOptions.deleteOutputPath) !== null && _a !== void 0 ? _a : true,
272
+ clean: (_b = buildOptions.deleteOutputPath) !== null && _b !== void 0 ? _b : true,
257
273
  path: path.resolve(root, buildOptions.outputPath),
258
- publicPath: (_b = buildOptions.deployUrl) !== null && _b !== void 0 ? _b : '',
274
+ publicPath: (_c = buildOptions.deployUrl) !== null && _c !== void 0 ? _c : '',
259
275
  filename: `[name]${hashFormat.chunk}.js`,
260
276
  chunkFilename: `[name]${hashFormat.chunk}.js`,
261
277
  libraryTarget: isPlatformServer ? 'commonjs' : undefined,
@@ -385,7 +401,15 @@ async function getCommonConfig(wco) {
385
401
  },
386
402
  },
387
403
  },
388
- plugins: [new named_chunks_plugin_1.NamedChunksPlugin(), new plugins_1.DedupeModuleResolvePlugin({ verbose }), ...extraPlugins],
404
+ plugins: [
405
+ new named_chunks_plugin_1.NamedChunksPlugin(),
406
+ new occurrences_plugin_1.OccurrencesPlugin({
407
+ aot,
408
+ scriptsOptimization,
409
+ }),
410
+ new plugins_1.DedupeModuleResolvePlugin({ verbose }),
411
+ ...extraPlugins,
412
+ ],
389
413
  node: false,
390
414
  };
391
415
  }
@@ -5,7 +5,6 @@
5
5
  * Use of this source code is governed by an MIT-style license that can be
6
6
  * found in the LICENSE file at https://angular.io/license
7
7
  */
8
- export * from './analytics';
9
8
  export * from './common';
10
9
  export * from './dev-server';
11
10
  export * from './styles';