ghostdep 0.1.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 (50) hide show
  1. package/README.md +176 -0
  2. package/dist/bin/cli.d.ts +3 -0
  3. package/dist/bin/cli.d.ts.map +1 -0
  4. package/dist/bin/cli.js +103 -0
  5. package/dist/bin/cli.js.map +1 -0
  6. package/dist/src/detectors/drift.d.ts +12 -0
  7. package/dist/src/detectors/drift.d.ts.map +1 -0
  8. package/dist/src/detectors/drift.js +95 -0
  9. package/dist/src/detectors/drift.js.map +1 -0
  10. package/dist/src/detectors/duplicates.d.ts +13 -0
  11. package/dist/src/detectors/duplicates.d.ts.map +1 -0
  12. package/dist/src/detectors/duplicates.js +151 -0
  13. package/dist/src/detectors/duplicates.js.map +1 -0
  14. package/dist/src/detectors/ghosts.d.ts +13 -0
  15. package/dist/src/detectors/ghosts.d.ts.map +1 -0
  16. package/dist/src/detectors/ghosts.js +209 -0
  17. package/dist/src/detectors/ghosts.js.map +1 -0
  18. package/dist/src/detectors/zombies.d.ts +22 -0
  19. package/dist/src/detectors/zombies.d.ts.map +1 -0
  20. package/dist/src/detectors/zombies.js +127 -0
  21. package/dist/src/detectors/zombies.js.map +1 -0
  22. package/dist/src/health.d.ts +22 -0
  23. package/dist/src/health.d.ts.map +1 -0
  24. package/dist/src/health.js +123 -0
  25. package/dist/src/health.js.map +1 -0
  26. package/dist/src/index.d.ts +4 -0
  27. package/dist/src/index.d.ts.map +1 -0
  28. package/dist/src/index.js +4 -0
  29. package/dist/src/index.js.map +1 -0
  30. package/dist/src/reporter.d.ts +9 -0
  31. package/dist/src/reporter.d.ts.map +1 -0
  32. package/dist/src/reporter.js +115 -0
  33. package/dist/src/reporter.js.map +1 -0
  34. package/dist/src/scanner.d.ts +12 -0
  35. package/dist/src/scanner.d.ts.map +1 -0
  36. package/dist/src/scanner.js +62 -0
  37. package/dist/src/scanner.js.map +1 -0
  38. package/dist/src/types.d.ts +115 -0
  39. package/dist/src/types.d.ts.map +1 -0
  40. package/dist/src/types.js +2 -0
  41. package/dist/src/types.js.map +1 -0
  42. package/dist/src/utils/fs.d.ts +29 -0
  43. package/dist/src/utils/fs.d.ts.map +1 -0
  44. package/dist/src/utils/fs.js +72 -0
  45. package/dist/src/utils/fs.js.map +1 -0
  46. package/dist/src/utils/packageManager.d.ts +20 -0
  47. package/dist/src/utils/packageManager.d.ts.map +1 -0
  48. package/dist/src/utils/packageManager.js +74 -0
  49. package/dist/src/utils/packageManager.js.map +1 -0
  50. package/package.json +85 -0
