rollup-plugin-webpack-stats
Version:
[](https://www.npmjs.com/package/rollup-plugin-webpack-stats) [](https://www.npmjs.com/package/rollup-plugin-webpack-stats)
250 lines (245 loc) • 7.91 kB
JavaScript
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
;