@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
@@ -21,7 +21,6 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
21
21
  for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
22
22
  };
23
23
  Object.defineProperty(exports, "__esModule", { value: true });
24
- __exportStar(require("./analytics"), exports);
25
24
  __exportStar(require("./common"), exports);
26
25
  __exportStar(require("./dev-server"), exports);
27
26
  __exportStar(require("./styles"), exports);
@@ -370,11 +370,28 @@ function getSassResolutionImporter(loaderContext, root, preserveSymlinks) {
370
370
  mainFiles: ['_index', 'index', '...'],
371
371
  });
372
372
  return {
373
- findFileUrl: (url, { fromImport }) => {
373
+ findFileUrl: async (url, { fromImport }) => {
374
+ if (url.charAt(0) === '.') {
375
+ // Let Sass handle relative imports.
376
+ return null;
377
+ }
378
+ let file;
374
379
  const resolve = fromImport ? resolveImport : resolveModule;
375
- return resolve(root, url)
376
- .then((file) => (0, url_1.pathToFileURL)(file))
377
- .catch(() => null);
380
+ try {
381
+ file = await resolve(root, url);
382
+ }
383
+ catch {
384
+ // Try to resolve a partial file
385
+ // @use '@material/button/button' as mdc-button;
386
+ // `@material/button/button` -> `@material/button/_button`
387
+ const lastSlashIndex = url.lastIndexOf('/');
388
+ const underscoreIndex = lastSlashIndex + 1;
389
+ if (underscoreIndex > 0 && url.charAt(underscoreIndex) !== '_') {
390
+ const partialFileUrl = `${url.slice(0, underscoreIndex)}_${url.slice(underscoreIndex)}`;
391
+ file = await resolve(root, partialFileUrl).catch(() => undefined);
392
+ }
393
+ }
394
+ return file ? (0, url_1.pathToFileURL)(file) : null;
378
395
  },
379
396
  };
380
397
  }
@@ -0,0 +1,18 @@
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 interface OccurrencesPluginOptions {
10
+ aot?: boolean;
11
+ scriptsOptimization?: boolean;
12
+ }
13
+ export declare class OccurrencesPlugin {
14
+ private options;
15
+ constructor(options: OccurrencesPluginOptions);
16
+ apply(compiler: Compiler): void;
17
+ private countOccurrences;
18
+ }
@@ -0,0 +1,79 @@
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.OccurrencesPlugin = void 0;
11
+ const PLUGIN_NAME = 'angular-occurrences-plugin';
12
+ class OccurrencesPlugin {
13
+ constructor(options) {
14
+ this.options = options;
15
+ }
16
+ apply(compiler) {
17
+ compiler.hooks.thisCompilation.tap(PLUGIN_NAME, (compilation) => {
18
+ compilation.hooks.processAssets.tapPromise({
19
+ name: PLUGIN_NAME,
20
+ stage: compiler.webpack.Compilation.PROCESS_ASSETS_STAGE_ANALYSE,
21
+ }, async (compilationAssets) => {
22
+ for (const assetName of Object.keys(compilationAssets)) {
23
+ if (!assetName.endsWith('.js')) {
24
+ continue;
25
+ }
26
+ const scriptAsset = compilation.getAsset(assetName);
27
+ if (!scriptAsset || scriptAsset.source.size() <= 0) {
28
+ continue;
29
+ }
30
+ const src = scriptAsset.source.source().toString('utf-8');
31
+ let ngComponentCount = 0;
32
+ if (!this.options.aot) {
33
+ // Count the number of `Component({` strings (case sensitive), which happens in __decorate().
34
+ ngComponentCount += this.countOccurrences(src, 'Component({');
35
+ }
36
+ if (this.options.scriptsOptimization) {
37
+ // for ascii_only true
38
+ ngComponentCount += this.countOccurrences(src, '.\\u0275cmp', false);
39
+ }
40
+ else {
41
+ // For Ivy we just count ɵcmp.src
42
+ ngComponentCount += this.countOccurrences(src, '.ɵcmp', true);
43
+ }
44
+ compilation.updateAsset(assetName, (s) => s, (assetInfo) => ({
45
+ ...assetInfo,
46
+ ngComponentCount,
47
+ }));
48
+ }
49
+ });
50
+ });
51
+ }
52
+ countOccurrences(source, match, wordBreak = false) {
53
+ let count = 0;
54
+ // We condition here so branch prediction happens out of the loop, not in it.
55
+ if (wordBreak) {
56
+ const re = /\w/;
57
+ for (let pos = source.lastIndexOf(match); pos >= 0; pos = source.lastIndexOf(match, pos)) {
58
+ if (!(re.test(source[pos - 1] || '') || re.test(source[pos + match.length] || ''))) {
59
+ count++; // 1 match, AH! AH! AH! 2 matches, AH! AH! AH!
60
+ }
61
+ pos -= match.length;
62
+ if (pos < 0) {
63
+ break;
64
+ }
65
+ }
66
+ }
67
+ else {
68
+ for (let pos = source.lastIndexOf(match); pos >= 0; pos = source.lastIndexOf(match, pos)) {
69
+ count++; // 1 match, AH! AH! AH! 2 matches, AH! AH! AH!
70
+ pos -= match.length;
71
+ if (pos < 0) {
72
+ break;
73
+ }
74
+ }
75
+ }
76
+ return count;
77
+ }
78
+ }
79
+ exports.OccurrencesPlugin = OccurrencesPlugin;
@@ -22,17 +22,22 @@ export interface BundleStats {
22
22
  initial: boolean;
23
23
  stats: BundleStatsData;
24
24
  }
