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.
- package/LICENSE +21 -0
- package/README.md +226 -0
- package/dist/analyzer.d.ts +24 -0
- package/dist/analyzer.d.ts.map +1 -0
- package/dist/analyzer.js +75 -0
- package/dist/analyzer.js.map +1 -0
- package/dist/analyzer.test.d.ts +1 -0
- package/dist/analyzer.test.js +89 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +27 -0
- package/dist/cli.js.map +1 -0
- package/dist/configLoader.d.ts +40 -0
- package/dist/configLoader.d.ts.map +1 -0
- package/dist/configLoader.js +97 -0
- package/dist/configLoader.js.map +1 -0
- package/dist/index.d.ts +24 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +179 -0
- package/dist/index.js.map +1 -0
- package/dist/index.test.d.ts +1 -0
- package/dist/index.test.js +316 -0
- package/dist/packageManager.d.ts +14 -0
- package/dist/packageManager.d.ts.map +1 -0
- package/dist/packageManager.js +67 -0
- package/dist/packageManager.js.map +1 -0
- package/dist/packageManagerFactory.d.ts +37 -0
- package/dist/packageManagerFactory.d.ts.map +1 -0
- package/dist/packageManagerFactory.js +158 -0
- package/dist/packageManagerFactory.js.map +1 -0
- package/dist/registry.d.ts +25 -0
- package/dist/registry.d.ts.map +1 -0
- package/dist/registry.js +61 -0
- package/dist/registry.js.map +1 -0
- package/dist/registry.test.d.ts +1 -0
- package/dist/registry.test.js +72 -0
- package/dist/scanner.d.ts +20 -0
- package/dist/scanner.d.ts.map +1 -0
- package/dist/scanner.js +89 -0
- package/dist/scanner.js.map +1 -0
- package/dist/scanner.test.d.ts +1 -0
- package/dist/scanner.test.js +79 -0
- package/package.json +53 -0
|
@@ -0,0 +1,158 @@
|
|
|
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.PackageManagerFactory = void 0;
|
|
37
|
+
const fs = __importStar(require("fs"));
|
|
38
|
+
const path = __importStar(require("path"));
|
|
39
|
+
/**
|
|
40
|
+
* NPM package manager implementation
|
|
41
|
+
*/
|
|
42
|
+
class NpmPackageManager {
|
|
43
|
+
getType() {
|
|
44
|
+
return "npm";
|
|
45
|
+
}
|
|
46
|
+
removeDependencies(projectRoot, packages) {
|
|
47
|
+
const pkgPath = path.join(projectRoot, "package.json");
|
|
48
|
+
if (!fs.existsSync(pkgPath)) {
|
|
49
|
+
throw new Error("package.json not found");
|
|
50
|
+
}
|
|
51
|
+
const content = fs.readFileSync(pkgPath, "utf-8");
|
|
52
|
+
const pkg = JSON.parse(content);
|
|
53
|
+
for (const packageName of packages) {
|
|
54
|
+
delete pkg.dependencies?.[packageName];
|
|
55
|
+
delete pkg.devDependencies?.[packageName];
|
|
56
|
+
}
|
|
57
|
+
fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + "\n");
|
|
58
|
+
return true;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Yarn package manager implementation
|
|
63
|
+
*/
|
|
64
|
+
class YarnPackageManager {
|
|
65
|
+
getType() {
|
|
66
|
+
return "yarn";
|
|
67
|
+
}
|
|
68
|
+
removeDependencies(projectRoot, packages) {
|
|
69
|
+
const pkgPath = path.join(projectRoot, "package.json");
|
|
70
|
+
if (!fs.existsSync(pkgPath)) {
|
|
71
|
+
throw new Error("package.json not found");
|
|
72
|
+
}
|
|
73
|
+
const content = fs.readFileSync(pkgPath, "utf-8");
|
|
74
|
+
const pkg = JSON.parse(content);
|
|
75
|
+
for (const packageName of packages) {
|
|
76
|
+
delete pkg.dependencies?.[packageName];
|
|
77
|
+
delete pkg.devDependencies?.[packageName];
|
|
78
|
+
}
|
|
79
|
+
fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + "\n");
|
|
80
|
+
return true;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* pnpm package manager implementation
|
|
85
|
+
*/
|
|
86
|
+
class PnpmPackageManager {
|
|
87
|
+
getType() {
|
|
88
|
+
return "pnpm";
|
|
89
|
+
}
|
|
90
|
+
removeDependencies(projectRoot, packages) {
|
|
91
|
+
const pkgPath = path.join(projectRoot, "package.json");
|
|
92
|
+
if (!fs.existsSync(pkgPath)) {
|
|
93
|
+
throw new Error("package.json not found");
|
|
94
|
+
}
|
|
95
|
+
const content = fs.readFileSync(pkgPath, "utf-8");
|
|
96
|
+
const pkg = JSON.parse(content);
|
|
97
|
+
for (const packageName of packages) {
|
|
98
|
+
delete pkg.dependencies?.[packageName];
|
|
99
|
+
delete pkg.devDependencies?.[packageName];
|
|
100
|
+
}
|
|
101
|
+
fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + "\n");
|
|
102
|
+
return true;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Factory for creating package manager instances.
|
|
107
|
+
* Detects the package manager type based on lock files in the project.
|
|
108
|
+
*/
|
|
109
|
+
class PackageManagerFactory {
|
|
110
|
+
/**
|
|
111
|
+
* Detects the package manager type by looking for lock files.
|
|
112
|
+
* @param projectRoot - The project root directory
|
|
113
|
+
* @returns The detected package manager type, defaults to 'npm'
|
|
114
|
+
*/
|
|
115
|
+
static detect(projectRoot) {
|
|
116
|
+
// Check for yarn.lock
|
|
117
|
+
if (fs.existsSync(path.join(projectRoot, "yarn.lock"))) {
|
|
118
|
+
return "yarn";
|
|
119
|
+
}
|
|
120
|
+
// Check for pnpm-lock.yaml
|
|
121
|
+
if (fs.existsSync(path.join(projectRoot, "pnpm-lock.yaml"))) {
|
|
122
|
+
return "pnpm";
|
|
123
|
+
}
|
|
124
|
+
// Check for package-lock.json
|
|
125
|
+
if (fs.existsSync(path.join(projectRoot, "package-lock.json"))) {
|
|
126
|
+
return "npm";
|
|
127
|
+
}
|
|
128
|
+
// Default to npm
|
|
129
|
+
return "npm";
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Creates a package manager instance.
|
|
133
|
+
* @param type - The type of package manager to create
|
|
134
|
+
* @returns An instance of the specified package manager
|
|
135
|
+
*/
|
|
136
|
+
static create(type = "npm") {
|
|
137
|
+
switch (type) {
|
|
138
|
+
case "yarn":
|
|
139
|
+
return new YarnPackageManager();
|
|
140
|
+
case "pnpm":
|
|
141
|
+
return new PnpmPackageManager();
|
|
142
|
+
case "npm":
|
|
143
|
+
default:
|
|
144
|
+
return new NpmPackageManager();
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Creates and detects the appropriate package manager for a project.
|
|
149
|
+
* @param projectRoot - The project root directory
|
|
150
|
+
* @returns An instance of the detected package manager
|
|
151
|
+
*/
|
|
152
|
+
static createForProject(projectRoot) {
|
|
153
|
+
const type = this.detect(projectRoot);
|
|
154
|
+
return this.create(type);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
exports.PackageManagerFactory = PackageManagerFactory;
|
|
158
|
+
//# sourceMappingURL=packageManagerFactory.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"packageManagerFactory.js","sourceRoot":"","sources":["../src/packageManagerFactory.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export interface DeprecationInfo {
|
|
2
|
+
name: string;
|
|
3
|
+
deprecated: boolean;
|
|
4
|
+
replacement?: string;
|
|
5
|
+
reason?: string;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Checks npm registry for package deprecation status.
|
|
9
|
+
* Results are cached to minimize network requests.
|
|
10
|
+
*/
|
|
11
|
+
export declare class Registry {
|
|
12
|
+
private cache;
|
|
13
|
+
/**
|
|
14
|
+
* Checks if a package is deprecated on npm.
|
|
15
|
+
* @param packageName - The name of the package to check
|
|
16
|
+
* @returns Deprecation information for the package
|
|
17
|
+
*/
|
|
18
|
+
checkDeprecation(packageName: string): Promise<DeprecationInfo>;
|
|
19
|
+
/**
|
|
20
|
+
* Checks deprecation status for multiple packages in parallel.
|
|
21
|
+
* @param packages - Array of package names to check
|
|
22
|
+
* @returns Array of deprecation info for each package
|
|
23
|
+
*/
|
|
24
|
+
checkMultiple(packages: string[]): Promise<DeprecationInfo[]>;
|
|
25
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../src/registry.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,OAAO,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;GAGG;AACH,qBAAa,QAAQ;IACnB,OAAO,CAAC,KAAK,CAAsC;IAEnD;;;;OAIG;IACG,gBAAgB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC;IA2CrE;;;;OAIG;IACG,aAAa,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,eAAe,EAAE,CAAC;CAGpE"}
|
package/dist/registry.js
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Registry = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Checks npm registry for package deprecation status.
|
|
6
|
+
* Results are cached to minimize network requests.
|
|
7
|
+
*/
|
|
8
|
+
class Registry {
|
|
9
|
+
constructor() {
|
|
10
|
+
this.cache = new Map();
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Checks if a package is deprecated on npm.
|
|
14
|
+
* @param packageName - The name of the package to check
|
|
15
|
+
* @returns Deprecation information for the package
|
|
16
|
+
*/
|
|
17
|
+
async checkDeprecation(packageName) {
|
|
18
|
+
if (this.cache.has(packageName)) {
|
|
19
|
+
return this.cache.get(packageName);
|
|
20
|
+
}
|
|
21
|
+
try {
|
|
22
|
+
const response = await fetch(`https://registry.npmjs.org/${packageName}`);
|
|
23
|
+
if (!response.ok) {
|
|
24
|
+
const result = {
|
|
25
|
+
name: packageName,
|
|
26
|
+
deprecated: false,
|
|
27
|
+
};
|
|
28
|
+
this.cache.set(packageName, result);
|
|
29
|
+
return result;
|
|
30
|
+
}
|
|
31
|
+
const data = (await response.json());
|
|
32
|
+
const deprecated = !!data.deprecated;
|
|
33
|
+
const reason = typeof data.deprecated === "string" ? data.deprecated : undefined;
|
|
34
|
+
const result = {
|
|
35
|
+
name: packageName,
|
|
36
|
+
deprecated,
|
|
37
|
+
reason,
|
|
38
|
+
};
|
|
39
|
+
this.cache.set(packageName, result);
|
|
40
|
+
return result;
|
|
41
|
+
}
|
|
42
|
+
catch (_a) {
|
|
43
|
+
// Network error or invalid package
|
|
44
|
+
const result = {
|
|
45
|
+
name: packageName,
|
|
46
|
+
deprecated: false,
|
|
47
|
+
};
|
|
48
|
+
this.cache.set(packageName, result);
|
|
49
|
+
return result;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Checks deprecation status for multiple packages in parallel.
|
|
54
|
+
* @param packages - Array of package names to check
|
|
55
|
+
* @returns Array of deprecation info for each package
|
|
56
|
+
*/
|
|
57
|
+
async checkMultiple(packages) {
|
|
58
|
+
return Promise.all(packages.map((pkg) => this.checkDeprecation(pkg)));
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
exports.Registry = Registry;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.js","sourceRoot":"","sources":["../src/registry.ts"],"names":[],"mappings":";;;AAOA;;;GAGG;AACH,MAAa,QAAQ;IAArB;QACU,UAAK,GAAG,IAAI,GAAG,EAA2B,CAAC;IA0DrD,CAAC;IAxDC;;;;OAIG;IACH,KAAK,CAAC,gBAAgB,CAAC,WAAmB;QACxC,IAAI,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,WAAW,CAAC,EAAE,CAAC;YAChC,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,WAAW,CAAE,CAAC;QACtC,CAAC;QAED,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,8BAA8B,WAAW,EAAE,CAAC,CAAC;YAE1E,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;gBACjB,MAAM,MAAM,GAAoB;oBAC9B,IAAI,EAAE,WAAW;oBACjB,UAAU,EAAE,KAAK;iBAClB,CAAC;gBACF,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC;gBACpC,OAAO,MAAM,CAAC;YAChB,CAAC;YAED,MAAM,IAAI,GAAG,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAElC,CAAC;YACF,MAAM,UAAU,GAAG,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC;YACrC,MAAM,MAAM,GACV,OAAO,IAAI,CAAC,UAAU,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,SAAS,CAAC;YAEpE,MAAM,MAAM,GAAoB;gBAC9B,IAAI,EAAE,WAAW;gBACjB,UAAU;gBACV,MAAM;aACP,CAAC;YAEF,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC;YACpC,OAAO,MAAM,CAAC;QAChB,CAAC;QAAC,MAAM,CAAC;YACP,mCAAmC;YACnC,MAAM,MAAM,GAAoB;gBAC9B,IAAI,EAAE,WAAW;gBACjB,UAAU,EAAE,KAAK;aAClB,CAAC;YACF,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC;YACpC,OAAO,MAAM,CAAC;QAChB,CAAC;IACH,CAAC;IAED;;;;OAIG;IACH,KAAK,CAAC,aAAa,CAAC,QAAkB;QACpC,OAAO,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;IACxE,CAAC;CACF;AA3DD,4BA2DC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const registry_1 = require("./registry");
|
|
4
|
+
describe("Registry", () => {
|
|
5
|
+
let registry;
|
|
6
|
+
beforeEach(() => {
|
|
7
|
+
registry = new registry_1.Registry();
|
|
8
|
+
// Mock fetch globally
|
|
9
|
+
global.fetch = jest.fn();
|
|
10
|
+
});
|
|
11
|
+
afterEach(() => {
|
|
12
|
+
jest.restoreAllMocks();
|
|
13
|
+
});
|
|
14
|
+
it("should cache deprecation checks", async () => {
|
|
15
|
+
global.fetch.mockResolvedValueOnce({
|
|
16
|
+
ok: true,
|
|
17
|
+
json: async () => ({ deprecated: false }),
|
|
18
|
+
});
|
|
19
|
+
// First call
|
|
20
|
+
const result1 = await registry.checkDeprecation("commander");
|
|
21
|
+
// Second call should use cache (fetch not called again)
|
|
22
|
+
const result2 = await registry.checkDeprecation("commander");
|
|
23
|
+
expect(result1.name).toBe("commander");
|
|
24
|
+
expect(result1.deprecated).toBe(false);
|
|
25
|
+
expect(result2).toEqual(result1);
|
|
26
|
+
expect(global.fetch).toHaveBeenCalledTimes(1);
|
|
27
|
+
});
|
|
28
|
+
it("should handle network errors gracefully", async () => {
|
|
29
|
+
global.fetch.mockRejectedValueOnce(new Error("Network error"));
|
|
30
|
+
const result = await registry.checkDeprecation("invalid-pkg-name-xyz123");
|
|
31
|
+
expect(result.name).toBe("invalid-pkg-name-xyz123");
|
|
32
|
+
expect(result.deprecated).toBe(false);
|
|
33
|
+
});
|
|
34
|
+
it("should parse deprecated string reason", async () => {
|
|
35
|
+
global.fetch.mockResolvedValueOnce({
|
|
36
|
+
ok: true,
|
|
37
|
+
json: async () => ({
|
|
38
|
+
deprecated: "Use package-x instead",
|
|
39
|
+
}),
|
|
40
|
+
});
|
|
41
|
+
const result = await registry.checkDeprecation("old-package");
|
|
42
|
+
expect(result.name).toBe("old-package");
|
|
43
|
+
expect(result.deprecated).toBe(true);
|
|
44
|
+
expect(result.reason).toBe("Use package-x instead");
|
|
45
|
+
});
|
|
46
|
+
it("should handle 404 responses gracefully", async () => {
|
|
47
|
+
global.fetch.mockResolvedValueOnce({
|
|
48
|
+
ok: false,
|
|
49
|
+
status: 404,
|
|
50
|
+
});
|
|
51
|
+
const result = await registry.checkDeprecation("nonexistent-package");
|
|
52
|
+
expect(result.name).toBe("nonexistent-package");
|
|
53
|
+
expect(result.deprecated).toBe(false);
|
|
54
|
+
});
|
|
55
|
+
it("should check multiple packages in parallel", async () => {
|
|
56
|
+
global.fetch
|
|
57
|
+
.mockResolvedValueOnce({
|
|
58
|
+
ok: true,
|
|
59
|
+
json: async () => ({ deprecated: false }),
|
|
60
|
+
})
|
|
61
|
+
.mockResolvedValueOnce({
|
|
62
|
+
ok: true,
|
|
63
|
+
json: async () => ({ deprecated: true, reason: "Deprecated" }),
|
|
64
|
+
});
|
|
65
|
+
const results = await registry.checkMultiple(["commander", "old-pkg"]);
|
|
66
|
+
expect(results).toHaveLength(2);
|
|
67
|
+
expect(results[0].name).toBe("commander");
|
|
68
|
+
expect(results[0].deprecated).toBe(false);
|
|
69
|
+
expect(results[1].name).toBe("old-pkg");
|
|
70
|
+
expect(results[1].deprecated).toBe(true);
|
|
71
|
+
});
|
|
72
|
+
});
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
interface ScannerOptions {
|
|
2
|
+
ignore?: string[];
|
|
3
|
+
}
|
|
4
|
+
/**
|
|
5
|
+
* Scans project source files to extract imported package names.
|
|
6
|
+
* Supports ES6 imports and CommonJS require() statements.
|
|
7
|
+
*/
|
|
8
|
+
export declare class Scanner {
|
|
9
|
+
private importRegex;
|
|
10
|
+
/**
|
|
11
|
+
* Recursively scans a directory for imported packages.
|
|
12
|
+
* @param projectRoot - The root directory to scan
|
|
13
|
+
* @param options - Scanner configuration options
|
|
14
|
+
* @param options.ignore - Array of directory names to exclude (default: node_modules, dist, build)
|
|
15
|
+
* @returns A set of unique package names found in imports
|
|
16
|
+
*/
|
|
17
|
+
scan(projectRoot: string, options?: ScannerOptions): Promise<Set<string>>;
|
|
18
|
+
private isCodeFile;
|
|
19
|
+
}
|
|
20
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scanner.d.ts","sourceRoot":"","sources":["../src/scanner.ts"],"names":[],"mappings":"AAGA,UAAU,cAAc;IACtB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;CACnB;AAED;;;GAGG;AACH,qBAAa,OAAO;IAElB,OAAO,CAAC,WAAW,CAC8D;IAEjF;;;;;;OAMG;IACG,IAAI,CACR,WAAW,EAAE,MAAM,EACnB,OAAO,GAAE,cAAmB,GAC3B,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IAmCvB,OAAO,CAAC,UAAU;CAInB"}
|
package/dist/scanner.js
ADDED
|
@@ -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
|
+
exports.Scanner = void 0;
|
|
37
|
+
const fs = __importStar(require("fs"));
|
|
38
|
+
const path = __importStar(require("path"));
|
|
39
|
+
/**
|
|
40
|
+
* Scans project source files to extract imported package names.
|
|
41
|
+
* Supports ES6 imports and CommonJS require() statements.
|
|
42
|
+
*/
|
|
43
|
+
class Scanner {
|
|
44
|
+
constructor() {
|
|
45
|
+
// Matches: import 'x', import from 'x', require('x'), etc.
|
|
46
|
+
this.importRegex = /(?:import\s+(?:.*\s+)?from\s+|import\s+|require\s*\()\s*['"`]([^'"`]+)['"`]/g;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Recursively scans a directory for imported packages.
|
|
50
|
+
* @param projectRoot - The root directory to scan
|
|
51
|
+
* @param options - Scanner configuration options
|
|
52
|
+
* @param options.ignore - Array of directory names to exclude (default: node_modules, dist, build)
|
|
53
|
+
* @returns A set of unique package names found in imports
|
|
54
|
+
*/
|
|
55
|
+
async scan(projectRoot, options = {}) {
|
|
56
|
+
const imports = new Set();
|
|
57
|
+
const ignore = new Set(options.ignore || ["node_modules", "dist", "build"]);
|
|
58
|
+
const scanDir = async (dir) => {
|
|
59
|
+
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
60
|
+
for (const entry of entries) {
|
|
61
|
+
if (ignore.has(entry.name))
|
|
62
|
+
continue;
|
|
63
|
+
const fullPath = path.join(dir, entry.name);
|
|
64
|
+
if (entry.isDirectory()) {
|
|
65
|
+
await scanDir(fullPath);
|
|
66
|
+
}
|
|
67
|
+
else if (this.isCodeFile(fullPath)) {
|
|
68
|
+
const content = fs.readFileSync(fullPath, "utf-8");
|
|
69
|
+
const matches = content.matchAll(this.importRegex);
|
|
70
|
+
for (const match of matches) {
|
|
71
|
+
const imported = match[1];
|
|
72
|
+
// Extract package name from path (handle scoped packages)
|
|
73
|
+
const packageName = imported.startsWith("@")
|
|
74
|
+
? imported.split("/").slice(0, 2).join("/")
|
|
75
|
+
: imported.split("/")[0];
|
|
76
|
+
imports.add(packageName);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
await scanDir(projectRoot);
|
|
82
|
+
return imports;
|
|
83
|
+
}
|
|
84
|
+
isCodeFile(filePath) {
|
|
85
|
+
const ext = path.extname(filePath);
|
|
86
|
+
return [".js", ".ts", ".jsx", ".tsx", ".mjs", ".cjs"].includes(ext);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
exports.Scanner = Scanner;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scanner.js","sourceRoot":"","sources":["../src/scanner.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,uCAAyB;AACzB,2CAA6B;AAM7B;;;GAGG;AACH,MAAa,OAAO;IAApB;QACE,2DAA2D;QACnD,gBAAW,GACjB,8EAA8E,CAAC;IAmDnF,CAAC;IAjDC;;;;;;OAMG;IACH,KAAK,CAAC,IAAI,CACR,WAAmB,EACnB,UAA0B,EAAE;QAE5B,MAAM,OAAO,GAAG,IAAI,GAAG,EAAU,CAAC;QAClC,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,MAAM,IAAI,CAAC,cAAc,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;QAE5E,MAAM,OAAO,GAAG,KAAK,EAAE,GAAW,EAAE,EAAE;YACpC,MAAM,OAAO,GAAG,EAAE,CAAC,WAAW,CAAC,GAAG,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;YAE7D,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;gBAC5B,IAAI,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC;oBAAE,SAAS;gBAErC,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;gBAE5C,IAAI,KAAK,CAAC,WAAW,EAAE,EAAE,CAAC;oBACxB,MAAM,OAAO,CAAC,QAAQ,CAAC,CAAC;gBAC1B,CAAC;qBAAM,IAAI,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;oBACrC,MAAM,OAAO,GAAG,EAAE,CAAC,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;oBACnD,MAAM,OAAO,GAAG,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;oBAEnD,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;wBAC5B,MAAM,QAAQ,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;wBAC1B,0DAA0D;wBAC1D,MAAM,WAAW,GAAG,QAAQ,CAAC,UAAU,CAAC,GAAG,CAAC;4BAC1C,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC;4BAC3C,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;wBAE3B,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;oBAC3B,CAAC;gBACH,CAAC;YACH,CAAC;QACH,CAAC,CAAC;QAEF,MAAM,OAAO,CAAC,WAAW,CAAC,CAAC;QAC3B,OAAO,OAAO,CAAC;IACjB,CAAC;IAEO,UAAU,CAAC,QAAgB;QACjC,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;QACnC,OAAO,CAAC,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;IACtE,CAAC;CACF;AAtDD,0BAsDC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,79 @@
|
|
|
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 scanner_1 = require("./scanner");
|
|
37
|
+
const fs = __importStar(require("fs"));
|
|
38
|
+
const path = __importStar(require("path"));
|
|
39
|
+
describe("Scanner", () => {
|
|
40
|
+
const scanner = new scanner_1.Scanner();
|
|
41
|
+
const testDir = path.join(__dirname, "fixtures");
|
|
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 extract import statements", async () => {
|
|
53
|
+
const code = `
|
|
54
|
+
import lodash from 'lodash';
|
|
55
|
+
import { useState } from 'react';
|
|
56
|
+
const express = require('express');
|
|
57
|
+
`;
|
|
58
|
+
const file = path.join(testDir, "test1.js");
|
|
59
|
+
fs.writeFileSync(file, code);
|
|
60
|
+
const imports = await scanner.scan(testDir);
|
|
61
|
+
expect(imports.has("lodash")).toBe(true);
|
|
62
|
+
expect(imports.has("react")).toBe(true);
|
|
63
|
+
expect(imports.has("express")).toBe(true);
|
|
64
|
+
});
|
|
65
|
+
it("should handle scoped packages", async () => {
|
|
66
|
+
const code = `import { Button } from '@mui/material';`;
|
|
67
|
+
const file = path.join(testDir, "test2.tsx");
|
|
68
|
+
fs.writeFileSync(file, code);
|
|
69
|
+
const imports = await scanner.scan(testDir);
|
|
70
|
+
expect(imports.has("@mui/material")).toBe(true);
|
|
71
|
+
});
|
|
72
|
+
it("should ignore node_modules by default", async () => {
|
|
73
|
+
const nmDir = path.join(testDir, "node_modules");
|
|
74
|
+
fs.mkdirSync(nmDir, { recursive: true });
|
|
75
|
+
fs.writeFileSync(path.join(nmDir, "test.js"), "import 'should-be-ignored';");
|
|
76
|
+
const imports = await scanner.scan(testDir);
|
|
77
|
+
expect(imports.has("should-be-ignored")).toBe(false);
|
|
78
|
+
});
|
|
79
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "pkg-clean",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Find and remove unused dependencies from your Node.js project",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"bin": {
|
|
8
|
+
"pkg-clean": "dist/cli.js"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"dist",
|
|
12
|
+
"README.md",
|
|
13
|
+
"LICENSE"
|
|
14
|
+
],
|
|
15
|
+
"scripts": {
|
|
16
|
+
"build": "tsc",
|
|
17
|
+
"test": "jest",
|
|
18
|
+
"test:watch": "jest --watch",
|
|
19
|
+
"prepublishOnly": "npm run build && npm test"
|
|
20
|
+
},
|
|
21
|
+
"keywords": [
|
|
22
|
+
"dependencies",
|
|
23
|
+
"package.json",
|
|
24
|
+
"cleanup",
|
|
25
|
+
"unused",
|
|
26
|
+
"npm",
|
|
27
|
+
"cli",
|
|
28
|
+
"tool",
|
|
29
|
+
"dependency-analysis"
|
|
30
|
+
],
|
|
31
|
+
"author": "Andrew Passanisi <passanisi.andrew@gmail.com>",
|
|
32
|
+
"license": "MIT",
|
|
33
|
+
"repository": {
|
|
34
|
+
"type": "git",
|
|
35
|
+
"url": "https://github.com/apassanisi/pkg-clean"
|
|
36
|
+
},
|
|
37
|
+
"homepage": "https://github.com/apassanisi/pkg-clean#readme",
|
|
38
|
+
"bugs": {
|
|
39
|
+
"url": "https://github.com/apassanisi/pkg-clean/issues"
|
|
40
|
+
},
|
|
41
|
+
"engines": {
|
|
42
|
+
"node": ">=14.0.0"
|
|
43
|
+
},
|
|
44
|
+
"dependencies": {
|
|
45
|
+
"commander": "^11.1.0"
|
|
46
|
+
},
|
|
47
|
+
"devDependencies": {
|
|
48
|
+
"@types/jest": "^29.0.0",
|
|
49
|
+
"jest": "^28.1.3",
|
|
50
|
+
"ts-jest": "^28.0.8",
|
|
51
|
+
"typescript": "^5.3.2"
|
|
52
|
+
}
|
|
53
|
+
}
|