UNPKG

vite-plugin-bundlesize

Version:

Monitor bundle sizes and analyze bundle contents

122 lines 5.78 kB
import * as fs from "node:fs"; import path from "node:path"; import { URL } from "node:url"; import * as vlq from "vlq"; import analyze, { DEFAULT_LIMIT } from "../core/index.js"; import { FG_BLUE_33, FG_RED_197, measure, RESET } from "../core/lib.js"; export default function vitePluginBundlesize(options) { let config; // validate if (Array.isArray(options?.limits)) { for (let i = 0; i < options.limits.length; i++) { if (!options.limits[i].name || typeof options.limits[i].name !== "string") { console.error(`${FG_RED_197}✘ vite-plugin-bundlesize: limit ${i} missing required "name" property. ${RESET}`); process.exit(1); } if (typeof options.limits[i].limit !== "number" && typeof options.limits[i].limit !== "string") { console.error(`${FG_RED_197}✘ vite-plugin-bundlesize: limit ${i} missing required "limit" property. ${RESET}`); process.exit(1); } } } const resolvedOptions = { outputFile: new URL(options?.outputFile || "./bundlemeta.json", `file://${process.cwd()}/`), limits: options?.limits || [{ name: "*.js", limit: DEFAULT_LIMIT }], allowFail: options?.allowFail ?? false, stats: options?.stats ?? "summary", }; const { version } = JSON.parse(fs.readFileSync(new URL("../../package.json", import.meta.url), "utf8")); const bundlemeta = { chunks: {} }; return { // expose resolved config for `npx bundlesize` // biome-ignore lint/suspicious/noExplicitAny: this is just for setup ...{ _internalConfig: resolvedOptions }, name: "vite-plugin-bundlesize", apply: "build", // only run on build config(cfg) { config = cfg; if (!config.build?.sourcemap) { console.error(` ${FG_RED_197}✘ vite-plugin-bundlesize: needs "build.sourcemap" enabled.${RESET} See ${FG_BLUE_33}https://vitejs.dev/config/build-options.html#build-sourcemap${RESET}`); process.exit(1); } }, generateBundle(_, bundle) { for (const [chunkID, chunk] of Object.entries(bundle)) { if (chunk.type !== "chunk" || !chunk.isEntry) { continue; } if (!chunk.map) { console.error(` ${FG_RED_197}✘ vite-plugin-bundlesize: needs "build.sourcemap" enabled.${RESET} See ${FG_BLUE_33}https://vitejs.dev/config/build-options.html#build-sourcemap${RESET}`); process.exit(1); } const codeLines = chunk.code.split("\n"); const contents = {}; const mapLines = chunk.map.mappings.split(";"); const fields = [0, 0, 0, 0, 0]; for (let l = 0; l < mapLines.length; l++) { if (!mapLines[l]) { continue; } fields[0] = 0; // reset column every line const cols = mapLines[l].split(","); for (let c = 0; c < cols.length; c++) { if (!cols[c]) { continue; } // get current values from modifying previous values, per the spec const nextFields = vlq.decode(cols[c]); for (let i = 0; i < nextFields.length; i++) { if (typeof nextFields[i] === "number") { fields[i] += nextFields[i]; } } const [col, filePathI] = fields; const filePath = chunk.map.sources[filePathI]; if (!(filePath in contents)) { contents[filePath] = { filePath, size: 0 }; } // size const [nextCol] = cols[c + 1] ? vlq.decode(cols[c + 1]) : [undefined]; const fieldCode = codeLines[l].substring(col, nextCol ? col + nextCol : undefined); const fieldSize = measure(fieldCode); contents[filePath].size += fieldSize; // mark packageName const parts = filePath.split(path.sep); if (parts.indexOf("node_modules") !== -1) { let packageNameI = parts.indexOf("node_modules") + 1; if (parts[packageNameI] === ".pnpm") { packageNameI++; } let packageName = parts[packageNameI]; if (packageName[0] === "@") { packageName = `${packageName}/${parts[packageNameI + 2]}`; } contents[filePath].packageName = packageName; } } } bundlemeta.chunks[chunkID] = { id: chunkID, isEntry: true, size: measure(chunk.code), sizeGzip: measure(chunk.code, "gzip"), sizeBrotli: measure(chunk.code, "brotli"), contents, }; } fs.writeFileSync(resolvedOptions.outputFile, JSON.stringify(bundlemeta)); }, closeBundle() { analyze({ bundlemeta, config: resolvedOptions, version }); }, }; } //# sourceMappingURL=index.js.map