25
- export declare function generateBundleStats(info: {
26
- rawSize?: number;
27
- estimatedTransferSize?: number;
28
- files?: string[];
29
- names?: string[];
30
- initial?: boolean;
31
- rendered?: boolean;
32
- }): BundleStats;
33
25
  export declare function statsWarningsToString(json: StatsCompilation, statsConfig: WebpackStatsOptions): string;
34
26
  export declare function statsErrorsToString(json: StatsCompilation, statsConfig: WebpackStatsOptions): string;
35
27
  export declare function statsHasErrors(json: StatsCompilation): boolean;
36
28
  export declare function statsHasWarnings(json: StatsCompilation): boolean;
37
29
  export declare function createWebpackLoggingCallback(options: BrowserBuilderOptions, logger: logging.LoggerApi): WebpackLoggingCallback;
30
+ export interface BuildEventStats {
31
+ aot: boolean;
32
+ optimization: boolean;
33
+ allChunksCount: number;
34
+ lazyChunksCount: number;
35
+ initialChunksCount: number;
36
+ changedChunksCount?: number;
37
+ durationInMs: number;
38
+ cssSizeInBytes: number;
39
+ jsSizeInBytes: number;
40
+ ngComponentCount: number;
41
+ }
42
+ export declare function generateBuildEventStats(webpackStats: StatsCompilation, browserBuilderOptions: BrowserBuilderOptions): BuildEventStats;
38
43
  export declare function webpackStatsLogger(logger: logging.LoggerApi, json: StatsCompilation, config: Configuration, budgetFailures?: BudgetCalculatorResult[]): void;
@@ -33,10 +33,12 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
33
33
  return (mod && mod.__esModule) ? mod : { "default": mod };
34
34
  };
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
- exports.webpackStatsLogger = exports.createWebpackLoggingCallback = exports.statsHasWarnings = exports.statsHasErrors = exports.statsErrorsToString = exports.statsWarningsToString = exports.generateBundleStats = exports.formatSize = void 0;
36
+ exports.webpackStatsLogger = exports.generateBuildEventStats = exports.createWebpackLoggingCallback = exports.statsHasWarnings = exports.statsHasErrors = exports.statsErrorsToString = exports.statsWarningsToString = exports.formatSize = void 0;
37
37
  const core_1 = require("@angular-devkit/core");
