@singhaman21/cleansweep 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +447 -0
- package/USAGE.md +81 -0
- package/delete.sh +489 -0
- package/dist/bin/cli.d.ts +7 -0
- package/dist/bin/cli.d.ts.map +1 -0
- package/dist/bin/cli.js +114 -0
- package/dist/bin/cli.js.map +1 -0
- package/dist/core/deletionEngine.d.ts +47 -0
- package/dist/core/deletionEngine.d.ts.map +1 -0
- package/dist/core/deletionEngine.js +184 -0
- package/dist/core/deletionEngine.js.map +1 -0
- package/dist/types.d.ts +26 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +6 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/fileUtils.d.ts +54 -0
- package/dist/utils/fileUtils.d.ts.map +1 -0
- package/dist/utils/fileUtils.js +232 -0
- package/dist/utils/fileUtils.js.map +1 -0
- package/dist/utils/logger.d.ts +31 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +110 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/prompts.d.ts +17 -0
- package/dist/utils/prompts.d.ts.map +1 -0
- package/dist/utils/prompts.js +86 -0
- package/dist/utils/prompts.js.map +1 -0
- package/package.json +49 -0
- package/src/bin/cli.ts +130 -0
- package/src/core/deletionEngine.ts +219 -0
- package/src/types.ts +30 -0
- package/src/utils/fileUtils.ts +218 -0
- package/src/utils/logger.ts +91 -0
- package/src/utils/prompts.ts +54 -0
- package/tsconfig.json +21 -0
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Logger utility for cleansweep
|
|
4
|
+
* Handles logging to console and log files with timestamps
|
|
5
|
+
*/
|
|
6
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
7
|
+
if (k2 === undefined) k2 = k;
|
|
8
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
9
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
10
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
11
|
+
}
|
|
12
|
+
Object.defineProperty(o, k2, desc);
|
|
13
|
+
}) : (function(o, m, k, k2) {
|
|
14
|
+
if (k2 === undefined) k2 = k;
|
|
15
|
+
o[k2] = m[k];
|
|
16
|
+
}));
|
|
17
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
18
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
19
|
+
}) : function(o, v) {
|
|
20
|
+
o["default"] = v;
|
|
21
|
+
});
|
|
22
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
23
|
+
var ownKeys = function(o) {
|
|
24
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
25
|
+
var ar = [];
|
|
26
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
27
|
+
return ar;
|
|
28
|
+
};
|
|
29
|
+
return ownKeys(o);
|
|
30
|
+
};
|
|
31
|
+
return function (mod) {
|
|
32
|
+
if (mod && mod.__esModule) return mod;
|
|
33
|
+
var result = {};
|
|
34
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
35
|
+
__setModuleDefault(result, mod);
|
|
36
|
+
return result;
|
|
37
|
+
};
|
|
38
|
+
})();
|
|
39
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
40
|
+
exports.Logger = void 0;
|
|
41
|
+
const fs = __importStar(require("fs"));
|
|
42
|
+
class Logger {
|
|
43
|
+
constructor(logFile, outputFormat = 'plain') {
|
|
44
|
+
this.logFile = logFile;
|
|
45
|
+
this.outputFormat = outputFormat;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Logs a message with timestamp to both console and log file (if specified)
|
|
49
|
+
* @param message - Message to log
|
|
50
|
+
* @param level - Log level (INFO, WARNING, ERROR)
|
|
51
|
+
*/
|
|
52
|
+
log(message, level = 'INFO') {
|
|
53
|
+
const timestamp = new Date().toISOString().replace('T', ' ').slice(0, 19);
|
|
54
|
+
const formattedMessage = `[${timestamp}] [${level}] ${message}`;
|
|
55
|
+
// Print to console
|
|
56
|
+
if (this.outputFormat === 'json') {
|
|
57
|
+
const jsonMessage = JSON.stringify({
|
|
58
|
+
timestamp,
|
|
59
|
+
level,
|
|
60
|
+
message
|
|
61
|
+
});
|
|
62
|
+
console.log(jsonMessage);
|
|
63
|
+
}
|
|
64
|
+
else {
|
|
65
|
+
console.log(formattedMessage);
|
|
66
|
+
}
|
|
67
|
+
// Write to log file if specified
|
|
68
|
+
if (this.logFile) {
|
|
69
|
+
try {
|
|
70
|
+
fs.appendFileSync(this.logFile, formattedMessage + '\n');
|
|
71
|
+
}
|
|
72
|
+
catch (error) {
|
|
73
|
+
console.error(`Failed to write to log file: ${error}`);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Initializes the log file with header information
|
|
79
|
+
* @param config - Configuration object with all settings
|
|
80
|
+
*/
|
|
81
|
+
initializeLogFile(config) {
|
|
82
|
+
if (!this.logFile)
|
|
83
|
+
return;
|
|
84
|
+
try {
|
|
85
|
+
const header = [
|
|
86
|
+
'==========================================',
|
|
87
|
+
`Deletion Log - ${new Date().toISOString().replace('T', ' ').slice(0, 19)}`,
|
|
88
|
+
'==========================================',
|
|
89
|
+
`Dry Run: ${config.dryRun}`,
|
|
90
|
+
`Interactive: ${config.interactive}`,
|
|
91
|
+
`Force: ${config.force}`,
|
|
92
|
+
...(config.filesPattern ? [`Files Pattern: ${config.filesPattern}`] : []),
|
|
93
|
+
...(config.foldersPattern ? [`Folders Pattern: ${config.foldersPattern}`] : []),
|
|
94
|
+
...(config.typesPattern ? [`Types Pattern: ${config.typesPattern}`] : []),
|
|
95
|
+
...(config.excludePatterns.length > 0
|
|
96
|
+
? [`Exclude Patterns: ${config.excludePatterns.join(', ')}`]
|
|
97
|
+
: []),
|
|
98
|
+
...(config.maxDepth ? [`Max Depth: ${config.maxDepth}`] : []),
|
|
99
|
+
'==========================================',
|
|
100
|
+
''
|
|
101
|
+
].join('\n');
|
|
102
|
+
fs.writeFileSync(this.logFile, header);
|
|
103
|
+
}
|
|
104
|
+
catch (error) {
|
|
105
|
+
console.error(`Failed to initialize log file: ${error}`);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
exports.Logger = Logger;
|
|
110
|
+
//# sourceMappingURL=logger.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logger.js","sourceRoot":"","sources":["../../src/utils/logger.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Interactive prompt utilities for cleansweep
|
|
3
|
+
* Handles user confirmation prompts
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Prompts the user for confirmation before deleting an item
|
|
7
|
+
* @param item - Path of the item to delete
|
|
8
|
+
* @param force - If true, always returns true without prompting
|
|
9
|
+
* @returns Promise that resolves to true if confirmed, false otherwise
|
|
10
|
+
*/
|
|
11
|
+
export declare function confirmDeletion(item: string, force?: boolean): Promise<boolean>;
|
|
12
|
+
/**
|
|
13
|
+
* Prompts the user to proceed with deletion after preview
|
|
14
|
+
* @returns Promise that resolves to true if confirmed, false otherwise
|
|
15
|
+
*/
|
|
16
|
+
export declare function confirmProceed(): Promise<boolean>;
|
|
17
|
+
//# sourceMappingURL=prompts.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prompts.d.ts","sourceRoot":"","sources":["../../src/utils/prompts.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAcH;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,GAAE,OAAe,GAAG,OAAO,CAAC,OAAO,CAAC,CActF;AAED;;;GAGG;AACH,wBAAgB,cAAc,IAAI,OAAO,CAAC,OAAO,CAAC,CASjD"}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Interactive prompt utilities for cleansweep
|
|
4
|
+
* Handles user confirmation prompts
|
|
5
|
+
*/
|
|
6
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
7
|
+
if (k2 === undefined) k2 = k;
|
|
8
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
9
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
10
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
11
|
+
}
|
|
12
|
+
Object.defineProperty(o, k2, desc);
|
|
13
|
+
}) : (function(o, m, k, k2) {
|
|
14
|
+
if (k2 === undefined) k2 = k;
|
|
15
|
+
o[k2] = m[k];
|
|
16
|
+
}));
|
|
17
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
18
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
19
|
+
}) : function(o, v) {
|
|
20
|
+
o["default"] = v;
|
|
21
|
+
});
|
|
22
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
23
|
+
var ownKeys = function(o) {
|
|
24
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
25
|
+
var ar = [];
|
|
26
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
27
|
+
return ar;
|
|
28
|
+
};
|
|
29
|
+
return ownKeys(o);
|
|
30
|
+
};
|
|
31
|
+
return function (mod) {
|
|
32
|
+
if (mod && mod.__esModule) return mod;
|
|
33
|
+
var result = {};
|
|
34
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
35
|
+
__setModuleDefault(result, mod);
|
|
36
|
+
return result;
|
|
37
|
+
};
|
|
38
|
+
})();
|
|
39
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
40
|
+
exports.confirmDeletion = confirmDeletion;
|
|
41
|
+
exports.confirmProceed = confirmProceed;
|
|
42
|
+
const readline = __importStar(require("readline"));
|
|
43
|
+
/**
|
|
44
|
+
* Creates a readline interface for user input
|
|
45
|
+
*/
|
|
46
|
+
function createReadlineInterface() {
|
|
47
|
+
return readline.createInterface({
|
|
48
|
+
input: process.stdin,
|
|
49
|
+
output: process.stdout
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Prompts the user for confirmation before deleting an item
|
|
54
|
+
* @param item - Path of the item to delete
|
|
55
|
+
* @param force - If true, always returns true without prompting
|
|
56
|
+
* @returns Promise that resolves to true if confirmed, false otherwise
|
|
57
|
+
*/
|
|
58
|
+
function confirmDeletion(item, force = false) {
|
|
59
|
+
return new Promise((resolve) => {
|
|
60
|
+
if (force) {
|
|
61
|
+
resolve(true);
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
const rl = createReadlineInterface();
|
|
65
|
+
rl.question(`Delete '${item}'? [y/N]: `, (answer) => {
|
|
66
|
+
rl.close();
|
|
67
|
+
const response = answer.trim().toLowerCase();
|
|
68
|
+
resolve(response === 'y' || response === 'yes');
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Prompts the user to proceed with deletion after preview
|
|
74
|
+
* @returns Promise that resolves to true if confirmed, false otherwise
|
|
75
|
+
*/
|
|
76
|
+
function confirmProceed() {
|
|
77
|
+
return new Promise((resolve) => {
|
|
78
|
+
const rl = createReadlineInterface();
|
|
79
|
+
rl.question('Proceed with deletion? [y/N]: ', (answer) => {
|
|
80
|
+
rl.close();
|
|
81
|
+
const response = answer.trim().toLowerCase();
|
|
82
|
+
resolve(response === 'y' || response === 'yes');
|
|
83
|
+
});
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
//# sourceMappingURL=prompts.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prompts.js","sourceRoot":"","sources":["../../src/utils/prompts.ts"],"names":[],"mappings":";AAAA;;;GAGG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAoBH,0CAcC;AAMD,wCASC;AA/CD,mDAAqC;AAErC;;GAEG;AACH,SAAS,uBAAuB;IAC9B,OAAO,QAAQ,CAAC,eAAe,CAAC;QAC9B,KAAK,EAAE,OAAO,CAAC,KAAK;QACpB,MAAM,EAAE,OAAO,CAAC,MAAM;KACvB,CAAC,CAAC;AACL,CAAC;AAED;;;;;GAKG;AACH,SAAgB,eAAe,CAAC,IAAY,EAAE,QAAiB,KAAK;IAClE,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;QAC7B,IAAI,KAAK,EAAE,CAAC;YACV,OAAO,CAAC,IAAI,CAAC,CAAC;YACd,OAAO;QACT,CAAC;QAED,MAAM,EAAE,GAAG,uBAAuB,EAAE,CAAC;QACrC,EAAE,CAAC,QAAQ,CAAC,WAAW,IAAI,YAAY,EAAE,CAAC,MAAM,EAAE,EAAE;YAClD,EAAE,CAAC,KAAK,EAAE,CAAC;YACX,MAAM,QAAQ,GAAG,MAAM,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;YAC7C,OAAO,CAAC,QAAQ,KAAK,GAAG,IAAI,QAAQ,KAAK,KAAK,CAAC,CAAC;QAClD,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC;AAED;;;GAGG;AACH,SAAgB,cAAc;IAC5B,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;QAC7B,MAAM,EAAE,GAAG,uBAAuB,EAAE,CAAC;QACrC,EAAE,CAAC,QAAQ,CAAC,gCAAgC,EAAE,CAAC,MAAM,EAAE,EAAE;YACvD,EAAE,CAAC,KAAK,EAAE,CAAC;YACX,MAAM,QAAQ,GAAG,MAAM,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;YAC7C,OAAO,CAAC,QAAQ,KAAK,GAAG,IAAI,QAAQ,KAAK,KAAK,CAAC,CAAC;QAClD,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@singhaman21/cleansweep",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "A powerful command-line tool for safely deleting files and folders with comprehensive safety features",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"cleansweep": "./dist/bin/cli.js"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"build": "tsc",
|
|
11
|
+
"start": "node dist/bin/cli.js",
|
|
12
|
+
"dev": "ts-node src/bin/cli.ts",
|
|
13
|
+
"prepublishOnly": "npm run build"
|
|
14
|
+
},
|
|
15
|
+
"repository": {
|
|
16
|
+
"type": "git",
|
|
17
|
+
"url": "git+https://github.com/SinghAman21/cleansweep.git"
|
|
18
|
+
},
|
|
19
|
+
"keywords": [
|
|
20
|
+
"devtool",
|
|
21
|
+
"script",
|
|
22
|
+
"bash",
|
|
23
|
+
"delete",
|
|
24
|
+
"cleanup",
|
|
25
|
+
"files",
|
|
26
|
+
"folders",
|
|
27
|
+
"cli",
|
|
28
|
+
"typescript"
|
|
29
|
+
],
|
|
30
|
+
"author": "SinghAman21",
|
|
31
|
+
"license": "MIT",
|
|
32
|
+
"bugs": {
|
|
33
|
+
"url": "https://github.com/SinghAman21/cleansweep/issues"
|
|
34
|
+
},
|
|
35
|
+
"homepage": "https://github.com/SinghAman21/cleansweep#readme",
|
|
36
|
+
"engines": {
|
|
37
|
+
"node": ">=14.0.0"
|
|
38
|
+
},
|
|
39
|
+
"devDependencies": {
|
|
40
|
+
"@types/node": "^20.10.0",
|
|
41
|
+
"ts-node": "^10.9.2",
|
|
42
|
+
"typescript": "^5.3.3"
|
|
43
|
+
},
|
|
44
|
+
"dependencies": {
|
|
45
|
+
"commander": "^11.1.0",
|
|
46
|
+
"glob": "^10.3.10",
|
|
47
|
+
"readline": "^1.3.0"
|
|
48
|
+
}
|
|
49
|
+
}
|
package/src/bin/cli.ts
ADDED
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* cleansweep CLI Entry Point
|
|
5
|
+
* Command-line interface for the cleansweep deletion tool
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { Command } from 'commander';
|
|
9
|
+
import { Config } from '../types';
|
|
10
|
+
import { Logger } from '../utils/logger';
|
|
11
|
+
import { DeletionEngine } from '../core/deletionEngine';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Main function to run the CLI
|
|
15
|
+
*/
|
|
16
|
+
async function main() {
|
|
17
|
+
const program = new Command();
|
|
18
|
+
|
|
19
|
+
program
|
|
20
|
+
.name('cleansweep')
|
|
21
|
+
.description('A powerful command-line tool for safely deleting files and folders')
|
|
22
|
+
.version('1.0.0')
|
|
23
|
+
.option('-fi, --files <pattern>', 'Specify file patterns to delete (e.g., "*.tmp", "*.log")')
|
|
24
|
+
.option('-fo, --folders <pattern>', 'Specify folder patterns to delete (e.g., "temp", "cache")')
|
|
25
|
+
.option('-ty, --types <pattern>', 'Specify multiple types of files/directories to delete')
|
|
26
|
+
.option('-ex, --exclude <pattern>', 'Exclude patterns or folders from deletion (can be used multiple times)', (val: string, prev: string[]) => {
|
|
27
|
+
prev.push(val);
|
|
28
|
+
return prev;
|
|
29
|
+
}, [] as string[])
|
|
30
|
+
.option('-d, --depth <number>', 'Limit the search depth in directory structure', parseInt)
|
|
31
|
+
.option('-dr, --dry-run', 'Simulate deletion without actually deleting anything', false)
|
|
32
|
+
.option('-in, --interactive', 'Prompt for confirmation before each deletion', false)
|
|
33
|
+
.option('-pr, --preview', 'Display list of items that will be deleted', false)
|
|
34
|
+
.option('-f, --force', 'Bypass safety checks and interactive warnings', false)
|
|
35
|
+
.option('-lg, --log <file>', 'Generate a log file with timestamps of deletions')
|
|
36
|
+
.option('-fm, --format <format>', 'Output format: plain, json (default: plain)', 'plain')
|
|
37
|
+
.addHelpText('after', `
|
|
38
|
+
Examples:
|
|
39
|
+
npx cleansweep --files "*.tmp" --folders "temp" --exclude "important" --log deletion_log.txt --dry-run
|
|
40
|
+
npx cleansweep -fi "*.tmp" -fo "temp" -ex "important" -lg deletion_log.txt -dr
|
|
41
|
+
npx cleansweep --types "*.log" --depth 2 --interactive
|
|
42
|
+
npx cleansweep -ty "*.log" -d 2 -in
|
|
43
|
+
npx cleansweep --files "*.tmp" --preview
|
|
44
|
+
npx cleansweep -fi "*.tmp" -pr
|
|
45
|
+
npx cleansweep --folders "cache" --force --log cleanup.log
|
|
46
|
+
npx cleansweep -fo "cache" -f -lg cleanup.log
|
|
47
|
+
`);
|
|
48
|
+
|
|
49
|
+
program.parse(process.argv);
|
|
50
|
+
const options = program.opts();
|
|
51
|
+
|
|
52
|
+
// Build configuration
|
|
53
|
+
const config: Config = {
|
|
54
|
+
dryRun: options.dryRun || false,
|
|
55
|
+
interactive: options.interactive || false,
|
|
56
|
+
force: options.force || false,
|
|
57
|
+
preview: options.preview || false,
|
|
58
|
+
logFile: options.log,
|
|
59
|
+
outputFormat: options.format === 'json' ? 'json' : 'plain',
|
|
60
|
+
maxDepth: options.depth,
|
|
61
|
+
filesPattern: options.files,
|
|
62
|
+
foldersPattern: options.folders,
|
|
63
|
+
typesPattern: options.types,
|
|
64
|
+
excludePatterns: options.exclude || []
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
// Validate that at least one pattern is specified
|
|
68
|
+
if (!config.filesPattern && !config.foldersPattern && !config.typesPattern) {
|
|
69
|
+
console.error('Error: At least one of --files, --folders, or --types must be specified');
|
|
70
|
+
program.help();
|
|
71
|
+
process.exit(1);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Validate output format
|
|
75
|
+
if (config.outputFormat !== 'plain' && config.outputFormat !== 'json') {
|
|
76
|
+
console.error("Error: --format must be 'plain' or 'json'");
|
|
77
|
+
process.exit(1);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Validate depth if provided
|
|
81
|
+
if (config.maxDepth !== undefined && (isNaN(config.maxDepth) || config.maxDepth < 1)) {
|
|
82
|
+
console.error('Error: --depth must be a positive number');
|
|
83
|
+
process.exit(1);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Initialize logger
|
|
87
|
+
const logger = new Logger(config.logFile, config.outputFormat);
|
|
88
|
+
|
|
89
|
+
// Initialize log file
|
|
90
|
+
logger.initializeLogFile({
|
|
91
|
+
dryRun: config.dryRun,
|
|
92
|
+
interactive: config.interactive,
|
|
93
|
+
force: config.force,
|
|
94
|
+
filesPattern: config.filesPattern,
|
|
95
|
+
foldersPattern: config.foldersPattern,
|
|
96
|
+
typesPattern: config.typesPattern,
|
|
97
|
+
excludePatterns: config.excludePatterns,
|
|
98
|
+
maxDepth: config.maxDepth
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
// Log start of operation
|
|
102
|
+
logger.log('Starting deletion operation...', 'INFO');
|
|
103
|
+
|
|
104
|
+
try {
|
|
105
|
+
// Create deletion engine
|
|
106
|
+
const engine = new DeletionEngine(config, logger);
|
|
107
|
+
|
|
108
|
+
// Collect items to delete
|
|
109
|
+
await engine.collectItemsToDelete();
|
|
110
|
+
|
|
111
|
+
// Process deletions
|
|
112
|
+
await engine.processDeletions();
|
|
113
|
+
|
|
114
|
+
// Print summary
|
|
115
|
+
engine.printSummary();
|
|
116
|
+
|
|
117
|
+
// Exit with appropriate code
|
|
118
|
+
process.exit(engine.getExitCode());
|
|
119
|
+
} catch (error) {
|
|
120
|
+
logger.log(`Error: ${error instanceof Error ? error.message : String(error)}`, 'ERROR');
|
|
121
|
+
process.exit(1);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// Run main function
|
|
126
|
+
main().catch((error) => {
|
|
127
|
+
console.error('Fatal error:', error);
|
|
128
|
+
process.exit(1);
|
|
129
|
+
});
|
|
130
|
+
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deletion Engine for cleansweep
|
|
3
|
+
* Core logic for collecting and processing deletions
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { Config, DeletionResult } from '../types';
|
|
7
|
+
import { Logger } from '../utils/logger';
|
|
8
|
+
import {
|
|
9
|
+
findFiles,
|
|
10
|
+
findDirectories,
|
|
11
|
+
findItems,
|
|
12
|
+
shouldExclude,
|
|
13
|
+
deleteItem,
|
|
14
|
+
isFile,
|
|
15
|
+
isDirectory
|
|
16
|
+
} from '../utils/fileUtils';
|
|
17
|
+
import { confirmDeletion, confirmProceed } from '../utils/prompts';
|
|
18
|
+
|
|
19
|
+
export class DeletionEngine {
|
|
20
|
+
private config: Config;
|
|
21
|
+
private logger: Logger;
|
|
22
|
+
private itemsToDelete: string[] = [];
|
|
23
|
+
private deletedItems: string[] = [];
|
|
24
|
+
private failedDeletions: string[] = [];
|
|
25
|
+
|
|
26
|
+
constructor(config: Config, logger: Logger) {
|
|
27
|
+
this.config = config;
|
|
28
|
+
this.logger = logger;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Collects all items (files/folders) that match the specified patterns
|
|
33
|
+
* and stores them in the itemsToDelete array
|
|
34
|
+
*/
|
|
35
|
+
async collectItemsToDelete(): Promise<void> {
|
|
36
|
+
this.itemsToDelete = [];
|
|
37
|
+
const allItems: string[] = [];
|
|
38
|
+
|
|
39
|
+
// Process file patterns
|
|
40
|
+
if (this.config.filesPattern) {
|
|
41
|
+
const files = await findFiles(
|
|
42
|
+
this.config.filesPattern,
|
|
43
|
+
this.config.maxDepth
|
|
44
|
+
);
|
|
45
|
+
allItems.push(...files);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Process folder patterns
|
|
49
|
+
if (this.config.foldersPattern) {
|
|
50
|
+
const dirs = await findDirectories(
|
|
51
|
+
this.config.foldersPattern,
|
|
52
|
+
this.config.maxDepth
|
|
53
|
+
);
|
|
54
|
+
allItems.push(...dirs);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Process types pattern (can match both files and directories)
|
|
58
|
+
if (this.config.typesPattern) {
|
|
59
|
+
const items = await findItems(
|
|
60
|
+
this.config.typesPattern,
|
|
61
|
+
this.config.maxDepth
|
|
62
|
+
);
|
|
63
|
+
allItems.push(...items);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Filter out excluded items and remove duplicates
|
|
67
|
+
const uniqueItems = [...new Set(allItems)];
|
|
68
|
+
for (const item of uniqueItems) {
|
|
69
|
+
if (!shouldExclude(item, this.config.excludePatterns)) {
|
|
70
|
+
this.itemsToDelete.push(item);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Sort for consistent output
|
|
75
|
+
this.itemsToDelete.sort();
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Displays a preview of all items that will be deleted
|
|
80
|
+
*/
|
|
81
|
+
displayPreview(): void {
|
|
82
|
+
if (this.itemsToDelete.length === 0) {
|
|
83
|
+
this.logger.log('No items found matching the specified patterns.', 'INFO');
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
this.logger.log(
|
|
88
|
+
`Preview: Items that will be deleted (${this.itemsToDelete.length} items):`,
|
|
89
|
+
'INFO'
|
|
90
|
+
);
|
|
91
|
+
|
|
92
|
+
if (this.config.outputFormat === 'json') {
|
|
93
|
+
const items = this.itemsToDelete.map(path => ({ path }));
|
|
94
|
+
console.log(JSON.stringify({ items }, null, 2));
|
|
95
|
+
} else {
|
|
96
|
+
for (const item of this.itemsToDelete) {
|
|
97
|
+
console.log(` - ${item}`);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Deletes a single item (file or directory) with appropriate logging
|
|
104
|
+
* @param item - Path of the item to delete
|
|
105
|
+
*/
|
|
106
|
+
async deleteItem(item: string): Promise<void> {
|
|
107
|
+
// Skip if dry run
|
|
108
|
+
if (this.config.dryRun) {
|
|
109
|
+
this.logger.log(`Would delete: ${item}`, 'INFO');
|
|
110
|
+
this.deletedItems.push(item);
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Interactive confirmation
|
|
115
|
+
if (this.config.interactive) {
|
|
116
|
+
const confirmed = await confirmDeletion(item, this.config.force);
|
|
117
|
+
if (!confirmed) {
|
|
118
|
+
this.logger.log(`Skipped: ${item}`, 'INFO');
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// Perform deletion
|
|
124
|
+
if (isDirectory(item)) {
|
|
125
|
+
if (deleteItem(item)) {
|
|
126
|
+
this.logger.log(`Deleted directory: ${item}`, 'INFO');
|
|
127
|
+
this.deletedItems.push(item);
|
|
128
|
+
} else {
|
|
129
|
+
this.logger.log(`Failed to delete directory: ${item}`, 'ERROR');
|
|
130
|
+
this.failedDeletions.push(item);
|
|
131
|
+
}
|
|
132
|
+
} else if (isFile(item)) {
|
|
133
|
+
if (deleteItem(item)) {
|
|
134
|
+
this.logger.log(`Deleted file: ${item}`, 'INFO');
|
|
135
|
+
this.deletedItems.push(item);
|
|
136
|
+
} else {
|
|
137
|
+
this.logger.log(`Failed to delete file: ${item}`, 'ERROR');
|
|
138
|
+
this.failedDeletions.push(item);
|
|
139
|
+
}
|
|
140
|
+
} else {
|
|
141
|
+
this.logger.log(`Item not found or already deleted: ${item}`, 'WARNING');
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Processes all collected items for deletion based on the current mode
|
|
147
|
+
*/
|
|
148
|
+
async processDeletions(): Promise<void> {
|
|
149
|
+
if (this.itemsToDelete.length === 0) {
|
|
150
|
+
this.logger.log('No items to delete.', 'INFO');
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// Show preview if requested
|
|
155
|
+
if (this.config.preview) {
|
|
156
|
+
this.displayPreview();
|
|
157
|
+
if (!this.config.dryRun && !this.config.force) {
|
|
158
|
+
const proceed = await confirmProceed();
|
|
159
|
+
if (!proceed) {
|
|
160
|
+
this.logger.log('Deletion cancelled by user.', 'INFO');
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// Process each item
|
|
167
|
+
for (const item of this.itemsToDelete) {
|
|
168
|
+
await this.deleteItem(item);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Gets the deletion summary
|
|
174
|
+
* @returns DeletionResult object with statistics
|
|
175
|
+
*/
|
|
176
|
+
getSummary(): DeletionResult {
|
|
177
|
+
return {
|
|
178
|
+
totalItems: this.itemsToDelete.length,
|
|
179
|
+
deleted: this.deletedItems.length,
|
|
180
|
+
failed: this.failedDeletions.length,
|
|
181
|
+
dryRun: this.config.dryRun,
|
|
182
|
+
failedItems: this.failedDeletions.length > 0 ? this.failedDeletions : undefined
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Prints a summary of the deletion operation
|
|
188
|
+
*/
|
|
189
|
+
printSummary(): void {
|
|
190
|
+
const summary = this.getSummary();
|
|
191
|
+
|
|
192
|
+
if (this.config.outputFormat === 'json') {
|
|
193
|
+
console.log(JSON.stringify({ summary }, null, 2));
|
|
194
|
+
} else {
|
|
195
|
+
this.logger.log('=== Deletion Summary ===', 'INFO');
|
|
196
|
+
this.logger.log(`Total items found: ${summary.totalItems}`, 'INFO');
|
|
197
|
+
if (summary.dryRun) {
|
|
198
|
+
this.logger.log(`Items that would be deleted: ${summary.deleted}`, 'INFO');
|
|
199
|
+
} else {
|
|
200
|
+
this.logger.log(`Items successfully deleted: ${summary.deleted}`, 'INFO');
|
|
201
|
+
}
|
|
202
|
+
if (summary.failed > 0 && summary.failedItems) {
|
|
203
|
+
this.logger.log(`Failed deletions: ${summary.failed}`, 'ERROR');
|
|
204
|
+
for (const item of summary.failedItems) {
|
|
205
|
+
this.logger.log(` - ${item}`, 'ERROR');
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Gets the exit code based on deletion results
|
|
213
|
+
* @returns 0 for success, 1 for failures
|
|
214
|
+
*/
|
|
215
|
+
getExitCode(): number {
|
|
216
|
+
return this.failedDeletions.length > 0 ? 1 : 0;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Type definitions for cleansweep
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export type LogLevel = 'INFO' | 'WARNING' | 'ERROR';
|
|
6
|
+
|
|
7
|
+
export type OutputFormat = 'plain' | 'json';
|
|
8
|
+
|
|
9
|
+
export interface Config {
|
|
10
|
+
dryRun: boolean;
|
|
11
|
+
interactive: boolean;
|
|
12
|
+
force: boolean;
|
|
13
|
+
preview: boolean;
|
|
14
|
+
logFile?: string;
|
|
15
|
+
outputFormat: OutputFormat;
|
|
16
|
+
maxDepth?: number;
|
|
17
|
+
filesPattern?: string;
|
|
18
|
+
foldersPattern?: string;
|
|
19
|
+
typesPattern?: string;
|
|
20
|
+
excludePatterns: string[];
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface DeletionResult {
|
|
24
|
+
totalItems: number;
|
|
25
|
+
deleted: number;
|
|
26
|
+
failed: number;
|
|
27
|
+
dryRun: boolean;
|
|
28
|
+
failedItems?: string[];
|
|
29
|
+
}
|
|
30
|
+
|