pkg-clean 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.
Files changed (43) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +226 -0
  3. package/dist/analyzer.d.ts +24 -0
  4. package/dist/analyzer.d.ts.map +1 -0
  5. package/dist/analyzer.js +75 -0
  6. package/dist/analyzer.js.map +1 -0
  7. package/dist/analyzer.test.d.ts +1 -0
  8. package/dist/analyzer.test.js +89 -0
  9. package/dist/cli.d.ts +2 -0
  10. package/dist/cli.d.ts.map +1 -0
  11. package/dist/cli.js +27 -0
  12. package/dist/cli.js.map +1 -0
  13. package/dist/configLoader.d.ts +40 -0
  14. package/dist/configLoader.d.ts.map +1 -0
  15. package/dist/configLoader.js +97 -0
  16. package/dist/configLoader.js.map +1 -0
  17. package/dist/index.d.ts +24 -0
  18. package/dist/index.d.ts.map +1 -0
  19. package/dist/index.js +179 -0
  20. package/dist/index.js.map +1 -0
  21. package/dist/index.test.d.ts +1 -0
  22. package/dist/index.test.js +316 -0
  23. package/dist/packageManager.d.ts +14 -0
  24. package/dist/packageManager.d.ts.map +1 -0
  25. package/dist/packageManager.js +67 -0
  26. package/dist/packageManager.js.map +1 -0
  27. package/dist/packageManagerFactory.d.ts +37 -0
  28. package/dist/packageManagerFactory.d.ts.map +1 -0
  29. package/dist/packageManagerFactory.js +158 -0
  30. package/dist/packageManagerFactory.js.map +1 -0
  31. package/dist/registry.d.ts +25 -0
  32. package/dist/registry.d.ts.map +1 -0
  33. package/dist/registry.js +61 -0
  34. package/dist/registry.js.map +1 -0
  35. package/dist/registry.test.d.ts +1 -0
  36. package/dist/registry.test.js +72 -0
  37. package/dist/scanner.d.ts +20 -0
  38. package/dist/scanner.d.ts.map +1 -0
  39. package/dist/scanner.js +89 -0
  40. package/dist/scanner.js.map +1 -0
  41. package/dist/scanner.test.d.ts +1 -0
  42. package/dist/scanner.test.js +79 -0
  43. package/package.json +53 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026
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,226 @@
1
+ # pkg-clean
2
+
3
+ > Find and remove unused dependencies from your Node.js project
4
+
5
+ [![npm version](https://img.shields.io/npm/v/pkg-clean)](https://www.npmjs.com/package/pkg-clean)
6
+ [![npm downloads](https://img.shields.io/npm/dm/pkg-clean)](https://www.npmjs.com/package/pkg-clean)
7
+ [![GitHub license](https://img.shields.io/github/license/apassanisi/pkg-clean)](LICENSE)
8
+ [![Tests](https://github.com/apassanisi/pkg-clean/actions/workflows/tests.yml/badge.svg)](https://github.com/apassanisi/pkg-clean/actions)
9
+
10
+ A minimal, fast CLI tool that scans your project for unused npm dependencies. Identifies unused packages, duplicates, and deprecated packages in seconds.
11
+
12
+ ## Features
13
+
14
+ - **Scan for unused dependencies** - finds packages in package.json that aren't imported anywhere
15
+ - **Detect duplicates** - spots packages listed in both `dependencies` and `devDependencies`
16
+ - **Check deprecation status** - warns about deprecated packages via NPM registry
17
+ - **Safe by default** - uses dry-run mode, only removes with `--remove` flag
18
+ - **Fast** - scans large projects in under a second
19
+ - **Minimal & slick CLI** - beautiful colored output with clear formatting
20
+
21
+ ## Installation
22
+
23
+ ```bash
24
+ npm install -g pkg-clean
25
+ ```
26
+
27
+ Or run directly without installing:
28
+
29
+ ```bash
30
+ npx pkg-clean
31
+ ```
32
+
33
+ ## Usage
34
+
35
+ ### Scan current project
36
+
37
+ ```bash
38
+ pkg-clean
39
+ ```
40
+
41
+ Output:
42
+ ```
43
+ ◆ pkg-clean
44
+ → Scanning files...
45
+ ✓ Found 12 imports
46
+ → Analyzing dependencies...
47
+ → Checking for deprecated packages...
48
+
49
+ ◆ Report
50
+ ◆ Unused dependencies (3)
51
+ ● @types/jest
52
+ ● @types/node
53
+ ● ts-jest
54
+
55
+ → Run with --remove to clean up unused dependencies
56
+ ```
57
+
58
+ ### Remove unused dependencies
59
+
60
+ ```bash
61
+ pkg-clean --remove
62
+ ```
63
+
64
+ This will:
65
+ 1. Remove unused packages from your `package.json`
66
+ 2. Show you which packages were removed
67
+ 3. Suggest running `npm install`
68
+
69
+ ### Show verbose output
70
+
71
+ ```bash
72
+ pkg-clean --verbose
73
+ ```
74
+
75
+ Displays detailed information about all imports, dependencies, and analysis results.
76
+
77
+ ### Keep specific packages
78
+
79
+ ```bash
80
+ pkg-clean --keep lodash react
81
+ ```
82
+
83
+ Excludes specified packages from removal (even if unused).
84
+
85
+ ### Scan a different directory
86
+
87
+ ```bash
88
+ pkg-clean /path/to/project
89
+ ```
90
+
91
+ ### Combine options
92
+
93
+ ```bash
94
+ pkg-clean /path/to/project --remove --verbose --keep lodash
95
+ ```
96
+
97
+ ### Dry-run to preview changes
98
+
99
+ ```bash
100
+ pkg-clean --remove --verbose
101
+ ```
102
+
103
+ Output (without `--remove`):
104
+ ```
105
+ → Run with --remove to clean up unused dependencies
106
+ ```
107
+
108
+ This allows you to review what would be removed before applying changes.
109
+
110
+ ### Use with npm scripts
111
+
112
+ Add to your `package.json`:
113
+
114
+ ```json
115
+ {
116
+ "scripts": {
117
+ "clean:deps": "pkg-clean",
118
+ "clean:deps:remove": "pkg-clean --remove",
119
+ "clean:deps:check": "pkg-clean --verbose"
120
+ }
121
+ }
122
+ ```
123
+
124
+ Then run:
125
+ ```bash
126
+ npm run clean:deps:check # Preview unused dependencies
127
+ npm run clean:deps:remove # Remove unused dependencies
128
+ ```
129
+
130
+ ### CI/CD Integration
131
+
132
+ Check for unused dependencies in your CI pipeline:
133
+
134
+ ```bash
135
+ # Fail if unused dependencies are found
136
+ pkg-clean --verbose
137
+ ```
138
+
139
+ Exit code will be non-zero if unused dependencies are detected (useful for CI gates).
140
+
141
+ ## Configuration
142
+
143
+ Create a `pkg-clean.config.json` file in your project root to set default packages to keep:
144
+
145
+ ```json
146
+ {
147
+ "keep": ["lodash", "moment"]
148
+ }
149
+ ```
150
+
151
+ Example configurations:
152
+
153
+ **For a React project:**
154
+ ```json
155
+ {
156
+ "keep": ["react", "react-dom", "react-router-dom"]
157
+ }
158
+ ```
159
+
160
+ **For a monorepo:**
161
+ ```json
162
+ {
163
+ "keep": ["@company/shared-utils", "@company/types"]
164
+ }
165
+ ```
166
+
167
+ **For a library with peer dependencies:**
168
+ ```json
169
+ {
170
+ "keep": ["tslib", "@types/node"]
171
+ }
172
+ ```
173
+
174
+ ## How it works
175
+
176
+ 1. **Scans** all `.js`, `.ts`, `.jsx`, `.tsx` files in your project
177
+ 2. **Extracts** import/require statements using regex patterns
178
+ 3. **Compares** found imports against package.json entries
179
+ 4. **Reports** unused, duplicates, and deprecated packages
180
+ 5. **Removes** from package.json (if `--remove` flag is used)
181
+
182
+ ## What it detects
183
+
184
+ ### ✅ Detects
185
+
186
+ - `import` statements: `import foo from 'bar'`
187
+ - `require()` calls: `require('foo')`
188
+ - Scoped packages: `@babel/core`
189
+ - Nested imports: `lodash/map` → detects `lodash`
190
+ - Both `dependencies` and `devDependencies`
191
+ - Duplicate packages in both sections
192
+ - Deprecated packages from NPM registry
193
+
194
+ ### ⚠️ Limitations
195
+
196
+ - Doesn't analyze dynamic imports like `require(variable)`
197
+ - Doesn't check `.json`, `.html`, or config files
198
+ - Doesn't evaluate webpack/babel configs
199
+ - Doesn't track indirect dependencies
200
+
201
+ ## Development
202
+
203
+ ```bash
204
+ # Install dependencies
205
+ npm install
206
+
207
+ # Build TypeScript
208
+ npm run build
209
+
210
+ # Run tests
211
+ npm test
212
+
213
+ # Run tests in watch mode
214
+ npm test:watch
215
+
216
+ # Run locally
217
+ node dist/cli.js
218
+ ```
219
+
220
+ ## Contributing
221
+
222
+ Contributions are welcome! Please feel free to submit a Pull Request.
223
+
224
+ ## License
225
+
226
+ MIT © 2026
@@ -0,0 +1,24 @@
1
+ export interface PackageJson {
2
+ dependencies?: Record<string, string>;
3
+ devDependencies?: Record<string, string>;
4
+ [key: string]: unknown;
5
+ }
6
+ export interface AnalysisResult {
7
+ unused: string[];
8
+ duplicates: string[];
9
+ allDeps: string[];
10
+ }
11
+ /**
12
+ * Analyzes package.json dependencies against imported packages.
13
+ * Identifies unused packages and duplicates across dependency types.
14
+ */
15
+ export declare class Analyzer {
16
+ /**
17
+ * Analyzes a project's dependencies.
18
+ * @param projectRoot - The root directory containing package.json
19
+ * @param importedPackages - Set of package names found by Scanner
20
+ * @returns Analysis result containing unused packages, duplicates, and all dependencies
21
+ * @throws {Error} If package.json is not found
22
+ */
23
+ analyzeProject(projectRoot: string, importedPackages: Set<string>): AnalysisResult;
24
+ }
@@ -0,0 +1 @@
1
+ {"version":3,"file":"analyzer.d.ts","sourceRoot":"","sources":["../src/analyzer.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,WAAW;IAC1B,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACtC,eAAe,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACzC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB;AAED;;;GAGG;AACH,qBAAa,QAAQ;IACnB;;;;;;OAMG;IACH,cAAc,CACZ,WAAW,EAAE,MAAM,EACnB,gBAAgB,EAAE,GAAG,CAAC,MAAM,CAAC,GAC5B,cAAc;CA4BlB"}
@@ -0,0 +1,75 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.Analyzer = void 0;
37
+ const fs = __importStar(require("fs"));
38
+ const path = __importStar(require("path"));
39
+ /**
40
+ * Analyzes package.json dependencies against imported packages.
41
+ * Identifies unused packages and duplicates across dependency types.
42
+ */
43
+ class Analyzer {
44
+ /**
45
+ * Analyzes a project's dependencies.
46
+ * @param projectRoot - The root directory containing package.json
47
+ * @param importedPackages - Set of package names found by Scanner
48
+ * @returns Analysis result containing unused packages, duplicates, and all dependencies
49
+ * @throws {Error} If package.json is not found
50
+ */
51
+ analyzeProject(projectRoot, importedPackages) {
52
+ const pkgPath = path.join(projectRoot, "package.json");
53
+ if (!fs.existsSync(pkgPath)) {
54
+ throw new Error("package.json not found");
55
+ }
56
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
57
+ const deps = { ...pkg.dependencies, ...pkg.devDependencies };
58
+ const allDeps = Object.keys(deps);
59
+ const unused = allDeps.filter((dep) => !importedPackages.has(dep));
60
+ const duplicates = [];
61
+ if (pkg.dependencies && pkg.devDependencies) {
62
+ for (const dep of Object.keys(pkg.dependencies)) {
63
+ if (pkg.devDependencies[dep]) {
64
+ duplicates.push(dep);
65
+ }
66
+ }
67
+ }
68
+ return {
69
+ unused,
70
+ duplicates,
71
+ allDeps,
72
+ };
73
+ }
74
+ }
75
+ exports.Analyzer = Analyzer;
@@ -0,0 +1 @@
1
+ {"version":3,"file":"analyzer.js","sourceRoot":"","sources":["../src/analyzer.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,uCAAyB;AACzB,2CAA6B;AAc7B;;;GAGG;AACH,MAAa,QAAQ;IACnB;;;;;;OAMG;IACH,cAAc,CACZ,WAAmB,EACnB,gBAA6B;QAE7B,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,cAAc,CAAC,CAAC;QAEvD,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;YAC5B,MAAM,IAAI,KAAK,CAAC,wBAAwB,CAAC,CAAC;QAC5C,CAAC;QAED,MAAM,GAAG,GAAgB,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC;QACvE,MAAM,IAAI,GAAG,EAAE,GAAG,GAAG,CAAC,YAAY,EAAE,GAAG,GAAG,CAAC,eAAe,EAAE,CAAC;QAC7D,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAElC,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,gBAAgB,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC;QAEnE,MAAM,UAAU,GAAa,EAAE,CAAC;QAChC,IAAI,GAAG,CAAC,YAAY,IAAI,GAAG,CAAC,eAAe,EAAE,CAAC;YAC5C,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,YAAY,CAAC,EAAE,CAAC;gBAChD,IAAI,GAAG,CAAC,eAAe,CAAC,GAAG,CAAC,EAAE,CAAC;oBAC7B,UAAU,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;gBACvB,CAAC;YACH,CAAC;QACH,CAAC;QAED,OAAO;YACL,MAAM;YACN,UAAU;YACV,OAAO;SACR,CAAC;IACJ,CAAC;CACF;AAvCD,4BAuCC"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,89 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ const analyzer_1 = require("./analyzer");
37
+ const fs = __importStar(require("fs"));
38
+ const path = __importStar(require("path"));
39
+ describe("Analyzer", () => {
40
+ const analyzer = new analyzer_1.Analyzer();
41
+ const testDir = path.join(__dirname, "fixtures-analyzer");
42
+ beforeAll(() => {
43
+ if (!fs.existsSync(testDir)) {
44
+ fs.mkdirSync(testDir, { recursive: true });
45
+ }
46
+ });
47
+ afterAll(() => {
48
+ if (fs.existsSync(testDir)) {
49
+ fs.rmSync(testDir, { recursive: true });
50
+ }
51
+ });
52
+ it("should detect unused dependencies", () => {
53
+ const pkg = {
54
+ dependencies: {
55
+ lodash: "^4.17.0",
56
+ react: "^18.0.0",
57
+ },
58
+ devDependencies: {
59
+ jest: "^29.0.0",
60
+ },
61
+ };
62
+ fs.writeFileSync(path.join(testDir, "package.json"), JSON.stringify(pkg));
63
+ const imported = new Set(["react"]);
64
+ const result = analyzer.analyzeProject(testDir, imported);
65
+ expect(result.unused).toContain("lodash");
66
+ expect(result.unused).toContain("jest");
67
+ expect(result.unused).not.toContain("react");
68
+ });
69
+ it("should detect duplicate dependencies", () => {
70
+ const pkg = {
71
+ dependencies: {
72
+ typescript: "^5.0.0",
73
+ },
74
+ devDependencies: {
75
+ typescript: "^5.0.0",
76
+ jest: "^29.0.0",
77
+ },
78
+ };
79
+ fs.writeFileSync(path.join(testDir, "package.json"), JSON.stringify(pkg));
80
+ const imported = new Set(["typescript", "jest"]);
81
+ const result = analyzer.analyzeProject(testDir, imported);
82
+ expect(result.duplicates).toContain("typescript");
83
+ });
84
+ it("should throw error when package.json not found", () => {
85
+ const nonExistentDir = path.join(testDir, "nonexistent");
86
+ const imported = new Set();
87
+ expect(() => analyzer.analyzeProject(nonExistentDir, imported)).toThrow();
88
+ });
89
+ });
package/dist/cli.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":""}
package/dist/cli.js ADDED
@@ -0,0 +1,27 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ const commander_1 = require("commander");
5
+ const index_1 = require("./index");
6
+ commander_1.program
7
+ .name("pkg-clean")
8
+ .description("Find and remove unused dependencies")
9
+ .version("1.0.0");
10
+ commander_1.program
11
+ .arguments("[path]")
12
+ .option("--remove", "Remove unused dependencies from package.json")
13
+ .option("--verbose", "Show verbose output")
14
+ .option("--keep <packages...>", "Packages to keep (space-separated)")
15
+ .action(async (dirPath, options) => {
16
+ try {
17
+ const projectRoot = dirPath || process.cwd();
18
+ const cleaner = new index_1.Cleaner();
19
+ await cleaner.clean(projectRoot, options);
20
+ }
21
+ catch (error) {
22
+ const message = error instanceof Error ? error.message : String(error);
23
+ console.error(`\x1b[31m✗\x1b[0m ${message}`);
24
+ process.exit(1);
25
+ }
26
+ });
27
+ commander_1.program.parse();
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";;;AAEA,yCAAoC;AACpC,mCAAkC;AAElC,mBAAO;KACJ,IAAI,CAAC,WAAW,CAAC;KACjB,WAAW,CAAC,qCAAqC,CAAC;KAClD,OAAO,CAAC,OAAO,CAAC,CAAC;AAEpB,mBAAO;KACJ,SAAS,CAAC,QAAQ,CAAC;KACnB,MAAM,CAAC,UAAU,EAAE,8CAA8C,CAAC;KAClE,MAAM,CAAC,WAAW,EAAE,qBAAqB,CAAC;KAC1C,MAAM,CAAC,sBAAsB,EAAE,oCAAoC,CAAC;KACpE,MAAM,CAAC,KAAK,EAAE,OAA2B,EAAE,OAAO,EAAE,EAAE;IACrD,IAAI,CAAC;QACH,MAAM,WAAW,GAAG,OAAO,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC;QAC7C,MAAM,OAAO,GAAG,IAAI,eAAO,EAAE,CAAC;QAC9B,MAAM,OAAO,CAAC,KAAK,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC;IAC5C,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QACvE,OAAO,CAAC,KAAK,CAAC,YAAY,OAAO,EAAE,CAAC,CAAC;QACrC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;AACH,CAAC,CAAC,CAAC;AAEL,mBAAO,CAAC,KAAK,EAAE,CAAC"}
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Configuration options for pkg-clean
3
+ */
4
+ export interface PkgCleanConfig {
5
+ /** Directories to ignore during scanning */
6
+ ignore?: string[];
7
+ /** Packages to always keep, even if unused */
8
+ keep?: string[];
9
+ /** Check for deprecated packages */
10
+ checkDeprecation?: boolean;
11
+ /** Enable verbose output */
12
+ verbose?: boolean;
13
+ }
14
+ /**
15
+ * Loads and manages pkg-clean configuration from pkg-clean.config.json
16
+ */
17
+ export declare class ConfigLoader {
18
+ private static readonly CONFIG_FILE;
19
+ /**
20
+ * Loads configuration from pkg-clean.config.json if it exists
21
+ * @param projectRoot - The project root directory
22
+ * @returns The loaded configuration or empty object if file doesn't exist
23
+ */
24
+ static load(projectRoot: string): PkgCleanConfig;
25
+ /**
26
+ * Creates a default configuration file
27
+ * @param projectRoot - The project root directory
28
+ * @returns The created configuration
29
+ */
30
+ static createDefault(projectRoot: string): PkgCleanConfig;
31
+ /**
32
+ * Merges user options with loaded configuration
33
+ * CLI options take precedence over config file
34
+ * @param configFile - Configuration from file
35
+ * @param cliOptions - Command-line options
36
+ * @returns Merged configuration
37
+ */
38
+ static merge(configFile: PkgCleanConfig, cliOptions: Partial<PkgCleanConfig>): PkgCleanConfig;
39
+ }
40
+ //# sourceMappingURL=configLoader.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"configLoader.d.ts","sourceRoot":"","sources":["../src/configLoader.ts"],"names":[],"mappings":"AAGA;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,4CAA4C;IAC5C,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,8CAA8C;IAC9C,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,oCAAoC;IACpC,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,4BAA4B;IAC5B,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED;;GAEG;AACH,qBAAa,YAAY;IACvB,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,WAAW,CAA2B;IAE9D;;;;OAIG;IACH,MAAM,CAAC,IAAI,CAAC,WAAW,EAAE,MAAM,GAAG,cAAc;IAiBhD;;;;OAIG;IACH,MAAM,CAAC,aAAa,CAAC,WAAW,EAAE,MAAM,GAAG,cAAc;IAczD;;;;;;OAMG;IACH,MAAM,CAAC,KAAK,CACV,UAAU,EAAE,cAAc,EAC1B,UAAU,EAAE,OAAO,CAAC,cAAc,CAAC,GAClC,cAAc;CASlB"}
@@ -0,0 +1,97 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.ConfigLoader = void 0;
37
+ const fs = __importStar(require("fs"));
38
+ const path = __importStar(require("path"));
39
+ /**
40
+ * Loads and manages pkg-clean configuration from pkg-clean.config.json
41
+ */
42
+ class ConfigLoader {
43
+ /**
44
+ * Loads configuration from pkg-clean.config.json if it exists
45
+ * @param projectRoot - The project root directory
46
+ * @returns The loaded configuration or empty object if file doesn't exist
47
+ */
48
+ static load(projectRoot) {
49
+ const configPath = path.join(projectRoot, this.CONFIG_FILE);
50
+ if (!fs.existsSync(configPath)) {
51
+ return {};
52
+ }
53
+ try {
54
+ const content = fs.readFileSync(configPath, "utf-8");
55
+ const config = JSON.parse(content);
56
+ return config;
57
+ }
58
+ catch (error) {
59
+ const message = error instanceof Error ? error.message : String(error);
60
+ throw new Error(`Failed to parse ${this.CONFIG_FILE}: ${message}`);
61
+ }
62
+ }
63
+ /**
64
+ * Creates a default configuration file
65
+ * @param projectRoot - The project root directory
66
+ * @returns The created configuration
67
+ */
68
+ static createDefault(projectRoot) {
69
+ const configPath = path.join(projectRoot, this.CONFIG_FILE);
70
+ const defaultConfig = {
71
+ ignore: ["node_modules", "dist", "build"],
72
+ keep: [],
73
+ checkDeprecation: true,
74
+ verbose: false,
75
+ };
76
+ fs.writeFileSync(configPath, JSON.stringify(defaultConfig, null, 2) + "\n");
77
+ return defaultConfig;
78
+ }
79
+ /**
80
+ * Merges user options with loaded configuration
81
+ * CLI options take precedence over config file
82
+ * @param configFile - Configuration from file
83
+ * @param cliOptions - Command-line options
84
+ * @returns Merged configuration
85
+ */
86
+ static merge(configFile, cliOptions) {
87
+ return {
88
+ ignore: cliOptions.ignore ?? configFile.ignore,
89
+ keep: cliOptions.keep ?? configFile.keep,
90
+ checkDeprecation: cliOptions.checkDeprecation ?? configFile.checkDeprecation ?? true,
91
+ verbose: cliOptions.verbose ?? configFile.verbose ?? false,
92
+ };
93
+ }
94
+ }
95
+ exports.ConfigLoader = ConfigLoader;
96
+ ConfigLoader.CONFIG_FILE = "pkg-clean.config.json";
97
+ //# sourceMappingURL=configLoader.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"configLoader.js","sourceRoot":"","sources":["../src/configLoader.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,uCAAyB;AACzB,2CAA6B;AAgB7B;;GAEG;AACH,MAAa,YAAY;IAGvB;;;;OAIG;IACH,MAAM,CAAC,IAAI,CAAC,WAAmB;QAC7B,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,IAAI,CAAC,WAAW,CAAC,CAAC;QAE5D,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;YAC/B,OAAO,EAAE,CAAC;QACZ,CAAC;QAED,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,EAAE,CAAC,YAAY,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;YACrD,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;YACnC,OAAO,MAAwB,CAAC;QAClC,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YACvE,MAAM,IAAI,KAAK,CAAC,mBAAmB,IAAI,CAAC,WAAW,KAAK,OAAO,EAAE,CAAC,CAAC;QACrE,CAAC;IACH,CAAC;IAED;;;;OAIG;IACH,MAAM,CAAC,aAAa,CAAC,WAAmB;QACtC,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,IAAI,CAAC,WAAW,CAAC,CAAC;QAE5D,MAAM,aAAa,GAAmB;YACpC,MAAM,EAAE,CAAC,cAAc,EAAE,MAAM,EAAE,OAAO,CAAC;YACzC,IAAI,EAAE,EAAE;YACR,gBAAgB,EAAE,IAAI;YACtB,OAAO,EAAE,KAAK;SACf,CAAC;QAEF,EAAE,CAAC,aAAa,CAAC,UAAU,EAAE,IAAI,CAAC,SAAS,CAAC,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;QAC5E,OAAO,aAAa,CAAC;IACvB,CAAC;IAED;;;;;;OAMG;IACH,MAAM,CAAC,KAAK,CACV,UAA0B,EAC1B,UAAmC;QAEnC,OAAO;YACL,MAAM,EAAE,UAAU,CAAC,MAAM,IAAI,UAAU,CAAC,MAAM;YAC9C,IAAI,EAAE,UAAU,CAAC,IAAI,IAAI,UAAU,CAAC,IAAI;YACxC,gBAAgB,EACd,UAAU,CAAC,gBAAgB,IAAI,UAAU,CAAC,gBAAgB,IAAI,IAAI;YACpE,OAAO,EAAE,UAAU,CAAC,OAAO,IAAI,UAAU,CAAC,OAAO,IAAI,KAAK;SAC3D,CAAC;IACJ,CAAC;;AA9DH,oCA+DC;AA9DyB,wBAAW,GAAG,uBAAuB,CAAC"}
@@ -0,0 +1,24 @@
1
+ export interface CleanerOptions {
2
+ remove?: boolean;
3
+ verbose?: boolean;
4
+ keep?: string[];
5
+ }
6
+ /**
7
+ * Main cleaner class that orchestrates the dependency analysis workflow.
8
+ * Scans project files, analyzes dependencies, checks for deprecation, and optionally removes unused packages.
9
+ */
10
+ export declare class Cleaner {
11
+ private scanner;
12
+ private analyzer;
13
+ private registry;
14
+ constructor();
15
+ /**
16
+ * Scans a project for unused dependencies and optionally removes them.
17
+ * @param projectRoot - The root directory of the project to scan
18
+ * @param options - Configuration options for the clean operation
19
+ * @param options.remove - If true, removes unused dependencies from package.json
20
+ * @param options.verbose - If true, outputs detailed diagnostic information
21
+ * @throws {Error} If package.json is not found in the project root
22
+ */
23
+ clean(projectRoot: string, options?: CleanerOptions): Promise<void>;
24
+ }
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAMA,MAAM,WAAW,cAAc;IAC7B,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;CACjB;AA6ED;;;GAGG;AACH,qBAAa,OAAO;IAClB,OAAO,CAAC,OAAO,CAAU;IACzB,OAAO,CAAC,QAAQ,CAAW;IAC3B,OAAO,CAAC,QAAQ,CAAW;;IAQ3B;;;;;;;OAOG;IACG,KAAK,CAAC,WAAW,EAAE,MAAM,EAAE,OAAO,GAAE,cAAmB;CA8H9D"}