UNPKG

assetdrain

Version:

๐Ÿงน A blazing-fast CLI to detect and clean unused assets from your codebase with interactive UX.

153 lines (151 loc) โ€ข 5.5 kB
#!/usr/bin/env node // Use built-in `createRequire` for safely loading package.json import { createRequire } from "module"; const require = createRequire(import.meta.url); const pkg = require("../../package.json"); // Handle CLI flags before anything else if (process.argv.includes("--version") || process.argv.includes("-v")) { console.log(`๐Ÿงน assetdrain v${pkg.version}`); process.exit(0); } if (process.argv.includes("--help")) { console.log(` Usage: npx assetdrain [asset-folder] Options: --version, -v Show version --help Show help `); process.exit(0); } // Built-ins import path from "path"; // Third-party import chalk from "chalk"; import ora from "ora"; // Local modules import { scanForImages } from "../core/findImages.js"; import { scanForUsages } from "../core/findUsages.js"; import { generateReport } from "../core/report.js"; import { defaultAssetExts, imageExts, videoExts, gifExts, defaultCodeExts, } from "../core/fileTypes.js"; import { askAssetTypes, askCodeFileTypes, askAction, askExportFormat, askIfShouldDelete, } from "./prompts.js"; async function main() { console.log(chalk.cyanBright.bold("\n๐Ÿงน Welcome to assetdrain!\n")); // Ask for asset types const assetChoice = await askAssetTypes(); let assetExts = []; if (assetChoice === "default") assetExts = defaultAssetExts; else if (assetChoice === "images") assetExts = imageExts; else if (assetChoice === "videos") assetExts = videoExts; else if (assetChoice === "gifs") assetExts = gifExts; else if (Array.isArray(assetChoice)) assetExts = assetChoice; // Ask for code file types const codeChoice = await askCodeFileTypes(); const codeExts = codeChoice === "default" ? defaultCodeExts : codeChoice; // Ask what action to perform const action = await askAction(); // Directories const assetScanDir = path.resolve(process.cwd(), process.argv[2] || "."); const projectRoot = process.cwd(); // code scan will always be done from root console.log(chalk.gray(`\n๐Ÿ“ Scanning assets in: ${assetScanDir}`)); console.log(chalk.gray(`๐Ÿ”Ž Analyzing code usage in: ${projectRoot}\n`)); // Spinner: scanning assets const assetSpinner = ora("๐Ÿ” Scanning for asset files...").start(); let allAssets = []; try { allAssets = await scanForImages(assetScanDir, assetExts, [ "**/node_modules/**", ]); assetSpinner.succeed(`๐Ÿ“ฆ Found ${allAssets.length} asset files.`); } catch (err) { assetSpinner.fail("โŒ Failed to scan asset files."); throw err; } // Spinner: scanning usages const usageSpinner = ora("๐Ÿ“š Analyzing code usage...").start(); let usedAssets; try { usedAssets = await scanForUsages(projectRoot, allAssets, codeExts, 20 // concurrency ); usageSpinner.succeed("โœ… Usage analysis complete."); } catch (err) { usageSpinner.fail("โŒ Failed to analyze usage."); throw err; } // Spinner: generate report (terminal view) const reportSpinner = ora("๐Ÿงพ Generating report...").start(); try { await generateReport(allAssets, usedAssets, { mode: action, export: undefined, }); reportSpinner.succeed("๐Ÿ“Š Report complete."); } catch (err) { reportSpinner.fail("โŒ Failed to generate report."); throw err; } let deleted = false; if (action === "review") { const shouldDelete = await askIfShouldDelete(); if (shouldDelete) { const deleteSpinner = ora("๐Ÿงน Deleting unused assets...").start(); try { await generateReport(allAssets, usedAssets, { mode: "delete", export: undefined, }); deleteSpinner.succeed("โœ… Unused assets deleted."); deleted = true; } catch (err) { deleteSpinner.fail("โŒ Failed to delete unused assets."); throw err; } } } else if (action === "delete") { const deleteSpinner = ora(chalk.red("๐Ÿงน Deleting unused assets...")).start(); try { await generateReport(allAssets, usedAssets, { mode: "delete", export: undefined, }); deleteSpinner.succeed(chalk.redBright("๐Ÿšจ Unused assets deleted automatically.")); deleted = true; } catch (err) { deleteSpinner.fail("โŒ Failed to delete unused assets."); throw err; } } else { deleted = false; // Scan Only } const exportFormat = await askExportFormat(); if (exportFormat !== "no") { const exportSpinner = ora(`๐Ÿ’พ Saving report as ${exportFormat.toUpperCase()}...`).start(); try { await generateReport(allAssets, usedAssets, { mode: "dry", // safe for export export: exportFormat, deleted, // โœ… now correct and accurate }); exportSpinner.succeed(`โœ… Report saved to assetdrain-report.${exportFormat}`); } catch (err) { exportSpinner.fail("โŒ Failed to save report."); throw err; } } } main().catch((err) => { console.error(chalk.red("โŒ Error running assetdrain:"), err); process.exit(1); });