@mycodemap/mycodemap 0.1.0 → 0.2.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/CHANGELOG.md +164 -6
- package/README.md +407 -141
- package/dist/cli/commands/ci.d.ts +7 -1
- package/dist/cli/commands/ci.d.ts.map +1 -1
- package/dist/cli/commands/ci.js +38 -0
- package/dist/cli/commands/ci.js.map +1 -1
- package/dist/cli/commands/cycles.d.ts.map +1 -1
- package/dist/cli/commands/cycles.js +2 -0
- package/dist/cli/commands/cycles.js.map +1 -1
- package/dist/cli/commands/export.d.ts +6 -0
- package/dist/cli/commands/export.d.ts.map +1 -0
- package/dist/cli/commands/export.js +108 -0
- package/dist/cli/commands/export.js.map +1 -0
- package/dist/cli/commands/generate.d.ts.map +1 -1
- package/dist/cli/commands/generate.js +96 -0
- package/dist/cli/commands/generate.js.map +1 -1
- package/dist/cli/commands/init.d.ts.map +1 -1
- package/dist/cli/commands/init.js +3 -1
- package/dist/cli/commands/init.js.map +1 -1
- package/dist/cli/commands/logs.d.ts +5 -0
- package/dist/cli/commands/logs.d.ts.map +1 -0
- package/dist/cli/commands/logs.js +189 -0
- package/dist/cli/commands/logs.js.map +1 -0
- package/dist/cli/commands/report.d.ts +12 -0
- package/dist/cli/commands/report.d.ts.map +1 -0
- package/dist/cli/commands/report.js +158 -0
- package/dist/cli/commands/report.js.map +1 -0
- package/dist/cli/commands/server.d.ts +9 -0
- package/dist/cli/commands/server.d.ts.map +1 -0
- package/dist/cli/commands/server.js +68 -0
- package/dist/cli/commands/server.js.map +1 -0
- package/dist/cli/commands/watch-foreground.d.ts.map +1 -1
- package/dist/cli/commands/watch-foreground.js +2 -0
- package/dist/cli/commands/watch-foreground.js.map +1 -1
- package/dist/cli/commands/watch.d.ts.map +1 -1
- package/dist/cli/commands/watch.js +2 -0
- package/dist/cli/commands/watch.js.map +1 -1
- package/dist/cli/first-run-guide.d.ts +23 -0
- package/dist/cli/first-run-guide.d.ts.map +1 -0
- package/dist/cli/first-run-guide.js +83 -0
- package/dist/cli/first-run-guide.js.map +1 -0
- package/dist/cli/index.js +85 -1
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/platform-check.d.ts +21 -0
- package/dist/cli/platform-check.d.ts.map +1 -0
- package/dist/cli/platform-check.js +94 -0
- package/dist/cli/platform-check.js.map +1 -0
- package/dist/cli/tree-sitter-check.d.ts +35 -0
- package/dist/cli/tree-sitter-check.d.ts.map +1 -0
- package/dist/cli/tree-sitter-check.js +133 -0
- package/dist/cli/tree-sitter-check.js.map +1 -0
- package/dist/cli/utils/sanitize.d.ts +54 -0
- package/dist/cli/utils/sanitize.d.ts.map +1 -0
- package/dist/cli/utils/sanitize.js +131 -0
- package/dist/cli/utils/sanitize.js.map +1 -0
- package/dist/cli-new/commands/export.d.ts +15 -0
- package/dist/cli-new/commands/export.d.ts.map +1 -0
- package/dist/cli-new/commands/export.js +107 -0
- package/dist/cli-new/commands/export.js.map +1 -0
- package/dist/cli-new/commands/query.d.ts +14 -0
- package/dist/cli-new/commands/query.d.ts.map +1 -0
- package/dist/cli-new/commands/query.js +120 -0
- package/dist/cli-new/commands/query.js.map +1 -0
- package/dist/cli-new/commands/server.d.ts +13 -0
- package/dist/cli-new/commands/server.d.ts.map +1 -0
- package/dist/cli-new/commands/server.js +94 -0
- package/dist/cli-new/commands/server.js.map +1 -0
- package/dist/cli-new/index.d.ts +11 -0
- package/dist/cli-new/index.d.ts.map +1 -0
- package/dist/cli-new/index.js +63 -0
- package/dist/cli-new/index.js.map +1 -0
- package/dist/cli-new/types/index.d.ts +88 -0
- package/dist/cli-new/types/index.d.ts.map +1 -0
- package/dist/cli-new/types/index.js +7 -0
- package/dist/cli-new/types/index.js.map +1 -0
- package/dist/domain/entities/CodeGraph.d.ts +134 -0
- package/dist/domain/entities/CodeGraph.d.ts.map +1 -0
- package/dist/domain/entities/CodeGraph.js +316 -0
- package/dist/domain/entities/CodeGraph.js.map +1 -0
- package/dist/domain/entities/Dependency.d.ts +78 -0
- package/dist/domain/entities/Dependency.d.ts.map +1 -0
- package/dist/domain/entities/Dependency.js +132 -0
- package/dist/domain/entities/Dependency.js.map +1 -0
- package/dist/domain/entities/Module.d.ts +75 -0
- package/dist/domain/entities/Module.d.ts.map +1 -0
- package/dist/domain/entities/Module.js +151 -0
- package/dist/domain/entities/Module.js.map +1 -0
- package/dist/domain/entities/Project.d.ts +50 -0
- package/dist/domain/entities/Project.d.ts.map +1 -0
- package/dist/domain/entities/Project.js +99 -0
- package/dist/domain/entities/Project.js.map +1 -0
- package/dist/domain/entities/Symbol.d.ts +75 -0
- package/dist/domain/entities/Symbol.d.ts.map +1 -0
- package/dist/domain/entities/Symbol.js +130 -0
- package/dist/domain/entities/Symbol.js.map +1 -0
- package/dist/domain/events/DomainEvent.d.ts +76 -0
- package/dist/domain/events/DomainEvent.d.ts.map +1 -0
- package/dist/domain/events/DomainEvent.js +153 -0
- package/dist/domain/events/DomainEvent.js.map +1 -0
- package/dist/domain/index.d.ts +10 -0
- package/dist/domain/index.d.ts.map +1 -0
- package/dist/domain/index.js +18 -0
- package/dist/domain/index.js.map +1 -0
- package/dist/domain/repositories/CodeGraphRepository.d.ts +58 -0
- package/dist/domain/repositories/CodeGraphRepository.d.ts.map +1 -0
- package/dist/domain/repositories/CodeGraphRepository.js +37 -0
- package/dist/domain/repositories/CodeGraphRepository.js.map +1 -0
- package/dist/domain/services/CodeGraphBuilder.d.ts +50 -0
- package/dist/domain/services/CodeGraphBuilder.d.ts.map +1 -0
- package/dist/domain/services/CodeGraphBuilder.js +121 -0
- package/dist/domain/services/CodeGraphBuilder.js.map +1 -0
- package/dist/infrastructure/parser/implementations/GoParser.d.ts +25 -0
- package/dist/infrastructure/parser/implementations/GoParser.d.ts.map +1 -0
- package/dist/infrastructure/parser/implementations/GoParser.js +158 -0
- package/dist/infrastructure/parser/implementations/GoParser.js.map +1 -0
- package/dist/infrastructure/parser/implementations/PythonParser.d.ts +30 -0
- package/dist/infrastructure/parser/implementations/PythonParser.d.ts.map +1 -0
- package/dist/infrastructure/parser/implementations/PythonParser.js +201 -0
- package/dist/infrastructure/parser/implementations/PythonParser.js.map +1 -0
- package/dist/infrastructure/parser/implementations/TypeScriptParser.d.ts +63 -0
- package/dist/infrastructure/parser/implementations/TypeScriptParser.d.ts.map +1 -0
- package/dist/infrastructure/parser/implementations/TypeScriptParser.js +420 -0
- package/dist/infrastructure/parser/implementations/TypeScriptParser.js.map +1 -0
- package/dist/infrastructure/parser/index.d.ts +13 -0
- package/dist/infrastructure/parser/index.d.ts.map +1 -0
- package/dist/infrastructure/parser/index.js +32 -0
- package/dist/infrastructure/parser/index.js.map +1 -0
- package/dist/infrastructure/parser/interfaces/ParserBase.d.ts +124 -0
- package/dist/infrastructure/parser/interfaces/ParserBase.d.ts.map +1 -0
- package/dist/infrastructure/parser/interfaces/ParserBase.js +200 -0
- package/dist/infrastructure/parser/interfaces/ParserBase.js.map +1 -0
- package/dist/infrastructure/parser/registry/ParserRegistry.d.ts +68 -0
- package/dist/infrastructure/parser/registry/ParserRegistry.d.ts.map +1 -0
- package/dist/infrastructure/parser/registry/ParserRegistry.js +116 -0
- package/dist/infrastructure/parser/registry/ParserRegistry.js.map +1 -0
- package/dist/infrastructure/repositories/CodeGraphRepositoryImpl.d.ts +44 -0
- package/dist/infrastructure/repositories/CodeGraphRepositoryImpl.d.ts.map +1 -0
- package/dist/infrastructure/repositories/CodeGraphRepositoryImpl.js +129 -0
- package/dist/infrastructure/repositories/CodeGraphRepositoryImpl.js.map +1 -0
- package/dist/infrastructure/repositories/index.d.ts +3 -0
- package/dist/infrastructure/repositories/index.d.ts.map +1 -0
- package/dist/infrastructure/repositories/index.js +7 -0
- package/dist/infrastructure/repositories/index.js.map +1 -0
- package/dist/infrastructure/storage/StorageFactory.d.ts +53 -0
- package/dist/infrastructure/storage/StorageFactory.d.ts.map +1 -0
- package/dist/infrastructure/storage/StorageFactory.js +150 -0
- package/dist/infrastructure/storage/StorageFactory.js.map +1 -0
- package/dist/infrastructure/storage/adapters/FileSystemStorage.d.ts +52 -0
- package/dist/infrastructure/storage/adapters/FileSystemStorage.d.ts.map +1 -0
- package/dist/infrastructure/storage/adapters/FileSystemStorage.js +315 -0
- package/dist/infrastructure/storage/adapters/FileSystemStorage.js.map +1 -0
- package/dist/infrastructure/storage/adapters/KuzuDBStorage.d.ts +52 -0
- package/dist/infrastructure/storage/adapters/KuzuDBStorage.d.ts.map +1 -0
- package/dist/infrastructure/storage/adapters/KuzuDBStorage.js +235 -0
- package/dist/infrastructure/storage/adapters/KuzuDBStorage.js.map +1 -0
- package/dist/infrastructure/storage/adapters/MemoryStorage.d.ts +37 -0
- package/dist/infrastructure/storage/adapters/MemoryStorage.d.ts.map +1 -0
- package/dist/infrastructure/storage/adapters/MemoryStorage.js +229 -0
- package/dist/infrastructure/storage/adapters/MemoryStorage.js.map +1 -0
- package/dist/infrastructure/storage/adapters/Neo4jStorage.d.ts +49 -0
- package/dist/infrastructure/storage/adapters/Neo4jStorage.d.ts.map +1 -0
- package/dist/infrastructure/storage/adapters/Neo4jStorage.js +222 -0
- package/dist/infrastructure/storage/adapters/Neo4jStorage.js.map +1 -0
- package/dist/infrastructure/storage/index.d.ts +6 -0
- package/dist/infrastructure/storage/index.d.ts.map +1 -0
- package/dist/infrastructure/storage/index.js +13 -0
- package/dist/infrastructure/storage/index.js.map +1 -0
- package/dist/infrastructure/storage/interfaces/StorageBase.d.ts +76 -0
- package/dist/infrastructure/storage/interfaces/StorageBase.d.ts.map +1 -0
- package/dist/infrastructure/storage/interfaces/StorageBase.js +116 -0
- package/dist/infrastructure/storage/interfaces/StorageBase.js.map +1 -0
- package/dist/interface/config/index.d.ts +102 -0
- package/dist/interface/config/index.d.ts.map +1 -0
- package/dist/interface/config/index.js +7 -0
- package/dist/interface/config/index.js.map +1 -0
- package/dist/interface/types/index.d.ts +425 -0
- package/dist/interface/types/index.d.ts.map +1 -0
- package/dist/interface/types/index.js +8 -0
- package/dist/interface/types/index.js.map +1 -0
- package/dist/interface/types/parser.d.ts +103 -0
- package/dist/interface/types/parser.d.ts.map +1 -0
- package/dist/interface/types/parser.js +7 -0
- package/dist/interface/types/parser.js.map +1 -0
- package/dist/interface/types/storage.d.ts +98 -0
- package/dist/interface/types/storage.d.ts.map +1 -0
- package/dist/interface/types/storage.js +7 -0
- package/dist/interface/types/storage.js.map +1 -0
- package/dist/orchestrator/test-linker.js +1 -1
- package/dist/orchestrator/test-linker.js.map +1 -1
- package/dist/server/CodeMapServer.d.ts +51 -0
- package/dist/server/CodeMapServer.d.ts.map +1 -0
- package/dist/server/CodeMapServer.js +146 -0
- package/dist/server/CodeMapServer.js.map +1 -0
- package/dist/server/handlers/AnalysisHandler.d.ts +82 -0
- package/dist/server/handlers/AnalysisHandler.d.ts.map +1 -0
- package/dist/server/handlers/AnalysisHandler.js +196 -0
- package/dist/server/handlers/AnalysisHandler.js.map +1 -0
- package/dist/server/handlers/QueryHandler.d.ts +57 -0
- package/dist/server/handlers/QueryHandler.d.ts.map +1 -0
- package/dist/server/handlers/QueryHandler.js +260 -0
- package/dist/server/handlers/QueryHandler.js.map +1 -0
- package/dist/server/index.d.ts +7 -0
- package/dist/server/index.d.ts.map +1 -0
- package/dist/server/index.js +13 -0
- package/dist/server/index.js.map +1 -0
- package/dist/server/routes/api.d.ts +8 -0
- package/dist/server/routes/api.d.ts.map +1 -0
- package/dist/server/routes/api.js +372 -0
- package/dist/server/routes/api.js.map +1 -0
- package/dist/server/types/index.d.ts +171 -0
- package/dist/server/types/index.d.ts.map +1 -0
- package/dist/server/types/index.js +7 -0
- package/dist/server/types/index.js.map +1 -0
- package/dist/types/index.d.ts +6 -372
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +9 -3
- package/dist/types/index.js.map +1 -1
- package/docs/AI_ASSISTANT_SETUP.md +811 -0
- package/docs/PUBLISHING.md +162 -0
- package/docs/README.md +37 -0
- package/docs/SETUP_GUIDE.md +493 -0
- package/docs/ai-guide/COMMANDS.md +369 -0
- package/docs/ai-guide/INTEGRATION.md +513 -0
- package/docs/ai-guide/OUTPUT.md +465 -0
- package/docs/ai-guide/PATTERNS.md +409 -0
- package/docs/ai-guide/PROMPTS.md +414 -0
- package/docs/ai-guide/QUICKSTART.md +114 -0
- package/docs/ai-guide/README.md +66 -0
- package/docs/archive/AI_INTEGRATION_GUIDE_ARCHIVED.md +391 -0
- package/docs/archive/ARCHIVE.md +39 -0
- package/docs/archive/MYCLAUDE_GUIDE.md +305 -0
- package/docs/archive/PUBLISH_NPM_DESIGN_V1.md +1699 -0
- package/docs/archive/PUBLISH_NPM_DESIGN_V2.md +396 -0
- package/docs/archive/README.md +29 -0
- package/docs/archive/TASK_DESIGN_COVERAGE_REPORT.md +320 -0
- package/docs/archive/TEST_SUMMARY.md +140 -0
- package/docs/archive/comprehensive_test_report.md +337 -0
- package/docs/archive/design-docs/CI_GATEWAY_DESIGN.md +790 -0
- package/docs/archive/design-docs/PUBLISH_NPM_DESIGN_FINAL.md +491 -0
- package/docs/archive/design-docs/REFACTOR_ARCHITECTURE_OVERVIEW.md +558 -0
- package/docs/archive/design-docs/REFACTOR_CONFIDENCE_DESIGN.md +250 -0
- package/docs/archive/design-docs/REFACTOR_GIT_ANALYZER_DESIGN.md +791 -0
- package/docs/archive/design-docs/REFACTOR_ORCHESTRATOR_DESIGN.md +1071 -0
- package/docs/archive/design-docs/REFACTOR_RESULT_FUSION_DESIGN.md +321 -0
- package/docs/archive/design-docs/REFACTOR_TEST_LINKER_DESIGN.md +317 -0
- package/docs/archive/myclaude.md +1084 -0
- package/docs/archive/plans/2026-03-14-go-language-support-design.md +92 -0
- package/docs/archive/product-specs/REFACTOR_REQUIREMENTS.md +976 -0
- package/docs/archive/scenario-2-deps-analysis.md +353 -0
- package/docs/archive/test-report-symbol-search.md +384 -0
- package/docs/archive/test-scenario-4-complexity-analysis.md +460 -0
- package/docs/archive/test_report_scenario5.md +615 -0
- package/docs/archive/test_scenario_3_impact_analysis_report.md +520 -0
- package/docs/design-docs/README.md +26 -0
- package/docs/exec-plans/MVP3-IMPLEMENTATION-ROADMAP.md +524 -0
- package/docs/exec-plans/README.md +29 -0
- package/docs/exec-plans/active/.gitkeep +0 -0
- package/docs/exec-plans/completed/.gitkeep +0 -0
- package/docs/exec-plans/completed/2026-03-03-deps-path-extension-fix.md +186 -0
- package/docs/exec-plans/completed/2026-03-03-post-task-plan.md +135 -0
- package/docs/exec-plans/completed/harness-engineering-rollout.md +184 -0
- package/docs/exec-plans/tech-debt/.gitkeep +0 -0
- package/docs/exec-plans/tech-debt/2026-03-15-lint-guardrail-gap.md +30 -0
- package/docs/generated/README.md +19 -0
- package/docs/product-specs/MVP3-ARCHITECTURE-COMPARISON.md +504 -0
- package/docs/product-specs/MVP3-ARCHITECTURE-REDESIGN-PRD.md +322 -0
- package/docs/product-specs/MVP3-ARCHITECTURE-REDESIGN-TECH-PRD.md +1374 -0
- package/docs/product-specs/README.md +22 -0
- package/docs/references/README.md +15 -0
- package/docs/references/tmp.md +527 -0
- package/docs/rules/README.md +16 -0
- package/docs/rules/architecture-guardrails.md +349 -0
- package/docs/rules/code-quality-redlines.md +321 -0
- package/docs/rules/deployment.md +23 -0
- package/docs/rules/engineering-with-codex-openai.md +202 -0
- package/docs/rules/testing.md +73 -0
- package/docs/rules/validation.md +39 -0
- package/examples/README.md +61 -0
- package/examples/claude/codemap-skill.md +94 -0
- package/examples/codex/codemap-agent.md +66 -0
- package/examples/copilot/copilot-instructions.md +24 -0
- package/examples/kimi/codemap-skill.md +92 -0
- package/package.json +22 -7
|
@@ -0,0 +1,1374 @@
|
|
|
1
|
+
# CodeMap MVP3 架构重构技术需求文档 (Tech-PRD)
|
|
2
|
+
|
|
3
|
+
> **版本**: v1.0.0
|
|
4
|
+
> **状态**: Draft
|
|
5
|
+
> **日期**: 2026-03-17
|
|
6
|
+
> **负责人**: Architecture Team
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## 1. 技术目标
|
|
11
|
+
|
|
12
|
+
### 1.1 核心架构原则
|
|
13
|
+
|
|
14
|
+
```
|
|
15
|
+
┌─────────────────────────────────────────────────────────────────┐
|
|
16
|
+
│ 架构设计原则 │
|
|
17
|
+
├─────────────────────────────────────────────────────────────────┤
|
|
18
|
+
│ │
|
|
19
|
+
│ 1. 依赖方向: Interface → Infrastructure → Domain → Server → CLI│
|
|
20
|
+
│ (外层依赖内层,内层不依赖外层) │
|
|
21
|
+
│ │
|
|
22
|
+
│ 2. 接口隔离: 每层通过明确定义的接口与其他层交互 │
|
|
23
|
+
│ │
|
|
24
|
+
│ 3. 依赖注入: 通过构造函数注入依赖,便于测试和替换 │
|
|
25
|
+
│ │
|
|
26
|
+
│ 4. 单一职责: 每个模块只有一个变化理由 │
|
|
27
|
+
│ │
|
|
28
|
+
└─────────────────────────────────────────────────────────────────┘
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
### 1.2 目录结构重构
|
|
32
|
+
|
|
33
|
+
```
|
|
34
|
+
src/
|
|
35
|
+
├── interface/ # Layer 1: 接口层
|
|
36
|
+
│ ├── types/
|
|
37
|
+
│ │ ├── index.ts # 核心类型定义
|
|
38
|
+
│ │ ├── storage.ts # 存储接口
|
|
39
|
+
│ │ ├── parser.ts # 解析器接口
|
|
40
|
+
│ │ └── analyzer.ts # 分析器接口
|
|
41
|
+
│ └── config/
|
|
42
|
+
│ └── index.ts # 配置类型
|
|
43
|
+
│
|
|
44
|
+
├── infrastructure/ # Layer 2: 基础设施层
|
|
45
|
+
│ ├── storage/ # 存储实现
|
|
46
|
+
│ │ ├── interfaces/
|
|
47
|
+
│ │ │ └── IStorage.ts
|
|
48
|
+
│ │ ├── implementations/
|
|
49
|
+
│ │ │ ├── FileSystemStorage.ts
|
|
50
|
+
│ │ │ ├── KuzuDBStorage.ts
|
|
51
|
+
│ │ │ └── Neo4jStorage.ts
|
|
52
|
+
│ │ ├── StorageFactory.ts
|
|
53
|
+
│ │ └── index.ts
|
|
54
|
+
│ │
|
|
55
|
+
│ ├── parser/ # 解析器实现
|
|
56
|
+
│ │ ├── interfaces/
|
|
57
|
+
│ │ │ └── ILanguageParser.ts
|
|
58
|
+
│ │ ├── implementations/
|
|
59
|
+
│ │ │ ├── typescript/
|
|
60
|
+
│ │ │ ├── python/
|
|
61
|
+
│ │ │ ├── java/
|
|
62
|
+
│ │ │ ├── rust/
|
|
63
|
+
│ │ │ ├── cpp/
|
|
64
|
+
│ │ │ └── ... (其他语言)
|
|
65
|
+
│ │ ├── ParserRegistry.ts
|
|
66
|
+
│ │ └── index.ts
|
|
67
|
+
│ │
|
|
68
|
+
│ ├── cache/ # 缓存系统
|
|
69
|
+
│ ├── file-system/ # 文件系统抽象
|
|
70
|
+
│ └── logger/ # 日志系统
|
|
71
|
+
│
|
|
72
|
+
├── domain/ # Layer 3: 领域层
|
|
73
|
+
│ ├── entities/ # 领域实体
|
|
74
|
+
│ │ ├── Project.ts
|
|
75
|
+
│ │ ├── Module.ts
|
|
76
|
+
│ │ ├── Symbol.ts
|
|
77
|
+
│ │ ├── Dependency.ts
|
|
78
|
+
│ │ └── CodeGraph.ts
|
|
79
|
+
│ │
|
|
80
|
+
│ ├── services/ # 领域服务
|
|
81
|
+
│ │ ├── AnalysisService.ts
|
|
82
|
+
│ │ ├── DependencyService.ts
|
|
83
|
+
│ │ ├── SymbolIndexService.ts
|
|
84
|
+
│ │ └── ComplexityService.ts
|
|
85
|
+
│ │
|
|
86
|
+
│ ├── value-objects/ # 值对象
|
|
87
|
+
│ │ ├── FilePath.ts
|
|
88
|
+
│ │ ├── Position.ts
|
|
89
|
+
│ │ └── Range.ts
|
|
90
|
+
│ │
|
|
91
|
+
│ └── repositories/ # 仓储接口
|
|
92
|
+
│ └── ICodeGraphRepository.ts
|
|
93
|
+
│
|
|
94
|
+
├── server/ # Layer 4: 服务层 ⭐ 新增
|
|
95
|
+
│ ├── usecases/ # 用例
|
|
96
|
+
│ │ ├── GenerateCodeMap.ts
|
|
97
|
+
│ │ ├── QuerySymbol.ts
|
|
98
|
+
│ │ ├── AnalyzeImpact.ts
|
|
99
|
+
│ │ ├── DetectCycles.ts
|
|
100
|
+
│ │ ├── StartWorkflow.ts
|
|
101
|
+
│ │ └── ...
|
|
102
|
+
│ │
|
|
103
|
+
│ ├── dto/ # 数据传输对象
|
|
104
|
+
│ │ ├── GenerateRequest.ts
|
|
105
|
+
│ │ ├── GenerateResponse.ts
|
|
106
|
+
│ │ └── QueryRequest.ts
|
|
107
|
+
│ │
|
|
108
|
+
│ ├── mappers/ # 对象映射
|
|
109
|
+
│ │ ├── ModuleMapper.ts
|
|
110
|
+
│ │ └── SymbolMapper.ts
|
|
111
|
+
│ │
|
|
112
|
+
│ └── services/ # 应用服务
|
|
113
|
+
│ ├── CodeMapService.ts
|
|
114
|
+
│ ├── QueryService.ts
|
|
115
|
+
│ └── WorkflowService.ts
|
|
116
|
+
│
|
|
117
|
+
└── cli/ # Layer 5: CLI 层
|
|
118
|
+
├── commands/ # 命令实现
|
|
119
|
+
├── visualizers/ # CLI 可视化
|
|
120
|
+
│ ├── TreeVisualizer.ts
|
|
121
|
+
│ ├── GraphVisualizer.ts
|
|
122
|
+
│ └── HeatmapVisualizer.ts
|
|
123
|
+
├── formatters/ # 输出格式化
|
|
124
|
+
└── index.ts # 入口
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
---
|
|
128
|
+
|
|
129
|
+
## 2. 核心接口设计
|
|
130
|
+
|
|
131
|
+
### 2.1 存储抽象接口
|
|
132
|
+
|
|
133
|
+
```typescript
|
|
134
|
+
// src/infrastructure/storage/interfaces/IStorage.ts
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* 存储抽象接口
|
|
138
|
+
* 定义所有存储后端必须实现的能力
|
|
139
|
+
*/
|
|
140
|
+
export interface IStorage {
|
|
141
|
+
/** 存储类型标识 */
|
|
142
|
+
readonly type: StorageType;
|
|
143
|
+
|
|
144
|
+
/** 初始化存储 */
|
|
145
|
+
initialize(projectPath: string): Promise<void>;
|
|
146
|
+
|
|
147
|
+
/** 关闭存储连接 */
|
|
148
|
+
close(): Promise<void>;
|
|
149
|
+
|
|
150
|
+
// ========== 项目级别操作 ==========
|
|
151
|
+
|
|
152
|
+
/** 保存完整代码图 */
|
|
153
|
+
saveCodeGraph(graph: CodeGraph): Promise<void>;
|
|
154
|
+
|
|
155
|
+
/** 加载完整代码图 */
|
|
156
|
+
loadCodeGraph(): Promise<CodeGraph>;
|
|
157
|
+
|
|
158
|
+
/** 删除项目数据 */
|
|
159
|
+
deleteProject(): Promise<void>;
|
|
160
|
+
|
|
161
|
+
// ========== 增量更新 ==========
|
|
162
|
+
|
|
163
|
+
/** 更新单个模块 */
|
|
164
|
+
updateModule(module: Module): Promise<void>;
|
|
165
|
+
|
|
166
|
+
/** 删除模块 */
|
|
167
|
+
deleteModule(moduleId: string): Promise<void>;
|
|
168
|
+
|
|
169
|
+
// ========== 查询操作 ==========
|
|
170
|
+
|
|
171
|
+
/** 查询模块 */
|
|
172
|
+
findModuleById(id: string): Promise<Module | null>;
|
|
173
|
+
findModulesByPath(path: string): Promise<Module[]>;
|
|
174
|
+
|
|
175
|
+
/** 查询符号 */
|
|
176
|
+
findSymbolByName(name: string): Promise<Symbol[]>;
|
|
177
|
+
findSymbolById(id: string): Promise<Symbol | null>;
|
|
178
|
+
|
|
179
|
+
/** 查询依赖 */
|
|
180
|
+
findDependencies(moduleId: string): Promise<Dependency[]>;
|
|
181
|
+
findDependents(moduleId: string): Promise<Dependency[]>;
|
|
182
|
+
|
|
183
|
+
/** 查询调用关系 */
|
|
184
|
+
findCallers(functionId: string): Promise<Symbol[]>;
|
|
185
|
+
findCallees(functionId: string): Promise<Symbol[]>;
|
|
186
|
+
|
|
187
|
+
/** 模糊搜索 */
|
|
188
|
+
search(query: string, options: SearchOptions): Promise<SearchResult[]>;
|
|
189
|
+
|
|
190
|
+
// ========== 分析操作 ==========
|
|
191
|
+
|
|
192
|
+
/** 检测循环依赖 */
|
|
193
|
+
detectCycles(): Promise<Cycle[]>;
|
|
194
|
+
|
|
195
|
+
/** 计算影响范围 */
|
|
196
|
+
calculateImpact(moduleId: string, depth: number): Promise<ImpactResult>;
|
|
197
|
+
|
|
198
|
+
/** 获取项目统计 */
|
|
199
|
+
getStatistics(): Promise<ProjectStatistics>;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/** 存储类型 */
|
|
203
|
+
export type StorageType = 'filesystem' | 'kuzudb' | 'neo4j' | 'memory';
|
|
204
|
+
|
|
205
|
+
/** 搜索选项 */
|
|
206
|
+
export interface SearchOptions {
|
|
207
|
+
type?: 'symbol' | 'module' | 'file';
|
|
208
|
+
language?: string;
|
|
209
|
+
limit?: number;
|
|
210
|
+
offset?: number;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/** 存储配置 */
|
|
214
|
+
export interface StorageConfig {
|
|
215
|
+
type: StorageType | 'auto';
|
|
216
|
+
|
|
217
|
+
// FileSystem 配置
|
|
218
|
+
outputPath?: string;
|
|
219
|
+
|
|
220
|
+
// KùzuDB 配置
|
|
221
|
+
databasePath?: string;
|
|
222
|
+
|
|
223
|
+
// Neo4j 配置
|
|
224
|
+
uri?: string;
|
|
225
|
+
username?: string;
|
|
226
|
+
password?: string;
|
|
227
|
+
|
|
228
|
+
// 自动选择配置
|
|
229
|
+
autoThresholds?: {
|
|
230
|
+
useGraphDBWhenFileCount: number;
|
|
231
|
+
useGraphDBWhenNodeCount: number;
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/** 存储工厂 */
|
|
236
|
+
export interface IStorageFactory {
|
|
237
|
+
create(config: StorageConfig): Promise<IStorage>;
|
|
238
|
+
createForProject(projectPath: string, config: StorageConfig): Promise<IStorage>;
|
|
239
|
+
}
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
### 2.2 语言解析器接口
|
|
243
|
+
|
|
244
|
+
```typescript
|
|
245
|
+
// src/infrastructure/parser/interfaces/ILanguageParser.ts
|
|
246
|
+
|
|
247
|
+
import type { Module, Symbol, Dependency, ParseOptions } from '../../../interface/types/index.js';
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* 语言解析器接口
|
|
251
|
+
* 所有语言解析器必须实现此接口
|
|
252
|
+
*/
|
|
253
|
+
export interface ILanguageParser {
|
|
254
|
+
/** 支持的语言标识 */
|
|
255
|
+
readonly languageId: LanguageId;
|
|
256
|
+
|
|
257
|
+
/** 支持的文件扩展名 */
|
|
258
|
+
readonly fileExtensions: string[];
|
|
259
|
+
|
|
260
|
+
/** 解析器名称 */
|
|
261
|
+
readonly name: string;
|
|
262
|
+
|
|
263
|
+
/** 初始化解析器 */
|
|
264
|
+
initialize(): Promise<void>;
|
|
265
|
+
|
|
266
|
+
/** 释放解析器资源 */
|
|
267
|
+
dispose(): Promise<void>;
|
|
268
|
+
|
|
269
|
+
/** 解析单个文件 */
|
|
270
|
+
parseFile(filePath: string, content: string, options?: ParseOptions): Promise<ParseResult>;
|
|
271
|
+
|
|
272
|
+
/** 批量解析文件 */
|
|
273
|
+
parseFiles(files: Array<{ path: string; content: string }>, options?: ParseOptions): Promise<ParseResult[]>;
|
|
274
|
+
|
|
275
|
+
/** 提取导入信息 */
|
|
276
|
+
extractImports(content: string): Promise<ImportInfo[]>;
|
|
277
|
+
|
|
278
|
+
/** 提取导出信息 */
|
|
279
|
+
extractExports(content: string): Promise<ExportInfo[]>;
|
|
280
|
+
|
|
281
|
+
/** 提取符号信息 */
|
|
282
|
+
extractSymbols(content: string): Promise<SymbolInfo[]>;
|
|
283
|
+
|
|
284
|
+
/** 构建调用图 */
|
|
285
|
+
buildCallGraph(content: string): Promise<CallGraphInfo>;
|
|
286
|
+
|
|
287
|
+
/** 计算复杂度 */
|
|
288
|
+
calculateComplexity(content: string): Promise<ComplexityMetrics>;
|
|
289
|
+
|
|
290
|
+
/** 检测语言特性支持 */
|
|
291
|
+
supportsFeature(feature: LanguageFeature): boolean;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
/** 支持的语言 */
|
|
295
|
+
export type LanguageId =
|
|
296
|
+
| 'typescript' | 'javascript' | 'go' // Phase 1 (已支持)
|
|
297
|
+
| 'python' | 'java' | 'rust' | 'cpp' // Phase 2 (MVP3)
|
|
298
|
+
| 'csharp' | 'ruby' | 'php' // Phase 3
|
|
299
|
+
| 'swift' | 'kotlin' | 'dart' | 'perl'; // Phase 3
|
|
300
|
+
|
|
301
|
+
/** 解析结果 */
|
|
302
|
+
export interface ParseResult {
|
|
303
|
+
/** 文件路径 */
|
|
304
|
+
filePath: string;
|
|
305
|
+
|
|
306
|
+
/** 语言 */
|
|
307
|
+
language: LanguageId;
|
|
308
|
+
|
|
309
|
+
/** 模块信息 */
|
|
310
|
+
module: Module;
|
|
311
|
+
|
|
312
|
+
/** 符号列表 */
|
|
313
|
+
symbols: Symbol[];
|
|
314
|
+
|
|
315
|
+
/** 导入列表 */
|
|
316
|
+
imports: Import[];
|
|
317
|
+
|
|
318
|
+
/** 导出列表 */
|
|
319
|
+
exports: Export[];
|
|
320
|
+
|
|
321
|
+
/** 依赖列表 */
|
|
322
|
+
dependencies: Dependency[];
|
|
323
|
+
|
|
324
|
+
/** 调用图 */
|
|
325
|
+
callGraph?: CallGraph;
|
|
326
|
+
|
|
327
|
+
/** 复杂度指标 */
|
|
328
|
+
complexity?: ComplexityMetrics;
|
|
329
|
+
|
|
330
|
+
/** 解析耗时 (ms) */
|
|
331
|
+
parseTime: number;
|
|
332
|
+
|
|
333
|
+
/** 错误信息 */
|
|
334
|
+
errors?: ParseError[];
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
/** 语言特性 */
|
|
338
|
+
export type LanguageFeature =
|
|
339
|
+
| 'type-inference' // 类型推导
|
|
340
|
+
| 'generic-types' // 泛型
|
|
341
|
+
| 'decorators' // 装饰器
|
|
342
|
+
| 'call-graph' // 调用图
|
|
343
|
+
| 'cross-file-analysis' // 跨文件分析
|
|
344
|
+
| 'complexity-metrics'; // 复杂度指标
|
|
345
|
+
|
|
346
|
+
/** 解析器注册表 */
|
|
347
|
+
export interface IParserRegistry {
|
|
348
|
+
/** 注册解析器 */
|
|
349
|
+
register(parser: ILanguageParser): void;
|
|
350
|
+
|
|
351
|
+
/** 获取解析器 */
|
|
352
|
+
getParser(language: LanguageId): ILanguageParser | undefined;
|
|
353
|
+
getParserByFile(filePath: string): ILanguageParser | undefined;
|
|
354
|
+
|
|
355
|
+
/** 获取所有支持的扩展名 */
|
|
356
|
+
getSupportedExtensions(): string[];
|
|
357
|
+
|
|
358
|
+
/** 获取所有支持的语言 */
|
|
359
|
+
getSupportedLanguages(): LanguageId[];
|
|
360
|
+
}
|
|
361
|
+
```
|
|
362
|
+
|
|
363
|
+
### 2.3 Server 层用例接口
|
|
364
|
+
|
|
365
|
+
```typescript
|
|
366
|
+
// src/server/usecases/IGenerateCodeMap.ts
|
|
367
|
+
|
|
368
|
+
import type { GenerateRequest, GenerateResponse } from '../dto/index.js';
|
|
369
|
+
|
|
370
|
+
/**
|
|
371
|
+
* 生成代码图用例
|
|
372
|
+
*/
|
|
373
|
+
export interface IGenerateCodeMapUseCase {
|
|
374
|
+
execute(request: GenerateRequest): Promise<GenerateResponse>;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
// src/server/dto/GenerateRequest.ts
|
|
378
|
+
export interface GenerateRequest {
|
|
379
|
+
/** 项目根目录 */
|
|
380
|
+
projectPath: string;
|
|
381
|
+
|
|
382
|
+
/** 包含模式 */
|
|
383
|
+
include?: string[];
|
|
384
|
+
|
|
385
|
+
/** 排除模式 */
|
|
386
|
+
exclude?: string[];
|
|
387
|
+
|
|
388
|
+
/** 分析模式 */
|
|
389
|
+
mode: 'fast' | 'smart' | 'hybrid';
|
|
390
|
+
|
|
391
|
+
/** 存储配置 */
|
|
392
|
+
storage?: StorageConfig;
|
|
393
|
+
|
|
394
|
+
/** 输出配置 */
|
|
395
|
+
output?: {
|
|
396
|
+
formats: ('aimap' | 'context' | 'json' | 'deps-graph')[];
|
|
397
|
+
directory: string;
|
|
398
|
+
};
|
|
399
|
+
|
|
400
|
+
/** 是否启用进度回调 */
|
|
401
|
+
enableProgress?: boolean;
|
|
402
|
+
|
|
403
|
+
/** 进度回调 */
|
|
404
|
+
onProgress?: (progress: ProgressInfo) => void;
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
// src/server/dto/GenerateResponse.ts
|
|
408
|
+
export interface GenerateResponse {
|
|
409
|
+
/** 成功状态 */
|
|
410
|
+
success: boolean;
|
|
411
|
+
|
|
412
|
+
/** 代码图 */
|
|
413
|
+
codeGraph: CodeGraph;
|
|
414
|
+
|
|
415
|
+
/** 生成的文件路径 */
|
|
416
|
+
generatedFiles: string[];
|
|
417
|
+
|
|
418
|
+
/** 统计信息 */
|
|
419
|
+
statistics: {
|
|
420
|
+
totalFiles: number;
|
|
421
|
+
parsedFiles: number;
|
|
422
|
+
failedFiles: number;
|
|
423
|
+
totalSymbols: number;
|
|
424
|
+
totalDependencies: number;
|
|
425
|
+
duration: number;
|
|
426
|
+
};
|
|
427
|
+
|
|
428
|
+
/** 警告信息 */
|
|
429
|
+
warnings?: string[];
|
|
430
|
+
|
|
431
|
+
/** 错误信息 */
|
|
432
|
+
error?: string;
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
// 其他用例接口...
|
|
436
|
+
export interface IQuerySymbolUseCase {
|
|
437
|
+
execute(request: QuerySymbolRequest): Promise<QuerySymbolResponse>;
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
export interface IAnalyzeImpactUseCase {
|
|
441
|
+
execute(request: AnalyzeImpactRequest): Promise<AnalyzeImpactResponse>;
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
export interface IDetectCyclesUseCase {
|
|
445
|
+
execute(request: DetectCyclesRequest): Promise<DetectCyclesResponse>;
|
|
446
|
+
}
|
|
447
|
+
```
|
|
448
|
+
|
|
449
|
+
---
|
|
450
|
+
|
|
451
|
+
## 3. 存储实现详解
|
|
452
|
+
|
|
453
|
+
### 3.1 文件系统存储 (默认)
|
|
454
|
+
|
|
455
|
+
```typescript
|
|
456
|
+
// src/infrastructure/storage/implementations/FileSystemStorage.ts
|
|
457
|
+
|
|
458
|
+
export class FileSystemStorage implements IStorage {
|
|
459
|
+
readonly type = 'filesystem';
|
|
460
|
+
private outputPath: string = '';
|
|
461
|
+
private codeGraph: CodeGraph | null = null;
|
|
462
|
+
|
|
463
|
+
async initialize(projectPath: string): Promise<void> {
|
|
464
|
+
this.outputPath = path.join(projectPath, '.mycodemap');
|
|
465
|
+
await fs.mkdir(this.outputPath, { recursive: true });
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
async saveCodeGraph(graph: CodeGraph): Promise<void> {
|
|
469
|
+
// 保存 codemap.json
|
|
470
|
+
const jsonPath = path.join(this.outputPath, 'codemap.json');
|
|
471
|
+
await fs.writeFile(jsonPath, JSON.stringify(graph, null, 2));
|
|
472
|
+
|
|
473
|
+
// 生成 AI_MAP.md
|
|
474
|
+
const aiMap = this.generateAIMap(graph);
|
|
475
|
+
await fs.writeFile(path.join(this.outputPath, 'AI_MAP.md'), aiMap);
|
|
476
|
+
|
|
477
|
+
// 生成 CONTEXT.md
|
|
478
|
+
const context = this.generateContext(graph);
|
|
479
|
+
await fs.writeFile(path.join(this.outputPath, 'CONTEXT.md'), context);
|
|
480
|
+
|
|
481
|
+
this.codeGraph = graph;
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
async loadCodeGraph(): Promise<CodeGraph> {
|
|
485
|
+
if (this.codeGraph) return this.codeGraph;
|
|
486
|
+
|
|
487
|
+
const jsonPath = path.join(this.outputPath, 'codemap.json');
|
|
488
|
+
const content = await fs.readFile(jsonPath, 'utf-8');
|
|
489
|
+
this.codeGraph = JSON.parse(content);
|
|
490
|
+
return this.codeGraph;
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
async findSymbolByName(name: string): Promise<Symbol[]> {
|
|
494
|
+
const graph = await this.loadCodeGraph();
|
|
495
|
+
return graph.modules
|
|
496
|
+
.flatMap(m => m.symbols)
|
|
497
|
+
.filter(s => s.name === name);
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
// ... 其他方法实现
|
|
501
|
+
}
|
|
502
|
+
```
|
|
503
|
+
|
|
504
|
+
### 3.2 KùzuDB 存储
|
|
505
|
+
|
|
506
|
+
```typescript
|
|
507
|
+
// src/infrastructure/storage/implementations/KuzuDBStorage.ts
|
|
508
|
+
|
|
509
|
+
export class KuzuDBStorage implements IStorage {
|
|
510
|
+
readonly type = 'kuzudb';
|
|
511
|
+
private db: kuzu.Database | null = null;
|
|
512
|
+
private conn: kuzu.Connection | null = null;
|
|
513
|
+
private dbPath: string = '';
|
|
514
|
+
|
|
515
|
+
async initialize(projectPath: string): Promise<void> {
|
|
516
|
+
this.dbPath = path.join(projectPath, '.mycodemap', 'graph.db');
|
|
517
|
+
await fs.mkdir(path.dirname(this.dbPath), { recursive: true });
|
|
518
|
+
|
|
519
|
+
this.db = new kuzu.Database(this.dbPath);
|
|
520
|
+
this.conn = new kuzu.Connection(this.db);
|
|
521
|
+
|
|
522
|
+
await this.createSchema();
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
private async createSchema(): Promise<void> {
|
|
526
|
+
// 创建节点表
|
|
527
|
+
await this.conn?.query(`
|
|
528
|
+
CREATE NODE TABLE IF NOT EXISTS Module(
|
|
529
|
+
id STRING PRIMARY KEY,
|
|
530
|
+
path STRING,
|
|
531
|
+
language STRING,
|
|
532
|
+
lines INT64
|
|
533
|
+
)
|
|
534
|
+
`);
|
|
535
|
+
|
|
536
|
+
await this.conn?.query(`
|
|
537
|
+
CREATE NODE TABLE IF NOT EXISTS Symbol(
|
|
538
|
+
id STRING PRIMARY KEY,
|
|
539
|
+
name STRING,
|
|
540
|
+
kind STRING,
|
|
541
|
+
moduleId STRING
|
|
542
|
+
)
|
|
543
|
+
`);
|
|
544
|
+
|
|
545
|
+
// 创建关系表
|
|
546
|
+
await this.conn?.query(`
|
|
547
|
+
CREATE REL TABLE IF NOT EXISTS IMPORTS(
|
|
548
|
+
FROM Module TO Module,
|
|
549
|
+
MANY_MANY
|
|
550
|
+
)
|
|
551
|
+
`);
|
|
552
|
+
|
|
553
|
+
await this.conn?.query(`
|
|
554
|
+
CREATE REL TABLE IF NOT EXISTS CALLS(
|
|
555
|
+
FROM Symbol TO Symbol,
|
|
556
|
+
MANY_MANY
|
|
557
|
+
)
|
|
558
|
+
`);
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
async saveCodeGraph(graph: CodeGraph): Promise<void> {
|
|
562
|
+
// 批量导入节点
|
|
563
|
+
const modules = graph.modules.map(m => ({
|
|
564
|
+
id: m.id,
|
|
565
|
+
path: m.path,
|
|
566
|
+
language: m.language,
|
|
567
|
+
lines: m.stats.lines
|
|
568
|
+
}));
|
|
569
|
+
|
|
570
|
+
await this.bulkInsert('Module', modules);
|
|
571
|
+
|
|
572
|
+
// 批量导入关系
|
|
573
|
+
const imports = graph.modules.flatMap(m =>
|
|
574
|
+
m.dependencies.map(dep => ({
|
|
575
|
+
from: m.id,
|
|
576
|
+
to: dep.targetId
|
|
577
|
+
}))
|
|
578
|
+
);
|
|
579
|
+
|
|
580
|
+
await this.bulkInsertRelations('IMPORTS', imports);
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
async findCallers(functionId: string): Promise<Symbol[]> {
|
|
584
|
+
const result = await this.conn?.query(`
|
|
585
|
+
MATCH (caller:Symbol)-[:CALLS]->(callee:Symbol {id: '${functionId}'})
|
|
586
|
+
RETURN caller.id, caller.name, caller.kind
|
|
587
|
+
`);
|
|
588
|
+
|
|
589
|
+
return result ? this.mapToSymbols(result) : [];
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
async calculateImpact(moduleId: string, depth: number): Promise<ImpactResult> {
|
|
593
|
+
const result = await this.conn?.query(`
|
|
594
|
+
MATCH (start:Module {id: '${moduleId}'})-[:IMPORTS*1..${depth}]->(impact:Module)
|
|
595
|
+
RETURN impact.id, impact.path
|
|
596
|
+
`);
|
|
597
|
+
|
|
598
|
+
return {
|
|
599
|
+
rootModule: moduleId,
|
|
600
|
+
affectedModules: result ? this.mapToModules(result) : [],
|
|
601
|
+
depth
|
|
602
|
+
};
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
// ... 其他方法实现
|
|
606
|
+
}
|
|
607
|
+
```
|
|
608
|
+
|
|
609
|
+
### 3.3 存储工厂
|
|
610
|
+
|
|
611
|
+
```typescript
|
|
612
|
+
// src/infrastructure/storage/StorageFactory.ts
|
|
613
|
+
|
|
614
|
+
export class StorageFactory implements IStorageFactory {
|
|
615
|
+
async create(config: StorageConfig): Promise<IStorage> {
|
|
616
|
+
const type = await this.resolveStorageType(config);
|
|
617
|
+
|
|
618
|
+
switch (type) {
|
|
619
|
+
case 'filesystem':
|
|
620
|
+
return new FileSystemStorage();
|
|
621
|
+
case 'kuzudb':
|
|
622
|
+
return new KuzuDBStorage();
|
|
623
|
+
case 'neo4j':
|
|
624
|
+
return new Neo4jStorage();
|
|
625
|
+
default:
|
|
626
|
+
throw new Error(`Unknown storage type: ${type}`);
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
async createForProject(
|
|
631
|
+
projectPath: string,
|
|
632
|
+
config: StorageConfig
|
|
633
|
+
): Promise<IStorage> {
|
|
634
|
+
const storage = await this.create(config);
|
|
635
|
+
await storage.initialize(projectPath);
|
|
636
|
+
return storage;
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
private async resolveStorageType(config: StorageConfig): Promise<StorageType> {
|
|
640
|
+
if (config.type !== 'auto') {
|
|
641
|
+
return config.type;
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
// 自动选择逻辑
|
|
645
|
+
const thresholds = config.autoThresholds || {
|
|
646
|
+
useGraphDBWhenFileCount: 500,
|
|
647
|
+
useGraphDBWhenNodeCount: 10000
|
|
648
|
+
};
|
|
649
|
+
|
|
650
|
+
const fileCount = await this.estimateFileCount();
|
|
651
|
+
|
|
652
|
+
return fileCount >= thresholds.useGraphDBWhenFileCount
|
|
653
|
+
? 'kuzudb'
|
|
654
|
+
: 'filesystem';
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
private async estimateFileCount(): Promise<number> {
|
|
658
|
+
// 快速估算文件数量
|
|
659
|
+
return 0;
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
```
|
|
663
|
+
|
|
664
|
+
---
|
|
665
|
+
|
|
666
|
+
## 4. 多语言支持实现
|
|
667
|
+
|
|
668
|
+
### 4.1 Tree-sitter 解析器基类
|
|
669
|
+
|
|
670
|
+
```typescript
|
|
671
|
+
// src/infrastructure/parser/implementations/TreeSitterParser.ts
|
|
672
|
+
|
|
673
|
+
import Parser from 'tree-sitter';
|
|
674
|
+
|
|
675
|
+
/**
|
|
676
|
+
* Tree-sitter 解析器基类
|
|
677
|
+
* 所有 Tree-sitter 语言解析器继承此类
|
|
678
|
+
*/
|
|
679
|
+
export abstract class TreeSitterParser implements ILanguageParser {
|
|
680
|
+
protected parser: Parser | null = null;
|
|
681
|
+
protected grammar: any;
|
|
682
|
+
|
|
683
|
+
async initialize(): Promise<void> {
|
|
684
|
+
this.parser = new Parser();
|
|
685
|
+
this.parser.setLanguage(this.grammar);
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
async parseFile(
|
|
689
|
+
filePath: string,
|
|
690
|
+
content: string,
|
|
691
|
+
options?: ParseOptions
|
|
692
|
+
): Promise<ParseResult> {
|
|
693
|
+
if (!this.parser) {
|
|
694
|
+
throw new Error('Parser not initialized');
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
const tree = this.parser.parse(content);
|
|
698
|
+
const root = tree.rootNode;
|
|
699
|
+
|
|
700
|
+
const startTime = performance.now();
|
|
701
|
+
|
|
702
|
+
// 并行提取信息
|
|
703
|
+
const [imports, exports, symbols, callGraph, complexity] = await Promise.all([
|
|
704
|
+
this.extractImports(content),
|
|
705
|
+
this.extractExports(content),
|
|
706
|
+
this.extractSymbols(content),
|
|
707
|
+
options?.includeCallGraph ? this.buildCallGraph(content) : Promise.resolve(undefined),
|
|
708
|
+
options?.includeComplexity ? this.calculateComplexity(content) : Promise.resolve(undefined)
|
|
709
|
+
]);
|
|
710
|
+
|
|
711
|
+
const parseTime = performance.now() - startTime;
|
|
712
|
+
|
|
713
|
+
return {
|
|
714
|
+
filePath,
|
|
715
|
+
language: this.languageId,
|
|
716
|
+
module: this.buildModule(filePath, content, imports, exports),
|
|
717
|
+
symbols,
|
|
718
|
+
imports,
|
|
719
|
+
exports,
|
|
720
|
+
dependencies: this.buildDependencies(imports),
|
|
721
|
+
callGraph,
|
|
722
|
+
complexity,
|
|
723
|
+
parseTime
|
|
724
|
+
};
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
abstract extractImports(content: string): Promise<Import[]>;
|
|
728
|
+
abstract extractExports(content: string): Promise<Export[]>;
|
|
729
|
+
abstract extractSymbols(content: string): Promise<Symbol[]>;
|
|
730
|
+
abstract buildCallGraph(content: string): Promise<CallGraph>;
|
|
731
|
+
abstract calculateComplexity(content: string): Promise<ComplexityMetrics>;
|
|
732
|
+
|
|
733
|
+
// ... 其他方法实现
|
|
734
|
+
}
|
|
735
|
+
```
|
|
736
|
+
|
|
737
|
+
### 4.2 Python 解析器
|
|
738
|
+
|
|
739
|
+
```typescript
|
|
740
|
+
// src/infrastructure/parser/implementations/python/PythonParser.ts
|
|
741
|
+
|
|
742
|
+
import Python from 'tree-sitter-python';
|
|
743
|
+
|
|
744
|
+
export class PythonParser extends TreeSitterParser {
|
|
745
|
+
readonly languageId = 'python';
|
|
746
|
+
readonly fileExtensions = ['.py', '.pyw', '.pyi'];
|
|
747
|
+
readonly name = 'Python Parser';
|
|
748
|
+
protected grammar = Python;
|
|
749
|
+
|
|
750
|
+
async extractImports(content: string): Promise<Import[]> {
|
|
751
|
+
const tree = this.parser!.parse(content);
|
|
752
|
+
const imports: Import[] = [];
|
|
753
|
+
|
|
754
|
+
// 查询 import 语句
|
|
755
|
+
const importQuery = new Parser.Query(Python, `
|
|
756
|
+
(import_statement
|
|
757
|
+
name: (dotted_name) @name)
|
|
758
|
+
|
|
759
|
+
(import_from_statement
|
|
760
|
+
module_name: (dotted_name) @module
|
|
761
|
+
name: (dotted_name) @name)
|
|
762
|
+
`);
|
|
763
|
+
|
|
764
|
+
const matches = importQuery.matches(tree.rootNode);
|
|
765
|
+
|
|
766
|
+
for (const match of matches) {
|
|
767
|
+
// 解析 import 信息
|
|
768
|
+
imports.push(this.parseImportMatch(match));
|
|
769
|
+
}
|
|
770
|
+
|
|
771
|
+
return imports;
|
|
772
|
+
}
|
|
773
|
+
|
|
774
|
+
async extractSymbols(content: string): Promise<Symbol[]> {
|
|
775
|
+
const tree = this.parser!.parse(content);
|
|
776
|
+
const symbols: Symbol[] = [];
|
|
777
|
+
|
|
778
|
+
// 查询类定义
|
|
779
|
+
const classQuery = new Parser.Query(Python, `
|
|
780
|
+
(class_definition
|
|
781
|
+
name: (identifier) @name) @class
|
|
782
|
+
`);
|
|
783
|
+
|
|
784
|
+
// 查询函数定义
|
|
785
|
+
const funcQuery = new Parser.Query(Python, `
|
|
786
|
+
(function_definition
|
|
787
|
+
name: (identifier) @name) @func
|
|
788
|
+
`);
|
|
789
|
+
|
|
790
|
+
// 处理类
|
|
791
|
+
const classMatches = classQuery.matches(tree.rootNode);
|
|
792
|
+
for (const match of classMatches) {
|
|
793
|
+
symbols.push(this.parseClassMatch(match));
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
// 处理函数
|
|
797
|
+
const funcMatches = funcQuery.matches(tree.rootNode);
|
|
798
|
+
for (const match of funcMatches) {
|
|
799
|
+
symbols.push(this.parseFunctionMatch(match));
|
|
800
|
+
}
|
|
801
|
+
|
|
802
|
+
return symbols;
|
|
803
|
+
}
|
|
804
|
+
|
|
805
|
+
supportsFeature(feature: LanguageFeature): boolean {
|
|
806
|
+
const supported: LanguageFeature[] = [
|
|
807
|
+
'type-inference',
|
|
808
|
+
'decorators',
|
|
809
|
+
'call-graph',
|
|
810
|
+
'cross-file-analysis',
|
|
811
|
+
'complexity-metrics'
|
|
812
|
+
];
|
|
813
|
+
return supported.includes(feature);
|
|
814
|
+
}
|
|
815
|
+
|
|
816
|
+
// ... 其他方法实现
|
|
817
|
+
}
|
|
818
|
+
```
|
|
819
|
+
|
|
820
|
+
### 4.3 解析器注册表
|
|
821
|
+
|
|
822
|
+
```typescript
|
|
823
|
+
// src/infrastructure/parser/ParserRegistry.ts
|
|
824
|
+
|
|
825
|
+
export class ParserRegistry implements IParserRegistry {
|
|
826
|
+
private parsers = new Map<LanguageId, ILanguageParser>();
|
|
827
|
+
private extToLanguage = new Map<string, LanguageId>();
|
|
828
|
+
|
|
829
|
+
constructor() {
|
|
830
|
+
this.registerBuiltInParsers();
|
|
831
|
+
}
|
|
832
|
+
|
|
833
|
+
private registerBuiltInParsers(): void {
|
|
834
|
+
// Phase 1: 已支持
|
|
835
|
+
this.register(new TypeScriptParser());
|
|
836
|
+
this.register(new JavaScriptParser());
|
|
837
|
+
this.register(new GoParser());
|
|
838
|
+
|
|
839
|
+
// Phase 2: MVP3 新增
|
|
840
|
+
this.register(new PythonParser());
|
|
841
|
+
this.register(new JavaParser());
|
|
842
|
+
this.register(new RustParser());
|
|
843
|
+
this.register(new CppParser());
|
|
844
|
+
|
|
845
|
+
// Phase 3: 后续添加
|
|
846
|
+
// this.register(new CSharpParser());
|
|
847
|
+
// ...
|
|
848
|
+
}
|
|
849
|
+
|
|
850
|
+
register(parser: ILanguageParser): void {
|
|
851
|
+
this.parsers.set(parser.languageId, parser);
|
|
852
|
+
|
|
853
|
+
for (const ext of parser.fileExtensions) {
|
|
854
|
+
this.extToLanguage.set(ext, parser.languageId);
|
|
855
|
+
}
|
|
856
|
+
}
|
|
857
|
+
|
|
858
|
+
getParser(language: LanguageId): ILanguageParser | undefined {
|
|
859
|
+
return this.parsers.get(language);
|
|
860
|
+
}
|
|
861
|
+
|
|
862
|
+
getParserByFile(filePath: string): ILanguageParser | undefined {
|
|
863
|
+
const ext = path.extname(filePath).toLowerCase();
|
|
864
|
+
const language = this.extToLanguage.get(ext);
|
|
865
|
+
return language ? this.parsers.get(language) : undefined;
|
|
866
|
+
}
|
|
867
|
+
|
|
868
|
+
getSupportedExtensions(): string[] {
|
|
869
|
+
return Array.from(this.extToLanguage.keys());
|
|
870
|
+
}
|
|
871
|
+
|
|
872
|
+
getSupportedLanguages(): LanguageId[] {
|
|
873
|
+
return Array.from(this.parsers.keys());
|
|
874
|
+
}
|
|
875
|
+
}
|
|
876
|
+
```
|
|
877
|
+
|
|
878
|
+
---
|
|
879
|
+
|
|
880
|
+
## 5. Server 层实现
|
|
881
|
+
|
|
882
|
+
### 5.1 CodeMap 服务
|
|
883
|
+
|
|
884
|
+
```typescript
|
|
885
|
+
// src/server/services/CodeMapService.ts
|
|
886
|
+
|
|
887
|
+
export class CodeMapService {
|
|
888
|
+
constructor(
|
|
889
|
+
private storageFactory: IStorageFactory,
|
|
890
|
+
private parserRegistry: IParserRegistry,
|
|
891
|
+
private analysisService: AnalysisService
|
|
892
|
+
) {}
|
|
893
|
+
|
|
894
|
+
async generate(request: GenerateRequest): Promise<GenerateResponse> {
|
|
895
|
+
const startTime = performance.now();
|
|
896
|
+
|
|
897
|
+
try {
|
|
898
|
+
// 1. 初始化存储
|
|
899
|
+
const storage = await this.storageFactory.createForProject(
|
|
900
|
+
request.projectPath,
|
|
901
|
+
request.storage || { type: 'filesystem' }
|
|
902
|
+
);
|
|
903
|
+
|
|
904
|
+
// 2. 发现文件
|
|
905
|
+
const files = await this.discoverFiles(request);
|
|
906
|
+
|
|
907
|
+
// 3. 解析文件
|
|
908
|
+
const parsedModules = await this.parseFiles(files, request, storage);
|
|
909
|
+
|
|
910
|
+
// 4. 构建代码图
|
|
911
|
+
const codeGraph = await this.buildCodeGraph(parsedModules, request);
|
|
912
|
+
|
|
913
|
+
// 5. 保存到存储
|
|
914
|
+
await storage.saveCodeGraph(codeGraph);
|
|
915
|
+
|
|
916
|
+
// 6. 生成输出文件
|
|
917
|
+
const generatedFiles = await this.generateOutputFiles(codeGraph, request);
|
|
918
|
+
|
|
919
|
+
const duration = performance.now() - startTime;
|
|
920
|
+
|
|
921
|
+
return {
|
|
922
|
+
success: true,
|
|
923
|
+
codeGraph,
|
|
924
|
+
generatedFiles,
|
|
925
|
+
statistics: {
|
|
926
|
+
totalFiles: files.length,
|
|
927
|
+
parsedFiles: parsedModules.length,
|
|
928
|
+
failedFiles: files.length - parsedModules.length,
|
|
929
|
+
totalSymbols: codeGraph.modules.reduce((sum, m) => sum + m.symbols.length, 0),
|
|
930
|
+
totalDependencies: codeGraph.dependencies.length,
|
|
931
|
+
duration
|
|
932
|
+
}
|
|
933
|
+
};
|
|
934
|
+
} catch (error) {
|
|
935
|
+
return {
|
|
936
|
+
success: false,
|
|
937
|
+
codeGraph: {} as CodeGraph,
|
|
938
|
+
generatedFiles: [],
|
|
939
|
+
statistics: {
|
|
940
|
+
totalFiles: 0,
|
|
941
|
+
parsedFiles: 0,
|
|
942
|
+
failedFiles: 0,
|
|
943
|
+
totalSymbols: 0,
|
|
944
|
+
totalDependencies: 0,
|
|
945
|
+
duration: performance.now() - startTime
|
|
946
|
+
},
|
|
947
|
+
error: error instanceof Error ? error.message : String(error)
|
|
948
|
+
};
|
|
949
|
+
}
|
|
950
|
+
}
|
|
951
|
+
|
|
952
|
+
private async discoverFiles(request: GenerateRequest): Promise<string[]> {
|
|
953
|
+
const include = request.include || ['src/**/*'];
|
|
954
|
+
const exclude = request.exclude || ['node_modules/**', 'dist/**'];
|
|
955
|
+
|
|
956
|
+
// 支持多语言扩展名
|
|
957
|
+
const extensions = this.parserRegistry.getSupportedExtensions();
|
|
958
|
+
const patterns = include.map(i =>
|
|
959
|
+
extensions.map(ext => `${i}${ext}`)
|
|
960
|
+
).flat();
|
|
961
|
+
|
|
962
|
+
return globby(patterns, {
|
|
963
|
+
cwd: request.projectPath,
|
|
964
|
+
ignore: exclude,
|
|
965
|
+
absolute: true
|
|
966
|
+
});
|
|
967
|
+
}
|
|
968
|
+
|
|
969
|
+
private async parseFiles(
|
|
970
|
+
files: string[],
|
|
971
|
+
request: GenerateRequest,
|
|
972
|
+
storage: IStorage
|
|
973
|
+
): Promise<Module[]> {
|
|
974
|
+
const modules: Module[] = [];
|
|
975
|
+
const batchSize = 50;
|
|
976
|
+
|
|
977
|
+
for (let i = 0; i < files.length; i += batchSize) {
|
|
978
|
+
const batch = files.slice(i, i + batchSize);
|
|
979
|
+
|
|
980
|
+
// 并行解析批次
|
|
981
|
+
const results = await Promise.all(
|
|
982
|
+
batch.map(async (file) => {
|
|
983
|
+
const parser = this.parserRegistry.getParserByFile(file);
|
|
984
|
+
if (!parser) return null;
|
|
985
|
+
|
|
986
|
+
try {
|
|
987
|
+
const content = await fs.readFile(file, 'utf-8');
|
|
988
|
+
const result = await parser.parseFile(file, content, {
|
|
989
|
+
includeCallGraph: request.mode === 'smart',
|
|
990
|
+
includeComplexity: request.mode === 'smart'
|
|
991
|
+
});
|
|
992
|
+
return result.module;
|
|
993
|
+
} catch (error) {
|
|
994
|
+
console.warn(`Failed to parse ${file}:`, error);
|
|
995
|
+
return null;
|
|
996
|
+
}
|
|
997
|
+
})
|
|
998
|
+
);
|
|
999
|
+
|
|
1000
|
+
modules.push(...results.filter((m): m is Module => m !== null));
|
|
1001
|
+
|
|
1002
|
+
// 进度回调
|
|
1003
|
+
if (request.onProgress) {
|
|
1004
|
+
request.onProgress({
|
|
1005
|
+
current: Math.min(i + batchSize, files.length),
|
|
1006
|
+
total: files.length,
|
|
1007
|
+
percentage: Math.round(((i + batchSize) / files.length) * 100),
|
|
1008
|
+
currentFile: batch[batch.length - 1]
|
|
1009
|
+
});
|
|
1010
|
+
}
|
|
1011
|
+
}
|
|
1012
|
+
|
|
1013
|
+
return modules;
|
|
1014
|
+
}
|
|
1015
|
+
|
|
1016
|
+
// ... 其他方法
|
|
1017
|
+
}
|
|
1018
|
+
```
|
|
1019
|
+
|
|
1020
|
+
---
|
|
1021
|
+
|
|
1022
|
+
## 6. CLI 可视化实现
|
|
1023
|
+
|
|
1024
|
+
### 6.1 树形可视化
|
|
1025
|
+
|
|
1026
|
+
```typescript
|
|
1027
|
+
// src/cli/visualizers/TreeVisualizer.ts
|
|
1028
|
+
|
|
1029
|
+
import chalk from 'chalk';
|
|
1030
|
+
import { TreeNode, TreeOptions } from './types.js';
|
|
1031
|
+
|
|
1032
|
+
export class TreeVisualizer {
|
|
1033
|
+
private options: TreeOptions;
|
|
1034
|
+
|
|
1035
|
+
constructor(options: TreeOptions = {}) {
|
|
1036
|
+
this.options = {
|
|
1037
|
+
maxDepth: options.maxDepth ?? Infinity,
|
|
1038
|
+
showIcons: options.showIcons ?? true,
|
|
1039
|
+
showSize: options.showSize ?? false,
|
|
1040
|
+
...options
|
|
1041
|
+
};
|
|
1042
|
+
}
|
|
1043
|
+
|
|
1044
|
+
visualize(root: TreeNode): string {
|
|
1045
|
+
return this.renderNode(root, '', 0, true);
|
|
1046
|
+
}
|
|
1047
|
+
|
|
1048
|
+
private renderNode(
|
|
1049
|
+
node: TreeNode,
|
|
1050
|
+
prefix: string,
|
|
1051
|
+
depth: number,
|
|
1052
|
+
isLast: boolean
|
|
1053
|
+
): string {
|
|
1054
|
+
if (depth > this.options.maxDepth!) {
|
|
1055
|
+
return '';
|
|
1056
|
+
}
|
|
1057
|
+
|
|
1058
|
+
const lines: string[] = [];
|
|
1059
|
+
|
|
1060
|
+
// 当前节点行
|
|
1061
|
+
const icon = this.getIcon(node);
|
|
1062
|
+
const name = this.formatName(node);
|
|
1063
|
+
const size = this.options.showSize && node.size
|
|
1064
|
+
? chalk.gray(` (${this.formatSize(node.size)})`)
|
|
1065
|
+
: '';
|
|
1066
|
+
|
|
1067
|
+
const line = prefix + (isLast ? '└── ' : '├── ') + icon + name + size;
|
|
1068
|
+
lines.push(line);
|
|
1069
|
+
|
|
1070
|
+
// 子节点
|
|
1071
|
+
if (node.children && node.children.length > 0) {
|
|
1072
|
+
const childPrefix = prefix + (isLast ? ' ' : '│ ');
|
|
1073
|
+
|
|
1074
|
+
for (let i = 0; i < node.children.length; i++) {
|
|
1075
|
+
const child = node.children[i];
|
|
1076
|
+
const childIsLast = i === node.children.length - 1;
|
|
1077
|
+
lines.push(this.renderNode(child, childPrefix, depth + 1, childIsLast));
|
|
1078
|
+
}
|
|
1079
|
+
}
|
|
1080
|
+
|
|
1081
|
+
return lines.join('\n');
|
|
1082
|
+
}
|
|
1083
|
+
|
|
1084
|
+
private getIcon(node: TreeNode): string {
|
|
1085
|
+
if (!this.options.showIcons) return '';
|
|
1086
|
+
|
|
1087
|
+
if (node.type === 'directory') {
|
|
1088
|
+
return chalk.blue('📁 ');
|
|
1089
|
+
}
|
|
1090
|
+
|
|
1091
|
+
// 根据扩展名返回图标
|
|
1092
|
+
const ext = node.name.split('.').pop()?.toLowerCase();
|
|
1093
|
+
const iconMap: Record<string, string> = {
|
|
1094
|
+
'ts': '📘 ',
|
|
1095
|
+
'js': '📒 ',
|
|
1096
|
+
'py': '🐍 ',
|
|
1097
|
+
'java': '☕ ',
|
|
1098
|
+
'go': '🐹 ',
|
|
1099
|
+
'rs': '🦀 ',
|
|
1100
|
+
'json': '📋 ',
|
|
1101
|
+
'md': '📝 ',
|
|
1102
|
+
'default': '📄 '
|
|
1103
|
+
};
|
|
1104
|
+
|
|
1105
|
+
return iconMap[ext || 'default'] || iconMap.default;
|
|
1106
|
+
}
|
|
1107
|
+
|
|
1108
|
+
private formatName(node: TreeNode): string {
|
|
1109
|
+
if (node.highlight) {
|
|
1110
|
+
return chalk.yellow(node.name);
|
|
1111
|
+
}
|
|
1112
|
+
return node.name;
|
|
1113
|
+
}
|
|
1114
|
+
|
|
1115
|
+
private formatSize(bytes: number): string {
|
|
1116
|
+
if (bytes < 1024) return `${bytes}B`;
|
|
1117
|
+
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)}KB`;
|
|
1118
|
+
return `${(bytes / (1024 * 1024)).toFixed(1)}MB`;
|
|
1119
|
+
}
|
|
1120
|
+
}
|
|
1121
|
+
```
|
|
1122
|
+
|
|
1123
|
+
### 6.2 ASCII 依赖图
|
|
1124
|
+
|
|
1125
|
+
```typescript
|
|
1126
|
+
// src/cli/visualizers/GraphVisualizer.ts
|
|
1127
|
+
|
|
1128
|
+
export class GraphVisualizer {
|
|
1129
|
+
visualize(graph: DependencyGraph): string {
|
|
1130
|
+
const lines: string[] = [];
|
|
1131
|
+
const visited = new Set<string>();
|
|
1132
|
+
|
|
1133
|
+
// 找到根节点
|
|
1134
|
+
const roots = this.findRootNodes(graph);
|
|
1135
|
+
|
|
1136
|
+
for (const root of roots) {
|
|
1137
|
+
lines.push(...this.renderSubgraph(root, graph, visited, '', true));
|
|
1138
|
+
}
|
|
1139
|
+
|
|
1140
|
+
return lines.join('\n');
|
|
1141
|
+
}
|
|
1142
|
+
|
|
1143
|
+
private renderSubgraph(
|
|
1144
|
+
nodeId: string,
|
|
1145
|
+
graph: DependencyGraph,
|
|
1146
|
+
visited: Set<string>,
|
|
1147
|
+
prefix: string,
|
|
1148
|
+
isLast: boolean
|
|
1149
|
+
): string[] {
|
|
1150
|
+
const lines: string[] = [];
|
|
1151
|
+
|
|
1152
|
+
if (visited.has(nodeId)) {
|
|
1153
|
+
lines.push(prefix + (isLast ? '└── ' : '├── ') + chalk.gray(`${nodeId} (circular)`));
|
|
1154
|
+
return lines;
|
|
1155
|
+
}
|
|
1156
|
+
|
|
1157
|
+
visited.add(nodeId);
|
|
1158
|
+
|
|
1159
|
+
const node = graph.nodes[nodeId];
|
|
1160
|
+
const deps = graph.edges.filter(e => e.from === nodeId);
|
|
1161
|
+
|
|
1162
|
+
lines.push(prefix + (isLast ? '└── ' : '├── ') + node.path);
|
|
1163
|
+
|
|
1164
|
+
for (let i = 0; i < deps.length; i++) {
|
|
1165
|
+
const dep = deps[i];
|
|
1166
|
+
const childPrefix = prefix + (isLast ? ' ' : '│ ');
|
|
1167
|
+
const childIsLast = i === deps.length - 1;
|
|
1168
|
+
|
|
1169
|
+
lines.push(...this.renderSubgraph(dep.to, graph, new Set(visited), childPrefix, childIsLast));
|
|
1170
|
+
}
|
|
1171
|
+
|
|
1172
|
+
return lines;
|
|
1173
|
+
}
|
|
1174
|
+
|
|
1175
|
+
private findRootNodes(graph: DependencyGraph): string[] {
|
|
1176
|
+
// 找到没有入边的节点
|
|
1177
|
+
const hasIncoming = new Set(graph.edges.map(e => e.to));
|
|
1178
|
+
return Object.keys(graph.nodes).filter(id => !hasIncoming.has(id));
|
|
1179
|
+
}
|
|
1180
|
+
}
|
|
1181
|
+
```
|
|
1182
|
+
|
|
1183
|
+
### 6.3 热力图
|
|
1184
|
+
|
|
1185
|
+
```typescript
|
|
1186
|
+
// src/cli/visualizers/HeatmapVisualizer.ts
|
|
1187
|
+
|
|
1188
|
+
import chalk from 'chalk';
|
|
1189
|
+
|
|
1190
|
+
export class HeatmapVisualizer {
|
|
1191
|
+
visualize(items: HeatmapItem[]): string {
|
|
1192
|
+
// 按值排序
|
|
1193
|
+
const sorted = [...items].sort((a, b) => b.value - a.value);
|
|
1194
|
+
|
|
1195
|
+
// 计算最大值用于归一化
|
|
1196
|
+
const maxValue = Math.max(...items.map(i => i.value));
|
|
1197
|
+
|
|
1198
|
+
const lines: string[] = [];
|
|
1199
|
+
|
|
1200
|
+
for (const item of sorted) {
|
|
1201
|
+
const intensity = item.value / maxValue;
|
|
1202
|
+
const color = this.getColor(intensity);
|
|
1203
|
+
const bar = this.renderBar(intensity);
|
|
1204
|
+
|
|
1205
|
+
lines.push(`${color(item.name.padEnd(40))} ${bar} ${chalk.gray(item.value)}`);
|
|
1206
|
+
}
|
|
1207
|
+
|
|
1208
|
+
return lines.join('\n');
|
|
1209
|
+
}
|
|
1210
|
+
|
|
1211
|
+
private getColor(intensity: number): chalk.Chalk {
|
|
1212
|
+
if (intensity > 0.75) return chalk.red;
|
|
1213
|
+
if (intensity > 0.5) return chalk.yellow;
|
|
1214
|
+
if (intensity > 0.25) return chalk.green;
|
|
1215
|
+
return chalk.blue;
|
|
1216
|
+
}
|
|
1217
|
+
|
|
1218
|
+
private renderBar(intensity: number): string {
|
|
1219
|
+
const width = 20;
|
|
1220
|
+
const filled = Math.round(intensity * width);
|
|
1221
|
+
const empty = width - filled;
|
|
1222
|
+
|
|
1223
|
+
return '█'.repeat(filled) + '░'.repeat(empty);
|
|
1224
|
+
}
|
|
1225
|
+
}
|
|
1226
|
+
|
|
1227
|
+
interface HeatmapItem {
|
|
1228
|
+
name: string;
|
|
1229
|
+
value: number;
|
|
1230
|
+
}
|
|
1231
|
+
```
|
|
1232
|
+
|
|
1233
|
+
---
|
|
1234
|
+
|
|
1235
|
+
## 7. 测试策略
|
|
1236
|
+
|
|
1237
|
+
### 7.1 分层测试
|
|
1238
|
+
|
|
1239
|
+
```
|
|
1240
|
+
┌─────────────────────────────────────────────────────────────────┐
|
|
1241
|
+
│ 测试金字塔 │
|
|
1242
|
+
├─────────────────────────────────────────────────────────────────┤
|
|
1243
|
+
│ │
|
|
1244
|
+
│ ┌───────────┐ │
|
|
1245
|
+
│ │ E2E Tests │ < 10 个场景 │
|
|
1246
|
+
│ │ (CLI) │ │
|
|
1247
|
+
│ └─────┬─────┘ │
|
|
1248
|
+
│ │ │
|
|
1249
|
+
│ ┌────────┴────────┐ │
|
|
1250
|
+
│ │ Integration Tests│ < 50 个场景 │
|
|
1251
|
+
│ │ (Server + Infra) │ │
|
|
1252
|
+
│ └────────┬────────┘ │
|
|
1253
|
+
│ │ │
|
|
1254
|
+
│ ┌─────────────┴─────────────┐ │
|
|
1255
|
+
│ │ Unit Tests │ > 500 个测试 │
|
|
1256
|
+
│ │ (Domain + Infrastructure) │ │
|
|
1257
|
+
│ └───────────────────────────┘ │
|
|
1258
|
+
│ │
|
|
1259
|
+
└─────────────────────────────────────────────────────────────────┘
|
|
1260
|
+
```
|
|
1261
|
+
|
|
1262
|
+
### 7.2 关键测试用例
|
|
1263
|
+
|
|
1264
|
+
```typescript
|
|
1265
|
+
// 存储抽象测试示例
|
|
1266
|
+
describe('IStorage', () => {
|
|
1267
|
+
const implementations = [
|
|
1268
|
+
{ name: 'FileSystemStorage', factory: () => new FileSystemStorage() },
|
|
1269
|
+
{ name: 'KuzuDBStorage', factory: () => new KuzuDBStorage() },
|
|
1270
|
+
];
|
|
1271
|
+
|
|
1272
|
+
for (const { name, factory } of implementations) {
|
|
1273
|
+
describe(name, () => {
|
|
1274
|
+
let storage: IStorage;
|
|
1275
|
+
|
|
1276
|
+
beforeEach(async () => {
|
|
1277
|
+
storage = factory();
|
|
1278
|
+
await storage.initialize('/tmp/test-project');
|
|
1279
|
+
});
|
|
1280
|
+
|
|
1281
|
+
it('should save and load code graph', async () => {
|
|
1282
|
+
const graph = createMockCodeGraph();
|
|
1283
|
+
await storage.saveCodeGraph(graph);
|
|
1284
|
+
const loaded = await storage.loadCodeGraph();
|
|
1285
|
+
expect(loaded).toEqual(graph);
|
|
1286
|
+
});
|
|
1287
|
+
|
|
1288
|
+
it('should find symbols by name', async () => {
|
|
1289
|
+
// ... 测试实现
|
|
1290
|
+
});
|
|
1291
|
+
|
|
1292
|
+
it('should detect cycles', async () => {
|
|
1293
|
+
// ... 测试实现
|
|
1294
|
+
});
|
|
1295
|
+
});
|
|
1296
|
+
}
|
|
1297
|
+
});
|
|
1298
|
+
```
|
|
1299
|
+
|
|
1300
|
+
---
|
|
1301
|
+
|
|
1302
|
+
## 8. 迁移计划
|
|
1303
|
+
|
|
1304
|
+
### 8.1 代码迁移清单
|
|
1305
|
+
|
|
1306
|
+
| 原文件 | 新位置 | 变更类型 |
|
|
1307
|
+
|--------|--------|----------|
|
|
1308
|
+
| `src/types/index.ts` | `src/interface/types/index.ts` | 移动 |
|
|
1309
|
+
| `src/core/analyzer.ts` | `src/domain/services/AnalysisService.ts` | 重构 |
|
|
1310
|
+
| `src/parser/index.ts` | `src/infrastructure/parser/` | 拆分 |
|
|
1311
|
+
| `src/cache/*.ts` | `src/infrastructure/cache/` | 移动 |
|
|
1312
|
+
| `src/generator/*.ts` | `src/server/services/` | 重构 |
|
|
1313
|
+
| `src/cli/commands/*.ts` | `src/cli/commands/` | 适配新接口 |
|
|
1314
|
+
|
|
1315
|
+
### 8.2 向后兼容方案
|
|
1316
|
+
|
|
1317
|
+
```typescript
|
|
1318
|
+
// src/index.ts - 保持向后兼容的导出
|
|
1319
|
+
|
|
1320
|
+
// 旧 API 兼容层
|
|
1321
|
+
export { analyze } from './domain/services/AnalysisService.js';
|
|
1322
|
+
export { parseFile } from './infrastructure/parser/adapters/LegacyParserAdapter.js';
|
|
1323
|
+
|
|
1324
|
+
// 新 API
|
|
1325
|
+
export { CodeMapService } from './server/services/CodeMapService.js';
|
|
1326
|
+
export { StorageFactory } from './infrastructure/storage/StorageFactory.js';
|
|
1327
|
+
export { ParserRegistry } from './infrastructure/parser/ParserRegistry.js';
|
|
1328
|
+
```
|
|
1329
|
+
|
|
1330
|
+
---
|
|
1331
|
+
|
|
1332
|
+
## 9. 依赖列表
|
|
1333
|
+
|
|
1334
|
+
### 9.1 新增依赖
|
|
1335
|
+
|
|
1336
|
+
```json
|
|
1337
|
+
{
|
|
1338
|
+
"dependencies": {
|
|
1339
|
+
"kuzu": "^0.8.0",
|
|
1340
|
+
"neo4j-driver": "^5.28.0",
|
|
1341
|
+
"tree-sitter": "^0.22.0",
|
|
1342
|
+
"tree-sitter-python": "^0.23.0",
|
|
1343
|
+
"tree-sitter-java": "^0.23.0",
|
|
1344
|
+
"tree-sitter-rust": "^0.23.0",
|
|
1345
|
+
"tree-sitter-cpp": "^0.23.0",
|
|
1346
|
+
"chalk": "^5.4.0",
|
|
1347
|
+
"cli-progress": "^3.12.0",
|
|
1348
|
+
"ink": "^5.1.0"
|
|
1349
|
+
},
|
|
1350
|
+
"devDependencies": {
|
|
1351
|
+
"@types/cli-progress": "^3.11.6"
|
|
1352
|
+
}
|
|
1353
|
+
}
|
|
1354
|
+
```
|
|
1355
|
+
|
|
1356
|
+
---
|
|
1357
|
+
|
|
1358
|
+
## 10. 附录
|
|
1359
|
+
|
|
1360
|
+
### 10.1 术语定义
|
|
1361
|
+
|
|
1362
|
+
| 术语 | 定义 |
|
|
1363
|
+
|------|------|
|
|
1364
|
+
| Server Layer | 应用服务层,协调用例和基础设施 |
|
|
1365
|
+
| Storage Abstraction | 存储抽象,支持多种后端实现 |
|
|
1366
|
+
| Tree-sitter | 增量解析器生成工具 |
|
|
1367
|
+
| KùzuDB | 高性能嵌入式图数据库 |
|
|
1368
|
+
| CLI Visualizer | 命令行可视化组件 |
|
|
1369
|
+
|
|
1370
|
+
### 10.2 参考资料
|
|
1371
|
+
|
|
1372
|
+
- [KùzuDB Documentation](https://docs.kuzudb.com/)
|
|
1373
|
+
- [Tree-sitter Documentation](https://tree-sitter.github.io/tree-sitter/)
|
|
1374
|
+
- [Clean Architecture](https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html)
|