@involvex/rmdir-cli 2.0.7 → 2.0.8
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/README.md +54 -1
- package/bin/rmdir-cli.js +262 -8
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -20,7 +20,60 @@ rmdir <dir>
|
|
|
20
20
|
npx @involvex/rmdir-cli <dir>
|
|
21
21
|
```
|
|
22
22
|
|
|
23
|
-
##
|
|
23
|
+
## Parameters
|
|
24
24
|
|
|
25
25
|
- `-h`, `--help` - output usage information
|
|
26
26
|
- `-v`, `--version` - output the version number
|
|
27
|
+
- `-f`, `--force` - enable recursive deletion of non-empty directories (requires confirmation)
|
|
28
|
+
- `-y`, `--yes` - skip confirmation prompts (non-interactive mode)
|
|
29
|
+
|
|
30
|
+
## Usage Examples
|
|
31
|
+
|
|
32
|
+
### Basic Usage
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
# Delete an empty directory
|
|
36
|
+
rmdir mydir
|
|
37
|
+
|
|
38
|
+
# Delete a non-empty directory with confirmation prompt
|
|
39
|
+
rmdir --force mydir
|
|
40
|
+
|
|
41
|
+
# Delete a non-empty directory without confirmation
|
|
42
|
+
rmdir --force --yes mydir
|
|
43
|
+
|
|
44
|
+
# Delete multiple directories
|
|
45
|
+
rmdir --force dir1 dir2 dir3
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### Safety Features
|
|
49
|
+
|
|
50
|
+
The `--force` parameter enables recursive deletion of non-empty directories, but includes safety measures:
|
|
51
|
+
|
|
52
|
+
1. **Confirmation Prompt**: By default, the CLI will prompt for confirmation before deleting non-empty directories
|
|
53
|
+
2. **Non-Interactive Mode**: Use `--yes` to skip confirmation prompts for automated scripts
|
|
54
|
+
3. **Error Handling**: Clear error messages for permission issues, non-existent directories, or invalid paths
|
|
55
|
+
4. **Progress Information**: Shows directory size and file count before deletion
|
|
56
|
+
|
|
57
|
+
### Help Information
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
rmdir --help
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Output:
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
Usage: rmdir [options] <dir> [dir2 ...]
|
|
67
|
+
|
|
68
|
+
Options:
|
|
69
|
+
-h, --help output usage information
|
|
70
|
+
-v, --version output the version number
|
|
71
|
+
-f, --force enable recursive deletion of non-empty directories
|
|
72
|
+
-y, --yes skip confirmation prompts (non-interactive mode)
|
|
73
|
+
|
|
74
|
+
Examples:
|
|
75
|
+
rmdir mydir # Delete empty directory
|
|
76
|
+
rmdir --force mydir # Delete non-empty directory with confirmation
|
|
77
|
+
rmdir --force --yes mydir # Delete non-empty directory without confirmation
|
|
78
|
+
rmdir --force dir1 dir2 dir3 # Delete multiple directories
|
|
79
|
+
```
|
package/bin/rmdir-cli.js
CHANGED
|
@@ -3,16 +3,270 @@
|
|
|
3
3
|
"use strict";
|
|
4
4
|
var rmdir = require("../index");
|
|
5
5
|
var version = require("../package.json").version;
|
|
6
|
+
var fs = require("fs");
|
|
7
|
+
var path = require("path");
|
|
6
8
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
9
|
+
// Parse command line arguments
|
|
10
|
+
function parseArgs() {
|
|
11
|
+
var args = process.argv.slice(2);
|
|
12
|
+
var options = {
|
|
13
|
+
force: false,
|
|
14
|
+
yes: false,
|
|
15
|
+
help: false,
|
|
16
|
+
version: false,
|
|
17
|
+
directories: [],
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
for (var i = 0; i < args.length; i++) {
|
|
21
|
+
var arg = args[i];
|
|
22
|
+
|
|
23
|
+
if (arg === "--help" || arg === "-h") {
|
|
24
|
+
options.help = true;
|
|
25
|
+
} else if (arg === "--version" || arg === "-v") {
|
|
26
|
+
options.version = true;
|
|
27
|
+
} else if (arg === "--force" || arg === "-f") {
|
|
28
|
+
options.force = true;
|
|
29
|
+
} else if (arg === "--yes" || arg === "-y") {
|
|
30
|
+
options.yes = true;
|
|
31
|
+
} else if (arg.startsWith("-")) {
|
|
32
|
+
console.error("Unknown option: " + arg);
|
|
33
|
+
showUsage();
|
|
34
|
+
process.exit(1);
|
|
35
|
+
} else {
|
|
36
|
+
options.directories.push(arg);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return options;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Show usage information
|
|
44
|
+
function showUsage() {
|
|
45
|
+
console.log("Usage: rmdir [options] <dir> [dir2 ...]");
|
|
46
|
+
console.log("");
|
|
47
|
+
console.log("Options:");
|
|
48
|
+
console.log(" -h, --help output usage information");
|
|
49
|
+
console.log(" -v, --version output the version number");
|
|
50
|
+
console.log(
|
|
51
|
+
" -f, --force enable recursive deletion of non-empty directories",
|
|
52
|
+
);
|
|
53
|
+
console.log(
|
|
54
|
+
" -y, --yes skip confirmation prompts (non-interactive mode)",
|
|
55
|
+
);
|
|
56
|
+
console.log("");
|
|
57
|
+
console.log("Examples:");
|
|
58
|
+
console.log(" rmdir mydir # Delete empty directory");
|
|
59
|
+
console.log(
|
|
60
|
+
" rmdir --force mydir # Delete non-empty directory with confirmation",
|
|
61
|
+
);
|
|
62
|
+
console.log(
|
|
63
|
+
" rmdir --force --yes mydir # Delete non-empty directory without confirmation",
|
|
64
|
+
);
|
|
65
|
+
console.log(" rmdir --force dir1 dir2 dir3 # Delete multiple directories");
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Show version information
|
|
69
|
+
function showVersion() {
|
|
70
|
+
console.log("rmdir-cli version: " + version);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Check if directory exists and is accessible
|
|
74
|
+
function checkDirectory(dirpath) {
|
|
75
|
+
try {
|
|
76
|
+
var stats = fs.statSync(dirpath);
|
|
77
|
+
if (!stats.isDirectory()) {
|
|
78
|
+
console.error("Error: " + dirpath + " is not a directory");
|
|
79
|
+
return false;
|
|
80
|
+
}
|
|
81
|
+
return true;
|
|
82
|
+
} catch (err) {
|
|
83
|
+
if (err.code === "ENOENT") {
|
|
84
|
+
console.error("Error: Directory '" + dirpath + "' does not exist");
|
|
85
|
+
return false;
|
|
86
|
+
} else if (err.code === "EACCES") {
|
|
87
|
+
console.error("Error: Permission denied accessing '" + dirpath + "'");
|
|
88
|
+
return false;
|
|
89
|
+
} else {
|
|
90
|
+
console.error(
|
|
91
|
+
"Error: Unable to access '" + dirpath + "': " + err.message,
|
|
92
|
+
);
|
|
93
|
+
return false;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Check if directory is empty
|
|
99
|
+
function isDirectoryEmpty(dirpath) {
|
|
100
|
+
try {
|
|
101
|
+
var files = fs.readdirSync(dirpath);
|
|
102
|
+
return files.length === 0;
|
|
103
|
+
} catch (ignoreErr) {
|
|
104
|
+
console.log("Error:", ignoreErr);
|
|
105
|
+
return false;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Get directory size for progress indication
|
|
110
|
+
function getDirectorySize(dirpath) {
|
|
111
|
+
var totalSize = 0;
|
|
112
|
+
var totalFiles = 0;
|
|
113
|
+
|
|
114
|
+
function walkDir(currentPath) {
|
|
115
|
+
try {
|
|
116
|
+
var files = fs.readdirSync(currentPath);
|
|
117
|
+
files.forEach(function (file) {
|
|
118
|
+
var filePath = path.join(currentPath, file);
|
|
119
|
+
var stats = fs.statSync(filePath);
|
|
120
|
+
|
|
121
|
+
if (stats.isDirectory()) {
|
|
122
|
+
walkDir(filePath);
|
|
123
|
+
} else {
|
|
124
|
+
totalSize += stats.size;
|
|
125
|
+
totalFiles++;
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
} catch (calcErr) {
|
|
129
|
+
console.log("Error:", calcErr);
|
|
130
|
+
// Ignore errors when calculating size
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
walkDir(dirpath);
|
|
135
|
+
return { size: totalSize, files: totalFiles };
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// Confirm deletion with user
|
|
139
|
+
function confirmDeletion(dirpath, options) {
|
|
140
|
+
if (options.yes) {
|
|
141
|
+
return Promise.resolve(true);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
var readline = require("readline");
|
|
145
|
+
var rl = readline.createInterface({
|
|
146
|
+
input: process.stdin,
|
|
147
|
+
output: process.stdout,
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
return new Promise(function (resolve) {
|
|
151
|
+
var sizeInfo = getDirectorySize(dirpath);
|
|
152
|
+
var sizeMB = (sizeInfo.size / (1024 * 1024)).toFixed(2);
|
|
153
|
+
|
|
154
|
+
console.log("About to delete: " + dirpath);
|
|
155
|
+
console.log(
|
|
156
|
+
"Directory contains: " + sizeInfo.files + " files, " + sizeMB + " MB",
|
|
157
|
+
);
|
|
158
|
+
console.log("");
|
|
159
|
+
|
|
160
|
+
rl.question(
|
|
161
|
+
"Are you sure you want to delete this directory? [y/N]: ",
|
|
162
|
+
function (answer) {
|
|
163
|
+
rl.close();
|
|
164
|
+
var confirmed =
|
|
165
|
+
answer.toLowerCase() === "y" || answer.toLowerCase() === "yes";
|
|
166
|
+
if (!confirmed) {
|
|
167
|
+
console.log("Operation cancelled.");
|
|
168
|
+
}
|
|
169
|
+
resolve(confirmed);
|
|
170
|
+
},
|
|
171
|
+
);
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// Enhanced rmdir function with progress and confirmation
|
|
176
|
+
function rmdirWithConfirmation(dirpath, options) {
|
|
177
|
+
return new Promise(function (resolve, reject) {
|
|
178
|
+
if (!checkDirectory(dirpath)) {
|
|
179
|
+
reject(new Error("Directory check failed"));
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
var isEmpty = isDirectoryEmpty(dirpath);
|
|
184
|
+
|
|
185
|
+
if (!isEmpty && !options.force) {
|
|
186
|
+
console.error("Error: Directory '" + dirpath + "' is not empty.");
|
|
187
|
+
console.error("Use --force to delete non-empty directories.");
|
|
188
|
+
reject(new Error("Directory not empty"));
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
if (!isEmpty) {
|
|
193
|
+
confirmDeletion(dirpath, options)
|
|
194
|
+
.then(function (confirmed) {
|
|
195
|
+
if (!confirmed) {
|
|
196
|
+
resolve(false);
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
console.log("Deleting directory: " + dirpath);
|
|
201
|
+
try {
|
|
202
|
+
rmdir(dirpath);
|
|
203
|
+
console.log("Successfully deleted: " + dirpath);
|
|
204
|
+
resolve(true);
|
|
205
|
+
} catch (err) {
|
|
206
|
+
console.error(
|
|
207
|
+
"Error deleting directory '" + dirpath + "': " + err.message,
|
|
208
|
+
);
|
|
209
|
+
reject(err);
|
|
210
|
+
}
|
|
211
|
+
})
|
|
212
|
+
.catch(function (err) {
|
|
213
|
+
reject(err);
|
|
214
|
+
});
|
|
215
|
+
} else {
|
|
216
|
+
try {
|
|
217
|
+
rmdir(dirpath);
|
|
218
|
+
console.log("Successfully deleted: " + dirpath);
|
|
219
|
+
resolve(true);
|
|
220
|
+
} catch (err) {
|
|
221
|
+
console.error(
|
|
222
|
+
"Error deleting directory '" + dirpath + "': " + err.message,
|
|
223
|
+
);
|
|
224
|
+
reject(err);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// Main execution
|
|
231
|
+
var options = parseArgs();
|
|
232
|
+
|
|
233
|
+
if (options.help) {
|
|
234
|
+
showUsage();
|
|
10
235
|
process.exit(0);
|
|
11
|
-
} else if (
|
|
12
|
-
|
|
236
|
+
} else if (options.version) {
|
|
237
|
+
showVersion();
|
|
13
238
|
process.exit(0);
|
|
239
|
+
} else if (options.directories.length === 0) {
|
|
240
|
+
console.error("Error: No directory specified");
|
|
241
|
+
showUsage();
|
|
242
|
+
process.exit(1);
|
|
14
243
|
} else {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
244
|
+
// Process each directory
|
|
245
|
+
var promises = options.directories.map(function (dirpath) {
|
|
246
|
+
return rmdirWithConfirmation(dirpath, options);
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
Promise.all(promises)
|
|
250
|
+
.then(function (results) {
|
|
251
|
+
var successCount = results.filter(Boolean).length;
|
|
252
|
+
var totalCount = results.length;
|
|
253
|
+
|
|
254
|
+
if (successCount === totalCount) {
|
|
255
|
+
console.log("\nAll directories deleted successfully.");
|
|
256
|
+
process.exit(0);
|
|
257
|
+
} else {
|
|
258
|
+
console.log(
|
|
259
|
+
"\n" +
|
|
260
|
+
successCount +
|
|
261
|
+
" out of " +
|
|
262
|
+
totalCount +
|
|
263
|
+
" directories deleted successfully.",
|
|
264
|
+
);
|
|
265
|
+
process.exit(1);
|
|
266
|
+
}
|
|
267
|
+
})
|
|
268
|
+
.catch(function (err) {
|
|
269
|
+
console.error("Operation failed:", err.message);
|
|
270
|
+
process.exit(1);
|
|
271
|
+
});
|
|
18
272
|
}
|