qleaner 1.1.3 → 1.1.5

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,6 +1,19 @@
1
1
  # Qleaner
2
2
 
3
- A powerful CLI tool to analyze and clean up your React codebase by finding unused files and images, providing project insights, and analyzing dependencies.
3
+ Afraid to delete files in a React project?
4
+ ❌ Unsure what's still used?
5
+ ❌ Large repos slowing down devs?
6
+
7
+ **Qleaner answers those questions safely.**
8
+
9
+ Qleaner scans your entire codebase with precision, finding unused files, dead assets, and architectural rot—without the risk. It tells you exactly what's safe to delete, backed by comprehensive dependency analysis.
10
+
11
+ **Developers star tools that:**
12
+
13
+ - **Save them time** — No more manual hunting through imports and references
14
+ - **Reduce fear** — Dry-run mode shows you exactly what would be deleted before you commit
15
+ - **Prevent mistakes** — Smart caching and incremental scans catch everything
16
+ - **Make them look smart at work** — Clean codebases speak volumes
4
17
 
5
18
  ## Features
6
19
 
@@ -17,6 +30,9 @@ A powerful CLI tool to analyze and clean up your React codebase by finding unuse
17
30
  - 💾 **Smart caching**: Caches scan results for faster subsequent runs (automatically invalidates on file changes)
18
31
  - 🎨 **CSS and styled-components support**: Detects images used in CSS files and styled-components
19
32
  - 🔧 **Configuration file**: Use `qleaner init` to create a `qleaner.config.json` file with default settings for your project
33
+ - 🔗 **Dead link detection**: Identifies images referenced in code but not found in the file system
34
+ - 🔥 **File hotspot analysis**: Discover the most imported files and image hotspots in your codebase
35
+ - ⚠️ **Large file warnings**: Automatically flags code files exceeding 100KB for optimization
20
36
 
21
37
  ## Installation
22
38
 
@@ -43,31 +59,81 @@ qleaner init
43
59
 
44
60
  This creates a configuration file with default settings for exclusions and image scanning options. You can then modify `qleaner.config.json` to customize your settings.
45
61
 
62
+ ## Configuration
63
+
64
+ **Configuration is key!** A good setup saves a lot of time when typing commands. Instead of repeatedly typing long command-line options, configure your defaults once in `qleaner.config.json` and they'll be used automatically.
65
+
66
+ Qleaner supports configuration through both command-line options and a configuration file (`qleaner.config.json`). Use `qleaner init` to create a default configuration file.
67
+
68
+ ### Configuration File
69
+
70
+ The `qleaner.config.json` file allows you to set default options that will be merged with command-line options. Command-line options take precedence over configuration file options.
71
+
72
+ **Configuration options:**
73
+ - `excludeDir` - Array of directories to exclude from scans
74
+ - `excludeFile` - Array of file names to exclude from scans (just use the file name, e.g., `["test.js", "config.js"]`)
75
+ - `excludeExtensions` - Array of file extensions to exclude (e.g., `["test.tsx", "test.ts"]`)
76
+ - `excludeFilePrint` - Array of file names to scan but not print in results (just use the file name). These are also known as **entrypoints** - files that are entry points to your application (like `page.tsx`, `route.ts`, `layout.tsx` in Next.js) that may not be directly imported but are still used by the framework
77
+ - `excludeDirAssets` - Array of directories to exclude from asset scans
78
+ - `excludeFileAssets` - Array of file names to exclude from asset scans (just use the file name)
79
+ - `excludeDirCode` - Array of directories to exclude from code scans
80
+ - `excludeFileCode` - Array of file names to exclude from code scans (just use the file name)
81
+ - `isRootFolderReferenced` - Boolean indicating if images are referenced relative to the file path (e.g., `/img/a.png` where `img` is the root folder and the path is relative to the file location)
82
+ - `alias` - Boolean indicating if aliases are used in image paths (e.g., `@/assets/images/a.png`)
83
+
84
+ **Common exclusions:**
85
+ - Test files: `-f test.js spec.js` or `excludeExtensions: ["test.tsx", "test.ts"]`
86
+ - Storybook files: `-f stories.js`
87
+ - Test directories: `-e __tests__ __mocks__ test`
88
+ - Build outputs: `-e dist build .next`
89
+ - Configuration files: `-f config.js`
90
+ - Third-party code: `-e node_modules vendor`
91
+
92
+ **Example configuration file:**
93
+ ```json
94
+ {
95
+ "excludeDir": ["node_modules", "dist", "build"],
96
+ "excludeFile": [],
97
+ "excludeExtensions": ["test.tsx", "test.ts"],
98
+ "excludeFilePrint": ["page.tsx", "route.ts", "layout.tsx"],
99
+ "isRootFolderReferenced": false,
100
+ "alias": true
101
+ }
102
+ ```
103
+
46
104
  ### Project Summary
