Skip to content

Commit 9e42530

Browse files
clydinalan-agius4
authored andcommittedOct 9, 2023
feat(@angular-devkit/build-angular): support component style budgets in esbuild builders
The `anyComponentStyle` budget type is now supported when using bundle budgets in the esbuild- based builders (`browser-esbuild`/`application`). The configuration and behavior are intended to match that of the Webpack-based builder (`browser`). With this addition, the bundle budget feature is now at feature parity with the Webpack-based builder. The implementation of this budget type requires less code than the Webpack implementation due to the component stylesheet information being present in the application's output metadata. This removes the need for a custom budget plugin to extract the stylesheet size information during the build.
1 parent c920802 commit 9e42530

File tree

5 files changed

+36
-17
lines changed

5 files changed

+36
-17
lines changed
 

‎packages/angular_devkit/build_angular/src/builders/application/execute-build.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -257,7 +257,7 @@ export async function executeBuild(
257257
let budgetFailures;
258258
if (options.budgets) {
259259
const compatStats = generateBudgetStats(metafile, initialFiles);
260-
budgetFailures = [...checkBudgets(options.budgets, compatStats)];
260+
budgetFailures = [...checkBudgets(options.budgets, compatStats, true)];
261261
for (const { severity, message } of budgetFailures) {
262262
if (severity === 'error') {
263263
context.logger.error(message);

‎packages/angular_devkit/build_angular/src/builders/application/options.ts

-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ import {
1414
normalizeGlobalStyles,
1515
} from '../../tools/webpack/utils/helpers';
1616
import { normalizeAssetPatterns, normalizeOptimization, normalizeSourceMaps } from '../../utils';
17-
import { calculateThresholds } from '../../utils/bundle-calculator';
1817
import { I18nOptions, createI18nOptions } from '../../utils/i18n-options';
1918
import { normalizeCacheOptions } from '../../utils/normalize-cache';
2019
import { generateEntryPoints } from '../../utils/package-chunk-sort';

‎packages/angular_devkit/build_angular/src/builders/application/tests/options/bundle-budgets_spec.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ describeBuilder(buildApplication, APPLICATION_BUILDER_INFO, (harness) => {
8888
});
8989

9090
CSS_EXTENSIONS.forEach((ext) => {
91-
xit(`shows warnings for large component ${ext} when using 'anyComponentStyle' when AOT`, async () => {
91+
it(`shows warnings for large component ${ext} when using 'anyComponentStyle' when AOT`, async () => {
9292
const cssContent = `
9393
.foo { color: white; padding: 1px; }
9494
.buz { color: white; padding: 2px; }
@@ -118,7 +118,7 @@ describeBuilder(buildApplication, APPLICATION_BUILDER_INFO, (harness) => {
118118
expect(logs).toContain(
119119
jasmine.objectContaining<logging.LogEntry>({
120120
level: 'warn',
121-
message: jasmine.stringMatching(new RegExp(`Warning.+app.component.${ext}`)),
121+
message: jasmine.stringMatching(new RegExp(`app.component.${ext}`)),
122122
}),
123123
);
124124
});

‎packages/angular_devkit/build_angular/src/tools/esbuild/budget-stats.ts

+11-1
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,17 @@ export function generateBudgetStats(
4747
initial: !!initialRecord,
4848
names: name ? [name] : undefined,
4949
});
50-
stats.assets.push({ name: file, size: entry.bytes });
50+
51+
// 'ng-component' is set by the angular plugin's component stylesheet bundler
52+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
53+
const componentStyle: boolean = (entry as any)['ng-component'];
54+
55+
stats.assets.push({
56+
// Component styles use the input file while all other outputs use the result file
57+
name: (componentStyle && Object.keys(entry.inputs)[0]) || file,
58+
size: entry.bytes,
59+
componentStyle,
60+
});
5161
}
5262

5363
return stats;

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

+22-12
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ export interface BudgetChunk {
4545
export interface BudgetAsset {
4646
name: string;
4747
size: number;
48+
componentStyle?: boolean;
4849
}
4950

5051
export interface BudgetStats {
@@ -118,23 +119,15 @@ export function* calculateThresholds(budget: Budget): IterableIterator<Threshold
118119
* Calculates the sizes for bundles in the budget type provided.
119120
*/
120121
function calculateSizes(budget: Budget, stats: BudgetStats): Size[] {
121-
if (budget.type === Type.AnyComponentStyle) {
122-
// Component style size information is not available post-build, this must
123-
// be checked mid-build via the `AnyComponentStyleBudgetChecker` plugin.
124-
throw new Error(
125-
'Can not calculate size of AnyComponentStyle. Use `AnyComponentStyleBudgetChecker` instead.',
126-
);
127-
}
128-
129-
type NonComponentStyleBudgetTypes = Exclude<Budget['type'], Type.AnyComponentStyle>;
130122
type CalculatorTypes = {
131123
new (budget: Budget, chunks: BudgetChunk[], assets: BudgetAsset[]): Calculator;
132124
};
133-
const calculatorMap: Record<NonComponentStyleBudgetTypes, CalculatorTypes> = {
125+
const calculatorMap: Record<Budget['type'], CalculatorTypes> = {
134126
all: AllCalculator,
135127
allScript: AllScriptCalculator,
136128
any: AnyCalculator,
137129
anyScript: AnyScriptCalculator,
130+
anyComponentStyle: AnyComponentStyleCalculator,
138131
bundle: BundleCalculator,
139132
initial: InitialCalculator,
140133
};
@@ -279,6 +272,20 @@ class AnyCalculator extends Calculator {
279272
}
280273
}
281274

275+
/**
276+
* Any compoonent stylesheet
277+
*/
278+
class AnyComponentStyleCalculator extends Calculator {
279+
calculate() {
280+
return this.assets
281+
.filter((asset) => asset.componentStyle)
282+
.map((asset) => ({
283+
size: this.getAssetSize(asset),
284+
label: asset.name,
285+
}));
286+
}
287+
}
288+
282289
/**
283290
* Calculate the bytes given a string value.
284291
*/
@@ -316,9 +323,12 @@ function calculateBytes(input: string, baseline?: string, factor: 1 | -1 = 1): n
316323
export function* checkBudgets(
317324
budgets: Budget[],
318325
stats: BudgetStats,
326+
checkComponentStyles?: boolean,
319327
): IterableIterator<BudgetCalculatorResult> {
320-
// Ignore AnyComponentStyle budgets as these are handled in `AnyComponentStyleBudgetChecker`.
321-
const computableBudgets = budgets.filter((budget) => budget.type !== Type.AnyComponentStyle);
328+
// Ignore AnyComponentStyle budgets as these are handled in `AnyComponentStyleBudgetChecker` unless requested
329+
const computableBudgets = checkComponentStyles
330+
? budgets
331+
: budgets.filter((budget) => budget.type !== Type.AnyComponentStyle);
322332

323333
for (const budget of computableBudgets) {
324334
const sizes = calculateSizes(budget, stats);

0 commit comments

Comments
 (0)
Please sign in to comment.