/* * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ import { calcAndFormatDeltaBytes, calcAndFormatDeltaNumber, calcAndFormatDeltaPercent, formatBytes, formatNumber, } from 'diagnostics-shared/format'; /** * rollup-plugin-visualizer が出力する `stats.json` のうち、ここで使う部分のみの型。 */ export type VisualizerReport = { nodeParts?: Record; nodeMetas?: Record; renderedLength: number; gzipLength: number; brotliLength: number; }>; options?: Record; }; type ModuleRow = { id: string; bundles: number; renderedLength: number; gzipLength: number; brotliLength: number; importedByCount: number; importedCount: number; }; type BundleRow = { id: string; modules: number; renderedLength: number; gzipLength: number; brotliLength: number; }; export function collectVisualizerReport(data: VisualizerReport) { const nodeParts = data.nodeParts ?? {}; const nodeMetas = Object.values(data.nodeMetas ?? {}); const moduleRows: ModuleRow[] = []; const bundleMap = new Map(); for (const meta of nodeMetas) { const row: ModuleRow = { id: meta.id, bundles: 0, renderedLength: 0, gzipLength: 0, brotliLength: 0, importedByCount: meta.importedBy?.length ?? 0, importedCount: meta.imported?.length ?? 0, }; for (const [bundleId, partUid] of Object.entries(meta.moduleParts ?? {})) { const part = nodeParts[partUid]; // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition if (part == null) continue; row.bundles += 1; row.renderedLength += part.renderedLength; row.gzipLength += part.gzipLength; row.brotliLength += part.brotliLength; const bundle = bundleMap.get(bundleId) ?? { id: bundleId, modules: 0, renderedLength: 0, gzipLength: 0, brotliLength: 0, }; bundle.modules += 1; bundle.renderedLength += part.renderedLength; bundle.gzipLength += part.gzipLength; bundle.brotliLength += part.brotliLength; bundleMap.set(bundleId, bundle); } // どのバンドルにも含まれないモジュール (tree-shake 済み等) は集計対象外 if (row.bundles > 0) { moduleRows.push(row); } } let staticImports = 0; let dynamicImports = 0; for (const meta of nodeMetas) { for (const imported of meta.imported ?? []) { if (imported.dynamic) { dynamicImports += 1; } else { staticImports += 1; } } } const bundleRows = [...bundleMap.values()].sort((a, b) => b.renderedLength - a.renderedLength); const hotModules = [...moduleRows].sort((a, b) => b.renderedLength - a.renderedLength); const totalRendered = moduleRows.reduce((sum, row) => sum + row.renderedLength, 0); const totalGzip = moduleRows.reduce((sum, row) => sum + row.gzipLength, 0); const totalBrotli = moduleRows.reduce((sum, row) => sum + row.brotliLength, 0); return { options: data.options ?? {}, summary: { bundles: bundleRows.length, modules: moduleRows.length, entries: nodeMetas.filter((meta) => meta.isEntry).length, externals: nodeMetas.filter((meta) => meta.isExternal).length, staticImports, dynamicImports, }, metrics: { renderedLength: totalRendered, gzipLength: totalGzip, brotliLength: totalBrotli, }, hotModules, }; } /** * NOTE: 以前はこの関数が `string[]` を返しており、呼び出し側の `[...].join('\n')` の * 要素として配列のまま埋め込まれていたため、テーブルがカンマ区切りの1行に潰れていた。 * 呼び出し側で意識しなくて済むよう、ここで文字列にして返す。 */ export function renderVisualizerSummaryTable(base: ReturnType, head: ReturnType) { const summary = [ 'bundles', 'modules', 'entries', //'externals', 'staticImports', 'dynamicImports', ] as const; const metrics = [ 'renderedLength', 'gzipLength', 'brotliLength', ] as const; return [ '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', ...summary.map((key) => ``), ...metrics.map((key) => ``), '', '', '', ...summary.map((key) => ``), ...metrics.map((key) => ``), '', '', '', '', ...summary.map((key) => ``), ...metrics.map((key) => ``), '', '', '', ...summary.map((key) => ``), ...metrics.map((key) => ``), '', '', '
BundlesModulesEntriesImportsSize
StaticDynamicRenderedGzipBrotli
Base${formatNumber(base.summary[key])}${formatBytes(base.metrics[key])}
Head${formatNumber(head.summary[key])}${formatBytes(head.metrics[key])}
Δ${calcAndFormatDeltaNumber(base.summary[key], head.summary[key], 0)}${calcAndFormatDeltaBytes(base.metrics[key], head.metrics[key], 1000)}
Δ (%)${calcAndFormatDeltaPercent(base.summary[key], head.summary[key], 0.1)}${calcAndFormatDeltaPercent(base.metrics[key], head.metrics[key], 0.1)}
', ].join('\n'); }