Node.js
const PUBLIC_DIR = join(process.cwd(), 'public');
const searchPattern = '/' + relativePath.split(sep).join('/');
for (const projectFile of projectFiles) {
if (readFileSync(projectFile, 'utf8').includes(searchPattern)) {
isUsed = true;
}
}
if (!isUsed) unusedFiles.push(publicFile);Unused public assets
delete.js · Node.js
Unused public assets
Scans public/ and your source tree, then prompts before deleting files nothing references.
import { existsSync, readFileSync, readdirSync, unlinkSync } from 'node:fs';import { extname, join, relative, sep } from 'node:path';import { createInterface } from 'node:readline/promises';import { stdin as input, stdout as output } from 'node:process';// Configurationconst PUBLIC_DIR = join(process.cwd(), 'public');const IGNORE_DIRS = ['node_modules', '.git', 'dist', 'build'];const FILE_EXTENSIONS = ['.html', '.css', '.js', '.jsx', '.ts', '.tsx', '.vue', '.svelte', '.php', '.md'];// Get all files recursively in a directoryfunction getFilesRecursively(dir, ignoreDirs = []) { if (!existsSync(dir)) return []; const files = readdirSync(dir, { withFileTypes: true }); let results = []; for (const file of files) { const fullPath = join(dir, file.name); if (file.isDirectory()) { if (!ignoreDirs.includes(file.name)) { results = results.concat(getFilesRecursively(fullPath, ignoreDirs)); } } else { results.push(fullPath); } } return results;}// Main functionasync function findUnusedAssets() { console.log('Scanning public directory...'); const publicFiles = getFilesRecursively(PUBLIC_DIR); if (publicFiles.length === 0) { console.log('No files found in public directory.'); return; } console.log('Scanning project files...'); const projectFiles = getFilesRecursively(process.cwd(), IGNORE_DIRS) .filter(file => FILE_EXTENSIONS.includes(extname(file).toLowerCase())); const unusedFiles = []; const checkedPatterns = new Set(); console.log('Checking for unused assets...'); for (const publicFile of publicFiles) { const relativePath = relative(PUBLIC_DIR, publicFile); const searchPattern = `/${relativePath.split(sep).join('/')}`; if (checkedPatterns.has(searchPattern)) continue; checkedPatterns.add(searchPattern); let isUsed = false; for (const projectFile of projectFiles) { try { const content = readFileSync(projectFile, 'utf8'); if (content.includes(searchPattern)) { isUsed = true; break; } } catch (err) { console.error(`Skipping ${projectFile}: ${err.message}`); } } if (!isUsed) { unusedFiles.push(publicFile); } } if (unusedFiles.length === 0) { console.log('No unused assets found.'); return; } console.log('\nFound unused files:'); unusedFiles.forEach(file => console.log(`- ${file}`)); const rl = createInterface({ input, output }); const confirm = await rl.question('\nDo you want to delete these files? (y/n) '); rl.close(); if (confirm.toLowerCase() === 'y') { console.log('\nDeleting files...'); unusedFiles.forEach((file) => { try { unlinkSync(file); console.log(`Deleted: ${file}`); } catch (err) { console.error(`Error deleting ${file}: ${err.message}`); } }); console.log('Deletion complete.'); } else { console.log('Deletion cancelled.'); }}// Run the scriptfindUnusedAssets().catch(console.error);Part of the utils collection on GitHub.