diff --git a/e2e/cases/performance/print-file-size/index.test.ts b/e2e/cases/performance/print-file-size/index.test.ts index 12882880f6..6bd19b5570 100644 --- a/e2e/cases/performance/print-file-size/index.test.ts +++ b/e2e/cases/performance/print-file-size/index.test.ts @@ -33,7 +33,7 @@ test.describe('should print file size correctly', async () => { expect(logs.some((log) => log.includes('Gzipped size:'))).toBeTruthy(); }); - test('should print size of multiple targets correctly', async () => { + test('should print size of multiple environments correctly', async () => { await build({ cwd, rsbuildConfig: { @@ -59,6 +59,10 @@ test.describe('should print file size correctly', async () => { }); // dist/index.html + expect( + logs.some((log) => log.includes('File') && log.includes('(web)')), + ).toBeTruthy(); + expect( logs.some( (log) => @@ -69,6 +73,9 @@ test.describe('should print file size correctly', async () => { ).toBeTruthy(); // dist/server/index.js + expect( + logs.some((log) => log.includes('File') && log.includes('(node)')), + ).toBeTruthy(); expect( logs.some( (log) => diff --git a/packages/core/src/plugins/fileSize.ts b/packages/core/src/plugins/fileSize.ts index 645c63f7d6..57faf2d8af 100644 --- a/packages/core/src/plugins/fileSize.ts +++ b/packages/core/src/plugins/fileSize.ts @@ -6,12 +6,7 @@ import fs from 'node:fs'; import path from 'node:path'; import { JS_REGEX } from '@rsbuild/shared'; import { color } from '@rsbuild/shared'; -import type { - MultiStats, - PrintFileSizeOptions, - Stats, - StatsAsset, -} from '@rsbuild/shared'; +import type { PrintFileSizeOptions, Stats, StatsAsset } from '@rsbuild/shared'; import { CSS_REGEX, HTML_REGEX } from '../constants'; import { logger } from '../logger'; import type { RsbuildPlugin } from '../types'; @@ -30,22 +25,26 @@ const getAssetColor = (size: number) => { return color.green; }; -async function printHeader( +function getHeader( longestFileLength: number, longestLabelLength: number, + environment: string, ) { const longestLengths = [longestFileLength, longestLabelLength]; - const headerRow = ['File', 'Size', 'Gzipped'].reduce((prev, cur, index) => { - const length = longestLengths[index]; - let curLabel = cur; - if (length) { - curLabel = - cur.length < length ? cur + ' '.repeat(length - cur.length) : cur; - } - return `${prev + curLabel} `; - }, ' '); + const headerRow = [`File (${environment})`, 'Size', 'Gzipped'].reduce( + (prev, cur, index) => { + const length = longestLengths[index]; + let curLabel = cur; + if (length) { + curLabel = + cur.length < length ? cur + ' '.repeat(length - cur.length) : cur; + } + return `${prev + curLabel} `; + }, + ' ', + ); - logger.log(color.bold(color.blue(headerRow))); + return color.bold(color.blue(headerRow)); } const calcFileSize = (len: number) => { @@ -68,11 +67,13 @@ const coloringAssetName = (assetName: string) => { async function printFileSizes( config: PrintFileSizeOptions, - stats: Stats | MultiStats, + stats: Stats, rootPath: string, + environment: string, ) { + const logs: string[] = []; if (config.detail === false && config.total === false) { - return; + return logs; } const { default: gzipSize } = await import('@rsbuild/shared/gzip-size'); @@ -97,55 +98,51 @@ async function printFileSizes( }; }; - const multiStats = 'stats' in stats ? stats.stats : [stats]; - const assets = multiStats - .map((stats) => { - const distPath = stats.compilation.outputOptions.path; + const getAssets = () => { + const distPath = stats.compilation.outputOptions.path; - if (!distPath) { - return []; - } + if (!distPath) { + return []; + } - const origin = stats.toJson({ - all: false, - assets: true, - // TODO: need supported in rspack - // @ts-expect-error - cachedAssets: true, - groupAssetsByInfo: false, - groupAssetsByPath: false, - groupAssetsByChunk: false, - groupAssetsByExtension: false, - groupAssetsByEmitStatus: false, - }); + const origin = stats.toJson({ + all: false, + assets: true, + // TODO: need supported in rspack + // @ts-expect-error + cachedAssets: true, + groupAssetsByInfo: false, + groupAssetsByPath: false, + groupAssetsByChunk: false, + groupAssetsByExtension: false, + groupAssetsByEmitStatus: false, + }); - const filteredAssets = origin.assets!.filter((asset) => - filterAsset(asset.name), - ); + const filteredAssets = origin.assets!.filter((asset) => + filterAsset(asset.name), + ); - const distFolder = path.relative(rootPath, distPath); + const distFolder = path.relative(rootPath, distPath); - return filteredAssets.map((asset) => - formatAsset(asset, distPath, distFolder), - ); - }) - .reduce((single, all) => all.concat(single), []); + return filteredAssets.map((asset) => + formatAsset(asset, distPath, distFolder), + ); + }; + const assets = getAssets(); if (assets.length === 0) { - return; + return logs; } assets.sort((a, b) => a.size - b.size); - logger.info('Production file sizes:\n'); - const longestLabelLength = Math.max(...assets.map((a) => a.sizeLabel.length)); const longestFileLength = Math.max( ...assets.map((a) => (a.folder + path.sep + a.name).length), ); if (config.detail !== false) { - printHeader(longestFileLength, longestLabelLength); + logs.push(getHeader(longestFileLength, longestLabelLength, environment)); } let totalSize = 0; @@ -174,7 +171,7 @@ async function printFileSizes( fileNameLabel += rightPadding; } - logger.log(` ${fileNameLabel} ${sizeLabel} ${gzipSizeLabel}`); + logs.push(` ${fileNameLabel} ${sizeLabel} ${gzipSizeLabel}`); } } @@ -185,8 +182,10 @@ async function printFileSizes( const gzippedSizeLabel = `${color.bold( color.blue('Gzipped size:'), )} ${calcFileSize(totalGzipSize)}`; - logger.log(`\n ${totalSizeLabel}\n ${gzippedSizeLabel}\n`); + logs.push(`\n ${totalSizeLabel}\n ${gzippedSizeLabel}\n`); } + + return logs; } export const pluginFileSize = (): RsbuildPlugin => ({ @@ -194,30 +193,50 @@ export const pluginFileSize = (): RsbuildPlugin => ({ setup(api) { api.onAfterBuild(async ({ stats }) => { - const { printFileSize } = api.getNormalizedConfig().performance; - - if (printFileSize === false) { + if (!stats) { return; } - const printFileSizeConfig = - typeof printFileSize === 'boolean' - ? { - total: true, - detail: true, + const logs = await Promise.all( + Object.keys(api.context.environments).map( + async (environment, index) => { + const { printFileSize } = api.getNormalizedConfig({ + environment, + }).performance; + + const multiStats = 'stats' in stats ? stats.stats : [stats]; + + const printFileSizeConfig = + typeof printFileSize === 'boolean' + ? { + total: true, + detail: true, + } + : printFileSize; + + if (printFileSize) { + return printFileSizes( + printFileSizeConfig, + multiStats[index], + api.context.rootPath, + environment, + ); } - : printFileSize; - - if (stats) { - try { - await printFileSizes( - printFileSizeConfig, - stats, - api.context.rootPath, - ); - } catch (err) { - logger.warn('Failed to print file size.'); - logger.warn(err as Error); + return []; + }, + ), + ).catch((err) => { + logger.warn('Failed to print file size.'); + logger.warn(err as Error); + return []; + }); + + if (logs.filter((log) => log.length).length) { + logger.info('Production file sizes:\n'); + for (const statsLog of logs) { + for (const log of statsLog) { + logger.log(log); + } } } });