UNPKG

unplugin-unused

Version:
84 lines (82 loc) 2.87 kB
import { t as resolveOptions } from "./options-DJtl1Tg1.mjs"; import { readFile } from "node:fs/promises"; import { styleText } from "node:util"; import { up } from "empathic/package"; import escapeStringRegexp from "escape-string-regexp"; import jsTokens from "js-tokens"; import { createUnplugin } from "unplugin"; //#region src/index.ts const Unused = createUnplugin((rawOptions = {}) => { const options = resolveOptions(rawOptions); const depsRegex = {}; const depsState = /* @__PURE__ */ new WeakMap(); let pkgPath; const defaultKey = {}; function getBuildId(context) { const native = context.getNativeBuildContext?.(); if (!native) return defaultKey; if (native.framework === "esbuild") return native.build; if (native.framework === "rspack" || native.framework === "webpack") return native.compiler; if (native.framework === "farm") return native.context; return defaultKey; } return { name: "unplugin-unused", enforce: "pre", async buildStart() { const pkgJsonPath = up({ cwd: options.root }); if (!pkgJsonPath) throw new Error(`Cannot find package.json from root: ${options.root}`); pkgPath = pkgJsonPath; const pkg = JSON.parse(await readFile(pkgPath, "utf8")); const deps = /* @__PURE__ */ new Set(); for (const kind of options.depKinds) { const dependencies = Object.keys(pkg[kind] || {}); for (const dep of dependencies) { if ((Array.isArray(options.ignore) ? options.ignore : options.ignore[kind] || []).includes(dep) || deps.has(dep)) continue; deps.add(dep); depsRegex[dep] = new RegExp(String.raw`["']${escapeStringRegexp(dep)}['"\/]`); } } const buildId = getBuildId(this); depsState.set(buildId, deps); }, transform: { filter: { id: { include: options.include, exclude: options.exclude } }, handler(code, id) { const tokens = jsTokens(code, { jsx: /\.[jt]sx?$/.test(id) }); const deps = depsState.get(getBuildId(this)) || /* @__PURE__ */ new Set(); for (const { type, value } of tokens) { if (type.endsWith("Comment")) continue; for (const dep of deps) if (depsRegex[dep].test(value)) { deps.delete(dep); break; } } } }, buildEnd(...args) { if (!!args[0]) return; const id = getBuildId(this); const deps = depsState.get(id); if (deps?.size) { const message = `Unused ${styleText("cyan", String(deps.size))} dependencies found: \n\n${Array.from(deps).map((dep) => `- ${styleText("bold", dep)}`).join("\n")}\n\nYou can remove them from ${pkgPath}`; if (options.level === "error") throw new Error(String(styleText("red", message))); else { const error = new Error(styleText("yellow", message)); console.warn(error); } } }, vite: { apply: "build", configResolved(config) { options.root ||= config.root; } } }; }); //#endregion export { Unused };