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

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 (36) hide show
  1. package/package.json +20 -17
  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 +127 -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/karma/index.d.ts +1 -1
  19. package/src/builders/karma/index.js +4 -5
  20. package/src/builders/server/schema.json +1 -1
  21. package/src/sass/sass-service.d.ts +12 -1
  22. package/src/sass/sass-service.js +19 -11
  23. package/src/utils/environment-options.d.ts +1 -0
  24. package/src/utils/environment-options.js +3 -1
  25. package/src/webpack/configs/common.js +29 -5
  26. package/src/webpack/configs/index.d.ts +0 -1
  27. package/src/webpack/configs/index.js +0 -1
  28. package/src/webpack/configs/styles.js +49 -25
  29. package/src/webpack/plugins/occurrences-plugin.d.ts +18 -0
  30. package/src/webpack/plugins/occurrences-plugin.js +79 -0
  31. package/src/webpack/utils/stats.d.ts +13 -8
  32. package/src/webpack/utils/stats.js +57 -6
  33. package/src/webpack/configs/analytics.d.ts +0 -11
  34. package/src/webpack/configs/analytics.js +0 -27
  35. package/src/webpack/plugins/analytics.d.ts +0 -66
  36. 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,46 @@ 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
+ const transformedData = await transformWithBabel(args.path, data, pluginOptions);
353
+ contents = Buffer.from(transformedData, 'utf-8');
354
+ babelDataCache.set(data, contents);
355
+ }
309
356
  return {
310
- contents: await transformWithBabelCached(args.path, (_c = typescriptResult.content) !== null && _c !== void 0 ? _c : '', pluginOptions, babelMemoryCache),
357
+ contents,
311
358
  loader: 'js',
312
359
  };
313
- });
314
- build.onLoad({ filter: /\.[cm]?js$/ }, async (args) => {
315
- const data = await fs_1.promises.readFile(args.path, 'utf-8');
360
+ }, true));
361
+ build.onLoad({ filter: /\.[cm]?js$/ }, (args) => (0, profiling_1.profileAsync)('NG_EMIT_JS*', async () => {
362
+ var _a, _b;
363
+ // The filename is currently used as a cache key. Since the cache is memory only,
364
+ // the options cannot change and do not need to be represented in the key. If the
365
+ // cache is later stored to disk, then the options that affect transform output
366
+ // would need to be added to the key as well as a check for any change of content.
367
+ let contents = (_a = pluginOptions.sourceFileCache) === null || _a === void 0 ? void 0 : _a.babelFileCache.get(args.path);
368
+ if (contents === undefined) {
369
+ const data = await fs_1.promises.readFile(args.path, 'utf-8');
370
+ const transformedData = await transformWithBabel(args.path, data, pluginOptions);
371
+ contents = Buffer.from(transformedData, 'utf-8');
372
+ (_b = pluginOptions.sourceFileCache) === null || _b === void 0 ? void 0 : _b.babelFileCache.set(args.path, contents);
373
+ }
316
374
  return {
317
- contents: await transformWithBabelCached(args.path, data, pluginOptions, babelMemoryCache),
375
+ contents,
318
376
  loader: 'js',
319
377
  };
320
- });
378
+ }, true));
321
379
  build.onEnd((result) => {
322
380
  var _a;
323
381
  if (stylesheetResourceFiles.length) {
324
382
  (_a = result.outputFiles) === null || _a === void 0 ? void 0 : _a.push(...stylesheetResourceFiles);
325
383
  }
384
+ (0, profiling_1.logCumulativeDurations)();
326
385
  });
327
386
  },
328
387
  };
@@ -388,25 +447,40 @@ async function transformWithBabel(filename, data, pluginOptions) {
388
447
  });
389
448
  return (_a = result === null || result === void 0 ? void 0 : result.code) !== null && _a !== void 0 ? _a : data;
390
449
  }
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);
450
+ function findAffectedFiles(builder, { ignoreForDiagnostics, ignoreForEmit, incrementalCompilation }) {
451
+ const affectedFiles = new Set();
452
+ // eslint-disable-next-line no-constant-condition
453
+ while (true) {
454
+ const result = builder.getSemanticDiagnosticsOfNextAffectedFile(undefined, (sourceFile) => {
455
+ // If the affected file is a TTC shim, add the shim's original source file.
456
+ // This ensures that changes that affect TTC are typechecked even when the changes
457
+ // are otherwise unrelated from a TS perspective and do not result in Ivy codegen changes.
458
+ // For example, changing @Input property types of a directive used in another component's
459
+ // template.
460
+ // A TTC shim is a file that has been ignored for diagnostics and has a filename ending in `.ngtypecheck.ts`.
461
+ if (ignoreForDiagnostics.has(sourceFile) && sourceFile.fileName.endsWith('.ngtypecheck.ts')) {
462
+ // This file name conversion relies on internal compiler logic and should be converted
463
+ // to an official method when available. 15 is length of `.ngtypecheck.ts`
464
+ const originalFilename = sourceFile.fileName.slice(0, -15) + '.ts';
465
+ const originalSourceFile = builder.getSourceFile(originalFilename);
466
+ if (originalSourceFile) {
467
+ affectedFiles.add(originalSourceFile);
468
+ }
469
+ return true;
470
+ }
471
+ return false;
472
+ });
473
+ if (!result) {
474
+ break;
475
+ }
476
+ affectedFiles.add(result.affected);
410
477
  }
411
- return result;
478
+ // A file is also affected if the Angular compiler requires it to be emitted
479
+ for (const sourceFile of builder.getSourceFiles()) {
480
+ if (ignoreForEmit.has(sourceFile) || incrementalCompilation.safeToSkipEmit(sourceFile)) {
481
+ continue;
482
+ }
483
+ affectedFiles.add(sourceFile);
484
+ }
485
+ return affectedFiles;
412
486
  }