47
105
 
48
- Get a comprehensive summary of your project including file counts, unused files/images, and dependencies:
106
+ Get a comprehensive summary of your project including file counts, unused files/images, dead links, dependencies, and file hotspots:
49
107
 
50
108
  ```bash
51
109
  qleaner summary [options]
52
110
  ```
53
111
 
54
112
  **Options:**
55
- - `-l, --largest-files` - List the largest files in the project
56
- - `-d, --dependencies` - List the dependencies in the project
113
+ - `-l, --largest-files` - List the largest files in the project (top 10 code files, top 10 image files, files above 100KB, and total sizes)
114
+ - `-d, --dependencies` - List dependency analysis (heavy/light dependencies, file hotspots, dead/alive image hotspots)
57
115
 
58
116
  **Examples:**
59
117
 
60
118
  ```bash
61
119
  # Get project summary (default)
120
+ # Shows: total code files, total image files, unused files, unused images, dead image links
62
121
  qleaner summary
63
122
 
64
123
  # List the largest files
124
+ # Shows: top 10 largest code files, top 10 largest image files, files above 100KB, total sizes
65
125
  qleaner summary --largest-files
66
126
 
67
127
  # List dependencies
128
+ # Shows: files with heavy/light dependencies, file hotspots (most imported files), dead/alive image hotspots
68
129
  qleaner summary --dependencies
69
130
  ```
70
131
 
132
+ **Summary includes:**
133
+ - **Default summary**: Total code/image files, unused files/images count, dead image links count
134
+ - **Largest files** (`--largest-files`): Top 10 largest code files, top 10 largest image files, code files exceeding 100KB, total code/image sizes
135
+ - **Dependencies** (`--dependencies`): Top 10 files with heavy dependencies, top 10 files with light dependencies, top 10 file hotspots (most imported), top 10 dead/alive image hotspots
136
+
71
137
  **Important:** Before viewing the summary, make sure to run a fresh scan with `--clear-cache` to ensure accurate results. The summary reads from the cache, so outdated cache data will show outdated results.
72
138
 
73
139
  ### Scan for Unused Files
