Skip to content

Commit

Permalink
refactor(@angular/build): use build output files directly in stats an…
Browse files Browse the repository at this point in the history
…d budgets

The bundle budget calculators and the console build stats output are now
calculated directly from the build output file information instead of the
esbuild metafile where possible. This provides a more generic method of
accessing the information and can more accurately account for post-processing
steps that may alter the output files. The metafile is still used for
component style budgets and lazy chunk name information.
  • Loading branch information
clydin committed Jun 27, 2024
1 parent f3ba208 commit 75abe2c
Show file tree
Hide file tree
Showing 3 changed files with 37 additions and 28 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ export async function executeBuild(
// Analyze files for bundle budget failures if present
let budgetFailures: BudgetCalculatorResult[] | undefined;
if (options.budgets) {
const compatStats = generateBudgetStats(metafile, initialFiles);
const compatStats = generateBudgetStats(metafile, outputFiles, initialFiles);
budgetFailures = [...checkBudgets(options.budgets, compatStats, true)];
for (const { message, severity } of budgetFailures) {
if (severity === 'error') {
Expand Down Expand Up @@ -191,6 +191,7 @@ export async function executeBuild(
executionResult.addLog(
logBuildStats(
metafile,
outputFiles,
initialFiles,
budgetFailures,
colors,
Expand Down
36 changes: 24 additions & 12 deletions packages/angular/build/src/tools/esbuild/budget-stats.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,12 @@

import type { Metafile } from 'esbuild';
import type { BudgetStats } from '../../utils/bundle-calculator';
import type { InitialFileRecord } from './bundler-context';
import { getEntryPointName } from './utils';
import {
type BuildOutputFile,
BuildOutputFileType,
type InitialFileRecord,
} from './bundler-context';
import { getChunkNameFromMetafile } from './utils';

/**
* Generates a bundle budget calculator compatible stats object that provides
Expand All @@ -21,44 +25,52 @@ import { getEntryPointName } from './utils';
*/
export function generateBudgetStats(
metafile: Metafile,
outputFiles: BuildOutputFile[],
initialFiles: Map<string, InitialFileRecord>,
): BudgetStats {
const stats: Required<BudgetStats> = {
chunks: [],
assets: [],
};

for (const [file, entry] of Object.entries(metafile.outputs)) {
for (const { path: file, size, type } of outputFiles) {
if (!file.endsWith('.js') && !file.endsWith('.css')) {
continue;
}

// Exclude server bundles
// eslint-disable-next-line @typescript-eslint/no-explicit-any
if ((entry as any)['ng-platform-server']) {
if (type === BuildOutputFileType.Server) {
continue;
}

const initialRecord = initialFiles.get(file);
let name = initialRecord?.name;
if (name === undefined && entry.entryPoint) {
// For non-initial lazy modules, convert the entry point file into a Webpack compatible name
name = getEntryPointName(entry.entryPoint);
}
const name = initialRecord?.name ?? getChunkNameFromMetafile(metafile, file);

stats.chunks.push({
files: [file],
initial: !!initialRecord,
names: name ? [name] : undefined,
});

stats.assets.push({
name: file,
size,
});
}

// Add component styles from metafile
// TODO: Provide this information directly from the AOT compiler
for (const entry of Object.values(metafile.outputs)) {
// 'ng-component' is set by the angular plugin's component stylesheet bundler
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const componentStyle: boolean = (entry as any)['ng-component'];
if (!componentStyle) {
continue;
}

stats.assets.push({
// Component styles use the input file while all other outputs use the result file
name: (componentStyle && Object.keys(entry.inputs)[0]) || file,
// Component styles use the input file
name: Object.keys(entry.inputs)[0],
size: entry.bytes,
componentStyle,
});
Expand Down
26 changes: 11 additions & 15 deletions packages/angular/build/src/tools/esbuild/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import { BuildOutputAsset, ExecutionResult } from './bundler-execution-result';

export function logBuildStats(
metafile: Metafile,
outputFiles: BuildOutputFile[],
initial: Map<string, InitialFileRecord>,
budgetFailures: BudgetCalculatorResult[] | undefined,
colors: boolean,
Expand All @@ -39,39 +40,28 @@ export function logBuildStats(
const serverStats: BundleStats[] = [];
let unchangedCount = 0;

for (const [file, output] of Object.entries(metafile.outputs)) {
for (const { path: file, size, type } of outputFiles) {
// Only display JavaScript and CSS files
if (!/\.(?:css|m?js)$/.test(file)) {
continue;
}

// Skip internal component resources
// eslint-disable-next-line @typescript-eslint/no-explicit-any
if ((output as any)['ng-component']) {
continue;
}

// Show only changed files if a changed list is provided
if (changedFiles && !changedFiles.has(file)) {
++unchangedCount;
continue;
}

// eslint-disable-next-line @typescript-eslint/no-explicit-any
const isPlatformServer = (output as any)['ng-platform-server'];
const isPlatformServer = type === BuildOutputFileType.Server;
if (isPlatformServer && !ssrOutputEnabled) {
// Only log server build stats when SSR is enabled.
continue;
}

let name = initial.get(file)?.name;
if (name === undefined && output.entryPoint) {
name = getEntryPointName(output.entryPoint);
}

const name = initial.get(file)?.name ?? getChunkNameFromMetafile(metafile, file);
const stat: BundleStats = {
initial: initial.has(file),
stats: [file, name ?? '-', output.bytes, estimatedTransferSizes?.get(file) ?? '-'],
stats: [file, name ?? '-', size, estimatedTransferSizes?.get(file) ?? '-'],
};

if (isPlatformServer) {
Expand Down Expand Up @@ -102,6 +92,12 @@ export function logBuildStats(
return '';
}

export function getChunkNameFromMetafile(metafile: Metafile, file: string): string | undefined {
if (metafile.outputs[file]?.entryPoint) {
return getEntryPointName(metafile.outputs[file].entryPoint);
}
}

export async function calculateEstimatedTransferSizes(
outputFiles: OutputFile[],
): Promise<Map<string, number>> {
Expand Down

0 comments on commit 75abe2c

Please sign in to comment.