@didnhdj/fnmap 0.2.0 → 0.2.1
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/README.md +62 -58
- package/README_CN.md +60 -56
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/index.js +23 -23
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
# fnmap
|
|
2
2
|
|
|
3
|
+
[](https://www.npmjs.com/package/@didnhdj/fnmap)
|
|
4
|
+
[](https://www.npmjs.com/package/@didnhdj/fnmap)
|
|
5
|
+
|
|
3
6
|
> AI code indexing tool for analyzing JS/TS code structure and generating structured code maps
|
|
4
7
|
|
|
5
8
|
[中文文档](./README_CN.md)
|
|
@@ -9,7 +12,6 @@
|
|
|
9
12
|
- 🚀 **Fast Analysis**: Quickly analyze JavaScript/TypeScript code structure using AST
|
|
10
13
|
- 📊 **Structured Output**: Generate `.fnmap` index files with imports, functions, classes, and constants
|
|
11
14
|
- 🔗 **Call Graph**: Track function call relationships and dependencies
|
|
12
|
-
- 📈 **Mermaid Diagrams**: Generate visual call graphs in Mermaid format
|
|
13
15
|
- 🎯 **Git Integration**: Process only changed files for efficient workflows
|
|
14
16
|
- ⚙️ **Flexible Configuration**: Support for multiple configuration methods
|
|
15
17
|
- 🔌 **Pre-commit Hook**: Integrate seamlessly with git hooks
|
|
@@ -37,7 +39,20 @@ npm install --save-dev @didnhdj/fnmap
|
|
|
37
39
|
fnmap --init
|
|
38
40
|
```
|
|
39
41
|
|
|
40
|
-
This
|
|
42
|
+
This interactive command will:
|
|
43
|
+
1. Create a `.fnmaprc` configuration file in your project root
|
|
44
|
+
2. Add fnmap-generated files to `.gitignore` (optional)
|
|
45
|
+
3. Automatically append fnmap format documentation to:
|
|
46
|
+
- `CLAUDE.md` (project-level instructions for Claude AI)
|
|
47
|
+
- `~/.claude/CLAUDE.md` (user-level global instructions)
|
|
48
|
+
- `AGENTS.md` (project-level agent instructions)
|
|
49
|
+
- Or any custom file path you specify
|
|
50
|
+
|
|
51
|
+
The appended documentation helps AI assistants (like Claude) understand the `.fnmap` format, enabling them to:
|
|
52
|
+
- Quickly navigate your codebase using the index
|
|
53
|
+
- Locate functions and classes by line number
|
|
54
|
+
- Understand code structure and call graphs
|
|
55
|
+
- Make more informed code suggestions
|
|
41
56
|
|
|
42
57
|
### Basic Usage
|
|
43
58
|
|
|
@@ -55,17 +70,13 @@ fnmap --files index.js,utils.js
|
|
|
55
70
|
fnmap --changed
|
|
56
71
|
|
|
57
72
|
# Process git staged files (for pre-commit hook)
|
|
58
|
-
fnmap --staged
|
|
59
|
-
```
|
|
73
|
+
fnmap --staged
|
|
60
74
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
```bash
|
|
64
|
-
# Generate file-level Mermaid diagrams
|
|
65
|
-
fnmap --mermaid file --dir src
|
|
75
|
+
# Show detailed processing logs
|
|
76
|
+
fnmap --log --dir src
|
|
66
77
|
|
|
67
|
-
#
|
|
68
|
-
fnmap --
|
|
78
|
+
# Clear generated files
|
|
79
|
+
fnmap --clear
|
|
69
80
|
```
|
|
70
81
|
|
|
71
82
|
## Configuration
|
|
@@ -133,25 +144,6 @@ The `.fnmap` file contains structured information about your code:
|
|
|
133
144
|
- ` +methodName(params) line description →calls` - Static method
|
|
134
145
|
- `CONSTANT_NAME line description` - Constant definition
|
|
135
146
|
|
|
136
|
-
### Mermaid Call Graph
|
|
137
|
-
|
|
138
|
-
When using `--mermaid` option, generates visual call graphs:
|
|
139
|
-
|
|
140
|
-
**File-level** (`filename.mermaid`):
|
|
141
|
-
```mermaid
|
|
142
|
-
flowchart TD
|
|
143
|
-
subgraph utils["utils"]
|
|
144
|
-
readConfig["readConfig"]
|
|
145
|
-
parseData["parseData"]
|
|
146
|
-
saveFile["saveFile"]
|
|
147
|
-
end
|
|
148
|
-
readConfig --> parseData
|
|
149
|
-
saveFile --> parseData
|
|
150
|
-
```
|
|
151
|
-
|
|
152
|
-
**Project-level** (`.fnmap.mermaid`):
|
|
153
|
-
Shows call relationships across all files in the project.
|
|
154
|
-
|
|
155
147
|
## CLI Options
|
|
156
148
|
|
|
157
149
|
```
|
|
@@ -159,14 +151,15 @@ Usage: fnmap [options] [files...]
|
|
|
159
151
|
|
|
160
152
|
Options:
|
|
161
153
|
-v, --version Show version number
|
|
162
|
-
-f, --files <files> Process
|
|
154
|
+
-f, --files <files> Process specified files (comma-separated)
|
|
163
155
|
-d, --dir <dir> Process all code files in directory
|
|
164
156
|
-p, --project <dir> Specify project root directory (default: current directory)
|
|
165
|
-
-c, --changed Process git changed files (staged + modified + untracked)
|
|
166
|
-
-s, --staged Process git staged files (for pre-commit hook)
|
|
157
|
+
-c, --changed Process only git changed files (staged + modified + untracked)
|
|
158
|
+
-s, --staged Process only git staged files (for pre-commit hook)
|
|
167
159
|
-m, --mermaid [mode] Generate Mermaid call graph (file=file-level, project=project-level)
|
|
168
|
-
-
|
|
169
|
-
--init Create default
|
|
160
|
+
-l, --log Show detailed processing logs
|
|
161
|
+
--init Create default config file and setup project (interactive)
|
|
162
|
+
--clear Clear generated files (.fnmap, *.fnmap, *.mermaid)
|
|
170
163
|
-h, --help Display help information
|
|
171
164
|
```
|
|
172
165
|
|
|
@@ -239,19 +232,43 @@ interface FileInfo {
|
|
|
239
232
|
|
|
240
233
|
## Use Cases
|
|
241
234
|
|
|
242
|
-
### 1.
|
|
235
|
+
### 1. AI Assistant Integration
|
|
236
|
+
|
|
237
|
+
fnmap is designed to help AI coding assistants understand your codebase better:
|
|
238
|
+
|
|
239
|
+
```bash
|
|
240
|
+
# Initialize and setup AI documentation
|
|
241
|
+
fnmap --init
|
|
242
|
+
|
|
243
|
+
# Generate code index for your project
|
|
244
|
+
fnmap --dir src
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
The `.fnmap` files help AI assistants:
|
|
248
|
+
- Navigate large codebases efficiently
|
|
249
|
+
- Find specific functions/classes by name and line number
|
|
250
|
+
- Understand module dependencies and call graphs
|
|
251
|
+
- Provide context-aware code suggestions
|
|
252
|
+
|
|
253
|
+
**Recommended workflow with Claude Code or similar AI tools:**
|
|
254
|
+
1. Run `fnmap --init` to add format documentation to `CLAUDE.md`
|
|
255
|
+
2. Generate index files with `fnmap --dir src`
|
|
256
|
+
3. AI assistants will automatically read `.fnmap` files for context
|
|
257
|
+
4. Update index when code changes with `fnmap --changed`
|
|
258
|
+
|
|
259
|
+
### 2. Pre-commit Hook
|
|
243
260
|
|
|
244
261
|
Add to `.husky/pre-commit` or `.git/hooks/pre-commit`:
|
|
245
262
|
|
|
246
263
|
```bash
|
|
247
264
|
#!/bin/sh
|
|
248
|
-
fnmap --staged
|
|
265
|
+
fnmap --staged
|
|
249
266
|
git add .fnmap
|
|
250
267
|
```
|
|
251
268
|
|
|
252
269
|
This automatically updates the `.fnmap` index when committing code.
|
|
253
270
|
|
|
254
|
-
###
|
|
271
|
+
### 3. CI/CD Integration
|
|
255
272
|
|
|
256
273
|
```yaml
|
|
257
274
|
# .github/workflows/ci.yml
|
|
@@ -262,23 +279,14 @@ This automatically updates the `.fnmap` index when committing code.
|
|
|
262
279
|
git diff --exit-code .fnmap || echo "Code index updated"
|
|
263
280
|
```
|
|
264
281
|
|
|
265
|
-
###
|
|
282
|
+
### 4. Code Review
|
|
266
283
|
|
|
267
284
|
```bash
|
|
268
285
|
# Generate index for changed files
|
|
269
286
|
fnmap --changed
|
|
270
287
|
|
|
271
|
-
#
|
|
272
|
-
fnmap --
|
|
273
|
-
```
|
|
274
|
-
|
|
275
|
-
### 4. Documentation Generation
|
|
276
|
-
|
|
277
|
-
```bash
|
|
278
|
-
# Generate project-level call graph
|
|
279
|
-
fnmap --mermaid project
|
|
280
|
-
|
|
281
|
-
# Use the .fnmap.mermaid file in your documentation
|
|
288
|
+
# Show detailed logs during analysis
|
|
289
|
+
fnmap --log --changed
|
|
282
290
|
```
|
|
283
291
|
|
|
284
292
|
## Supported File Types
|
|
@@ -305,7 +313,6 @@ To ensure performance and safety, fnmap has the following default limits:
|
|
|
305
313
|
2. **Structure Analysis**: Traverses AST to extract imports, functions, classes, constants
|
|
306
314
|
3. **Call Graph**: Tracks function call relationships and dependencies
|
|
307
315
|
4. **Index Generation**: Generates compact `.fnmap` files with structured information
|
|
308
|
-
5. **Visualization**: Optionally generates Mermaid diagrams for visual representation
|
|
309
316
|
|
|
310
317
|
## Examples
|
|
311
318
|
|
|
@@ -332,17 +339,14 @@ Complete! Analyzed: 1, Failed: 0
|
|
|
332
339
|
==================================================
|
|
333
340
|
```
|
|
334
341
|
|
|
335
|
-
### Example 2: Analyze Directory
|
|
342
|
+
### Example 2: Analyze Directory
|
|
336
343
|
|
|
337
344
|
```bash
|
|
338
|
-
fnmap --dir src
|
|
345
|
+
fnmap --dir src
|
|
339
346
|
```
|
|
340
347
|
|
|
341
348
|
Generates:
|
|
342
|
-
- `src/.fnmap` - Code index
|
|
343
|
-
- `src/utils.mermaid` - Call graph for utils.js
|
|
344
|
-
- `src/parser.mermaid` - Call graph for parser.js
|
|
345
|
-
- etc.
|
|
349
|
+
- `src/.fnmap` - Code index for all files in src directory
|
|
346
350
|
|
|
347
351
|
### Example 3: Git Workflow
|
|
348
352
|
|
|
@@ -351,7 +355,7 @@ Generates:
|
|
|
351
355
|
git add .
|
|
352
356
|
|
|
353
357
|
# Generate index for staged files
|
|
354
|
-
fnmap --staged
|
|
358
|
+
fnmap --staged
|
|
355
359
|
|
|
356
360
|
# Add updated index
|
|
357
361
|
git add .fnmap
|
package/README_CN.md
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
# fnmap
|
|
2
2
|
|
|
3
|
+
[](https://www.npmjs.com/package/@didnhdj/fnmap)
|
|
4
|
+
[](https://www.npmjs.com/package/@didnhdj/fnmap)
|
|
5
|
+
|
|
3
6
|
> AI 代码索引生成工具,分析 JS/TS 代码结构,生成结构化代码映射
|
|
4
7
|
|
|
5
8
|
[English Documentation](./README.md)
|
|
@@ -9,7 +12,6 @@
|
|
|
9
12
|
- 🚀 **快速分析**:使用 AST 快速分析 JavaScript/TypeScript 代码结构
|
|
10
13
|
- 📊 **结构化输出**:生成包含导入、函数、类、常量的 `.fnmap` 索引文件
|
|
11
14
|
- 🔗 **调用图谱**:追踪函数调用关系和依赖
|
|
12
|
-
- 📈 **Mermaid 图表**:生成 Mermaid 格式的可视化调用图
|
|
13
15
|
- 🎯 **Git 集成**:只处理改动的文件,提高工作效率
|
|
14
16
|
- ⚙️ **灵活配置**:支持多种配置方式
|
|
15
17
|
- 🔌 **Pre-commit Hook**:无缝集成 git hooks
|
|
@@ -37,7 +39,20 @@ npm install --save-dev @didnhdj/fnmap
|
|
|
37
39
|
fnmap --init
|
|
38
40
|
```
|
|
39
41
|
|
|
40
|
-
|
|
42
|
+
这个交互式命令会:
|
|
43
|
+
1. 在项目根目录创建 `.fnmaprc` 配置文件
|
|
44
|
+
2. 添加 fnmap 生成的文件到 `.gitignore`(可选)
|
|
45
|
+
3. 自动追加 fnmap 格式文档到:
|
|
46
|
+
- `CLAUDE.md`(项目级 Claude AI 指令)
|
|
47
|
+
- `~/.claude/CLAUDE.md`(用户级全局指令)
|
|
48
|
+
- `AGENTS.md`(项目级 Agent 指令)
|
|
49
|
+
- 或任何你指定的自定义文件路径
|
|
50
|
+
|
|
51
|
+
追加的文档帮助 AI 助手(如 Claude)理解 `.fnmap` 格式,使其能够:
|
|
52
|
+
- 使用索引快速导航代码库
|
|
53
|
+
- 通过行号定位函数和类
|
|
54
|
+
- 理解代码结构和调用图
|
|
55
|
+
- 提供更准确的代码建议
|
|
41
56
|
|
|
42
57
|
### 基本用法
|
|
43
58
|
|
|
@@ -55,17 +70,13 @@ fnmap --files index.js,utils.js
|
|
|
55
70
|
fnmap --changed
|
|
56
71
|
|
|
57
72
|
# 处理 git staged 文件(用于 pre-commit hook)
|
|
58
|
-
fnmap --staged
|
|
59
|
-
```
|
|
73
|
+
fnmap --staged
|
|
60
74
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
```bash
|
|
64
|
-
# 生成文件级 Mermaid 图表
|
|
65
|
-
fnmap --mermaid file --dir src
|
|
75
|
+
# 显示详细处理日志
|
|
76
|
+
fnmap --log --dir src
|
|
66
77
|
|
|
67
|
-
#
|
|
68
|
-
fnmap --
|
|
78
|
+
# 清理生成的文件
|
|
79
|
+
fnmap --clear
|
|
69
80
|
```
|
|
70
81
|
|
|
71
82
|
## 配置
|
|
@@ -133,25 +144,6 @@ fnmap 支持多种配置方式(按优先级排序):
|
|
|
133
144
|
- ` +methodName(params) line description →calls` - 静态方法
|
|
134
145
|
- `CONSTANT_NAME line description` - 常量定义
|
|
135
146
|
|
|
136
|
-
### Mermaid 调用图
|
|
137
|
-
|
|
138
|
-
使用 `--mermaid` 选项时,生成可视化调用图:
|
|
139
|
-
|
|
140
|
-
**文件级** (`filename.mermaid`):
|
|
141
|
-
```mermaid
|
|
142
|
-
flowchart TD
|
|
143
|
-
subgraph utils["utils"]
|
|
144
|
-
readConfig["readConfig"]
|
|
145
|
-
parseData["parseData"]
|
|
146
|
-
saveFile["saveFile"]
|
|
147
|
-
end
|
|
148
|
-
readConfig --> parseData
|
|
149
|
-
saveFile --> parseData
|
|
150
|
-
```
|
|
151
|
-
|
|
152
|
-
**项目级** (`.fnmap.mermaid`):
|
|
153
|
-
显示项目中所有文件的调用关系。
|
|
154
|
-
|
|
155
147
|
## CLI 选项
|
|
156
148
|
|
|
157
149
|
```
|
|
@@ -159,14 +151,15 @@ flowchart TD
|
|
|
159
151
|
|
|
160
152
|
选项:
|
|
161
153
|
-v, --version 显示版本号
|
|
162
|
-
-f, --files <files>
|
|
154
|
+
-f, --files <files> 处理指定文件(逗号分隔)
|
|
163
155
|
-d, --dir <dir> 处理目录下所有代码文件
|
|
164
156
|
-p, --project <dir> 指定项目根目录(默认:当前目录)
|
|
165
157
|
-c, --changed 处理 git 改动的文件(staged + modified + untracked)
|
|
166
158
|
-s, --staged 处理 git staged 文件(用于 pre-commit hook)
|
|
167
159
|
-m, --mermaid [mode] 生成 Mermaid 调用图(file=文件级,project=项目级)
|
|
168
|
-
-
|
|
169
|
-
--init
|
|
160
|
+
-l, --log 显示详细处理日志
|
|
161
|
+
--init 创建默认配置文件并设置项目(交互式)
|
|
162
|
+
--clear 清理生成的文件(.fnmap、*.fnmap、*.mermaid)
|
|
170
163
|
-h, --help 显示帮助信息
|
|
171
164
|
```
|
|
172
165
|
|
|
@@ -239,19 +232,43 @@ interface FileInfo {
|
|
|
239
232
|
|
|
240
233
|
## 使用场景
|
|
241
234
|
|
|
242
|
-
### 1.
|
|
235
|
+
### 1. AI 助手集成
|
|
236
|
+
|
|
237
|
+
fnmap 专为帮助 AI 编程助手更好地理解代码库而设计:
|
|
238
|
+
|
|
239
|
+
```bash
|
|
240
|
+
# 初始化并设置 AI 文档
|
|
241
|
+
fnmap --init
|
|
242
|
+
|
|
243
|
+
# 为项目生成代码索引
|
|
244
|
+
fnmap --dir src
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
`.fnmap` 文件帮助 AI 助手:
|
|
248
|
+
- 高效导航大型代码库
|
|
249
|
+
- 按名称和行号查找特定函数/类
|
|
250
|
+
- 理解模块依赖和调用图
|
|
251
|
+
- 提供上下文感知的代码建议
|
|
252
|
+
|
|
253
|
+
**与 Claude Code 或类似 AI 工具的推荐工作流:**
|
|
254
|
+
1. 运行 `fnmap --init` 将格式文档添加到 `CLAUDE.md`
|
|
255
|
+
2. 使用 `fnmap --dir src` 生成索引文件
|
|
256
|
+
3. AI 助手将自动读取 `.fnmap` 文件以获取上下文
|
|
257
|
+
4. 代码变更时使用 `fnmap --changed` 更新索引
|
|
258
|
+
|
|
259
|
+
### 2. Pre-commit Hook
|
|
243
260
|
|
|
244
261
|
添加到 `.husky/pre-commit` 或 `.git/hooks/pre-commit`:
|
|
245
262
|
|
|
246
263
|
```bash
|
|
247
264
|
#!/bin/sh
|
|
248
|
-
fnmap --staged
|
|
265
|
+
fnmap --staged
|
|
249
266
|
git add .fnmap
|
|
250
267
|
```
|
|
251
268
|
|
|
252
269
|
这样在提交代码时会自动更新 `.fnmap` 索引。
|
|
253
270
|
|
|
254
|
-
###
|
|
271
|
+
### 3. CI/CD 集成
|
|
255
272
|
|
|
256
273
|
```yaml
|
|
257
274
|
# .github/workflows/ci.yml
|
|
@@ -262,23 +279,14 @@ git add .fnmap
|
|
|
262
279
|
git diff --exit-code .fnmap || echo "Code index updated"
|
|
263
280
|
```
|
|
264
281
|
|
|
265
|
-
###
|
|
282
|
+
### 4. 代码审查
|
|
266
283
|
|
|
267
284
|
```bash
|
|
268
285
|
# 为改动的文件生成索引
|
|
269
286
|
fnmap --changed
|
|
270
287
|
|
|
271
|
-
#
|
|
272
|
-
fnmap --
|
|
273
|
-
```
|
|
274
|
-
|
|
275
|
-
### 4. 文档生成
|
|
276
|
-
|
|
277
|
-
```bash
|
|
278
|
-
# 生成项目级调用图
|
|
279
|
-
fnmap --mermaid project
|
|
280
|
-
|
|
281
|
-
# 在文档中使用 .fnmap.mermaid 文件
|
|
288
|
+
# 分析时显示详细日志
|
|
289
|
+
fnmap --log --changed
|
|
282
290
|
```
|
|
283
291
|
|
|
284
292
|
## 支持的文件类型
|
|
@@ -305,7 +313,6 @@ fnmap --mermaid project
|
|
|
305
313
|
2. **结构分析**:遍历 AST 提取导入、函数、类、常量
|
|
306
314
|
3. **调用图谱**:追踪函数调用关系和依赖
|
|
307
315
|
4. **索引生成**:生成紧凑的 `.fnmap` 文件,包含结构化信息
|
|
308
|
-
5. **可视化**:可选生成 Mermaid 图表进行可视化展示
|
|
309
316
|
|
|
310
317
|
## 示例
|
|
311
318
|
|
|
@@ -332,17 +339,14 @@ Complete! Analyzed: 1, Failed: 0
|
|
|
332
339
|
==================================================
|
|
333
340
|
```
|
|
334
341
|
|
|
335
|
-
### 示例 2
|
|
342
|
+
### 示例 2:分析目录
|
|
336
343
|
|
|
337
344
|
```bash
|
|
338
|
-
fnmap --dir src
|
|
345
|
+
fnmap --dir src
|
|
339
346
|
```
|
|
340
347
|
|
|
341
348
|
生成:
|
|
342
|
-
- `src/.fnmap` -
|
|
343
|
-
- `src/utils.mermaid` - utils.js 的调用图
|
|
344
|
-
- `src/parser.mermaid` - parser.js 的调用图
|
|
345
|
-
- 等等
|
|
349
|
+
- `src/.fnmap` - src 目录下所有文件的代码索引
|
|
346
350
|
|
|
347
351
|
### 示例 3:Git 工作流
|
|
348
352
|
|
|
@@ -351,7 +355,7 @@ fnmap --dir src --mermaid file
|
|
|
351
355
|
git add .
|
|
352
356
|
|
|
353
357
|
# 为 staged 文件生成索引
|
|
354
|
-
fnmap --staged
|
|
358
|
+
fnmap --staged
|
|
355
359
|
|
|
356
360
|
# 添加更新的索引
|
|
357
361
|
git add .fnmap
|
package/dist/cli/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAUpC,eAAO,MAAM,MAAM;iBACJ,MAAM,KAAG,IAAI;mBAGX,MAAM,KAAG,IAAI;gBAGhB,MAAM,KAAG,IAAI;gBAGb,MAAM,KAAG,IAAI;iBAGZ,MAAM,KAAG,IAAI;iBAIb,MAAM,KAAG,IAAI;CAG3B,CAAC;AAEF,wBAAgB,YAAY,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,CAEjD;AAED,wBAAgB,WAAW,IAAI,OAAO,CAErC;AAED;;GAEG;AACH,wBAAgB,UAAU,IAAI,MAAM,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAUpC,eAAO,MAAM,MAAM;iBACJ,MAAM,KAAG,IAAI;mBAGX,MAAM,KAAG,IAAI;gBAGhB,MAAM,KAAG,IAAI;gBAGb,MAAM,KAAG,IAAI;iBAGZ,MAAM,KAAG,IAAI;iBAIb,MAAM,KAAG,IAAI;CAG3B,CAAC;AAEF,wBAAgB,YAAY,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,CAEjD;AAED,wBAAgB,WAAW,IAAI,OAAO,CAErC;AAED;;GAEG;AACH,wBAAgB,UAAU,IAAI,MAAM,CAenC;AAED;;GAEG;AACH,wBAAgB,QAAQ,IAAI,OAAO,CAqDlC;AAGD,wBAAgB,UAAU,IAAI,OAAO,CAKpC;AAGD,eAAO,MAAM,OAAO;;;;CAUnB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const F=require("fs"),g=require("path"),
|
|
3
|
-
`)}let ye=!0,be=null;const E={error:e=>{ye||console.error(`${S.red}✗${S.reset} ${e}`)},success:e=>{ye||console.log(`${S.green}✓${S.reset} ${e}`)},info:e=>{ye||console.log(e)},warn:e=>{ye||console.warn(`${S.yellow}!${S.reset} ${e}`)},title:e=>{ye||console.log(`${S.bold}${e}${S.reset}`)},stats:e=>{console.log(e)}};function he(e){ye=e}function _e(){return ye}function
|
|
2
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const F=require("fs"),g=require("path"),en=require("commander"),Ie=require("child_process"),nn=require("@babel/parser"),Re=require("@babel/traverse"),tn=require("os"),rn=require("node:readline");function sn(e){const r=Object.create(null,{[Symbol.toStringTag]:{value:"Module"}});if(e){for(const n in e)if(n!=="default"){const s=Object.getOwnPropertyDescriptor(e,n);Object.defineProperty(r,n,s.get?s:{enumerable:!0,get:()=>e[n]})}}return r.default=e,Object.freeze(r)}const on=sn(rn),z={FILE_NOT_FOUND:"FILE_NOT_FOUND",FILE_READ_ERROR:"FILE_READ_ERROR",PARSE_ERROR:"PARSE_ERROR",CONFIG_ERROR:"CONFIG_ERROR",VALIDATION_ERROR:"VALIDATION_ERROR",PERMISSION_ERROR:"PERMISSION_ERROR",FILE_TOO_LARGE:"FILE_TOO_LARGE"};function ke(e){return"parseError"in e}function an(e){return e.success===!0}function cn(e){return e.success===!1}function ln(e){return e.valid===!0}function fn(e){return e.valid===!1}const S={reset:"\x1B[0m",red:"\x1B[31m",green:"\x1B[32m",yellow:"\x1B[33m",blue:"\x1B[34m",gray:"\x1B[90m",bold:"\x1B[1m"},Le=10*1024*1024,je=50,Fe=[".js",".ts",".jsx",".tsx",".mjs"],Te=["node_modules",".git","dist","build",".next","coverage","__pycache__",".cache"],Ne={enable:!0,include:["**/*.js","**/*.ts","**/*.jsx","**/*.tsx","**/*.mjs"],exclude:[]};function $e(e){if(!e)return e;const r=e.replace(/\\/g,"/");return g.normalize(r)}function dn(e){return e.map($e)}function ze(e){if(!e||typeof e!="string")return{valid:!1,error:"File path is required and must be a string / 文件路径必须是字符串",errorType:z.VALIDATION_ERROR};if(!F.existsSync(e))return{valid:!1,error:`File not found: ${e} / 文件不存在: ${e}`,errorType:z.FILE_NOT_FOUND};try{const r=F.statSync(e);if(!r.isFile())return{valid:!1,error:`Path is not a file: ${e} / 路径不是文件: ${e}`,errorType:z.VALIDATION_ERROR};if(r.size>Le)return{valid:!1,error:`File too large (${(r.size/1024/1024).toFixed(2)}MB > ${Le/1024/1024}MB): ${e} / 文件过大`,errorType:z.FILE_TOO_LARGE}}catch(r){return{valid:!1,error:`Cannot access file: ${e}. Reason: ${r.message} / 无法访问文件`,errorType:z.PERMISSION_ERROR}}return{valid:!0}}function De(e){if(!e||typeof e!="object")return{valid:!1,error:"Config must be an object / 配置必须是对象"};const r=e;return r.enable!==void 0&&typeof r.enable!="boolean"?{valid:!1,error:"Config.enable must be a boolean / enable 字段必须是布尔值"}:r.include!==void 0&&!Array.isArray(r.include)?{valid:!1,error:"Config.include must be an array / include 字段必须是数组"}:r.exclude!==void 0&&!Array.isArray(r.exclude)?{valid:!1,error:"Config.exclude must be an array / exclude 字段必须是数组"}:{valid:!0}}function Se(e,r,n={}){const s=[r];return n.file&&s.push(`File: ${n.file}`),n.line!==void 0&&n.column!==void 0&&s.push(`Location: Line ${n.line}, Column ${n.column}`),n.suggestion&&s.push(`Suggestion: ${n.suggestion}`),s.join(`
|
|
3
|
+
`)}let ye=!0,be=null;const E={error:e=>{ye||console.error(`${S.red}✗${S.reset} ${e}`)},success:e=>{ye||console.log(`${S.green}✓${S.reset} ${e}`)},info:e=>{ye||console.log(e)},warn:e=>{ye||console.warn(`${S.yellow}!${S.reset} ${e}`)},title:e=>{ye||console.log(`${S.bold}${e}${S.reset}`)},stats:e=>{console.log(e)}};function he(e){ye=e}function _e(){return ye}function We(){try{let e;try{e=require("../../package.json")}catch{e=require("../package.json")}return e.version}catch{return"0.1.0"}}function Me(){return be=new en.Command,be.name("fnmap").description("AI code indexing tool - Analyzes JS/TS code structure and generates structured code maps").version(We(),"-v, --version","Show version number").option("-f, --files <files>","Process specified files (comma-separated)",e=>e.split(",").map(r=>$e(r.trim())).filter(Boolean)).option("-d, --dir <dir>","Process all code files in directory",e=>$e(e)).option("-p, --project <dir>","Specify project root directory (default: current directory)",e=>$e(e),process.env.CLAUDE_PROJECT_DIR??process.cwd()).option("-c, --changed","Process only git changed files (staged + modified + untracked)").option("-s, --staged","Process only git staged files (for pre-commit hook)").option("-m, --mermaid [mode]","Generate Mermaid call graph (file=file-level, project=project-level)").option("-l, --log","Show detailed processing logs").option("--init","Create default config file and setup project (interactive)").option("--clear","Clear generated files (.fnmap, *.fnmap, *.mermaid)").argument("[files...]","Directly specify file paths").allowUnknownOption(!1).addHelpText("after",`
|
|
4
4
|
Configuration files (by priority):
|
|
5
5
|
.fnmaprc JSON config file
|
|
6
6
|
.fnmaprc.json JSON config file
|
|
@@ -23,16 +23,16 @@ Examples:
|
|
|
23
23
|
$ fnmap --init Interactive project setup
|
|
24
24
|
$ fnmap --clear Clear all generated files
|
|
25
25
|
$ fnmap --clear --dir src Clear generated files in src directory
|
|
26
|
-
`),be}function xe(){return be||Me()}const
|
|
26
|
+
`),be}function xe(){return be||Me()}const mn={get opts(){return xe().opts.bind(xe())},get args(){return xe().args},get parse(){return xe().parse.bind(xe())}};function Be(e){const r=[".fnmaprc",".fnmaprc.json"];for(const s of r){const c=g.join(e,s);if(F.existsSync(c))try{const f=F.readFileSync(c,"utf-8");if(!f.trim()){E.warn(`Config file is empty: ${s}. Using default config / 配置文件为空,使用默认配置`);continue}let $;try{$=JSON.parse(f)}catch(p){const h=p,o=Se(z.CONFIG_ERROR,`Failed to parse config file: ${s} / 配置文件解析失败`,{file:c,line:h.lineNumber,column:h.columnNumber,suggestion:"Check JSON syntax, ensure proper quotes and commas / 检查 JSON 语法,确保引号和逗号正确"});E.warn(o);continue}const u=De($);if(!u.valid){E.warn(`Invalid config in ${s}: ${u.error}`);continue}return{config:$,source:s}}catch(f){const $=f,u=Se(z.FILE_READ_ERROR,`Failed to read config file: ${s} / 配置文件读取失败`,{file:c,suggestion:$.message});E.warn(u)}}const n=g.join(e,"package.json");if(F.existsSync(n))try{const s=JSON.parse(F.readFileSync(n,"utf-8"));if(s.fnmap){const c=De(s.fnmap);if(!c.valid)E.warn(`Invalid fnmap config in package.json: ${c.error}`);else return{config:s.fnmap,source:"package.json#fnmap"}}}catch{}return{config:null,source:null}}function Je(e){return e?{...Ne,...e,exclude:[...e.exclude??[]]}:Ne}function Pe(e){return e.endsWith(".d.ts")||e.endsWith(".d.tsx")||e.endsWith(".d.mts")}function pn(e){try{return Ie.execSync("git rev-parse --show-toplevel",{cwd:e,encoding:"utf-8"}).trim()}catch{return null}}function Ke(e,r=!1){const n=[];try{const s=pn(e);if(!s)return[];let c;if(r)c=Ie.execSync("git diff --cached --name-only --diff-filter=ACMR",{cwd:e,encoding:"utf-8"});else{const u=Ie.execSync("git diff --cached --name-only --diff-filter=ACMR",{cwd:e,encoding:"utf-8"}),p=Ie.execSync("git diff --name-only --diff-filter=ACMR",{cwd:e,encoding:"utf-8"}),h=Ie.execSync("git ls-files --others --exclude-standard",{cwd:e,encoding:"utf-8"});c=`${u}
|
|
27
27
|
${p}
|
|
28
28
|
${h}`}const f=c.split(`
|
|
29
|
-
`).map(u=>u.trim()).filter(Boolean).filter(u=>{const p=g.extname(u);return Fe.includes(p)&&!Pe(u)}),$=[...new Set(f)];for(const u of $){const p=g.resolve(s,u);F.existsSync(p)&&p.startsWith(g.resolve(e))&&
|
|
30
|
-
`).map(s=>s.replace(/^\s*\*\s?/,"").trim());for(const s of
|
|
31
|
-
`).map(i=>i.replace(/^\s*\*\s?/,"").trim()).filter(i=>i&&!i.startsWith("/")&&!i.startsWith("@ai"));for(const i of n)if(i.startsWith("@description ")){t.description=i.slice(13).trim();break}if(!t.description&&n.length>0){const i=n.find(m=>!m.startsWith("@"));i&&(t.description=i)}}let c;try{const a=r&&(r.endsWith(".ts")||r.endsWith(".tsx"));c=tn.parse(e,{sourceType:"unambiguous",plugins:["jsx","classPrivateProperties","classPrivateMethods",...a?["typescript"]:[]]})}catch(a){const n=a;return{parseError:Se(W.PARSE_ERROR,`Syntax error: ${n.message} / 语法错误`,{file:r??void 0,line:(I=n.loc)==null?void 0:I.line,column:(v=n.loc)==null?void 0:v.column,suggestion:"Check syntax errors in the file / 检查文件中的语法错误"}),loc:n.loc,errorType:W.PARSE_ERROR}}const f=new Map,$=new Map,u=new Map;function p(a){if(!a)return null;if(a.type==="Identifier")return a.name;if(a.type==="MemberExpression"){const n=a.object,i=a.property;if(n.type==="Identifier"&&i.type==="Identifier")return n.name}return null}function h(a){var i,m,b;let n=a;for(;n;){if(n.node.type==="FunctionDeclaration"){const x=n.node;if(x.id)return x.id.name}if(n.node.type==="ClassMethod"){const x=n.node,R=(i=n.parentPath)==null?void 0:i.parentPath,C=R==null?void 0:R.node,j=((m=C==null?void 0:C.id)==null?void 0:m.name)??"",O=((b=x.key)==null?void 0:b.name)??"";return j?`${j}.${O}`:O}if(n.node.type==="ArrowFunctionExpression"||n.node.type==="FunctionExpression"){const x=n.parent;if((x==null?void 0:x.type)==="VariableDeclarator"){const C=x.id;if(C!=null&&C.name)return C.name}}n=n.parentPath}return null}ve(c,{VariableDeclarator(a){var i,m,b,x;const n=a.node;if(((i=n.init)==null?void 0:i.type)==="CallExpression"&&((m=n.init.callee)==null?void 0:m.type)==="Identifier"&&n.init.callee.name==="require"&&((x=(b=n.init.arguments)==null?void 0:b[0])==null?void 0:x.type)==="StringLiteral"){const R=n.init.arguments[0].value;if(f.has(R)||f.set(R,new Set),n.id.type==="Identifier"){const C=n.id.name;f.get(R).add(C),$.set(C,R),u.set(C,new Set)}else if(n.id.type==="ObjectPattern"){for(const C of n.id.properties)if(C.type==="ObjectProperty"&&C.key.type==="Identifier"){const j=C.value.type==="Identifier"?C.value.name:C.key.name;f.get(R).add(C.key.name),$.set(j,R),u.set(j,new Set)}}}},CallExpression(a){var i,m,b,x,R,C;const n=a.node;if(((i=n.callee)==null?void 0:i.type)==="Identifier"&&n.callee.name==="require"&&((b=(m=n.arguments)==null?void 0:m[0])==null?void 0:b.type)==="StringLiteral"){const j=a.parent;if((j==null?void 0:j.type)==="MemberExpression"&&((x=j.property)==null?void 0:x.type)==="Identifier"){const O=n.arguments[0].value;f.has(O)||f.set(O,new Set),f.get(O).add(j.property.name);const U=(R=a.parentPath)==null?void 0:R.parent;if((U==null?void 0:U.type)==="VariableDeclarator"){const V=U;((C=V.id)==null?void 0:C.type)==="Identifier"&&($.set(V.id.name,O),u.set(V.id.name,new Set))}}}},ImportDeclaration(a){const n=a.node,i=n.source.value;f.has(i)||f.set(i,new Set);for(const m of n.specifiers){let b,x;if(m.type==="ImportDefaultSpecifier")b="default",x=m.local.name;else if(m.type==="ImportNamespaceSpecifier")b="*",x=m.local.name;else if(m.type==="ImportSpecifier"){const R=m.imported;b=R.type==="Identifier"?R.name:R.value,x=m.local.name}b&&x&&(f.get(i).add(b),$.set(x,i),u.set(x,new Set))}}}),ve(c,{Identifier(a){const n=a.node.name;if(u.has(n)){const i=a.parent;if((i==null?void 0:i.type)==="VariableDeclarator"&&i.id===a.node||(i==null?void 0:i.type)==="ImportSpecifier"||(i==null?void 0:i.type)==="ImportDefaultSpecifier")return;const m=h(a);m&&u.get(n).add(m)}},FunctionDeclaration(a){var C,j,O,U,V;if(a.parent.type==="ExportDefaultDeclaration")return;const n=a.node,i=((C=n.id)==null?void 0:C.name)??"[anonymous]",m=n.params.map(G=>{var K,X;return G.type==="Identifier"?G.name:G.type==="AssignmentPattern"&&((K=G.left)==null?void 0:K.type)==="Identifier"?G.left.name+"?":G.type==="RestElement"&&((X=G.argument)==null?void 0:X.type)==="Identifier"?"..."+G.argument.name:"?"}),b=((O=(j=n.loc)==null?void 0:j.start)==null?void 0:O.line)??0,x=((V=(U=n.loc)==null?void 0:U.end)==null?void 0:V.line)??0,R=Y(ge(n,a.parent));t.functions.push({name:i,params:m.join(","),startLine:b,endLine:x,description:R})},ClassDeclaration(a){var j,O,U,V,G,K,X,ee,ne;if(a.parent.type==="ExportDefaultDeclaration")return;const n=a.node,i=((j=n.id)==null?void 0:j.name)??"[anonymous]",m=((U=(O=n.loc)==null?void 0:O.start)==null?void 0:U.line)??0,b=((G=(V=n.loc)==null?void 0:V.end)==null?void 0:G.line)??0,x=p(n.superClass),R=Y(ge(n,a.parent)),C=[];if((K=n.body)!=null&&K.body){for(const z of n.body.body)if(z.type==="ClassMethod"){const se=((X=z.key)==null?void 0:X.type)==="Identifier"?z.key.name:"[computed]",ie=z.params.map(H=>{var re;return H.type==="Identifier"?H.name:H.type==="AssignmentPattern"&&((re=H.left)==null?void 0:re.type)==="Identifier"?H.left.name+"?":"?"}),oe=((ne=(ee=z.loc)==null?void 0:ee.start)==null?void 0:ne.line)??0;let te="";const Z=z.leadingComments;Z&&Z.length>0&&(te=Y(Z[Z.length-1])),C.push({name:se,params:ie.join(","),line:oe,static:z.static,kind:z.kind,description:te})}}t.classes.push({name:i,superClass:x,startLine:m,endLine:b,methods:C,description:R})},VariableDeclaration(a){var b,x,R,C,j,O,U,V,G,K,X,ee,ne,z,se,ie,oe,te,Z,H,re,ce,le,fe,de,me,pe,ae,B,L,J;const n=a.parent.type;if(n!=="Program"&&n!=="ExportNamedDeclaration")return;const i=a.node,m=Y(ge(i,a.parent));for(const k of i.declarations){const _=((b=k.id)==null?void 0:b.type)==="Identifier"?k.id.name:void 0;if(_){if(((x=k.init)==null?void 0:x.type)==="ArrowFunctionExpression"||((R=k.init)==null?void 0:R.type)==="FunctionExpression"){const d=k.init.params.map(P=>{var N,q;return P.type==="Identifier"?P.name:P.type==="AssignmentPattern"&&((N=P.left)==null?void 0:N.type)==="Identifier"?P.left.name+"?":P.type==="RestElement"&&((q=P.argument)==null?void 0:q.type)==="Identifier"?"..."+P.argument.name:"?"}),A=((j=(C=i.loc)==null?void 0:C.start)==null?void 0:j.line)??0,M=((U=(O=i.loc)==null?void 0:O.end)==null?void 0:U.line)??0;t.functions.push({name:_,params:d.join(","),startLine:A,endLine:M,description:m});continue}if(((V=k.init)==null?void 0:V.type)==="ClassExpression"){const w=k.init,d=((K=(G=i.loc)==null?void 0:G.start)==null?void 0:K.line)??0,A=((ee=(X=i.loc)==null?void 0:X.end)==null?void 0:ee.line)??0,M=p(w.superClass),P=[];if((ne=w.body)!=null&&ne.body){for(const N of w.body.body)if(N.type==="ClassMethod"){const q=((z=N.key)==null?void 0:z.type)==="Identifier"?N.key.name:"[computed]",T=N.params.map(Ee=>{var Ue;return Ee.type==="Identifier"?Ee.name:Ee.type==="AssignmentPattern"&&((Ue=Ee.left)==null?void 0:Ue.type)==="Identifier"?Ee.left.name+"?":"?"}),D=((ie=(se=N.loc)==null?void 0:se.start)==null?void 0:ie.line)??0;let Q="";const ue=N.leadingComments;ue&&ue.length>0&&(Q=Y(ue[ue.length-1])),P.push({name:q,params:T.join(","),line:D,static:N.static,kind:N.kind,description:Q})}}t.classes.push({name:_,superClass:M,startLine:d,endLine:A,methods:P,description:m});continue}if(((oe=k.init)==null?void 0:oe.type)==="ObjectExpression"){const w=k.init;for(const d of w.properties)if(d.type==="ObjectMethod"){const A=((te=d.key)==null?void 0:te.type)==="Identifier"?`${_}.${d.key.name}`:`${_}.[computed]`,M=d.params.map(T=>{var D,Q;return T.type==="Identifier"?T.name:T.type==="AssignmentPattern"&&((D=T.left)==null?void 0:D.type)==="Identifier"?T.left.name+"?":T.type==="RestElement"&&((Q=T.argument)==null?void 0:Q.type)==="Identifier"?"..."+T.argument.name:"?"}),P=((H=(Z=d.loc)==null?void 0:Z.start)==null?void 0:H.line)??0,N=((ce=(re=d.loc)==null?void 0:re.end)==null?void 0:ce.line)??0;let q="";d.leadingComments&&d.leadingComments.length>0&&(q=Y(d.leadingComments[d.leadingComments.length-1])),t.functions.push({name:A,params:M.join(","),startLine:P,endLine:N,description:q})}else if(d.type==="ObjectProperty"&&((le=d.key)==null?void 0:le.type)==="Identifier"&&(((fe=d.value)==null?void 0:fe.type)==="ArrowFunctionExpression"||((de=d.value)==null?void 0:de.type)==="FunctionExpression")){const A=d.value,M=`${_}.${d.key.name}`,P=A.params.map(D=>{var Q,ue;return D.type==="Identifier"?D.name:D.type==="AssignmentPattern"&&((Q=D.left)==null?void 0:Q.type)==="Identifier"?D.left.name+"?":D.type==="RestElement"&&((ue=D.argument)==null?void 0:ue.type)==="Identifier"?"..."+D.argument.name:"?"}),N=((pe=(me=d.loc)==null?void 0:me.start)==null?void 0:pe.line)??0,q=((B=(ae=d.loc)==null?void 0:ae.end)==null?void 0:B.line)??0;let T="";d.leadingComments&&d.leadingComments.length>0&&(T=Y(d.leadingComments[d.leadingComments.length-1])),t.functions.push({name:M,params:P.join(","),startLine:N,endLine:q,description:T})}}if(i.kind==="const"&&_===_.toUpperCase()&&_.length>2){const w=((J=(L=i.loc)==null?void 0:L.start)==null?void 0:J.line)??0;t.constants.push({name:_,line:w,description:m})}}}},ExportDefaultDeclaration(a){var b,x,R,C,j,O,U,V,G,K,X,ee,ne,z,se,ie,oe,te,Z,H,re,ce,le,fe,de,me,pe,ae,B;const n=a.node,i=n.declaration,m=Y(ge(n,a.parent));if(i.type==="FunctionDeclaration"){const L=i,J=((b=L.id)==null?void 0:b.name)??"[default]",k=L.params.map(d=>{var A,M;return d.type==="Identifier"?d.name:d.type==="AssignmentPattern"&&((A=d.left)==null?void 0:A.type)==="Identifier"?d.left.name+"?":d.type==="RestElement"&&((M=d.argument)==null?void 0:M.type)==="Identifier"?"..."+d.argument.name:"?"}),_=((R=(x=n.loc)==null?void 0:x.start)==null?void 0:R.line)??0,w=((j=(C=n.loc)==null?void 0:C.end)==null?void 0:j.line)??0;t.functions.push({name:J,params:k.join(","),startLine:_,endLine:w,description:m});return}if(i.type==="ClassDeclaration"){const L=i,J=((O=L.id)==null?void 0:O.name)??"[default]",k=((V=(U=n.loc)==null?void 0:U.start)==null?void 0:V.line)??0,_=((K=(G=n.loc)==null?void 0:G.end)==null?void 0:K.line)??0,w=p(L.superClass),d=[];if((X=L.body)!=null&&X.body){for(const A of L.body.body)if(A.type==="ClassMethod"){const M=((ee=A.key)==null?void 0:ee.type)==="Identifier"?A.key.name:"[computed]",P=A.params.map(D=>{var Q;return D.type==="Identifier"?D.name:D.type==="AssignmentPattern"&&((Q=D.left)==null?void 0:Q.type)==="Identifier"?D.left.name+"?":"?"}),N=((z=(ne=A.loc)==null?void 0:ne.start)==null?void 0:z.line)??0;let q="";const T=A.leadingComments;T&&T.length>0&&(q=Y(T[T.length-1])),d.push({name:M,params:P.join(","),line:N,static:A.static,kind:A.kind,description:q})}}t.classes.push({name:J,superClass:w,startLine:k,endLine:_,methods:d,description:m});return}if(i.type==="ArrowFunctionExpression"||i.type==="FunctionExpression"){const L=i,J=L.type==="FunctionExpression"&&((se=L.id)!=null&&se.name)?L.id.name:"[default]",k=L.params.map(d=>{var A,M;return d.type==="Identifier"?d.name:d.type==="AssignmentPattern"&&((A=d.left)==null?void 0:A.type)==="Identifier"?d.left.name+"?":d.type==="RestElement"&&((M=d.argument)==null?void 0:M.type)==="Identifier"?"..."+d.argument.name:"?"}),_=((oe=(ie=n.loc)==null?void 0:ie.start)==null?void 0:oe.line)??0,w=((Z=(te=n.loc)==null?void 0:te.end)==null?void 0:Z.line)??0;t.functions.push({name:J,params:k.join(","),startLine:_,endLine:w,description:m});return}if(i.type==="ClassExpression"){const L=i,J=((H=L.id)==null?void 0:H.name)??"[default]",k=((ce=(re=n.loc)==null?void 0:re.start)==null?void 0:ce.line)??0,_=((fe=(le=n.loc)==null?void 0:le.end)==null?void 0:fe.line)??0,w=((de=L.superClass)==null?void 0:de.type)==="Identifier"?L.superClass.name:null,d=[];if((me=L.body)!=null&&me.body){for(const A of L.body.body)if(A.type==="ClassMethod"){const M=((pe=A.key)==null?void 0:pe.type)==="Identifier"?A.key.name:"[computed]",P=A.params.map(D=>{var Q;return D.type==="Identifier"?D.name:D.type==="AssignmentPattern"&&((Q=D.left)==null?void 0:Q.type)==="Identifier"?D.left.name+"?":"?"}),N=((B=(ae=A.loc)==null?void 0:ae.start)==null?void 0:B.line)??0;let q="";const T=A.leadingComments;T&&T.length>0&&(q=Y(T[T.length-1])),d.push({name:M,params:P.join(","),line:N,static:A.static,kind:A.kind,description:q})}}t.classes.push({name:J,superClass:w,startLine:k,endLine:_,methods:d,description:m})}},AssignmentExpression(a){var x,R,C,j,O,U,V,G,K,X,ee,ne,z,se,ie,oe,te,Z,H,re,ce,le,fe,de,me,pe;const n=a.node;if(a.parent.type!=="ExpressionStatement")return;const i=(x=a.parentPath)==null?void 0:x.parent;if((i==null?void 0:i.type)!=="Program")return;const m=n.left,b=n.right;if(m.type==="MemberExpression"&&((R=m.object)==null?void 0:R.type)==="Identifier"&&m.object.name==="module"&&((C=m.property)==null?void 0:C.type)==="Identifier"&&m.property.name==="exports"){const ae=Y(ge(a.parent,i));if(b.type==="FunctionExpression"||b.type==="ArrowFunctionExpression"){const B=b,L=b.type==="FunctionExpression"&&((j=b.id)!=null&&j.name)?b.id.name:"[exports]",J=B.params.map(w=>{var d,A;return w.type==="Identifier"?w.name:w.type==="AssignmentPattern"&&((d=w.left)==null?void 0:d.type)==="Identifier"?w.left.name+"?":w.type==="RestElement"&&((A=w.argument)==null?void 0:A.type)==="Identifier"?"..."+w.argument.name:"?"}),k=((U=(O=n.loc)==null?void 0:O.start)==null?void 0:U.line)??0,_=((G=(V=n.loc)==null?void 0:V.end)==null?void 0:G.line)??0;t.functions.push({name:L,params:J.join(","),startLine:k,endLine:_,description:ae});return}if(b.type==="ClassExpression"){const B=b,L=((K=B.id)==null?void 0:K.name)??"[exports]",J=((ee=(X=n.loc)==null?void 0:X.start)==null?void 0:ee.line)??0,k=((z=(ne=n.loc)==null?void 0:ne.end)==null?void 0:z.line)??0,_=p(B.superClass),w=[];if((se=B.body)!=null&&se.body){for(const d of B.body.body)if(d.type==="ClassMethod"){const A=((ie=d.key)==null?void 0:ie.type)==="Identifier"?d.key.name:"[computed]",M=d.params.map(N=>{var q;return N.type==="Identifier"?N.name:N.type==="AssignmentPattern"&&((q=N.left)==null?void 0:q.type)==="Identifier"?N.left.name+"?":"?"}),P=((te=(oe=d.loc)==null?void 0:oe.start)==null?void 0:te.line)??0;w.push({name:A,params:M.join(","),line:P,static:d.static,kind:d.kind,description:""})}}t.classes.push({name:L,superClass:_,startLine:J,endLine:k,methods:w,description:ae});return}}if(m.type==="MemberExpression"&&((Z=m.property)==null?void 0:Z.type)==="Identifier"){const ae=m.property.name;let B=!1;if(((H=m.object)==null?void 0:H.type)==="Identifier"&&m.object.name==="exports"&&(B=!0),((re=m.object)==null?void 0:re.type)==="MemberExpression"&&((ce=m.object.object)==null?void 0:ce.type)==="Identifier"&&m.object.object.name==="module"&&((le=m.object.property)==null?void 0:le.type)==="Identifier"&&m.object.property.name==="exports"&&(B=!0),B){const L=Y(ge(a.parent,i));if(b.type==="FunctionExpression"||b.type==="ArrowFunctionExpression"){const k=b.params.map(d=>{var A,M;return d.type==="Identifier"?d.name:d.type==="AssignmentPattern"&&((A=d.left)==null?void 0:A.type)==="Identifier"?d.left.name+"?":d.type==="RestElement"&&((M=d.argument)==null?void 0:M.type)==="Identifier"?"..."+d.argument.name:"?"}),_=((de=(fe=n.loc)==null?void 0:fe.start)==null?void 0:de.line)??0,w=((pe=(me=n.loc)==null?void 0:me.end)==null?void 0:pe.line)??0;t.functions.push({name:ae,params:k.join(","),startLine:_,endLine:w,description:L})}}}}});for(const[a,n]of f){const i=new Set;for(const m of $.keys())if($.get(m)===a&&u.has(m))for(const b of u.get(m))i.add(b);t.imports.push({module:a,members:Array.from(n),usedIn:Array.from(i)})}const o=new Set;for(const a of t.functions)o.add(a.name);for(const a of t.classes)for(const n of a.methods)o.add(n.name),o.add(`${a.name}.${n.name}`);const y=new Set($.keys()),l=new Map;ve(c,{CallExpression(a){var m,b;const n=a.node;let i=null;if(n.callee.type==="Identifier")i=n.callee.name;else if(n.callee.type==="MemberExpression"&&((m=n.callee.property)==null?void 0:m.type)==="Identifier"){const x=((b=n.callee.object)==null?void 0:b.type)==="Identifier"?n.callee.object.name:void 0,R=n.callee.property.name;x&&y.has(x)?i=`${x}.${R}`:i=R}if(i){const x=h(a);if(x&&x!==i){const R=o.has(i),C=y.has(i)||i.includes(".")&&y.has(i.split(".")[0]);(R||C)&&(l.has(x)||l.set(x,new Set),l.get(x).add(i))}}}}),t.callGraph={};for(const[a,n]of l)t.callGraph[a]=Array.from(n);return t.isPureType=mn(c),t}function mn(e){let r=!1;for(const t of e.program.body){switch(t.type){case"TSTypeAliasDeclaration":case"TSInterfaceDeclaration":case"TSEnumDeclaration":break;case"ImportDeclaration":t.importKind!=="type"&&t.specifiers.some(c=>c.type==="ImportSpecifier"?c.importKind!=="type":!0)&&t.specifiers.length>0&&(r=!0);break;case"ExportNamedDeclaration":if(t.exportKind==="type")break;if(t.declaration){const s=t.declaration.type;s!=="TSTypeAliasDeclaration"&&s!=="TSInterfaceDeclaration"&&(r=!0)}else t.specifiers&&t.specifiers.length>0&&t.specifiers.some(c=>c.type==="ExportSpecifier"?c.exportKind!=="type":!0)&&(r=!0);break;case"ExportDefaultDeclaration":r=!0;break;case"ExportAllDeclaration":t.exportKind!=="type"&&(r=!0);break;default:r=!0;break}if(r)break}return!r}function pn(e,r){const t=[];let s=`/*@AI ${r}`;e.description&&(s+=` - ${e.description.slice(0,50)}`),t.push(s);for(const c of e.imports){const f=Array.isArray(c.members)?c.members.join(","):"";let $=`<${c.module}:${f}`;Array.isArray(c.usedIn)&&c.usedIn.length>0&&($+=` ->${c.usedIn.join(",")}`),t.push($)}for(const c of e.classes){let f=c.name;c.superClass&&(f+=`:${c.superClass}`),f+=` ${c.startLine}-${c.endLine}`,c.description&&(f+=` ${c.description}`),t.push(f);for(const $ of c.methods){const u=$.static?" +":" .",p=$.kind==="get"?"get:":$.kind==="set"?"set:":"";let h=`${u}${p}${$.name}(${$.params}) ${$.line}`;$.description&&(h+=` ${$.description}`),t.push(h)}}for(const c of e.functions){let f=`${c.name}(${c.params}) ${c.startLine}-${c.endLine}`;c.description&&(f+=` ${c.description}`),t.push(f)}for(const c of e.constants){let f=`${c.name} ${c.line}`;c.description&&(f+=` ${c.description}`),t.push(f)}return t.push("@AI*/"),t.join(`
|
|
32
|
-
`)}function
|
|
33
|
-
`)}function
|
|
34
|
-
`)}function
|
|
35
|
-
`)}function
|
|
29
|
+
`).map(u=>u.trim()).filter(Boolean).filter(u=>{const p=g.extname(u);return Fe.includes(p)&&!Pe(u)}),$=[...new Set(f)];for(const u of $){const p=g.resolve(s,u);F.existsSync(p)&&p.startsWith(g.resolve(e))&&n.push(p)}}catch{return[]}return n}function un(e){const r=[];if(!F.existsSync(e))return r;try{const n=F.readdirSync(e,{withFileTypes:!0});for(const s of n)if(s.isFile()){const c=g.extname(s.name);Fe.includes(c)&&!Pe(s.name)&&r.push(g.join(e,s.name))}}catch{return r}return r}function Ae(e,r=e,n=Te,s=0,c=new Set){const f=[];if(!F.existsSync(e))return E.warn(`Directory does not exist: ${e} / 目录不存在`),f;if(s>je)return E.warn(`Max directory depth (${je}) exceeded: ${e} / 超过最大目录深度`),f;let $;try{$=F.realpathSync(e)}catch(p){const h=p;return E.warn(`Cannot resolve real path: ${e}. Reason: ${h.message} / 无法解析真实路径`),f}if(c.has($))return E.warn(`Circular reference detected, skipping: ${e} / 检测到循环引用`),f;c.add($);let u;try{u=F.readdirSync(e,{withFileTypes:!0})}catch(p){const h=p;return h.code==="EACCES"||h.code==="EPERM"?E.warn(`Permission denied: ${e} / 权限不足`):E.warn(`Failed to read directory: ${e}. Reason: ${h.message} / 读取目录失败`),f}for(const p of u)try{const h=g.join(e,p.name);if(p.isDirectory())n.includes(p.name)||f.push(...Ae(h,r,n,s+1,c));else if(p.isFile()){const o=g.extname(p.name);Fe.includes(o)&&!Pe(p.name)&&f.push(g.relative(r,h))}}catch(h){const o=h;E.warn(`Error processing entry: ${p.name}. Reason: ${o.message} / 处理条目出错`)}return f}function Y(e){if(!e)return"";const n=e.value.split(`
|
|
30
|
+
`).map(s=>s.replace(/^\s*\*\s?/,"").trim());for(const s of n)if(s.startsWith("@description "))return s.slice(13).trim().slice(0,60);for(const s of n)if(s&&!s.startsWith("@")&&!s.startsWith("/"))return s.slice(0,60);return""}function ge(e,r){if(e.leadingComments&&e.leadingComments.length>0)return e.leadingComments[e.leadingComments.length-1]??null;if(r&&(r.type==="ExportNamedDeclaration"||r.type==="ExportDefaultDeclaration")){const n=r;if(n.leadingComments&&n.leadingComments.length>0)return n.leadingComments[n.leadingComments.length-1]??null}return null}const ve=typeof Re=="function"?Re:Re.default;function Ge(e,r){var I,v;if(e==null)return{parseError:"Code content is null or undefined / 代码内容为空",errorType:z.VALIDATION_ERROR};if(typeof e!="string")return{parseError:"Code must be a string / 代码必须是字符串类型",errorType:z.VALIDATION_ERROR};if(!e.trim())return{description:"",imports:[],functions:[],classes:[],constants:[],callGraph:{}};const n={description:"",imports:[],functions:[],classes:[],constants:[],callGraph:{}},s=e.match(/^\/\*\*[\s\S]*?\*\//);if(s){const t=s[0].split(`
|
|
31
|
+
`).map(i=>i.replace(/^\s*\*\s?/,"").trim()).filter(i=>i&&!i.startsWith("/")&&!i.startsWith("@ai"));for(const i of t)if(i.startsWith("@description ")){n.description=i.slice(13).trim();break}if(!n.description&&t.length>0){const i=t.find(m=>!m.startsWith("@"));i&&(n.description=i)}}let c;try{const a=r&&(r.endsWith(".ts")||r.endsWith(".tsx"));c=nn.parse(e,{sourceType:"unambiguous",plugins:["jsx","classPrivateProperties","classPrivateMethods",...a?["typescript"]:[]]})}catch(a){const t=a;return{parseError:Se(z.PARSE_ERROR,`Syntax error: ${t.message} / 语法错误`,{file:r??void 0,line:(I=t.loc)==null?void 0:I.line,column:(v=t.loc)==null?void 0:v.column,suggestion:"Check syntax errors in the file / 检查文件中的语法错误"}),loc:t.loc,errorType:z.PARSE_ERROR}}const f=new Map,$=new Map,u=new Map;function p(a){if(!a)return null;if(a.type==="Identifier")return a.name;if(a.type==="MemberExpression"){const t=a.object,i=a.property;if(t.type==="Identifier"&&i.type==="Identifier")return t.name}return null}function h(a){var i,m,b;let t=a;for(;t;){if(t.node.type==="FunctionDeclaration"){const x=t.node;if(x.id)return x.id.name}if(t.node.type==="ClassMethod"){const x=t.node,R=(i=t.parentPath)==null?void 0:i.parentPath,C=R==null?void 0:R.node,j=((m=C==null?void 0:C.id)==null?void 0:m.name)??"",T=((b=x.key)==null?void 0:b.name)??"";return j?`${j}.${T}`:T}if(t.node.type==="ArrowFunctionExpression"||t.node.type==="FunctionExpression"){const x=t.parent;if((x==null?void 0:x.type)==="VariableDeclarator"){const C=x.id;if(C!=null&&C.name)return C.name}}t=t.parentPath}return null}ve(c,{VariableDeclarator(a){var i,m,b,x;const t=a.node;if(((i=t.init)==null?void 0:i.type)==="CallExpression"&&((m=t.init.callee)==null?void 0:m.type)==="Identifier"&&t.init.callee.name==="require"&&((x=(b=t.init.arguments)==null?void 0:b[0])==null?void 0:x.type)==="StringLiteral"){const R=t.init.arguments[0].value;if(f.has(R)||f.set(R,new Set),t.id.type==="Identifier"){const C=t.id.name;f.get(R).add(C),$.set(C,R),u.set(C,new Set)}else if(t.id.type==="ObjectPattern"){for(const C of t.id.properties)if(C.type==="ObjectProperty"&&C.key.type==="Identifier"){const j=C.value.type==="Identifier"?C.value.name:C.key.name;f.get(R).add(C.key.name),$.set(j,R),u.set(j,new Set)}}}},CallExpression(a){var i,m,b,x,R,C;const t=a.node;if(((i=t.callee)==null?void 0:i.type)==="Identifier"&&t.callee.name==="require"&&((b=(m=t.arguments)==null?void 0:m[0])==null?void 0:b.type)==="StringLiteral"){const j=a.parent;if((j==null?void 0:j.type)==="MemberExpression"&&((x=j.property)==null?void 0:x.type)==="Identifier"){const T=t.arguments[0].value;f.has(T)||f.set(T,new Set),f.get(T).add(j.property.name);const U=(R=a.parentPath)==null?void 0:R.parent;if((U==null?void 0:U.type)==="VariableDeclarator"){const q=U;((C=q.id)==null?void 0:C.type)==="Identifier"&&($.set(q.id.name,T),u.set(q.id.name,new Set))}}}},ImportDeclaration(a){const t=a.node,i=t.source.value;f.has(i)||f.set(i,new Set);for(const m of t.specifiers){let b,x;if(m.type==="ImportDefaultSpecifier")b="default",x=m.local.name;else if(m.type==="ImportNamespaceSpecifier")b="*",x=m.local.name;else if(m.type==="ImportSpecifier"){const R=m.imported;b=R.type==="Identifier"?R.name:R.value,x=m.local.name}b&&x&&(f.get(i).add(b),$.set(x,i),u.set(x,new Set))}}}),ve(c,{Identifier(a){const t=a.node.name;if(u.has(t)){const i=a.parent;if((i==null?void 0:i.type)==="VariableDeclarator"&&i.id===a.node||(i==null?void 0:i.type)==="ImportSpecifier"||(i==null?void 0:i.type)==="ImportDefaultSpecifier")return;const m=h(a);m&&u.get(t).add(m)}},FunctionDeclaration(a){var C,j,T,U,q;if(a.parent.type==="ExportDefaultDeclaration")return;const t=a.node,i=((C=t.id)==null?void 0:C.name)??"[anonymous]",m=t.params.map(G=>{var K,X;return G.type==="Identifier"?G.name:G.type==="AssignmentPattern"&&((K=G.left)==null?void 0:K.type)==="Identifier"?G.left.name+"?":G.type==="RestElement"&&((X=G.argument)==null?void 0:X.type)==="Identifier"?"..."+G.argument.name:"?"}),b=((T=(j=t.loc)==null?void 0:j.start)==null?void 0:T.line)??0,x=((q=(U=t.loc)==null?void 0:U.end)==null?void 0:q.line)??0,R=Y(ge(t,a.parent));n.functions.push({name:i,params:m.join(","),startLine:b,endLine:x,description:R})},ClassDeclaration(a){var j,T,U,q,G,K,X,ee,ne;if(a.parent.type==="ExportDefaultDeclaration")return;const t=a.node,i=((j=t.id)==null?void 0:j.name)??"[anonymous]",m=((U=(T=t.loc)==null?void 0:T.start)==null?void 0:U.line)??0,b=((G=(q=t.loc)==null?void 0:q.end)==null?void 0:G.line)??0,x=p(t.superClass),R=Y(ge(t,a.parent)),C=[];if((K=t.body)!=null&&K.body){for(const W of t.body.body)if(W.type==="ClassMethod"){const se=((X=W.key)==null?void 0:X.type)==="Identifier"?W.key.name:"[computed]",ie=W.params.map(H=>{var re;return H.type==="Identifier"?H.name:H.type==="AssignmentPattern"&&((re=H.left)==null?void 0:re.type)==="Identifier"?H.left.name+"?":"?"}),oe=((ne=(ee=W.loc)==null?void 0:ee.start)==null?void 0:ne.line)??0;let te="";const Z=W.leadingComments;Z&&Z.length>0&&(te=Y(Z[Z.length-1])),C.push({name:se,params:ie.join(","),line:oe,static:W.static,kind:W.kind,description:te})}}n.classes.push({name:i,superClass:x,startLine:m,endLine:b,methods:C,description:R})},VariableDeclaration(a){var b,x,R,C,j,T,U,q,G,K,X,ee,ne,W,se,ie,oe,te,Z,H,re,ce,le,fe,de,me,pe,ae,B,L,J;const t=a.parent.type;if(t!=="Program"&&t!=="ExportNamedDeclaration")return;const i=a.node,m=Y(ge(i,a.parent));for(const O of i.declarations){const _=((b=O.id)==null?void 0:b.type)==="Identifier"?O.id.name:void 0;if(_){if(((x=O.init)==null?void 0:x.type)==="ArrowFunctionExpression"||((R=O.init)==null?void 0:R.type)==="FunctionExpression"){const d=O.init.params.map(P=>{var N,V;return P.type==="Identifier"?P.name:P.type==="AssignmentPattern"&&((N=P.left)==null?void 0:N.type)==="Identifier"?P.left.name+"?":P.type==="RestElement"&&((V=P.argument)==null?void 0:V.type)==="Identifier"?"..."+P.argument.name:"?"}),A=((j=(C=i.loc)==null?void 0:C.start)==null?void 0:j.line)??0,M=((U=(T=i.loc)==null?void 0:T.end)==null?void 0:U.line)??0;n.functions.push({name:_,params:d.join(","),startLine:A,endLine:M,description:m});continue}if(((q=O.init)==null?void 0:q.type)==="ClassExpression"){const w=O.init,d=((K=(G=i.loc)==null?void 0:G.start)==null?void 0:K.line)??0,A=((ee=(X=i.loc)==null?void 0:X.end)==null?void 0:ee.line)??0,M=p(w.superClass),P=[];if((ne=w.body)!=null&&ne.body){for(const N of w.body.body)if(N.type==="ClassMethod"){const V=((W=N.key)==null?void 0:W.type)==="Identifier"?N.key.name:"[computed]",k=N.params.map(Ee=>{var Ue;return Ee.type==="Identifier"?Ee.name:Ee.type==="AssignmentPattern"&&((Ue=Ee.left)==null?void 0:Ue.type)==="Identifier"?Ee.left.name+"?":"?"}),D=((ie=(se=N.loc)==null?void 0:se.start)==null?void 0:ie.line)??0;let Q="";const ue=N.leadingComments;ue&&ue.length>0&&(Q=Y(ue[ue.length-1])),P.push({name:V,params:k.join(","),line:D,static:N.static,kind:N.kind,description:Q})}}n.classes.push({name:_,superClass:M,startLine:d,endLine:A,methods:P,description:m});continue}if(((oe=O.init)==null?void 0:oe.type)==="ObjectExpression"){const w=O.init;for(const d of w.properties)if(d.type==="ObjectMethod"){const A=((te=d.key)==null?void 0:te.type)==="Identifier"?`${_}.${d.key.name}`:`${_}.[computed]`,M=d.params.map(k=>{var D,Q;return k.type==="Identifier"?k.name:k.type==="AssignmentPattern"&&((D=k.left)==null?void 0:D.type)==="Identifier"?k.left.name+"?":k.type==="RestElement"&&((Q=k.argument)==null?void 0:Q.type)==="Identifier"?"..."+k.argument.name:"?"}),P=((H=(Z=d.loc)==null?void 0:Z.start)==null?void 0:H.line)??0,N=((ce=(re=d.loc)==null?void 0:re.end)==null?void 0:ce.line)??0;let V="";d.leadingComments&&d.leadingComments.length>0&&(V=Y(d.leadingComments[d.leadingComments.length-1])),n.functions.push({name:A,params:M.join(","),startLine:P,endLine:N,description:V})}else if(d.type==="ObjectProperty"&&((le=d.key)==null?void 0:le.type)==="Identifier"&&(((fe=d.value)==null?void 0:fe.type)==="ArrowFunctionExpression"||((de=d.value)==null?void 0:de.type)==="FunctionExpression")){const A=d.value,M=`${_}.${d.key.name}`,P=A.params.map(D=>{var Q,ue;return D.type==="Identifier"?D.name:D.type==="AssignmentPattern"&&((Q=D.left)==null?void 0:Q.type)==="Identifier"?D.left.name+"?":D.type==="RestElement"&&((ue=D.argument)==null?void 0:ue.type)==="Identifier"?"..."+D.argument.name:"?"}),N=((pe=(me=d.loc)==null?void 0:me.start)==null?void 0:pe.line)??0,V=((B=(ae=d.loc)==null?void 0:ae.end)==null?void 0:B.line)??0;let k="";d.leadingComments&&d.leadingComments.length>0&&(k=Y(d.leadingComments[d.leadingComments.length-1])),n.functions.push({name:M,params:P.join(","),startLine:N,endLine:V,description:k})}}if(i.kind==="const"&&_===_.toUpperCase()&&_.length>2){const w=((J=(L=i.loc)==null?void 0:L.start)==null?void 0:J.line)??0;n.constants.push({name:_,line:w,description:m})}}}},ExportDefaultDeclaration(a){var b,x,R,C,j,T,U,q,G,K,X,ee,ne,W,se,ie,oe,te,Z,H,re,ce,le,fe,de,me,pe,ae,B;const t=a.node,i=t.declaration,m=Y(ge(t,a.parent));if(i.type==="FunctionDeclaration"){const L=i,J=((b=L.id)==null?void 0:b.name)??"[default]",O=L.params.map(d=>{var A,M;return d.type==="Identifier"?d.name:d.type==="AssignmentPattern"&&((A=d.left)==null?void 0:A.type)==="Identifier"?d.left.name+"?":d.type==="RestElement"&&((M=d.argument)==null?void 0:M.type)==="Identifier"?"..."+d.argument.name:"?"}),_=((R=(x=t.loc)==null?void 0:x.start)==null?void 0:R.line)??0,w=((j=(C=t.loc)==null?void 0:C.end)==null?void 0:j.line)??0;n.functions.push({name:J,params:O.join(","),startLine:_,endLine:w,description:m});return}if(i.type==="ClassDeclaration"){const L=i,J=((T=L.id)==null?void 0:T.name)??"[default]",O=((q=(U=t.loc)==null?void 0:U.start)==null?void 0:q.line)??0,_=((K=(G=t.loc)==null?void 0:G.end)==null?void 0:K.line)??0,w=p(L.superClass),d=[];if((X=L.body)!=null&&X.body){for(const A of L.body.body)if(A.type==="ClassMethod"){const M=((ee=A.key)==null?void 0:ee.type)==="Identifier"?A.key.name:"[computed]",P=A.params.map(D=>{var Q;return D.type==="Identifier"?D.name:D.type==="AssignmentPattern"&&((Q=D.left)==null?void 0:Q.type)==="Identifier"?D.left.name+"?":"?"}),N=((W=(ne=A.loc)==null?void 0:ne.start)==null?void 0:W.line)??0;let V="";const k=A.leadingComments;k&&k.length>0&&(V=Y(k[k.length-1])),d.push({name:M,params:P.join(","),line:N,static:A.static,kind:A.kind,description:V})}}n.classes.push({name:J,superClass:w,startLine:O,endLine:_,methods:d,description:m});return}if(i.type==="ArrowFunctionExpression"||i.type==="FunctionExpression"){const L=i,J=L.type==="FunctionExpression"&&((se=L.id)!=null&&se.name)?L.id.name:"[default]",O=L.params.map(d=>{var A,M;return d.type==="Identifier"?d.name:d.type==="AssignmentPattern"&&((A=d.left)==null?void 0:A.type)==="Identifier"?d.left.name+"?":d.type==="RestElement"&&((M=d.argument)==null?void 0:M.type)==="Identifier"?"..."+d.argument.name:"?"}),_=((oe=(ie=t.loc)==null?void 0:ie.start)==null?void 0:oe.line)??0,w=((Z=(te=t.loc)==null?void 0:te.end)==null?void 0:Z.line)??0;n.functions.push({name:J,params:O.join(","),startLine:_,endLine:w,description:m});return}if(i.type==="ClassExpression"){const L=i,J=((H=L.id)==null?void 0:H.name)??"[default]",O=((ce=(re=t.loc)==null?void 0:re.start)==null?void 0:ce.line)??0,_=((fe=(le=t.loc)==null?void 0:le.end)==null?void 0:fe.line)??0,w=((de=L.superClass)==null?void 0:de.type)==="Identifier"?L.superClass.name:null,d=[];if((me=L.body)!=null&&me.body){for(const A of L.body.body)if(A.type==="ClassMethod"){const M=((pe=A.key)==null?void 0:pe.type)==="Identifier"?A.key.name:"[computed]",P=A.params.map(D=>{var Q;return D.type==="Identifier"?D.name:D.type==="AssignmentPattern"&&((Q=D.left)==null?void 0:Q.type)==="Identifier"?D.left.name+"?":"?"}),N=((B=(ae=A.loc)==null?void 0:ae.start)==null?void 0:B.line)??0;let V="";const k=A.leadingComments;k&&k.length>0&&(V=Y(k[k.length-1])),d.push({name:M,params:P.join(","),line:N,static:A.static,kind:A.kind,description:V})}}n.classes.push({name:J,superClass:w,startLine:O,endLine:_,methods:d,description:m})}},AssignmentExpression(a){var x,R,C,j,T,U,q,G,K,X,ee,ne,W,se,ie,oe,te,Z,H,re,ce,le,fe,de,me,pe;const t=a.node;if(a.parent.type!=="ExpressionStatement")return;const i=(x=a.parentPath)==null?void 0:x.parent;if((i==null?void 0:i.type)!=="Program")return;const m=t.left,b=t.right;if(m.type==="MemberExpression"&&((R=m.object)==null?void 0:R.type)==="Identifier"&&m.object.name==="module"&&((C=m.property)==null?void 0:C.type)==="Identifier"&&m.property.name==="exports"){const ae=Y(ge(a.parent,i));if(b.type==="FunctionExpression"||b.type==="ArrowFunctionExpression"){const B=b,L=b.type==="FunctionExpression"&&((j=b.id)!=null&&j.name)?b.id.name:"[exports]",J=B.params.map(w=>{var d,A;return w.type==="Identifier"?w.name:w.type==="AssignmentPattern"&&((d=w.left)==null?void 0:d.type)==="Identifier"?w.left.name+"?":w.type==="RestElement"&&((A=w.argument)==null?void 0:A.type)==="Identifier"?"..."+w.argument.name:"?"}),O=((U=(T=t.loc)==null?void 0:T.start)==null?void 0:U.line)??0,_=((G=(q=t.loc)==null?void 0:q.end)==null?void 0:G.line)??0;n.functions.push({name:L,params:J.join(","),startLine:O,endLine:_,description:ae});return}if(b.type==="ClassExpression"){const B=b,L=((K=B.id)==null?void 0:K.name)??"[exports]",J=((ee=(X=t.loc)==null?void 0:X.start)==null?void 0:ee.line)??0,O=((W=(ne=t.loc)==null?void 0:ne.end)==null?void 0:W.line)??0,_=p(B.superClass),w=[];if((se=B.body)!=null&&se.body){for(const d of B.body.body)if(d.type==="ClassMethod"){const A=((ie=d.key)==null?void 0:ie.type)==="Identifier"?d.key.name:"[computed]",M=d.params.map(N=>{var V;return N.type==="Identifier"?N.name:N.type==="AssignmentPattern"&&((V=N.left)==null?void 0:V.type)==="Identifier"?N.left.name+"?":"?"}),P=((te=(oe=d.loc)==null?void 0:oe.start)==null?void 0:te.line)??0;w.push({name:A,params:M.join(","),line:P,static:d.static,kind:d.kind,description:""})}}n.classes.push({name:L,superClass:_,startLine:J,endLine:O,methods:w,description:ae});return}}if(m.type==="MemberExpression"&&((Z=m.property)==null?void 0:Z.type)==="Identifier"){const ae=m.property.name;let B=!1;if(((H=m.object)==null?void 0:H.type)==="Identifier"&&m.object.name==="exports"&&(B=!0),((re=m.object)==null?void 0:re.type)==="MemberExpression"&&((ce=m.object.object)==null?void 0:ce.type)==="Identifier"&&m.object.object.name==="module"&&((le=m.object.property)==null?void 0:le.type)==="Identifier"&&m.object.property.name==="exports"&&(B=!0),B){const L=Y(ge(a.parent,i));if(b.type==="FunctionExpression"||b.type==="ArrowFunctionExpression"){const O=b.params.map(d=>{var A,M;return d.type==="Identifier"?d.name:d.type==="AssignmentPattern"&&((A=d.left)==null?void 0:A.type)==="Identifier"?d.left.name+"?":d.type==="RestElement"&&((M=d.argument)==null?void 0:M.type)==="Identifier"?"..."+d.argument.name:"?"}),_=((de=(fe=t.loc)==null?void 0:fe.start)==null?void 0:de.line)??0,w=((pe=(me=t.loc)==null?void 0:me.end)==null?void 0:pe.line)??0;n.functions.push({name:ae,params:O.join(","),startLine:_,endLine:w,description:L})}}}}});for(const[a,t]of f){const i=new Set;for(const m of $.keys())if($.get(m)===a&&u.has(m))for(const b of u.get(m))i.add(b);n.imports.push({module:a,members:Array.from(t),usedIn:Array.from(i)})}const o=new Set;for(const a of n.functions)o.add(a.name);for(const a of n.classes)for(const t of a.methods)o.add(t.name),o.add(`${a.name}.${t.name}`);const y=new Set($.keys()),l=new Map;ve(c,{CallExpression(a){var m,b;const t=a.node;let i=null;if(t.callee.type==="Identifier")i=t.callee.name;else if(t.callee.type==="MemberExpression"&&((m=t.callee.property)==null?void 0:m.type)==="Identifier"){const x=((b=t.callee.object)==null?void 0:b.type)==="Identifier"?t.callee.object.name:void 0,R=t.callee.property.name;x&&y.has(x)?i=`${x}.${R}`:i=R}if(i){const x=h(a);if(x&&x!==i){const R=o.has(i),C=y.has(i)||i.includes(".")&&y.has(i.split(".")[0]);(R||C)&&(l.has(x)||l.set(x,new Set),l.get(x).add(i))}}}}),n.callGraph={};for(const[a,t]of l)n.callGraph[a]=Array.from(t);return n.isPureType=yn(c),n}function yn(e){let r=!1;for(const n of e.program.body){switch(n.type){case"TSTypeAliasDeclaration":case"TSInterfaceDeclaration":case"TSEnumDeclaration":break;case"ImportDeclaration":n.importKind!=="type"&&n.specifiers.some(c=>c.type==="ImportSpecifier"?c.importKind!=="type":!0)&&n.specifiers.length>0&&(r=!0);break;case"ExportNamedDeclaration":if(n.exportKind==="type")break;if(n.declaration){const s=n.declaration.type;s!=="TSTypeAliasDeclaration"&&s!=="TSInterfaceDeclaration"&&(r=!0)}else n.specifiers&&n.specifiers.length>0&&n.specifiers.some(c=>c.type==="ExportSpecifier"?c.exportKind!=="type":!0)&&(r=!0);break;case"ExportDefaultDeclaration":r=!0;break;case"ExportAllDeclaration":n.exportKind!=="type"&&(r=!0);break;default:r=!0;break}if(r)break}return!r}function gn(e,r){const n=[];let s=`/*@AI ${r}`;e.description&&(s+=` - ${e.description.slice(0,50)}`),n.push(s);for(const c of e.imports){const f=Array.isArray(c.members)?c.members.join(","):"";let $=`<${c.module}:${f}`;Array.isArray(c.usedIn)&&c.usedIn.length>0&&($+=` ->${c.usedIn.join(",")}`),n.push($)}for(const c of e.classes){let f=c.name;c.superClass&&(f+=`:${c.superClass}`),f+=` ${c.startLine}-${c.endLine}`,c.description&&(f+=` ${c.description}`),n.push(f);for(const $ of c.methods){const u=$.static?" +":" .",p=$.kind==="get"?"get:":$.kind==="set"?"set:":"";let h=`${u}${p}${$.name}(${$.params}) ${$.line}`;$.description&&(h+=` ${$.description}`),n.push(h)}}for(const c of e.functions){let f=`${c.name}(${c.params}) ${c.startLine}-${c.endLine}`;c.description&&(f+=` ${c.description}`),n.push(f)}for(const c of e.constants){let f=`${c.name} ${c.line}`;c.description&&(f+=` ${c.description}`),n.push(f)}return n.push("@AI*/"),n.join(`
|
|
32
|
+
`)}function hn(e){let r=e;return r=r.replace(/\/\*@AI[\s\S]*?@AI\*\/\s*/g,""),r=r.replace(/\/\*\*[\s\S]*?@ai-context-end[\s\S]*?\*\/\s*/g,""),r=r.replace(/^\/\*\*[\s\S]*?\*\/\s*\n?/,""),r}function Oe(e,r){var s,c,f;const n=[`@FNMAP ${g.basename(e)}/`];for(const{relativePath:$,info:u}of r){let h=`#${g.basename($)}`;u.description&&(h+=` ${u.description.slice(0,50)}`),n.push(h);for(const o of u.imports){const y=Array.isArray(o.members)?o.members.join(","):"";n.push(` <${o.module}:${y}`)}for(const o of u.classes){let y=` ${o.name}`;o.superClass&&(y+=`:${o.superClass}`),y+=` ${o.startLine}-${o.endLine}`,o.description&&(y+=` ${o.description}`),n.push(y);for(const l of o.methods){const I=l.static?" +":" .",v=l.kind==="get"?"get:":l.kind==="set"?"set:":"";let a=`${I}${v}${l.name}(${l.params}) ${l.line}`;l.description&&(a+=` ${l.description}`);const t=`${o.name}.${l.name}`,i=((s=u.callGraph)==null?void 0:s[t])??((c=u.callGraph)==null?void 0:c[l.name]);Array.isArray(i)&&i.length>0&&(a+=` →${i.join(",")}`),n.push(a)}}for(const o of u.functions){let y=` ${o.name}(${o.params}) ${o.startLine}-${o.endLine}`;o.description&&(y+=` ${o.description}`);const l=(f=u.callGraph)==null?void 0:f[o.name];Array.isArray(l)&&l.length>0&&(y+=` →${l.join(",")}`),n.push(y)}for(const o of u.constants){let y=` ${o.name} ${o.line}`;o.description&&(y+=` ${o.description}`),n.push(y)}}return n.push("@FNMAP"),n.join(`
|
|
33
|
+
`)}function Xe(e,r){const n=["flowchart TD"],s=o=>"id_"+o.replace(/[^a-zA-Z0-9]/g,y=>`_${y.charCodeAt(0)}_`),c=o=>o.replace(/"/g,"#quot;"),f=r.functions.map(o=>o.name),$=[];for(const o of r.classes)for(const y of o.methods)$.push(`${o.name}.${y.name}`);const u=[...f,...$];if(u.length===0)return null;const p=g.basename(e,g.extname(e));n.push(` subgraph ${s(p)}["${p}"]`);for(const o of u)n.push(` ${s(o)}["${c(o)}"]`);n.push(" end");const h=r.callGraph??{};for(const[o,y]of Object.entries(h))if(Array.isArray(y)){for(const l of y)if(u.includes(l)||l.includes(".")){const I=u.includes(l)?l:l.split(".").pop();(u.includes(l)||u.some(v=>v.endsWith(I)))&&n.push(` ${s(o)} --> ${s(l)}`)}}return n.join(`
|
|
34
|
+
`)}function He(e,r){const n=["flowchart TD"],s=p=>"id_"+p.replace(/[^a-zA-Z0-9]/g,h=>`_${h.charCodeAt(0)}_`),c=p=>p.replace(/"/g,"#quot;"),f=new Map,$=[];for(const{relativePath:p,info:h}of r){const o=g.basename(p,g.extname(p)),y=h.functions.map(a=>a.name),l=[];for(const a of h.classes)for(const t of a.methods)l.push(`${a.name}.${t.name}`);const I=[...y,...l];f.set(p,{fileName:o,functions:I});const v=h.callGraph??{};for(const[a,t]of Object.entries(v))if(Array.isArray(t))for(const i of t)$.push({file:p,fileName:o,caller:a,callee:i})}for(const[,{fileName:p,functions:h}]of f)if(h.length!==0){n.push(` subgraph ${s(p)}["${c(p)}"]`);for(const o of h)n.push(` ${s(p)}_${s(o)}["${c(o)}"]`);n.push(" end")}const u=new Set;for(const{fileName:p,caller:h,callee:o}of $){const y=`${s(p)}_${s(h)}`;let l=null;for(const[,{fileName:I,functions:v}]of f)if(v.includes(o)){l=`${s(I)}_${s(o)}`;break}if(!l){const I=[...f.keys()].find(v=>{var a;return((a=f.get(v))==null?void 0:a.fileName)===p});if(I){const v=f.get(I);v!=null&&v.functions.includes(o)&&(l=`${s(p)}_${s(o)}`)}}if(l){const I=`${y}-->${l}`;u.has(I)||(n.push(` ${y} --> ${l}`),u.add(I))}}return n.join(`
|
|
35
|
+
`)}function $n(e,r){const n=(r==null?void 0:r.filePath)??null;try{const s=Ge(e,n);return s?ke(s)?{success:!1,error:s.parseError,errorType:s.errorType,loc:s.loc}:{success:!0,info:s}:{success:!1,error:"Analysis returned null / 分析返回空值",errorType:z.PARSE_ERROR}}catch(s){return{success:!1,error:`Failed to process code / 处理代码失败: ${s.message}`,errorType:z.PARSE_ERROR}}}function Qe(e){const r=ze(e);if(!r.valid)return{success:!1,error:r.error,errorType:r.errorType??z.VALIDATION_ERROR};try{const n=F.readFileSync(e,"utf-8"),s=Ge(n,e);return s?ke(s)?{success:!1,error:s.parseError,errorType:s.errorType,loc:s.loc}:{success:!0,info:s}:{success:!1,error:"Analysis returned null / 分析返回空值",errorType:z.PARSE_ERROR}}catch(n){const s=n;return{success:!1,error:Se(z.FILE_READ_ERROR,"Failed to read or process file / 读取或处理文件失败",{file:e,suggestion:s.message}),errorType:z.FILE_READ_ERROR}}}const qe=`
|
|
36
36
|
|
|
37
37
|
## .fnmap Code Index Format
|
|
38
38
|
|
|
@@ -57,24 +57,24 @@ The \`.fnmap\` file provides a structured code index for quick navigation. Read
|
|
|
57
57
|
1. Every global variable, function, class, and file module must have a **concise comment describing its purpose or functionality** - avoid describing anything else
|
|
58
58
|
2. When updating code, always update related comments to reflect the changes
|
|
59
59
|
3. Prefer encapsulating logic in functions rather than writing flat, sequential code
|
|
60
|
-
`,
|
|
60
|
+
`,Ve=`
|
|
61
61
|
# fnmap generated files
|
|
62
62
|
.fnmap
|
|
63
63
|
*.fnmap
|
|
64
64
|
*.mermaid
|
|
65
65
|
.fnmap.mermaid
|
|
66
|
-
`;function Ce(e,r){return new Promise(
|
|
67
|
-
`);console.log(`${S.green}✓${S.reset} Added fnmap rules to .gitignore`)}function we(e,r){const
|
|
68
|
-
`);console.log(`${S.green}✓${S.reset} Added fnmap documentation to ${r}`)}async function
|
|
69
|
-
${S.bold}fnmap - Interactive Setup${S.reset}`),console.log("=".repeat(50));try{const
|
|
70
|
-
Add fnmap rules to .gitignore? (Y/n): `)).toLowerCase()!=="n"&&
|
|
66
|
+
`;function Ce(e,r){return new Promise(n=>{e.question(r,s=>{n(s.trim())})})}function Ze(e,r){let n=0;const s=["node_modules",".git","dist","build",".next","coverage"];if(!F.existsSync(e))return E.warn(`Directory does not exist: ${e}`),0;const c=F.readdirSync(e,{withFileTypes:!0});for(const f of c){const $=g.join(e,f.name);if(f.isDirectory()){if(s.includes(f.name))continue;n+=Ze($,r)}else if(f.isFile()){const u=f.name;if(u===".fnmap"||u.endsWith(".fnmap")||u.endsWith(".mermaid"))try{F.unlinkSync($),E.success(`Deleted: ${g.relative(r,$)}`),n++}catch(p){const h=p;E.error(`Failed to delete ${g.relative(r,$)}: ${h.message}`)}}}return n}function En(e,r){E.title("fnmap - Clear Generated Files"),E.info("=".repeat(50));const n=r?g.resolve(e,r):e,s=Ze(n,e);E.info("=".repeat(50)),s>0?E.success(`Cleared ${s} generated file(s)`):E.info("No generated files found")}function xn(e){const r=g.join(e,".gitignore");if(F.existsSync(r)){const n=F.readFileSync(r,"utf-8");if(n.includes("fnmap generated files")||n.includes("*.fnmap")){console.log(`${S.yellow}!${S.reset} .gitignore already contains fnmap rules`);return}F.appendFileSync(r,Ve)}else F.writeFileSync(r,Ve.trim()+`
|
|
67
|
+
`);console.log(`${S.green}✓${S.reset} Added fnmap rules to .gitignore`)}function we(e,r){const n=g.dirname(e);if(F.existsSync(n)||F.mkdirSync(n,{recursive:!0}),F.existsSync(e)){if(F.readFileSync(e,"utf-8").includes(".fnmap Code Index Format")){console.log(`${S.yellow}!${S.reset} ${r} already contains fnmap documentation`);return}F.appendFileSync(e,qe)}else F.writeFileSync(e,qe.trim()+`
|
|
68
|
+
`);console.log(`${S.green}✓${S.reset} Added fnmap documentation to ${r}`)}async function In(e){const r=on.createInterface({input:process.stdin,output:process.stdout});console.log(`
|
|
69
|
+
${S.bold}fnmap - Interactive Setup${S.reset}`),console.log("=".repeat(50));try{const n=g.join(e,".fnmaprc");if(F.existsSync(n))console.log(`${S.yellow}!${S.reset} Config file already exists: .fnmaprc`);else{const y={enable:!0,include:["src/**/*.js","src/**/*.ts","src/**/*.jsx","src/**/*.tsx"],exclude:["node_modules","dist","build",".next","coverage","__pycache__",".cache"]};F.writeFileSync(n,JSON.stringify(y,null,2)),console.log(`${S.green}✓${S.reset} Created config file: .fnmaprc`)}(await Ce(r,`
|
|
70
|
+
Add fnmap rules to .gitignore? (Y/n): `)).toLowerCase()!=="n"&&xn(e);const c=g.join(e,"CLAUDE.md"),f=g.join(e,"AGENTS.md"),$=g.join(tn.homedir(),".claude","CLAUDE.md"),u=F.existsSync(c),p=F.existsSync(f),h=F.existsSync($);if(!(u||p))console.log(`
|
|
71
71
|
${S.yellow}!${S.reset} No CLAUDE.md or AGENTS.md found in project.`),(await Ce(r,"Create CLAUDE.md with fnmap documentation? (Y/n): ")).toLowerCase()!=="n"&&we(c,"CLAUDE.md");else{console.log(`
|
|
72
72
|
${S.bold}Select files to add fnmap documentation:${S.reset}`),console.log(`(Enter numbers separated by comma, e.g., 1,2)
|
|
73
|
-
`);const y=[];let l=1;u&&y.push({key:String(l++),label:"Project CLAUDE.md",path:c,exists:!0}),y.push({key:String(l++),label:"User CLAUDE.md (~/.claude/CLAUDE.md)",path:$,exists:h}),p&&y.push({key:String(l++),label:"AGENTS.md",path:f,exists:!0});const I=String(l);for(const
|
|
74
|
-
`);const a=(await Ce(r,"Your choice: ")).split(",").map(
|
|
73
|
+
`);const y=[];let l=1;u&&y.push({key:String(l++),label:"Project CLAUDE.md",path:c,exists:!0}),y.push({key:String(l++),label:"User CLAUDE.md (~/.claude/CLAUDE.md)",path:$,exists:h}),p&&y.push({key:String(l++),label:"AGENTS.md",path:f,exists:!0});const I=String(l);for(const t of y){const i=t.exists?`${S.green}[exists]${S.reset}`:`${S.gray}[new]${S.reset}`;console.log(` ${t.key}. ${t.label} ${i}`)}console.log(` ${I}. Custom file path`),console.log(` 0. Skip
|
|
74
|
+
`);const a=(await Ce(r,"Your choice: ")).split(",").map(t=>t.trim()).filter(Boolean);if(a.includes("0")||a.length===0)console.log("Skipped adding documentation to files.");else for(const t of a)if(t===I){const i=await Ce(r,"Enter custom file path: ");if(i){const m=g.isAbsolute(i)?i:g.resolve(e,i);we(m,i)}}else{const i=y.find(m=>m.key===t);i&&we(i.path,i.label)}}console.log(`
|
|
75
75
|
`+"=".repeat(50)),console.log(`${S.green}✓${S.reset} Setup complete!`),console.log(`
|
|
76
|
-
Run ${S.bold}fnmap${S.reset} or ${S.bold}fnmap --dir src${S.reset} to generate code index.`)}finally{r.close()}}async function
|
|
77
|
-
Analyzing: ${y}`);const l=
|
|
78
|
-
Generating .fnmap index...`),$)for(const[o,y]of h)for(const{relativePath:l,info:I}of y)try{const v=
|
|
79
|
-
Generating Mermaid call graphs...`),r.mermaid==="file"||r.mermaid===!0)for(const[o,y]of h)for(const{relativePath:l,info:I}of y)try{const v=
|
|
80
|
-
`+"=".repeat(50)),E.stats(`Complete! Analyzed: ${S.green}${u}${S.reset}, Failed: ${p>0?S.red:""}${p}${S.reset}`),E.info("=".repeat(50))}require.main===module&&
|
|
76
|
+
Run ${S.bold}fnmap${S.reset} or ${S.bold}fnmap --dir src${S.reset} to generate code index.`)}finally{r.close()}}async function Ye(){const e=Me();e.parse(process.argv);const r=e.opts(),n=e.args;r.log&&he(!1);const s=g.resolve(r.project);if(r.clear){const o=_e();he(!1),En(s,r.dir),he(o);return}if(r.init){const o=_e();he(!1),await In(s),he(o);return}const c=[...r.files??[],...n.map($e)].filter(o=>F.existsSync(o));let f=[],$=!1;if(r.changed||r.staged){const o=Ke(s,r.staged);if(o.length===0){E.info("No git changed code files detected");return}const y=new Set;for(const l of o)y.add(g.dirname(l));for(const l of y){const I=un(l);f.push(...I)}}else if(c.length>0)$=!0,f=c.map(o=>g.isAbsolute(o)?o:g.resolve(s,o));else if(r.dir){const o=g.resolve(s,r.dir);f=Ae(o,s).map(l=>g.join(s,l))}else{const{config:o,source:y}=Be(s);if(o){if(E.info(`Using config: ${y}`),o.enable===!1){E.info("Config file has enable set to false, skipping processing");return}const l=Je(o),I=[...Te,...l.exclude];if(l.include&&l.include.length>0){const v=new Set;for(const a of l.include){const t=a.replace(/\/\*\*\/.*$/,"").replace(/\*\*\/.*$/,"").replace(/\/\*\..*$/,"").replace(/\*\..*$/,"");t&&v.add(t)}for(const a of v){const t=g.resolve(s,a);if(F.existsSync(t)){const i=Ae(t,s,I);f.push(...i.map(m=>g.join(s,m)))}}}}else{E.warn("No config file found. Use fnmap init to create config, or use --dir/--files to specify scope"),E.info(""),E.info("Supported config files: .fnmaprc, .fnmaprc.json, package.json#fnmap");return}}if(f.length===0){E.info("No files found to process");return}f=[...new Set(f)],E.info("=".repeat(50)),E.title("fnmap - AI Code Indexing Tool"),E.info("=".repeat(50));let u=0,p=0;const h=new Map;for(const o of f){const y=g.relative(s,o);E.info(`
|
|
77
|
+
Analyzing: ${y}`);const l=Qe(o);if(l.success){u++;const I=l.info;if(I.isPureType){E.info("Skipped (pure type file) / 跳过纯类型文件");continue}E.success(`Imports: ${I.imports.length}, Functions: ${I.functions.length}, Classes: ${I.classes.length}, Constants: ${I.constants.length}`);const v=g.dirname(o);h.has(v)||h.set(v,[]),h.get(v).push({relativePath:y,info:I})}else p++,E.error(l.error)}if(h.size>0)if(E.info(`
|
|
78
|
+
Generating .fnmap index...`),$)for(const[o,y]of h)for(const{relativePath:l,info:I}of y)try{const v=Oe(o,[{relativePath:l,info:I}]),a=g.basename(l,g.extname(l)),t=g.join(o,`${a}.fnmap`);F.writeFileSync(t,v),E.success(g.relative(s,t))}catch(v){const a=v;E.error(`Failed to generate .fnmap for ${l}: ${a.message}`)}else for(const[o,y]of h)try{const l=Oe(o,y),I=g.join(o,".fnmap");F.writeFileSync(I,l),E.success(g.relative(s,I))}catch(l){const I=l;E.error(`Failed to generate .fnmap for ${g.relative(s,o)}: ${I.message}`)}if(r.mermaid&&h.size>0){if(E.info(`
|
|
79
|
+
Generating Mermaid call graphs...`),r.mermaid==="file"||r.mermaid===!0)for(const[o,y]of h)for(const{relativePath:l,info:I}of y)try{const v=Xe(l,I);if(v){const a=g.basename(l,g.extname(l)),t=g.join(o,`${a}.mermaid`);F.writeFileSync(t,v),E.success(g.relative(s,t))}}catch(v){const a=v;E.error(`Failed to generate mermaid for ${l}: ${a.message}`)}else if(r.mermaid==="project")try{const o=[];for(const[,I]of h)o.push(...I);const y=He(s,o),l=g.join(s,".fnmap.mermaid");F.writeFileSync(l,y),E.success(g.relative(s,l))}catch(o){const y=o;E.error(`Failed to generate project mermaid: ${y.message}`)}}E.info(`
|
|
80
|
+
`+"=".repeat(50)),E.stats(`Complete! Analyzed: ${S.green}${u}${S.reset}, Failed: ${p>0?S.red:""}${p}${S.reset}`),E.info("=".repeat(50))}require.main===module&&Ye().catch(e=>{console.error(e),process.exit(1)});exports.COLORS=S;exports.DEFAULT_CONFIG=Ne;exports.DEFAULT_EXCLUDES=Te;exports.ErrorTypes=z;exports.MAX_DIR_DEPTH=je;exports.MAX_FILE_SIZE=Le;exports.SUPPORTED_EXTENSIONS=Fe;exports.analyzeFile=Ge;exports.extractJSDocDescription=Y;exports.formatError=Se;exports.generateAiMap=Oe;exports.generateFileMermaid=Xe;exports.generateHeader=gn;exports.generateProjectMermaid=He;exports.getGitChangedFiles=Ke;exports.getVersion=We;exports.isParseError=ke;exports.isProcessFailure=cn;exports.isProcessSuccess=an;exports.isQuietMode=_e;exports.isValidationFailure=fn;exports.isValidationSuccess=ln;exports.loadConfig=Be;exports.logger=E;exports.main=Ye;exports.mergeConfig=Je;exports.normalizePath=$e;exports.normalizePaths=dn;exports.processCode=$n;exports.processFile=Qe;exports.program=mn;exports.removeExistingHeaders=hn;exports.scanDirectory=Ae;exports.setQuietMode=he;exports.setupCLI=Me;exports.validateConfig=De;exports.validateFilePath=ze;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@didnhdj/fnmap",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "AI code indexing tool for analyzing JS/TS code structure and generating structured code maps to help AI understand code quickly",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|