@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.
@@ -0,0 +1,218 @@
1
+ /**
2
+ * File utility functions for cleansweep
3
+ * Handles file system operations and pattern matching
4
+ */
5
+
6
+ import * as fs from 'fs';
7
+ import * as path from 'path';
8
+ import { glob } from 'glob';
9
+
10
+ /**
11
+ * Checks if a given path should be excluded based on exclusion patterns
12
+ * @param filePath - Path to check
13
+ * @param excludePatterns - Array of exclusion patterns
14
+ * @returns true if should be excluded, false otherwise
15
+ */
16
+ export function shouldExclude(filePath: string, excludePatterns: string[]): boolean {
17
+ for (const pattern of excludePatterns) {
18
+ // Check if path contains the pattern or basename matches
19
+ if (filePath.includes(pattern) || path.basename(filePath) === pattern) {
20
+ return true;
21
+ }
22
+ }
23
+ return false;
24
+ }
25
+
26
+ /**
27
+ * Finds files matching a pattern with optional depth limit
28
+ * @param pattern - Glob pattern to match
29
+ * @param maxDepth - Maximum depth to search (optional)
30
+ * @param currentDir - Current directory to search in
31
+ * @returns Array of matching file paths
32
+ */
33
+ export async function findFiles(
34
+ pattern: string,
35
+ maxDepth?: number,
36
+ currentDir: string = process.cwd()
37
+ ): Promise<string[]> {
38
+ const options: any = {
39
+ cwd: currentDir,
40
+ absolute: false,
41
+ nodir: true
42
+ };
43
+
44
+ if (maxDepth !== undefined) {
45
+ options.maxDepth = maxDepth;
46
+ }
47
+
48
+ try {
49
+ const files = await glob(pattern, options);
50
+ return files.map(file => path.relative(process.cwd(), path.resolve(currentDir, file)));
51
+ } catch (error) {
52
+ return [];
53
+ }
54
+ }
55
+
56
+ /**
57
+ * Finds directories matching a pattern with optional depth limit
58
+ * @param pattern - Glob pattern to match
59
+ * @param maxDepth - Maximum depth to search (optional)
60
+ * @param currentDir - Current directory to search in
61
+ * @returns Array of matching directory paths
62
+ */
63
+ export async function findDirectories(
64
+ pattern: string,
65
+ maxDepth?: number,
66
+ currentDir: string = process.cwd()
67
+ ): Promise<string[]> {
68
+ const directories: string[] = [];
69
+
70
+ /**
71
+ * Recursively search for directories
72
+ */
73
+ function searchDir(dir: string, currentDepth: number = 0): void {
74
+ if (maxDepth !== undefined && currentDepth > maxDepth) {
75
+ return;
76
+ }
77
+
78
+ try {
79
+ const entries = fs.readdirSync(dir, { withFileTypes: true });
80
+
81
+ for (const entry of entries) {
82
+ const fullPath = path.join(dir, entry.name);
83
+ const relativePath = path.relative(process.cwd(), fullPath);
84
+
85
+ if (entry.isDirectory()) {
86
+ // Check if directory name matches pattern
87
+ if (matchesPattern(entry.name, pattern)) {
88
+ directories.push(relativePath);
89
+ }
90
+ // Recursively search subdirectories
91
+ searchDir(fullPath, currentDepth + 1);
92
+ }
93
+ }
94
+ } catch (error) {
95
+ // Skip directories we can't read
96
+ return;
97
+ }
98
+ }
99
+
100
+ searchDir(currentDir);
101
+ return directories;
102
+ }
103
+
104
+ /**
105
+ * Finds both files and directories matching a pattern
106
+ * @param pattern - Glob pattern to match
107
+ * @param maxDepth - Maximum depth to search (optional)
108
+ * @param currentDir - Current directory to search in
109
+ * @returns Array of matching paths
110
+ */
111
+ export async function findItems(
112
+ pattern: string,
113
+ maxDepth?: number,
114
+ currentDir: string = process.cwd()
115
+ ): Promise<string[]> {
116
+ const items: string[] = [];
117
+
118
+ /**
119
+ * Recursively search for items
120
+ */
121
+ function searchDir(dir: string, currentDepth: number = 0): void {
122
+ if (maxDepth !== undefined && currentDepth > maxDepth) {
123
+ return;
124
+ }
125
+
126
+ try {
127
+ const entries = fs.readdirSync(dir, { withFileTypes: true });
128
+
129
+ for (const entry of entries) {
130
+ const fullPath = path.join(dir, entry.name);
131
+ const relativePath = path.relative(process.cwd(), fullPath);
132
+
133
+ // Check if name matches pattern
134
+ if (matchesPattern(entry.name, pattern)) {
135
+ items.push(relativePath);
136
+ }
137
+
138
+ // Recursively search subdirectories
139
+ if (entry.isDirectory()) {
140
+ searchDir(fullPath, currentDepth + 1);
141
+ }
142
+ }
143
+ } catch (error) {
144
+ // Skip directories we can't read
145
+ return;
146
+ }
147
+ }
148
+
149
+ searchDir(currentDir);
150
+ return items;
151
+ }
152
+
153
+ /**
154
+ * Simple pattern matching (converts glob to regex-like matching)
155
+ * @param name - Name to check
156
+ * @param pattern - Pattern to match against
157
+ * @returns true if matches, false otherwise
158
+ */
159
+ function matchesPattern(name: string, pattern: string): boolean {
160
+ // Convert glob pattern to regex
161
+ const regexPattern = pattern
162
+ .replace(/\*/g, '.*')
163
+ .replace(/\?/g, '.');
164
+ const regex = new RegExp(`^${regexPattern}$`);
165
+ return regex.test(name);
166
+ }
167
+
168
+ /**
169
+ * Deletes a file or directory
170
+ * @param itemPath - Path to the item to delete
171
+ * @returns true if successful, false otherwise
172
+ */
173
+ export function deleteItem(itemPath: string): boolean {
174
+ try {
175
+ const fullPath = path.resolve(itemPath);
176
+ const stat = fs.statSync(fullPath);
177
+
178
+ if (stat.isDirectory()) {
179
+ fs.rmSync(fullPath, { recursive: true, force: true });
180
+ return true;
181
+ } else if (stat.isFile()) {
182
+ fs.unlinkSync(fullPath);
183
+ return true;
184
+ }
185
+ return false;
186
+ } catch (error) {
187
+ return false;
188
+ }
189
+ }
190
+
191
+ /**
192
+ * Checks if a path exists and is a file
193
+ * @param itemPath - Path to check
194
+ * @returns true if exists and is a file
195
+ */
196
+ export function isFile(itemPath: string): boolean {
197
+ try {
198
+ const stat = fs.statSync(itemPath);
199
+ return stat.isFile();
200
+ } catch {
201
+ return false;
202
+ }
203
+ }
204
+
205
+ /**
206
+ * Checks if a path exists and is a directory
207
+ * @param itemPath - Path to check
208
+ * @returns true if exists and is a directory
209
+ */
210
+ export function isDirectory(itemPath: string): boolean {
211
+ try {
212
+ const stat = fs.statSync(itemPath);
213
+ return stat.isDirectory();
214
+ } catch {
215
+ return false;
216
+ }
217
+ }
218
+
@@ -0,0 +1,91 @@
1
+ /**
2
+ * Logger utility for cleansweep
3
+ * Handles logging to console and log files with timestamps
4
+ */
5
+
6
+ import * as fs from 'fs';
7
+ import * as path from 'path';
8
+ import { LogLevel, OutputFormat } from '../types';
9
+
10
+ export class Logger {
11
+ private logFile?: string;
12
+ private outputFormat: OutputFormat;
13
+
14
+ constructor(logFile?: string, outputFormat: OutputFormat = 'plain') {
15
+ this.logFile = logFile;
16
+ this.outputFormat = outputFormat;
17
+ }
18
+
19
+ /**
20
+ * Logs a message with timestamp to both console and log file (if specified)
21
+ * @param message - Message to log
22
+ * @param level - Log level (INFO, WARNING, ERROR)
23
+ */
24
+ log(message: string, level: LogLevel = 'INFO'): void {
25
+ const timestamp = new Date().toISOString().replace('T', ' ').slice(0, 19);
26
+ const formattedMessage = `[${timestamp}] [${level}] ${message}`;
27
+
28
+ // Print to console
29
+ if (this.outputFormat === 'json') {
30
+ const jsonMessage = JSON.stringify({
31
+ timestamp,
32
+ level,
33
+ message
34
+ });
35
+ console.log(jsonMessage);
36
+ } else {
37
+ console.log(formattedMessage);
38
+ }
39
+
40
+ // Write to log file if specified
41
+ if (this.logFile) {
42
+ try {
43
+ fs.appendFileSync(this.logFile, formattedMessage + '\n');
44
+ } catch (error) {
45
+ console.error(`Failed to write to log file: ${error}`);
46
+ }
47
+ }
48
+ }
49
+
50
+ /**
51
+ * Initializes the log file with header information
52
+ * @param config - Configuration object with all settings
53
+ */
54
+ initializeLogFile(config: {
55
+ dryRun: boolean;
56
+ interactive: boolean;
57
+ force: boolean;
58
+ filesPattern?: string;
59
+ foldersPattern?: string;
60
+ typesPattern?: string;
61
+ excludePatterns: string[];
62
+ maxDepth?: number;
63
+ }): void {
64
+ if (!this.logFile) return;
65
+
66
+ try {
67
+ const header = [
68
+ '==========================================',
69
+ `Deletion Log - ${new Date().toISOString().replace('T', ' ').slice(0, 19)}`,
70
+ '==========================================',
71
+ `Dry Run: ${config.dryRun}`,
72
+ `Interactive: ${config.interactive}`,
73
+ `Force: ${config.force}`,
74
+ ...(config.filesPattern ? [`Files Pattern: ${config.filesPattern}`] : []),
75
+ ...(config.foldersPattern ? [`Folders Pattern: ${config.foldersPattern}`] : []),
76
+ ...(config.typesPattern ? [`Types Pattern: ${config.typesPattern}`] : []),
77
+ ...(config.excludePatterns.length > 0
78
+ ? [`Exclude Patterns: ${config.excludePatterns.join(', ')}`]
79
+ : []),
80
+ ...(config.maxDepth ? [`Max Depth: ${config.maxDepth}`] : []),
81
+ '==========================================',
82
+ ''
83
+ ].join('\n');
84
+
85
+ fs.writeFileSync(this.logFile, header);
86
+ } catch (error) {
87
+ console.error(`Failed to initialize log file: ${error}`);
88
+ }
89
+ }
90
+ }
91
+
@@ -0,0 +1,54 @@
1
+ /**
2
+ * Interactive prompt utilities for cleansweep
3
+ * Handles user confirmation prompts
4
+ */
5
+
6
+ import * as readline from 'readline';
7
+
8
+ /**
9
+ * Creates a readline interface for user input
10
+ */
11
+ function createReadlineInterface(): readline.Interface {
12
+ return readline.createInterface({
13
+ input: process.stdin,
14
+ output: process.stdout
15
+ });
16
+ }
17
+
18
+ /**
19
+ * Prompts the user for confirmation before deleting an item
20
+ * @param item - Path of the item to delete
21
+ * @param force - If true, always returns true without prompting
22
+ * @returns Promise that resolves to true if confirmed, false otherwise
23
+ */
24
+ export function confirmDeletion(item: string, force: boolean = false): Promise<boolean> {
25
+ return new Promise((resolve) => {
26
+ if (force) {
27
+ resolve(true);
28
+ return;
29
+ }
30
+
31
+ const rl = createReadlineInterface();
32
+ rl.question(`Delete '${item}'? [y/N]: `, (answer) => {
33
+ rl.close();
34
+ const response = answer.trim().toLowerCase();
35
+ resolve(response === 'y' || response === 'yes');
36
+ });
37
+ });
38
+ }
39
+
40
+ /**
41
+ * Prompts the user to proceed with deletion after preview
42
+ * @returns Promise that resolves to true if confirmed, false otherwise
43
+ */
44
+ export function confirmProceed(): Promise<boolean> {
45
+ return new Promise((resolve) => {
46
+ const rl = createReadlineInterface();
47
+ rl.question('Proceed with deletion? [y/N]: ', (answer) => {
48
+ rl.close();
49
+ const response = answer.trim().toLowerCase();
50
+ resolve(response === 'y' || response === 'yes');
51
+ });
52
+ });
53
+ }
54
+
package/tsconfig.json ADDED
@@ -0,0 +1,21 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2020",
4
+ "module": "commonjs",
5
+ "lib": ["ES2020"],
6
+ "outDir": "./dist",
7
+ "rootDir": "./src",
8
+ "strict": true,
9
+ "esModuleInterop": true,
10
+ "skipLibCheck": true,
11
+ "forceConsistentCasingInFileNames": true,
12
+ "resolveJsonModule": true,
13
+ "declaration": true,
14
+ "declarationMap": true,
15
+ "sourceMap": true,
16
+ "moduleResolution": "node"
17
+ },
18
+ "include": ["src/**/*"],
19
+ "exclude": ["node_modules", "dist"]
20
+ }
21
+