@@ -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) {
@@ -6,7 +6,7 @@
6
6
  * found in the LICENSE file at https://angular.io/license
7
7
  */
8
8
  import { BuilderContext, BuilderOutput } from '@angular-devkit/architect';
9
- import { ConfigOptions } from 'karma';
9
+ import type { ConfigOptions } from 'karma';
10
10
  import { Observable } from 'rxjs';
11
11
  import { Configuration } from 'webpack';
12
12
  import { ExecutionTransformer } from '../../transforms';
@@ -33,7 +33,6 @@ Object.defineProperty(exports, "__esModule", { value: true });
33
33
  exports.execute = void 0;
34
34
  const architect_1 = require("@angular-devkit/architect");
35
35
  const core_1 = require("@angular-devkit/core");
36
- const karma_1 = require("karma");
37
36
  const module_1 = require("module");
38
37
  const path = __importStar(require("path"));
39
38
  const rxjs_1 = require("rxjs");
@@ -91,7 +90,7 @@ function execute(options, context, transforms = {}) {
91
90
  }
92
91
  const karmaOptions = options.karmaConfig
93
92
  ? {}
94
- : getBuiltInKarmaConfig(context.workspaceRoot, projectName);
93
+ : getBuiltInKarmaConfig(karma, context.workspaceRoot, projectName);
95
94
  karmaOptions.singleRun = singleRun;
96
95
  // Convert browsers from a string to an array
97
96
  if (options.browsers) {
@@ -130,7 +129,7 @@ function execute(options, context, transforms = {}) {
130
129
  webpackConfig,
131
130
  logger: context.logger,
132
131
  };
133
- const parsedKarmaConfig = await karma_1.config.parseConfig(options.karmaConfig && path.resolve(context.workspaceRoot, options.karmaConfig), transforms.karmaOptions ? transforms.karmaOptions(karmaOptions) : karmaOptions, { promiseConfig: true, throwErrors: true });
132
+ const parsedKarmaConfig = await karma.config.parseConfig(options.karmaConfig && path.resolve(context.workspaceRoot, options.karmaConfig), transforms.karmaOptions ? transforms.karmaOptions(karmaOptions) : karmaOptions, { promiseConfig: true, throwErrors: true });
134
133
  return [karma, parsedKarmaConfig];
135
134
  }), (0, operators_1.switchMap)(([karma, karmaConfig]) => new rxjs_1.Observable((subscriber) => {
136
135
  var _a, _b, _c;
@@ -154,7 +153,7 @@ function execute(options, context, transforms = {}) {
154
153
  })), (0, operators_1.defaultIfEmpty)({ success: false }));
155
154
  }
156
155
  exports.execute = execute;
157
- function getBuiltInKarmaConfig(workspaceRoot, projectName) {
156
+ function getBuiltInKarmaConfig(karma, workspaceRoot, projectName) {
158
157
  let coverageFolderName = projectName.charAt(0) === '@' ? projectName.slice(1) : projectName;
159
158
  if (/[A-Z]/.test(coverageFolderName)) {
160
159
  coverageFolderName = core_1.strings.dasherize(coverageFolderName);
@@ -184,7 +183,7 @@ function getBuiltInKarmaConfig(workspaceRoot, projectName) {
184
183
  reporters: ['progress', 'kjhtml'],
185
184
  port: 9876,
186
185
  colors: true,
187
- logLevel: karma_1.constants.LOG_INFO,
186
+ logLevel: karma.constants.LOG_INFO,
188
187
  autoWatch: true,
189
188
  browsers: ['Chrome'],
190
189
  restartOnFileChange: true,
@@ -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",
@@ -5,7 +5,17 @@
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
- import { CompileResult, StringOptionsWithImporter, StringOptionsWithoutImporter } from 'sass';
8
+ import { CompileResult, FileImporter, StringOptionsWithImporter, StringOptionsWithoutImporter } from 'sass';
9
+ declare type FileImporterOptions = Parameters<FileImporter['findFileUrl']>[1];
10
+ export interface FileImporterWithRequestContextOptions extends FileImporterOptions {
11
+ /**
12
+ * This is a custom option and is required as SASS does not provide context from which the file is being resolved.
13
+ * This breaks Yarn PNP as transitive deps cannot be resolved from the workspace root.
14
+ *
15
+ * Workaround until https://github.com/sass/sass/issues/3247 is addressed.
16
+ */
17
+ previousResolvedModules?: Set<string>;
18
+ }
9
19
  /**
10
20
  * A Sass renderer implementation that provides an interface that can be used by Webpack's
11
21
  * `sass-loader`. The implementation uses a Worker thread to perform the Sass rendering
@@ -45,3 +55,4 @@ export declare class SassWorkerImplementation {
45
55
  private createRequest;
46
56
  private isImporter;
47
57
  }
58
+ export {};