UNPKG

rollup-plugin-webpack-stats

Version:

[![](https://img.shields.io/npm/v/rollup-plugin-webpack-stats.svg)](https://www.npmjs.com/package/rollup-plugin-webpack-stats) [![](https://img.shields.io/npm/dm/rollup-plugin-webpack-stats.svg)](https://www.npmjs.com/package/rollup-plugin-webpack-stats)

250 lines (245 loc) 7.91 kB
'use strict'; var path$1 = require('path'); var path = require('node:path'); var crypto = require('node:crypto'); const HASH_LENGTH = 7; /** * Get content byte size */ function getByteSize(content) { if (!content) { return 0; } if (typeof content === 'string') { return Buffer.byteLength(content); } return content?.length || 0; } /** * Generate a 7 chars hash from a filepath */ function getHash(filepath) { const digest = crypto.createHash('sha256'); return digest.update(filepath).digest('hex').substr(0, HASH_LENGTH); } function getChunkId(chunk) { let value = chunk.name; // Use entry module relative path if (chunk.moduleIds?.length > 0) { const absoluteModulePath = chunk.moduleIds[chunk.moduleIds.length - 1]; value = path.relative(process.cwd(), absoluteModulePath); } return getHash([chunk, value].join('-')); } function round(value, precision = 2) { const multiplier = 10 ^ precision; return Math.round(value * multiplier) / multiplier; } const FILE_SIZE = { BYTE: { symbol: 'B', multiplier: 1, }, KILO: { symbol: 'KiB', multiplier: 1024, }, MEGA: { symbol: 'MiB', multiplier: 1024 * 1024, }, }; function formatFileSize(value) { let unit = FILE_SIZE.BYTE; if (typeof value !== 'number') { return `0${unit.symbol}`; } if (value < FILE_SIZE.KILO.multiplier) { unit = FILE_SIZE.BYTE; } else if (value < FILE_SIZE.MEGA.multiplier) { unit = FILE_SIZE.KILO; } else { unit = FILE_SIZE.MEGA; } return `${round(value / unit.multiplier, 2)}${unit.symbol}`; } const DEFAULT_FILE_NAME = 'webpack-stats.json'; function resolveFilepath(fileName = DEFAULT_FILE_NAME, outputDir) { // Check if the fileName is an absolute path if (path.isAbsolute(fileName)) { return fileName; } // If the fileName is not an absolute path, join it with the output directory or the current working directory return path.join(outputDir || process.cwd(), fileName); } /** * Recursivily check if a chunk is async based on the chunks issuers */ const lookupChunkAsync = (chunksIssuers, chunk, processedChunks = []) => { // When the chunks are having a circular dependency, return true to continue the recursive check if (processedChunks.includes(chunk.fileName)) { return true; } if (chunk.isEntry) { return false; } if (chunk.isDynamicEntry) { return true; } const chunkIssuers = chunksIssuers[chunk.fileName]; /** * A sync chunk without issuer chunks, is sync */ if (!chunkIssuers) { return false; } const syncChunksIssuers = chunkIssuers.filter((chunkIssuer) => { return chunkIssuer.isDynamicEntry === false; }); /** * A sync chunk with all the chunk issuer async, is async */ if (syncChunksIssuers.length === 0) { return true; } /** * Recursively lookup for sync loads on the 2nd level issuers * - if at least one issuer is sync, the chunk is sync * - if none of the issuers are sync, the chunk is async */ let isAsync = true; for (let i = 0; i < syncChunksIssuers.length && isAsync; i++) { isAsync = lookupChunkAsync(chunksIssuers, syncChunksIssuers[i], [...processedChunks, chunk.fileName]); } return isAsync; }; /** * Store transformed sources */ class TransformSources { constructor() { this.entries = {}; } entries = {}; push(id, source) { this.entries[id] = source; } /** * Get asset source */ getByAsset = (asset) => { return this.entries[asset.name]; }; /** * Get chunk source */ getByChunk = (chunk) => { return this.entries[chunk.id]; }; /** * Get module source */ getByModule = (module) => { return this.entries[module.name]; }; } const defaultTransform = (stats) => stats; const bundleToWebpackStats = (bundle, pluginOptions) => { const options = { moduleOriginalSize: false, ...pluginOptions }; const { moduleOriginalSize, transform = defaultTransform } = options; const assets = []; const chunks = []; const moduleByFileName = {}; const sources = new TransformSources(); const chunksIssuers = {}; const entries = Object.values(bundle); // Collect metadata entries.forEach((entry) => { if (entry.type === 'chunk') { entry.imports?.forEach((chunkImportFileName) => { // Skip circular references if (chunksIssuers[entry.fileName]?.find((chunkIssuer) => chunkIssuer.fileName === chunkImportFileName)) { return; } if (!chunksIssuers[chunkImportFileName]) { chunksIssuers[chunkImportFileName] = []; } chunksIssuers[chunkImportFileName].push(entry); }); } }); // Process data entries.forEach((entry) => { if (entry.type === 'chunk') { assets.push({ name: entry.fileName, size: getByteSize(entry.code), }); sources.push(entry.fileName, entry); const chunkId = getChunkId(entry); const chunkAsync = lookupChunkAsync(chunksIssuers, entry); chunks.push({ id: chunkId, entry: entry.isEntry, initial: !chunkAsync, files: [entry.fileName], names: [entry.name], }); sources.push(chunkId, entry); Object.entries(entry.modules).forEach(([modulePath, moduleInfo]) => { // Remove unexpected rollup null prefix const normalizedModulePath = modulePath.replace('\u0000', ''); const relativeModulePath = path$1.relative(process.cwd(), normalizedModulePath); // Match webpack output - add current directory prefix for child modules const relativeModulePathWithPrefix = relativeModulePath.match(/^\.\./) ? relativeModulePath : `.${path$1.sep}${relativeModulePath}`; const moduleEntry = moduleByFileName[relativeModulePathWithPrefix]; if (moduleEntry) { moduleEntry.chunks.push(chunkId); } else { moduleByFileName[relativeModulePathWithPrefix] = { name: relativeModulePathWithPrefix, size: moduleOriginalSize ? moduleInfo.originalLength : moduleInfo.renderedLength, chunks: [chunkId], }; sources.push(relativeModulePathWithPrefix, { fileName: modulePath, ...moduleInfo }); } }); } else if (entry.type === 'asset') { assets.push({ name: entry.fileName, size: getByteSize(entry.source), }); sources.push(entry.fileName, entry); } else ; }); const stats = { builtAt: Date.now(), assets, chunks, modules: Object.values(moduleByFileName), }; let result; try { result = transform(stats, sources, bundle); } catch (error) { console.error('Custom transform failed! Returning stats without any transforms.', error); result = stats; } return result; }; exports.bundleToWebpackStats = bundleToWebpackStats; exports.formatFileSize = formatFileSize; exports.getByteSize = getByteSize; exports.lookupChunkAsync = lookupChunkAsync; exports.resolveFilepath = resolveFilepath; //# sourceMappingURL=transform.cjs.map