whybundled
Advanced tools
Comparing version 1.1.0 to 1.2.0
@@ -6,2 +6,3 @@ #!/usr/bin/env node | ||
const chalk = require("chalk"); | ||
const createProgressBar = require("./lib/console/progress-bar"); | ||
const defaultCommand = require("./commands/default"); | ||
@@ -56,6 +57,8 @@ const byCommand = require("./commands/by"); | ||
const updateProgressBar = createProgressBar(); | ||
if (flags.by) { | ||
byCommand(input[0], flags, input[1]); | ||
byCommand(input[0], flags, input[1], updateProgressBar); | ||
} else { | ||
defaultCommand(input[0], flags, input[1]); | ||
defaultCommand(input[0], flags, input[1], updateProgressBar); | ||
} | ||
@@ -67,1 +70,2 @@ | ||
console.log(`🏁 Done in ${rounded}s.`); | ||
process.exit(0); |
/* @flow */ | ||
/*:: | ||
import type { UpdateProgressBar } from '../lib/console/progress-bar'; | ||
*/ | ||
const { analyze, print, getStats } = require("../lib"); | ||
const validate = require("../lib/validate"); | ||
const { log, invalidStatsJson } = require("../lib/messages"); | ||
const { log, invalidStatsJson } = require("../lib/console/messages"); | ||
@@ -10,3 +14,4 @@ module.exports = function byCommand( | ||
flags /*: { limit: number, by: string, ignore?: string } */, | ||
pattern /*: string */ | ||
pattern /*: string */, | ||
updateProgressBar /*: UpdateProgressBar */ = () => {} | ||
) { | ||
@@ -21,4 +26,6 @@ const stats = getStats(statsFilePath); | ||
const ignore = flags.ignore ? flags.ignore.split(",") : []; | ||
const report = analyze(stats, ignore).filter(mod => { | ||
return ( | ||
const report = analyze(stats, ignore, updateProgressBar); | ||
const modules = report.modules.filter( | ||
mod => | ||
mod.reasons.some( | ||
@@ -28,6 +35,6 @@ reason => | ||
) || (mod.depsChains || []).some(deps => deps.indexOf(flags.by) !== -1) | ||
); | ||
}); | ||
); | ||
const limit /*: number */ = pattern ? 0 : flags.limit >= 0 ? flags.limit : 20; | ||
print(report, { by: flags.by }, limit); | ||
print(modules, report.chunks, { by: flags.by }, limit); | ||
}; |
@@ -6,5 +6,7 @@ /* @flow */ | ||
const validate = require("../lib/validate"); | ||
const { log, invalidStatsJson } = require("../lib/messages"); | ||
const { log, invalidStatsJson } = require("../lib/console/messages"); | ||
/*:: | ||
import type { UpdateProgressBar } from '../lib/console/progress-bar'; | ||
type Flags = { | ||
@@ -25,3 +27,4 @@ limit: number, | ||
flags /*: Flags */, | ||
pattern /*: string*/ | ||
pattern /*: string*/, | ||
updateProgressBar /*: UpdateProgressBar */ = () => {} | ||
) { | ||
@@ -36,3 +39,4 @@ const stats = getStats(statsFilePath); | ||
const ignore = flags.ignore ? flags.ignore.split(",") : []; | ||
const report = analyze(stats, ignore).filter(module => { | ||
const report = analyze(stats, ignore, updateProgressBar); | ||
const modules = report.modules.filter(module => { | ||
if (pattern && mm.isMatch(module.name, pattern)) { | ||
@@ -56,3 +60,3 @@ return true; | ||
const limit = pattern ? 0 : flags.limit >= 0 ? flags.limit : 20; | ||
print(report, flags, limit); | ||
print(modules, report.chunks, flags, limit); | ||
}; |
{ | ||
"chunks": [ | ||
{ | ||
"id": 1, | ||
"names": [], | ||
"size": 0, | ||
"modules": [ | ||
@@ -5,0 +8,0 @@ { |
@@ -17,3 +17,9 @@ // flow-typed signature: a71a6e955d88c74dfbccba23202e0f98 | ||
declare module "micromatch" { | ||
declare module.exports: any; | ||
declare module.exports: { | ||
isMatch: ( | ||
what: string, | ||
patterns: Array<string> | string, | ||
options?: Object | ||
) => boolean | ||
}; | ||
} | ||
@@ -20,0 +26,0 @@ |
@@ -20,4 +20,41 @@ const stripAnsi = require("strip-ansi"); | ||
const logger = createPrint(); | ||
print(stats, {}, 0, logger); | ||
print(stats.modules, stats.chunks, {}, 0, logger); | ||
t.snapshot(logger()); | ||
}); | ||
test("should properly print simple stats.json", t => { | ||
const stats = analyze(getStats(f.find("example-simple-stats.json"))); | ||
const logger = createPrint(); | ||
print(stats.modules, stats.chunks, {}, 0, logger); | ||
t.snapshot(logger()); | ||
}); | ||
test("should properly print multi entry stats.json", t => { | ||
const stats = analyze(getStats(f.find("multi-entry-stats.json"))); | ||
const logger = createPrint(); | ||
print(stats.modules, stats.chunks, {}, 0, logger); | ||
t.snapshot(logger()); | ||
}); | ||
test("should properly print multi entry stats.json with dynamic import", t => { | ||
const stats = analyze( | ||
getStats(f.find("multi-entry-dynamic-import-stats.json")) | ||
); | ||
const logger = createPrint(); | ||
print(stats.modules, stats.chunks, {}, 0, logger); | ||
t.snapshot(logger()); | ||
}); | ||
test("should properly print multi entry stats.json with no chunks information", t => { | ||
const stats = analyze(getStats(f.find("multi-entry-no-chunks-stats.json"))); | ||
const logger = createPrint(); | ||
print(stats.modules, stats.chunks, {}, 0, logger); | ||
t.snapshot(logger()); | ||
}); | ||
test("should properly print stats.json with nested children", t => { | ||
const stats = analyze(getStats(f.find("nested-children-stats.json"))); | ||
const logger = createPrint(); | ||
print(stats.modules, stats.chunks, {}, 0, logger); | ||
t.snapshot(logger()); | ||
}); |
@@ -21,1 +21,6 @@ const test = require("ava"); | ||
}); | ||
test("should return true for a valid stats file with children", t => { | ||
const stats = getStats(f.find("valid-with-children.json")); | ||
t.truthy(validate(stats)); | ||
}); |
/* @flow */ | ||
/*:: | ||
import type { UpdateProgressBar } from "./console/progress-bar"; | ||
export type WebpackStats = { | ||
chunks?: Array<WebpackChunk>, | ||
modules: Array<WebpackModule> | ||
modules: Array<WebpackModule>, | ||
children?: Array<WebpackStats> | ||
}; | ||
export type WebpackChunk = { | ||
id: number, | ||
size: number, | ||
names: Array<string>, | ||
modules: Array<WebpackModule> | ||
@@ -16,2 +23,3 @@ }; | ||
size: string, | ||
chunks: Array<number>, | ||
reasons: Array<WebpackReason> | ||
@@ -31,2 +39,3 @@ }; | ||
clearName: string, | ||
chunks: Array<number>, | ||
type: string, | ||
@@ -40,2 +49,4 @@ reasons: Array<WebpackReason>, | ||
name: string, | ||
size: number, | ||
chunks: Array<number>, | ||
clearName: string, | ||
@@ -67,2 +78,9 @@ imported: number, | ||
export type Chunks = { | ||
[key: number]: { | ||
id: number, | ||
size: number, | ||
names: Array<string> | ||
} | ||
} | ||
*/ | ||
@@ -97,4 +115,6 @@ | ||
const getModuleType = (name /*: string */) => | ||
name.startsWith("multi ") ? "entry" : isNodeModules(name) ? "module" : "file"; | ||
const getModuleType = (name /*?: string */) => | ||
!name || name.startsWith("multi ") | ||
? "entry" | ||
: isNodeModules(name) ? "module" : "file"; | ||
@@ -109,2 +129,4 @@ const flattenChunks = (stats /*: WebpackStats */) /*: Array<WebpackModule> */ => | ||
const safeModuleSize = (size /*?: number */) => (size ? size : 0); | ||
const joinModules = (modules) /*: { [string]: Module } */ => | ||
@@ -115,2 +137,4 @@ modules.reduce((acc, module /*: PreModule */) => { | ||
type: module.type, | ||
size: safeModuleSize(module.size), | ||
chunks: module.chunks, | ||
imported: 0, | ||
@@ -136,4 +160,6 @@ reasons: [], | ||
type: module.type, | ||
size: 0, | ||
depsType: "unknown", | ||
reasons: [], | ||
chunks: module.chunks, | ||
locations: [], | ||
@@ -199,2 +225,4 @@ filesIncluded: [], | ||
joined.filesIncluded.push(module.name); | ||
joined.size += safeModuleSize(module.size); | ||
joined.chunks = Array.from(new Set(joined.chunks.concat(module.chunks))); | ||
@@ -216,2 +244,3 @@ acc[module.clearName] = joined; | ||
location: clearName ? getLocation(module.name, clearName) : "", | ||
chunks: module.chunks, | ||
size: module.size, | ||
@@ -246,4 +275,6 @@ reasons: module.reasons | ||
const postProcessModules = (modules, ignore) => { | ||
return Object.keys(modules).reduce((acc, name) => { | ||
const postProcessModules = (modules, ignore, updateProgressBar) => { | ||
return Object.keys(modules).reduce((acc, name, index) => { | ||
updateProgressBar(index + 1, "processing", name); | ||
if (mm.isMatch(name, ignore)) { | ||
@@ -267,3 +298,3 @@ return acc; | ||
module.reasons = module.reasons.filter( | ||
reason => !mm.isMatch(reason.clearName || reason.moduleName, ignore) | ||
reason => !mm.isMatch(reason.clearName || reason.moduleName || "", ignore) | ||
); | ||
@@ -278,10 +309,73 @@ | ||
function joinStats(children) { | ||
const flattenChildren = (children, id = "0") => | ||
children.reduce((acc, child, index) => { | ||
child.id = `${id}.${index}`; | ||
acc = acc.concat(child); | ||
acc = acc.concat(flattenChildren(child.children || [], child.id)); | ||
}, []); | ||
return children.reduce( | ||
(acc, child) => { | ||
acc.chunks = child.chunks ? acc.chunks.concat(child.chunks) : acc.chunks; | ||
acc.modules = child.modules | ||
? acc.modules.concat(child.modules) | ||
: acc.modules; | ||
return acc; | ||
}, | ||
{ chunks: [], modules: [] } | ||
); | ||
} | ||
function getChunksData(stats /*: WebpackStats */) { | ||
return stats.chunks | ||
? stats.chunks.reduce((acc, chunk) => { | ||
acc[chunk.id] = { | ||
id: chunk.id, | ||
names: chunk.names, | ||
size: chunk.size | ||
}; | ||
return acc; | ||
}, {}) | ||
: {}; | ||
} | ||
module.exports = function analyze( | ||
stats /*: WebpackStats */, | ||
ignore /*: Array<string> */ = [] | ||
rawStats /*: WebpackStats */, | ||
ignore /*: Array<string> */ = [], | ||
updateProgressBar /*: UpdateProgressBar */ = () => {} | ||
) { | ||
const stats = | ||
rawStats.children && | ||
rawStats.children.length && | ||
(!rawStats.chunks || !rawStats.chunks.length) | ||
? joinStats(rawStats.children) | ||
: rawStats; | ||
const chunks = getChunksData(stats); | ||
const rawModules = flattenChunks(stats); | ||
const ignorePatterns = [].concat(DEFAULT_IGNORE).concat(ignore); | ||
const modules = pickFromModules(rawModules); | ||
return toArray(postProcessModules(joinModules(modules), ignorePatterns)); | ||
const joinedModules = joinModules(modules); | ||
const joinedModulesCount = Object.keys(joinedModules).length; | ||
const updateProgressBarWithTotal = ( | ||
cur /*: number */, | ||
title /*: string */, | ||
name /*: string */ | ||
) => | ||
updateProgressBar({ | ||
title, | ||
text: name, | ||
progress: Math.ceil(cur / joinedModulesCount * 100) | ||
}); | ||
return { | ||
modules: toArray( | ||
postProcessModules( | ||
joinedModules, | ||
ignorePatterns, | ||
updateProgressBarWithTotal | ||
) | ||
), | ||
chunks | ||
}; | ||
}; |
@@ -15,3 +15,3 @@ /* @flow */ | ||
statsFilePath /*: string */ | ||
) /*?: WebpackStats */ { | ||
) /*: WebpackStats */ { | ||
try { | ||
@@ -23,3 +23,3 @@ // $FlowFixMe | ||
try { | ||
return JSON.parse(cleanContent); | ||
return JSON.parse(cleanContent) /* as WebpackStats */; | ||
} catch (e) { | ||
@@ -26,0 +26,0 @@ throw new Error(`Stats file "${statsFilePath}" is not a valid json...`); |
@@ -7,3 +7,3 @@ /* @flow */ | ||
/*:: | ||
import type { Module, Reason, SubReason } from './analyze'; | ||
import type { Module, Reason, SubReason, Chunks } from './analyze'; | ||
*/ | ||
@@ -23,2 +23,5 @@ | ||
const isEntry = (reasons /*: Array<Reason>*/) => | ||
reasons.length === 1 && reasons[0].type === "entry"; | ||
const takeSubset = (array /*: Array<any> */, limit) => | ||
@@ -159,16 +162,65 @@ limit === 0 ? array : array.slice(0, limit); | ||
const printFile = (module /*: Module */, limit, by) => { | ||
const printSize = (size /*: number */, mod = false) => { | ||
const sizeInKiB = Math.ceil(size / 1024); | ||
const level = | ||
sizeInKiB === 0 | ||
? "unknown" | ||
: sizeInKiB > 20 | ||
? "red" | ||
: sizeInKiB < 20 && sizeInKiB > 10 ? "yellow" : "default"; | ||
const sizeFormatted = | ||
level === "unknown" | ||
? chalk.dim("unknown") | ||
: level === "default" | ||
? sizeInKiB + " KiB" | ||
: chalk[level](sizeInKiB + " KiB"); | ||
return `${chalk.magenta("size")}: ${sizeFormatted}${ | ||
mod ? chalk.dim(" [for all included files]") : "" | ||
}`; | ||
}; | ||
const printChunkInfo = (module /*: Module */, chunks /*: Chunks */) => { | ||
return module.chunks.reduce((acc, chunkId) => { | ||
const chunk = chunks[chunkId]; | ||
if (!chunk) return acc; | ||
acc = | ||
chunk.names && chunk.names.length | ||
? acc.concat(chunk.names) | ||
: acc.concat(chunk.id); | ||
return acc; | ||
}, []); | ||
}; | ||
const printFile = (module /*: Module */, chunks, limit, by) => { | ||
const chunksInfo = printChunkInfo(module, chunks); | ||
const entry = isEntry(module.reasons); | ||
return [ | ||
`${fileBadge()} ${chalk.green(module.name)}`, | ||
`${fileBadge()} ${chalk.green(module.name)}${ | ||
entry ? " " + entryPointBadge() : "" | ||
}`, | ||
`├─ ${chalk.magenta("imported")}: ${printImportedCount(module.imported)}`, | ||
`└─ ${chalk.magenta("reasons")}:`, | ||
indent(printReasons(module.reasons, limit, by), " ").join("\n") | ||
]; | ||
`├─ ${printSize(module.size)}`, | ||
chunksInfo && | ||
chunksInfo.length && | ||
`${entry ? "└─" : "├─"} ${chalk.magenta("chunks")}: ${chunksInfo.join( | ||
", " | ||
)}`, | ||
!entry && `└─ ${chalk.magenta("reasons")}:`, | ||
!entry && indent(printReasons(module.reasons, limit, by), " ").join("\n") | ||
].filter(msg => !!msg); | ||
}; | ||
const printModule = (module /*: Module */, limit, by) => { | ||
const printModule = (module /*: Module */, chunks, limit, by) => { | ||
const chunksInfo = printChunkInfo(module, chunks); | ||
const entry = isEntry(module.reasons); | ||
return [ | ||
`${moduleBadge()} ${chalk.yellow(module.name)}`, | ||
`${moduleBadge()} ${chalk.yellow(module.name)}${ | ||
entry ? " " + entryPointBadge() : "" | ||
}`, | ||
`├─ ${chalk.magenta("imported")}: ${printImportedCount(module.imported)}`, | ||
`├─ ${printSize(module.size, true)}`, | ||
printType(module, by, limit).join("\n"), | ||
chunksInfo && | ||
chunksInfo.length && | ||
`├─ ${chalk.magenta("chunks")}: ${chunksInfo.join(", ")}`, | ||
`├─ ${chalk.magenta("locations")}: ${ | ||
@@ -178,7 +230,7 @@ module.locations.length > 1 ? redBadge("multiple") : "" | ||
indent(printLocations(module.locations)).join("\n"), | ||
`├─ ${chalk.magenta("files")}: `, | ||
`${entry ? "└─" : "├─"} ${chalk.magenta("files")}: `, | ||
indent(printIncludedFiles(module.filesIncluded, limit)).join("\n"), | ||
`└─ ${chalk.magenta("reasons")}:`, | ||
indent(printReasons(module.reasons, limit, by), " ").join("\n") | ||
]; | ||
!entry && `└─ ${chalk.magenta("reasons")}:`, | ||
!entry && indent(printReasons(module.reasons, limit, by), " ").join("\n") | ||
].filter(msg => !!msg); | ||
}; | ||
@@ -188,2 +240,3 @@ | ||
report /*: Array<Module> */, | ||
chunks /*: Chunks */, | ||
flags /*: { by?: string } */, | ||
@@ -199,3 +252,3 @@ limit /*: number */, | ||
logger(); | ||
logger(printFile(module, limit, flags.by).join("\n")); | ||
logger(printFile(module, chunks, limit, flags.by).join("\n")); | ||
logger(chalk.dim("--------------------")); | ||
@@ -205,3 +258,3 @@ logger(); | ||
logger(); | ||
logger(printModule(module, limit, flags.by).join("\n")); | ||
logger(printModule(module, chunks, limit, flags.by).join("\n")); | ||
logger(chalk.dim("--------------------")); | ||
@@ -208,0 +261,0 @@ logger(); |
@@ -22,8 +22,27 @@ /* @flow */ | ||
!stats || | ||
((!stats.chunks || !stats.chunks[0].modules) && !stats.modules) | ||
((!stats.chunks || !stats.chunks[0].modules) && | ||
!stats.modules && | ||
(!stats.children || | ||
((!stats.children[0].chunks || !stats.children[0].chunks[0].modules) && | ||
!stats.children[0].modules))) | ||
) { | ||
return false; | ||
} | ||
const samples = (stats.modules || stats.chunks[0].modules).slice(0, 10); | ||
let modules = []; | ||
if (stats.modules) { | ||
modules = stats.modules; | ||
} else if (stats.chunks) { | ||
modules = stats.chunks[0].modules; | ||
} else if (stats.children) { | ||
if (stats.children[0].modules) { | ||
modules = stats.children[0].modules; | ||
} else if (stats.children[0].chunks) { | ||
modules = stats.children[0].chunks[0].modules; | ||
} | ||
} | ||
const samples = modules.slice(0, 10); | ||
return samples.some(isValidModule); | ||
}; |
{ | ||
"name": "whybundled", | ||
"version": "1.1.0", | ||
"version": "1.2.0", | ||
"description": "Answers the question – Why the hell is this module in a bundle?", | ||
@@ -5,0 +5,0 @@ "bin": "./cli.js", |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
14459815
47
38935