package/README.md ADDED
@@ -0,0 +1,176 @@
1
+ # ghostdep 👻
2
+
3
+ [![CI](https://github.com/yourusername/ghostdep/actions/workflows/ci.yml/badge.svg)](https://github.com/yourusername/ghostdep/actions/workflows/ci.yml)
4
+ [![npm version](https://img.shields.io/npm/v/ghostdep.svg?style=flat)](https://www.npmjs.com/package/ghostdep)
5
+ [![license](https://img.shields.io/npm/l/ghostdep.svg)](https://github.com/yourusername/ghostdep/blob/main/LICENSE)
6
+
7
+ An ultra-fast, zero-config **npm dependency scanner** and **nodejs tooling** utility to analyze, audit, and optimize your project dependencies. Instantly audit project health, calculate a dependency health score, and prune duplicate or version drift mismatches.
8
+
9
+ Use **ghostdep** to optimize your dependency tree, secure build-time scripts, and clean up your `package.json` configurations.
10
+
11
+ ---
12
+
13
+ ## 📖 Table of Contents
14
+ - [Introduction](#-introduction)
15
+ - [Features](#-features)
16
+ - [Installation](#-installation)
17
+ - [CLI Commands & Usage](#-cli-commands--usage)
18
+ - [Examples](#-examples)
19
+ - [Programmatic API](#-programmatic-api)
20
+ - [FAQ](#-faq)
21
+ - [Contributing](#-contributing)
22
+ - [License](#-license)
23
+
24
+ ---
25
+
26
+ ## 🌟 Introduction
27
+
28
+ In modern Node.js and TypeScript codebases, dependencies can rapidly drift. Issues like **ghost dependencies** (importing packages in your code that are not declared in `package.json`) and **unused dependencies** (packages declared but never actually used) bloat lockfiles, slow down installation times, and cause runtime crashes in containerized/production environments.
29
+
30
+ `ghostdep` resolves these problems by scanning your source code, comparing actual imports with your `package.json` declarations, walking the `node_modules` hierarchy, and calculating a precise **dependency health score**.
31
+
32
+ ---
33
+
34
+ ## ✨ Features
35
+
36
+ - 👻 **Ghost Dependency Detection**: Identify packages installed in `node_modules` and used in code, but missing from `package.json`.
37
+ - 🧟 **Zombie Dependency Detection**: Spot **unused dependencies** that are declared but never imported or required in your codebase.
38
+ - 📦 **Duplicate Package Finder**: Scan the active node_modules tree (with full PNPM symlink cycle protection) to identify package bloat from multiple resolved versions.
39
+ - 🔄 **Version Drift Auditing**: Compare declared semver ranges in `package.json` with actual installed versions to trace major, minor, or patch drifts.
40
+ - 📊 **Dependency Health Score**: Get an automated quality grade (A-F) based on customizable penalties.
41
+ - ⚡ **TS Compiler AST Parser**: Scans code utilizing the TypeScript Compiler API for ESM imports/exports, dynamic imports, and CommonJS require statements.
42
+
43
+ ---
44
+
45
+ ## 🚀 Installation
46
+
47
+ Install globally to use the CLI anywhere:
48
+
49
+ ```bash
50
+ npm install -g ghostdep
51
+ ```
52
+
53
+ Or install locally in your project:
54
+
55
+ ```bash
56
+ npm install --save-dev ghostdep
57
+ ```
58
+
59
+ ---
60
+
61
+ ## 💻 CLI Commands & Usage
62
+
63
+ Run the scanner directly from your project folder:
64
+
65
+ ```bash
66
+ npx ghostdep
67
+ ```
68
+
69
+ ### Options
70
+
71
+ ```
72
+ Usage: ghostdep [options] [command]
73
+
74
+ Audit Node.js/TypeScript projects for ghost, zombie, duplicate, and drifted dependencies.
75
+
76
+ Options:
77
+ -V, --version output the version number
78
+ -d, --cwd <path> Working directory of the target project (default: current directory)
79
+ --include-dev Include devDependencies in analysis checks (default: false)
80
+ --include <patterns> Comma-separated glob patterns of source files to include
81
+ --exclude <patterns> Comma-separated glob patterns of source files to ignore
82
+ -h, --help display help for command
83
+
84
+ Commands:
85
+ scan Scan project dependencies and print a detailed console report (default)
86
+ json Scan project dependencies and output a raw JSON report
87
+ health Scan project dependencies and print only the health score & grade summary
88
+ help [command] display help for command
89
+ ```
90
+
91
+ ---
92
+
93
+ ## 📝 Examples
94
+
95
+ ### Pretty Report Output
96
+ To execute a standard audit scan in the current directory and display a clean terminal report:
97
+ ```bash
98
+ npx ghostdep scan
99
+ ```
100
+
101
+ ### CI/CD Automation (JSON Output)
102
+ To capture a full structured JSON report file for logging or build pipeline consumption:
103
+ ```bash
104
+ npx ghostdep json > report.json
105
+ ```
106
+
107
+ ### Quick Score Summary
108
+ To query just the final computed health grade of the codebase:
109
+ ```bash
110
+ npx ghostdep health
111
+ # Output: Health Score: 95/100 (A)
112
+ ```
113
+
114
+ ---
115
+
116
+ ## ⚙️ Programmatic API
117
+
118
+ You can import `ghostdep` directly into your Node.js/TypeScript scripts:
119
+
120
+ ```typescript
121
+ import { runScan } from 'ghostdep';
122
+
123
+ async function audit() {
124
+ const report = await runScan({
125
+ cwd: '/path/to/project',
126
+ includeDev: true,
127
+ exclude: ['**/test/**', '**/dist/**']
128
+ });
129
+
130
+ console.log(`Project: ${report.project.name} (v${report.project.version})`);
131
+ console.log(`Overall Score: ${report.health.score}% (${report.health.grade})`);
132
+
133
+ if (report.ghosts.length > 0) {
134
+ console.log('Detected Ghost dependencies:', report.ghosts.map(g => g.name));
135
+ }
136
+ }
137
+
138
+ audit();
139
+ ```
140
+
141
+ ---
142
+
143
+ ## ❓ FAQ
144
+
145
+ ### What are "Ghost Dependencies"?
146
+ Ghost dependencies are packages that your code imports (e.g. `import axios from 'axios'`), but which are not listed in your `package.json` dependencies. This often happens because the package is installed transitively by another dependency, meaning your code works locally but will fail when deployed or run in environments with flat node_modules trees.
147
+
148
+ ### What are "Zombie Dependencies"?
149
+ Zombie dependencies are the opposite: they are listed in your `package.json` file but are never imported or required in your codebase. These waste disk space, slow down `npm install` times, and create security/maintenance overhead.
150
+
151
+ ### Does it support PNPM and Yarn?
152
+ Yes! `ghostdep` implements a symlink-resolving walk algorithm that successfully navigates PNPM virtual stores (`.pnpm`) and Yarn workspaces without infinite loops or duplicate evaluation of symlinks.
153
+
154
+ ---
155
+
156
+ ## 🤝 Contributing
157
+
158
+ Contributions are welcome! Please follow these steps:
159
+ 1. Fork the repository.
160
+ 2. Create your feature branch (`git checkout -b feature/amazing-feature`).
161
+ 3. Commit your changes (`git commit -m 'Add amazing feature'`).
162
+ 4. Push to the branch (`git push origin feature/amazing-feature`).
163
+ 5. Open a Pull Request.
164
+
165
+ Make sure to run formatting and test suites before committing:
166
+ ```bash
167
+ npm run format
168
+ npm run lint
169
+ npm test
170
+ ```
171
+
172
+ ---
173
+
174
+ ## 📄 License
175
+
176
+ This project is licensed under the [MIT License](LICENSE).
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../../bin/cli.ts"],"names":[],"mappings":""}
@@ -0,0 +1,103 @@
1
+ #!/usr/bin/env node
2
+ import { Command } from 'commander';
3
+ import chalk from 'chalk';
4
+ import { runScan } from '../src/scanner.js';
5
+ import { reportToConsole } from '../src/reporter.js';
6
+ const program = new Command();
7
+ // Setup CLI defaults, options, and help metadata
8
+ program
9
+ .name('ghostdep')
10
+ .description('Audit Node.js/TypeScript projects for ghost, zombie, duplicate, and drifted dependencies.')
11
+ .version('0.1.0')
12
+ .option('-d, --cwd <path>', 'Working directory of the target project', process.cwd())
13
+ .option('--include-dev', 'Include devDependencies in analysis checks', false)
14
+ .option('--include <patterns>', 'Comma-separated glob patterns of source files to include')
15
+ .option('--exclude <patterns>', 'Comma-separated glob patterns of source files to ignore');
16
+ /**
17
+ * Utility helper to parse comma-separated CLI inputs into string arrays.
18
+ */
19
+ function parseList(value) {
20
+ return value ? value.split(',').map((item) => item.trim()) : undefined;
21
+ }
22
+ /**
23
+ * Common execution runner that triggers the scanner and formats the report.
24
+ */
25
+ async function executeScan(format) {
26
+ const options = program.opts();
27
+ try {
28
+ const scanOptions = {
29
+ cwd: options['cwd'] || process.cwd(),
30
+ includeDev: !!options['includeDev'],
31
+ };
32
+ const includeList = parseList(options['include']);
33
+ if (includeList) {
34
+ scanOptions.include = includeList;
35
+ }
36
+ const excludeList = parseList(options['exclude']);
37
+ if (excludeList) {
38
+ scanOptions.exclude = excludeList;
39
+ }
40
+ // Import the type ScanOptions inside bin/cli.ts
41
+ const report = await runScan(scanOptions);
42
+ if (format === 'json') {
43
+ console.log(JSON.stringify(report, null, 2));
44
+ }
45
+ else if (format === 'health') {
46
+ const score = report.health.score;
47
+ const grade = report.health.grade;
48
+ const gradeColor = grade === 'A' || grade === 'B'
49
+ ? chalk.bold.green
50
+ : grade === 'C' || grade === 'D'
51
+ ? chalk.bold.yellow
52
+ : chalk.bold.red;
53
+ const scoreColor = score >= 90
54
+ ? chalk.bold.green
55
+ : score >= 75
56
+ ? chalk.bold.cyan
57
+ : score >= 60
58
+ ? chalk.bold.yellow
59
+ : chalk.bold.red;
60
+ console.log(`Health Score: ${scoreColor(score)}/100 (${gradeColor(grade)})`);
61
+ }
62
+ else {
63
+ reportToConsole(report);
64
+ }
65
+ // Set non-zero exit code if critical issue (ghost dependencies) is discovered
66
+ if (report.ghosts.length > 0) {
67
+ process.exitCode = 1;
68
+ }
69
+ }
70
+ catch (error) {
71
+ const err = error;
72
+ console.error(chalk.red.bold(`\nError: ${err.message}`));
73
+ process.exit(1);
74
+ }
75
+ }
76
+ // 1. Default action (when no subcommand is passed)
77
+ program.action(async () => {
78
+ await executeScan('pretty');
79
+ });
80
+ // 2. "scan" subcommand (same behavior as default, but explicit)
81
+ program
82
+ .command('scan')
83
+ .description('Scan project dependencies and print a detailed console report (default action)')
84
+ .action(async () => {
85
+ await executeScan('pretty');
86
+ });
87
+ // 3. "json" subcommand (outputs raw JSON report for CI/pipeline consumption)
88
+ program
89
+ .command('json')
90
+ .description('Scan project dependencies and output a raw JSON report')
91
+ .action(async () => {
92
+ await executeScan('json');
93
+ });
94
+ // 4. "health" subcommand (outputs only the score summary)
95
+ program
96
+ .command('health')
97
+ .description('Scan project dependencies and print only the health score & grade summary')
98
+ .action(async () => {
99
+ await executeScan('health');
100
+ });
101
+ // Run parser
102
+ program.parse(process.argv);
103
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../../bin/cli.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ import { VersionDrift, PackageJson } from '../types.js';
2
+ /**
3
+ * Detects version drift issues: discrepancies between the declared semver ranges
4
+ * in package.json and the actual installed package versions in node_modules.
5
+ *
6
+ * @param projectRoot Absolute path to the project root directory
7
+ * @param packageJson Parsed package.json object
8
+ * @param includeDev Whether to include devDependencies in the drift check
9
+ * @returns Array of VersionDrift results
10
+ */
11
+ export declare function detectDrifts(projectRoot: string, packageJson: PackageJson, includeDev?: boolean): Promise<VersionDrift[]>;
12
+ //# sourceMappingURL=drift.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"drift.d.ts","sourceRoot":"","sources":["../../../src/detectors/drift.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAqDxD;;;;;;;;GAQG;AACH,wBAAsB,YAAY,CAChC,WAAW,EAAE,MAAM,EACnB,WAAW,EAAE,WAAW,EACxB,UAAU,GAAE,OAAe,GAC1B,OAAO,CAAC,YAAY,EAAE,CAAC,CA4CzB"}
@@ -0,0 +1,95 @@
1
+ import { promises as fs } from 'fs';
2
+ import { join } from 'path';
3
+ import semver from 'semver';
4
+ /**
5
+ * Resolves the installed version of a package by reading its package.json inside node_modules.
6
+ */
7
+ async function getInstalledVersion(projectRoot, packageName) {
8
+ const packageJsonPath = join(projectRoot, 'node_modules', packageName, 'package.json');
9
+ try {
10
+ const content = await fs.readFile(packageJsonPath, 'utf8');
11
+ const pkg = JSON.parse(content);
12
+ return pkg.version || null;
13
+ }
14
+ catch {
15
+ return null;
16
+ }
17
+ }
18
+ /**
19
+ * Calculates the semver drift type between a declared range and the installed version.
20
+ */
21
+ function calculateDriftType(declaredRange, installedVersion) {
22
+ if (!installedVersion) {
23
+ return 'missing';
24
+ }
25
+ // If the range is not valid semver (e.g. workspace:*, git URL, file path, latest)
26
+ if (!semver.validRange(declaredRange)) {
27
+ // Exact string match comparison fallback
28
+ return declaredRange === installedVersion ? 'none' : 'major';
29
+ }
30
+ const minVersion = semver.minVersion(declaredRange);
31
+ if (!minVersion) {
32
+ return 'none';
33
+ }
34
+ const diff = semver.diff(minVersion.version, installedVersion);
35
+ if (!diff) {
36
+ return 'none';
37
+ }
38
+ if (diff === 'major' || diff === 'premajor') {
39
+ return 'major';
40
+ }
41
+ if (diff === 'minor' || diff === 'preminor') {
42
+ return 'minor';
43
+ }
44
+ if (diff === 'patch' || diff === 'prepatch' || diff === 'prerelease') {
45
+ return 'patch';
46
+ }
47
+ return 'none';
48
+ }
49
+ /**
50
+ * Detects version drift issues: discrepancies between the declared semver ranges
51
+ * in package.json and the actual installed package versions in node_modules.
52
+ *
53
+ * @param projectRoot Absolute path to the project root directory
54
+ * @param packageJson Parsed package.json object
55
+ * @param includeDev Whether to include devDependencies in the drift check
56
+ * @returns Array of VersionDrift results
57
+ */
58
+ export async function detectDrifts(projectRoot, packageJson, includeDev = false) {
59
+ try {
60
+ const candidates = [];
61
+ const addCandidates = (deps, dependencyType) => {
62
+ if (deps) {
63
+ for (const [name, version] of Object.entries(deps)) {
64
+ candidates.push({ name, declaredVersion: version, dependencyType });
65
+ }
66
+ }
67
+ };
68
+ addCandidates(packageJson.dependencies, 'dependencies');
69
+ if (includeDev) {
70
+ addCandidates(packageJson.devDependencies, 'devDependencies');
71
+ }
72
+ addCandidates(packageJson.peerDependencies, 'peerDependencies');
73
+ addCandidates(packageJson.optionalDependencies, 'optionalDependencies');
74
+ const drifts = [];
75
+ for (const candidate of candidates) {
76
+ const installedVersion = await getInstalledVersion(projectRoot, candidate.name);
77
+ const driftType = calculateDriftType(candidate.declaredVersion, installedVersion);
78
+ if (driftType !== 'none') {
79
+ drifts.push({
80
+ name: candidate.name,
81
+ dependencyType: candidate.dependencyType,
82
+ declaredVersion: candidate.declaredVersion,
83
+ installedVersion,
84
+ driftType,
85
+ });
86
+ }
87
+ }
88
+ return drifts;
89
+ }
90
+ catch (error) {
91
+ const err = error;
92
+ throw new Error(`Version drift detection failed: ${err.message}`);
93
+ }
94
+ }
95
+ //# sourceMappingURL=drift.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"drift.js","sourceRoot":"","sources":["../../../src/detectors/drift.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ import { DuplicateDependency } from '../types.js';
2
+ /**
3
+ * Detects packages with duplicate versions installed under node_modules.
4
+ *
5
+ * Works cross-platform and supports npm, pnpm, and yarn by walking the active
6
+ * dependency graph in node_modules, resolving symlinks canonical-paths (to handle
7
+ * pnpm structures without infinite loops) and tracking visited packages.
8
+ *
9
+ * @param projectRoot Absolute path to the project root directory
10
+ * @returns Array of DuplicateDependency details
11
+ */
12
+ export declare function detectDuplicates(projectRoot: string): Promise<DuplicateDependency[]>;
13
+ //# sourceMappingURL=duplicates.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"duplicates.d.ts","sourceRoot":"","sources":["../../../src/detectors/duplicates.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,mBAAmB,EAA+B,MAAM,aAAa,CAAC;AAE/E;;;;;;;;;GASG;AACH,wBAAsB,gBAAgB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,mBAAmB,EAAE,CAAC,CAiJ1F"}
@@ -0,0 +1,151 @@
1
+ import { promises as fs } from 'fs';
2
+ import { join } from 'path';
3
+ /**
4
+ * Detects packages with duplicate versions installed under node_modules.
5
+ *
6
+ * Works cross-platform and supports npm, pnpm, and yarn by walking the active
7
+ * dependency graph in node_modules, resolving symlinks canonical-paths (to handle
8
+ * pnpm structures without infinite loops) and tracking visited packages.
9
+ *
10
+ * @param projectRoot Absolute path to the project root directory
11
+ * @returns Array of DuplicateDependency details
12
+ */
13
+ export async function detectDuplicates(projectRoot) {
14
+ const nodeModulesPath = join(projectRoot, 'node_modules');
15
+ const visitedPaths = new Set();
16
+ // Maps: packageName -> Map(versionString -> Array of relative path strings)
17
+ const packageVersionsMap = new Map();
18
+ /**
19
+ * Helper recursive function to walk package directories.
20
+ */
21
+ async function walk(dirPath, relativePathPrefix) {
22
+ try {
23
+ // Canonicalize the path to handle symlinks (crucial for pnpm / yarn berry)
24
+ const realPath = await fs.realpath(dirPath);
25
+ if (visitedPaths.has(realPath)) {
26
+ return;
27
+ }
28
+ visitedPaths.add(realPath);
29
+ let pkgName;
30
+ let pkgVersion;
31
+ // Try reading package.json of the current directory
32
+ try {
33
+ const pkgJsonContent = await fs.readFile(join(dirPath, 'package.json'), 'utf8');
34
+ const pkgJson = JSON.parse(pkgJsonContent);
35
+ pkgName = pkgJson.name;
36
+ pkgVersion = pkgJson.version;
37
+ }
38
+ catch {
39
+ // No valid package.json in this directory, skip reading it as a package
40
+ }
41
+ if (pkgName && pkgVersion) {
42
+ let versionMap = packageVersionsMap.get(pkgName);
43
+ if (!versionMap) {
44
+ versionMap = new Map();
45
+ packageVersionsMap.set(pkgName, versionMap);
46
+ }
47
+ let pathsList = versionMap.get(pkgVersion);
48
+ if (!pathsList) {
49
+ pathsList = [];
50
+ versionMap.set(pkgVersion, pathsList);
51
+ }
52
+ pathsList.push(relativePathPrefix);
53
+ }
54
+ // Check for nested node_modules folder (typical in npm / yarn classic)
55
+ const nestedNodeModules = join(dirPath, 'node_modules');
56
+ try {
57
+ const entries = await fs.readdir(nestedNodeModules, { withFileTypes: true });
58
+ for (const entry of entries) {
59
+ // Skip internal/hidden folders
60
+ if (entry.name.startsWith('.')) {
61
+ continue;
62
+ }
63
+ if (entry.isDirectory() || entry.isSymbolicLink()) {
64
+ const entryPath = join(nestedNodeModules, entry.name);
65
+ const nextPrefix = relativePathPrefix
66
+ ? `${relativePathPrefix}/node_modules/${entry.name}`
67
+ : `node_modules/${entry.name}`;
68
+ if (entry.name.startsWith('@')) {
69
+ // Read scoped packages directory
70
+ try {
71
+ const scopeEntries = await fs.readdir(entryPath, { withFileTypes: true });
72
+ for (const scopeEntry of scopeEntries) {
73
+ if (scopeEntry.isDirectory() || scopeEntry.isSymbolicLink()) {
74
+ const scopeEntryPath = join(entryPath, scopeEntry.name);
75
+ const scopeNextPrefix = `${nextPrefix}/${scopeEntry.name}`;
76
+ await walk(scopeEntryPath, scopeNextPrefix);
77
+ }
78
+ }
79
+ }
80
+ catch {
81
+ // Ignore failures to read scoped package directories
82
+ }
83
+ }
84
+ else {
85
+ await walk(entryPath, nextPrefix);
86
+ }
87
+ }
88
+ }
89
+ }
90
+ catch {
91
+ // Nested node_modules directory does not exist or is unreadable
92
+ }
93
+ }
94
+ catch {
95
+ // Path resolution failed (e.g. broken symlink), skip directory safely
96
+ }
97
+ }
98
+ // Start walking from the root node_modules directory
99
+ try {
100
+ const rootEntries = await fs.readdir(nodeModulesPath, { withFileTypes: true });
101
+ for (const entry of rootEntries) {
102
+ if (entry.name.startsWith('.')) {
103
+ continue;
104
+ }
105
+ if (entry.isDirectory() || entry.isSymbolicLink()) {
106
+ const entryPath = join(nodeModulesPath, entry.name);
107
+ const relativePrefix = `node_modules/${entry.name}`;
108
+ if (entry.name.startsWith('@')) {
109
+ try {
110
+ const scopeEntries = await fs.readdir(entryPath, { withFileTypes: true });
111
+ for (const scopeEntry of scopeEntries) {
112
+ if (scopeEntry.isDirectory() || scopeEntry.isSymbolicLink()) {
113
+ const scopeEntryPath = join(entryPath, scopeEntry.name);
114
+ const scopeRelativePrefix = `${relativePrefix}/${scopeEntry.name}`;
115
+ await walk(scopeEntryPath, scopeRelativePrefix);
116
+ }
117
+ }
118
+ }
119
+ catch {
120
+ // Ignore scoped subdirectory read errors
121
+ }
122
+ }
123
+ else {
124
+ await walk(entryPath, relativePrefix);
125
+ }
126
+ }
127
+ }
128
+ }
129
+ catch {
130
+ // node_modules doesn't exist, is unreadable, or project isn't installed
131
+ }
132
+ // Format and collect final duplicates mapping
133
+ const duplicates = [];
134
+ for (const [name, versionMap] of packageVersionsMap.entries()) {
135
+ if (versionMap.size > 1) {
136
+ const versions = [];
137
+ for (const [version, paths] of versionMap.entries()) {
138
+ versions.push({
139
+ version,
140
+ paths,
141
+ });
142
+ }
143
+ duplicates.push({
144
+ name,
145
+ versions,
146
+ });
147
+ }
148
+ }
149
+ return duplicates;
150
+ }
151
+ //# sourceMappingURL=duplicates.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"duplicates.js","sourceRoot":"","sources":["../../../src/detectors/duplicates.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ import { GhostDependency, PackageJson } from '../types.js';
2
+ /**
3
+ * Detects ghost dependencies: packages imported in the source code
4
+ * that are installed in node_modules but not listed in package.json.
5
+ *
6
+ * @param projectRoot Absolute path to the project root directory
7
+ * @param sourceFiles List of absolute paths of source files to analyze
8
+ * @param packageJson Parsed package.json object
9
+ * @param includeDev Whether to include devDependencies as declared packages
10
+ * @returns Array of detected ghost dependencies
11
+ */
12
+ export declare function detectGhosts(projectRoot: string, sourceFiles: string[], packageJson: PackageJson, includeDev?: boolean): Promise<GhostDependency[]>;
13
+ //# sourceMappingURL=ghosts.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ghosts.d.ts","sourceRoot":"","sources":["../../../src/detectors/ghosts.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAiJ3D;;;;;;;;;GASG;AACH,wBAAsB,YAAY,CAChC,WAAW,EAAE,MAAM,EACnB,WAAW,EAAE,MAAM,EAAE,EACrB,WAAW,EAAE,WAAW,EACxB,UAAU,GAAE,OAAe,GAC1B,OAAO,CAAC,eAAe,EAAE,CAAC,CAoE5B"}