projscan 0.3.1 → 0.4.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 (46) hide show
  1. package/README.md +35 -1
  2. package/dist/analyzers/dependencyRiskCheck.js +29 -10
  3. package/dist/analyzers/dependencyRiskCheck.js.map +1 -1
  4. package/dist/analyzers/unusedDependencyCheck.d.ts +2 -0
  5. package/dist/analyzers/unusedDependencyCheck.js +144 -0
  6. package/dist/analyzers/unusedDependencyCheck.js.map +1 -0
  7. package/dist/cli/index.js +113 -4
  8. package/dist/cli/index.js.map +1 -1
  9. package/dist/core/auditRunner.d.ts +16 -0
  10. package/dist/core/auditRunner.js +222 -0
  11. package/dist/core/auditRunner.js.map +1 -0
  12. package/dist/core/importGraph.d.ts +31 -0
  13. package/dist/core/importGraph.js +139 -0
  14. package/dist/core/importGraph.js.map +1 -0
  15. package/dist/core/issueEngine.js +2 -0
  16. package/dist/core/issueEngine.js.map +1 -1
  17. package/dist/core/outdatedDetector.d.ts +9 -0
  18. package/dist/core/outdatedDetector.js +87 -0
  19. package/dist/core/outdatedDetector.js.map +1 -0
  20. package/dist/core/upgradePreview.d.ts +2 -0
  21. package/dist/core/upgradePreview.js +167 -0
  22. package/dist/core/upgradePreview.js.map +1 -0
  23. package/dist/index.d.ts +7 -1
  24. package/dist/index.js +6 -0
  25. package/dist/index.js.map +1 -1
  26. package/dist/mcp/tools.js +46 -0
  27. package/dist/mcp/tools.js.map +1 -1
  28. package/dist/reporters/consoleReporter.d.ts +4 -1
  29. package/dist/reporters/consoleReporter.js +131 -0
  30. package/dist/reporters/consoleReporter.js.map +1 -1
  31. package/dist/reporters/jsonReporter.d.ts +4 -1
  32. package/dist/reporters/jsonReporter.js +9 -0
  33. package/dist/reporters/jsonReporter.js.map +1 -1
  34. package/dist/reporters/markdownReporter.d.ts +4 -1
  35. package/dist/reporters/markdownReporter.js +84 -0
  36. package/dist/reporters/markdownReporter.js.map +1 -1
  37. package/dist/types.d.ts +44 -0
  38. package/dist/utils/banner.js +9 -6
  39. package/dist/utils/banner.js.map +1 -1
  40. package/dist/utils/packageJsonLocator.d.ts +11 -0
  41. package/dist/utils/packageJsonLocator.js +58 -0
  42. package/dist/utils/packageJsonLocator.js.map +1 -0
  43. package/dist/utils/semver.d.ts +13 -0
  44. package/dist/utils/semver.js +45 -0
  45. package/dist/utils/semver.js.map +1 -0
  46. package/package.json +2 -2
