|
| 1 | +/** |
| 2 | + * @fileoverview Unified clean runner with flag-based configuration. |
| 3 | + * Removes build artifacts, caches, and other generated files. |
| 4 | + */ |
| 5 | + |
| 6 | +import path from 'node:path' |
| 7 | +import { fileURLToPath } from 'node:url' |
| 8 | + |
| 9 | +import { deleteAsync } from 'del' |
| 10 | +import fastGlob from 'fast-glob' |
| 11 | + |
| 12 | +import { isQuiet } from '@socketsecurity/lib/argv/flags' |
| 13 | +import { parseArgs } from '@socketsecurity/lib/argv/parse' |
| 14 | +import { logger } from '@socketsecurity/lib/logger' |
| 15 | +import { createSectionHeader } from '@socketsecurity/lib/stdio/header' |
| 16 | + |
| 17 | +const rootPath = path.resolve( |
| 18 | + path.dirname(fileURLToPath(import.meta.url)), |
| 19 | + '..', |
| 20 | +) |
| 21 | + |
| 22 | +/** |
| 23 | + * Clean specific directories. |
| 24 | + */ |
| 25 | +async function cleanDirectories(tasks, options = {}) { |
| 26 | + const { quiet = false } = options |
| 27 | + |
| 28 | + for (const task of tasks) { |
| 29 | + const { name, pattern, patterns } = task |
| 30 | + const patternsToDelete = patterns || [pattern] |
| 31 | + |
| 32 | + if (!quiet) { |
| 33 | + logger.progress(`Cleaning ${name}`) |
| 34 | + } |
| 35 | + |
| 36 | + try { |
| 37 | + // Find all files/dirs matching the patterns |
| 38 | + const files = await fastGlob(patternsToDelete, { |
| 39 | + cwd: rootPath, |
| 40 | + absolute: true, |
| 41 | + dot: true, |
| 42 | + onlyFiles: false, |
| 43 | + markDirectories: true, |
| 44 | + }) |
| 45 | + |
| 46 | + // Delete each file/directory |
| 47 | + await deleteAsync(files) |
| 48 | + |
| 49 | + if (!quiet) { |
| 50 | + if (files.length > 0) { |
| 51 | + logger.done(`Cleaned ${name} (${files.length} items)`) |
| 52 | + } else { |
| 53 | + logger.done(`Cleaned ${name} (already clean)`) |
| 54 | + } |
| 55 | + } |
| 56 | + } catch (error) { |
| 57 | + if (!quiet) { |
| 58 | + logger.error(`Failed to clean ${name}`) |
| 59 | + console.error(error.message) |
| 60 | + } |
| 61 | + return 1 |
| 62 | + } |
| 63 | + } |
| 64 | + |
| 65 | + return 0 |
| 66 | +} |
| 67 | + |
| 68 | +async function main() { |
| 69 | + try { |
| 70 | + // Parse arguments |
| 71 | + const { values } = parseArgs({ |
| 72 | + options: { |
| 73 | + help: { |
| 74 | + type: 'boolean', |
| 75 | + default: false, |
| 76 | + }, |
| 77 | + all: { |
| 78 | + type: 'boolean', |
| 79 | + default: false, |
| 80 | + }, |
| 81 | + cache: { |
| 82 | + type: 'boolean', |
| 83 | + default: false, |
| 84 | + }, |
| 85 | + coverage: { |
| 86 | + type: 'boolean', |
| 87 | + default: false, |
| 88 | + }, |
| 89 | + dist: { |
| 90 | + type: 'boolean', |
| 91 | + default: false, |
| 92 | + }, |
| 93 | + types: { |
| 94 | + type: 'boolean', |
| 95 | + default: false, |
| 96 | + }, |
| 97 | + modules: { |
| 98 | + type: 'boolean', |
| 99 | + default: false, |
| 100 | + }, |
| 101 | + quiet: { |
| 102 | + type: 'boolean', |
| 103 | + default: false, |
| 104 | + }, |
| 105 | + silent: { |
| 106 | + type: 'boolean', |
| 107 | + default: false, |
| 108 | + }, |
| 109 | + }, |
| 110 | + allowPositionals: false, |
| 111 | + strict: false, |
| 112 | + }) |
| 113 | + |
| 114 | + // Show help if requested |
| 115 | + if (values.help) { |
| 116 | + console.log('Clean Runner') |
| 117 | + console.log('\nUsage: pnpm clean [options]') |
| 118 | + console.log('\nOptions:') |
| 119 | + console.log(' --help Show this help message') |
| 120 | + console.log( |
| 121 | + ' --all Clean everything (default if no flags)', |
| 122 | + ) |
| 123 | + console.log(' --cache Clean cache directories') |
| 124 | + console.log(' --coverage Clean coverage reports') |
| 125 | + console.log(' --dist Clean build output') |
| 126 | + console.log(' --types Clean TypeScript declarations only') |
| 127 | + console.log(' --modules Clean node_modules') |
| 128 | + console.log(' --quiet, --silent Suppress progress messages') |
| 129 | + console.log('\nExamples:') |
| 130 | + console.log( |
| 131 | + ' pnpm clean # Clean everything except node_modules', |
| 132 | + ) |
| 133 | + console.log(' pnpm clean --dist # Clean build output only') |
| 134 | + console.log(' pnpm clean --cache --coverage # Clean cache and coverage') |
| 135 | + console.log( |
| 136 | + ' pnpm clean --all --modules # Clean everything including node_modules', |
| 137 | + ) |
| 138 | + process.exitCode = 0 |
| 139 | + return |
| 140 | + } |
| 141 | + |
| 142 | + const quiet = isQuiet(values) |
| 143 | + |
| 144 | + // Determine what to clean |
| 145 | + const cleanAll = |
| 146 | + values.all || |
| 147 | + (!values.cache && |
| 148 | + !values.coverage && |
| 149 | + !values.dist && |
| 150 | + !values.types && |
| 151 | + !values.modules) |
| 152 | + |
| 153 | + const tasks = [] |
| 154 | + |
| 155 | + // Build task list |
| 156 | + if (cleanAll || values.cache) { |
| 157 | + tasks.push({ name: 'cache', pattern: '**/.cache' }) |
| 158 | + } |
| 159 | + |
| 160 | + if (cleanAll || values.coverage) { |
| 161 | + tasks.push({ name: 'coverage', pattern: 'coverage' }) |
| 162 | + } |
| 163 | + |
| 164 | + if (cleanAll || values.dist) { |
| 165 | + tasks.push({ |
| 166 | + name: 'dist', |
| 167 | + patterns: ['dist', '*.tsbuildinfo', '.tsbuildinfo'], |
| 168 | + }) |
| 169 | + } else if (values.types) { |
| 170 | + tasks.push({ name: 'dist/types', patterns: ['dist/types'] }) |
| 171 | + } |
| 172 | + |
| 173 | + if (values.modules) { |
| 174 | + tasks.push({ name: 'node_modules', pattern: '**/node_modules' }) |
| 175 | + } |
| 176 | + |
| 177 | + // Check if there's anything to clean |
| 178 | + if (tasks.length === 0) { |
| 179 | + if (!quiet) { |
| 180 | + logger.info('Nothing to clean') |
| 181 | + } |
| 182 | + process.exitCode = 0 |
| 183 | + return |
| 184 | + } |
| 185 | + |
| 186 | + if (!quiet) { |
| 187 | + console.log( |
| 188 | + createSectionHeader('Clean Runner', { width: 56, borderChar: '=' }), |
| 189 | + ) |
| 190 | + logger.step('Cleaning project directories') |
| 191 | + } |
| 192 | + |
| 193 | + // Clean directories |
| 194 | + const exitCode = await cleanDirectories(tasks, { quiet }) |
| 195 | + |
| 196 | + if (exitCode !== 0) { |
| 197 | + if (!quiet) { |
| 198 | + logger.error('Clean failed') |
| 199 | + } |
| 200 | + process.exitCode = exitCode |
| 201 | + } else { |
| 202 | + if (!quiet) { |
| 203 | + logger.success('Clean completed successfully!') |
| 204 | + } |
| 205 | + } |
| 206 | + } catch (error) { |
| 207 | + logger.error(`Clean runner failed: ${error.message}`) |
| 208 | + process.exitCode = 1 |
| 209 | + } |
| 210 | +} |
| 211 | + |
| 212 | +main().catch(console.error) |
0 commit comments