From 69ae9989616d3c88aa9b2d7278a3b805f3858423 Mon Sep 17 00:00:00 2001 From: Alan Agius Date: Mon, 29 Jan 2024 14:12:11 +0000 Subject: [PATCH] fix(@angular-devkit/build-angular): display server bundles in build stats This commit adds server bundle information to the build logs Closes #25855 --- .../src/builders/application/execute-build.ts | 2 + .../builders/application/setup-bundling.ts | 8 +- .../src/tools/esbuild/budget-stats.ts | 6 ++ .../src/tools/esbuild/bundler-context.ts | 9 ++ .../build_angular/src/tools/esbuild/utils.ts | 36 +++++-- .../src/tools/webpack/utils/stats.ts | 94 +++++++++++++------ 6 files changed, 111 insertions(+), 44 deletions(-) diff --git a/packages/angular_devkit/build_angular/src/builders/application/execute-build.ts b/packages/angular_devkit/build_angular/src/builders/application/execute-build.ts index aeef526f2834..b839376dae34 100644 --- a/packages/angular_devkit/build_angular/src/builders/application/execute-build.ts +++ b/packages/angular_devkit/build_angular/src/builders/application/execute-build.ts @@ -36,6 +36,7 @@ export async function executeBuild( assets, cacheOptions, prerenderOptions, + ssrOptions, } = options; // TODO: Consider integrating into watch mode. Would require full rebuild on target changes. @@ -188,6 +189,7 @@ export async function executeBuild( budgetFailures, changedFiles, estimatedTransferSizes, + !!ssrOptions, ); // Write metafile if stats option is enabled diff --git a/packages/angular_devkit/build_angular/src/builders/application/setup-bundling.ts b/packages/angular_devkit/build_angular/src/builders/application/setup-bundling.ts index dea588873038..498d72ffa5b5 100644 --- a/packages/angular_devkit/build_angular/src/builders/application/setup-bundling.ts +++ b/packages/angular_devkit/build_angular/src/builders/application/setup-bundling.ts @@ -103,7 +103,6 @@ export function setupBundlerContexts( nodeTargets, codeBundleCache, ), - () => false, ), ); @@ -116,12 +115,7 @@ export function setupBundlerContexts( if (serverPolyfillBundleOptions) { bundlerContexts.push( - new BundlerContext( - workspaceRoot, - !!options.watch, - serverPolyfillBundleOptions, - () => false, - ), + new BundlerContext(workspaceRoot, !!options.watch, serverPolyfillBundleOptions), ); } } diff --git a/packages/angular_devkit/build_angular/src/tools/esbuild/budget-stats.ts b/packages/angular_devkit/build_angular/src/tools/esbuild/budget-stats.ts index 0737e9825f24..d5148ffed3f4 100644 --- a/packages/angular_devkit/build_angular/src/tools/esbuild/budget-stats.ts +++ b/packages/angular_devkit/build_angular/src/tools/esbuild/budget-stats.ts @@ -33,6 +33,12 @@ export function generateBudgetStats( continue; } + // Exclude server bundles + // eslint-disable-next-line @typescript-eslint/no-explicit-any + if ((entry as any)['ng-platform-server']) { + continue; + } + const initialRecord = initialFiles.get(file); let name = initialRecord?.name; if (name === undefined && entry.entryPoint) { diff --git a/packages/angular_devkit/build_angular/src/tools/esbuild/bundler-context.ts b/packages/angular_devkit/build_angular/src/tools/esbuild/bundler-context.ts index b278c2d50e8b..d9828f1171b2 100644 --- a/packages/angular_devkit/build_angular/src/tools/esbuild/bundler-context.ts +++ b/packages/angular_devkit/build_angular/src/tools/esbuild/bundler-context.ts @@ -221,6 +221,15 @@ export class BundlerContext { // For non-incremental builds, perform a single build result = await build(this.#esbuildOptions); } + + const platform = this.#esbuildOptions?.platform; + // Default platform in esbuild is browser, see: https://esbuild.github.io/api/#platform + if (platform && platform !== 'browser') { + for (const entry of Object.values(result.metafile.outputs)) { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + (entry as any)['ng-platform-server'] = true; + } + } } catch (failure) { // Build failures will throw an exception which contains errors/warnings if (isEsBuildFailure(failure)) { diff --git a/packages/angular_devkit/build_angular/src/tools/esbuild/utils.ts b/packages/angular_devkit/build_angular/src/tools/esbuild/utils.ts index 424e2bd0252e..8451fcd207b5 100644 --- a/packages/angular_devkit/build_angular/src/tools/esbuild/utils.ts +++ b/packages/angular_devkit/build_angular/src/tools/esbuild/utils.ts @@ -17,7 +17,7 @@ import { coerce } from 'semver'; import { NormalizedOutputOptions } from '../../builders/application/options'; import { BudgetCalculatorResult } from '../../utils/bundle-calculator'; import { Spinner } from '../../utils/spinner'; -import { BundleStats, generateBuildStatsTable } from '../webpack/utils/stats'; +import { BundleStats, generateEsbuildBuildStatsTable } from '../webpack/utils/stats'; import { BuildOutputFile, BuildOutputFileType, InitialFileRecord } from './bundler-context'; import { BuildOutputAsset } from './bundler-execution-result'; @@ -28,14 +28,18 @@ export function logBuildStats( budgetFailures: BudgetCalculatorResult[] | undefined, changedFiles?: Set, estimatedTransferSizes?: Map, + ssrOutputEnabled?: boolean, ): void { - const stats: BundleStats[] = []; + const browserStats: BundleStats[] = []; + const serverStats: BundleStats[] = []; let unchangedCount = 0; + for (const [file, output] of Object.entries(metafile.outputs)) { // Only display JavaScript and CSS files - if (!file.endsWith('.js') && !file.endsWith('.css')) { + 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']) { @@ -48,6 +52,13 @@ export function logBuildStats( continue; } + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const isPlatformServer = (output as any)['ng-platform-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 = path @@ -56,22 +67,29 @@ export function logBuildStats( .replace(/[\\/.]/g, '-'); } - stats.push({ + const stat: BundleStats = { initial: initial.has(file), stats: [file, name ?? '-', output.bytes, estimatedTransferSizes?.get(file) ?? '-'], - }); + }; + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + if (isPlatformServer) { + serverStats.push(stat); + } else { + browserStats.push(stat); + } } - if (stats.length > 0) { - const tableText = generateBuildStatsTable( - stats, + if (browserStats.length > 0 || serverStats.length > 0) { + const tableText = generateEsbuildBuildStatsTable( + [browserStats, serverStats], true, unchangedCount === 0, !!estimatedTransferSizes, budgetFailures, ); - logger.info('\n' + tableText + '\n'); + logger.info(tableText + '\n'); } else if (changedFiles !== undefined) { logger.info('\nNo output file changes.\n'); } diff --git a/packages/angular_devkit/build_angular/src/tools/webpack/utils/stats.ts b/packages/angular_devkit/build_angular/src/tools/webpack/utils/stats.ts index 2a7d506712ec..e01bdcb76d39 100644 --- a/packages/angular_devkit/build_angular/src/tools/webpack/utils/stats.ts +++ b/packages/angular_devkit/build_angular/src/tools/webpack/utils/stats.ts @@ -7,7 +7,7 @@ */ import { WebpackLoggingCallback } from '@angular-devkit/build-webpack'; -import { logging, tags } from '@angular-devkit/core'; +import { logging } from '@angular-devkit/core'; import assert from 'node:assert'; import * as path from 'node:path'; import { Configuration, StatsCompilation } from 'webpack'; @@ -75,6 +75,38 @@ function generateBundleStats(info: { }; } +export function generateEsbuildBuildStatsTable( + [browserStats, serverStats]: [browserStats: BundleStats[], serverStats: BundleStats[]], + colors: boolean, + showTotalSize: boolean, + showEstimatedTransferSize: boolean, + budgetFailures?: BudgetCalculatorResult[], +): string { + const bundleInfo = generateBuildStatsData( + browserStats, + colors, + showTotalSize, + showEstimatedTransferSize, + budgetFailures, + ); + + if (serverStats.length) { + const m = (x: string) => (colors ? ansiColors.magenta(x) : x); + if (browserStats.length) { + bundleInfo.unshift([m('Browser bundles')]); + // Add seperators between browser and server logs + bundleInfo.push([], []); + } + + bundleInfo.push( + [m('Server bundles')], + ...generateBuildStatsData(serverStats, colors, false, false, undefined), + ); + } + + return generateTableText(bundleInfo, colors); +} + export function generateBuildStatsTable( data: BundleStats[], colors: boolean, @@ -82,11 +114,34 @@ export function generateBuildStatsTable( showEstimatedTransferSize: boolean, budgetFailures?: BudgetCalculatorResult[], ): string { - const g = (x: string) => (colors ? ansiColors.greenBright(x) : x); - const c = (x: string) => (colors ? ansiColors.cyanBright(x) : x); + const bundleInfo = generateBuildStatsData( + data, + colors, + showTotalSize, + showEstimatedTransferSize, + budgetFailures, + ); + + return generateTableText(bundleInfo, colors); +} + +function generateBuildStatsData( + data: BundleStats[], + colors: boolean, + showTotalSize: boolean, + showEstimatedTransferSize: boolean, + budgetFailures?: BudgetCalculatorResult[], +): (string | number)[][] { + if (data.length === 0) { + return []; + } + + const g = (x: string) => (colors ? ansiColors.green(x) : x); + const c = (x: string) => (colors ? ansiColors.cyan(x) : x); const r = (x: string) => (colors ? ansiColors.redBright(x) : x); const y = (x: string) => (colors ? ansiColors.yellowBright(x) : x); const bold = (x: string) => (colors ? ansiColors.bold(x) : x); + const dim = (x: string) => (colors ? ansiColors.dim(x) : x); const getSizeColor = (name: string, file?: string, defaultColor = c) => { const severity = budgets.get(name) || (file && budgets.get(file)); @@ -138,7 +193,7 @@ export function generateBuildStatsTable( if (showEstimatedTransferSize) { data = [ g(files), - names, + dim(names), getRawSizeColor(typeof rawSize === 'number' ? formatSize(rawSize) : rawSize), c( typeof estimatedTransferSize === 'number' @@ -149,7 +204,7 @@ export function generateBuildStatsTable( } else { data = [ g(files), - names, + dim(names), getRawSizeColor(typeof rawSize === 'number' ? formatSize(rawSize) : rawSize), '', ]; @@ -214,7 +269,7 @@ export function generateBuildStatsTable( bundleInfo.push(['Lazy Chunk Files', ...baseTitles].map(bold), ...changedLazyChunksStats); } - return generateTableText(bundleInfo, colors); + return bundleInfo; } function generateTableText(bundleInfo: (string | number)[][], colors: boolean): string { @@ -330,30 +385,13 @@ function statsToString( // In some cases we do things outside of webpack context // Such us index generation, service worker augmentation etc... // This will correct the time and include these. - const time = getBuildDuration(json); - if (unchangedChunkNumber > 0) { - return ( - '\n' + - rs(tags.stripIndents` - ${statsTable} - - ${unchangedChunkNumber} unchanged chunks - - ${generateBuildStats(json.hash || '', time, colors)} - `) - ); - } else { - return ( - '\n' + - rs(tags.stripIndents` - ${statsTable} - - ${generateBuildStats(json.hash || '', time, colors)} - `) - ); - } + return rs( + `\n${statsTable}\n\n` + + (unchangedChunkNumber > 0 ? `${unchangedChunkNumber} unchanged chunks\n\n` : '') + + `${generateBuildStats(json.hash || '', time, colors)}`, + ); } export function statsWarningsToString(