@@ -0,0 +1,222 @@
1
+ import { execFile } from 'node:child_process';
2
+ import { promisify } from 'node:util';
3
+ import fs from 'node:fs/promises';
4
+ import path from 'node:path';
5
+ const execFileAsync = promisify(execFile);
6
+ const EMPTY_SUMMARY = {
7
+ critical: 0,
8
+ high: 0,
9
+ moderate: 0,
10
+ low: 0,
11
+ info: 0,
12
+ };
13
+ /**
14
+ * Run `npm audit --json` and normalize the output.
15
+ *
16
+ * npm's audit JSON format has changed between npm 6/7/8/9+ — we handle the
17
+ * modern format (npm 7+) first and fall back to a friendly error otherwise.
18
+ * Yarn/pnpm projects: we don't try to translate; we report "not available"
19
+ * with a hint.
20
+ */
21
+ export async function runAudit(rootPath, options = {}) {
22
+ const hasPackageJson = await fileExists(path.join(rootPath, 'package.json'));
23
+ if (!hasPackageJson) {
24
+ return unavailable('No package.json found in this directory');
25
+ }
26
+ const hasNpmLock = await fileExists(path.join(rootPath, 'package-lock.json'));
27
+ const hasYarnLock = await fileExists(path.join(rootPath, 'yarn.lock'));
28
+ const hasPnpmLock = await fileExists(path.join(rootPath, 'pnpm-lock.yaml'));
29
+ if (!hasNpmLock) {
30
+ if (hasYarnLock) {
31
+ return unavailable('yarn.lock detected — run `yarn npm audit` instead');
32
+ }
33
+ if (hasPnpmLock) {
34
+ return unavailable('pnpm-lock.yaml detected — run `pnpm audit` instead');
35
+ }
36
+ return unavailable('No package-lock.json — run `npm install` first, then retry');
37
+ }
38
+ const timeoutMs = options.timeoutMs ?? 60_000;
39
+ let stdout;
40
+ try {
41
+ const result = await execFileAsync('npm', ['audit', '--json'], {
42
+ cwd: rootPath,
43
+ maxBuffer: 16 * 1024 * 1024,
44
+ timeout: timeoutMs,
45
+ });
46
+ stdout = result.stdout;
47
+ }
48
+ catch (err) {
49
+ // `npm audit` exits non-zero when vulnerabilities exist — this is normal.
50
+ // The stdout still contains the JSON payload.
51
+ const e = err;
52
+ if (typeof e.stdout === 'string' && e.stdout) {
53
+ stdout = e.stdout;
54
+ }
55
+ else if (e.stdout instanceof Buffer) {
56
+ stdout = e.stdout.toString('utf-8');
57
+ }
58
+ else {
59
+ return unavailable(`npm audit failed: ${e.message ?? 'unknown error'}`);
60
+ }
61
+ }
62
+ if (!stdout)
63
+ return unavailable('npm audit returned no output');
64
+ let payload;
65
+ try {
66
+ payload = JSON.parse(stdout);
67
+ }
68
+ catch {
69
+ return unavailable('npm audit returned invalid JSON');
70
+ }
71
+ return normalize(payload);
72
+ }
73
+ function normalize(payload) {
74
+ // Modern format (npm 7+): { vulnerabilities: { name: { severity, via, range, fixAvailable, ... } }, metadata: { vulnerabilities: { critical, high, ... } } }
75
+ const vulns = payload.vulnerabilities;
76
+ const metadata = payload.metadata;
77
+ if (!vulns || typeof vulns !== 'object') {
78
+ return {
79
+ available: true,
80
+ summary: { ...EMPTY_SUMMARY },
81
+ findings: [],
82
+ };
83
+ }
84
+ const findings = [];
85
+ for (const [name, info] of Object.entries(vulns)) {
86
+ if (!info || typeof info !== 'object')
87
+ continue;
88
+ const entry = info;
89
+ const severity = normalizeSeverity(entry.severity);
90
+ const via = normalizeVia(entry.via);
91
+ const fixAvailable = entry.fixAvailable !== false && entry.fixAvailable !== undefined;
92
+ findings.push({
93
+ name,
94
+ severity,
95
+ title: extractTitle(via, name, severity),
96
+ url: extractUrl(via),
97
+ cve: extractCves(via),
98
+ via,
99
+ range: typeof entry.range === 'string' ? entry.range : undefined,
100
+ fixAvailable,
101
+ });
102
+ }
103
+ const summary = { ...EMPTY_SUMMARY };
104
+ if (metadata?.vulnerabilities) {
105
+ for (const k of ['critical', 'high', 'moderate', 'low', 'info']) {
106
+ summary[k] = metadata.vulnerabilities[k] ?? 0;
107
+ }
108
+ }
109
+ else {
110
+ for (const f of findings)
111
+ summary[f.severity]++;
112
+ }
113
+ findings.sort((a, b) => severityWeight(b.severity) - severityWeight(a.severity));
114
+ return {
115
+ available: true,
116
+ summary,
117
+ findings,
118
+ };
119
+ }
120
+ function normalizeSeverity(raw) {
121
+ if (typeof raw !== 'string')
122
+ return 'info';
123
+ const v = raw.toLowerCase();
124
+ if (v === 'critical' || v === 'high' || v === 'moderate' || v === 'low' || v === 'info') {
125
+ return v;
126
+ }
127
+ return 'info';
128
+ }
129
+ function normalizeVia(raw) {
130
+ if (!raw)
131
+ return [];
132
+ if (typeof raw === 'string')
133
+ return [raw];
134
+ if (!Array.isArray(raw))
135
+ return [];
136
+ const out = [];
137
+ for (const item of raw) {
138
+ if (typeof item === 'string')
139
+ out.push(item);
140
+ else if (item && typeof item === 'object' && 'name' in item) {
141
+ out.push(String(item.name));
142
+ }
143
+ }
144
+ return out;
145
+ }
146
+ function extractTitle(via, name, severity) {
147
+ const first = via[0];
148
+ if (first && first !== name)
149
+ return `${first} (${severity})`;
150
+ return `Vulnerability in ${name} (${severity})`;
151
+ }
152
+ function extractUrl(via) {
153
+ if (!Array.isArray(via))
154
+ return undefined;
155
+ for (const item of via) {
156
+ if (item && typeof item === 'object' && 'url' in item) {
157
+ const url = item.url;
158
+ if (typeof url === 'string')
159
+ return url;
160
+ }
161
+ }
162
+ return undefined;
163
+ }
164
+ function extractCves(via) {
165
+ if (!Array.isArray(via))
166
+ return undefined;
167
+ const cves = new Set();
168
+ for (const item of via) {
169
+ if (item && typeof item === 'object' && 'cwe' in item) {
170
+ const cwe = item.cwe;
171
+ if (Array.isArray(cwe))
172
+ for (const c of cwe)
173
+ if (typeof c === 'string')
174
+ cves.add(c);
175
+ }
176
+ }
177
+ return cves.size > 0 ? [...cves] : undefined;
178
+ }
179
+ function severityWeight(s) {
180
+ return { critical: 5, high: 4, moderate: 3, low: 2, info: 1 }[s];
181
+ }
182
+ function unavailable(reason) {
183
+ return {
184
+ available: false,
185
+ reason,
186
+ summary: { ...EMPTY_SUMMARY },
187
+ findings: [],
188
+ };
189
+ }
190
+ async function fileExists(p) {
191
+ try {
192
+ await fs.access(p);
193
+ return true;
194
+ }
195
+ catch {
196
+ return false;
197
+ }
198
+ }
199
+ /** Convert an AuditReport into projscan Issues for SARIF emission. */
200
+ export function auditFindingsToIssues(report) {
201
+ if (!report.available)
202
+ return [];
203
+ const severityMap = {
204
+ critical: 'error',
205
+ high: 'error',
206
+ moderate: 'warning',
207
+ low: 'info',
208
+ info: 'info',
209
+ };
210
+ return report.findings.map((f) => ({
211
+ id: `audit-${f.name}`,
212
+ title: f.title,
213
+ description: f.url !== undefined
214
+ ? `${f.title} — ${f.url}${f.range ? ` (range: ${f.range})` : ''}`
215
+ : `${f.title}${f.range ? ` (range: ${f.range})` : ''}`,
216
+ severity: severityMap[f.severity],
217
+ category: 'security',
218
+ fixAvailable: f.fixAvailable,
219
+ locations: [{ file: 'package.json' }],
220
+ }));
221
+ }
222
+ //# sourceMappingURL=auditRunner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auditRunner.js","sourceRoot":"","sources":["../../src/core/auditRunner.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,31 @@
1
+ import type { FileEntry } from '../types.js';
2
+ export interface ImportGraph {
3
+ /** file → set of import specifiers exactly as they appear in source */
4
+ byFile: Map<string, Set<string>>;
5
+ /** unique set of non-relative, non-builtin specifiers (package names) */
6
+ externalPackages: Set<string>;
7
+ /** count of source files scanned */
8
+ scannedFiles: number;
9
+ }
10
+ /**
11
+ * Walk source files and build an import graph. Extracts ES imports and
12
+ * CommonJS requires; falls back silently on unreadable / oversized files.
13
+ */
14
+ export declare function buildImportGraph(rootPath: string, files: FileEntry[]): Promise<ImportGraph>;
15
+ /**
16
+ * Convert an import specifier to a bare package name.
17
+ * Returns null for relative paths ("./", "../", "/"), node: builtins, and bare builtins.
18
+ *
19
+ * 'react' -> 'react'
20
+ * 'react/jsx-runtime' -> 'react'
21
+ * '@types/node' -> '@types/node'
22
+ * '@scope/pkg/deep' -> '@scope/pkg'
23
+ * './local' -> null
24
+ * 'node:fs' -> null
25
+ * 'fs' -> null (node builtin)
26
+ */
27
+ export declare function toPackageName(specifier: string): string | null;
28
+ /** Check if a package is referenced by at least one file in the graph. */
29
+ export declare function isPackageUsed(graph: ImportGraph, pkg: string): boolean;
30
+ /** List files that import a given package. */
31
+ export declare function filesImporting(graph: ImportGraph, pkg: string): string[];
@@ -0,0 +1,139 @@
1
+ import fs from 'node:fs/promises';
2
+ import path from 'node:path';
3
+ import { extractImports } from './fileInspector.js';
4
+ const SOURCE_EXTENSIONS = new Set([
5
+ '.ts',
6
+ '.tsx',
7
+ '.js',
8
+ '.jsx',
9
+ '.mjs',
10
+ '.cjs',
11
+ '.mts',
12
+ '.cts',
13
+ ]);
14
+ const MAX_FILE_SIZE = 1024 * 1024; // 1 MB — skip giant generated files
15
+ const NODE_BUILTINS = new Set([
16
+ 'assert',
17
+ 'async_hooks',
18
+ 'buffer',
19
+ 'child_process',
20
+ 'cluster',
21
+ 'console',
22
+ 'constants',
23
+ 'crypto',
24
+ 'dgram',
25
+ 'dns',
26
+ 'domain',
27
+ 'events',
28
+ 'fs',
29
+ 'fs/promises',
30
+ 'http',
31
+ 'http2',
32
+ 'https',
33
+ 'inspector',
34
+ 'module',
35
+ 'net',
36
+ 'os',
37
+ 'path',
38
+ 'perf_hooks',
39
+ 'process',
40
+ 'punycode',
41
+ 'querystring',
42
+ 'readline',
43
+ 'repl',
44
+ 'stream',
45
+ 'string_decoder',
46
+ 'sys',
47
+ 'timers',
48
+ 'tls',
49
+ 'trace_events',
50
+ 'tty',
51
+ 'url',
52
+ 'util',
53
+ 'v8',
54
+ 'vm',
55
+ 'wasi',
56
+ 'worker_threads',
57
+ 'zlib',
58
+ ]);
59
+ /**
60
+ * Walk source files and build an import graph. Extracts ES imports and
61
+ * CommonJS requires; falls back silently on unreadable / oversized files.
62
+ */
63
+ export async function buildImportGraph(rootPath, files) {
64
+ const byFile = new Map();
65
+ const externalPackages = new Set();
66
+ let scannedFiles = 0;
67
+ const sourceFiles = files.filter((f) => SOURCE_EXTENSIONS.has(f.extension) && f.sizeBytes <= MAX_FILE_SIZE);
68
+ await Promise.all(sourceFiles.map(async (file) => {
69
+ const abs = path.isAbsolute(file.absolutePath)
70
+ ? file.absolutePath
71
+ : path.resolve(rootPath, file.relativePath);
72
+ let content;
73
+ try {
74
+ content = await fs.readFile(abs, 'utf-8');
75
+ }
76
+ catch {
77
+ return;
78
+ }
79
+ scannedFiles++;
80
+ const imports = extractImports(content);
81
+ const specifiers = new Set();
82
+ for (const imp of imports) {
83
+ specifiers.add(imp.source);
84
+ const pkg = toPackageName(imp.source);
85
+ if (pkg && !NODE_BUILTINS.has(pkg) && !pkg.startsWith('node:')) {
86
+ externalPackages.add(pkg);
87
+ }
88
+ }
89
+ byFile.set(file.relativePath, specifiers);
90
+ }));
91
+ return { byFile, externalPackages, scannedFiles };
92
+ }
93
+ /**
94
+ * Convert an import specifier to a bare package name.
95
+ * Returns null for relative paths ("./", "../", "/"), node: builtins, and bare builtins.
96
+ *
97
+ * 'react' -> 'react'
98
+ * 'react/jsx-runtime' -> 'react'
99
+ * '@types/node' -> '@types/node'
100
+ * '@scope/pkg/deep' -> '@scope/pkg'
101
+ * './local' -> null
102
+ * 'node:fs' -> null
103
+ * 'fs' -> null (node builtin)
104
+ */
105
+ export function toPackageName(specifier) {
106
+ if (!specifier)
107
+ return null;
108
+ if (specifier.startsWith('.') || specifier.startsWith('/'))
109
+ return null;
110
+ if (specifier.startsWith('node:'))
111
+ return null;
112
+ if (NODE_BUILTINS.has(specifier))
113
+ return null;
114
+ if (specifier.startsWith('@')) {
115
+ const segments = specifier.split('/');
116
+ if (segments.length < 2)
117
+ return null;
118
+ return `${segments[0]}/${segments[1]}`;
119
+ }
120
+ return specifier.split('/')[0];
121
+ }
122
+ /** Check if a package is referenced by at least one file in the graph. */
123
+ export function isPackageUsed(graph, pkg) {
124
+ return graph.externalPackages.has(pkg);
125
+ }
126
+ /** List files that import a given package. */
127
+ export function filesImporting(graph, pkg) {
128
+ const out = [];
129
+ for (const [file, specifiers] of graph.byFile) {
130
+ for (const spec of specifiers) {
131
+ if (toPackageName(spec) === pkg) {
132
+ out.push(file);
133
+ break;
134
+ }
135
+ }
136
+ }
137
+ return out.sort();
138
+ }
139
+ //# sourceMappingURL=importGraph.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"importGraph.js","sourceRoot":"","sources":["../../src/core/importGraph.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAClC,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAEpD,MAAM,iBAAiB,GAAG,IAAI,GAAG,CAAC;IAChC,KAAK;IACL,MAAM;IACN,KAAK;IACL,MAAM;IACN,MAAM;IACN,MAAM;IACN,MAAM;IACN,MAAM;CACP,CAAC,CAAC;AAEH,MAAM,aAAa,GAAG,IAAI,GAAG,IAAI,CAAC,CAAC,oCAAoC;AAWvE,MAAM,aAAa,GAAG,IAAI,GAAG,CAAC;IAC5B,QAAQ;IACR,aAAa;IACb,QAAQ;IACR,eAAe;IACf,SAAS;IACT,SAAS;IACT,WAAW;IACX,QAAQ;IACR,OAAO;IACP,KAAK;IACL,QAAQ;IACR,QAAQ;IACR,IAAI;IACJ,aAAa;IACb,MAAM;IACN,OAAO;IACP,OAAO;IACP,WAAW;IACX,QAAQ;IACR,KAAK;IACL,IAAI;IACJ,MAAM;IACN,YAAY;IACZ,SAAS;IACT,UAAU;IACV,aAAa;IACb,UAAU;IACV,MAAM;IACN,QAAQ;IACR,gBAAgB;IAChB,KAAK;IACL,QAAQ;IACR,KAAK;IACL,cAAc;IACd,KAAK;IACL,KAAK;IACL,MAAM;IACN,IAAI;IACJ,IAAI;IACJ,MAAM;IACN,gBAAgB;IAChB,MAAM;CACP,CAAC,CAAC;AAEH;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACpC,QAAgB,EAChB,KAAkB;IAElB,MAAM,MAAM,GAAG,IAAI,GAAG,EAAuB,CAAC;IAC9C,MAAM,gBAAgB,GAAG,IAAI,GAAG,EAAU,CAAC;IAC3C,IAAI,YAAY,GAAG,CAAC,CAAC;IAErB,MAAM,WAAW,GAAG,KAAK,CAAC,MAAM,CAC9B,CAAC,CAAC,EAAE,EAAE,CAAC,iBAAiB,CAAC,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,SAAS,IAAI,aAAa,CAC1E,CAAC;IAEF,MAAM,OAAO,CAAC,GAAG,CACf,WAAW,CAAC,GAAG,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE;QAC7B,MAAM,GAAG,GAAG,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,YAAY,CAAC;YAC5C,CAAC,CAAC,IAAI,CAAC,YAAY;YACnB,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,IAAI,CAAC,YAAY,CAAC,CAAC;QAC9C,IAAI,OAAe,CAAC;QACpB,IAAI,CAAC;YACH,OAAO,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;QAC5C,CAAC;QAAC,MAAM,CAAC;YACP,OAAO;QACT,CAAC;QACD,YAAY,EAAE,CAAC;QACf,MAAM,OAAO,GAAG,cAAc,CAAC,OAAO,CAAC,CAAC;QACxC,MAAM,UAAU,GAAG,IAAI,GAAG,EAAU,CAAC;QACrC,KAAK,MAAM,GAAG,IAAI,OAAO,EAAE,CAAC;YAC1B,UAAU,CAAC,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;YAC3B,MAAM,GAAG,GAAG,aAAa,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;YACtC,IAAI,GAAG,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;gBAC/D,gBAAgB,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;YAC5B,CAAC;QACH,CAAC;QACD,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,YAAY,EAAE,UAAU,CAAC,CAAC;IAC5C,CAAC,CAAC,CACH,CAAC;IAEF,OAAO,EAAE,MAAM,EAAE,gBAAgB,EAAE,YAAY,EAAE,CAAC;AACpD,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,aAAa,CAAC,SAAiB;IAC7C,IAAI,CAAC,SAAS;QAAE,OAAO,IAAI,CAAC;IAC5B,IAAI,SAAS,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,SAAS,CAAC,UAAU,CAAC,GAAG,CAAC;QAAE,OAAO,IAAI,CAAC;IACxE,IAAI,SAAS,CAAC,UAAU,CAAC,OAAO,CAAC;QAAE,OAAO,IAAI,CAAC;IAC/C,IAAI,aAAa,CAAC,GAAG,CAAC,SAAS,CAAC;QAAE,OAAO,IAAI,CAAC;IAE9C,IAAI,SAAS,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;QAC9B,MAAM,QAAQ,GAAG,SAAS,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QACtC,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC;YAAE,OAAO,IAAI,CAAC;QACrC,OAAO,GAAG,QAAQ,CAAC,CAAC,CAAC,IAAI,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC;IACzC,CAAC;IAED,OAAO,SAAS,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;AACjC,CAAC;AAED,0EAA0E;AAC1E,MAAM,UAAU,aAAa,CAAC,KAAkB,EAAE,GAAW;IAC3D,OAAO,KAAK,CAAC,gBAAgB,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;AACzC,CAAC;AAED,8CAA8C;AAC9C,MAAM,UAAU,cAAc,CAAC,KAAkB,EAAE,GAAW;IAC5D,MAAM,GAAG,GAAa,EAAE,CAAC;IACzB,KAAK,MAAM,CAAC,IAAI,EAAE,UAAU,CAAC,IAAI,KAAK,CAAC,MAAM,EAAE,CAAC;QAC9C,KAAK,MAAM,IAAI,IAAI,UAAU,EAAE,CAAC;YAC9B,IAAI,aAAa,CAAC,IAAI,CAAC,KAAK,GAAG,EAAE,CAAC;gBAChC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;gBACf,MAAM;YACR,CAAC;QACH,CAAC;IACH,CAAC;IACD,OAAO,GAAG,CAAC,IAAI,EAAE,CAAC;AACpB,CAAC"}
@@ -4,6 +4,7 @@ import { check as testCheck } from '../analyzers/testCheck.js';
4
4
  import { check as architectureCheck } from '../analyzers/architectureCheck.js';
