source: trip-planner-front/node_modules/@angular-devkit/build-angular/src/webpack/plugins/analytics.js@ 6a3a178

Last change on this file since 6a3a178 was 6a3a178, checked in by Ema <ema_spirova@…>, 3 years ago

initial commit

  • Property mode set to 100644
File size: 9.8 KB
Line 
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 */
9Object.defineProperty(exports, "__esModule", { value: true });
10exports.NgBuildAnalyticsPlugin = exports.countOccurrences = void 0;
11const core_1 = require("@angular-devkit/core");
12const webpack_1 = require("webpack");
13const webpackAllErrorMessageRe = /^([^(]+)\(\d+,\d\): (.*)$/gm;
14const 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 */
23function 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}
52exports.countOccurrences = countOccurrences;
53/**
54 * Holder of statistics related to the build.
55 */
56class 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 */
75class NgBuildAnalyticsPlugin {
76 constructor(_projectRoot, _analytics, _category, _isIvy) {
77 this._projectRoot = _projectRoot;
78 this._analytics = _analytics;
79 this._category = _category;
80 this._isIvy = _isIvy;
81 this._built = false;
82 this._stats = new AnalyticsBuildStats();
83 }
84 _reset() {
85 this._stats = new AnalyticsBuildStats();
86 }
87 _getMetrics(stats) {
88 const startTime = +(stats.startTime || 0);
89 const endTime = +(stats.endTime || 0);
90 const metrics = [];
91 metrics[core_1.analytics.NgCliAnalyticsMetrics.BuildTime] = endTime - startTime;
92 metrics[core_1.analytics.NgCliAnalyticsMetrics.NgOnInitCount] = this._stats.numberOfNgOnInit;
93 metrics[core_1.analytics.NgCliAnalyticsMetrics.NgComponentCount] = this._stats.numberOfComponents;
94 metrics[core_1.analytics.NgCliAnalyticsMetrics.InitialChunkSize] = this._stats.initialChunkSize;
95 metrics[core_1.analytics.NgCliAnalyticsMetrics.TotalChunkCount] = this._stats.totalChunkCount;
96 metrics[core_1.analytics.NgCliAnalyticsMetrics.TotalChunkSize] = this._stats.totalChunkSize;
97 metrics[core_1.analytics.NgCliAnalyticsMetrics.LazyChunkCount] = this._stats.lazyChunkCount;
98 metrics[core_1.analytics.NgCliAnalyticsMetrics.LazyChunkSize] = this._stats.lazyChunkSize;
99 metrics[core_1.analytics.NgCliAnalyticsMetrics.AssetCount] = this._stats.assetCount;
100 metrics[core_1.analytics.NgCliAnalyticsMetrics.AssetSize] = this._stats.assetSize;
101 metrics[core_1.analytics.NgCliAnalyticsMetrics.PolyfillSize] = this._stats.polyfillSize;
102 metrics[core_1.analytics.NgCliAnalyticsMetrics.CssSize] = this._stats.cssSize;
103 return metrics;
104 }
105 _getDimensions() {
106 const dimensions = [];
107 if (this._stats.errors.length) {
108 // Adding commas before and after so the regex are easier to define filters.
109 dimensions[core_1.analytics.NgCliAnalyticsDimensions.BuildErrors] = `,${this._stats.errors.join()},`;
110 }
111 dimensions[core_1.analytics.NgCliAnalyticsDimensions.NgIvyEnabled] = this._isIvy;
112 return dimensions;
113 }
114 _reportBuildMetrics(stats) {
115 const dimensions = this._getDimensions();
116 const metrics = this._getMetrics(stats);
117 this._analytics.event(this._category, 'build', { dimensions, metrics });
118 }
119 _reportRebuildMetrics(stats) {
120 const dimensions = this._getDimensions();
121 const metrics = this._getMetrics(stats);
122 this._analytics.event(this._category, 'rebuild', { dimensions, metrics });
123 }
124 _checkTsNormalModule(module) {
125 const originalSource = module.originalSource();
126 if (!originalSource) {
127 return;
128 }
129 const originalContent = originalSource.source().toString();
130 // PLEASE REMEMBER:
131 // We're dealing with ES5 _or_ ES2015 JavaScript at this point (we don't know for sure).
132 // Just count the ngOnInit occurences. Comments/Strings/calls occurences should be sparse
133 // so we just consider them within the margin of error. We do break on word break though.
134 this._stats.numberOfNgOnInit += countOccurrences(originalContent, 'ngOnInit', true);
135 // Count the number of `Component({` strings (case sensitive), which happens in __decorate().
136 this._stats.numberOfComponents += countOccurrences(originalContent, 'Component({');
137 // For Ivy we just count ɵcmp.
138 this._stats.numberOfComponents += countOccurrences(originalContent, '.ɵcmp', true);
139 // for ascii_only true
140 this._stats.numberOfComponents += countOccurrences(originalContent, '.\u0275cmp', true);
141 }
142 _collectErrors(stats) {
143 if (stats.hasErrors()) {
144 for (const errObject of stats.compilation.errors) {
145 if (errObject instanceof Error) {
146 const allErrors = errObject.message.match(webpackAllErrorMessageRe);
147 for (const err of [...(allErrors || [])].slice(1)) {
148 const message = (err.match(webpackTsErrorMessageRe) || [])[1];
149 if (message) {
150 // At this point this should be a TS1234.
151 this._stats.errors.push(message);
152 }
153 }
154 }
155 }
156 }
157 }
158 _collectBundleStats(compilation) {
159 var _a, _b;
160 const chunkAssets = new Set();
161 for (const chunk of compilation.chunks) {
162 if (!chunk.rendered) {
163 continue;
164 }
165 const firstFile = Array.from(chunk.files)[0];
166 const size = (_b = (_a = compilation.getAsset(firstFile)) === null || _a === void 0 ? void 0 : _a.source.size()) !== null && _b !== void 0 ? _b : 0;
167 chunkAssets.add(firstFile);
168 if (chunk.canBeInitial()) {
169 this._stats.initialChunkSize += size;
170 }
171 else {
172 this._stats.lazyChunkCount++;
173 this._stats.lazyChunkSize += size;
174 }
175 this._stats.totalChunkCount++;
176 this._stats.totalChunkSize += size;
177 if (firstFile.endsWith('.css')) {
178 this._stats.cssSize += size;
179 }
180 }
181 for (const asset of compilation.getAssets()) {
182 // Only count non-JavaScript related files
183 if (chunkAssets.has(asset.name)) {
184 continue;
185 }
186 this._stats.assetSize += asset.source.size();
187 this._stats.assetCount++;
188 if (asset.name == 'polyfill') {
189 this._stats.polyfillSize += asset.source.size();
190 }
191 }
192 }
193 /** **********************************************************************************************
194 * The next section is all the different Webpack hooks for this plugin.
195 */
196 /**
197 * Reports a succeed module.
198 * @private
199 */
200 _succeedModule(module) {
201 // Only report NormalModule instances.
202 if (!(module instanceof webpack_1.NormalModule)) {
203 return;
204 }
205 // Only reports modules that are part of the user's project. We also don't do node_modules.
206 // There is a chance that someone name a file path `hello_node_modules` or something and we
207 // will ignore that file for the purpose of gathering, but we're willing to take the risk.
208 if (!module.resource ||
209 !module.resource.startsWith(this._projectRoot) ||
210 module.resource.indexOf('node_modules') >= 0) {
211 return;
212 }
213 // Check that it's a source file from the project.
214 if (module.resource.endsWith('.ts')) {
215 this._checkTsNormalModule(module);
216 }
217 }
218 _compilation(compiler, compilation) {
219 this._reset();
220 compilation.hooks.succeedModule.tap('NgBuildAnalyticsPlugin', this._succeedModule.bind(this));
221 }
222 _done(stats) {
223 this._collectErrors(stats);
224 this._collectBundleStats(stats.compilation);
225 if (this._built) {
226 this._reportRebuildMetrics(stats);
227 }
228 else {
229 this._reportBuildMetrics(stats);
230 this._built = true;
231 }
232 }
233 apply(compiler) {
234 compiler.hooks.compilation.tap('NgBuildAnalyticsPlugin', this._compilation.bind(this, compiler));
235 compiler.hooks.done.tap('NgBuildAnalyticsPlugin', this._done.bind(this));
236 }
237}
238exports.NgBuildAnalyticsPlugin = NgBuildAnalyticsPlugin;
Note: See TracBrowser for help on using the repository browser.