UNPKG

webpack-bundle-analyzer

Version:

Webpack plugin and CLI utility that represents bundle content as convenient interactive zoomable treemap

142 lines (137 loc) 5.55 kB
#! /usr/bin/env node "use strict"; const { dirname, resolve } = require("node:path"); const { program: commanderProgram } = require("commander"); const { magenta } = require("picocolors"); const Logger = require("../Logger"); const analyzer = require("../analyzer"); const { isZstdSupported } = require("../sizeUtils"); const utils = require("../utils"); const viewer = require("../viewer"); const SIZES = new Set(["stat", "parsed", "gzip"]); const COMPRESSION_ALGORITHMS = new Set(isZstdSupported ? ["gzip", "brotli", "zstd"] : ["gzip", "brotli"]); /** * @param {string} str string * @returns {string} break with string */ function br(str) { return `\n${" ".repeat(32)}${str}`; } /** * @template T * @returns {(val: T) => T[]} array */ function array() { /** @type {T[]} */ const arr = []; return val => { arr.push(val); return arr; }; } const program = commanderProgram.version(require("../../package.json").version).argument("<bundleStatsFile>", "Path to Webpack Stats JSON file.").argument("[bundleDir]", "Directory containing all generated bundles. You should provided it if you want analyzer to show you the real parsed module sizes. By default a directory of stats file is used.").option("-m, --mode <mode>", `Analyzer mode. Should be \`server\`,\`static\` or \`json\`.${br("In `server` mode analyzer will start HTTP server to show bundle report.")}${br("In `static` mode single HTML file with bundle report will be generated.")}${br("In `json` mode single JSON file with bundle report will be generated.")}`, "server").option( // Had to make `host` parameter optional in order to let `-h` flag output help message // Fixes https://github.com/webpack/webpack-bundle-analyzer/issues/239 "-h, --host [host]", "Host that will be used in `server` mode to start HTTP server.", "127.0.0.1").option("-p, --port <n>", "Port that will be used in `server` mode to start HTTP server.", "8888").option("-r, --report <file>", "Path to bundle report file that will be generated in `static` mode.").option("-t, --title <title>", "String to use in title element of html report.").option("-s, --default-sizes <type>", `Module sizes to show in treemap by default.${br(`Possible values: ${[...SIZES].join(", ")}`)}`, "parsed").option("--compression-algorithm <type>", `Compression algorithm that will be used to calculate the compressed module sizes.${br(`Possible values: ${[...COMPRESSION_ALGORITHMS].join(", ")}`)}`, "gzip").option("-O, --no-open", "Don't open report in default browser automatically.").option("-e, --exclude <regexp>", `Assets that should be excluded from the report.${br("Can be specified multiple times.")}`, array()).option("-l, --log-level <level>", `Log level.${br(`Possible values: ${[...Logger.levels].join(", ")}`)}`, Logger.defaultLevel).parse(); let [bundleStatsFile, bundleDir] = program.args; let { mode, host, port, report: reportFilename, title: reportTitle, defaultSizes, compressionAlgorithm, logLevel, open: openBrowser, exclude: excludeAssets } = program.opts(); const logger = new Logger(logLevel); if (typeof reportTitle === "undefined") { reportTitle = utils.defaultTitle; } /** * @param {string} error error message */ function showHelp(error) { if (error) console.log(`\n ${magenta(error)}\n`); program.outputHelp(); process.exit(1); } if (!bundleStatsFile) { showHelp("Provide path to Webpack Stats file as first argument"); } if (mode !== "server" && mode !== "static" && mode !== "json") { showHelp("Invalid mode. Should be either `server`, `static` or `json`."); } if (mode === "server") { if (!host) showHelp("Invalid host name"); port = port === "auto" ? 0 : Number(port); if (Number.isNaN(port)) { showHelp("Invalid port. Should be a number or `auto`"); } } if (!COMPRESSION_ALGORITHMS.has(compressionAlgorithm)) { showHelp(`Invalid compression algorithm option. Possible values are: ${[...COMPRESSION_ALGORITHMS].join(", ")}`); } if (!SIZES.has(defaultSizes)) { showHelp(`Invalid default sizes option. Possible values are: ${[...SIZES].join(", ")}`); } bundleStatsFile = resolve(bundleStatsFile); if (!bundleDir) bundleDir = dirname(bundleStatsFile); /** * @param {string} bundleStatsFile bundle stats file * @returns {Promise<void>} */ async function parseAndAnalyse(bundleStatsFile) { try { const bundleStats = await analyzer.readStatsFromFile(bundleStatsFile); if (mode === "server") { viewer.startServer(bundleStats, { openBrowser, port, host, defaultSizes, compressionAlgorithm, reportTitle, bundleDir, excludeAssets, logger: new Logger(logLevel), analyzerUrl: utils.defaultAnalyzerUrl }); } else if (mode === "static") { viewer.generateReport(bundleStats, { openBrowser, reportFilename: resolve(reportFilename || "report.html"), reportTitle, defaultSizes, compressionAlgorithm, bundleDir, excludeAssets, logger: new Logger(logLevel) }); } else if (mode === "json") { viewer.generateJSONReport(bundleStats, { reportFilename: resolve(reportFilename || "report.json"), compressionAlgorithm, bundleDir, excludeAssets, logger: new Logger(logLevel) }); } } catch (err) { logger.error(`Couldn't read webpack bundle stats from "${bundleStatsFile}":\n${err}`); logger.debug(/** @type {Error} */err.stack); process.exit(1); } } parseAndAnalyse(bundleStatsFile);