diff --git a/packages/angular/build/src/builders/application/execute-build.ts b/packages/angular/build/src/builders/application/execute-build.ts index 7ac168d2768c..5bb6ace1cc02 100644 --- a/packages/angular/build/src/builders/application/execute-build.ts +++ b/packages/angular/build/src/builders/application/execute-build.ts @@ -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') { @@ -191,6 +191,7 @@ export async function executeBuild( executionResult.addLog( logBuildStats( metafile, + outputFiles, initialFiles, budgetFailures, colors, diff --git a/packages/angular/build/src/tools/esbuild/budget-stats.ts b/packages/angular/build/src/tools/esbuild/budget-stats.ts index 981d9bf5604b..1adc7672c50c 100644 --- a/packages/angular/build/src/tools/esbuild/budget-stats.ts +++ b/packages/angular/build/src/tools/esbuild/budget-stats.ts @@ -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 @@ -21,6 +25,7 @@ import { getEntryPointName } from './utils'; */ export function generateBudgetStats( metafile: Metafile, + outputFiles: BuildOutputFile[], initialFiles: Map, ): BudgetStats { const stats: Required = { @@ -28,23 +33,18 @@ export function generateBudgetStats( 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], @@ -52,13 +52,25 @@ export function generateBudgetStats( 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, }); diff --git a/packages/angular/build/src/tools/esbuild/utils.ts b/packages/angular/build/src/tools/esbuild/utils.ts index 8a68edca3424..3b64ddd60e83 100644 --- a/packages/angular/build/src/tools/esbuild/utils.ts +++ b/packages/angular/build/src/tools/esbuild/utils.ts @@ -27,6 +27,7 @@ import { BuildOutputAsset, ExecutionResult } from './bundler-execution-result'; export function logBuildStats( metafile: Metafile, + outputFiles: BuildOutputFile[], initial: Map, budgetFailures: BudgetCalculatorResult[] | undefined, colors: boolean, @@ -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) { @@ -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> {