38
+ const assert_1 = __importDefault(require("assert"));
38
39
  const path = __importStar(require("path"));
39
40
  const text_table_1 = __importDefault(require("text-table"));
41
+ const utils_1 = require("../../utils");
40
42
  const color_1 = require("../../utils/color");
41
43
  const async_chunks_1 = require("./async-chunks");
42
44
  const helpers_1 = require("./helpers");
@@ -52,6 +54,11 @@ function formatSize(size) {
52
54
  return `${roundedSize.toFixed(fractionDigits)} ${abbreviations[index]}`;
53
55
  }
54
56
  exports.formatSize = formatSize;
57
+ function getBuildDuration(webpackStats) {
58
+ (0, assert_1.default)(webpackStats.builtAt, 'buildAt cannot be undefined');
59
+ (0, assert_1.default)(webpackStats.time, 'time cannot be undefined');
60
+ return Date.now() - webpackStats.builtAt + webpackStats.time;
61
+ }
55
62
  function generateBundleStats(info) {
56
63
  var _a, _b, _c;
57
64
  const rawSize = typeof info.rawSize === 'number' ? info.rawSize : '-';
@@ -64,7 +71,6 @@ function generateBundleStats(info) {
64
71
  stats: [files, names, rawSize, estimatedTransferSize],
65
72
  };
66
73
  }