@@ -186,13 +252,15 @@ qleaner image public/images src --clear-cache
186
252
  ```
187
253
 
188
254
  **What it detects:**
189
- - Images imported via `import` statements
255
+ - Images imported via `import` statements (static and dynamic imports)
190
256
  - Images required via `require()` calls
191
- - Images used in JSX `src` attributes
257
+ - Images used in JSX `src` attributes (string literals and expressions)
192
258
  - Images in CSS `url()` functions (CSS and SCSS files)
193
259
  - Images in styled-components and CSS-in-JS template literals
194
- - Images in inline styles (backgroundImage, etc.)
195
- - Images referenced in string literals and template literals
260
+ - Images in inline styles (backgroundImage, background, mask, and other image-related properties)
261
+ - Images referenced in string literals and template literals anywhere in code
262
+ - Images in array expressions and spread elements
263
+ - **Dead links**: Images referenced in code but not found in the file system
196
264
 
197
265
  ## Output Formats
198
266
 
@@ -207,7 +275,7 @@ Qleaner provides two output formats:
207
275
  2. **Table output**: Formatted tables with organized columns (use `--table` flag)
208
276
  - Unused files table shows: Unused file paths with sizes (or "Would Delete" in dry run mode)
209
277
  - Unused images table shows: Unused image paths with information about whether they exist and are referenced in code (or "Would Delete" in dry run mode)
210
- - Summary tables show: Project statistics, largest files, dependencies, and more
278
+ - Summary tables show: Project statistics, largest files, dependencies, file hotspots, dead/alive image hotspots, and more
211
279
 
212
280
  ## How It Works
213
281
 
@@ -228,12 +296,15 @@ Qleaner provides two output formats:
228
296
  2. **Code Scanning**: Scans all code files (`.js`, `.jsx`, `.ts`, `.tsx`) for image references
229
297
  3. **Image Detection**: Detects images through multiple methods:
230
298
  - Import statements (`import img from './image.png'`)
299
+ - Dynamic imports (`import('./image.png')`)
231
300
  - Require calls (`require('./image.png')`)
232
- - JSX src attributes (`<img src="./image.png" />`)
301
+ - JSX src attributes (`<img src="./image.png" />` and expressions)
233
302
  - CSS url() functions in CSS/SCSS files
234
303
  - Styled-components and CSS-in-JS template literals
235
- - Inline styles (backgroundImage, etc.)
236
- - String and template literals containing image paths
304
+ - Inline styles (backgroundImage, background, mask, etc.)
305
+ - String and template literals containing image paths (anywhere in code)
306
+ - Array expressions with image paths (`['/img/a.png', '/img/b.png']`)
307
+ - Spread elements in arrays with image references
237
308
  4. **Path Normalization**: Normalizes all detected image paths for consistent matching
238
309
  5. **Analysis**: Compares discovered image files with detected references to identify unused images
239
310
  6. **Reporting**: Outputs the results in standard or table format
@@ -265,49 +336,12 @@ Qleaner provides two output formats:
265
336
  - 📊 **Code analysis**: Understand import patterns and dependencies in your codebase through the summary command
266
337
  - 🔍 **Project audit**: Identify orphaned files and assets that may have been forgotten
267
338
  - 📦 **Bundle optimization**: Find files and images that can be removed to reduce bundle size
268
- - 📈 **Project insights**: Analyze largest files, dependency patterns, and project statistics
339
+ - 📈 **Project insights**: Analyze largest files, dependency patterns, file hotspots, and project statistics
340
+ - 🔗 **Dead link detection**: Find broken image references (images in code that don't exist on disk)
341
+ - 🔥 **Architecture insights**: Discover which files are most imported (hotspots) and identify dependency-heavy files
342
+ - ⚠️ **Performance warnings**: Get alerted about large files (100KB+) that may impact performance
269
343
  - 🎯 **Maintenance**: Keep your codebase clean and maintainable
270
344
 
271
- ## Configuration
272
-
273
- Qleaner supports configuration through both command-line options and a configuration file (`qleaner.config.json`). Use `qleaner init` to create a default configuration file.
274
-
275
- ### Configuration File
276
-
277
- The `qleaner.config.json` file allows you to set default options that will be merged with command-line options. Command-line options take precedence over configuration file options.
278
-
279
- **Configuration options:**
280
- - `excludeDir` - Array of directories to exclude from scans
281
- - `excludeFile` - Array of file names to exclude from scans (just use the file name, e.g., `["test.js", "config.js"]`)
282
- - `excludeExtensions` - Array of file extensions to exclude (e.g., `["test.tsx", "test.ts"]`)
283
- - `excludeFilePrint` - Array of file names to scan but not print in results (just use the file name)
284
- - `excludeDirAssets` - Array of directories to exclude from asset scans
285
- - `excludeFileAssets` - Array of file names to exclude from asset scans (just use the file name)
286
- - `excludeDirCode` - Array of directories to exclude from code scans
287
- - `excludeFileCode` - Array of file names to exclude from code scans (just use the file name)
288
- - `isRootFolderReferenced` - Boolean indicating if root folder is referenced in image paths
289
- - `alias` - Boolean indicating if aliases are used in image paths
290
-
291
- **Common exclusions:**
292
- - Test files: `-f test.js spec.js` or `excludeExtensions: ["test.tsx", "test.ts"]`
293
- - Storybook files: `-f stories.js`
294
- - Test directories: `-e __tests__ __mocks__ test`
295
- - Build outputs: `-e dist build .next`
296
- - Configuration files: `-f config.js`
297
- - Third-party code: `-e node_modules vendor`
298
-
299
- **Example configuration file:**
300
- ```json
301
- {
302
- "excludeDir": ["node_modules", "dist", "build"],
303
- "excludeFile": [],
304
- "excludeExtensions": ["test.tsx", "test.ts"],
305
- "excludeFilePrint": ["page.tsx", "route.ts", "layout.tsx"],
306
- "isRootFolderReferenced": false,
307
- "alias": true
308
- }
309
- ```
310
-
311
345
  ## Tips and Best Practices
312
346
 
313
347
  1. **Start with a small scope**: Begin by scanning a specific directory before scanning the entire project
@@ -365,4 +399,4 @@ MIT
365
399
 
366
400
  ## Version
367
401
 
368
- Current version: 1.0.34
402
+ Current version: 1.1.3
package/command.js CHANGED
@@ -10,7 +10,6 @@ const {
10
10
  getFileHash,
11
11
  needsRebuild,
12
12
  saveCache,
13
- clearCache,
14
13
  } = require("./utils/cache");
15
14
  const { isExcludedFile, createStepBar } = require("./utils/utils");
16
15
 
@@ -114,13 +113,8 @@ async function getFiles(directory = "src", options, chalk) {
114
113
 
115
114
  // Initializes the cache by clearing it (if requested) and loading it from disk
116
115
  function initializeCache(spinner, options, code = true) {
117
- if (options.clearCache) {
118
- spinner.text = "🔍 Clearing cache...";
119
- clearCache(process.cwd());
120
- spinner.succeed("Cache cleared successfully");
121
- }
122
116
  spinner.text = "🔍 Loading cache...";
123
- const cache = loadCache(process.cwd());
117
+ const cache = loadCache(process.cwd(), {code, clearCache: options.clearCache});
124
118
  let graph = null;
125
119
  let imageGraph = null;
126
120
  if (code) {
@@ -182,7 +176,7 @@ async function extractImportsFromFiles(files, graph, resolver, chalk) {
182
176
  if (isNeedsRebuild) {
183
177
  const ast = parser.parse(code, {
184
178
  sourceType: "module",
185
- plugins: ["jsx", "typescript"],
179
+ plugins: ["jsx", "typescript", "decorators-legacy"],
186
180
  });
187
181
  // check if file is already in graph
188
182
  if (graph.has(filePath)) {
@@ -361,7 +361,7 @@ async function scanCodeFilesForImages(
361
361
  if (needsRebuild(filePath, code, imageGraph)) {
362
362
  const ast = parser.parse(code, {
363
363
  sourceType: "module",
364
- plugins: ["jsx", "typescript"],
364
+ plugins: ["jsx", "typescript", "decorators-legacy"],
365
365
  });
366
366
  if (imageGraph.has(filePath)) {
367
367
  const oldFiles = new Set(imageGraph.get(filePath).imports);
@@ -13,6 +13,8 @@ async function init(chalk) {
13
13
  excludeExtensions: [
14
14
  "test.tsx",
15
15
  "test.ts",
16
+ "test.js",
17
+ "test.jsx",
16
18
  ], // Exclude file extensions from the scan like .test.tsx, .test.ts, .test.js, .test.jsx
17
19
  excludeFilePrint: [
18
20
  "page.tsx",
@@ -27,9 +29,7 @@ async function init(chalk) {
27
29
  "dist",
28
30
  "build",
29
31
  ], // Exclude directories from the code scan
30
- excludeFileCode: [
31
- "index.tsx",
32
- ], // Exclude files from the code scan
32
+ excludeFileCode: [], // Exclude files from the code scan
33
33
  isRootFolderReferenced: false, // Is the root folder referenced in the image path eg /img/a.png where img is the image root folder
34
34
  alias: true, // Is the alias referenced in the image path eg @/assets/images/a.png
35
35
  }, null, 2));
@@ -11,7 +11,8 @@ const {
11
11
  findCodeFilesAbove100KB,
12
12
  getTop10FilesWithHeavyDependencies,
13
13
  getTop10FilesWithLightDependencies,
14
- getTop10FilesHotspots
14
+ getTop10FilesHotspots,
15
+ getTotalImageFiles
15
16
  } = require("../utils/summary");
16
17
 
17
18
  function formatFilePath(filePath, maxLength = 70) {
@@ -96,15 +97,15 @@ function summarizeAll(chalk) {
96
97
  console.log(chalk.yellow(" Use the scan command to generate project data.\n"));
97
98
  return;
98
99
  }
99
-
100
+ const totalImageFiles = getTotalImageFiles(imageGraph.graph);
100
101
  // summarize the graph
101
102
  const summary = {
102
103
  totalCodeFiles: codeGraph.graph.size,
103
- totalImageFiles: imageGraph.graph.size,
104
+ totalImageFiles: totalImageFiles,
104
105
  totalUnusedFiles: codeGraph.unusedFiles.size,
105
106
  totalUnusedImages: imageGraph.unusedImages.size - deadImageLinks.size,
106
107
  totalDeadImageLinks: deadImageLinks.size,
107
- totalFiles: codeGraph.graph.size + imageGraph.graph.size,
108
+ totalFiles: codeGraph.graph.size + totalImageFiles,
108
109
  };
109
110
 
110
111
  console.log(chalk.green.bold("\n📋 Project Summary"));
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "qleaner",
3
3
  "description": "A CLI tool for analyzing and identifying unused files, images, imports, and dead code in React, TypeScript, and JavaScript projects to help optimize bundle size and maintain clean codebases",
4
4
  "packageManager": "yarn@4.6.0",
5
- "version": "1.1.3",
5
+ "version": "1.1.5",
6
6
  "license": "MIT",
7
7
  "main": "command.js",
8
8
  "bin": "bin/cli.js",
@@ -7,7 +7,9 @@
7
7
  "excludeFile": [],
8
8
  "excludeExtensions": [
9
9
  "test.tsx",
10
- "test.ts"
10
+ "test.ts",
11
+ "test.js",
12
+ "test.jsx"
11
13
  ],
12
14
  "excludeFilePrint": [
13
15
  "page.tsx",
@@ -22,9 +24,7 @@
22
24
  "dist",
23
25
  "build"
24
26
  ],
25
- "excludeFileCode": [
26
- "index.tsx"
27
- ],
27
+ "excludeFileCode": [],
28
28
  "isRootFolderReferenced": false,
29
29
  "alias": true
30
30
  }
package/utils/cache.js CHANGED
@@ -46,7 +46,7 @@ function serializeGraph({ parentGraph, imageParentGraph, isCode = true }) {
46
46
  };
47
47
  }
48
48
 
49
- function loadCache(rootPath) {
49
+ function loadCache(rootPath, {clearCache, code}) {
50
50
  const file = path.join(rootPath, "unused-check-cache.json");
51
51
  if (!fs.existsSync(file))
52
52
  return {
@@ -62,6 +62,14 @@ function loadCache(rootPath) {
62
62
  imageParentGraph: { imageGraph: {}, unusedImages: [] },
63
63
  };
64
64
  }
65
+ if(clearCache) {
66
+ if(code) {
67
+ cache.parentGraph = { graph: {}, unusedFiles: [] };
68
+ } else {
69
+ cache.imageParentGraph = { imageGraph: {}, unusedImages: [] };
70
+ }
71
+ fs.writeFileSync(file, JSON.stringify(cache, null, 2));
72
+ }
65
73
  return cache;
66
74
  } catch {
67
75
  return {
package/utils/summary.js CHANGED
@@ -89,6 +89,16 @@ function getTop10FilesHotspots(graph, isCheck = true) {
89
89
  }
90
90
  }
91
91
 
92
+ function getTotalImageFiles(graph) {
93
+ let totalFiles = 0;
94
+ for (const [filePath, node] of graph.entries()) {
95
+ if (node.isImage === true) {
96
+ totalFiles++;
97
+ }
98
+ }
99
+ return totalFiles;
100
+ }
101
+
92
102
  module.exports = {
93
103
  getDeadLinks,
94
104
  getTotalImageSize,
@@ -99,4 +109,5 @@ module.exports = {
99
109
  getTop10FilesWithHeavyDependencies,
100
110
  getTop10FilesWithLightDependencies,
101
111
  getTop10FilesHotspots,
112
+ getTotalImageFiles,
102
113
  };