5
5
  import { check as dependencyRiskCheck } from '../analyzers/dependencyRiskCheck.js';
6
6
  import { check as securityCheck } from '../analyzers/securityCheck.js';
7
+ import { check as unusedDependencyCheck } from '../analyzers/unusedDependencyCheck.js';
7
8
  const checkers = [
8
9
  eslintCheck,
9
10
  prettierCheck,
@@ -11,6 +12,7 @@ const checkers = [
11
12
  architectureCheck,
12
13
  dependencyRiskCheck,
13
14
  securityCheck,
15
+ unusedDependencyCheck,
14
16
  ];
15
17
  export async function collectIssues(rootPath, files) {
16
18
  const results = await Promise.all(checkers.map((check) => check(rootPath, files)));
@@ -1 +1 @@
1
- {"version":3,"file":"issueEngine.js","sourceRoot":"","sources":["../../src/core/issueEngine.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,IAAI,WAAW,EAAE,MAAM,6BAA6B,CAAC;AACnE,OAAO,EAAE,KAAK,IAAI,aAAa,EAAE,MAAM,+BAA+B,CAAC;AACvE,OAAO,EAAE,KAAK,IAAI,SAAS,EAAE,MAAM,2BAA2B,CAAC;AAC/D,OAAO,EAAE,KAAK,IAAI,iBAAiB,EAAE,MAAM,mCAAmC,CAAC;AAC/E,OAAO,EAAE,KAAK,IAAI,mBAAmB,EAAE,MAAM,qCAAqC,CAAC;AACnF,OAAO,EAAE,KAAK,IAAI,aAAa,EAAE,MAAM,+BAA+B,CAAC;AAIvE,MAAM,QAAQ,GAAc;IAC1B,WAAW;IACX,aAAa;IACb,SAAS;IACT,iBAAiB;IACjB,mBAAmB;IACnB,aAAa;CACd,CAAC;AAEF,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,QAAgB,EAAE,KAAkB;IACtE,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC;IACnF,MAAM,MAAM,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC;IAE9B,2CAA2C;IAC3C,MAAM,aAAa,GAA2B,EAAE,KAAK,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC;IAChF,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAE3F,OAAO,MAAM,CAAC;AAChB,CAAC"}
1
+ {"version":3,"file":"issueEngine.js","sourceRoot":"","sources":["../../src/core/issueEngine.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,IAAI,WAAW,EAAE,MAAM,6BAA6B,CAAC;AACnE,OAAO,EAAE,KAAK,IAAI,aAAa,EAAE,MAAM,+BAA+B,CAAC;AACvE,OAAO,EAAE,KAAK,IAAI,SAAS,EAAE,MAAM,2BAA2B,CAAC;AAC/D,OAAO,EAAE,KAAK,IAAI,iBAAiB,EAAE,MAAM,mCAAmC,CAAC;AAC/E,OAAO,EAAE,KAAK,IAAI,mBAAmB,EAAE,MAAM,qCAAqC,CAAC;AACnF,OAAO,EAAE,KAAK,IAAI,aAAa,EAAE,MAAM,+BAA+B,CAAC;AACvE,OAAO,EAAE,KAAK,IAAI,qBAAqB,EAAE,MAAM,uCAAuC,CAAC;AAIvF,MAAM,QAAQ,GAAc;IAC1B,WAAW;IACX,aAAa;IACb,SAAS;IACT,iBAAiB;IACjB,mBAAmB;IACnB,aAAa;IACb,qBAAqB;CACtB,CAAC;AAEF,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,QAAgB,EAAE,KAAkB;IACtE,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC;IACnF,MAAM,MAAM,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC;IAE9B,2CAA2C;IAC3C,MAAM,aAAa,GAA2B,EAAE,KAAK,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC;IAChF,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAE3F,OAAO,MAAM,CAAC;AAChB,CAAC"}
@@ -0,0 +1,9 @@
1
+ import type { OutdatedReport } from '../types.js';
2
+ /**
3
+ * Offline outdated check — compares the version declared in package.json
4
+ * to the version installed under node_modules/<pkg>/package.json.
5
+ *
6
+ * Does not hit the npm registry. `latest` is filled in only when a node_modules
7
+ * install exists; the drift calculation uses installed vs declared.
8
+ */
9
+ export declare function detectOutdated(rootPath: string): Promise<OutdatedReport>;
@@ -0,0 +1,87 @@
1
+ import fs from 'node:fs/promises';
2
+ import path from 'node:path';
3
+ import { drift as semverDrift } from '../utils/semver.js';
4
+ /**
5
+ * Offline outdated check — compares the version declared in package.json
6
+ * to the version installed under node_modules/<pkg>/package.json.
7
+ *
8
+ * Does not hit the npm registry. `latest` is filled in only when a node_modules
9
+ * install exists; the drift calculation uses installed vs declared.
10
+ */
11
+ export async function detectOutdated(rootPath) {
12
+ const pkgPath = path.join(rootPath, 'package.json');
13
+ let raw;
14
+ try {
15
+ raw = await fs.readFile(pkgPath, 'utf-8');
16
+ }
17
+ catch {
18
+ return {
19
+ available: false,
20
+ reason: 'No package.json found in this directory',
21
+ totalPackages: 0,
22
+ packages: [],
23
+ };
24
+ }
25
+ let pkg;
26
+ try {
27
+ pkg = JSON.parse(raw);
28
+ }
29
+ catch {
30
+ return {
31
+ available: false,
32
+ reason: 'package.json is not valid JSON',
33
+ totalPackages: 0,
34
+ packages: [],
35
+ };
36
+ }
37
+ const dependencies = (pkg.dependencies ?? {});
38
+ const devDependencies = (pkg.devDependencies ?? {});
39
+ const entries = [
40
+ ...Object.entries(dependencies).map(([n, v]) => [n, v, 'dependency']),
41
+ ...Object.entries(devDependencies).map(([n, v]) => [n, v, 'devDependency']),
42
+ ];
43
+ const nodeModules = path.join(rootPath, 'node_modules');
44
+ const nodeModulesExists = await pathExists(nodeModules);
45
+ const packages = [];
46
+ for (const [name, declared, scope] of entries) {
47
+ let installed = null;
48
+ if (nodeModulesExists) {
49
+ installed = await readInstalledVersion(nodeModules, name);
50
+ }
51
+ const drift = semverDrift(declared, installed);
52
+ packages.push({
53
+ name,
54
+ declared,
55
+ installed,
56
+ latest: installed, // without registry lookup, installed is the best we know
57
+ drift,
58
+ scope,
59
+ });
60
+ }
61
+ return {
62
+ available: true,
63
+ totalPackages: packages.length,
64
+ packages,
65
+ };
66
+ }
67
+ async function pathExists(p) {
68
+ try {
69
+ await fs.access(p);
70
+ return true;
71
+ }
72
+ catch {
73
+ return false;
74
+ }
75
+ }
76
+ async function readInstalledVersion(nodeModules, name) {
77
+ const installedPath = path.join(nodeModules, name, 'package.json');
78
+ try {
79
+ const raw = await fs.readFile(installedPath, 'utf-8');
80
+ const pkg = JSON.parse(raw);
81
+ return pkg.version ?? null;
82
+ }
83
+ catch {
84
+ return null;
85
+ }
86
+ }
87
+ //# sourceMappingURL=outdatedDetector.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"outdatedDetector.js","sourceRoot":"","sources":["../../src/core/outdatedDetector.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import type { FileEntry, UpgradePreview } from '../types.js';
2
+ export declare function previewUpgrade(rootPath: string, pkgName: string, files: FileEntry[]): Promise<UpgradePreview>;