UNPKG

css-deadweigth

Version:

CLI tool to detect unused CSS in your project

107 lines (85 loc) • 3.52 kB
#!/usr/bin/env node import { program } from 'commander'; import chalk from 'chalk'; import { globby } from 'globby'; import path from 'path'; import chokidar from 'chokidar'; import { extractSelectors } from '../lib/parseCss.js'; import { extractUsedSelectors } from '../lib/scanSource.js'; import { findUnusedSelectors } from '../lib/compare.js'; import { loadConfig } from '../lib/config.js'; import { printUnusedSelectors, saveReport } from '../lib/reporter.js'; import { isIgnored } from '../lib/utils.js'; async function runScan(options) { const config = await loadConfig(); const ignoreSelectors = [...(config.ignoreSelectors || []), ...(options.ignore || [])]; const ignorePaths = (config.ignorePaths || []).map(p => `**/${p}/**`); const srcPath = path.resolve(process.cwd(), options.src); const cssPath = path.resolve(process.cwd(), options.css); const sourceFiles = await globby([`${srcPath}/**/*.{html,js,jsx,ts,tsx}`], { ignore: ignorePaths }); const cssFiles = await globby([`${cssPath}/**/*.css`], { ignore: ignorePaths }); console.log(chalk.green(`šŸ“ Scanning source: ${srcPath}`)); console.log(chalk.blue(`šŸŽØ Scanning CSS: ${cssPath}`)); console.log(chalk.yellow(`šŸ“¦ Found ${sourceFiles.length} source files.`)); console.log(chalk.yellow(`šŸ’… Found ${cssFiles.length} CSS files.`)); const allSelectors = await extractSelectors(cssFiles); const usedSelectors = await extractUsedSelectors(sourceFiles); let unused = findUnusedSelectors(allSelectors, usedSelectors); unused = unused.filter(sel => !isIgnored(sel, ignoreSelectors)); printUnusedSelectors(unused, allSelectors.size, allSelectors.size - unused.length); if (options.report) { await saveReport(unused, allSelectors.size, allSelectors.size - unused.length, options.report); } if (unused.length > 0) { console.log(chalk.red.bold('\nāŒ Unused CSS selectors found!')); if (!options.watch) process.exit(1); } else { console.log(chalk.green.bold('\nāœ… No unused CSS selectors found!')); if (!options.watch) process.exit(0); } } async function main() { program .version('1.0.0') .description('Detect unused CSS in your project') .requiredOption('--src <path>', 'Source folder') .requiredOption('--css <path>', 'CSS folder') .option('--report <file>', 'Save JSON report') .option('--ignore <selectors...>', 'Ignore selectors (supports wildcards)') .option('--watch', 'Watch mode: scan on file changes') .parse(process.argv); const options = program.opts(); if (options.watch) { console.log(chalk.magenta('šŸ‘€ Watch mode enabled. Watching for file changes...')); const watcher = chokidar.watch([options.src, options.css], { ignored: /(^|[\/\\])\../, // ignore dotfiles persistent: true, }); const debouncedRun = debounce(() => runScan(options), 500); watcher.on('add', path => { console.log(chalk.cyan(`File added: ${path}`)); debouncedRun(); }); watcher.on('change', path => { console.log(chalk.cyan(`File changed: ${path}`)); debouncedRun(); }); watcher.on('unlink', path => { console.log(chalk.cyan(`File removed: ${path}`)); debouncedRun(); }); // Initial scan await runScan(options); } else { await runScan(options); } } // Simple debounce to avoid rapid repeated scans function debounce(fn, delay) { let timeoutId; return () => { clearTimeout(timeoutId); timeoutId = setTimeout(fn, delay); }; } main();