qleaner 1.0.9 → 1.0.11

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 CHANGED
@@ -1 +1,188 @@
1
- # react-cleaner
1
+ # Qleaner
2
+
3
+ A powerful CLI tool to analyze and clean up your React codebase by finding unused files and listing all imports.
4
+
5
+ ## Features
6
+
7
+ - 🔍 **Scan for unused files**: Identify files that are not imported anywhere in your project
8
+ - 📋 **List all imports**: Get a complete list of all import statements in your codebase with file locations
9
+ - 🎯 **File listing**: List all files in your project
10
+ - 📊 **Table output**: Display results in formatted tables for better readability
11
+ - ⚙️ **Flexible configuration**: Exclude directories and files from scanning
12
+ - 🚀 **Fast performance**: Efficient file scanning across large codebases
13
+ - 💪 **TypeScript support**: Works with TypeScript, JavaScript, JSX, and TSX files
14
+
15
+ ## Installation
16
+
17
+ ```bash
18
+ # Install globally
19
+ npm install -g qleaner
20
+
21
+ # Or use with npx
22
+ npx qleaner
23
+
24
+ # Or install locally
25
+ yarn add qleaner
26
+ ```
27
+
28
+ ## Usage
29
+
30
+ ### List Imports and Files
31
+
32
+ List all imports and files in your project:
33
+
34
+ ```bash
35
+ qleaner qlean-list <path> [options]
36
+ ```
37
+
38
+ **Options:**
39
+ - `-l, --list-files` - List all the files in the project
40
+ - `-i, --list-imports` - List all the imports in the project
41
+ - `-e, --exclude-dir <dir...>` - Exclude directories from the scan
42
+ - `-f, --exclude-file <file...>` - Exclude files from the scan
43
+ - `-F, --exclude-file-print <file...>` - Do not print the excluded files
44
+ - `-t, --table` - Display results in a formatted table
45
+
46
+ **Examples:**
47
+
48
+ ```bash
49
+ # List all files in src directory
50
+ qleaner qlean-list src --list-files
51
+
52
+ # List all imports in src directory
53
+ qleaner qlean-list src --list-imports
54
+
55
+ # List both files and imports in table format
56
+ qleaner qlean-list src --list-files --list-imports --table
57
+
58
+ # List files excluding node_modules and dist
59
+ qleaner qlean-list src --list-files -e node_modules dist
60
+
61
+ # List imports excluding specific files
62
+ qleaner qlean-list src --list-imports -f "**/*.test.js" "**/*.spec.js"
63
+
64
+ # List with table output
65
+ qleaner qlean-list src --list-files --list-imports --table
66
+ ```
67
+
68
+ ### Scan for Unused Files
69
+
70
+ Find files that are not imported anywhere in your project:
71
+
72
+ ```bash
73
+ qleaner qlean-scan <path> [options]
74
+ ```
75
+
76
+ **Options:**
77
+ - `-e, --exclude-dir <dir...>` - Exclude directories from the scan
78
+ - `-f, --exclude-file <file...>` - Exclude files from the scan
79
+ - `-F, --exclude-file-print <files...>` - Do not print the excluded files
80
+ - `-t, --table` - Display results in a formatted table
81
+
82
+ **Examples:**
83
+
84
+ ```bash
85
+ # Scan src directory for unused files
86
+ qleaner qlean-scan src
87
+
88
+ # Display unused files in a table format
89
+ qleaner qlean-scan src --table
90
+
91
+ # Scan excluding test directories
92
+ qleaner qlean-scan src -e __tests__ __mocks__ test
93
+
94
+ # Scan excluding specific file patterns
95
+ qleaner qlean-scan src -f "**/*.test.js" "**/*.stories.js"
96
+
97
+ # Scan with multiple exclusions
98
+ qleaner qlean-scan src -e node_modules dist -f "**/*.config.js"
99
+
100
+ # Scan with table output and exclusions
101
+ qleaner qlean-scan src --table -e __tests__ dist -f "**/*.config.js"
102
+ ```
103
+
104
+ ## Output Formats
105
+
106
+ Qleaner provides two output formats:
107
+
108
+ 1. **Standard output**: Color-coded text output
109
+ - Green for files
110
+ - Yellow for imports
111
+ - Red for unused files
112
+
113
+ 2. **Table output**: Formatted tables with organized columns (use `--table` flag)
114
+ - Import tables show: File, Line, Column, and Import path
115
+ - File tables show: File path
116
+ - Unused files table shows: Unused file paths
117
+
118
+ ## How It Works
119
+
120
+ 1. **File Discovery**: Recursively finds all `.tsx`, `.ts`, `.js`, and `.jsx` files in the specified directory
121
+ 2. **Import Extraction**: Parses files and extracts all import statements
122
+ 3. **Analysis**: Compares file paths with import paths to identify unused files
123
+ 4. **Reporting**: Outputs the results in standard or table format based on your preferences
124
+
125
+ ## Supported File Types
126
+
127
+ - `.js` - JavaScript files
128
+ - `.jsx` - JavaScript React files
129
+ - `.ts` - TypeScript files
130
+ - `.tsx` - TypeScript React files
131
+
132
+ ## Use Cases
133
+
134
+ - 🧹 **Code cleanup**: Remove dead code and unused files from your React projects
135
+ - 📊 **Code analysis**: Understand import patterns and dependencies in your codebase
136
+ - 🔍 **Project audit**: Identify orphaned files that may have been forgotten
137
+ - 📦 **Bundle optimization**: Find files that can be removed to reduce bundle size
138
+ - 🎯 **Maintenance**: Keep your codebase clean and maintainable
139
+
140
+ ## Configuration
141
+
142
+ You can exclude directories and files from scanning using the command-line options. This is useful for:
143
+ - Excluding test files
144
+ - Excluding build outputs
145
+ - Excluding third-party libraries
146
+ - Excluding configuration files
147
+
148
+ **Common exclusions:**
149
+ - Test files: `-f "**/*.test.*" "**/*.spec.*"`
150
+ - Storybook files: `-f "**/*.stories.*"`
151
+ - Test directories: `-e __tests__ __mocks__ test`
152
+ - Build outputs: `-e dist build .next`
153
+ - Configuration files: `-f "**/*.config.*"`
154
+ - Third-party code: `-e node_modules vendor`
155
+
156
+ ## Tips and Best Practices
157
+
158
+ 1. **Start with a small scope**: Begin by scanning a specific directory before scanning the entire project
159
+ 2. **Use exclusions**: Exclude test files and build outputs when scanning for unused files
160
+ 3. **Review before deleting**: Always review the unused files list before removing them - some files might be used dynamically (e.g., through dynamic imports, configuration files, or asset references)
161
+ 4. **Use table format**: The table format is easier to read for large results
162
+ 5. **Combine options**: Use multiple flags together for comprehensive analysis
163
+ 6. **Check dynamic imports**: Files imported using dynamic imports (`import()`) may appear as unused but are actually needed
164
+
165
+ ## Important Notes
166
+
167
+ ⚠️ **Warning**: Always review files before deletion. Some files might be:
168
+ - Used dynamically (dynamic imports)
169
+ - Referenced in configuration files
170
+ - Required for build processes
171
+ - Used as entry points that aren't directly imported
172
+
173
+ ## Requirements
174
+
175
+ - Node.js 14+
176
+ - Yarn or npm
177
+
178
+ ## Contributing
179
+
180
+ Contributions are welcome! Please feel free to submit a Pull Request.
181
+
182
+ ## License
183
+
184
+ MIT
185
+
186
+ ## Version
187
+
188
+ Current version: 1.0.9
package/bin/cli.js CHANGED
@@ -1,38 +1,74 @@
1
1
  #!/usr/bin/env node
