| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160 | "use strict";const fs = require('fs');const path = require('path');const _ = require('lodash');const gzipSize = require('gzip-size');const Logger = require('./Logger');const Folder = require('./tree/Folder').default;const {  parseBundle} = require('./parseUtils');const {  createAssetsFilter} = require('./utils');const FILENAME_QUERY_REGEXP = /\?.*$/u;const FILENAME_EXTENSIONS = /\.(js|mjs)$/iu;module.exports = {  getViewerData,  readStatsFromFile};function getViewerData(bundleStats, bundleDir, opts) {  const {    logger = new Logger(),    excludeAssets = null  } = opts || {};  const isAssetIncluded = createAssetsFilter(excludeAssets); // Sometimes all the information is located in `children` array (e.g. problem in #10)  if (_.isEmpty(bundleStats.assets) && !_.isEmpty(bundleStats.children)) {    const {      children    } = bundleStats;    bundleStats = bundleStats.children[0]; // Sometimes if there are additional child chunks produced add them as child assets,    // leave the 1st one as that is considered the 'root' asset.    for (let i = 1; i < children.length; i++) {      bundleStats.children[i].assets.forEach(asset => {        asset.isChild = true;        bundleStats.assets.push(asset);      });    }  } else if (!_.isEmpty(bundleStats.children)) {    // Sometimes if there are additional child chunks produced add them as child assets    bundleStats.children.forEach(child => {      child.assets.forEach(asset => {        asset.isChild = true;        bundleStats.assets.push(asset);      });    });  } // Picking only `*.js or *.mjs` assets from bundle that has non-empty `chunks` array  bundleStats.assets = _.filter(bundleStats.assets, asset => {    // Removing query part from filename (yes, somebody uses it for some reason and Webpack supports it)    // See #22    asset.name = asset.name.replace(FILENAME_QUERY_REGEXP, '');    return FILENAME_EXTENSIONS.test(asset.name) && !_.isEmpty(asset.chunks) && isAssetIncluded(asset.name);  }); // Trying to parse bundle assets and get real module sizes if `bundleDir` is provided  let bundlesSources = null;  let parsedModules = null;  if (bundleDir) {    bundlesSources = {};    parsedModules = {};    for (const statAsset of bundleStats.assets) {      const assetFile = path.join(bundleDir, statAsset.name);      let bundleInfo;      try {        bundleInfo = parseBundle(assetFile);      } catch (err) {        const msg = err.code === 'ENOENT' ? 'no such file' : err.message;        logger.warn(`Error parsing bundle asset "${assetFile}": ${msg}`);        continue;      }      bundlesSources[statAsset.name] = bundleInfo.src;      _.assign(parsedModules, bundleInfo.modules);    }    if (_.isEmpty(bundlesSources)) {      bundlesSources = null;      parsedModules = null;      logger.warn('\nNo bundles were parsed. Analyzer will show only original module sizes from stats file.\n');    }  }  const assets = _.transform(bundleStats.assets, (result, statAsset) => {    // If asset is a childAsset, then calculate appropriate bundle modules by looking through stats.children    const assetBundles = statAsset.isChild ? getChildAssetBundles(bundleStats, statAsset.name) : bundleStats;    const modules = assetBundles ? getBundleModules(assetBundles) : [];    const asset = result[statAsset.name] = _.pick(statAsset, 'size');    if (bundlesSources && _.has(bundlesSources, statAsset.name)) {      asset.parsedSize = Buffer.byteLength(bundlesSources[statAsset.name]);      asset.gzipSize = gzipSize.sync(bundlesSources[statAsset.name]);    } // Picking modules from current bundle script    asset.modules = _(modules).filter(statModule => assetHasModule(statAsset, statModule)).each(statModule => {      if (parsedModules) {        statModule.parsedSrc = parsedModules[statModule.id];      }    });    asset.tree = createModulesTree(asset.modules);  }, {});  return _.transform(assets, (result, asset, filename) => {    result.push({      label: filename,      isAsset: true,      // Not using `asset.size` here provided by Webpack because it can be very confusing when `UglifyJsPlugin` is used.      // In this case all module sizes from stats file will represent unminified module sizes, but `asset.size` will      // be the size of minified bundle.      // Using `asset.size` only if current asset doesn't contain any modules (resulting size equals 0)      statSize: asset.tree.size || asset.size,      parsedSize: asset.parsedSize,      gzipSize: asset.gzipSize,      groups: _.invokeMap(asset.tree.children, 'toChartData')    });  }, []);}function readStatsFromFile(filename) {  return JSON.parse(fs.readFileSync(filename, 'utf8'));}function getChildAssetBundles(bundleStats, assetName) {  return _.find(bundleStats.children, c => _(c.assetsByChunkName).values().flatten().includes(assetName));}function getBundleModules(bundleStats) {  return _(bundleStats.chunks).map('modules').concat(bundleStats.modules).compact().flatten().uniqBy('id').value();}function assetHasModule(statAsset, statModule) {  // Checking if this module is the part of asset chunks  return _.some(statModule.chunks, moduleChunk => _.includes(statAsset.chunks, moduleChunk));}function createModulesTree(modules) {  const root = new Folder('.');  _.each(modules, module => root.addModule(module));  root.mergeNestedFolders();  return root;}
 |