exports-cleanup 0.0.1
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 +231 -0
- package/dist/analyzer/export-analyzer.d.ts +9 -0
- package/dist/analyzer/export-analyzer.d.ts.map +1 -0
- package/dist/analyzer/export-analyzer.js +292 -0
- package/dist/analyzer/export-analyzer.js.map +1 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +69 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/output.d.ts +18 -0
- package/dist/cli/output.d.ts.map +1 -0
- package/dist/cli/output.js +145 -0
- package/dist/cli/output.js.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -0
- package/dist/types/index.d.ts +63 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +2 -0
- package/dist/types/index.js.map +1 -0
- package/package.json +62 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
# exports-cleanup
|
|
2
|
+
|
|
3
|
+
> Find unused exports in your codebase - clean up dead code and reduce bundle size
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/exports-cleanup)
|
|
6
|
+
[](https://opensource.org/licenses/MIT)
|
|
7
|
+
|
|
8
|
+
## Problem
|
|
9
|
+
|
|
10
|
+
You export 100 functions. Only 20 are actually used. Dead code bloats your bundle.
|
|
11
|
+
|
|
12
|
+
## Solution
|
|
13
|
+
|
|
14
|
+
`exports-cleanup` scans your codebase, tracks all imports, and finds exports that are never used anywhere.
|
|
15
|
+
|
|
16
|
+
## Features
|
|
17
|
+
|
|
18
|
+
- **Fast scanning** - Uses regex-based parsing for speed
|
|
19
|
+
- **Bundle size estimation** - Shows potential savings in KB
|
|
20
|
+
- **TypeScript + JavaScript** - Works with .ts, .tsx, .js, .jsx, .mjs
|
|
21
|
+
- **Type-aware** - Optionally include/exclude type exports
|
|
22
|
+
- **CI/CD ready** - Exit code 1 when unused exports found
|
|
23
|
+
- **Zero config** - Just run it
|
|
24
|
+
|
|
25
|
+
## Installation
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
# Run directly with npx (recommended)
|
|
29
|
+
npx exports-cleanup
|
|
30
|
+
|
|
31
|
+
# Or install globally
|
|
32
|
+
npm install -g exports-cleanup
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Usage
|
|
36
|
+
|
|
37
|
+
### Basic Scan
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
# Scan current directory
|
|
41
|
+
npx exports-cleanup
|
|
42
|
+
|
|
43
|
+
# Scan specific directory
|
|
44
|
+
npx exports-cleanup ./src
|
|
45
|
+
|
|
46
|
+
# Compact output
|
|
47
|
+
npx exports-cleanup --compact
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### Include Types
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
# Also check type and interface exports
|
|
54
|
+
npx exports-cleanup --include-types
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### Show All Exports
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
# Show used exports too
|
|
61
|
+
npx exports-cleanup --show-used
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## Example Output
|
|
65
|
+
|
|
66
|
+
```
|
|
67
|
+
🔍 Unused Exports (47 found):
|
|
68
|
+
|
|
69
|
+
Summary:
|
|
70
|
+
Total exports: 120
|
|
71
|
+
Unused: 47
|
|
72
|
+
Used: 73
|
|
73
|
+
Potential savings: 23.4 KB
|
|
74
|
+
|
|
75
|
+
📁 src/utils/helpers.ts
|
|
76
|
+
❌ formatDate() [function]
|
|
77
|
+
Line 12 - exported but never imported
|
|
78
|
+
❌ calculateTax() [function]
|
|
79
|
+
Line 45 - exported but never imported
|
|
80
|
+
❌ DEPRECATED_CONSTANT [const]
|
|
81
|
+
Line 78 - exported but never imported
|
|
82
|
+
|
|
83
|
+
📁 src/utils/validation.ts
|
|
84
|
+
❌ validateEmail() [function]
|
|
85
|
+
Line 5 - exported but never imported
|
|
86
|
+
❌ validatePhone() [function]
|
|
87
|
+
Line 23 - exported but never imported
|
|
88
|
+
|
|
89
|
+
──────────────────────────────────────────────────
|
|
90
|
+
Potential bundle reduction: 23.4 KB
|
|
91
|
+
|
|
92
|
+
💡 Tips:
|
|
93
|
+
• Remove unused exports to reduce bundle size
|
|
94
|
+
• Some exports may be used dynamically (check manually)
|
|
95
|
+
• Entry points and public APIs may show as "unused"
|
|
96
|
+
|
|
97
|
+
──────────────────────────────────────────────────
|
|
98
|
+
Cleaned up dead code? Consider supporting:
|
|
99
|
+
☕ https://buymeacoffee.com/willzhangfly
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
### Compact Output
|
|
103
|
+
|
|
104
|
+
```
|
|
105
|
+
🔍 Found 47 unused exports:
|
|
106
|
+
|
|
107
|
+
src/utils/helpers.ts
|
|
108
|
+
formatDate, calculateTax, DEPRECATED_CONSTANT
|
|
109
|
+
src/utils/validation.ts
|
|
110
|
+
validateEmail, validatePhone
|
|
111
|
+
src/components/OldButton.tsx
|
|
112
|
+
OldButton, OldButtonProps
|
|
113
|
+
|
|
114
|
+
Potential savings: 23.4 KB
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
## Comparison with Alternatives
|
|
118
|
+
|
|
119
|
+
| Feature | exports-cleanup | TypeScript | ESLint | ts-prune | knip |
|
|
120
|
+
|---------|---------------|------------|--------|----------|------|
|
|
121
|
+
| Find unused exports | ✅ | ❌ | ❌ | ✅ | ✅ |
|
|
122
|
+
| Bundle size estimate | ✅ | ❌ | ❌ | ❌ | ❌ |
|
|
123
|
+
| Zero config | ✅ | ❌ | ❌ | ⚠️ | ❌ |
|
|
124
|
+
| Fast | ✅ | ✅ | ✅ | ❌ | ⚠️ |
|
|
125
|
+
| CI/CD exit codes | ✅ | ✅ | ✅ | ⚠️ | ✅ |
|
|
126
|
+
| Actively maintained | ✅ | ✅ | ✅ | ❌ (2021) | ✅ |
|
|
127
|
+
|
|
128
|
+
## CLI Options
|
|
129
|
+
|
|
130
|
+
```
|
|
131
|
+
Usage: exports-cleanup [options] [path]
|
|
132
|
+
|
|
133
|
+
Arguments:
|
|
134
|
+
path Directory to scan (default: ".")
|
|
135
|
+
|
|
136
|
+
Options:
|
|
137
|
+
--json Output results as JSON
|
|
138
|
+
--compact Compact output format
|
|
139
|
+
--include-types Include type and interface exports
|
|
140
|
+
--show-used Also show used exports
|
|
141
|
+
--ignore <patterns> Additional patterns to ignore (comma-separated)
|
|
142
|
+
-V, --version Output version number
|
|
143
|
+
-h, --help Display help
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
## CI/CD Integration
|
|
147
|
+
|
|
148
|
+
```yaml
|
|
149
|
+
# GitHub Actions
|
|
150
|
+
- name: Check for unused exports
|
|
151
|
+
run: npx exports-cleanup
|
|
152
|
+
# Exits with code 1 if unused exports found
|
|
153
|
+
|
|
154
|
+
# With threshold (using jq)
|
|
155
|
+
- name: Check unused exports count
|
|
156
|
+
run: |
|
|
157
|
+
npx exports-cleanup --json > unused.json
|
|
158
|
+
COUNT=$(cat unused.json | jq '.unusedExports')
|
|
159
|
+
if [ "$COUNT" -gt 10 ]; then
|
|
160
|
+
echo "Too many unused exports: $COUNT"
|
|
161
|
+
exit 1
|
|
162
|
+
fi
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
## Programmatic Usage
|
|
166
|
+
|
|
167
|
+
```typescript
|
|
168
|
+
import { analyzeExports } from 'exports-cleanup';
|
|
169
|
+
|
|
170
|
+
const result = await analyzeExports('./src', {
|
|
171
|
+
includeTypes: false,
|
|
172
|
+
exclude: ['**/*.test.ts'],
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
console.log(`Found ${result.unusedExports} unused exports`);
|
|
176
|
+
console.log(`Potential savings: ${result.estimatedSavings} bytes`);
|
|
177
|
+
|
|
178
|
+
// Get unused export names
|
|
179
|
+
for (const file of result.files) {
|
|
180
|
+
for (const exp of file.exports) {
|
|
181
|
+
if (exp.isUnused) {
|
|
182
|
+
console.log(`${exp.export.name} in ${file.file}`);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
## False Positives
|
|
189
|
+
|
|
190
|
+
Some exports may appear unused but are actually used:
|
|
191
|
+
|
|
192
|
+
1. **Entry points** - Main exports used by consumers of your package
|
|
193
|
+
2. **Dynamic imports** - `import()` expressions aren't always detected
|
|
194
|
+
3. **Re-exports** - `export * from './module'`
|
|
195
|
+
4. **Framework conventions** - Next.js pages, React components loaded by name
|
|
196
|
+
5. **Public APIs** - Exports meant for external use
|
|
197
|
+
|
|
198
|
+
Review results manually before removing exports.
|
|
199
|
+
|
|
200
|
+
## Ignored by Default
|
|
201
|
+
|
|
202
|
+
- `node_modules/`
|
|
203
|
+
- `dist/`, `build/`
|
|
204
|
+
- `.next/`
|
|
205
|
+
- `coverage/`
|
|
206
|
+
- `*.d.ts` (declaration files)
|
|
207
|
+
- `*.test.*`, `*.spec.*`
|
|
208
|
+
- `__tests__/`
|
|
209
|
+
|
|
210
|
+
## Requirements
|
|
211
|
+
|
|
212
|
+
- Node.js 18.0.0 or higher
|
|
213
|
+
|
|
214
|
+
## Support
|
|
215
|
+
|
|
216
|
+
This project is maintained in my free time. If it helped clean up your codebase, I'd really appreciate your support:
|
|
217
|
+
|
|
218
|
+
- ⭐ Star the repo—it helps others discover this tool
|
|
219
|
+
- 📢 Share with your team or on social media
|
|
220
|
+
- 🐛 [Report bugs or suggest features](https://github.com/willzhangfly/exports-cleanup/issues)
|
|
221
|
+
- ☕ [Buy me a coffee](https://buymeacoffee.com/willzhangfly) if you'd like to support development
|
|
222
|
+
|
|
223
|
+
Thank you to everyone who has contributed, shared feedback, or helped spread the word!
|
|
224
|
+
|
|
225
|
+
## License
|
|
226
|
+
|
|
227
|
+
MIT
|
|
228
|
+
|
|
229
|
+
---
|
|
230
|
+
|
|
231
|
+
**Made with ❤️ for cleaner codebases**
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { ScanResult } from '../types/index.js';
|
|
2
|
+
/**
|
|
3
|
+
* Analyze exports in a directory
|
|
4
|
+
*/
|
|
5
|
+
export declare function analyzeExports(rootDir: string, options?: {
|
|
6
|
+
includeTypes?: boolean;
|
|
7
|
+
exclude?: string[];
|
|
8
|
+
}): Promise<ScanResult>;
|
|
9
|
+
//# sourceMappingURL=export-analyzer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"export-analyzer.d.ts","sourceRoot":"","sources":["../../src/analyzer/export-analyzer.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAMV,UAAU,EACX,MAAM,mBAAmB,CAAC;AAyN3B;;GAEG;AACH,wBAAsB,cAAc,CAClC,OAAO,EAAE,MAAM,EACf,OAAO,GAAE;IACP,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;CACf,GACL,OAAO,CAAC,UAAU,CAAC,CAyGrB"}
|
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
import { readFileSync } from 'fs';
|
|
2
|
+
import { resolve, dirname } from 'path';
|
|
3
|
+
import glob from 'fast-glob';
|
|
4
|
+
/**
|
|
5
|
+
* Default patterns
|
|
6
|
+
*/
|
|
7
|
+
const DEFAULT_INCLUDE = ['**/*.ts', '**/*.tsx', '**/*.js', '**/*.jsx', '**/*.mjs'];
|
|
8
|
+
const DEFAULT_EXCLUDE = [
|
|
9
|
+
'**/node_modules/**',
|
|
10
|
+
'**/dist/**',
|
|
11
|
+
'**/build/**',
|
|
12
|
+
'**/.next/**',
|
|
13
|
+
'**/coverage/**',
|
|
14
|
+
'**/*.d.ts',
|
|
15
|
+
'**/*.test.*',
|
|
16
|
+
'**/*.spec.*',
|
|
17
|
+
'**/__tests__/**',
|
|
18
|
+
];
|
|
19
|
+
/**
|
|
20
|
+
* Estimate size of a code block in bytes
|
|
21
|
+
*/
|
|
22
|
+
function estimateSize(code) {
|
|
23
|
+
// Rough estimate: minified code is ~60% of original
|
|
24
|
+
return Math.round(code.length * 0.6);
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Extract exports from a file
|
|
28
|
+
*/
|
|
29
|
+
function extractExports(content, filePath) {
|
|
30
|
+
const exports = [];
|
|
31
|
+
const lines = content.split('\n');
|
|
32
|
+
// Regex patterns for different export types
|
|
33
|
+
const patterns = [
|
|
34
|
+
// export function name()
|
|
35
|
+
{ regex: /^export\s+function\s+(\w+)/gm, type: 'function' },
|
|
36
|
+
// export async function name()
|
|
37
|
+
{ regex: /^export\s+async\s+function\s+(\w+)/gm, type: 'function' },
|
|
38
|
+
// export class Name
|
|
39
|
+
{ regex: /^export\s+class\s+(\w+)/gm, type: 'class' },
|
|
40
|
+
// export const name =
|
|
41
|
+
{ regex: /^export\s+const\s+(\w+)\s*[=:]/gm, type: 'const' },
|
|
42
|
+
// export let name =
|
|
43
|
+
{ regex: /^export\s+let\s+(\w+)\s*[=:]/gm, type: 'variable' },
|
|
44
|
+
// export var name =
|
|
45
|
+
{ regex: /^export\s+var\s+(\w+)\s*[=:]/gm, type: 'variable' },
|
|
46
|
+
// export type Name =
|
|
47
|
+
{ regex: /^export\s+type\s+(\w+)/gm, type: 'type' },
|
|
48
|
+
// export interface Name
|
|
49
|
+
{ regex: /^export\s+interface\s+(\w+)/gm, type: 'interface' },
|
|
50
|
+
// export enum Name
|
|
51
|
+
{ regex: /^export\s+enum\s+(\w+)/gm, type: 'enum' },
|
|
52
|
+
// export default function/class/etc
|
|
53
|
+
{ regex: /^export\s+default\s+(?:function|class)\s+(\w+)?/gm, type: 'default' },
|
|
54
|
+
// export default name
|
|
55
|
+
{ regex: /^export\s+default\s+(\w+)\s*;?$/gm, type: 'default' },
|
|
56
|
+
];
|
|
57
|
+
// Find line numbers
|
|
58
|
+
const getLineNumber = (index) => {
|
|
59
|
+
let line = 1;
|
|
60
|
+
for (let i = 0; i < index && i < content.length; i++) {
|
|
61
|
+
if (content[i] === '\n')
|
|
62
|
+
line++;
|
|
63
|
+
}
|
|
64
|
+
return line;
|
|
65
|
+
};
|
|
66
|
+
for (const { regex, type } of patterns) {
|
|
67
|
+
let match;
|
|
68
|
+
while ((match = regex.exec(content)) !== null) {
|
|
69
|
+
const name = match[1] || 'default';
|
|
70
|
+
const line = getLineNumber(match.index);
|
|
71
|
+
// Estimate size of the export
|
|
72
|
+
let endIndex = content.indexOf('\n\n', match.index);
|
|
73
|
+
if (endIndex === -1)
|
|
74
|
+
endIndex = content.length;
|
|
75
|
+
const code = content.slice(match.index, endIndex);
|
|
76
|
+
exports.push({
|
|
77
|
+
name,
|
|
78
|
+
type,
|
|
79
|
+
file: filePath,
|
|
80
|
+
line,
|
|
81
|
+
isDefault: type === 'default',
|
|
82
|
+
estimatedSize: estimateSize(code),
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
// Handle export { name1, name2 }
|
|
87
|
+
const namedExportRegex = /^export\s*\{([^}]+)\}/gm;
|
|
88
|
+
let match;
|
|
89
|
+
while ((match = namedExportRegex.exec(content)) !== null) {
|
|
90
|
+
const names = match[1].split(',').map(n => {
|
|
91
|
+
const parts = n.trim().split(/\s+as\s+/);
|
|
92
|
+
return parts[parts.length - 1].trim();
|
|
93
|
+
});
|
|
94
|
+
const line = getLineNumber(match.index);
|
|
95
|
+
for (const name of names) {
|
|
96
|
+
if (name && !exports.some(e => e.name === name)) {
|
|
97
|
+
exports.push({
|
|
98
|
+
name,
|
|
99
|
+
type: 'const',
|
|
100
|
+
file: filePath,
|
|
101
|
+
line,
|
|
102
|
+
isDefault: false,
|
|
103
|
+
estimatedSize: 100, // Rough estimate for re-exports
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
return exports;
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Extract imports from a file
|
|
112
|
+
*/
|
|
113
|
+
function extractImports(content, filePath, rootDir) {
|
|
114
|
+
const imports = [];
|
|
115
|
+
const lines = content.split('\n');
|
|
116
|
+
// Patterns for imports
|
|
117
|
+
const importPatterns = [
|
|
118
|
+
// import { name } from './file'
|
|
119
|
+
/import\s*\{([^}]+)\}\s*from\s*['"]([^'"]+)['"]/g,
|
|
120
|
+
// import name from './file'
|
|
121
|
+
/import\s+(\w+)\s+from\s*['"]([^'"]+)['"]/g,
|
|
122
|
+
// import * as name from './file'
|
|
123
|
+
/import\s*\*\s*as\s+(\w+)\s+from\s*['"]([^'"]+)['"]/g,
|
|
124
|
+
];
|
|
125
|
+
const getLineNumber = (index) => {
|
|
126
|
+
let line = 1;
|
|
127
|
+
for (let i = 0; i < index && i < content.length; i++) {
|
|
128
|
+
if (content[i] === '\n')
|
|
129
|
+
line++;
|
|
130
|
+
}
|
|
131
|
+
return line;
|
|
132
|
+
};
|
|
133
|
+
// Named imports
|
|
134
|
+
const namedImportRegex = /import\s*\{([^}]+)\}\s*from\s*['"]([^'"]+)['"]/g;
|
|
135
|
+
let match;
|
|
136
|
+
while ((match = namedImportRegex.exec(content)) !== null) {
|
|
137
|
+
const names = match[1].split(',').map(n => {
|
|
138
|
+
const parts = n.trim().split(/\s+as\s+/);
|
|
139
|
+
return parts[0].trim();
|
|
140
|
+
});
|
|
141
|
+
const fromPath = match[2];
|
|
142
|
+
const line = getLineNumber(match.index);
|
|
143
|
+
// Skip external packages
|
|
144
|
+
if (!fromPath.startsWith('.') && !fromPath.startsWith('/'))
|
|
145
|
+
continue;
|
|
146
|
+
const resolvedPath = resolveImportPath(fromPath, filePath, rootDir);
|
|
147
|
+
for (const name of names) {
|
|
148
|
+
if (name) {
|
|
149
|
+
imports.push({
|
|
150
|
+
name,
|
|
151
|
+
fromFile: resolvedPath,
|
|
152
|
+
toFile: filePath,
|
|
153
|
+
line,
|
|
154
|
+
isDefault: false,
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
// Default imports
|
|
160
|
+
const defaultImportRegex = /import\s+(\w+)(?:\s*,\s*\{[^}]*\})?\s+from\s*['"]([^'"]+)['"]/g;
|
|
161
|
+
while ((match = defaultImportRegex.exec(content)) !== null) {
|
|
162
|
+
const name = match[1];
|
|
163
|
+
const fromPath = match[2];
|
|
164
|
+
const line = getLineNumber(match.index);
|
|
165
|
+
if (!fromPath.startsWith('.') && !fromPath.startsWith('/'))
|
|
166
|
+
continue;
|
|
167
|
+
const resolvedPath = resolveImportPath(fromPath, filePath, rootDir);
|
|
168
|
+
imports.push({
|
|
169
|
+
name: 'default',
|
|
170
|
+
fromFile: resolvedPath,
|
|
171
|
+
toFile: filePath,
|
|
172
|
+
line,
|
|
173
|
+
isDefault: true,
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
return imports;
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Resolve import path to absolute path
|
|
180
|
+
*/
|
|
181
|
+
function resolveImportPath(importPath, fromFile, rootDir) {
|
|
182
|
+
const dir = dirname(fromFile);
|
|
183
|
+
let resolved = resolve(dir, importPath);
|
|
184
|
+
// Try adding extensions
|
|
185
|
+
const extensions = ['.ts', '.tsx', '.js', '.jsx', '.mjs', '/index.ts', '/index.tsx', '/index.js'];
|
|
186
|
+
for (const ext of extensions) {
|
|
187
|
+
const withExt = resolved + ext;
|
|
188
|
+
try {
|
|
189
|
+
readFileSync(withExt);
|
|
190
|
+
return withExt;
|
|
191
|
+
}
|
|
192
|
+
catch {
|
|
193
|
+
continue;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
return resolved;
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Analyze exports in a directory
|
|
200
|
+
*/
|
|
201
|
+
export async function analyzeExports(rootDir, options = {}) {
|
|
202
|
+
const { includeTypes = false, exclude = [] } = options;
|
|
203
|
+
const allExcludes = [...DEFAULT_EXCLUDE, ...exclude];
|
|
204
|
+
// Get all files
|
|
205
|
+
const files = await glob(DEFAULT_INCLUDE, {
|
|
206
|
+
cwd: rootDir,
|
|
207
|
+
absolute: true,
|
|
208
|
+
ignore: allExcludes,
|
|
209
|
+
onlyFiles: true,
|
|
210
|
+
});
|
|
211
|
+
// Extract all exports and imports
|
|
212
|
+
const allExports = [];
|
|
213
|
+
const allImports = [];
|
|
214
|
+
for (const file of files) {
|
|
215
|
+
try {
|
|
216
|
+
const content = readFileSync(file, 'utf-8');
|
|
217
|
+
const exports = extractExports(content, file);
|
|
218
|
+
const imports = extractImports(content, file, rootDir);
|
|
219
|
+
allExports.push(...exports);
|
|
220
|
+
allImports.push(...imports);
|
|
221
|
+
}
|
|
222
|
+
catch {
|
|
223
|
+
continue;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
// Filter out type exports if not including them
|
|
227
|
+
const filteredExports = includeTypes
|
|
228
|
+
? allExports
|
|
229
|
+
: allExports.filter(e => e.type !== 'type' && e.type !== 'interface');
|
|
230
|
+
// Analyze usage
|
|
231
|
+
const fileAnalysisMap = new Map();
|
|
232
|
+
for (const exp of filteredExports) {
|
|
233
|
+
// Find imports of this export
|
|
234
|
+
const usages = allImports.filter(imp => {
|
|
235
|
+
const isSameFile = imp.fromFile === exp.file ||
|
|
236
|
+
imp.fromFile.replace(/\.(ts|tsx|js|jsx|mjs)$/, '') === exp.file.replace(/\.(ts|tsx|js|jsx|mjs)$/, '');
|
|
237
|
+
if (!isSameFile)
|
|
238
|
+
return false;
|
|
239
|
+
if (exp.isDefault) {
|
|
240
|
+
return imp.isDefault;
|
|
241
|
+
}
|
|
242
|
+
return imp.name === exp.name;
|
|
243
|
+
});
|
|
244
|
+
const usedIn = [...new Set(usages.map(u => u.toFile))];
|
|
245
|
+
const isUnused = usedIn.length === 0;
|
|
246
|
+
const analysis = {
|
|
247
|
+
export: exp,
|
|
248
|
+
usageCount: usedIn.length,
|
|
249
|
+
usedIn,
|
|
250
|
+
isUnused,
|
|
251
|
+
};
|
|
252
|
+
// Add to file analysis
|
|
253
|
+
if (!fileAnalysisMap.has(exp.file)) {
|
|
254
|
+
fileAnalysisMap.set(exp.file, {
|
|
255
|
+
file: exp.file,
|
|
256
|
+
exports: [],
|
|
257
|
+
unusedCount: 0,
|
|
258
|
+
usedCount: 0,
|
|
259
|
+
});
|
|
260
|
+
}
|
|
261
|
+
const fileAnalysis = fileAnalysisMap.get(exp.file);
|
|
262
|
+
fileAnalysis.exports.push(analysis);
|
|
263
|
+
if (isUnused) {
|
|
264
|
+
fileAnalysis.unusedCount++;
|
|
265
|
+
}
|
|
266
|
+
else {
|
|
267
|
+
fileAnalysis.usedCount++;
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
// Calculate totals
|
|
271
|
+
const fileAnalyses = Array.from(fileAnalysisMap.values())
|
|
272
|
+
.filter(f => f.exports.length > 0)
|
|
273
|
+
.sort((a, b) => b.unusedCount - a.unusedCount);
|
|
274
|
+
let totalExports = 0;
|
|
275
|
+
let unusedExports = 0;
|
|
276
|
+
let estimatedSavings = 0;
|
|
277
|
+
for (const fa of fileAnalyses) {
|
|
278
|
+
totalExports += fa.exports.length;
|
|
279
|
+
unusedExports += fa.unusedCount;
|
|
280
|
+
estimatedSavings += fa.exports
|
|
281
|
+
.filter(e => e.isUnused)
|
|
282
|
+
.reduce((sum, e) => sum + e.export.estimatedSize, 0);
|
|
283
|
+
}
|
|
284
|
+
return {
|
|
285
|
+
files: fileAnalyses,
|
|
286
|
+
totalExports,
|
|
287
|
+
unusedExports,
|
|
288
|
+
usedExports: totalExports - unusedExports,
|
|
289
|
+
estimatedSavings,
|
|
290
|
+
};
|
|
291
|
+
}
|
|
292
|
+
//# sourceMappingURL=export-analyzer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"export-analyzer.js","sourceRoot":"","sources":["../../src/analyzer/export-analyzer.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { Command } from 'commander';
|
|
3
|
+
import { resolve } from 'path';
|
|
4
|
+
import chalk from 'chalk';
|
|
5
|
+
import ora from 'ora';
|
|
6
|
+
import { analyzeExports } from '../analyzer/export-analyzer.js';
|
|
7
|
+
import { printScanResult, printCompactResult, printJsonResult, printSupportMessage } from './output.js';
|
|
8
|
+
const program = new Command();
|
|
9
|
+
program
|
|
10
|
+
.name('exports-cleanup')
|
|
11
|
+
.description('Find unused exports in your codebase')
|
|
12
|
+
.version('0.0.1');
|
|
13
|
+
program
|
|
14
|
+
.argument('[path]', 'Directory to scan', '.')
|
|
15
|
+
.option('--json', 'Output results as JSON', false)
|
|
16
|
+
.option('--compact', 'Compact output format', false)
|
|
17
|
+
.option('--include-types', 'Include type and interface exports', false)
|
|
18
|
+
.option('--show-used', 'Also show used exports', false)
|
|
19
|
+
.option('--ignore <patterns>', 'Additional patterns to ignore (comma-separated)', '')
|
|
20
|
+
.action(async (pathArg, options) => {
|
|
21
|
+
try {
|
|
22
|
+
const projectPath = resolve(pathArg);
|
|
23
|
+
// Parse ignore patterns
|
|
24
|
+
const ignorePatterns = options.ignore
|
|
25
|
+
? options.ignore.split(',').map(p => p.trim())
|
|
26
|
+
: [];
|
|
27
|
+
// Scan
|
|
28
|
+
const spinner = ora('Scanning for exports...').start();
|
|
29
|
+
let result;
|
|
30
|
+
try {
|
|
31
|
+
result = await analyzeExports(projectPath, {
|
|
32
|
+
includeTypes: options.includeTypes,
|
|
33
|
+
exclude: ignorePatterns,
|
|
34
|
+
});
|
|
35
|
+
spinner.stop();
|
|
36
|
+
}
|
|
37
|
+
catch (error) {
|
|
38
|
+
spinner.fail('Scan failed');
|
|
39
|
+
throw error;
|
|
40
|
+
}
|
|
41
|
+
// Output
|
|
42
|
+
if (options.json) {
|
|
43
|
+
printJsonResult(result, projectPath);
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
if (options.compact) {
|
|
47
|
+
printCompactResult(result, projectPath);
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
printScanResult(result, projectPath, options.showUsed);
|
|
51
|
+
}
|
|
52
|
+
printSupportMessage();
|
|
53
|
+
// Exit with code 1 if unused exports found (useful for CI)
|
|
54
|
+
if (result.unusedExports > 0) {
|
|
55
|
+
process.exit(1);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
catch (error) {
|
|
59
|
+
if (error instanceof Error) {
|
|
60
|
+
console.error(chalk.red(`\n❌ Error: ${error.message}`));
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
console.error(chalk.red('\n❌ An unexpected error occurred'));
|
|
64
|
+
}
|
|
65
|
+
process.exit(1);
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
program.parse();
|
|
69
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { ScanResult } from '../types/index.js';
|
|
2
|
+
/**
|
|
3
|
+
* Print scan results
|
|
4
|
+
*/
|
|
5
|
+
export declare function printScanResult(result: ScanResult, cwd: string, showUsed?: boolean): void;
|
|
6
|
+
/**
|
|
7
|
+
* Print compact results
|
|
8
|
+
*/
|
|
9
|
+
export declare function printCompactResult(result: ScanResult, cwd: string): void;
|
|
10
|
+
/**
|
|
11
|
+
* Print JSON result
|
|
12
|
+
*/
|
|
13
|
+
export declare function printJsonResult(result: ScanResult, cwd: string): void;
|
|
14
|
+
/**
|
|
15
|
+
* Print support message
|
|
16
|
+
*/
|
|
17
|
+
export declare function printSupportMessage(): void;
|
|
18
|
+
//# sourceMappingURL=output.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"output.d.ts","sourceRoot":"","sources":["../../src/cli/output.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAgC,MAAM,mBAAmB,CAAC;AAmClF;;GAEG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,UAAU,EAAE,GAAG,EAAE,MAAM,EAAE,QAAQ,GAAE,OAAe,GAAG,IAAI,CAqDhG;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,UAAU,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI,CA2BxE;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,UAAU,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI,CAmBrE;AAED;;GAEG;AACH,wBAAgB,mBAAmB,IAAI,IAAI,CAK1C"}
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import { relative } from 'path';
|
|
3
|
+
/**
|
|
4
|
+
* Format bytes to human readable
|
|
5
|
+
*/
|
|
6
|
+
function formatBytes(bytes) {
|
|
7
|
+
if (bytes < 1024)
|
|
8
|
+
return `${bytes} B`;
|
|
9
|
+
if (bytes < 1024 * 1024)
|
|
10
|
+
return `${(bytes / 1024).toFixed(1)} KB`;
|
|
11
|
+
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Get color for export type
|
|
15
|
+
*/
|
|
16
|
+
function getTypeColor(type) {
|
|
17
|
+
switch (type) {
|
|
18
|
+
case 'function':
|
|
19
|
+
return chalk.blue;
|
|
20
|
+
case 'class':
|
|
21
|
+
return chalk.magenta;
|
|
22
|
+
case 'const':
|
|
23
|
+
case 'variable':
|
|
24
|
+
return chalk.cyan;
|
|
25
|
+
case 'type':
|
|
26
|
+
case 'interface':
|
|
27
|
+
return chalk.gray;
|
|
28
|
+
case 'enum':
|
|
29
|
+
return chalk.yellow;
|
|
30
|
+
case 'default':
|
|
31
|
+
return chalk.green;
|
|
32
|
+
default:
|
|
33
|
+
return chalk.white;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Print scan results
|
|
38
|
+
*/
|
|
39
|
+
export function printScanResult(result, cwd, showUsed = false) {
|
|
40
|
+
console.log();
|
|
41
|
+
if (result.unusedExports === 0) {
|
|
42
|
+
console.log(chalk.green('✅ No unused exports found!'));
|
|
43
|
+
console.log(chalk.gray(` Scanned ${result.totalExports} exports across ${result.files.length} files.`));
|
|
44
|
+
console.log();
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
console.log(chalk.bold.yellow(`🔍 Unused Exports (${result.unusedExports} found):`));
|
|
48
|
+
console.log();
|
|
49
|
+
// Summary
|
|
50
|
+
console.log(chalk.gray('Summary:'));
|
|
51
|
+
console.log(` Total exports: ${result.totalExports}`);
|
|
52
|
+
console.log(` ${chalk.red(`Unused:`)} ${result.unusedExports}`);
|
|
53
|
+
console.log(` ${chalk.green(`Used:`)} ${result.usedExports}`);
|
|
54
|
+
console.log(` Potential savings: ${chalk.cyan(formatBytes(result.estimatedSavings))}`);
|
|
55
|
+
console.log();
|
|
56
|
+
// Files with unused exports
|
|
57
|
+
for (const fileAnalysis of result.files) {
|
|
58
|
+
if (fileAnalysis.unusedCount === 0 && !showUsed)
|
|
59
|
+
continue;
|
|
60
|
+
const relativePath = relative(cwd, fileAnalysis.file);
|
|
61
|
+
console.log(chalk.bold.white(`📁 ${relativePath}`));
|
|
62
|
+
for (const exp of fileAnalysis.exports) {
|
|
63
|
+
if (exp.isUnused) {
|
|
64
|
+
const typeColor = getTypeColor(exp.export.type);
|
|
65
|
+
const typeLabel = typeColor(`[${exp.export.type}]`);
|
|
66
|
+
console.log(` ${chalk.red('❌')} ${exp.export.name}() ${typeLabel}`);
|
|
67
|
+
console.log(chalk.gray(` Line ${exp.export.line} - exported but never imported`));
|
|
68
|
+
}
|
|
69
|
+
else if (showUsed) {
|
|
70
|
+
const typeColor = getTypeColor(exp.export.type);
|
|
71
|
+
const typeLabel = typeColor(`[${exp.export.type}]`);
|
|
72
|
+
console.log(` ${chalk.green('✅')} ${exp.export.name}() ${typeLabel}`);
|
|
73
|
+
console.log(chalk.gray(` Used in ${exp.usageCount} file(s)`));
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
console.log();
|
|
77
|
+
}
|
|
78
|
+
// Tips
|
|
79
|
+
console.log(chalk.gray('─'.repeat(50)));
|
|
80
|
+
console.log(chalk.bold('Potential bundle reduction:'), chalk.cyan(formatBytes(result.estimatedSavings)));
|
|
81
|
+
console.log();
|
|
82
|
+
console.log(chalk.gray('💡 Tips:'));
|
|
83
|
+
console.log(chalk.gray(' • Remove unused exports to reduce bundle size'));
|
|
84
|
+
console.log(chalk.gray(' • Some exports may be used dynamically (check manually)'));
|
|
85
|
+
console.log(chalk.gray(' • Entry points and public APIs may show as "unused"'));
|
|
86
|
+
console.log();
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Print compact results
|
|
90
|
+
*/
|
|
91
|
+
export function printCompactResult(result, cwd) {
|
|
92
|
+
console.log();
|
|
93
|
+
if (result.unusedExports === 0) {
|
|
94
|
+
console.log(chalk.green('✅ No unused exports found!'));
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
console.log(chalk.bold.yellow(`🔍 Found ${result.unusedExports} unused exports:`));
|
|
98
|
+
console.log();
|
|
99
|
+
for (const fileAnalysis of result.files) {
|
|
100
|
+
if (fileAnalysis.unusedCount === 0)
|
|
101
|
+
continue;
|
|
102
|
+
const relativePath = relative(cwd, fileAnalysis.file);
|
|
103
|
+
const unusedNames = fileAnalysis.exports
|
|
104
|
+
.filter(e => e.isUnused)
|
|
105
|
+
.map(e => e.export.name)
|
|
106
|
+
.join(', ');
|
|
107
|
+
console.log(` ${chalk.gray(relativePath)}`);
|
|
108
|
+
console.log(` ${chalk.red(unusedNames)}`);
|
|
109
|
+
}
|
|
110
|
+
console.log();
|
|
111
|
+
console.log(chalk.gray(`Potential savings: ${formatBytes(result.estimatedSavings)}`));
|
|
112
|
+
console.log();
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Print JSON result
|
|
116
|
+
*/
|
|
117
|
+
export function printJsonResult(result, cwd) {
|
|
118
|
+
// Convert to relative paths for cleaner output
|
|
119
|
+
const output = {
|
|
120
|
+
...result,
|
|
121
|
+
files: result.files.map(f => ({
|
|
122
|
+
...f,
|
|
123
|
+
file: relative(cwd, f.file),
|
|
124
|
+
exports: f.exports.map(e => ({
|
|
125
|
+
...e,
|
|
126
|
+
export: {
|
|
127
|
+
...e.export,
|
|
128
|
+
file: relative(cwd, e.export.file),
|
|
129
|
+
},
|
|
130
|
+
usedIn: e.usedIn.map(u => relative(cwd, u)),
|
|
131
|
+
})),
|
|
132
|
+
})),
|
|
133
|
+
};
|
|
134
|
+
console.log(JSON.stringify(output, null, 2));
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Print support message
|
|
138
|
+
*/
|
|
139
|
+
export function printSupportMessage() {
|
|
140
|
+
console.log(chalk.gray('─'.repeat(50)));
|
|
141
|
+
console.log(chalk.gray('Cleaned up dead code? Consider supporting:'));
|
|
142
|
+
console.log(chalk.cyan('☕ https://buymeacoffee.com/willzhangfly'));
|
|
143
|
+
console.log();
|
|
144
|
+
}
|
|
145
|
+
//# sourceMappingURL=output.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"output.js","sourceRoot":"","sources":["../../src/cli/output.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* unused-exports
|
|
3
|
+
* Find and remove unused exports from your codebase
|
|
4
|
+
*/
|
|
5
|
+
export type { ExportType, ExportedItem, ImportReference, ExportAnalysis, FileAnalysis, ScanResult, CliOptions, } from './types/index.js';
|
|
6
|
+
export { analyzeExports } from './analyzer/export-analyzer.js';
|
|
7
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,YAAY,EACV,UAAU,EACV,YAAY,EACZ,eAAe,EACf,cAAc,EACd,YAAY,EACZ,UAAU,EACV,UAAU,GACX,MAAM,kBAAkB,CAAC;AAG1B,OAAO,EAAE,cAAc,EAAE,MAAM,+BAA+B,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAaH,WAAW;AACX,OAAO,EAAE,cAAc,EAAE,MAAM,+BAA+B,CAAC"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Type of export
|
|
3
|
+
*/
|
|
4
|
+
export type ExportType = 'function' | 'class' | 'const' | 'type' | 'interface' | 'enum' | 'default' | 'variable';
|
|
5
|
+
/**
|
|
6
|
+
* Represents an exported item
|
|
7
|
+
*/
|
|
8
|
+
export interface ExportedItem {
|
|
9
|
+
name: string;
|
|
10
|
+
type: ExportType;
|
|
11
|
+
file: string;
|
|
12
|
+
line: number;
|
|
13
|
+
isDefault: boolean;
|
|
14
|
+
estimatedSize: number;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Import reference
|
|
18
|
+
*/
|
|
19
|
+
export interface ImportReference {
|
|
20
|
+
name: string;
|
|
21
|
+
fromFile: string;
|
|
22
|
+
toFile: string;
|
|
23
|
+
line: number;
|
|
24
|
+
isDefault: boolean;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Analysis result for an export
|
|
28
|
+
*/
|
|
29
|
+
export interface ExportAnalysis {
|
|
30
|
+
export: ExportedItem;
|
|
31
|
+
usageCount: number;
|
|
32
|
+
usedIn: string[];
|
|
33
|
+
isUnused: boolean;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* File analysis result
|
|
37
|
+
*/
|
|
38
|
+
export interface FileAnalysis {
|
|
39
|
+
file: string;
|
|
40
|
+
exports: ExportAnalysis[];
|
|
41
|
+
unusedCount: number;
|
|
42
|
+
usedCount: number;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Overall scan result
|
|
46
|
+
*/
|
|
47
|
+
export interface ScanResult {
|
|
48
|
+
files: FileAnalysis[];
|
|
49
|
+
totalExports: number;
|
|
50
|
+
unusedExports: number;
|
|
51
|
+
usedExports: number;
|
|
52
|
+
estimatedSavings: number;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* CLI options
|
|
56
|
+
*/
|
|
57
|
+
export interface CliOptions {
|
|
58
|
+
path: string;
|
|
59
|
+
json: boolean;
|
|
60
|
+
includeTypes: boolean;
|
|
61
|
+
ignore: string[];
|
|
62
|
+
}
|
|
63
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,MAAM,UAAU,GAAG,UAAU,GAAG,OAAO,GAAG,OAAO,GAAG,MAAM,GAAG,WAAW,GAAG,MAAM,GAAG,SAAS,GAAG,UAAU,CAAC;AAEjH;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,UAAU,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,OAAO,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;CACvB;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,OAAO,CAAC;CACpB;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,YAAY,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,QAAQ,EAAE,OAAO,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,cAAc,EAAE,CAAC;IAC1B,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,YAAY,EAAE,CAAC;IACtB,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,MAAM,CAAC;IACpB,gBAAgB,EAAE,MAAM,CAAC;CAC1B;AAED;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,OAAO,CAAC;IACd,YAAY,EAAE,OAAO,CAAC;IACtB,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":""}
|
package/package.json
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "exports-cleanup",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "Find and remove unused exports - clean up dead code from your codebase",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"bin": {
|
|
9
|
+
"exports-cleanup": "./dist/cli/index.js"
|
|
10
|
+
},
|
|
11
|
+
"files": [
|
|
12
|
+
"dist",
|
|
13
|
+
"README.md",
|
|
14
|
+
"LICENSE"
|
|
15
|
+
],
|
|
16
|
+
"scripts": {
|
|
17
|
+
"build": "tsc",
|
|
18
|
+
"dev": "tsx src/cli/index.ts",
|
|
19
|
+
"prepublishOnly": "npm run build",
|
|
20
|
+
"clean": "rm -rf dist"
|
|
21
|
+
},
|
|
22
|
+
"keywords": [
|
|
23
|
+
"unused",
|
|
24
|
+
"exports",
|
|
25
|
+
"dead-code",
|
|
26
|
+
"cleanup",
|
|
27
|
+
"refactor",
|
|
28
|
+
"typescript",
|
|
29
|
+
"bundle",
|
|
30
|
+
"size",
|
|
31
|
+
"lint",
|
|
32
|
+
"analyzer"
|
|
33
|
+
],
|
|
34
|
+
"author": "",
|
|
35
|
+
"license": "MIT",
|
|
36
|
+
"repository": {
|
|
37
|
+
"type": "git",
|
|
38
|
+
"url": "git+https://github.com/willzhangfly/unused-exports.git"
|
|
39
|
+
},
|
|
40
|
+
"bugs": {
|
|
41
|
+
"url": "https://github.com/willzhangfly/unused-exports/issues"
|
|
42
|
+
},
|
|
43
|
+
"homepage": "https://github.com/willzhangfly/unused-exports#readme",
|
|
44
|
+
"funding": {
|
|
45
|
+
"type": "buymeacoffee",
|
|
46
|
+
"url": "https://buymeacoffee.com/willzhangfly"
|
|
47
|
+
},
|
|
48
|
+
"engines": {
|
|
49
|
+
"node": ">=18.0.0"
|
|
50
|
+
},
|
|
51
|
+
"dependencies": {
|
|
52
|
+
"commander": "^12.0.0",
|
|
53
|
+
"chalk": "^5.3.0",
|
|
54
|
+
"fast-glob": "^3.3.2",
|
|
55
|
+
"ora": "^8.0.0"
|
|
56
|
+
},
|
|
57
|
+
"devDependencies": {
|
|
58
|
+
"typescript": "^5.3.0",
|
|
59
|
+
"@types/node": "^20.11.0",
|
|
60
|
+
"tsx": "^4.7.0"
|
|
61
|
+
}
|
|
62
|
+
}
|