UNPKG

postcss-prune-var

Version:
86 lines (73 loc) 2.43 kB
const {minimatch} = require("minimatch"); const path = require("path"); /** * @typedef {object} UseRecord * @property {number} uses * @property {Set<any>} declarations * @property {Set<string>} dependencies */ module.exports = (options = {}) => { return { postcssPlugin: 'postcss-prune-var', Once(root) { const {skip = []} = options; if (skip.some((s) => minimatch(path.relative(process.cwd(), root.source.input.from), s, {dot: true}))) { return; } /** @type Map<string, UseRecord> */ const records = new Map(); /** @type Set<string> */ const usedVars = new Set(); /** @type {(variable: string) => UseRecord} */ const getRecord = (variable) => { let record = records.get(variable); if (!record) { record = {uses: 0, dependencies: new Set(), declarations: new Set()}; records.set(variable, record); } return record; }; /** @type {(variable: string, ignoreList?: Set<any>) => void} */ const registerUse = (variable, ignoreList = new Set()) => { const record = getRecord(variable); record.uses++; ignoreList.add(variable); for (const dependency of record.dependencies) { if (!ignoreList.has(dependency)) registerUse(dependency, ignoreList); } }; /** @type {(variable: string, dependency: string) => void} */ const registerDependency = (variable, dependency) => { const record = getRecord(variable); record.dependencies.add(dependency); }; // Detect variable uses root.walkDecls((decl) => { const isVar = decl.prop.startsWith('--'); // Initiate record if (isVar) getRecord(decl.prop).declarations.add(decl); if (!decl.value.includes('var(')) return; for (const match of decl.value.matchAll(/var\(\s*(?<name>--[^ ,\);]+)/g)) { const variable = match.groups.name.trim(); if (isVar) { registerDependency(decl.prop, variable); } else { usedVars.add(variable); } } }); // We register variable uses only after all variables have been entered into the graph, // otherwise we remove variables that were defined in CSS file after some property used them. for (const variable of usedVars) { registerUse(variable); } // Remove unused variables for (const {uses, declarations} of records.values()) { if (uses === 0) { for (let decl of declarations) decl.remove(); } } }, }; }; module.exports.postcss = true