Skip to content

Commit e6582e6

Browse files
clydinalan-agius4
authored andcommitted
refactor(@angular-devkit/build-angular): reduce Webpack specific overlap in stats table generation
Webpack-specific types and imports have been reduced within the logic for the build stats table generation. This reduces the amount of unused code that needs to be loaded when not using a Webpack-based bundler.
1 parent 4fe345a commit e6582e6

File tree

6 files changed

+314
-299
lines changed

6 files changed

+314
-299
lines changed

goldens/circular-deps/packages.json

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,6 @@
77
"packages/angular_devkit/build_angular/src/tools/esbuild/bundler-execution-result.ts",
88
"packages/angular_devkit/build_angular/src/tools/esbuild/utils.ts"
99
],
10-
[
11-
"packages/angular_devkit/build_angular/src/tools/webpack/utils/stats.ts",
12-
"packages/angular_devkit/build_angular/src/utils/bundle-calculator.ts"
13-
],
1410
[
1511
"packages/angular/cli/src/analytics/analytics-collector.ts",
1612
"packages/angular/cli/src/command-builder/command-module.ts"

packages/angular_devkit/build_angular/src/tools/esbuild/utils.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ import {
2121
} from '../../builders/application/options';
2222
import { BudgetCalculatorResult } from '../../utils/bundle-calculator';
2323
import { Spinner } from '../../utils/spinner';
24-
import { BundleStats, generateEsbuildBuildStatsTable } from '../webpack/utils/stats';
24+
import { BundleStats, generateEsbuildBuildStatsTable } from '../../utils/stats-table';
2525
import { BuildOutputFile, BuildOutputFileType, InitialFileRecord } from './bundler-context';
2626
import { BuildOutputAsset, ExecutionResult } from './bundler-execution-result';
2727

packages/angular_devkit/build_angular/src/tools/webpack/utils/stats.ts

Lines changed: 1 addition & 293 deletions
Original file line numberDiff line numberDiff line change
@@ -10,40 +10,15 @@ import { WebpackLoggingCallback } from '@angular-devkit/build-webpack';
1010
import { logging } from '@angular-devkit/core';
1111
import assert from 'node:assert';
1212
import * as path from 'node:path';
13-
import { stripVTControlCharacters } from 'node:util';
1413
import { Configuration, StatsCompilation } from 'webpack';
1514
import { Schema as BrowserBuilderOptions } from '../../../builders/browser/schema';
1615
import { normalizeOptimization } from '../../../utils';
1716
import { BudgetCalculatorResult } from '../../../utils/bundle-calculator';
1817
import { colors as ansiColors } from '../../../utils/color';
18+
import { BundleStats, generateBuildStatsTable } from '../../../utils/stats-table';
1919
import { markAsyncChunksNonInitial } from './async-chunks';
2020
import { WebpackStatsOptions, getStatsOptions, normalizeExtraEntryPoints } from './helpers';
2121

22-
export function formatSize(size: number): string {
23-
if (size <= 0) {
24-
return '0 bytes';
25-
}
26-
27-
const abbreviations = ['bytes', 'kB', 'MB', 'GB'];
28-
const index = Math.floor(Math.log(size) / Math.log(1024));
29-
const roundedSize = size / Math.pow(1024, index);
30-
// bytes don't have a fraction
31-
const fractionDigits = index === 0 ? 0 : 2;
32-
33-
return `${roundedSize.toFixed(fractionDigits)} ${abbreviations[index]}`;
34-
}
35-
36-
export type BundleStatsData = [
37-
files: string,
38-
names: string,
39-
rawSize: number | string,
40-
estimatedTransferSize: number | string,
41-
];
42-
export interface BundleStats {
43-
initial: boolean;
44-
stats: BundleStatsData;
45-
}
46-
4722
function getBuildDuration(webpackStats: StatsCompilation): number {
4823
assert(webpackStats.builtAt, 'buildAt cannot be undefined');
4924
assert(webpackStats.time, 'time cannot be undefined');
@@ -76,273 +51,6 @@ function generateBundleStats(info: {
7651
};
7752
}
7853

79-
export function generateEsbuildBuildStatsTable(
80-
[browserStats, serverStats]: [browserStats: BundleStats[], serverStats: BundleStats[]],
81-
colors: boolean,
82-
showTotalSize: boolean,
83-
showEstimatedTransferSize: boolean,
84-
budgetFailures?: BudgetCalculatorResult[],
85-
verbose?: boolean,
86-
): string {
87-
const bundleInfo = generateBuildStatsData(
88-
browserStats,
89-
colors,
90-
showTotalSize,
91-
showEstimatedTransferSize,
92-
budgetFailures,
93-
verbose,
94-
);
95-
96-
if (serverStats.length) {
97-
const m = (x: string) => (colors ? ansiColors.magenta(x) : x);
98-
if (browserStats.length) {
99-
bundleInfo.unshift([m('Browser bundles')]);
100-
// Add seperators between browser and server logs
101-
bundleInfo.push([], []);
102-
}
103-
104-
bundleInfo.push(
105-
[m('Server bundles')],
106-
...generateBuildStatsData(serverStats, colors, false, false, undefined, verbose),
107-
);
108-
}
109-
110-
return generateTableText(bundleInfo, colors);
111-
}
112-
113-
export function generateBuildStatsTable(
114-
data: BundleStats[],
115-
colors: boolean,
116-
showTotalSize: boolean,
117-
showEstimatedTransferSize: boolean,
118-
budgetFailures?: BudgetCalculatorResult[],
119-
): string {
120-
const bundleInfo = generateBuildStatsData(
121-
data,
122-
colors,
123-
showTotalSize,
124-
showEstimatedTransferSize,
125-
budgetFailures,
126-
true,
127-
);
128-
129-
return generateTableText(bundleInfo, colors);
130-
}
131-
132-
function generateBuildStatsData(
133-
data: BundleStats[],
134-
colors: boolean,
135-
showTotalSize: boolean,
136-
showEstimatedTransferSize: boolean,
137-
budgetFailures?: BudgetCalculatorResult[],
138-
verbose?: boolean,
139-
): (string | number)[][] {
140-
if (data.length === 0) {
141-
return [];
142-
}
143-
144-
const g = (x: string) => (colors ? ansiColors.green(x) : x);
145-
const c = (x: string) => (colors ? ansiColors.cyan(x) : x);
146-
const r = (x: string) => (colors ? ansiColors.redBright(x) : x);
147-
const y = (x: string) => (colors ? ansiColors.yellowBright(x) : x);
148-
const bold = (x: string) => (colors ? ansiColors.bold(x) : x);
149-
const dim = (x: string) => (colors ? ansiColors.dim(x) : x);
150-
151-
const getSizeColor = (name: string, file?: string, defaultColor = c) => {
152-
const severity = budgets.get(name) || (file && budgets.get(file));
153-
switch (severity) {
154-
case 'warning':
155-
return y;
156-
case 'error':
157-
return r;
158-
default:
159-
return defaultColor;
160-
}
161-
};
162-
163-
const changedEntryChunksStats: BundleStatsData[] = [];
164-
const changedLazyChunksStats: BundleStatsData[] = [];
165-
166-
let initialTotalRawSize = 0;
167-
let changedLazyChunksCount = 0;
168-
let initialTotalEstimatedTransferSize;
169-
const maxLazyChunksWithoutBudgetFailures = 15;
170-
171-
const budgets = new Map<string, string>();
172-
if (budgetFailures) {
173-
for (const { label, severity } of budgetFailures) {
174-
// In some cases a file can have multiple budget failures.
175-
// Favor error.
176-
if (label && (!budgets.has(label) || budgets.get(label) === 'warning')) {
177-
budgets.set(label, severity);
178-
}
179-
}
180-
}
181-
182-
// Sort descending by raw size
183-
data.sort((a, b) => {
184-
if (a.stats[2] > b.stats[2]) {
185-
return -1;
186-
}
187-
188-
if (a.stats[2] < b.stats[2]) {
189-
return 1;
190-
}
191-
192-
return 0;
193-
});
194-
195-
for (const { initial, stats } of data) {
196-
const [files, names, rawSize, estimatedTransferSize] = stats;
197-
if (
198-
!initial &&
199-
!verbose &&
200-
changedLazyChunksStats.length >= maxLazyChunksWithoutBudgetFailures &&
201-
!budgets.has(names) &&
202-
!budgets.has(files)
203-
) {
204-
// Limit the number of lazy chunks displayed in the stats table when there is no budget failure and not in verbose mode.
205-
changedLazyChunksCount++;
206-
continue;
207-
}
208-
209-
const getRawSizeColor = getSizeColor(names, files);
210-
let data: BundleStatsData;
211-
if (showEstimatedTransferSize) {
212-
data = [
213-
g(files),
214-
dim(names),
215-
getRawSizeColor(typeof rawSize === 'number' ? formatSize(rawSize) : rawSize),
216-
c(
217-
typeof estimatedTransferSize === 'number'
218-
? formatSize(estimatedTransferSize)
219-
: estimatedTransferSize,
220-
),
221-
];
222-
} else {
223-
data = [
224-
g(files),
225-
dim(names),
226-
getRawSizeColor(typeof rawSize === 'number' ? formatSize(rawSize) : rawSize),
227-
'',
228-
];
229-
}
230-
231-
if (initial) {
232-
changedEntryChunksStats.push(data);
233-
if (typeof rawSize === 'number') {
234-
initialTotalRawSize += rawSize;
235-
}
236-
if (showEstimatedTransferSize && typeof estimatedTransferSize === 'number') {
237-
if (initialTotalEstimatedTransferSize === undefined) {
238-
initialTotalEstimatedTransferSize = 0;
239-
}
240-
initialTotalEstimatedTransferSize += estimatedTransferSize;
241-
}
242-
} else {
243-
changedLazyChunksStats.push(data);
244-
changedLazyChunksCount++;
245-
}
246-
}
247-
248-
const bundleInfo: (string | number)[][] = [];
249-
const baseTitles = ['Names', 'Raw size'];
250-
251-
if (showEstimatedTransferSize) {
252-
baseTitles.push('Estimated transfer size');
253-
}
254-
255-
// Entry chunks
256-
if (changedEntryChunksStats.length) {
257-
bundleInfo.push(['Initial chunk files', ...baseTitles].map(bold), ...changedEntryChunksStats);
258-
259-
if (showTotalSize) {
260-
const initialSizeTotalColor = getSizeColor('bundle initial', undefined, (x) => x);
261-
const totalSizeElements = [
262-
' ',
263-
'Initial total',
264-
initialSizeTotalColor(formatSize(initialTotalRawSize)),
265-
];
266-
if (showEstimatedTransferSize) {
267-
totalSizeElements.push(
268-
typeof initialTotalEstimatedTransferSize === 'number'
269-
? formatSize(initialTotalEstimatedTransferSize)
270-
: '-',
271-
);
272-
}
273-
bundleInfo.push([], totalSizeElements.map(bold));
274-
}
275-
}
276-
277-
// Seperator
278-
if (changedEntryChunksStats.length && changedLazyChunksStats.length) {
279-
bundleInfo.push([]);
280-
}
281-
282-
// Lazy chunks
283-
if (changedLazyChunksStats.length) {
284-
bundleInfo.push(['Lazy chunk files', ...baseTitles].map(bold), ...changedLazyChunksStats);
285-
286-
if (changedLazyChunksCount > changedLazyChunksStats.length) {
287-
bundleInfo.push([
288-
dim(
289-
`...and ${changedLazyChunksCount - changedLazyChunksStats.length} more lazy chunks files. ` +
290-
'Use "--verbose" to show all the files.',
291-
),
292-
]);
293-
}
294-
}
295-
296-
return bundleInfo;
297-
}
298-
299-
function generateTableText(bundleInfo: (string | number)[][], colors: boolean): string {
300-
const skipText = (value: string) => value.includes('...and ');
301-
const longest: number[] = [];
302-
for (const item of bundleInfo) {
303-
for (let i = 0; i < item.length; i++) {
304-
if (item[i] === undefined) {
305-
continue;
306-
}
307-
308-
const currentItem = item[i].toString();
309-
if (skipText(currentItem)) {
310-
continue;
311-
}
312-
313-
const currentLongest = (longest[i] ??= 0);
314-
const currentItemLength = stripVTControlCharacters(currentItem).length;
315-
if (currentLongest < currentItemLength) {
316-
longest[i] = currentItemLength;
317-
}
318-
}
319-
}
320-
321-
const seperator = colors ? ansiColors.dim(' | ') : ' | ';
322-
const outputTable: string[] = [];
323-
for (const item of bundleInfo) {
324-
for (let i = 0; i < longest.length; i++) {
325-
if (item[i] === undefined) {
326-
continue;
327-
}
328-
329-
const currentItem = item[i].toString();
330-
if (skipText(currentItem)) {
331-
continue;
332-
}
333-
334-
const currentItemLength = stripVTControlCharacters(currentItem).length;
335-
const stringPad = ' '.repeat(longest[i] - currentItemLength);
336-
// Values in columns at index 2 and 3 (Raw and Estimated sizes) are always right aligned.
337-
item[i] = i >= 2 ? stringPad + currentItem : currentItem + stringPad;
338-
}
339-
340-
outputTable.push(item.join(seperator));
341-
}
342-
343-
return outputTable.join('\n');
344-
}
345-
34654
// We use this cache because we can have multiple builders running in the same process,
34755
// where each builder has different output path.
34856

packages/angular_devkit/build_angular/src/utils/bundle-calculator.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
*/
88

99
import { Budget, Type } from '../builders/browser/schema';
10-
import { formatSize } from '../tools/webpack/utils/stats';
10+
import { formatSize } from './format-bytes';
1111

1212
interface Size {
1313
size: number;
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
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+
9+
export function formatSize(size: number): string {
10+
if (size <= 0) {
11+
return '0 bytes';
12+
}
13+
14+
const abbreviations = ['bytes', 'kB', 'MB', 'GB'];
15+
const index = Math.floor(Math.log(size) / Math.log(1024));
16+
const roundedSize = size / Math.pow(1024, index);
17+
// bytes don't have a fraction
18+
const fractionDigits = index === 0 ? 0 : 2;
19+
20+
return `${roundedSize.toFixed(fractionDigits)} ${abbreviations[index]}`;
21+
}

0 commit comments

Comments
 (0)