2
2
  const { Command } = require("commander");
3
3
  const { getFiles, unUsedFiles } = require("../command");
4
+ const Table = require('cli-table3')
4
5
 
5
- const program = new Command();
6
+ async function loadChalk() {
7
+ return (await import("chalk")).default;
8
+ }
6
9
 
7
- program
8
- .name("qleaner")
9
- .description("A tool to clean up your React code")
10
- .version("1.0.0");
10
+ (async () => {
11
+ const chalk = await loadChalk();
11
12
 
12
- program
13
- .command("qlean-list")
14
- .description("List all the imports in the project")
15
- .argument("<path>", "The path to the directory to scan for imports")
16
- .option("-l, --list-files", "List all the files in the project")
17
- .option("-i, --list-imports", "List all the imports in the project")
18
- .option("-e, --exclude-dir <dir...>", "Exclude directories from the scan")
19
- .option("-f, --exclude-file <file...>", "Exclude files from the scan")
20
- .option("-F, --exclude-file-print <file...>", "Do not Print the excluded files")
21
- .action(async (path, options) => {
22
- const imports = await getFiles(path, options);
23
- });
13
+ const program = new Command();
24
14
 
25
- program.command("qlean-scan")
26
- .description("Scan the project for unused files")
27
- .argument("<path>", "The path to the directory to scan for unused files")
28
- .option("-e, --exclude-dir <dir...>", "Exclude directories from the scan")
29
- .option("-f, --exclude-file <file...>", "Exclude files from the scan")
30
- .option("-F, --exclude-file-print <files...>", "Do not Print the excluded files")
31
- .action(async (path, options) => {
32
- const unusedFiles = await unUsedFiles(path, options);
33
- // console.clear()
34
- unusedFiles.forEach((file) => {
35
- console.log(file);
15
+ program
16
+ .name("qleaner")
17
+ .description("A tool to clean up your React code")
18
+ .version("1.0.0");
19
+
20
+ program
21
+ .command("qlean-list")
22
+ .description("List all the imports in the project")
23
+ .argument("<path>", "The path to the directory to scan for imports")
24
+ .option("-l, --list-files", "List all the files in the project")
25
+ .option("-i, --list-imports", "List all the imports in the project")
26
+ .option("-e, --exclude-dir <dir...>", "Exclude directories from the scan")
27
+ .option("-f, --exclude-file <file...>", "Exclude files from the scan")
28
+ .option(
29
+ "-F, --exclude-file-print <file...>",
30
+ "Do not Print the excluded files"
31
+ )
32
+ .option("-t, --table", "Print the results in a table")
33
+ .action(async (path, options) => {
34
+ const { tableImports, tableFiles } = await getFiles(path, options, chalk);
35
+ if(options.table){
36
+ console.log(chalk.yellow('***************** Imported Files *****************'));
37
+ console.log(tableImports.toString());
38
+ console.log(chalk.green('***************** List Files *****************'));
39
+ console.log(tableFiles.toString());
40
+ }
41
+ });
42
+
43
+ program
44
+ .command("qlean-scan")
45
+ .description("Scan the project for unused files")
46
+ .argument("<path>", "The path to the directory to scan for unused files")
47
+ .option("-e, --exclude-dir <dir...>", "Exclude directories from the scan")
48
+ .option("-f, --exclude-file <file...>", "Exclude files from the scan")
49
+ .option(
50
+ "-F, --exclude-file-print <files...>",
51
+ "Scan but don't print the excluded files"
52
+ )
53
+ .option("-t, --table", "Print the results in a table")
54
+ .action(async (path, options) => {
55
+ const unusedFiles = await unUsedFiles(path, options);
56
+ // console.clear()
57
+ if(options.table){
58
+ const table = new Table({
59
+ head: ['Unused Files'],
60
+ colWidths: [50]
61
+ })
62
+ unusedFiles.forEach(file => {
63
+ table.push([file])
64
+ })
65
+ console.log(table.toString())
66
+
67
+ }else {
68
+ unusedFiles.forEach((file) => {
69
+ console.log(chalk.red(file));
70
+ });
71
+ }
36
72
  });
37
- });
38
- program.parse(process.argv);
73
+ program.parse(process.argv);
74
+ })();
package/command.js CHANGED
@@ -1,11 +1,13 @@
1
1
  const fg = require("fast-glob");
2
2
  const fs = require("fs");
3
- const path = require("path");
3
+ const Table = require('cli-table3');
4
4
  const parser = require("@babel/parser");
5
5
  const traverse = require("@babel/traverse").default;
6
6
 
7
7
 
8
- async function getFiles(directory = "src", options) {
8
+
9
+
10
+ async function getFiles(directory = "src", options, chalk) {
9
11
  const contentPaths = [`${directory}/**/*.{tsx,ts,js,jsx}`];
10
12
  if (options.excludeDir && options.excludeDir.length > 0) {
11
13
  options.excludeDir.forEach(dir => {
@@ -49,18 +51,42 @@ async function getFiles(directory = "src", options) {
49
51
  // },
50
52
  // });
51
53
  }
52
-
53
- if (options.listFiles) {
54
- console.log('***************** Files *****************');
55
- files.forEach((file) => {
56
- console.log(file);
57
- })
58
- }
59
- if (options.listImports) {
60
- console.log('***************** Imports *****************');
61
- imports.forEach((importStatement) => {
62
- console.log(`${importStatement.file}:${importStatement.line}:${importStatement.column} ${importStatement.from}`);
63
- })
54
+
55
+ if (options.table) {
56
+ const tableImports = new Table({
57
+ head: ['File', 'Line', 'Column', 'Import'],
58
+ colWidths: [20, 10, 10, 20],
59
+ });
60
+
61
+ const tableFiles = new Table({
62
+ head: ['File'],
63
+ colWidths: [20],
64
+ });
65
+
66
+ if (options.listFiles) {
67
+ files.forEach((file) => {
68
+ tableFiles.push([file]);
69
+ })
70
+ }
71
+ if (options.listImports) {
72
+ imports.forEach((importStatement) => {
73
+ tableImports.push([importStatement.file, importStatement.line, importStatement.column, importStatement.from]);
74
+ })
75
+ }
76
+ return { tableImports, tableFiles };
77
+ }else{
78
+ if (options.listFiles) {
79
+ console.log(chalk.green('***************** Files *****************'));
80
+ files.forEach((file) => {
81
+ console.log(chalk.green(file));
82
+ })
83
+ }
84
+ if (options.listImports) {
85
+ console.log(chalk.yellow('***************** Imports *****************'));
86
+ imports.forEach((importStatement) => {
87
+ console.log(chalk.yellow(`${importStatement.file}:${importStatement.line}:${importStatement.column} ${importStatement.from}`));
88
+ })
89
+ }
64
90
  }
65
91
  }
66
92
 
@@ -76,6 +102,7 @@ async function unUsedFiles(directory = "src", options) {
76
102
  contentPaths.push(`!${directory}/**/${file}`);
77
103
  });
78
104
  }
105
+
79
106
  const files = await fg(contentPaths);
80
107
  const imports = [];
81
108
  const unusedFiles = [];
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "qleaner",
3
3
  "packageManager": "yarn@4.6.0",
4
- "version": "1.0.9",
4
+ "version": "1.0.11",
5
5
  "main": "command.js",
6
6
  "bin": "./bin/cli.js",
7
7
  "scripts": {
@@ -15,7 +15,9 @@
15
15
  "@babel/parser": "^7.28.5",
16
16
  "@babel/traverse": "^7.28.5",
17
17
  "chalk": "^5.6.2",
18
+ "cli-table3": "^0.6.5",
18
19
  "commander": "^14.0.2",
19
- "fast-glob": "^3.3.3"
20
+ "fast-glob": "^3.3.3",
21
+ "prompts": "^2.4.2"
20
22
  }
21
23
  }
package/utils/utils.js ADDED
@@ -0,0 +1,57 @@
1
+ const prompts = require('prompts');
2
+ const fs = require('fs');
3
+ const path = require('path');
4
+
5
+ async function askDeleteFiles(files) {
6
+ const response = await prompts({
7
+ type: 'multiselect',
8
+ name: 'toDelete',
9
+ message: 'Select unused files to delete or move to .trash directory',
10
+ choices: files.map(file => ({
11
+ title: file,
12
+ value: file,
13
+ }))
14
+ })
15
+ if(response && response.toDelete && response.toDelete.length > 0) {
16
+ const method = await prompts({
17
+ type: 'select',
18
+ name: 'method',
19
+ message: 'Select a method to delete the files',
20
+ choices: [
21
+ { title: 'Move to .trash directory', value: 'moveToTrash' },
22
+ { title: 'Delete files', value: 'deleteFiles' },
23
+ ]
24
+ })
25
+ if(method.method === 'moveToTrash') {
26
+ return await moveToTrash(response.toDelete);
27
+ } else if(method.method === 'deleteFiles') {
28
+ return await deleteFiles(response.toDelete);
29
+ }
30
+ }
31
+ return response && response.toDelete ? response.toDelete : [];
32
+ }
33
+
34
+ async function moveToTrash(files) {
35
+ const trashDir = path.join(process.cwd(), '.trash');
36
+
37
+ if(!fs.existsSync(trashDir)) {
38
+ fs.mkdirSync(trashDir);
39
+ }
40
+
41
+ for(const file of files) {
42
+ const fileName = path.basename(file);
43
+ const destination = path.join(trashDir, fileName);
44
+ fs.renameSync(file, destination);
45
+ }
46
+
47
+ console.log(`Moved ${files.length} files to .trash directory`);
48
+ }
49
+
50
+ async function deleteFiles(files) {
51
+ for(const file of files) {
52
+ fs.unlinkSync(file);
53
+ }
54
+ console.log(`Deleted ${files.length} files`);
55
+ }
56
+
57
+ module.exports = askDeleteFiles;