const getModulePath = function (identifier) { return identifier.replace(/.*!/, '').replace(/\\/g, '/') } exports.filterModules = function (modules) { return modules.filter(module => module.name.indexOf('(webpack)') === -1) } exports.buildSortedModules = function (modules, sizeField) { let list = modules.slice() if (list.length) { list = list.map(module => { const size = module.size[sizeField] return { id: module.id, identifier: module.identifier, size } }) list.sort((a, b) => b.size - a.size) } return list } exports.buildDepModules = function (modules) { const deps = new Map() for (const module of modules) { const path = getModulePath(module.identifier) const pathParts = path.split('/node_modules/') if (pathParts.length === 2) { let name = pathParts[1] if (name.charAt(0) === '@') { // Scoped package name = name.substr(0, name.indexOf('/', name.indexOf('/') + 1)) } else { name = name.substr(0, name.indexOf('/')) } let dep = deps.get(name) if (!dep) { dep = { name, size: 0 } deps.set(name, dep) } dep.size += module.size } } const list = Array.from(deps.values()) list.sort((a, b) => b.size - a.size) if (list.length) { const max = list[0].size for (const dep of list) { dep.ratio = dep.size / max } } return list } /* { id: './node_modules', size: { stats: 1024, parsed: 0, gzip: 400 } fullPath: '/node_modules', children: [ { id: 'vuex', identifier: '...', size: { stats: 42, parsed: 0, gzip: 12 }, // Total size of previous children in list previousSize: { stats: 0, parsed: 0, gzip: 0 }, fullPath: '/node_modules/vuex', children: [ ... ], }, ... ] } */ exports.buildModulesTrees = function (modules, sizeType) { const trees = {} for (const module of modules) { const path = getModulePath(module.identifier) if (path.indexOf('multi ') === 0) continue const parts = path.split('/') for (const treeId of module.chunks) { let subtree = trees[treeId] if (!subtree) { subtree = trees[treeId] = { id: treeId, size: { stats: 0, parsed: 0, gzip: 0 }, children: {} } } const fullPath = [] for (let i = 0; i < parts.length; i++) { const part = parts[i] let child = subtree.children[part] if (!child) { fullPath.push(part) child = subtree.children[part] = { id: part, size: { stats: 0, parsed: 0, gzip: 0 }, fullPath: fullPath.join('/'), children: {} } } child.size.stats += module.size.stats child.size.parsed += module.size.parsed || 0 child.size.gzip += module.size.gzip || 0 // Leaf if (i === parts.length - 1) { child.identifier = module.identifier } subtree = child } } } for (const n in trees) { let tree = trees[n] let keys while ((keys = Object.keys(tree.children)).length !== 0 && keys.length === 1) { tree = tree.children[keys[0]] } walkTreeToSortChildren(tree, sizeType) trees[n] = tree } return trees } function walkTreeToSortChildren (tree, sizeType) { const size = { stats: 0, parsed: 0, gzip: 0 } tree.children = Object.keys(tree.children).map( key => tree.children[key] ).filter( child => child.size.stats > tree.size.stats * 0.01 && child.size.stats > 1024 ).sort((a, b) => b.size[sizeType] - a.size[sizeType]) for (const child of tree.children) { child.previousSize = { stats: size.stats, parsed: size.parsed, gzip: size.gzip } size.stats += child.size.stats size.parsed += child.size.parsed size.gzip += child.size.gzip walkTreeToSortChildren(child, sizeType) } }