@vpxa/kb 0.1.1 → 0.1.2
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/package.json +1 -1
- package/packages/analyzers/dist/blast-radius-analyzer.js +13 -114
- package/packages/analyzers/dist/dependency-analyzer.js +11 -425
- package/packages/analyzers/dist/diagram-generator.js +4 -86
- package/packages/analyzers/dist/entry-point-analyzer.js +5 -239
- package/packages/analyzers/dist/index.js +1 -23
- package/packages/analyzers/dist/knowledge-producer.js +24 -113
- package/packages/analyzers/dist/pattern-analyzer.js +5 -359
- package/packages/analyzers/dist/regex-call-graph.js +1 -428
- package/packages/analyzers/dist/structure-analyzer.js +4 -258
- package/packages/analyzers/dist/symbol-analyzer.js +13 -442
- package/packages/analyzers/dist/ts-call-graph.js +1 -160
- package/packages/analyzers/dist/types.js +0 -1
- package/packages/chunker/dist/call-graph-extractor.js +1 -90
- package/packages/chunker/dist/chunker-factory.js +1 -36
- package/packages/chunker/dist/chunker.interface.js +0 -1
- package/packages/chunker/dist/code-chunker.js +14 -134
- package/packages/chunker/dist/generic-chunker.js +5 -72
- package/packages/chunker/dist/index.js +1 -21
- package/packages/chunker/dist/markdown-chunker.js +7 -119
- package/packages/chunker/dist/treesitter-chunker.js +8 -234
- package/packages/cli/dist/commands/analyze.js +3 -112
- package/packages/cli/dist/commands/context-cmds.js +1 -155
- package/packages/cli/dist/commands/environment.js +2 -204
- package/packages/cli/dist/commands/execution.js +1 -137
- package/packages/cli/dist/commands/graph.js +7 -81
- package/packages/cli/dist/commands/init.js +9 -87
- package/packages/cli/dist/commands/knowledge.js +1 -139
- package/packages/cli/dist/commands/search.js +8 -267
- package/packages/cli/dist/commands/system.js +4 -241
- package/packages/cli/dist/commands/workspace.js +2 -388
- package/packages/cli/dist/context.js +1 -14
- package/packages/cli/dist/helpers.js +3 -458
- package/packages/cli/dist/index.js +3 -69
- package/packages/cli/dist/kb-init.js +1 -82
- package/packages/cli/dist/types.js +0 -1
- package/packages/core/dist/constants.js +1 -43
- package/packages/core/dist/content-detector.js +1 -79
- package/packages/core/dist/errors.js +1 -40
- package/packages/core/dist/index.js +1 -9
- package/packages/core/dist/logger.js +1 -34
- package/packages/core/dist/types.js +0 -1
- package/packages/embeddings/dist/embedder.interface.js +0 -1
- package/packages/embeddings/dist/index.js +1 -5
- package/packages/embeddings/dist/onnx-embedder.js +1 -82
- package/packages/indexer/dist/file-hasher.js +1 -13
- package/packages/indexer/dist/filesystem-crawler.js +1 -125
- package/packages/indexer/dist/graph-extractor.js +1 -111
- package/packages/indexer/dist/incremental-indexer.js +1 -278
- package/packages/indexer/dist/index.js +1 -14
- package/packages/server/dist/api.js +1 -9
- package/packages/server/dist/config.js +1 -75
- package/packages/server/dist/curated-manager.js +9 -356
- package/packages/server/dist/index.js +1 -134
- package/packages/server/dist/replay-interceptor.js +1 -38
- package/packages/server/dist/resources/resources.js +2 -40
- package/packages/server/dist/server.js +1 -247
- package/packages/server/dist/tools/analyze.tools.js +1 -288
- package/packages/server/dist/tools/forge.tools.js +11 -499
- package/packages/server/dist/tools/forget.tool.js +3 -39
- package/packages/server/dist/tools/graph.tool.js +5 -110
- package/packages/server/dist/tools/list.tool.js +5 -53
- package/packages/server/dist/tools/lookup.tool.js +8 -51
- package/packages/server/dist/tools/onboard.tool.js +2 -112
- package/packages/server/dist/tools/produce.tool.js +4 -74
- package/packages/server/dist/tools/read.tool.js +4 -47
- package/packages/server/dist/tools/reindex.tool.js +2 -70
- package/packages/server/dist/tools/remember.tool.js +3 -42
- package/packages/server/dist/tools/replay.tool.js +6 -88
- package/packages/server/dist/tools/search.tool.js +17 -327
- package/packages/server/dist/tools/status.tool.js +3 -68
- package/packages/server/dist/tools/toolkit.tools.js +20 -1673
- package/packages/server/dist/tools/update.tool.js +3 -39
- package/packages/server/dist/tools/utility.tools.js +19 -456
- package/packages/store/dist/graph-store.interface.js +0 -1
- package/packages/store/dist/index.js +1 -9
- package/packages/store/dist/lance-store.js +1 -258
- package/packages/store/dist/sqlite-graph-store.js +8 -309
- package/packages/store/dist/store-factory.js +1 -14
- package/packages/store/dist/store.interface.js +0 -1
- package/packages/tools/dist/batch.js +1 -45
- package/packages/tools/dist/changelog.js +2 -112
- package/packages/tools/dist/check.js +2 -59
- package/packages/tools/dist/checkpoint.js +2 -43
- package/packages/tools/dist/codemod.js +2 -69
- package/packages/tools/dist/compact.js +3 -60
- package/packages/tools/dist/data-transform.js +1 -124
- package/packages/tools/dist/dead-symbols.js +2 -71
- package/packages/tools/dist/delegate.js +3 -128
- package/packages/tools/dist/diff-parse.js +3 -153
- package/packages/tools/dist/digest.js +7 -242
- package/packages/tools/dist/encode.js +1 -46
- package/packages/tools/dist/env-info.js +1 -58
- package/packages/tools/dist/eval.js +3 -79
- package/packages/tools/dist/evidence-map.js +3 -203
- package/packages/tools/dist/file-summary.js +2 -106
- package/packages/tools/dist/file-walk.js +1 -75
- package/packages/tools/dist/find-examples.js +3 -48
- package/packages/tools/dist/find.js +1 -120
- package/packages/tools/dist/forge-classify.js +2 -319
- package/packages/tools/dist/forge-ground.js +1 -184
- package/packages/tools/dist/git-context.js +3 -46
- package/packages/tools/dist/graph-query.js +1 -194
- package/packages/tools/dist/health.js +1 -118
- package/packages/tools/dist/http-request.js +1 -58
- package/packages/tools/dist/index.js +1 -273
- package/packages/tools/dist/lane.js +7 -227
- package/packages/tools/dist/measure.js +2 -119
- package/packages/tools/dist/onboard.js +42 -1136
- package/packages/tools/dist/parse-output.js +2 -158
- package/packages/tools/dist/process-manager.js +1 -69
- package/packages/tools/dist/queue.js +2 -126
- package/packages/tools/dist/regex-test.js +1 -39
- package/packages/tools/dist/rename.js +2 -70
- package/packages/tools/dist/replay.js +6 -108
- package/packages/tools/dist/schema-validate.js +1 -141
- package/packages/tools/dist/scope-map.js +1 -72
- package/packages/tools/dist/snippet.js +1 -80
- package/packages/tools/dist/stash.js +2 -60
- package/packages/tools/dist/stratum-card.js +5 -238
- package/packages/tools/dist/symbol.js +3 -87
- package/packages/tools/dist/test-run.js +2 -55
- package/packages/tools/dist/text-utils.js +2 -31
- package/packages/tools/dist/time-utils.js +1 -135
- package/packages/tools/dist/trace.js +2 -114
- package/packages/tools/dist/truncation.js +10 -41
- package/packages/tools/dist/watch.js +1 -61
- package/packages/tools/dist/web-fetch.js +9 -244
- package/packages/tools/dist/web-search.js +1 -46
- package/packages/tools/dist/workset.js +2 -77
- package/packages/tui/dist/App.js +260 -52468
- package/packages/tui/dist/index.js +286 -54551
- package/packages/tui/dist/panels/CuratedPanel.js +211 -34291
- package/packages/tui/dist/panels/LogPanel.js +259 -51703
- package/packages/tui/dist/panels/SearchPanel.js +212 -34824
- package/packages/tui/dist/panels/StatusPanel.js +211 -34304
package/package.json
CHANGED
|
@@ -1,114 +1,13 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
for (const file of files) {
|
|
15
|
-
affected.set(file, { path: file, reason: "changed", depth: 0 });
|
|
16
|
-
const norm = file.replace(/\.[jt]sx?$/, "");
|
|
17
|
-
queue.push({ path: norm, depth: 0 });
|
|
18
|
-
}
|
|
19
|
-
while (queue.length > 0) {
|
|
20
|
-
const item = queue.shift();
|
|
21
|
-
if (!item) break;
|
|
22
|
-
const { path, depth } = item;
|
|
23
|
-
if (depth >= maxDepth) continue;
|
|
24
|
-
const importers = reverseGraph[path] ?? [];
|
|
25
|
-
for (const importer of importers) {
|
|
26
|
-
if (affected.has(importer)) continue;
|
|
27
|
-
const reason = depth === 0 ? "direct-importer" : "transitive-importer";
|
|
28
|
-
affected.set(importer, { path: importer, reason, depth: depth + 1 });
|
|
29
|
-
const importerNorm = importer.replace(/\.[jt]sx?$/, "");
|
|
30
|
-
queue.push({ path: importerNorm, depth: depth + 1 });
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
const affectedTests = /* @__PURE__ */ new Set();
|
|
34
|
-
for (const [filePath] of affected) {
|
|
35
|
-
const norm = filePath.replace(/\.[jt]sx?$/, "");
|
|
36
|
-
const tests = testCoverage[norm] ?? [];
|
|
37
|
-
for (const test of tests) {
|
|
38
|
-
if (!affected.has(test)) {
|
|
39
|
-
affectedTests.add(test);
|
|
40
|
-
affected.set(test, { path: test, reason: "test", depth: 999 });
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
const affectedList = [...affected.values()].sort((a, b) => a.depth - b.depth);
|
|
45
|
-
const output = format === "json" ? JSON.stringify({ changedFiles: files, affected: affectedList }, null, 2) : this.formatMarkdown(files, affectedList, reverseGraph);
|
|
46
|
-
return {
|
|
47
|
-
output,
|
|
48
|
-
data: { changedFiles: files, affected: affectedList },
|
|
49
|
-
meta: {
|
|
50
|
-
analyzedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
51
|
-
scope: rootPath,
|
|
52
|
-
fileCount: affectedList.length,
|
|
53
|
-
durationMs: Date.now() - startTime
|
|
54
|
-
}
|
|
55
|
-
};
|
|
56
|
-
}
|
|
57
|
-
formatMarkdown(changedFiles, affected, _reverseGraph) {
|
|
58
|
-
const lines = [];
|
|
59
|
-
lines.push("## Blast Radius Analysis\n");
|
|
60
|
-
lines.push(`**Changed files:** ${changedFiles.length}`);
|
|
61
|
-
lines.push(`**Total affected:** ${affected.length}
|
|
62
|
-
`);
|
|
63
|
-
const changed = affected.filter((a) => a.reason === "changed");
|
|
64
|
-
if (changed.length > 0) {
|
|
65
|
-
lines.push("### Changed Files\n");
|
|
66
|
-
for (const f of changed) {
|
|
67
|
-
lines.push(`- \`${f.path}\``);
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
const direct = affected.filter((a) => a.reason === "direct-importer");
|
|
71
|
-
if (direct.length > 0) {
|
|
72
|
-
lines.push(`
|
|
73
|
-
### Direct Importers (${direct.length} files)
|
|
74
|
-
`);
|
|
75
|
-
for (const f of direct) {
|
|
76
|
-
lines.push(`- \`${f.path}\``);
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
const transitive = affected.filter((a) => a.reason === "transitive-importer");
|
|
80
|
-
if (transitive.length > 0) {
|
|
81
|
-
lines.push(`
|
|
82
|
-
### Transitive Importers (${transitive.length} files)
|
|
83
|
-
`);
|
|
84
|
-
for (const f of transitive.slice(0, 20)) {
|
|
85
|
-
lines.push(`- \`${f.path}\` (depth ${f.depth})`);
|
|
86
|
-
}
|
|
87
|
-
if (transitive.length > 20) {
|
|
88
|
-
lines.push(`- ... and ${transitive.length - 20} more`);
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
const tests = affected.filter((a) => a.reason === "test");
|
|
92
|
-
if (tests.length > 0) {
|
|
93
|
-
lines.push(`
|
|
94
|
-
### Affected Tests (${tests.length} files)
|
|
95
|
-
`);
|
|
96
|
-
for (const f of tests) {
|
|
97
|
-
lines.push(`- \`${f.path}\``);
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
lines.push("\n### Review Summary\n");
|
|
101
|
-
lines.push(`| Category | Count |`);
|
|
102
|
-
lines.push(`|----------|-------|`);
|
|
103
|
-
lines.push(`| Changed | ${changed.length} |`);
|
|
104
|
-
lines.push(`| Direct importers | ${direct.length} |`);
|
|
105
|
-
lines.push(`| Transitive importers | ${transitive.length} |`);
|
|
106
|
-
lines.push(`| Affected tests | ${tests.length} |`);
|
|
107
|
-
lines.push(`| **Total review scope** | **${affected.length}** |`);
|
|
108
|
-
return lines.join("\n");
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
export {
|
|
112
|
-
BlastRadiusAnalyzer
|
|
113
|
-
};
|
|
114
|
-
//# sourceMappingURL=blast-radius-analyzer.js.map
|
|
1
|
+
import{DependencyAnalyzer as A}from"./dependency-analyzer.js";class j{name="blast-radius";depAnalyzer=new A;async analyze(d,n){const{files:c,maxDepth:t=5,format:f="markdown"}=n,p=Date.now(),o=(await this.depAnalyzer.analyze(d,{format:"json"})).data,e=o.reverseGraph??{},y=o.testCoverage??{},a=new Map,g=[];for(const s of c){a.set(s,{path:s,reason:"changed",depth:0});const i=s.replace(/\.[jt]sx?$/,"");g.push({path:i,depth:0})}for(;g.length>0;){const s=g.shift();if(!s)break;const{path:i,depth:h}=s;if(h>=t)continue;const l=e[i]??[];for(const m of l){if(a.has(m))continue;const v=h===0?"direct-importer":"transitive-importer";a.set(m,{path:m,reason:v,depth:h+1});const w=m.replace(/\.[jt]sx?$/,"");g.push({path:w,depth:h+1})}}const $=new Set;for(const[s]of a){const i=s.replace(/\.[jt]sx?$/,""),h=y[i]??[];for(const l of h)a.has(l)||($.add(l),a.set(l,{path:l,reason:"test",depth:999}))}const u=[...a.values()].sort((s,i)=>s.depth-i.depth);return{output:f==="json"?JSON.stringify({changedFiles:c,affected:u},null,2):this.formatMarkdown(c,u,e),data:{changedFiles:c,affected:u},meta:{analyzedAt:new Date().toISOString(),scope:d,fileCount:u.length,durationMs:Date.now()-p}}}formatMarkdown(d,n,c){const t=[];t.push(`## Blast Radius Analysis
|
|
2
|
+
`),t.push(`**Changed files:** ${d.length}`),t.push(`**Total affected:** ${n.length}
|
|
3
|
+
`);const f=n.filter(e=>e.reason==="changed");if(f.length>0){t.push(`### Changed Files
|
|
4
|
+
`);for(const e of f)t.push(`- \`${e.path}\``)}const p=n.filter(e=>e.reason==="direct-importer");if(p.length>0){t.push(`
|
|
5
|
+
### Direct Importers (${p.length} files)
|
|
6
|
+
`);for(const e of p)t.push(`- \`${e.path}\``)}const r=n.filter(e=>e.reason==="transitive-importer");if(r.length>0){t.push(`
|
|
7
|
+
### Transitive Importers (${r.length} files)
|
|
8
|
+
`);for(const e of r.slice(0,20))t.push(`- \`${e.path}\` (depth ${e.depth})`);r.length>20&&t.push(`- ... and ${r.length-20} more`)}const o=n.filter(e=>e.reason==="test");if(o.length>0){t.push(`
|
|
9
|
+
### Affected Tests (${o.length} files)
|
|
10
|
+
`);for(const e of o)t.push(`- \`${e.path}\``)}return t.push(`
|
|
11
|
+
### Review Summary
|
|
12
|
+
`),t.push("| Category | Count |"),t.push("|----------|-------|"),t.push(`| Changed | ${f.length} |`),t.push(`| Direct importers | ${p.length} |`),t.push(`| Transitive importers | ${r.length} |`),t.push(`| Affected tests | ${o.length} |`),t.push(`| **Total review scope** | **${n.length}** |`),t.join(`
|
|
13
|
+
`)}}export{j as BlastRadiusAnalyzer};
|
|
@@ -1,425 +1,11 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
".go",
|
|
13
|
-
".cs",
|
|
14
|
-
".kt",
|
|
15
|
-
".scala",
|
|
16
|
-
".rb",
|
|
17
|
-
".rs",
|
|
18
|
-
".php",
|
|
19
|
-
".swift"
|
|
20
|
-
]);
|
|
21
|
-
const IMPORT_PATTERNS = [
|
|
22
|
-
// ── JS/TS ──
|
|
23
|
-
// ES import: import { x } from 'module' / import x from 'module'
|
|
24
|
-
{
|
|
25
|
-
regex: /import\s+(?:(?:type\s+)?(?:(?:\{[^}]*\}|[\w*]+)\s+from\s+)?)['"]([^'"]+)['"]/g,
|
|
26
|
-
confidence: "high"
|
|
27
|
-
},
|
|
28
|
-
// Dynamic import: import('module')
|
|
29
|
-
{
|
|
30
|
-
regex: /import\(\s*['"]([^'"]+)['"]\s*\)/g,
|
|
31
|
-
confidence: "medium"
|
|
32
|
-
},
|
|
33
|
-
// CommonJS require: require('module')
|
|
34
|
-
{
|
|
35
|
-
regex: /require\(\s*['"]([^'"]+)['"]\s*\)/g,
|
|
36
|
-
confidence: "medium"
|
|
37
|
-
},
|
|
38
|
-
// ── Python ──
|
|
39
|
-
// from package import module
|
|
40
|
-
{
|
|
41
|
-
regex: /^from\s+([\w.]+)\s+import\b/gm,
|
|
42
|
-
confidence: "high",
|
|
43
|
-
lang: "python"
|
|
44
|
-
},
|
|
45
|
-
// import package
|
|
46
|
-
{
|
|
47
|
-
regex: /^import\s+([\w.]+)\s*$/gm,
|
|
48
|
-
confidence: "high",
|
|
49
|
-
lang: "python"
|
|
50
|
-
},
|
|
51
|
-
// ── Java / Kotlin / Scala ──
|
|
52
|
-
// import com.example.Class;
|
|
53
|
-
{
|
|
54
|
-
regex: /^import\s+(?:static\s+)?([\w.]+(?:\.\*)?)\s*;/gm,
|
|
55
|
-
confidence: "high",
|
|
56
|
-
lang: "java"
|
|
57
|
-
},
|
|
58
|
-
// ── Go ──
|
|
59
|
-
// import "package" or "package" inside import block
|
|
60
|
-
{
|
|
61
|
-
regex: /(?:^import\s+|^\s+)[""]([^""]+)[""]/gm,
|
|
62
|
-
confidence: "high",
|
|
63
|
-
lang: "go"
|
|
64
|
-
},
|
|
65
|
-
// ── C# ──
|
|
66
|
-
// using Namespace.Something;
|
|
67
|
-
{
|
|
68
|
-
regex: /^using\s+(?:static\s+)?([\w.]+)\s*;/gm,
|
|
69
|
-
confidence: "high",
|
|
70
|
-
lang: "csharp"
|
|
71
|
-
},
|
|
72
|
-
// ── Rust ──
|
|
73
|
-
// use crate::module; use std::io;
|
|
74
|
-
{
|
|
75
|
-
regex: /^use\s+([\w:]+(?:::\w+)*)/gm,
|
|
76
|
-
confidence: "high",
|
|
77
|
-
lang: "rust"
|
|
78
|
-
},
|
|
79
|
-
// ── PHP ──
|
|
80
|
-
// use Namespace\Class;
|
|
81
|
-
{
|
|
82
|
-
regex: /^use\s+([\w\\]+)\s*;/gm,
|
|
83
|
-
confidence: "high",
|
|
84
|
-
lang: "php"
|
|
85
|
-
},
|
|
86
|
-
// ── Ruby ──
|
|
87
|
-
// require 'gem' or require_relative 'file'
|
|
88
|
-
{
|
|
89
|
-
regex: /require(?:_relative)?\s+['"]([^'"]+)['"]/g,
|
|
90
|
-
confidence: "medium",
|
|
91
|
-
lang: "ruby"
|
|
92
|
-
},
|
|
93
|
-
// ── Swift ──
|
|
94
|
-
// import Foundation
|
|
95
|
-
{
|
|
96
|
-
regex: /^import\s+(\w+)\s*$/gm,
|
|
97
|
-
confidence: "high",
|
|
98
|
-
lang: "swift"
|
|
99
|
-
}
|
|
100
|
-
];
|
|
101
|
-
const DEFAULT_EXCLUDES = /* @__PURE__ */ new Set([
|
|
102
|
-
"node_modules",
|
|
103
|
-
".git",
|
|
104
|
-
"dist",
|
|
105
|
-
"build",
|
|
106
|
-
"coverage",
|
|
107
|
-
".turbo",
|
|
108
|
-
".cache",
|
|
109
|
-
"cdk.out"
|
|
110
|
-
]);
|
|
111
|
-
const TEST_PATTERNS = [/\.(test|spec)\.[jt]sx?$/, /\/__tests__\//, /\/test\//, /\/tests\//];
|
|
112
|
-
const EXT_TO_LANG = {
|
|
113
|
-
".ts": "js",
|
|
114
|
-
".tsx": "js",
|
|
115
|
-
".js": "js",
|
|
116
|
-
".jsx": "js",
|
|
117
|
-
".mjs": "js",
|
|
118
|
-
".cjs": "js",
|
|
119
|
-
".py": "python",
|
|
120
|
-
".java": "java",
|
|
121
|
-
".kt": "java",
|
|
122
|
-
".scala": "java",
|
|
123
|
-
".go": "go",
|
|
124
|
-
".cs": "csharp",
|
|
125
|
-
".rs": "rust",
|
|
126
|
-
".php": "php",
|
|
127
|
-
".rb": "ruby",
|
|
128
|
-
".swift": "swift"
|
|
129
|
-
};
|
|
130
|
-
function classifyExternal(source, lang) {
|
|
131
|
-
if (!lang || lang === "js") {
|
|
132
|
-
return !source.startsWith(".") && !source.startsWith("/");
|
|
133
|
-
}
|
|
134
|
-
if (lang === "python") {
|
|
135
|
-
return !source.startsWith(".");
|
|
136
|
-
}
|
|
137
|
-
if (lang === "java") {
|
|
138
|
-
return !source.startsWith("com.") || source.startsWith("com.amazonaws") || source.startsWith("com.google") || source.startsWith("com.fasterxml");
|
|
139
|
-
}
|
|
140
|
-
if (lang === "go") {
|
|
141
|
-
return source.includes(".") && !source.startsWith(".");
|
|
142
|
-
}
|
|
143
|
-
if (lang === "csharp") {
|
|
144
|
-
return source.startsWith("System") || source.startsWith("Microsoft") || source.startsWith("Newtonsoft") || source.startsWith("Amazon");
|
|
145
|
-
}
|
|
146
|
-
if (lang === "rust") {
|
|
147
|
-
return !source.startsWith("crate::") && !source.startsWith("self::") && !source.startsWith("super::");
|
|
148
|
-
}
|
|
149
|
-
return true;
|
|
150
|
-
}
|
|
151
|
-
function isTestFile(filePath) {
|
|
152
|
-
return TEST_PATTERNS.some((p) => p.test(filePath));
|
|
153
|
-
}
|
|
154
|
-
class DependencyAnalyzer {
|
|
155
|
-
name = "dependencies";
|
|
156
|
-
/** Map of workspace package names to their relative entry point paths */
|
|
157
|
-
workspacePackages = /* @__PURE__ */ new Map();
|
|
158
|
-
async analyze(rootPath, options = {}) {
|
|
159
|
-
const { format = "markdown" } = options;
|
|
160
|
-
const startTime = Date.now();
|
|
161
|
-
this.workspacePackages = await this.buildWorkspaceMap(rootPath);
|
|
162
|
-
const files = await this.collectFiles(rootPath);
|
|
163
|
-
const imports = [];
|
|
164
|
-
for (const filePath of files) {
|
|
165
|
-
const content = await readFile(filePath, "utf-8");
|
|
166
|
-
const fileImports = this.extractImports(content, filePath, rootPath);
|
|
167
|
-
imports.push(...fileImports);
|
|
168
|
-
}
|
|
169
|
-
const external = this.groupExternalDeps(imports);
|
|
170
|
-
const internal = this.groupInternalDeps(imports, rootPath);
|
|
171
|
-
const reverseGraph = this.buildReverseGraph(imports, rootPath);
|
|
172
|
-
const testCoverage = this.buildTestCoverage(imports, rootPath);
|
|
173
|
-
const output = format === "json" ? JSON.stringify({ external, internal, reverseGraph, testCoverage }, null, 2) : format === "mermaid" ? this.formatMermaid(internal) : this.formatMarkdown(external, internal, rootPath, testCoverage);
|
|
174
|
-
return {
|
|
175
|
-
output,
|
|
176
|
-
data: { external, internal, reverseGraph, testCoverage, totalImports: imports.length },
|
|
177
|
-
meta: {
|
|
178
|
-
analyzedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
179
|
-
scope: rootPath,
|
|
180
|
-
fileCount: files.length,
|
|
181
|
-
durationMs: Date.now() - startTime
|
|
182
|
-
}
|
|
183
|
-
};
|
|
184
|
-
}
|
|
185
|
-
async collectFiles(dirPath) {
|
|
186
|
-
const files = [];
|
|
187
|
-
const walk = async (dir) => {
|
|
188
|
-
const entries = await readdir(dir, { withFileTypes: true });
|
|
189
|
-
for (const entry of entries) {
|
|
190
|
-
if (DEFAULT_EXCLUDES.has(entry.name)) continue;
|
|
191
|
-
if (entry.name.startsWith(".")) continue;
|
|
192
|
-
const fullPath = join(dir, entry.name);
|
|
193
|
-
if (entry.isDirectory()) {
|
|
194
|
-
await walk(fullPath);
|
|
195
|
-
} else if (CODE_EXTENSIONS.has(extname(entry.name))) {
|
|
196
|
-
files.push(fullPath);
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
};
|
|
200
|
-
await walk(dirPath);
|
|
201
|
-
return files;
|
|
202
|
-
}
|
|
203
|
-
extractImports(content, filePath, rootPath) {
|
|
204
|
-
const results = [];
|
|
205
|
-
const ext = extname(filePath).toLowerCase();
|
|
206
|
-
const fileLang = EXT_TO_LANG[ext];
|
|
207
|
-
for (const pattern of IMPORT_PATTERNS) {
|
|
208
|
-
if (pattern.lang && pattern.lang !== fileLang) continue;
|
|
209
|
-
if (!pattern.lang && fileLang && fileLang !== "js") continue;
|
|
210
|
-
const regex = new RegExp(pattern.regex.source, pattern.regex.flags);
|
|
211
|
-
let match;
|
|
212
|
-
while ((match = regex.exec(content)) !== null) {
|
|
213
|
-
const source = match[1];
|
|
214
|
-
const isExternal = classifyExternal(source, fileLang);
|
|
215
|
-
results.push({
|
|
216
|
-
source,
|
|
217
|
-
specifiers: [],
|
|
218
|
-
filePath: relative(rootPath, filePath).replace(/\\/g, "/"),
|
|
219
|
-
isExternal,
|
|
220
|
-
confidence: pattern.confidence
|
|
221
|
-
});
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
return results;
|
|
225
|
-
}
|
|
226
|
-
groupExternalDeps(imports) {
|
|
227
|
-
const deps = {};
|
|
228
|
-
for (const imp of imports) {
|
|
229
|
-
if (!imp.isExternal) continue;
|
|
230
|
-
const ext = extname(imp.filePath).toLowerCase();
|
|
231
|
-
const lang = EXT_TO_LANG[ext];
|
|
232
|
-
let pkg;
|
|
233
|
-
if (lang === "java") {
|
|
234
|
-
const parts = imp.source.split(".");
|
|
235
|
-
while (parts.length > 1) {
|
|
236
|
-
const last = parts[parts.length - 1];
|
|
237
|
-
if (last === "*" || /^[A-Z]/.test(last)) {
|
|
238
|
-
parts.pop();
|
|
239
|
-
} else {
|
|
240
|
-
break;
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
|
-
if (parts.length >= 2) {
|
|
244
|
-
pkg = parts.slice(0, 2).join(".");
|
|
245
|
-
} else {
|
|
246
|
-
pkg = parts.join(".");
|
|
247
|
-
}
|
|
248
|
-
} else if (lang === "python") {
|
|
249
|
-
pkg = imp.source.split(".")[0];
|
|
250
|
-
} else if (lang === "go") {
|
|
251
|
-
pkg = imp.source;
|
|
252
|
-
} else if (lang === "csharp") {
|
|
253
|
-
const parts = imp.source.split(".");
|
|
254
|
-
pkg = parts.length >= 2 ? parts.slice(0, 2).join(".") : imp.source;
|
|
255
|
-
} else {
|
|
256
|
-
pkg = imp.source.startsWith("@") ? imp.source.split("/").slice(0, 2).join("/") : imp.source.split("/")[0];
|
|
257
|
-
}
|
|
258
|
-
if (!deps[pkg]) deps[pkg] = { count: 0, confidence: imp.confidence, usedBy: /* @__PURE__ */ new Set() };
|
|
259
|
-
deps[pkg].count++;
|
|
260
|
-
deps[pkg].usedBy.add(imp.filePath);
|
|
261
|
-
if (imp.confidence === "high") deps[pkg].confidence = "high";
|
|
262
|
-
else if (imp.confidence === "medium" && deps[pkg].confidence === "low")
|
|
263
|
-
deps[pkg].confidence = "medium";
|
|
264
|
-
}
|
|
265
|
-
const result = {};
|
|
266
|
-
for (const [pkg, data] of Object.entries(deps)) {
|
|
267
|
-
result[pkg] = { count: data.count, confidence: data.confidence, usedBy: [...data.usedBy] };
|
|
268
|
-
}
|
|
269
|
-
return result;
|
|
270
|
-
}
|
|
271
|
-
groupInternalDeps(imports, _rootPath) {
|
|
272
|
-
const deps = {};
|
|
273
|
-
for (const imp of imports) {
|
|
274
|
-
if (imp.isExternal) continue;
|
|
275
|
-
if (!deps[imp.filePath]) deps[imp.filePath] = /* @__PURE__ */ new Set();
|
|
276
|
-
deps[imp.filePath].add(imp.source);
|
|
277
|
-
}
|
|
278
|
-
const result = {};
|
|
279
|
-
for (const [file, sources] of Object.entries(deps)) {
|
|
280
|
-
result[file] = [...sources];
|
|
281
|
-
}
|
|
282
|
-
return result;
|
|
283
|
-
}
|
|
284
|
-
/**
|
|
285
|
-
* Build reverse graph: for each source file, who imports it?
|
|
286
|
-
*/
|
|
287
|
-
buildReverseGraph(imports, _rootPath) {
|
|
288
|
-
const reverse = {};
|
|
289
|
-
for (const imp of imports) {
|
|
290
|
-
const importerDir = dirname(imp.filePath);
|
|
291
|
-
const resolved = this.resolveImportPath(imp.source, importerDir);
|
|
292
|
-
if (!resolved) continue;
|
|
293
|
-
if (!reverse[resolved]) reverse[resolved] = /* @__PURE__ */ new Set();
|
|
294
|
-
reverse[resolved].add(imp.filePath);
|
|
295
|
-
}
|
|
296
|
-
const result = {};
|
|
297
|
-
for (const [file, importers] of Object.entries(reverse)) {
|
|
298
|
-
result[file] = [...importers];
|
|
299
|
-
}
|
|
300
|
-
return result;
|
|
301
|
-
}
|
|
302
|
-
/**
|
|
303
|
-
* Build test coverage map: for each source file, which test files exercise it?
|
|
304
|
-
*/
|
|
305
|
-
buildTestCoverage(imports, _rootPath) {
|
|
306
|
-
const coverage = {};
|
|
307
|
-
for (const imp of imports) {
|
|
308
|
-
if (!isTestFile(imp.filePath)) continue;
|
|
309
|
-
const importerDir = dirname(imp.filePath);
|
|
310
|
-
const resolved = this.resolveImportPath(imp.source, importerDir);
|
|
311
|
-
if (!resolved || isTestFile(resolved)) continue;
|
|
312
|
-
if (!coverage[resolved]) coverage[resolved] = /* @__PURE__ */ new Set();
|
|
313
|
-
coverage[resolved].add(imp.filePath);
|
|
314
|
-
}
|
|
315
|
-
const result = {};
|
|
316
|
-
for (const [file, tests] of Object.entries(coverage)) {
|
|
317
|
-
result[file] = [...tests];
|
|
318
|
-
}
|
|
319
|
-
return result;
|
|
320
|
-
}
|
|
321
|
-
/** Resolve a relative import path to a normalized file path (best-effort). */
|
|
322
|
-
resolveImportPath(source, fromDir) {
|
|
323
|
-
if (source.startsWith(".")) {
|
|
324
|
-
const resolved = join(fromDir, source).replace(/\\/g, "/");
|
|
325
|
-
return resolved.replace(/\.[jt]sx?$/, "");
|
|
326
|
-
}
|
|
327
|
-
const pkg = source.startsWith("@") ? source.split("/").slice(0, 2).join("/") : source.split("/")[0];
|
|
328
|
-
const entryPath = this.workspacePackages.get(pkg);
|
|
329
|
-
if (entryPath) {
|
|
330
|
-
return entryPath.replace(/\.[jt]sx?$/, "");
|
|
331
|
-
}
|
|
332
|
-
return null;
|
|
333
|
-
}
|
|
334
|
-
/**
|
|
335
|
-
* Build a map of workspace package names → relative entry point paths.
|
|
336
|
-
* Scans for package.json files in common workspace dirs.
|
|
337
|
-
*/
|
|
338
|
-
async buildWorkspaceMap(rootPath) {
|
|
339
|
-
const map = /* @__PURE__ */ new Map();
|
|
340
|
-
const searchDirs = ["packages", "functions", "libs", "apps", "cdk"];
|
|
341
|
-
for (const dir of searchDirs) {
|
|
342
|
-
const dirPath = join(rootPath, dir);
|
|
343
|
-
try {
|
|
344
|
-
const entries = await readdir(dirPath, { withFileTypes: true });
|
|
345
|
-
for (const entry of entries) {
|
|
346
|
-
if (!entry.isDirectory() || DEFAULT_EXCLUDES.has(entry.name)) continue;
|
|
347
|
-
try {
|
|
348
|
-
const pkgJsonPath = join(dirPath, entry.name, "package.json");
|
|
349
|
-
const pkgJson = JSON.parse(await readFile(pkgJsonPath, "utf-8"));
|
|
350
|
-
if (pkgJson.name) {
|
|
351
|
-
const main = pkgJson.main ?? pkgJson.exports?.["."] ?? "src/index.ts";
|
|
352
|
-
const entryRelative = relative(rootPath, resolve(dirPath, entry.name, main)).replace(
|
|
353
|
-
/\\/g,
|
|
354
|
-
"/"
|
|
355
|
-
);
|
|
356
|
-
map.set(pkgJson.name, entryRelative);
|
|
357
|
-
}
|
|
358
|
-
} catch {
|
|
359
|
-
}
|
|
360
|
-
}
|
|
361
|
-
} catch {
|
|
362
|
-
}
|
|
363
|
-
}
|
|
364
|
-
return map;
|
|
365
|
-
}
|
|
366
|
-
formatMarkdown(external, internal, rootPath, testCoverage) {
|
|
367
|
-
const lines = [];
|
|
368
|
-
lines.push(`## Dependencies: ${rootPath}
|
|
369
|
-
`);
|
|
370
|
-
const sortedExternal = Object.entries(external).sort((a, b) => b[1].count - a[1].count);
|
|
371
|
-
const totalExternal = sortedExternal.length;
|
|
372
|
-
const totalImports = sortedExternal.reduce((sum, [, d]) => sum + d.count, 0);
|
|
373
|
-
const top5 = sortedExternal.slice(0, 5).map(([pkg]) => pkg);
|
|
374
|
-
lines.push(
|
|
375
|
-
`**${totalExternal} external packages**, **${totalImports} total imports**, **${Object.keys(internal).length} files** with internal imports.
|
|
376
|
-
`
|
|
377
|
-
);
|
|
378
|
-
if (top5.length > 0) {
|
|
379
|
-
lines.push(`**Top dependencies**: ${top5.join(", ")}
|
|
380
|
-
`);
|
|
381
|
-
}
|
|
382
|
-
lines.push("### External Dependencies\n");
|
|
383
|
-
lines.push("| Package | Imports | Used By |");
|
|
384
|
-
lines.push("|---------|---------|---------|");
|
|
385
|
-
for (const [pkg, data] of sortedExternal) {
|
|
386
|
-
lines.push(
|
|
387
|
-
`| ${pkg} | ${data.count} | ${data.usedBy.length} ${data.usedBy.length === 1 ? "file" : "files"} |`
|
|
388
|
-
);
|
|
389
|
-
}
|
|
390
|
-
if (testCoverage && Object.keys(testCoverage).length > 0) {
|
|
391
|
-
const totalTested = Object.keys(testCoverage).length;
|
|
392
|
-
const untested = Object.keys(internal).filter(
|
|
393
|
-
(f) => !isTestFile(f) && !testCoverage[f.replace(/\\.[jt]sx?$/, "")]
|
|
394
|
-
);
|
|
395
|
-
lines.push("\n### Test Coverage Summary\n");
|
|
396
|
-
lines.push(`**${totalTested} source modules** with test coverage.`);
|
|
397
|
-
if (untested.length > 0) {
|
|
398
|
-
lines.push(`**${untested.length} source files** with no detected test coverage.`);
|
|
399
|
-
}
|
|
400
|
-
const sorted = Object.entries(testCoverage).sort((a, b) => b[1].length - a[1].length);
|
|
401
|
-
lines.push("\n**Most-tested modules:**\n");
|
|
402
|
-
for (const [source, tests] of sorted.slice(0, 10)) {
|
|
403
|
-
const displayPath = source.replace(/\/dist\/[^/]*$/, "/src/index").replace(/\.mjs$/, ".ts");
|
|
404
|
-
lines.push(`- ${displayPath} (${tests.length} ${tests.length === 1 ? "test" : "tests"})`);
|
|
405
|
-
}
|
|
406
|
-
}
|
|
407
|
-
return lines.join("\n");
|
|
408
|
-
}
|
|
409
|
-
formatMermaid(internal) {
|
|
410
|
-
const lines = ["graph LR"];
|
|
411
|
-
const nodeId = (path) => path.replace(/[^a-zA-Z0-9]/g, "_");
|
|
412
|
-
for (const [file, deps] of Object.entries(internal).slice(0, 40)) {
|
|
413
|
-
const fromId = nodeId(file);
|
|
414
|
-
for (const dep of deps) {
|
|
415
|
-
const toId = nodeId(dep);
|
|
416
|
-
lines.push(` ${fromId}["${file}"] --> ${toId}["${dep}"]`);
|
|
417
|
-
}
|
|
418
|
-
}
|
|
419
|
-
return lines.join("\n");
|
|
420
|
-
}
|
|
421
|
-
}
|
|
422
|
-
export {
|
|
423
|
-
DependencyAnalyzer
|
|
424
|
-
};
|
|
425
|
-
//# sourceMappingURL=dependency-analyzer.js.map
|
|
1
|
+
import{readdir as w,readFile as j}from"node:fs/promises";import{dirname as x,extname as m,join as u,relative as v,resolve as R}from"node:path";const $=new Set([".ts",".tsx",".js",".jsx",".mjs",".cjs",".py",".java",".go",".cs",".kt",".scala",".rb",".rs",".php",".swift"]),P=[{regex:/import\s+(?:(?:type\s+)?(?:(?:\{[^}]*\}|[\w*]+)\s+from\s+)?)['"]([^'"]+)['"]/g,confidence:"high"},{regex:/import\(\s*['"]([^'"]+)['"]\s*\)/g,confidence:"medium"},{regex:/require\(\s*['"]([^'"]+)['"]\s*\)/g,confidence:"medium"},{regex:/^from\s+([\w.]+)\s+import\b/gm,confidence:"high",lang:"python"},{regex:/^import\s+([\w.]+)\s*$/gm,confidence:"high",lang:"python"},{regex:/^import\s+(?:static\s+)?([\w.]+(?:\.\*)?)\s*;/gm,confidence:"high",lang:"java"},{regex:/(?:^import\s+|^\s+)[""]([^""]+)[""]/gm,confidence:"high",lang:"go"},{regex:/^using\s+(?:static\s+)?([\w.]+)\s*;/gm,confidence:"high",lang:"csharp"},{regex:/^use\s+([\w:]+(?:::\w+)*)/gm,confidence:"high",lang:"rust"},{regex:/^use\s+([\w\\]+)\s*;/gm,confidence:"high",lang:"php"},{regex:/require(?:_relative)?\s+['"]([^'"]+)['"]/g,confidence:"medium",lang:"ruby"},{regex:/^import\s+(\w+)\s*$/gm,confidence:"high",lang:"swift"}],I=new Set(["node_modules",".git","dist","build","coverage",".turbo",".cache","cdk.out"]),E=[/\.(test|spec)\.[jt]sx?$/,/\/__tests__\//,/\/test\//,/\/tests\//],b={".ts":"js",".tsx":"js",".js":"js",".jsx":"js",".mjs":"js",".cjs":"js",".py":"python",".java":"java",".kt":"java",".scala":"java",".go":"go",".cs":"csharp",".rs":"rust",".php":"php",".rb":"ruby",".swift":"swift"};function S(a,n){return!n||n==="js"?!a.startsWith(".")&&!a.startsWith("/"):n==="python"?!a.startsWith("."):n==="java"?!a.startsWith("com.")||a.startsWith("com.amazonaws")||a.startsWith("com.google")||a.startsWith("com.fasterxml"):n==="go"?a.includes(".")&&!a.startsWith("."):n==="csharp"?a.startsWith("System")||a.startsWith("Microsoft")||a.startsWith("Newtonsoft")||a.startsWith("Amazon"):n==="rust"?!a.startsWith("crate::")&&!a.startsWith("self::")&&!a.startsWith("super::"):!0}function y(a){return E.some(n=>n.test(a))}class W{name="dependencies";workspacePackages=new Map;async analyze(n,o={}){const{format:i="markdown"}=o,t=Date.now();this.workspacePackages=await this.buildWorkspaceMap(n);const e=await this.collectFiles(n),r=[];for(const d of e){const f=await j(d,"utf-8"),h=this.extractImports(f,d,n);r.push(...h)}const s=this.groupExternalDeps(r),c=this.groupInternalDeps(r,n),g=this.buildReverseGraph(r,n),l=this.buildTestCoverage(r,n);return{output:i==="json"?JSON.stringify({external:s,internal:c,reverseGraph:g,testCoverage:l},null,2):i==="mermaid"?this.formatMermaid(c):this.formatMarkdown(s,c,n,l),data:{external:s,internal:c,reverseGraph:g,testCoverage:l,totalImports:r.length},meta:{analyzedAt:new Date().toISOString(),scope:n,fileCount:e.length,durationMs:Date.now()-t}}}async collectFiles(n){const o=[],i=async t=>{const e=await w(t,{withFileTypes:!0});for(const r of e){if(I.has(r.name)||r.name.startsWith("."))continue;const s=u(t,r.name);r.isDirectory()?await i(s):$.has(m(r.name))&&o.push(s)}};return await i(n),o}extractImports(n,o,i){const t=[],e=m(o).toLowerCase(),r=b[e];for(const s of P){if(s.lang&&s.lang!==r||!s.lang&&r&&r!=="js")continue;const c=new RegExp(s.regex.source,s.regex.flags);let g;for(;(g=c.exec(n))!==null;){const l=g[1],p=S(l,r);t.push({source:l,specifiers:[],filePath:v(i,o).replace(/\\/g,"/"),isExternal:p,confidence:s.confidence})}}return t}groupExternalDeps(n){const o={};for(const t of n){if(!t.isExternal)continue;const e=m(t.filePath).toLowerCase(),r=b[e];let s;if(r==="java"){const c=t.source.split(".");for(;c.length>1;){const g=c[c.length-1];if(g==="*"||/^[A-Z]/.test(g))c.pop();else break}c.length>=2?s=c.slice(0,2).join("."):s=c.join(".")}else if(r==="python")s=t.source.split(".")[0];else if(r==="go")s=t.source;else if(r==="csharp"){const c=t.source.split(".");s=c.length>=2?c.slice(0,2).join("."):t.source}else s=t.source.startsWith("@")?t.source.split("/").slice(0,2).join("/"):t.source.split("/")[0];o[s]||(o[s]={count:0,confidence:t.confidence,usedBy:new Set}),o[s].count++,o[s].usedBy.add(t.filePath),t.confidence==="high"?o[s].confidence="high":t.confidence==="medium"&&o[s].confidence==="low"&&(o[s].confidence="medium")}const i={};for(const[t,e]of Object.entries(o))i[t]={count:e.count,confidence:e.confidence,usedBy:[...e.usedBy]};return i}groupInternalDeps(n,o){const i={};for(const e of n)e.isExternal||(i[e.filePath]||(i[e.filePath]=new Set),i[e.filePath].add(e.source));const t={};for(const[e,r]of Object.entries(i))t[e]=[...r];return t}buildReverseGraph(n,o){const i={};for(const e of n){const r=x(e.filePath),s=this.resolveImportPath(e.source,r);s&&(i[s]||(i[s]=new Set),i[s].add(e.filePath))}const t={};for(const[e,r]of Object.entries(i))t[e]=[...r];return t}buildTestCoverage(n,o){const i={};for(const e of n){if(!y(e.filePath))continue;const r=x(e.filePath),s=this.resolveImportPath(e.source,r);!s||y(s)||(i[s]||(i[s]=new Set),i[s].add(e.filePath))}const t={};for(const[e,r]of Object.entries(i))t[e]=[...r];return t}resolveImportPath(n,o){if(n.startsWith("."))return u(o,n).replace(/\\/g,"/").replace(/\.[jt]sx?$/,"");const i=n.startsWith("@")?n.split("/").slice(0,2).join("/"):n.split("/")[0],t=this.workspacePackages.get(i);return t?t.replace(/\.[jt]sx?$/,""):null}async buildWorkspaceMap(n){const o=new Map,i=["packages","functions","libs","apps","cdk"];for(const t of i){const e=u(n,t);try{const r=await w(e,{withFileTypes:!0});for(const s of r)if(!(!s.isDirectory()||I.has(s.name)))try{const c=u(e,s.name,"package.json"),g=JSON.parse(await j(c,"utf-8"));if(g.name){const l=g.main??g.exports?.["."]??"src/index.ts",p=v(n,R(e,s.name,l)).replace(/\\/g,"/");o.set(g.name,p)}}catch{}}catch{}}return o}formatMarkdown(n,o,i,t){const e=[];e.push(`## Dependencies: ${i}
|
|
2
|
+
`);const r=Object.entries(n).sort((l,p)=>p[1].count-l[1].count),s=r.length,c=r.reduce((l,[,p])=>l+p.count,0),g=r.slice(0,5).map(([l])=>l);e.push(`**${s} external packages**, **${c} total imports**, **${Object.keys(o).length} files** with internal imports.
|
|
3
|
+
`),g.length>0&&e.push(`**Top dependencies**: ${g.join(", ")}
|
|
4
|
+
`),e.push(`### External Dependencies
|
|
5
|
+
`),e.push("| Package | Imports | Used By |"),e.push("|---------|---------|---------|");for(const[l,p]of r)e.push(`| ${l} | ${p.count} | ${p.usedBy.length} ${p.usedBy.length===1?"file":"files"} |`);if(t&&Object.keys(t).length>0){const l=Object.keys(t).length,p=Object.keys(o).filter(f=>!y(f)&&!t[f.replace(/\\.[jt]sx?$/,"")]);e.push(`
|
|
6
|
+
### Test Coverage Summary
|
|
7
|
+
`),e.push(`**${l} source modules** with test coverage.`),p.length>0&&e.push(`**${p.length} source files** with no detected test coverage.`);const d=Object.entries(t).sort((f,h)=>h[1].length-f[1].length);e.push(`
|
|
8
|
+
**Most-tested modules:**
|
|
9
|
+
`);for(const[f,h]of d.slice(0,10)){const k=f.replace(/\/dist\/[^/]*$/,"/src/index").replace(/\.mjs$/,".ts");e.push(`- ${k} (${h.length} ${h.length===1?"test":"tests"})`)}}return e.join(`
|
|
10
|
+
`)}formatMermaid(n){const o=["graph LR"],i=t=>t.replace(/[^a-zA-Z0-9]/g,"_");for(const[t,e]of Object.entries(n).slice(0,40)){const r=i(t);for(const s of e){const c=i(s);o.push(` ${r}["${t}"] --> ${c}["${s}"]`)}}return o.join(`
|
|
11
|
+
`)}}export{W as DependencyAnalyzer};
|