Skip to content

Commit

Permalink
fix(@angular-devkit/build-angular): display server bundles in build s…
Browse files Browse the repository at this point in the history
…tats

This commit adds server bundle information to the build logs

Closes #25855
  • Loading branch information
alan-agius4 committed Jan 30, 2024
1 parent 45820d2 commit 41ea985
Show file tree
Hide file tree
Showing 10 changed files with 125 additions and 66 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -188,6 +189,7 @@ export async function executeBuild(
budgetFailures,
changedFiles,
estimatedTransferSizes,
!!ssrOptions,
);

// Write metafile if stats option is enabled
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,6 @@ export function setupBundlerContexts(
nodeTargets,
codeBundleCache,
),
() => false,
),
);

Expand All @@ -116,12 +115,7 @@ export function setupBundlerContexts(

if (serverPolyfillBundleOptions) {
bundlerContexts.push(
new BundlerContext(
workspaceRoot,
!!options.watch,
serverPolyfillBundleOptions,
() => false,
),
new BundlerContext(workspaceRoot, !!options.watch, serverPolyfillBundleOptions),
);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,6 @@ describe('Browser Builder scripts array', () => {
expect(joinedLogs).toMatch(/lazy-script.+\d+ bytes/);
expect(joinedLogs).toMatch(/renamed-script.+\d+ bytes/);
expect(joinedLogs).toMatch(/renamed-lazy-script.+\d+ bytes/);
expect(joinedLogs).not.toContain('Lazy Chunks');
expect(joinedLogs).not.toContain('Lazy chunks');
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,13 @@ export class BundlerContext {
// For non-incremental builds, perform a single build
result = await build(this.#esbuildOptions);
}

if (this.#esbuildOptions?.platform === 'node') {
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)) {
Expand Down
35 changes: 26 additions & 9 deletions packages/angular_devkit/build_angular/src/tools/esbuild/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand All @@ -28,14 +28,18 @@ export function logBuildStats(
budgetFailures: BudgetCalculatorResult[] | undefined,
changedFiles?: Set<string>,
estimatedTransferSizes?: Map<string, number>,
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']) {
Expand All @@ -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
Expand All @@ -56,22 +67,28 @@ export function logBuildStats(
.replace(/[\\/.]/g, '-');
}

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

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');
}
Expand Down
111 changes: 72 additions & 39 deletions packages/angular_devkit/build_angular/src/tools/webpack/utils/stats.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -75,18 +75,73 @@ 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,
showTotalSize: boolean,
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));
Expand Down Expand Up @@ -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'
Expand All @@ -149,7 +204,7 @@ export function generateBuildStatsTable(
} else {
data = [
g(files),
names,
dim(names),
getRawSizeColor(typeof rawSize === 'number' ? formatSize(rawSize) : rawSize),
'',
];
Expand All @@ -172,25 +227,25 @@ export function generateBuildStatsTable(
}

const bundleInfo: (string | number)[][] = [];
const baseTitles = ['Names', 'Raw Size'];
const baseTitles = ['Names', 'Raw size'];
const tableAlign: ('l' | 'r')[] = ['l', 'l', 'r'];

if (showEstimatedTransferSize) {
baseTitles.push('Estimated Transfer Size');
baseTitles.push('Estimated transfer size');
tableAlign.push('r');
}

// Entry chunks
if (changedEntryChunksStats.length) {
bundleInfo.push(['Initial Chunk Files', ...baseTitles].map(bold), ...changedEntryChunksStats);
bundleInfo.push(['Initial chunk files', ...baseTitles].map(bold), ...changedEntryChunksStats);

if (showTotalSize) {
bundleInfo.push([]);

const initialSizeTotalColor = getSizeColor('bundle initial', undefined, (x) => x);
const totalSizeElements = [
' ',
'Initial Total',
'Initial total',
initialSizeTotalColor(formatSize(initialTotalRawSize)),
];
if (showEstimatedTransferSize) {
Expand All @@ -211,10 +266,10 @@ export function generateBuildStatsTable(

// Lazy chunks
if (changedLazyChunksStats.length) {
bundleInfo.push(['Lazy Chunk Files', ...baseTitles].map(bold), ...changedLazyChunksStats);
bundleInfo.push(['Lazy chunk files', ...baseTitles].map(bold), ...changedLazyChunksStats);
}

return generateTableText(bundleInfo, colors);
return bundleInfo;
}

function generateTableText(bundleInfo: (string | number)[][], colors: boolean): string {
Expand Down Expand Up @@ -255,12 +310,6 @@ function generateTableText(bundleInfo: (string | number)[][], colors: boolean):
return outputTable.join('\n');
}

function generateBuildStats(hash: string, time: number, colors: boolean): string {
const w = (x: string) => (colors ? ansiColors.bold.white(x) : x);

return `Build at: ${w(new Date().toISOString())} - Hash: ${w(hash)} - Time: ${w('' + time)}ms`;
}

// We use this cache because we can have multiple builders running in the same process,
// where each builder has different output path.

Expand All @@ -279,6 +328,7 @@ function statsToString(

const colors = statsConfig.colors;
const rs = (x: string) => (colors ? ansiColors.reset(x) : x);
const w = (x: string) => (colors ? ansiColors.bold.white(x) : x);

const changedChunksStats: BundleStats[] = [];
let unchangedChunkNumber = 0;
Expand Down Expand Up @@ -330,30 +380,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` : '') +
`Build at: ${w(new Date().toISOString())} - Hash: ${w(json.hash || '')} - Time: ${w('' + time)}ms`,
);
}

export function statsWarningsToString(
Expand Down
8 changes: 4 additions & 4 deletions tests/legacy-cli/e2e/tests/basic/build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ export default async function () {
const { stdout: stdout1 } = await ng('build', '--configuration=development');
await expectFileToMatch('dist/test-project/browser/index.html', 'main.js');

if (stdout1.includes('Estimated Transfer Size')) {
if (stdout1.includes('Estimated transfer size')) {
throw new Error(
`Expected stdout not to contain 'Estimated Transfer Size' but it did.\n${stdout1}`,
`Expected stdout not to contain 'Estimated transfer size' but it did.\n${stdout1}`,
);
}

Expand All @@ -22,9 +22,9 @@ export default async function () {
await expectFileToMatch('dist/test-project/browser/index.html', /main\.[a-zA-Z0-9]{16}\.js/);
}

if (!stdout2.includes('Estimated Transfer Size')) {
if (!stdout2.includes('Estimated transfer size')) {
throw new Error(
`Expected stdout to contain 'Estimated Transfer Size' but it did not.\n${stdout2}`,
`Expected stdout to contain 'Estimated transfer size' but it did not.\n${stdout2}`,
);
}
}
4 changes: 2 additions & 2 deletions tests/legacy-cli/e2e/tests/basic/styles-array.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,8 @@ export default async function () {
);

// Non injected styles should be listed under lazy chunk files
if (!/Lazy Chunk Files[\s\S]+renamed-lazy-style\.css/m.test(stdout)) {
if (!/Lazy chunk files[\s\S]+renamed-lazy-style\.css/m.test(stdout)) {
console.log(stdout);
throw new Error(`Expected "renamed-lazy-style.css" to be listed under "Lazy Chunk Files".`);
throw new Error(`Expected "renamed-lazy-style.css" to be listed under "Lazy chunk files".`);
}
}
Loading

0 comments on commit 41ea985

Please sign in to comment.