67
- exports.generateBundleStats = generateBundleStats;
68
74
  function generateBuildStatsTable(data, colors, showTotalSize, showEstimatedTransferSize, budgetFailures) {
69
75
  const g = (x) => (colors ? color_1.colors.greenBright(x) : x);
70
76
  const c = (x) => (colors ? color_1.colors.cyanBright(x) : x);
@@ -238,10 +244,7 @@ statsConfig, budgetFailures) {
238
244
  // In some cases we do things outside of webpack context
239
245
  // Such us index generation, service worker augmentation etc...
240
246
  // This will correct the time and include these.
241
- let time = 0;
242
- if (json.builtAt !== undefined && json.time !== undefined) {
243
- time = Date.now() - json.builtAt + json.time;
244
- }
247
+ const time = getBuildDuration(json);
245
248
  if (unchangedChunkNumber > 0) {
246
249
  return ('\n' +
247
250
  rs(core_1.tags.stripIndents `
@@ -370,6 +373,54 @@ function createWebpackLoggingCallback(options, logger) {
370
373
  };
371
374
  }
372
375
  exports.createWebpackLoggingCallback = createWebpackLoggingCallback;
376
+ function generateBuildEventStats(webpackStats, browserBuilderOptions) {
377
+ var _a, _b;
378
+ const { chunks = [], assets = [] } = webpackStats;
379
+ let jsSizeInBytes = 0;
380
+ let cssSizeInBytes = 0;
381
+ let initialChunksCount = 0;
382
+ let ngComponentCount = 0;
383
+ let changedChunksCount = 0;
384
+ const allChunksCount = chunks.length;
385
+ const isFirstRun = !runsCache.has(webpackStats.outputPath || '');
386
+ const chunkFiles = new Set();
387
+ for (const chunk of chunks) {
388
+ if (!isFirstRun && chunk.rendered) {
389
+ changedChunksCount++;
390
+ }
391
+ if (chunk.initial) {
392
+ initialChunksCount++;
393
+ }
394
+ for (const file of (_a = chunk.files) !== null && _a !== void 0 ? _a : []) {
395
+ chunkFiles.add(file);
396
+ }
397
+ }
398
+ for (const asset of assets) {
399
+ if (asset.name.endsWith('.map') || !chunkFiles.has(asset.name)) {
400
+ continue;
401
+ }
402
+ if (asset.name.endsWith('.js')) {
403
+ jsSizeInBytes += asset.size;
404
+ ngComponentCount += (_b = asset.info.ngComponentCount) !== null && _b !== void 0 ? _b : 0;
405
+ }
406
+ else if (asset.name.endsWith('.css')) {
407
+ cssSizeInBytes += asset.size;
408
+ }
409
+ }
410
+ return {
411
+ optimization: !!(0, utils_1.normalizeOptimization)(browserBuilderOptions.optimization).scripts,
412
+ aot: browserBuilderOptions.aot !== false,
413
+ allChunksCount,
414
+ lazyChunksCount: allChunksCount - initialChunksCount,
415
+ initialChunksCount,
416
+ changedChunksCount,
417
+ durationInMs: getBuildDuration(webpackStats),
418
+ cssSizeInBytes,
419
+ jsSizeInBytes,
420
+ ngComponentCount,
421
+ };
422
+ }
423
+ exports.generateBuildEventStats = generateBuildEventStats;
373
424
  function webpackStatsLogger(logger, json, config, budgetFailures) {
374
425
  logger.info(statsToString(json, config.stats, budgetFailures));
375
426
  if (typeof config.stats !== 'object') {
@@ -1,11 +0,0 @@
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 { BuilderContext } from '@angular-devkit/architect';
9
- import { Configuration } from 'webpack';
10
- import { WebpackConfigOptions } from '../../utils/build-options';
11
- export declare function getAnalyticsConfig(wco: WebpackConfigOptions, context: BuilderContext): Configuration;
@@ -1,27 +0,0 @@
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.getAnalyticsConfig = void 0;
11
- const analytics_1 = require("../plugins/analytics");
12
- function getAnalyticsConfig(wco, context) {
13
- if (!context.analytics) {
14
- return {};
15
- }
16
- // If there's analytics, add our plugin. Otherwise no need to slow down the build.
17
- let category = 'build';
18
- if (context.builder) {
19
- // We already vetted that this is a "safe" package, otherwise the analytics would be noop.
20
- category = context.builder.builderName.split(':')[1] || context.builder.builderName || 'build';
21
- }
22
- // The category is the builder name if it's an angular builder.
23
- return {
24
- plugins: [new analytics_1.NgBuildAnalyticsPlugin(wco.projectRoot, context.analytics, category)],
25
- };
26
- }
27
- exports.getAnalyticsConfig = getAnalyticsConfig;
@@ -1,66 +0,0 @@
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 { analytics } from '@angular-devkit/core';
9
- import { Compilation, Compiler, Module, NormalModule, Stats } from 'webpack';
10
- /**
11
- * Faster than using a RegExp, so we use this to count occurences in source code.
12
- * @param source The source to look into.
13
- * @param match The match string to look for.
14
- * @param wordBreak Whether to check for word break before and after a match was found.
15
- * @return The number of matches found.
16
- * @private
17
- */
18
- export declare function countOccurrences(source: string, match: string, wordBreak?: boolean): number;
19
- /**
20
- * Holder of statistics related to the build.
21
- */
22
- declare class AnalyticsBuildStats {
23
- errors: string[];
24
- numberOfNgOnInit: number;
25
- numberOfComponents: number;
26
- initialChunkSize: number;
27
- totalChunkCount: number;
28
- totalChunkSize: number;
29
- lazyChunkCount: number;
30
- lazyChunkSize: number;
31
- assetCount: number;
32
- assetSize: number;
33
- polyfillSize: number;
34
- cssSize: number;
35
- }
36
- /**
37
- * Analytics plugin that reports the analytics we want from the CLI.
38
- */
39
- export declare class NgBuildAnalyticsPlugin {
40
- protected _projectRoot: string;
41
- protected _analytics: analytics.Analytics;
42
- protected _category: string;
43
- protected _built: boolean;
44
- protected _stats: AnalyticsBuildStats;
45
- constructor(_projectRoot: string, _analytics: analytics.Analytics, _category: string);
46
- protected _reset(): void;
47
- protected _getMetrics(stats: Stats): (string | number)[];
48
- protected _getDimensions(): (string | number | boolean)[];
49
- protected _reportBuildMetrics(stats: Stats): void;
50
- protected _reportRebuildMetrics(stats: Stats): void;
51
- protected _checkTsNormalModule(module: NormalModule): void;
52
- protected _collectErrors(stats: Stats): void;
53
- protected _collectBundleStats(compilation: Compilation): void;
54
- /** **********************************************************************************************
55
- * The next section is all the different Webpack hooks for this plugin.
56
- */
57
- /**
58
- * Reports a succeed module.
59
- * @private
60
- */
61
- protected _succeedModule(module: Module): void;
62
- protected _compilation(compiler: Compiler, compilation: Compilation): void;
63
- protected _done(stats: Stats): void;
64
- apply(compiler: Compiler): void;
65
- }
66
- export {};
@@ -1,236 +0,0 @@
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.NgBuildAnalyticsPlugin = exports.countOccurrences = void 0;
11
- const core_1 = require("@angular-devkit/core");
12
- const webpack_1 = require("webpack");
13
- const webpackAllErrorMessageRe = /^([^(]+)\(\d+,\d\): (.*)$/gm;
14
- const webpackTsErrorMessageRe = /^[^(]+\(\d+,\d\): error (TS\d+):/;
15
- /**
16
- * Faster than using a RegExp, so we use this to count occurences in source code.
17
- * @param source The source to look into.
18
- * @param match The match string to look for.
19
- * @param wordBreak Whether to check for word break before and after a match was found.
20
- * @return The number of matches found.
21
- * @private
22
- */
23
- function countOccurrences(source, match, wordBreak = false) {
24
- if (match.length == 0) {
25
- return source.length + 1;
26
- }
27
- let count = 0;
28
- // We condition here so branch prediction happens out of the loop, not in it.
29
- if (wordBreak) {
30
- const re = /\w/;
31
- for (let pos = source.lastIndexOf(match); pos >= 0; pos = source.lastIndexOf(match, pos)) {
32
- if (!(re.test(source[pos - 1] || '') || re.test(source[pos + match.length] || ''))) {
33
- count++; // 1 match, AH! AH! AH! 2 matches, AH! AH! AH!
34
- }
35
- pos -= match.length;
36
- if (pos < 0) {
37
- break;
38
- }
39
- }
40
- }
41
- else {
42
- for (let pos = source.lastIndexOf(match); pos >= 0; pos = source.lastIndexOf(match, pos)) {
43
- count++; // 1 match, AH! AH! AH! 2 matches, AH! AH! AH!
44
- pos -= match.length;
45
- if (pos < 0) {
46
- break;
47
- }
48
- }
49
- }
50
- return count;
51
- }
52
- exports.countOccurrences = countOccurrences;
53
- /**
54
- * Holder of statistics related to the build.
55
- */
56
- class AnalyticsBuildStats {
57
- constructor() {
58
- this.errors = [];
59
- this.numberOfNgOnInit = 0;
60
- this.numberOfComponents = 0;
61
- this.initialChunkSize = 0;
62
- this.totalChunkCount = 0;
63
- this.totalChunkSize = 0;
64
- this.lazyChunkCount = 0;
65
- this.lazyChunkSize = 0;
66
- this.assetCount = 0;
67
- this.assetSize = 0;
68
- this.polyfillSize = 0;
69
- this.cssSize = 0;
70
- }
71
- }
72
- /**
73
- * Analytics plugin that reports the analytics we want from the CLI.
74
- */
75
- class NgBuildAnalyticsPlugin {
76
- constructor(_projectRoot, _analytics, _category) {
77
- this._projectRoot = _projectRoot;
78
- this._analytics = _analytics;
79
- this._category = _category;
80
- this._built = false;
81
- this._stats = new AnalyticsBuildStats();
82
- }
83
- _reset() {
84
- this._stats = new AnalyticsBuildStats();
85
- }
86
- _getMetrics(stats) {
87
- const startTime = +(stats.startTime || 0);
88
- const endTime = +(stats.endTime || 0);
89
- const metrics = [];
90
- metrics[core_1.analytics.NgCliAnalyticsMetrics.BuildTime] = endTime - startTime;
91
- metrics[core_1.analytics.NgCliAnalyticsMetrics.NgOnInitCount] = this._stats.numberOfNgOnInit;
92
- metrics[core_1.analytics.NgCliAnalyticsMetrics.NgComponentCount] = this._stats.numberOfComponents;
93
- metrics[core_1.analytics.NgCliAnalyticsMetrics.InitialChunkSize] = this._stats.initialChunkSize;
94
- metrics[core_1.analytics.NgCliAnalyticsMetrics.TotalChunkCount] = this._stats.totalChunkCount;
95
- metrics[core_1.analytics.NgCliAnalyticsMetrics.TotalChunkSize] = this._stats.totalChunkSize;
96
- metrics[core_1.analytics.NgCliAnalyticsMetrics.LazyChunkCount] = this._stats.lazyChunkCount;
97
- metrics[core_1.analytics.NgCliAnalyticsMetrics.LazyChunkSize] = this._stats.lazyChunkSize;
98
- metrics[core_1.analytics.NgCliAnalyticsMetrics.AssetCount] = this._stats.assetCount;
99
- metrics[core_1.analytics.NgCliAnalyticsMetrics.AssetSize] = this._stats.assetSize;
100
- metrics[core_1.analytics.NgCliAnalyticsMetrics.PolyfillSize] = this._stats.polyfillSize;
101
- metrics[core_1.analytics.NgCliAnalyticsMetrics.CssSize] = this._stats.cssSize;
102
- return metrics;
103
- }
104
- _getDimensions() {
105
- const dimensions = [];
106
- if (this._stats.errors.length) {
107
- // Adding commas before and after so the regex are easier to define filters.
108
- dimensions[core_1.analytics.NgCliAnalyticsDimensions.BuildErrors] = `,${this._stats.errors.join()},`;
109
- }
110
- return dimensions;
111
- }
112
- _reportBuildMetrics(stats) {
113
- const dimensions = this._getDimensions();
114
- const metrics = this._getMetrics(stats);
115
- this._analytics.event(this._category, 'build', { dimensions, metrics });
116
- }
117
- _reportRebuildMetrics(stats) {
118
- const dimensions = this._getDimensions();
119
- const metrics = this._getMetrics(stats);
120
- this._analytics.event(this._category, 'rebuild', { dimensions, metrics });
121
- }
122
- _checkTsNormalModule(module) {
123
- const originalSource = module.originalSource();
124
- if (!originalSource) {
125
- return;
126
- }
127
- const originalContent = originalSource.source().toString();
128
- // PLEASE REMEMBER:
129
- // We're dealing with ES5 _or_ ES2015 JavaScript at this point (we don't know for sure).
130
- // Just count the ngOnInit occurences. Comments/Strings/calls occurences should be sparse
131
- // so we just consider them within the margin of error. We do break on word break though.
132
- this._stats.numberOfNgOnInit += countOccurrences(originalContent, 'ngOnInit', true);
133
- // Count the number of `Component({` strings (case sensitive), which happens in __decorate().
134
- this._stats.numberOfComponents += countOccurrences(originalContent, 'Component({');
135
- // For Ivy we just count ɵcmp.
136
- this._stats.numberOfComponents += countOccurrences(originalContent, '.ɵcmp', true);
137
- // for ascii_only true
138
- this._stats.numberOfComponents += countOccurrences(originalContent, '.\u0275cmp', true);
139
- }
140
- _collectErrors(stats) {
141
- if (stats.hasErrors()) {
142
- for (const errObject of stats.compilation.errors) {
143
- if (errObject instanceof Error) {
144
- const allErrors = errObject.message.match(webpackAllErrorMessageRe);
145
- for (const err of [...(allErrors || [])].slice(1)) {
146
- const message = (err.match(webpackTsErrorMessageRe) || [])[1];
147
- if (message) {
148
- // At this point this should be a TS1234.
149
- this._stats.errors.push(message);
150
- }
151
- }
152
- }
153
- }
154
- }
155
- }
156
- _collectBundleStats(compilation) {
157
- var _a, _b;
158
- const chunkAssets = new Set();
159
- for (const chunk of compilation.chunks) {
160
- if (!chunk.rendered || chunk.files.size === 0) {
161
- continue;
162
- }
163
- const firstFile = Array.from(chunk.files)[0];
164
- const size = (_b = (_a = compilation.getAsset(firstFile)) === null || _a === void 0 ? void 0 : _a.source.size()) !== null && _b !== void 0 ? _b : 0;
165
- chunkAssets.add(firstFile);
166
- if (chunk.canBeInitial()) {
167
- this._stats.initialChunkSize += size;
168
- }
169
- else {
170
- this._stats.lazyChunkCount++;
171
- this._stats.lazyChunkSize += size;
172
- }
173
- this._stats.totalChunkCount++;
174
- this._stats.totalChunkSize += size;
175
- if (firstFile.endsWith('.css')) {
176
- this._stats.cssSize += size;
177
- }
178
- }
179
- for (const asset of compilation.getAssets()) {
180
- // Only count non-JavaScript related files
181
- if (chunkAssets.has(asset.name)) {
182
- continue;
183
- }
184
- this._stats.assetSize += asset.source.size();
185
- this._stats.assetCount++;
186
- if (asset.name == 'polyfill') {
187
- this._stats.polyfillSize += asset.source.size();
188
- }
189
- }
190
- }
191
- /** **********************************************************************************************
192
- * The next section is all the different Webpack hooks for this plugin.
193
- */
194
- /**
195
- * Reports a succeed module.
196
- * @private
197
- */
198
- _succeedModule(module) {
199
- // Only report NormalModule instances.
200
- if (!(module instanceof webpack_1.NormalModule)) {
201
- return;
202
- }
203
- // Only reports modules that are part of the user's project. We also don't do node_modules.
204
- // There is a chance that someone name a file path `hello_node_modules` or something and we
205
- // will ignore that file for the purpose of gathering, but we're willing to take the risk.
206
- if (!module.resource ||
207
- !module.resource.startsWith(this._projectRoot) ||
208
- module.resource.indexOf('node_modules') >= 0) {
209
- return;
210
- }
211
- // Check that it's a source file from the project.
212
- if (module.resource.endsWith('.ts')) {
213
- this._checkTsNormalModule(module);
214
- }
215
- }
216
- _compilation(compiler, compilation) {
217
- this._reset();
218
- compilation.hooks.succeedModule.tap('NgBuildAnalyticsPlugin', this._succeedModule.bind(this));
219
- }
220
- _done(stats) {
221
- this._collectErrors(stats);
222
- this._collectBundleStats(stats.compilation);
223
- if (this._built) {
224
- this._reportRebuildMetrics(stats);
225
- }
226
- else {
227
- this._reportBuildMetrics(stats);
228
- this._built = true;
229
- }
230
- }
231
- apply(compiler) {
232
- compiler.hooks.compilation.tap('NgBuildAnalyticsPlugin', this._compilation.bind(this, compiler));
233
- compiler.hooks.done.tap('NgBuildAnalyticsPlugin', this._done.bind(this));
234
- }
235
- }
236
- exports.NgBuildAnalyticsPlugin = NgBuildAnalyticsPlugin;