@didnhdj/fnmap 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,307 @@
1
+ # fnmap
2
+
3
+ > AI code indexing tool for analyzing JS/TS code structure and generating structured code maps
4
+
5
+ [中文文档](./README_CN.md)
6
+
7
+ ## Features
8
+
9
+ - 🚀 **Fast Analysis**: Quickly analyze JavaScript/TypeScript code structure using AST
10
+ - 📊 **Structured Output**: Generate `.fnmap` index files with imports, functions, classes, and constants
11
+ - 🔗 **Call Graph**: Track function call relationships and dependencies
12
+ - 📈 **Mermaid Diagrams**: Generate visual call graphs in Mermaid format
13
+ - 🎯 **Git Integration**: Process only changed files for efficient workflows
14
+ - ⚙️ **Flexible Configuration**: Support for multiple configuration methods
15
+ - 🔌 **Pre-commit Hook**: Integrate seamlessly with git hooks
16
+
17
+ ## Installation
18
+
19
+ ```bash
20
+ npm install -g fnmap
21
+ ```
22
+
23
+ Or use in your project:
24
+
25
+ ```bash
26
+ npm install --save-dev fnmap
27
+ ```
28
+
29
+ ## Quick Start
30
+
31
+ ### Initialize Configuration
32
+
33
+ ```bash
34
+ fnmap --init
35
+ ```
36
+
37
+ This creates a `.fnmaprc` configuration file in your project root.
38
+
39
+ ### Basic Usage
40
+
41
+ ```bash
42
+ # Process files based on configuration
43
+ fnmap
44
+
45
+ # Process specific directory
46
+ fnmap --dir src
47
+
48
+ # Process specific files
49
+ fnmap --files index.js,utils.js
50
+
51
+ # Process git changed files
52
+ fnmap --changed
53
+
54
+ # Process git staged files (for pre-commit hook)
55
+ fnmap --staged -q
56
+ ```
57
+
58
+ ### Generate Call Graphs
59
+
60
+ ```bash
61
+ # Generate file-level Mermaid diagrams
62
+ fnmap --mermaid file --dir src
63
+
64
+ # Generate project-level Mermaid diagram
65
+ fnmap --mermaid project
66
+ ```
67
+
68
+ ## Configuration
69
+
70
+ fnmap supports multiple configuration methods (in priority order):
71
+
72
+ 1. `.fnmaprc` - JSON configuration file
73
+ 2. `.fnmaprc.json` - JSON configuration file
74
+ 3. `package.json#fnmap` - fnmap field in package.json
75
+
76
+ ### Configuration Example
77
+
78
+ **.fnmaprc**
79
+ ```json
80
+ {
81
+ "enable": true,
82
+ "include": [
83
+ "src/**/*.js",
84
+ "src/**/*.ts",
85
+ "src/**/*.jsx",
86
+ "src/**/*.tsx"
87
+ ],
88
+ "exclude": [
89
+ "node_modules",
90
+ "dist",
91
+ "build"
92
+ ]
93
+ }
94
+ ```
95
+
96
+ **package.json**
97
+ ```json
98
+ {
99
+ "fnmap": {
100
+ "enable": true,
101
+ "include": ["src/**/*.js", "src/**/*.ts"],
102
+ "exclude": ["dist"]
103
+ }
104
+ }
105
+ ```
106
+
107
+ ## Output Files
108
+
109
+ ### .fnmap Index File
110
+
111
+ The `.fnmap` file contains structured information about your code:
112
+
113
+ ```
114
+ @FNMAP src/
115
+ #utils.js Utility functions
116
+ <fs:readFileSync,writeFileSync
117
+ <path:join,resolve
118
+ readConfig(filePath) 10-25 Read configuration file
119
+ parseData(data) 27-40 Parse data →JSON.parse
120
+ saveFile(path,content) 42-50 Save file →fs.writeFileSync,path.join
121
+ @FNMAP
122
+ ```
123
+
124
+ **Format Description:**
125
+ - `#filename` - File name and description
126
+ - `<module:members` - Imported modules and members
127
+ - `functionName(params) startLine-endLine description →calls` - Function information with call graph
128
+ - `ClassName:SuperClass startLine-endLine` - Class information
129
+ - ` .methodName(params) line description →calls` - Instance method
130
+ - ` +methodName(params) line description →calls` - Static method
131
+ - `CONSTANT_NAME line description` - Constant definition
132
+
133
+ ### Mermaid Call Graph
134
+
135
+ When using `--mermaid` option, generates visual call graphs:
136
+
137
+ **File-level** (`filename.mermaid`):
138
+ ```mermaid
139
+ flowchart TD
140
+ subgraph utils["utils"]
141
+ readConfig["readConfig"]
142
+ parseData["parseData"]
143
+ saveFile["saveFile"]
144
+ end
145
+ readConfig --> parseData
146
+ saveFile --> parseData
147
+ ```
148
+
149
+ **Project-level** (`.fnmap.mermaid`):
150
+ Shows call relationships across all files in the project.
151
+
152
+ ## CLI Options
153
+
154
+ ```
155
+ Usage: fnmap [options] [files...]
156
+
157
+ Options:
158
+ -v, --version Show version number
159
+ -f, --files <files> Process specific files (comma-separated)
160
+ -d, --dir <dir> Process all code files in directory
161
+ -p, --project <dir> Specify project root directory (default: current directory)
162
+ -c, --changed Process git changed files (staged + modified + untracked)
163
+ -s, --staged Process git staged files (for pre-commit hook)
164
+ -m, --mermaid [mode] Generate Mermaid call graph (file=file-level, project=project-level)
165
+ -q, --quiet Quiet mode (suppress output)
166
+ --init Create default configuration file .fnmaprc
167
+ -h, --help Display help information
168
+ ```
169
+
170
+ ## Use Cases
171
+
172
+ ### 1. Pre-commit Hook
173
+
174
+ Add to `.husky/pre-commit` or `.git/hooks/pre-commit`:
175
+
176
+ ```bash
177
+ #!/bin/sh
178
+ fnmap --staged -q
179
+ git add .fnmap
180
+ ```
181
+
182
+ This automatically updates the `.fnmap` index when committing code.
183
+
184
+ ### 2. CI/CD Integration
185
+
186
+ ```yaml
187
+ # .github/workflows/ci.yml
188
+ - name: Generate Code Index
189
+ run: |
190
+ npm install -g fnmap
191
+ fnmap --dir src
192
+ git diff --exit-code .fnmap || echo "Code index updated"
193
+ ```
194
+
195
+ ### 3. Code Review
196
+
197
+ ```bash
198
+ # Generate index for changed files
199
+ fnmap --changed
200
+
201
+ # Generate call graph for review
202
+ fnmap --mermaid file --changed
203
+ ```
204
+
205
+ ### 4. Documentation Generation
206
+
207
+ ```bash
208
+ # Generate project-level call graph
209
+ fnmap --mermaid project
210
+
211
+ # Use the .fnmap.mermaid file in your documentation
212
+ ```
213
+
214
+ ## Supported File Types
215
+
216
+ - `.js` - JavaScript
217
+ - `.ts` - TypeScript
218
+ - `.jsx` - React JSX
219
+ - `.tsx` - React TypeScript
220
+ - `.mjs` - ES Modules
221
+
222
+ ## Limitations
223
+
224
+ To ensure performance and safety, fnmap has the following default limits:
225
+ - **File Size**: Maximum 10MB per file
226
+ - **Directory Depth**: Maximum recursion depth of 50 levels
227
+
228
+ ## How It Works
229
+
230
+ 1. **AST Parsing**: Uses `@babel/parser` to parse code into Abstract Syntax Tree
231
+ 2. **Structure Analysis**: Traverses AST to extract imports, functions, classes, constants
232
+ 3. **Call Graph**: Tracks function call relationships and dependencies
233
+ 4. **Index Generation**: Generates compact `.fnmap` files with structured information
234
+ 5. **Visualization**: Optionally generates Mermaid diagrams for visual representation
235
+
236
+ ## Examples
237
+
238
+ ### Example 1: Analyze Single File
239
+
240
+ ```bash
241
+ fnmap --files src/utils.js
242
+ ```
243
+
244
+ Output:
245
+ ```
246
+ ==================================================
247
+ fnmap - AI Code Indexing Tool
248
+ ==================================================
249
+
250
+ Analyzing: src/utils.js
251
+ ✓ Imports: 3, Functions: 5, Classes: 0, Constants: 2
252
+
253
+ Generating .fnmap index...
254
+ ✓ src/.fnmap
255
+
256
+ ==================================================
257
+ Complete! Analyzed: 1, Failed: 0
258
+ ==================================================
259
+ ```
260
+
261
+ ### Example 2: Analyze Directory with Call Graph
262
+
263
+ ```bash
264
+ fnmap --dir src --mermaid file
265
+ ```
266
+
267
+ Generates:
268
+ - `src/.fnmap` - Code index
269
+ - `src/utils.mermaid` - Call graph for utils.js
270
+ - `src/parser.mermaid` - Call graph for parser.js
271
+ - etc.
272
+
273
+ ### Example 3: Git Workflow
274
+
275
+ ```bash
276
+ # Make changes to code
277
+ git add .
278
+
279
+ # Generate index for staged files
280
+ fnmap --staged -q
281
+
282
+ # Add updated index
283
+ git add .fnmap
284
+
285
+ # Commit
286
+ git commit -m "Update feature"
287
+ ```
288
+
289
+ ## Contributing
290
+
291
+ Contributions are welcome! Please feel free to submit a Pull Request.
292
+
293
+ ## License
294
+
295
+ MIT
296
+
297
+ ## Related Projects
298
+
299
+ - [@babel/parser](https://babeljs.io/docs/en/babel-parser) - JavaScript parser
300
+ - [@babel/traverse](https://babeljs.io/docs/en/babel-traverse) - AST traversal
301
+ - [Mermaid](https://mermaid-js.github.io/) - Diagram generation
302
+
303
+ ## Support
304
+
305
+ - 🐛 [Report Issues](https://github.com/gqfx/fnmap/issues)
306
+ - 💡 [Feature Requests](https://github.com/gqfx/fnmap/issues)
307
+ - 📖 [Documentation](https://github.com/gqfx/fnmap)
package/README_CN.md ADDED
@@ -0,0 +1,308 @@
1
+ # fnmap
2
+
3
+ > AI 代码索引生成工具,分析 JS/TS 代码结构,生成结构化代码映射
4
+
5
+ [English Documentation](./README.md)
6
+
7
+ ## 特性
8
+
9
+ - 🚀 **快速分析**:使用 AST 快速分析 JavaScript/TypeScript 代码结构
10
+ - 📊 **结构化输出**:生成包含导入、函数、类、常量的 `.fnmap` 索引文件
11
+ - 🔗 **调用图谱**:追踪函数调用关系和依赖
12
+ - 📈 **Mermaid 图表**:生成 Mermaid 格式的可视化调用图
13
+ - 🎯 **Git 集成**:只处理改动的文件,提高工作效率
14
+ - ⚙️ **灵活配置**:支持多种配置方式
15
+ - 🔌 **Pre-commit Hook**:无缝集成 git hooks
16
+
17
+ ## 安装
18
+
19
+ ```bash
20
+ npm install -g fnmap
21
+ ```
22
+
23
+ 或在项目中使用:
24
+
25
+ ```bash
26
+ npm install --save-dev fnmap
27
+ ```
28
+
29
+ ## 快速开始
30
+
31
+ ### 初始化配置
32
+
33
+ ```bash
34
+ fnmap --init
35
+ ```
36
+
37
+ 这会在项目根目录创建 `.fnmaprc` 配置文件。
38
+
39
+ ### 基本用法
40
+
41
+ ```bash
42
+ # 根据配置文件处理
43
+ fnmap
44
+
45
+ # 处理指定目录
46
+ fnmap --dir src
47
+
48
+ # 处理指定文件
49
+ fnmap --files index.js,utils.js
50
+
51
+ # 处理 git 改动的文件
52
+ fnmap --changed
53
+
54
+ # 处理 git staged 文件(用于 pre-commit hook)
55
+ fnmap --staged -q
56
+ ```
57
+
58
+ ### 生成调用图
59
+
60
+ ```bash
61
+ # 生成文件级 Mermaid 图表
62
+ fnmap --mermaid file --dir src
63
+
64
+ # 生成项目级 Mermaid 图表
65
+ fnmap --mermaid project
66
+ ```
67
+
68
+ ## 配置
69
+
70
+ fnmap 支持多种配置方式(按优先级排序):
71
+
72
+ 1. `.fnmaprc` - JSON 配置文件
73
+ 2. `.fnmaprc.json` - JSON 配置文件
74
+ 3. `package.json#fnmap` - package.json 中的 fnmap 字段
75
+
76
+ ### 配置示例
77
+
78
+ **.fnmaprc**
79
+ ```json
80
+ {
81
+ "enable": true,
82
+ "include": [
83
+ "src/**/*.js",
84
+ "src/**/*.ts",
85
+ "src/**/*.jsx",
86
+ "src/**/*.tsx"
87
+ ],
88
+ "exclude": [
89
+ "node_modules",
90
+ "dist",
91
+ "build"
92
+ ]
93
+ }
94
+ ```
95
+
96
+ **package.json**
97
+ ```json
98
+ {
99
+ "fnmap": {
100
+ "enable": true,
101
+ "include": ["src/**/*.js", "src/**/*.ts"],
102
+ "exclude": ["dist"]
103
+ }
104
+ }
105
+ ```
106
+
107
+ ## 输出文件
108
+
109
+ ### .fnmap 索引文件
110
+
111
+ `.fnmap` 文件包含代码的结构化信息:
112
+
113
+ ```
114
+ @FNMAP src/
115
+ #utils.js 工具函数
116
+ <fs:readFileSync,writeFileSync
117
+ <path:join,resolve
118
+ readConfig(filePath) 10-25 读取配置文件
119
+ parseData(data) 27-40 解析数据 →JSON.parse
120
+ saveFile(path,content) 42-50 保存文件 →fs.writeFileSync,path.join
121
+ @FNMAP
122
+ ```
123
+
124
+ **格式说明:**
125
+ - `#filename` - 文件名和描述
126
+ - `<module:members` - 导入的模块和成员
127
+ - `functionName(params) startLine-endLine description →calls` - 函数信息及调用图
128
+ - `ClassName:SuperClass startLine-endLine` - 类信息
129
+ - ` .methodName(params) line description →calls` - 实例方法
130
+ - ` +methodName(params) line description →calls` - 静态方法
131
+ - `CONSTANT_NAME line description` - 常量定义
132
+
133
+ ### Mermaid 调用图
134
+
135
+ 使用 `--mermaid` 选项时,生成可视化调用图:
136
+
137
+ **文件级** (`filename.mermaid`):
138
+ ```mermaid
139
+ flowchart TD
140
+ subgraph utils["utils"]
141
+ readConfig["readConfig"]
142
+ parseData["parseData"]
143
+ saveFile["saveFile"]
144
+ end
145
+ readConfig --> parseData
146
+ saveFile --> parseData
147
+ ```
148
+
149
+ **项目级** (`.fnmap.mermaid`):
150
+ 显示项目中所有文件的调用关系。
151
+
152
+ ## CLI 选项
153
+
154
+ ```
155
+ 用法: fnmap [options] [files...]
156
+
157
+ 选项:
158
+ -v, --version 显示版本号
159
+ -f, --files <files> 处理指定文件(逗号分隔)
160
+ -d, --dir <dir> 处理目录下所有代码文件
161
+ -p, --project <dir> 指定项目根目录(默认:当前目录)
162
+ -c, --changed 处理 git 改动的文件(staged + modified + untracked)
163
+ -s, --staged 处理 git staged 文件(用于 pre-commit hook)
164
+ -m, --mermaid [mode] 生成 Mermaid 调用图(file=文件级,project=项目级)
165
+ -q, --quiet 静默模式(不输出信息)
166
+ --init 创建默认配置文件 .fnmaprc
167
+ -h, --help 显示帮助信息
168
+ ```
169
+
170
+ ## 使用场景
171
+
172
+ ### 1. Pre-commit Hook
173
+
174
+ 添加到 `.husky/pre-commit` 或 `.git/hooks/pre-commit`:
175
+
176
+ ```bash
177
+ #!/bin/sh
178
+ fnmap --staged -q
179
+ git add .fnmap
180
+ ```
181
+
182
+ 这样在提交代码时会自动更新 `.fnmap` 索引。
183
+
184
+ ### 2. CI/CD 集成
185
+
186
+ ```yaml
187
+ # .github/workflows/ci.yml
188
+ - name: Generate Code Index
189
+ run: |
190
+ npm install -g fnmap
191
+ fnmap --dir src
192
+ git diff --exit-code .fnmap || echo "Code index updated"
193
+ ```
194
+
195
+ ### 3. 代码审查
196
+
197
+ ```bash
198
+ # 为改动的文件生成索引
199
+ fnmap --changed
200
+
201
+ # 生成调用图用于审查
202
+ fnmap --mermaid file --changed
203
+ ```
204
+
205
+ ### 4. 文档生成
206
+
207
+ ```bash
208
+ # 生成项目级调用图
209
+ fnmap --mermaid project
210
+
211
+ # 在文档中使用 .fnmap.mermaid 文件
212
+ ```
213
+
214
+ ## 支持的文件类型
215
+
216
+ - `.js` - JavaScript
217
+ - `.ts` - TypeScript
218
+ - `.jsx` - React JSX
219
+ - `.tsx` - React TypeScript
220
+ - `.mjs` - ES Modules
221
+
222
+ ## 限制
223
+
224
+ 为了保证性能和安全,fnmap 有以下默认限制:
225
+ - **文件大小**:单个文件最大支持 10MB
226
+ - **目录深度**:最大递归深度为 50 层
227
+ - **超时**:目前没有硬性超时限制,但处理超大文件可能会较慢
228
+
229
+ ## 工作原理
230
+
231
+ 1. **AST 解析**:使用 `@babel/parser` 将代码解析为抽象语法树
232
+ 2. **结构分析**:遍历 AST 提取导入、函数、类、常量
233
+ 3. **调用图谱**:追踪函数调用关系和依赖
234
+ 4. **索引生成**:生成紧凑的 `.fnmap` 文件,包含结构化信息
235
+ 5. **可视化**:可选生成 Mermaid 图表进行可视化展示
236
+
237
+ ## 示例
238
+
239
+ ### 示例 1:分析单个文件
240
+
241
+ ```bash
242
+ fnmap --files src/utils.js
243
+ ```
244
+
245
+ 输出:
246
+ ```
247
+ ==================================================
248
+ fnmap - AI 代码索引生成工具
249
+ ==================================================
250
+
251
+ Analyzing: src/utils.js
252
+ ✓ Imports: 3, Functions: 5, Classes: 0, Constants: 2
253
+
254
+ Generating .fnmap index...
255
+ ✓ src/.fnmap
256
+
257
+ ==================================================
258
+ Complete! Analyzed: 1, Failed: 0
259
+ ==================================================
260
+ ```
261
+
262
+ ### 示例 2:分析目录并生成调用图
263
+
264
+ ```bash
265
+ fnmap --dir src --mermaid file
266
+ ```
267
+
268
+ 生成:
269
+ - `src/.fnmap` - 代码索引
270
+ - `src/utils.mermaid` - utils.js 的调用图
271
+ - `src/parser.mermaid` - parser.js 的调用图
272
+ - 等等
273
+
274
+ ### 示例 3:Git 工作流
275
+
276
+ ```bash
277
+ # 修改代码
278
+ git add .
279
+
280
+ # 为 staged 文件生成索引
281
+ fnmap --staged -q
282
+
283
+ # 添加更新的索引
284
+ git add .fnmap
285
+
286
+ # 提交
287
+ git commit -m "Update feature"
288
+ ```
289
+
290
+ ## 贡献
291
+
292
+ 欢迎贡献!请随时提交 Pull Request。
293
+
294
+ ## 许可证
295
+
296
+ MIT
297
+
298
+ ## 相关项目
299
+
300
+ - [@babel/parser](https://babeljs.io/docs/en/babel-parser) - JavaScript 解析器
301
+ - [@babel/traverse](https://babeljs.io/docs/en/babel-traverse) - AST 遍历
302
+ - [Mermaid](https://mermaid-js.github.io/) - 图表生成
303
+
304
+ ## 支持
305
+
306
+ - 🐛 [报告问题](https://github.com/gqfx/fnmap/issues)
307
+ - 💡 [功能请求](https://github.com/gqfx/fnmap/issues)
308
+ - 📖 [文档](https://github.com/gqfx/fnmap)
package/dist/index.js ADDED
@@ -0,0 +1,36 @@
1
+ #!/usr/bin/env node
2
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const v=require("fs"),I=require("path"),Re=require("commander"),U=require("child_process"),Ie=require("@babel/parser"),B=require("@babel/traverse"),O={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 oe(e){return"parseError"in e}function Se(e){return e.success===!0}function Fe(e){return e.success===!1}function _e(e){return e.valid===!0}function ve(e){return e.valid===!1}const C={reset:"\x1B[0m",red:"\x1B[31m",green:"\x1B[32m",yellow:"\x1B[33m",blue:"\x1B[34m",gray:"\x1B[90m",bold:"\x1B[1m"},W=10*1024*1024,X=50,Z=[".js",".ts",".jsx",".tsx",".mjs"],K=["node_modules",".git","dist","build",".next","coverage","__pycache__",".cache"],H={enable:!0,include:["**/*.js","**/*.ts","**/*.jsx","**/*.tsx","**/*.mjs"],exclude:[]};function ie(e){if(!e||typeof e!="string")return{valid:!1,error:"File path is required and must be a string / 文件路径必须是字符串",errorType:O.VALIDATION_ERROR};if(!v.existsSync(e))return{valid:!1,error:`File not found: ${e} / 文件不存在: ${e}`,errorType:O.FILE_NOT_FOUND};try{const a=v.statSync(e);if(!a.isFile())return{valid:!1,error:`Path is not a file: ${e} / 路径不是文件: ${e}`,errorType:O.VALIDATION_ERROR};if(a.size>W)return{valid:!1,error:`File too large (${(a.size/1024/1024).toFixed(2)}MB > ${W/1024/1024}MB): ${e} / 文件过大`,errorType:O.FILE_TOO_LARGE}}catch(a){return{valid:!1,error:`Cannot access file: ${e}. Reason: ${a.message} / 无法访问文件`,errorType:O.PERMISSION_ERROR}}return{valid:!0}}function Q(e){if(!e||typeof e!="object")return{valid:!1,error:"Config must be an object / 配置必须是对象"};const a=e;return a.enable!==void 0&&typeof a.enable!="boolean"?{valid:!1,error:"Config.enable must be a boolean / enable 字段必须是布尔值"}:a.include!==void 0&&!Array.isArray(a.include)?{valid:!1,error:"Config.include must be an array / include 字段必须是数组"}:a.exclude!==void 0&&!Array.isArray(a.exclude)?{valid:!1,error:"Config.exclude must be an array / exclude 字段必须是数组"}:{valid:!0}}function q(e,a,o={}){const t=[a];return o.file&&t.push(`File: ${o.file}`),o.line!==void 0&&o.column!==void 0&&t.push(`Location: Line ${o.line}, Column ${o.column}`),o.suggestion&&t.push(`Suggestion: ${o.suggestion}`),t.join(`
3
+ `)}let j=!1,G=null;const E={error:e=>{j||console.error(`${C.red}✗${C.reset} ${e}`)},success:e=>{j||console.log(`${C.green}✓${C.reset} ${e}`)},info:e=>{j||console.log(e)},warn:e=>{j||console.warn(`${C.yellow}!${C.reset} ${e}`)},title:e=>{j||console.log(`${C.bold}${e}${C.reset}`)}};function ae(e){j=e}function Ce(){return j}function ce(){try{return require("../../package.json").version}catch{return"0.1.0"}}function Y(){return G=new Re.Command,G.name("fnmap").description("AI code indexing tool - Analyzes JS/TS code structure and generates structured code maps").version(ce(),"-v, --version","Show version number").option("-f, --files <files>","Process specified files (comma-separated)",e=>e.split(",").map(a=>a.trim()).filter(Boolean)).option("-d, --dir <dir>","Process all code files in directory").option("-p, --project <dir>","Specify project root directory",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("-q, --quiet","Quiet mode").option("--init","Create default config file .fnmaprc").argument("[files...]","Directly specify file paths").allowUnknownOption(!1).addHelpText("after",`
4
+ Configuration files (by priority):
5
+ .fnmaprc JSON config file
6
+ .fnmaprc.json JSON config file
7
+ package.json#fnmap fnmap field in package.json
8
+
9
+ Output:
10
+ .fnmap Code index file (imports, functions, classes, constants, call graph)
11
+ *.mermaid Mermaid call graph (when using --mermaid file)
12
+ .fnmap.mermaid Project-level Mermaid call graph (when using --mermaid project)
13
+
14
+ Examples:
15
+ $ fnmap --dir src Process src directory
16
+ $ fnmap --files a.js,b.js Process specified files
17
+ $ fnmap --changed Process git changed files
18
+ $ fnmap --staged -q For pre-commit hook usage
19
+ $ fnmap --mermaid file --dir src Generate file-level call graphs
20
+ $ fnmap --mermaid project Generate project-level call graph
21
+ $ fnmap --init Create config file
22
+ `),G}function P(){return G||Y()}const Oe={get opts(){return P().opts.bind(P())},get args(){return P().args},get parse(){return P().parse.bind(P())}};function le(e){const a=[".fnmaprc",".fnmaprc.json"];for(const t of a){const R=I.join(e,t);if(v.existsSync(R))try{const r=v.readFileSync(R,"utf-8");if(!r.trim()){E.warn(`Config file is empty: ${t}. Using default config / 配置文件为空,使用默认配置`);continue}let p;try{p=JSON.parse(r)}catch(u){const c=u,s=q(O.CONFIG_ERROR,`Failed to parse config file: ${t} / 配置文件解析失败`,{file:R,line:c.lineNumber,column:c.columnNumber,suggestion:"Check JSON syntax, ensure proper quotes and commas / 检查 JSON 语法,确保引号和逗号正确"});E.warn(s);continue}const l=Q(p);if(!l.valid){E.warn(`Invalid config in ${t}: ${l.error}`);continue}return{config:p,source:t}}catch(r){const p=r,l=q(O.FILE_READ_ERROR,`Failed to read config file: ${t} / 配置文件读取失败`,{file:R,suggestion:p.message});E.warn(l)}}const o=I.join(e,"package.json");if(v.existsSync(o))try{const t=JSON.parse(v.readFileSync(o,"utf-8"));if(t.fnmap){const R=Q(t.fnmap);if(!R.valid)E.warn(`Invalid fnmap config in package.json: ${R.error}`);else return{config:t.fnmap,source:"package.json#fnmap"}}}catch{}return{config:null,source:null}}function fe(e){return e?{...H,...e,exclude:[...e.exclude??[]]}:H}function de(e,a=!1){const o=[];try{let t;if(a)t=U.execSync("git diff --cached --name-only --diff-filter=ACMR",{cwd:e,encoding:"utf-8"});else{const p=U.execSync("git diff --cached --name-only --diff-filter=ACMR",{cwd:e,encoding:"utf-8"}),l=U.execSync("git diff --name-only --diff-filter=ACMR",{cwd:e,encoding:"utf-8"}),u=U.execSync("git ls-files --others --exclude-standard",{cwd:e,encoding:"utf-8"});t=`${p}
23
+ ${l}
24
+ ${u}`}const R=t.split(`
25
+ `).map(p=>p.trim()).filter(Boolean).filter(p=>{const l=I.extname(p);return Z.includes(l)}),r=[...new Set(R)];for(const p of r){const l=I.resolve(e,p);v.existsSync(l)&&o.push(l)}}catch{return[]}return o}function z(e,a=e,o=K,t=0,R=new Set){const r=[];if(!v.existsSync(e))return E.warn(`Directory does not exist: ${e} / 目录不存在`),r;if(t>X)return E.warn(`Max directory depth (${X}) exceeded: ${e} / 超过最大目录深度`),r;let p;try{p=v.realpathSync(e)}catch(u){const c=u;return E.warn(`Cannot resolve real path: ${e}. Reason: ${c.message} / 无法解析真实路径`),r}if(R.has(p))return E.warn(`Circular reference detected, skipping: ${e} / 检测到循环引用`),r;R.add(p);let l;try{l=v.readdirSync(e,{withFileTypes:!0})}catch(u){const c=u;return c.code==="EACCES"||c.code==="EPERM"?E.warn(`Permission denied: ${e} / 权限不足`):E.warn(`Failed to read directory: ${e}. Reason: ${c.message} / 读取目录失败`),r}for(const u of l)try{const c=I.join(e,u.name);if(u.isDirectory())o.includes(u.name)||r.push(...z(c,a,o,t+1,R));else if(u.isFile()){const s=I.extname(u.name);Z.includes(s)&&r.push(I.relative(a,c))}}catch(c){const s=c;E.warn(`Error processing entry: ${u.name}. Reason: ${s.message} / 处理条目出错`)}return r}function k(e){if(!e)return"";const o=e.value.split(`
26
+ `).map(t=>t.replace(/^\s*\*\s?/,"").trim());for(const t of o)if(t.startsWith("@description "))return t.slice(13).trim().slice(0,60);for(const t of o)if(t&&!t.startsWith("@")&&!t.startsWith("/"))return t.slice(0,60);return""}const J=typeof B=="function"?B:B.default;function pe(e,a){var d,F;if(e==null)return{parseError:"Code content is null or undefined / 代码内容为空",errorType:O.VALIDATION_ERROR};if(typeof e!="string")return{parseError:"Code must be a string / 代码必须是字符串类型",errorType:O.VALIDATION_ERROR};if(!e.trim())return{description:"",imports:[],functions:[],classes:[],constants:[],callGraph:{}};const o={description:"",imports:[],functions:[],classes:[],constants:[],callGraph:{}},t=e.match(/^\/\*\*[\s\S]*?\*\//);if(t){const n=t[0].split(`
27
+ `).map(i=>i.replace(/^\s*\*\s?/,"").trim()).filter(i=>i&&!i.startsWith("/")&&!i.startsWith("@ai"));for(const i of n)if(i.startsWith("@description ")){o.description=i.slice(13).trim();break}if(!o.description&&n.length>0){const i=n.find(g=>!g.startsWith("@"));i&&(o.description=i)}}let R;try{const f=a&&(a.endsWith(".ts")||a.endsWith(".tsx"));R=Ie.parse(e,{sourceType:"unambiguous",plugins:["jsx","classPrivateProperties","classPrivateMethods",...f?["typescript"]:[]]})}catch(f){const n=f;return{parseError:q(O.PARSE_ERROR,`Syntax error: ${n.message} / 语法错误`,{file:a??void 0,line:(d=n.loc)==null?void 0:d.line,column:(F=n.loc)==null?void 0:F.column,suggestion:"Check syntax errors in the file / 检查文件中的语法错误"}),loc:n.loc,errorType:O.PARSE_ERROR}}const r=new Map,p=new Map,l=new Map;function u(f){var i,g,S;let n=f;for(;n;){if(n.node.type==="FunctionDeclaration"){const h=n.node;if(h.id)return h.id.name}if(n.node.type==="ClassMethod"){const h=n.node,$=(i=n.parentPath)==null?void 0:i.parentPath,y=$==null?void 0:$.node,_=((g=y==null?void 0:y.id)==null?void 0:g.name)??"",b=((S=h.key)==null?void 0:S.name)??"";return _?`${_}.${b}`:b}if(n.node.type==="ArrowFunctionExpression"||n.node.type==="FunctionExpression"){const h=n.parent;if((h==null?void 0:h.type)==="VariableDeclarator"){const y=h.id;if(y!=null&&y.name)return y.name}}n=n.parentPath}return null}J(R,{VariableDeclarator(f){var i,g,S,h;const n=f.node;if(((i=n.init)==null?void 0:i.type)==="CallExpression"&&((g=n.init.callee)==null?void 0:g.type)==="Identifier"&&n.init.callee.name==="require"&&((h=(S=n.init.arguments)==null?void 0:S[0])==null?void 0:h.type)==="StringLiteral"){const $=n.init.arguments[0].value;if(r.has($)||r.set($,new Set),n.id.type==="Identifier"){const y=n.id.name;r.get($).add(y),p.set(y,$),l.set(y,new Set)}else if(n.id.type==="ObjectPattern"){for(const y of n.id.properties)if(y.type==="ObjectProperty"&&y.key.type==="Identifier"){const _=y.value.type==="Identifier"?y.value.name:y.key.name;r.get($).add(y.key.name),p.set(_,$),l.set(_,new Set)}}}},CallExpression(f){var i,g,S,h,$,y;const n=f.node;if(((i=n.callee)==null?void 0:i.type)==="Identifier"&&n.callee.name==="require"&&((S=(g=n.arguments)==null?void 0:g[0])==null?void 0:S.type)==="StringLiteral"){const _=f.parent;if((_==null?void 0:_.type)==="MemberExpression"&&((h=_.property)==null?void 0:h.type)==="Identifier"){const b=n.arguments[0].value;r.has(b)||r.set(b,new Set),r.get(b).add(_.property.name);const A=($=f.parentPath)==null?void 0:$.parent;if((A==null?void 0:A.type)==="VariableDeclarator"){const N=A;((y=N.id)==null?void 0:y.type)==="Identifier"&&(p.set(N.id.name,b),l.set(N.id.name,new Set))}}}},ImportDeclaration(f){const n=f.node,i=n.source.value;r.has(i)||r.set(i,new Set);for(const g of n.specifiers){let S,h;if(g.type==="ImportDefaultSpecifier")S="default",h=g.local.name;else if(g.type==="ImportNamespaceSpecifier")S="*",h=g.local.name;else if(g.type==="ImportSpecifier"){const $=g.imported;S=$.type==="Identifier"?$.name:$.value,h=g.local.name}S&&h&&(r.get(i).add(S),p.set(h,i),l.set(h,new Set))}}}),J(R,{Identifier(f){const n=f.node.name;if(l.has(n)){const i=f.parent;if((i==null?void 0:i.type)==="VariableDeclarator"&&i.id===f.node||(i==null?void 0:i.type)==="ImportSpecifier"||(i==null?void 0:i.type)==="ImportDefaultSpecifier")return;const g=u(f);g&&l.get(n).add(g)}},FunctionDeclaration(f){var _,b,A,N,w;const n=f.node,i=((_=n.id)==null?void 0:_.name)??"[anonymous]",g=n.params.map(x=>{var M,T;return x.type==="Identifier"?x.name:x.type==="AssignmentPattern"&&((M=x.left)==null?void 0:M.type)==="Identifier"?x.left.name+"?":x.type==="RestElement"&&((T=x.argument)==null?void 0:T.type)==="Identifier"?"..."+x.argument.name:"?"}),S=((A=(b=n.loc)==null?void 0:b.start)==null?void 0:A.line)??0,h=((w=(N=n.loc)==null?void 0:N.end)==null?void 0:w.line)??0;let $="";const y=n.leadingComments;y&&y.length>0&&($=k(y[y.length-1])),o.functions.push({name:i,params:g.join(","),startLine:S,endLine:h,description:$})},ClassDeclaration(f){var b,A,N,w,x,M,T,ee,ne,te;const n=f.node,i=((b=n.id)==null?void 0:b.name)??"[anonymous]",g=((N=(A=n.loc)==null?void 0:A.start)==null?void 0:N.line)??0,S=((x=(w=n.loc)==null?void 0:w.end)==null?void 0:x.line)??0,h=((M=n.superClass)==null?void 0:M.type)==="Identifier"?n.superClass.name:null;let $="";const y=n.leadingComments;y&&y.length>0&&($=k(y[y.length-1]));const _=[];if((T=n.body)!=null&&T.body){for(const L of n.body.body)if(L.type==="ClassMethod"){const ye=((ee=L.key)==null?void 0:ee.type)==="Identifier"?L.key.name:"[computed]",$e=L.params.map(D=>{var re;return D.type==="Identifier"?D.name:D.type==="AssignmentPattern"&&((re=D.left)==null?void 0:re.type)==="Identifier"?D.left.name+"?":"?"}),Ee=((te=(ne=L.loc)==null?void 0:ne.start)==null?void 0:te.line)??0;let se="";const V=L.leadingComments;V&&V.length>0&&(se=k(V[V.length-1])),_.push({name:ye,params:$e.join(","),line:Ee,static:L.static,kind:L.kind,description:se})}}o.classes.push({name:i,superClass:h,startLine:g,endLine:S,methods:_,description:$})},VariableDeclaration(f){var i,g,S;if(f.parent.type!=="Program")return;const n=f.node;if(n.kind==="const"){let h="";const $=n.leadingComments;$&&$.length>0&&(h=k($[$.length-1]));for(const y of n.declarations){const _=((i=y.id)==null?void 0:i.type)==="Identifier"?y.id.name:void 0;if(_&&_===_.toUpperCase()&&_.length>2){const b=((S=(g=n.loc)==null?void 0:g.start)==null?void 0:S.line)??0;o.constants.push({name:_,line:b,description:h})}}}}});for(const[f,n]of r){const i=new Set;for(const g of p.keys())if(p.get(g)===f&&l.has(g))for(const S of l.get(g))i.add(S);o.imports.push({module:f,members:Array.from(n),usedIn:Array.from(i)})}const c=new Set;for(const f of o.functions)c.add(f.name);for(const f of o.classes)for(const n of f.methods)c.add(n.name),c.add(`${f.name}.${n.name}`);const s=new Set(p.keys()),m=new Map;J(R,{CallExpression(f){var g,S;const n=f.node;let i=null;if(n.callee.type==="Identifier")i=n.callee.name;else if(n.callee.type==="MemberExpression"&&((g=n.callee.property)==null?void 0:g.type)==="Identifier"){const h=((S=n.callee.object)==null?void 0:S.type)==="Identifier"?n.callee.object.name:void 0,$=n.callee.property.name;h&&s.has(h)?i=`${h}.${$}`:i=$}if(i){const h=u(f);if(h&&h!==i){const $=c.has(i),y=s.has(i)||i.includes(".")&&s.has(i.split(".")[0]);($||y)&&(m.has(h)||m.set(h,new Set),m.get(h).add(i))}}}}),o.callGraph={};for(const[f,n]of m)o.callGraph[f]=Array.from(n);return o}function be(e,a){var R;const o=[];let t=`/*@AI ${a}`;e.description&&(t+=` - ${e.description.slice(0,50)}`),o.push(t);for(const r of e.imports){const p=r.members.join(",");let l=`<${r.module}:${p}`;((R=r.usedIn)==null?void 0:R.length)>0&&(l+=` ->${r.usedIn.join(",")}`),o.push(l)}for(const r of e.classes){let p=r.name;r.superClass&&(p+=`:${r.superClass}`),p+=` ${r.startLine}-${r.endLine}`,r.description&&(p+=` ${r.description}`),o.push(p);for(const l of r.methods){const u=l.static?" +":" .",c=l.kind==="get"?"get:":l.kind==="set"?"set:":"";let s=`${u}${c}${l.name}(${l.params}) ${l.line}`;l.description&&(s+=` ${l.description}`),o.push(s)}}for(const r of e.functions){let p=`${r.name}(${r.params}) ${r.startLine}-${r.endLine}`;r.description&&(p+=` ${r.description}`),o.push(p)}for(const r of e.constants){let p=`${r.name} ${r.line}`;r.description&&(p+=` ${r.description}`),o.push(p)}return o.push("@AI*/"),o.join(`
28
+ `)}function xe(e){let a=e;return a=a.replace(/\/\*@AI[\s\S]*?@AI\*\/\s*/g,""),a=a.replace(/\/\*\*[\s\S]*?@ai-context-end[\s\S]*?\*\/\s*/g,""),a=a.replace(/^\/\*\*[\s\S]*?\*\/\s*\n?/,""),a}function ue(e,a){var t,R,r;const o=[`@FNMAP ${I.basename(e)}/`];for(const{relativePath:p,info:l}of a){let c=`#${I.basename(p)}`;l.description&&(c+=` ${l.description.slice(0,50)}`),o.push(c);for(const s of l.imports){const m=s.members.join(",");o.push(` <${s.module}:${m}`)}for(const s of l.classes){let m=` ${s.name}`;s.superClass&&(m+=`:${s.superClass}`),m+=` ${s.startLine}-${s.endLine}`,s.description&&(m+=` ${s.description}`),o.push(m);for(const d of s.methods){const F=d.static?" +":" .",f=d.kind==="get"?"get:":d.kind==="set"?"set:":"";let n=`${F}${f}${d.name}(${d.params}) ${d.line}`;d.description&&(n+=` ${d.description}`);const i=`${s.name}.${d.name}`,g=((t=l.callGraph)==null?void 0:t[i])??((R=l.callGraph)==null?void 0:R[d.name]);g&&g.length>0&&(n+=` →${g.join(",")}`),o.push(n)}}for(const s of l.functions){let m=` ${s.name}(${s.params}) ${s.startLine}-${s.endLine}`;s.description&&(m+=` ${s.description}`);const d=(r=l.callGraph)==null?void 0:r[s.name];d&&d.length>0&&(m+=` →${d.join(",")}`),o.push(m)}for(const s of l.constants){let m=` ${s.name} ${s.line}`;s.description&&(m+=` ${s.description}`),o.push(m)}}return o.push("@FNMAP"),o.join(`
29
+ `)}function me(e,a){const o=["flowchart TD"],t=s=>"id_"+s.replace(/[^a-zA-Z0-9]/g,m=>`_${m.charCodeAt(0)}_`),R=s=>s.replace(/"/g,"#quot;"),r=a.functions.map(s=>s.name),p=[];for(const s of a.classes)for(const m of s.methods)p.push(`${s.name}.${m.name}`);const l=[...r,...p];if(l.length===0)return null;const u=I.basename(e,I.extname(e));o.push(` subgraph ${t(u)}["${u}"]`);for(const s of l)o.push(` ${t(s)}["${R(s)}"]`);o.push(" end");const c=a.callGraph??{};for(const[s,m]of Object.entries(c))for(const d of m)if(l.includes(d)||d.includes(".")){const F=l.includes(d)?d:d.split(".").pop();(l.includes(d)||l.some(f=>f.endsWith(F)))&&o.push(` ${t(s)} --> ${t(d)}`)}return o.join(`
30
+ `)}function ge(e,a){const o=["flowchart TD"],t=u=>"id_"+u.replace(/[^a-zA-Z0-9]/g,c=>`_${c.charCodeAt(0)}_`),R=u=>u.replace(/"/g,"#quot;"),r=new Map,p=[];for(const{relativePath:u,info:c}of a){const s=I.basename(u,I.extname(u)),m=c.functions.map(n=>n.name),d=[];for(const n of c.classes)for(const i of n.methods)d.push(`${n.name}.${i.name}`);const F=[...m,...d];r.set(u,{fileName:s,functions:F});const f=c.callGraph??{};for(const[n,i]of Object.entries(f))for(const g of i)p.push({file:u,fileName:s,caller:n,callee:g})}for(const[,{fileName:u,functions:c}]of r)if(c.length!==0){o.push(` subgraph ${t(u)}["${R(u)}"]`);for(const s of c)o.push(` ${t(u)}_${t(s)}["${R(s)}"]`);o.push(" end")}const l=new Set;for(const{fileName:u,caller:c,callee:s}of p){const m=`${t(u)}_${t(c)}`;let d=null;for(const[,{fileName:F,functions:f}]of r)if(f.includes(s)){d=`${t(F)}_${t(s)}`;break}if(!d){const F=[...r.keys()].find(f=>{var n;return((n=r.get(f))==null?void 0:n.fileName)===u});if(F){const f=r.get(F);f!=null&&f.functions.includes(s)&&(d=`${t(u)}_${t(s)}`)}}if(d){const F=`${m}-->${d}`;l.has(F)||(o.push(` ${m} --> ${d}`),l.add(F))}}return o.join(`
31
+ `)}function he(e){const a=ie(e);if(!a.valid)return{success:!1,error:a.error,errorType:a.errorType??O.VALIDATION_ERROR};try{const o=v.readFileSync(e,"utf-8"),t=pe(o,e);return t?oe(t)?{success:!1,error:t.parseError,errorType:t.errorType,loc:t.loc}:{success:!0,info:t}:{success:!1,error:"Analysis returned null / 分析返回空值",errorType:O.PARSE_ERROR}}catch(o){const t=o;return{success:!1,error:q(O.FILE_READ_ERROR,"Failed to read or process file / 读取或处理文件失败",{file:e,suggestion:t.message}),errorType:O.FILE_READ_ERROR}}}function Ae(){const e=Y();e.parse(process.argv);const a=e.opts(),o=e.args;a.quiet&&ae(!0);const t=I.resolve(a.project);if(a.init){const c=I.join(t,".fnmaprc");if(v.existsSync(c)){console.log(`${C.yellow}!${C.reset} Config file already exists: .fnmaprc`);return}const s={enable:!0,include:["src/**/*.js","src/**/*.ts","src/**/*.jsx","src/**/*.tsx"],exclude:["node_modules","dist","build",".next","coverage","__pycache__",".cache"]};v.writeFileSync(c,JSON.stringify(s,null,2)),console.log(`${C.green}✓${C.reset} Created config file: .fnmaprc`);return}const R=[...a.files??[],...o].filter(c=>v.existsSync(c));let r=[];if(a.changed||a.staged){if(r=de(t,a.staged),r.length===0){E.info("No git changed code files detected");return}}else if(R.length>0)r=R.map(c=>I.isAbsolute(c)?c:I.resolve(t,c));else if(a.dir){const c=I.resolve(t,a.dir);r=z(c,t).map(m=>I.join(t,m))}else{const{config:c,source:s}=le(t);if(c){if(E.info(`Using config: ${s}`),c.enable===!1){E.info("Config file has enable set to false, skipping processing");return}const m=fe(c),d=[...K,...m.exclude];if(m.include)for(const F of m.include){const f=F.replace(/\/\*\*\/.*$/,"").replace(/\*\*\/.*$/,""),n=f?I.resolve(t,f):t;if(v.existsSync(n)){const i=z(n,t,d);r.push(...i.map(g=>I.join(t,g)))}}}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(r.length===0){E.info("No files found to process");return}r=[...new Set(r)],E.info("=".repeat(50)),E.title("fnmap - AI Code Indexing Tool"),E.info("=".repeat(50));let p=0,l=0;const u=new Map;for(const c of r){const s=I.relative(t,c);E.info(`
32
+ Analyzing: ${s}`);const m=he(c);if(m.success){p++;const d=m.info;E.success(`Imports: ${d.imports.length}, Functions: ${d.functions.length}, Classes: ${d.classes.length}, Constants: ${d.constants.length}`);const F=I.dirname(c);u.has(F)||u.set(F,[]),u.get(F).push({relativePath:s,info:d})}else l++,E.error(m.error)}if(u.size>0){E.info(`
33
+ Generating .fnmap index...`);for(const[c,s]of u){const m=ue(c,s),d=I.join(c,".fnmap");v.writeFileSync(d,m),E.success(I.relative(t,d))}}if(a.mermaid&&u.size>0){if(E.info(`
34
+ Generating Mermaid call graphs...`),a.mermaid==="file"||a.mermaid===!0)for(const[c,s]of u)for(const{relativePath:m,info:d}of s){const F=me(m,d);if(F){const f=I.basename(m,I.extname(m)),n=I.join(c,`${f}.mermaid`);v.writeFileSync(n,F),E.success(I.relative(t,n))}}else if(a.mermaid==="project"){const c=[];for(const[,d]of u)c.push(...d);const s=ge(t,c),m=I.join(t,".fnmap.mermaid");v.writeFileSync(m,s),E.success(I.relative(t,m))}}E.info(`
35
+ `+"=".repeat(50)),E.info(`Complete! Analyzed: ${C.green}${p}${C.reset}, Failed: ${l>0?C.red:""}${l}${C.reset}`),E.info("=".repeat(50))}if(require.main===module){const{main:e}=require("./main");e()}exports.COLORS=C;exports.DEFAULT_CONFIG=H;exports.DEFAULT_EXCLUDES=K;exports.ErrorTypes=O;exports.MAX_DIR_DEPTH=X;exports.MAX_FILE_SIZE=W;exports.SUPPORTED_EXTENSIONS=Z;exports.analyzeFile=pe;exports.extractJSDocDescription=k;exports.formatError=q;exports.generateAiMap=ue;exports.generateFileMermaid=me;exports.generateHeader=be;exports.generateProjectMermaid=ge;exports.getGitChangedFiles=de;exports.getVersion=ce;exports.isParseError=oe;exports.isProcessFailure=Fe;exports.isProcessSuccess=Se;exports.isQuietMode=Ce;exports.isValidationFailure=ve;exports.isValidationSuccess=_e;exports.loadConfig=le;exports.logger=E;exports.main=Ae;exports.mergeConfig=fe;exports.processFile=he;exports.program=Oe;exports.removeExistingHeaders=xe;exports.scanDirectory=z;exports.setQuietMode=ae;exports.setupCLI=Y;exports.validateConfig=Q;exports.validateFilePath=ie;
36
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sources":["../src/types/index.ts","../src/constants/index.ts","../src/validation/index.ts","../src/cli/index.ts","../src/config/index.ts","../src/scanner/index.ts","../src/analyzer/jsdoc.ts","../src/analyzer/index.ts","../src/generator/header.ts","../src/generator/fnmap.ts","../src/generator/mermaid.ts","../src/processor/index.ts","../src/main.ts","../src/index.ts"],"sourcesContent":["// ============== 错误类型 ==============\n\nexport const ErrorTypes = {\n FILE_NOT_FOUND: 'FILE_NOT_FOUND',\n FILE_READ_ERROR: 'FILE_READ_ERROR',\n PARSE_ERROR: 'PARSE_ERROR',\n CONFIG_ERROR: 'CONFIG_ERROR',\n VALIDATION_ERROR: 'VALIDATION_ERROR',\n PERMISSION_ERROR: 'PERMISSION_ERROR',\n FILE_TOO_LARGE: 'FILE_TOO_LARGE'\n} as const;\n\nexport type ErrorType = (typeof ErrorTypes)[keyof typeof ErrorTypes];\n\n// ============== 验证结果类型 ==============\n\nexport interface ValidationSuccess {\n valid: true;\n}\n\nexport interface ValidationFailure {\n valid: false;\n error: string;\n errorType?: ErrorType;\n}\n\nexport type ValidationResult = ValidationSuccess | ValidationFailure;\n\n// ============== 配置类型 ==============\n\nexport interface FnmapConfig {\n enable?: boolean;\n include?: string[];\n exclude?: string[];\n}\n\nexport interface LoadedConfig {\n config: FnmapConfig | null;\n source: string | null;\n}\n\n// ============== 代码分析结果类型 ==============\n\nexport interface ImportInfo {\n module: string;\n members: string[];\n usedIn: string[];\n}\n\nexport interface FunctionInfo {\n name: string;\n params: string;\n startLine: number;\n endLine: number;\n description: string;\n}\n\nexport interface MethodInfo {\n name: string;\n params: string;\n line: number;\n static: boolean;\n kind: 'method' | 'get' | 'set' | 'constructor';\n description: string;\n}\n\nexport interface ClassInfo {\n name: string;\n superClass: string | null;\n startLine: number;\n endLine: number;\n methods: MethodInfo[];\n description: string;\n}\n\nexport interface ConstantInfo {\n name: string;\n line: number;\n description: string;\n}\n\nexport type CallGraph = Record<string, string[]>;\n\nexport interface FileInfo {\n description: string;\n imports: ImportInfo[];\n functions: FunctionInfo[];\n classes: ClassInfo[];\n constants: ConstantInfo[];\n callGraph: CallGraph;\n}\n\nexport interface ParseErrorResult {\n parseError: string;\n errorType: ErrorType;\n loc?: { line: number; column: number };\n}\n\nexport type AnalyzeResult = FileInfo | ParseErrorResult;\n\n// ============== 处理结果类型 ==============\n\nexport interface ProcessSuccess {\n success: true;\n info: FileInfo;\n}\n\nexport interface ProcessFailure {\n success: false;\n error: string;\n errorType: ErrorType;\n loc?: { line: number; column: number };\n}\n\nexport type ProcessResult = ProcessSuccess | ProcessFailure;\n\n// ============== CLI 类型 ==============\n\nexport interface CLIOptions {\n files?: string[];\n dir?: string;\n project: string;\n changed?: boolean;\n staged?: boolean;\n mermaid?: boolean | 'file' | 'project';\n quiet?: boolean;\n init?: boolean;\n}\n\n// ============== 错误格式化上下文 ==============\n\nexport interface ErrorContext {\n file?: string;\n line?: number;\n column?: number;\n suggestion?: string;\n}\n\n// ============== 文件信息映射 ==============\n\nexport interface FileInfoEntry {\n relativePath: string;\n info: FileInfo;\n}\n\n// ============== 类型守卫 ==============\n\nexport function isParseError(result: AnalyzeResult): result is ParseErrorResult {\n return 'parseError' in result;\n}\n\nexport function isProcessSuccess(result: ProcessResult): result is ProcessSuccess {\n return result.success === true;\n}\n\nexport function isProcessFailure(result: ProcessResult): result is ProcessFailure {\n return result.success === false;\n}\n\nexport function isValidationSuccess(result: ValidationResult): result is ValidationSuccess {\n return result.valid === true;\n}\n\nexport function isValidationFailure(result: ValidationResult): result is ValidationFailure {\n return result.valid === false;\n}\n","import type { FnmapConfig } from '../types';\n\n// ANSI颜色常量\nexport const COLORS = {\n reset: '\\x1b[0m',\n red: '\\x1b[31m',\n green: '\\x1b[32m',\n yellow: '\\x1b[33m',\n blue: '\\x1b[34m',\n gray: '\\x1b[90m',\n bold: '\\x1b[1m'\n} as const;\n\n// 文件大小限制 (10MB)\nexport const MAX_FILE_SIZE = 10 * 1024 * 1024;\n\n// 最大目录深度限制\nexport const MAX_DIR_DEPTH = 50;\n\n// 默认支持的文件扩展名\nexport const SUPPORTED_EXTENSIONS = ['.js', '.ts', '.jsx', '.tsx', '.mjs'] as const;\nexport type SupportedExtension = (typeof SUPPORTED_EXTENSIONS)[number];\n\n// 默认排除的目录\nexport const DEFAULT_EXCLUDES = [\n 'node_modules',\n '.git',\n 'dist',\n 'build',\n '.next',\n 'coverage',\n '__pycache__',\n '.cache'\n] as const;\n\n// 默认配置\nexport const DEFAULT_CONFIG: Required<FnmapConfig> = {\n enable: true,\n include: ['**/*.js', '**/*.ts', '**/*.jsx', '**/*.tsx', '**/*.mjs'],\n exclude: []\n};\n","import fs from 'fs';\nimport type { ValidationResult, ErrorContext, FnmapConfig, ErrorType } from '../types';\nimport { ErrorTypes } from '../types';\nimport { MAX_FILE_SIZE } from '../constants';\n\n/**\n * 验证文件路径\n */\nexport function validateFilePath(filePath: unknown): ValidationResult {\n if (!filePath || typeof filePath !== 'string') {\n return {\n valid: false,\n error: 'File path is required and must be a string / 文件路径必须是字符串',\n errorType: ErrorTypes.VALIDATION_ERROR\n };\n }\n\n if (!fs.existsSync(filePath)) {\n return {\n valid: false,\n error: `File not found: ${filePath} / 文件不存在: ${filePath}`,\n errorType: ErrorTypes.FILE_NOT_FOUND\n };\n }\n\n try {\n const stats = fs.statSync(filePath);\n\n if (!stats.isFile()) {\n return {\n valid: false,\n error: `Path is not a file: ${filePath} / 路径不是文件: ${filePath}`,\n errorType: ErrorTypes.VALIDATION_ERROR\n };\n }\n\n if (stats.size > MAX_FILE_SIZE) {\n return {\n valid: false,\n error: `File too large (${(stats.size / 1024 / 1024).toFixed(2)}MB > ${MAX_FILE_SIZE / 1024 / 1024}MB): ${filePath} / 文件过大`,\n errorType: ErrorTypes.FILE_TOO_LARGE\n };\n }\n } catch (e) {\n const error = e as Error;\n return {\n valid: false,\n error: `Cannot access file: ${filePath}. Reason: ${error.message} / 无法访问文件`,\n errorType: ErrorTypes.PERMISSION_ERROR\n };\n }\n\n return { valid: true };\n}\n\n/**\n * 验证配置对象\n */\nexport function validateConfig(config: unknown): ValidationResult {\n if (!config || typeof config !== 'object') {\n return {\n valid: false,\n error: 'Config must be an object / 配置必须是对象'\n };\n }\n\n const cfg = config as Partial<FnmapConfig>;\n\n if (cfg.enable !== undefined && typeof cfg.enable !== 'boolean') {\n return {\n valid: false,\n error: 'Config.enable must be a boolean / enable 字段必须是布尔值'\n };\n }\n\n if (cfg.include !== undefined && !Array.isArray(cfg.include)) {\n return {\n valid: false,\n error: 'Config.include must be an array / include 字段必须是数组'\n };\n }\n\n if (cfg.exclude !== undefined && !Array.isArray(cfg.exclude)) {\n return {\n valid: false,\n error: 'Config.exclude must be an array / exclude 字段必须是数组'\n };\n }\n\n return { valid: true };\n}\n\n/**\n * 格式化错误信息\n */\nexport function formatError(\n _errorType: ErrorType | string,\n message: string,\n context: ErrorContext = {}\n): string {\n const parts: string[] = [message];\n\n if (context.file) {\n parts.push(`File: ${context.file}`);\n }\n\n if (context.line !== undefined && context.column !== undefined) {\n parts.push(`Location: Line ${context.line}, Column ${context.column}`);\n }\n\n if (context.suggestion) {\n parts.push(`Suggestion: ${context.suggestion}`);\n }\n\n return parts.join('\\n ');\n}\n","import { Command } from 'commander';\nimport { COLORS } from '../constants';\n\n// 日志工具\nlet quietMode = false;\n\n// 使用模块级变量存储 program 实例\nlet _program: Command | null = null;\n\nexport const logger = {\n error: (msg: string): void => {\n if (!quietMode) console.error(`${COLORS.red}✗${COLORS.reset} ${msg}`);\n },\n success: (msg: string): void => {\n if (!quietMode) console.log(`${COLORS.green}✓${COLORS.reset} ${msg}`);\n },\n info: (msg: string): void => {\n if (!quietMode) console.log(msg);\n },\n warn: (msg: string): void => {\n if (!quietMode) console.warn(`${COLORS.yellow}!${COLORS.reset} ${msg}`);\n },\n title: (msg: string): void => {\n if (!quietMode) console.log(`${COLORS.bold}${msg}${COLORS.reset}`);\n }\n};\n\nexport function setQuietMode(quiet: boolean): void {\n quietMode = quiet;\n}\n\nexport function isQuietMode(): boolean {\n return quietMode;\n}\n\n/**\n * 获取package.json中的版本号\n */\nexport function getVersion(): string {\n try {\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n const pkg = require('../../package.json') as { version: string };\n return pkg.version;\n } catch {\n return '0.1.0';\n }\n}\n\n/**\n * 配置CLI命令\n */\nexport function setupCLI(): Command {\n // 每次创建新的 Command 实例以支持测试\n _program = new Command();\n\n _program\n .name('fnmap')\n .description('AI code indexing tool - Analyzes JS/TS code structure and generates structured code maps')\n .version(getVersion(), '-v, --version', 'Show version number')\n .option('-f, --files <files>', 'Process specified files (comma-separated)', (val: string) =>\n val\n .split(',')\n .map((f) => f.trim())\n .filter(Boolean)\n )\n .option('-d, --dir <dir>', 'Process all code files in directory')\n .option('-p, --project <dir>', 'Specify project root directory', process.env.CLAUDE_PROJECT_DIR ?? process.cwd())\n .option('-c, --changed', 'Process only git changed files (staged + modified + untracked)')\n .option('-s, --staged', 'Process only git staged files (for pre-commit hook)')\n .option('-m, --mermaid [mode]', 'Generate Mermaid call graph (file=file-level, project=project-level)')\n .option('-q, --quiet', 'Quiet mode')\n .option('--init', 'Create default config file .fnmaprc')\n .argument('[files...]', 'Directly specify file paths')\n .allowUnknownOption(false)\n .addHelpText(\n 'after',\n `\nConfiguration files (by priority):\n .fnmaprc JSON config file\n .fnmaprc.json JSON config file\n package.json#fnmap fnmap field in package.json\n\nOutput:\n .fnmap Code index file (imports, functions, classes, constants, call graph)\n *.mermaid Mermaid call graph (when using --mermaid file)\n .fnmap.mermaid Project-level Mermaid call graph (when using --mermaid project)\n\nExamples:\n $ fnmap --dir src Process src directory\n $ fnmap --files a.js,b.js Process specified files\n $ fnmap --changed Process git changed files\n $ fnmap --staged -q For pre-commit hook usage\n $ fnmap --mermaid file --dir src Generate file-level call graphs\n $ fnmap --mermaid project Generate project-level call graph\n $ fnmap --init Create config file\n`\n );\n\n return _program;\n}\n\n// 导出 program getter\nexport function getProgram(): Command {\n if (!_program) {\n return setupCLI();\n }\n return _program;\n}\n\n// 为了兼容性,导出 program 为 getter\nexport const program = {\n get opts() {\n return getProgram().opts.bind(getProgram());\n },\n get args() {\n return getProgram().args;\n },\n get parse() {\n return getProgram().parse.bind(getProgram());\n }\n};\n","import fs from 'fs';\nimport path from 'path';\nimport type { FnmapConfig, LoadedConfig } from '../types';\nimport { ErrorTypes } from '../types';\nimport { DEFAULT_CONFIG } from '../constants';\nimport { validateConfig, formatError } from '../validation';\nimport { logger } from '../cli';\n\n/**\n * 加载配置文件\n * 优先级: .fnmaprc > .fnmaprc.json > package.json#fnmap > 默认配置\n */\nexport function loadConfig(projectDir: string): LoadedConfig {\n const configFiles = ['.fnmaprc', '.fnmaprc.json'];\n\n for (const file of configFiles) {\n const configPath = path.join(projectDir, file);\n if (fs.existsSync(configPath)) {\n try {\n const content = fs.readFileSync(configPath, 'utf-8');\n\n // 检查是否为空文件\n if (!content.trim()) {\n logger.warn(`Config file is empty: ${file}. Using default config / 配置文件为空,使用默认配置`);\n continue;\n }\n\n let config: unknown;\n try {\n config = JSON.parse(content);\n } catch (parseError) {\n const pe = parseError as Error & { lineNumber?: number; columnNumber?: number };\n const errorMsg = formatError(\n ErrorTypes.CONFIG_ERROR,\n `Failed to parse config file: ${file} / 配置文件解析失败`,\n {\n file: configPath,\n line: pe.lineNumber,\n column: pe.columnNumber,\n suggestion: 'Check JSON syntax, ensure proper quotes and commas / 检查 JSON 语法,确保引号和逗号正确'\n }\n );\n logger.warn(errorMsg);\n continue;\n }\n\n // 验证配置\n const validation = validateConfig(config);\n if (!validation.valid) {\n logger.warn(`Invalid config in ${file}: ${validation.error}`);\n continue;\n }\n\n return { config: config as FnmapConfig, source: file };\n } catch (e) {\n const error = e as Error;\n const errorMsg = formatError(\n ErrorTypes.FILE_READ_ERROR,\n `Failed to read config file: ${file} / 配置文件读取失败`,\n {\n file: configPath,\n suggestion: error.message\n }\n );\n logger.warn(errorMsg);\n }\n }\n }\n\n // 检查 package.json 中的 fnmap 字段\n const pkgPath = path.join(projectDir, 'package.json');\n if (fs.existsSync(pkgPath)) {\n try {\n const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8')) as { fnmap?: unknown };\n if (pkg.fnmap) {\n const validation = validateConfig(pkg.fnmap);\n if (!validation.valid) {\n logger.warn(`Invalid fnmap config in package.json: ${validation.error}`);\n } else {\n return { config: pkg.fnmap as FnmapConfig, source: 'package.json#fnmap' };\n }\n }\n } catch {\n // ignore package.json parse errors\n }\n }\n\n return { config: null, source: null };\n}\n\n/**\n * 合并配置\n */\nexport function mergeConfig(userConfig: FnmapConfig | null): Required<FnmapConfig> {\n if (!userConfig) return DEFAULT_CONFIG;\n return {\n ...DEFAULT_CONFIG,\n ...userConfig,\n exclude: [...(userConfig.exclude ?? [])]\n };\n}\n","import fs from 'fs';\nimport path from 'path';\nimport child_process from 'child_process';\nimport { SUPPORTED_EXTENSIONS, DEFAULT_EXCLUDES, MAX_DIR_DEPTH } from '../constants';\nimport { logger } from '../cli';\n\n/**\n * 获取 git 改动的文件列表\n */\nexport function getGitChangedFiles(projectDir: string, stagedOnly = false): string[] {\n const files: string[] = [];\n\n try {\n let output: string;\n if (stagedOnly) {\n // 只获取 staged 文件\n output = child_process.execSync('git diff --cached --name-only --diff-filter=ACMR', {\n cwd: projectDir,\n encoding: 'utf-8'\n });\n } else {\n // 获取所有改动文件(包括 staged、modified、untracked)\n const staged = child_process.execSync('git diff --cached --name-only --diff-filter=ACMR', {\n cwd: projectDir,\n encoding: 'utf-8'\n });\n const modified = child_process.execSync('git diff --name-only --diff-filter=ACMR', {\n cwd: projectDir,\n encoding: 'utf-8'\n });\n const untracked = child_process.execSync('git ls-files --others --exclude-standard', {\n cwd: projectDir,\n encoding: 'utf-8'\n });\n output = `${staged}\\n${modified}\\n${untracked}`;\n }\n\n const changedFiles = output\n .split('\\n')\n .map((f) => f.trim())\n .filter(Boolean)\n .filter((f) => {\n const ext = path.extname(f);\n return (SUPPORTED_EXTENSIONS as readonly string[]).includes(ext);\n });\n\n // 去重并转换为绝对路径\n const uniqueFiles = [...new Set(changedFiles)];\n for (const f of uniqueFiles) {\n const fullPath = path.resolve(projectDir, f);\n if (fs.existsSync(fullPath)) {\n files.push(fullPath);\n }\n }\n } catch {\n // 不是 git 仓库或其他错误\n return [];\n }\n\n return files;\n}\n\n/**\n * 递归扫描目录获取所有代码文件\n */\nexport function scanDirectory(\n dir: string,\n baseDir: string = dir,\n excludes: readonly string[] = DEFAULT_EXCLUDES,\n depth = 0,\n visited: Set<string> = new Set()\n): string[] {\n const files: string[] = [];\n\n if (!fs.existsSync(dir)) {\n logger.warn(`Directory does not exist: ${dir} / 目录不存在`);\n return files;\n }\n\n // 检查深度限制\n if (depth > MAX_DIR_DEPTH) {\n logger.warn(`Max directory depth (${MAX_DIR_DEPTH}) exceeded: ${dir} / 超过最大目录深度`);\n return files;\n }\n\n // 获取规范化的真实路径,处理符号链接\n let realPath: string;\n try {\n realPath = fs.realpathSync(dir);\n } catch (e) {\n const error = e as Error;\n logger.warn(`Cannot resolve real path: ${dir}. Reason: ${error.message} / 无法解析真实路径`);\n return files;\n }\n\n // 检测循环引用\n if (visited.has(realPath)) {\n logger.warn(`Circular reference detected, skipping: ${dir} / 检测到循环引用`);\n return files;\n }\n visited.add(realPath);\n\n let entries: fs.Dirent[];\n try {\n entries = fs.readdirSync(dir, { withFileTypes: true });\n } catch (e) {\n const error = e as NodeJS.ErrnoException;\n if (error.code === 'EACCES' || error.code === 'EPERM') {\n logger.warn(`Permission denied: ${dir} / 权限不足`);\n } else {\n logger.warn(`Failed to read directory: ${dir}. Reason: ${error.message} / 读取目录失败`);\n }\n return files;\n }\n\n for (const entry of entries) {\n try {\n const fullPath = path.join(dir, entry.name);\n\n if (entry.isDirectory()) {\n if (!excludes.includes(entry.name)) {\n files.push(...scanDirectory(fullPath, baseDir, excludes, depth + 1, visited));\n }\n } else if (entry.isFile()) {\n const ext = path.extname(entry.name);\n if ((SUPPORTED_EXTENSIONS as readonly string[]).includes(ext)) {\n files.push(path.relative(baseDir, fullPath));\n }\n }\n // 忽略符号链接、设备文件等其他类型\n } catch (e) {\n const error = e as Error;\n logger.warn(`Error processing entry: ${entry.name}. Reason: ${error.message} / 处理条目出错`);\n }\n }\n\n return files;\n}\n","import type { Comment } from '@babel/types';\n\n/**\n * 从JSDoc注释中提取描述\n */\nexport function extractJSDocDescription(comment: Comment | null | undefined): string {\n if (!comment) return '';\n\n const text = comment.value;\n const lines = text.split('\\n').map((l) => l.replace(/^\\s*\\*\\s?/, '').trim());\n\n // 优先查找 @description 标签\n for (const line of lines) {\n if (line.startsWith('@description ')) {\n return line.slice(13).trim().slice(0, 60);\n }\n }\n\n // 否则取第一行非空非标签内容\n for (const line of lines) {\n if (line && !line.startsWith('@') && !line.startsWith('/')) {\n return line.slice(0, 60);\n }\n }\n\n return '';\n}\n","import parser from '@babel/parser';\nimport _traverse from '@babel/traverse';\nimport type { NodePath } from '@babel/traverse';\nimport type * as t from '@babel/types';\nimport type {\n FileInfo,\n AnalyzeResult,\n ImportInfo,\n FunctionInfo,\n ClassInfo,\n MethodInfo,\n ConstantInfo\n} from '../types';\nimport { ErrorTypes } from '../types';\nimport { formatError } from '../validation';\nimport { extractJSDocDescription } from './jsdoc';\n\n// 处理 ESM/CJS 兼容性\nconst traverse = typeof _traverse === 'function' ? _traverse : (_traverse as { default: typeof _traverse }).default;\n\n/**\n * 分析JS/TS文件,提取结构信息\n */\nexport function analyzeFile(code: unknown, filePath: string | null): AnalyzeResult {\n // 输入验证\n if (code === null || code === undefined) {\n return {\n parseError: 'Code content is null or undefined / 代码内容为空',\n errorType: ErrorTypes.VALIDATION_ERROR\n };\n }\n\n if (typeof code !== 'string') {\n return {\n parseError: 'Code must be a string / 代码必须是字符串类型',\n errorType: ErrorTypes.VALIDATION_ERROR\n };\n }\n\n // 检查空文件\n if (!code.trim()) {\n return {\n description: '',\n imports: [],\n functions: [],\n classes: [],\n constants: [],\n callGraph: {}\n };\n }\n\n const info: FileInfo = {\n description: '',\n imports: [],\n functions: [],\n classes: [],\n constants: [],\n callGraph: {}\n };\n\n // 提取现有的文件描述注释\n const existingCommentMatch = code.match(/^\\/\\*\\*[\\s\\S]*?\\*\\//);\n if (existingCommentMatch) {\n const commentText = existingCommentMatch[0];\n const lines = commentText\n .split('\\n')\n .map((l) => l.replace(/^\\s*\\*\\s?/, '').trim())\n .filter((l) => l && !l.startsWith('/') && !l.startsWith('@ai'));\n\n for (const line of lines) {\n if (line.startsWith('@description ')) {\n info.description = line.slice(13).trim();\n break;\n }\n }\n if (!info.description && lines.length > 0) {\n const firstLine = lines.find((l) => !l.startsWith('@'));\n if (firstLine) info.description = firstLine;\n }\n }\n\n let ast: t.File;\n try {\n const isTS = filePath && (filePath.endsWith('.ts') || filePath.endsWith('.tsx'));\n ast = parser.parse(code, {\n sourceType: 'unambiguous',\n plugins: ['jsx', 'classPrivateProperties', 'classPrivateMethods', ...(isTS ? (['typescript'] as const) : [])]\n });\n } catch (e) {\n const error = e as Error & { loc?: { line: number; column: number } };\n const errorMsg = formatError(ErrorTypes.PARSE_ERROR, `Syntax error: ${error.message} / 语法错误`, {\n file: filePath ?? undefined,\n line: error.loc?.line,\n column: error.loc?.column,\n suggestion: 'Check syntax errors in the file / 检查文件中的语法错误'\n });\n return {\n parseError: errorMsg,\n loc: error.loc,\n errorType: ErrorTypes.PARSE_ERROR\n };\n }\n\n // 收集导入信息\n const importsMap = new Map<string, Set<string>>();\n const localToModule = new Map<string, string>();\n const usageMap = new Map<string, Set<string>>();\n\n // 辅助函数:获取当前所在的函数/方法名\n function getEnclosingFunctionName(nodePath: NodePath): string | null {\n let current: NodePath | null = nodePath;\n while (current) {\n if (current.node.type === 'FunctionDeclaration') {\n const funcNode = current.node as t.FunctionDeclaration;\n if (funcNode.id) {\n return funcNode.id.name;\n }\n }\n if (current.node.type === 'ClassMethod') {\n const methodNode = current.node as t.ClassMethod;\n const classPath = current.parentPath?.parentPath;\n const classNode = classPath?.node as t.ClassDeclaration | undefined;\n const className = classNode?.id?.name ?? '';\n const methodName = (methodNode.key as t.Identifier)?.name ?? '';\n return className ? `${className}.${methodName}` : methodName;\n }\n if (current.node.type === 'ArrowFunctionExpression' || current.node.type === 'FunctionExpression') {\n const parent = current.parent as t.Node;\n if (parent?.type === 'VariableDeclarator') {\n const varDecl = parent as t.VariableDeclarator;\n const id = varDecl.id as t.Identifier;\n if (id?.name) return id.name;\n }\n }\n current = current.parentPath;\n }\n return null;\n }\n\n // 第一遍:收集导入信息\n traverse(ast, {\n VariableDeclarator(nodePath: NodePath<t.VariableDeclarator>) {\n const node = nodePath.node;\n if (\n node.init?.type === 'CallExpression' &&\n node.init.callee?.type === 'Identifier' &&\n node.init.callee.name === 'require' &&\n node.init.arguments?.[0]?.type === 'StringLiteral'\n ) {\n const moduleName = node.init.arguments[0].value;\n if (!importsMap.has(moduleName)) {\n importsMap.set(moduleName, new Set());\n }\n\n if (node.id.type === 'Identifier') {\n const localName = node.id.name;\n importsMap.get(moduleName)!.add(localName);\n localToModule.set(localName, moduleName);\n usageMap.set(localName, new Set());\n } else if (node.id.type === 'ObjectPattern') {\n for (const prop of node.id.properties) {\n if (prop.type === 'ObjectProperty' && prop.key.type === 'Identifier') {\n const localName = prop.value.type === 'Identifier' ? prop.value.name : prop.key.name;\n importsMap.get(moduleName)!.add(prop.key.name);\n localToModule.set(localName, moduleName);\n usageMap.set(localName, new Set());\n }\n }\n }\n }\n },\n\n CallExpression(nodePath: NodePath<t.CallExpression>) {\n const node = nodePath.node;\n if (\n node.callee?.type === 'Identifier' &&\n node.callee.name === 'require' &&\n node.arguments?.[0]?.type === 'StringLiteral'\n ) {\n const parent = nodePath.parent;\n if (parent?.type === 'MemberExpression' && parent.property?.type === 'Identifier') {\n const moduleName = node.arguments[0].value;\n if (!importsMap.has(moduleName)) {\n importsMap.set(moduleName, new Set());\n }\n importsMap.get(moduleName)!.add(parent.property.name);\n const grandParent = nodePath.parentPath?.parent;\n if (grandParent?.type === 'VariableDeclarator') {\n const varDecl = grandParent as t.VariableDeclarator;\n if (varDecl.id?.type === 'Identifier') {\n localToModule.set(varDecl.id.name, moduleName);\n usageMap.set(varDecl.id.name, new Set());\n }\n }\n }\n }\n },\n\n ImportDeclaration(nodePath: NodePath<t.ImportDeclaration>) {\n const node = nodePath.node;\n const moduleName = node.source.value;\n if (!importsMap.has(moduleName)) {\n importsMap.set(moduleName, new Set());\n }\n\n for (const specifier of node.specifiers) {\n let importedName: string | undefined;\n let localName: string | undefined;\n if (specifier.type === 'ImportDefaultSpecifier') {\n importedName = 'default';\n localName = specifier.local.name;\n } else if (specifier.type === 'ImportNamespaceSpecifier') {\n importedName = '*';\n localName = specifier.local.name;\n } else if (specifier.type === 'ImportSpecifier') {\n const imported = specifier.imported;\n importedName = imported.type === 'Identifier' ? imported.name : imported.value;\n localName = specifier.local.name;\n }\n if (importedName && localName) {\n importsMap.get(moduleName)!.add(importedName);\n localToModule.set(localName, moduleName);\n usageMap.set(localName, new Set());\n }\n }\n }\n });\n\n // 第二遍:收集函数/类信息,并追踪导入使用\n traverse(ast, {\n Identifier(nodePath: NodePath<t.Identifier>) {\n const name = nodePath.node.name;\n if (usageMap.has(name)) {\n const parent = nodePath.parent;\n if (parent?.type === 'VariableDeclarator' && (parent as t.VariableDeclarator).id === nodePath.node) return;\n if (parent?.type === 'ImportSpecifier' || parent?.type === 'ImportDefaultSpecifier') return;\n const enclosing = getEnclosingFunctionName(nodePath);\n if (enclosing) {\n usageMap.get(name)!.add(enclosing);\n }\n }\n },\n\n FunctionDeclaration(nodePath: NodePath<t.FunctionDeclaration>) {\n const node = nodePath.node;\n const name = node.id?.name ?? '[anonymous]';\n const params = node.params.map((p) => {\n if (p.type === 'Identifier') return p.name;\n if (p.type === 'AssignmentPattern' && p.left?.type === 'Identifier') return p.left.name + '?';\n if (p.type === 'RestElement' && p.argument?.type === 'Identifier') return '...' + p.argument.name;\n return '?';\n });\n const startLine = node.loc?.start?.line ?? 0;\n const endLine = node.loc?.end?.line ?? 0;\n\n let desc = '';\n const comments = node.leadingComments;\n if (comments && comments.length > 0) {\n desc = extractJSDocDescription(comments[comments.length - 1]);\n }\n\n info.functions.push({\n name,\n params: params.join(','),\n startLine,\n endLine,\n description: desc\n } as FunctionInfo);\n },\n\n ClassDeclaration(nodePath: NodePath<t.ClassDeclaration>) {\n const node = nodePath.node;\n const name = node.id?.name ?? '[anonymous]';\n const startLine = node.loc?.start?.line ?? 0;\n const endLine = node.loc?.end?.line ?? 0;\n const superClass = node.superClass?.type === 'Identifier' ? node.superClass.name : null;\n\n let desc = '';\n const comments = node.leadingComments;\n if (comments && comments.length > 0) {\n desc = extractJSDocDescription(comments[comments.length - 1]);\n }\n\n const methods: MethodInfo[] = [];\n if (node.body?.body) {\n for (const member of node.body.body) {\n if (member.type === 'ClassMethod') {\n const methodName = member.key?.type === 'Identifier' ? member.key.name : '[computed]';\n const methodParams = member.params.map((p) => {\n if (p.type === 'Identifier') return p.name;\n if (p.type === 'AssignmentPattern' && p.left?.type === 'Identifier') return p.left.name + '?';\n return '?';\n });\n const methodLine = member.loc?.start?.line ?? 0;\n\n let methodDesc = '';\n const methodComments = member.leadingComments;\n if (methodComments && methodComments.length > 0) {\n methodDesc = extractJSDocDescription(methodComments[methodComments.length - 1]);\n }\n\n methods.push({\n name: methodName,\n params: methodParams.join(','),\n line: methodLine,\n static: member.static,\n kind: member.kind as MethodInfo['kind'],\n description: methodDesc\n });\n }\n }\n }\n\n info.classes.push({\n name,\n superClass,\n startLine,\n endLine,\n methods,\n description: desc\n } as ClassInfo);\n },\n\n VariableDeclaration(nodePath: NodePath<t.VariableDeclaration>) {\n if (nodePath.parent.type !== 'Program') return;\n\n const node = nodePath.node;\n if (node.kind === 'const') {\n let desc = '';\n const comments = node.leadingComments;\n if (comments && comments.length > 0) {\n desc = extractJSDocDescription(comments[comments.length - 1]);\n }\n\n for (const decl of node.declarations) {\n const name = decl.id?.type === 'Identifier' ? decl.id.name : undefined;\n if (name && name === name.toUpperCase() && name.length > 2) {\n const startLine = node.loc?.start?.line ?? 0;\n info.constants.push({\n name,\n line: startLine,\n description: desc\n } as ConstantInfo);\n }\n }\n }\n }\n });\n\n // 转换导入信息\n for (const [moduleName, members] of importsMap) {\n const usedIn = new Set<string>();\n for (const localName of localToModule.keys()) {\n if (localToModule.get(localName) === moduleName && usageMap.has(localName)) {\n for (const fn of usageMap.get(localName)!) {\n usedIn.add(fn);\n }\n }\n }\n info.imports.push({\n module: moduleName,\n members: Array.from(members),\n usedIn: Array.from(usedIn)\n } as ImportInfo);\n }\n\n // 第三遍:构建函数调用图\n const definedFunctions = new Set<string>();\n for (const fn of info.functions) {\n definedFunctions.add(fn.name);\n }\n for (const cls of info.classes) {\n for (const method of cls.methods) {\n definedFunctions.add(method.name);\n definedFunctions.add(`${cls.name}.${method.name}`);\n }\n }\n\n // 收集所有导入的标识符\n const importedNames = new Set(localToModule.keys());\n\n const callGraph = new Map<string, Set<string>>();\n\n traverse(ast, {\n CallExpression(nodePath: NodePath<t.CallExpression>) {\n const node = nodePath.node;\n let calleeName: string | null = null;\n\n // 直接函数调用: funcName()\n if (node.callee.type === 'Identifier') {\n calleeName = node.callee.name;\n }\n // 方法调用: this.method() 或 obj.method()\n else if (node.callee.type === 'MemberExpression' && node.callee.property?.type === 'Identifier') {\n const objName = node.callee.object?.type === 'Identifier' ? node.callee.object.name : undefined;\n const propName = node.callee.property.name;\n // 导入对象的方法调用: fs.readFileSync()\n if (objName && importedNames.has(objName)) {\n calleeName = `${objName}.${propName}`;\n } else {\n calleeName = propName;\n }\n }\n\n if (calleeName) {\n const caller = getEnclosingFunctionName(nodePath);\n if (caller && caller !== calleeName) {\n // 文件内定义的函数 或 导入的函数\n const isDefinedFunc = definedFunctions.has(calleeName);\n const isImportedFunc =\n importedNames.has(calleeName) ||\n (calleeName.includes('.') && importedNames.has(calleeName.split('.')[0]!));\n\n if (isDefinedFunc || isImportedFunc) {\n if (!callGraph.has(caller)) callGraph.set(caller, new Set());\n callGraph.get(caller)!.add(calleeName);\n }\n }\n }\n }\n });\n\n // 转换调用图为对象格式\n info.callGraph = {};\n for (const [caller, callees] of callGraph) {\n info.callGraph[caller] = Array.from(callees);\n }\n\n return info;\n}\n\nexport { extractJSDocDescription };\n","import type { FileInfo } from '../types';\n\n/**\n * 生成紧凑格式AI注释头\n */\nexport function generateHeader(info: FileInfo, fileName: string): string {\n const lines: string[] = [];\n\n let headerLine = `/*@AI ${fileName}`;\n if (info.description) {\n headerLine += ` - ${info.description.slice(0, 50)}`;\n }\n lines.push(headerLine);\n\n // 导入信息\n for (const imp of info.imports) {\n const members = imp.members.join(',');\n let line = `<${imp.module}:${members}`;\n if (imp.usedIn?.length > 0) {\n line += ` ->${imp.usedIn.join(',')}`;\n }\n lines.push(line);\n }\n\n // 类信息\n for (const cls of info.classes) {\n let clsLine = cls.name;\n if (cls.superClass) clsLine += `:${cls.superClass}`;\n clsLine += ` ${cls.startLine}-${cls.endLine}`;\n if (cls.description) clsLine += ` ${cls.description}`;\n lines.push(clsLine);\n\n for (const method of cls.methods) {\n const prefix = method.static ? ' +' : ' .';\n const kindMark = method.kind === 'get' ? 'get:' : method.kind === 'set' ? 'set:' : '';\n let methodLine = `${prefix}${kindMark}${method.name}(${method.params}) ${method.line}`;\n if (method.description) methodLine += ` ${method.description}`;\n lines.push(methodLine);\n }\n }\n\n // 函数信息\n for (const fn of info.functions) {\n let fnLine = `${fn.name}(${fn.params}) ${fn.startLine}-${fn.endLine}`;\n if (fn.description) fnLine += ` ${fn.description}`;\n lines.push(fnLine);\n }\n\n // 常量信息\n for (const c of info.constants) {\n let constLine = `${c.name} ${c.line}`;\n if (c.description) constLine += ` ${c.description}`;\n lines.push(constLine);\n }\n\n lines.push('@AI*/');\n return lines.join('\\n');\n}\n\n/**\n * 移除现有的AI注释头\n */\nexport function removeExistingHeaders(code: string): string {\n let result = code;\n result = result.replace(/\\/\\*@AI[\\s\\S]*?@AI\\*\\/\\s*/g, '');\n result = result.replace(/\\/\\*\\*[\\s\\S]*?@ai-context-end[\\s\\S]*?\\*\\/\\s*/g, '');\n result = result.replace(/^\\/\\*\\*[\\s\\S]*?\\*\\/\\s*\\n?/, '');\n return result;\n}\n","import path from 'path';\nimport type { FileInfoEntry } from '../types';\n\n/**\n * 生成目录级 .fnmap 索引文件(包含完整信息)\n */\nexport function generateAiMap(dirPath: string, filesInfo: FileInfoEntry[]): string {\n const lines: string[] = [`@FNMAP ${path.basename(dirPath)}/`];\n\n for (const { relativePath, info } of filesInfo) {\n const fileName = path.basename(relativePath);\n let fileLine = `#${fileName}`;\n if (info.description) {\n fileLine += ` ${info.description.slice(0, 50)}`;\n }\n lines.push(fileLine);\n\n // 添加导入信息(不含使用位置,由函数行的调用图提供)\n for (const imp of info.imports) {\n const members = imp.members.join(',');\n lines.push(` <${imp.module}:${members}`);\n }\n\n // 添加类信息\n for (const cls of info.classes) {\n let clsLine = ` ${cls.name}`;\n if (cls.superClass) clsLine += `:${cls.superClass}`;\n clsLine += ` ${cls.startLine}-${cls.endLine}`;\n if (cls.description) clsLine += ` ${cls.description}`;\n lines.push(clsLine);\n\n // 类方法\n for (const method of cls.methods) {\n const prefix = method.static ? ' +' : ' .';\n const kindMark = method.kind === 'get' ? 'get:' : method.kind === 'set' ? 'set:' : '';\n let methodLine = `${prefix}${kindMark}${method.name}(${method.params}) ${method.line}`;\n if (method.description) methodLine += ` ${method.description}`;\n // 追加调用信息\n const methodKey = `${cls.name}.${method.name}`;\n const calls = info.callGraph?.[methodKey] ?? info.callGraph?.[method.name];\n if (calls && calls.length > 0) methodLine += ` →${calls.join(',')}`;\n lines.push(methodLine);\n }\n }\n\n // 添加函数信息\n for (const fn of info.functions) {\n let fnLine = ` ${fn.name}(${fn.params}) ${fn.startLine}-${fn.endLine}`;\n if (fn.description) fnLine += ` ${fn.description}`;\n // 追加调用信息\n const calls = info.callGraph?.[fn.name];\n if (calls && calls.length > 0) fnLine += ` →${calls.join(',')}`;\n lines.push(fnLine);\n }\n\n // 添加常量信息\n for (const c of info.constants) {\n let constLine = ` ${c.name} ${c.line}`;\n if (c.description) constLine += ` ${c.description}`;\n lines.push(constLine);\n }\n }\n\n lines.push('@FNMAP');\n return lines.join('\\n');\n}\n","import path from 'path';\nimport type { FileInfo, FileInfoEntry } from '../types';\n\n/**\n * 生成单文件的mermaid调用图\n */\nexport function generateFileMermaid(fileName: string, info: FileInfo): string | null {\n const lines: string[] = ['flowchart TD'];\n // 使用更安全的ID生成策略以避免冲突\n const safeId = (name: string): string =>\n 'id_' +\n name.replace(/[^a-zA-Z0-9]/g, (c) => `_${c.charCodeAt(0)}_`);\n const escapeLabel = (text: string): string => text.replace(/\"/g, '#quot;');\n\n // 收集所有函数节点\n const functions = info.functions.map((fn) => fn.name);\n const classMethods: string[] = [];\n for (const cls of info.classes) {\n for (const method of cls.methods) {\n classMethods.push(`${cls.name}.${method.name}`);\n }\n }\n const allFunctions = [...functions, ...classMethods];\n\n if (allFunctions.length === 0) {\n return null; // 没有函数,跳过\n }\n\n // 添加子图标题\n const baseName = path.basename(fileName, path.extname(fileName));\n lines.push(` subgraph ${safeId(baseName)}[\"${baseName}\"]`);\n\n // 添加函数节点\n for (const fn of allFunctions) {\n lines.push(` ${safeId(fn)}[\"${escapeLabel(fn)}\"]`);\n }\n lines.push(' end');\n\n // 添加调用关系边\n const callGraph = info.callGraph ?? {};\n for (const [caller, callees] of Object.entries(callGraph)) {\n for (const callee of callees) {\n // 只显示文件内部的调用关系\n if (allFunctions.includes(callee) || callee.includes('.')) {\n const calleeName = allFunctions.includes(callee) ? callee : callee.split('.').pop()!;\n if (allFunctions.includes(callee) || allFunctions.some((f) => f.endsWith(calleeName))) {\n lines.push(` ${safeId(caller)} --> ${safeId(callee)}`);\n }\n }\n }\n }\n\n return lines.join('\\n');\n}\n\n/**\n * 生成项目级mermaid调用图\n */\nexport function generateProjectMermaid(_projectDir: string, allFilesInfo: FileInfoEntry[]): string {\n const lines: string[] = ['flowchart TD'];\n\n const safeId = (name: string): string =>\n 'id_' +\n name.replace(/[^a-zA-Z0-9]/g, (c) => `_${c.charCodeAt(0)}_`);\n const escapeLabel = (text: string): string => text.replace(/\"/g, '#quot;');\n\n // 收集所有文件的函数和它们的调用关系\n const fileToFunctions = new Map<string, { fileName: string; functions: string[] }>();\n const allCallEdges: Array<{ file: string; fileName: string; caller: string; callee: string }> = [];\n\n for (const { relativePath, info } of allFilesInfo) {\n const fileName = path.basename(relativePath, path.extname(relativePath));\n const functions = info.functions.map((fn) => fn.name);\n const classMethods: string[] = [];\n for (const cls of info.classes) {\n for (const method of cls.methods) {\n classMethods.push(`${cls.name}.${method.name}`);\n }\n }\n const allFunctions = [...functions, ...classMethods];\n fileToFunctions.set(relativePath, { fileName, functions: allFunctions });\n\n // 收集调用边\n const callGraph = info.callGraph ?? {};\n for (const [caller, callees] of Object.entries(callGraph)) {\n for (const callee of callees) {\n allCallEdges.push({\n file: relativePath,\n fileName,\n caller,\n callee\n });\n }\n }\n }\n\n // 按文件生成子图\n for (const [, { fileName, functions }] of fileToFunctions) {\n if (functions.length === 0) continue;\n\n lines.push(` subgraph ${safeId(fileName)}[\"${escapeLabel(fileName)}\"]`);\n for (const fn of functions) {\n lines.push(` ${safeId(fileName)}_${safeId(fn)}[\"${escapeLabel(fn)}\"]`);\n }\n lines.push(' end');\n }\n\n // 添加调用关系边\n const addedEdges = new Set<string>();\n for (const { fileName, caller, callee } of allCallEdges) {\n const callerId = `${safeId(fileName)}_${safeId(caller)}`;\n\n // 查找callee所在的文件\n let calleeId: string | null = null;\n for (const [, { fileName: fn, functions }] of fileToFunctions) {\n if (functions.includes(callee)) {\n calleeId = `${safeId(fn)}_${safeId(callee)}`;\n break;\n }\n }\n\n // 如果没找到,可能是同文件内调用或外部调用\n if (!calleeId) {\n const matchingKey = [...fileToFunctions.keys()].find((k) => fileToFunctions.get(k)?.fileName === fileName);\n if (matchingKey) {\n const fileData = fileToFunctions.get(matchingKey);\n if (fileData?.functions.includes(callee)) {\n calleeId = `${safeId(fileName)}_${safeId(callee)}`;\n }\n }\n }\n\n if (calleeId) {\n const edgeKey = `${callerId}-->${calleeId}`;\n if (!addedEdges.has(edgeKey)) {\n lines.push(` ${callerId} --> ${calleeId}`);\n addedEdges.add(edgeKey);\n }\n }\n }\n\n return lines.join('\\n');\n}\n","import fs from 'fs';\nimport type { ProcessResult } from '../types';\nimport { ErrorTypes, isParseError } from '../types';\nimport { validateFilePath, formatError } from '../validation';\nimport { analyzeFile } from '../analyzer';\n\n/**\n * 处理单个文件(只分析,不修改文件)\n */\nexport function processFile(filePath: string): ProcessResult {\n // 使用验证函数\n const validation = validateFilePath(filePath);\n if (!validation.valid) {\n return {\n success: false,\n error: validation.error,\n errorType: validation.errorType ?? ErrorTypes.VALIDATION_ERROR\n };\n }\n\n try {\n const code = fs.readFileSync(filePath, 'utf-8');\n const result = analyzeFile(code, filePath);\n\n if (!result) {\n return {\n success: false,\n error: 'Analysis returned null / 分析返回空值',\n errorType: ErrorTypes.PARSE_ERROR\n };\n }\n\n // 检查是否有解析错误\n if (isParseError(result)) {\n return {\n success: false,\n error: result.parseError,\n errorType: result.errorType,\n loc: result.loc\n };\n }\n\n return { success: true, info: result };\n } catch (e) {\n const error = e as Error;\n // 捕获文件读取错误\n const errorMsg = formatError(ErrorTypes.FILE_READ_ERROR, `Failed to read or process file / 读取或处理文件失败`, {\n file: filePath,\n suggestion: error.message\n });\n return {\n success: false,\n error: errorMsg,\n errorType: ErrorTypes.FILE_READ_ERROR\n };\n }\n}\n","import fs from 'fs';\nimport path from 'path';\nimport type { CLIOptions, FileInfoEntry } from './types';\nimport { COLORS, DEFAULT_EXCLUDES } from './constants';\nimport { logger, setupCLI, setQuietMode } from './cli';\nimport { loadConfig, mergeConfig } from './config';\nimport { scanDirectory, getGitChangedFiles } from './scanner';\nimport { processFile } from './processor';\nimport { generateAiMap, generateFileMermaid, generateProjectMermaid } from './generator';\n\n/**\n * 主函数\n */\nexport function main(): void {\n // 配置并解析CLI\n const program = setupCLI();\n program.parse(process.argv);\n\n const options = program.opts<CLIOptions>();\n const args = program.args;\n\n // 设置静默模式\n if (options.quiet) {\n setQuietMode(true);\n }\n\n const projectDir = path.resolve(options.project);\n\n // init命令:创建默认配置文件\n if (options.init) {\n const configPath = path.join(projectDir, '.fnmaprc');\n if (fs.existsSync(configPath)) {\n console.log(`${COLORS.yellow}!${COLORS.reset} Config file already exists: .fnmaprc`);\n return;\n }\n\n const defaultConfig = {\n enable: true,\n include: ['src/**/*.js', 'src/**/*.ts', 'src/**/*.jsx', 'src/**/*.tsx'],\n exclude: ['node_modules', 'dist', 'build', '.next', 'coverage', '__pycache__', '.cache']\n };\n\n fs.writeFileSync(configPath, JSON.stringify(defaultConfig, null, 2));\n console.log(`${COLORS.green}✓${COLORS.reset} Created config file: .fnmaprc`);\n return;\n }\n\n // 合并文件参数\n const fileArgs = [...(options.files ?? []), ...args].filter((f) => fs.existsSync(f));\n\n // 确定要处理的文件列表\n let filesToProcess: string[] = [];\n\n if (options.changed || options.staged) {\n // 基于git改动\n filesToProcess = getGitChangedFiles(projectDir, options.staged);\n if (filesToProcess.length === 0) {\n logger.info('No git changed code files detected');\n return;\n }\n } else if (fileArgs.length > 0) {\n // 指定的文件\n filesToProcess = fileArgs.map((f) => (path.isAbsolute(f) ? f : path.resolve(projectDir, f)));\n } else if (options.dir) {\n // 扫描指定目录\n const targetDir = path.resolve(projectDir, options.dir);\n const relFiles = scanDirectory(targetDir, projectDir);\n filesToProcess = relFiles.map((f) => path.join(projectDir, f));\n } else {\n // 检查项目配置文件\n const { config, source } = loadConfig(projectDir);\n\n if (config) {\n logger.info(`Using config: ${source}`);\n\n // 检查是否启用\n if (config.enable === false) {\n logger.info('Config file has enable set to false, skipping processing');\n return;\n }\n\n // 合并配置\n const mergedConfig = mergeConfig(config);\n const excludes = [...DEFAULT_EXCLUDES, ...mergedConfig.exclude];\n\n if (mergedConfig.include) {\n for (const pattern of mergedConfig.include) {\n const dir = pattern.replace(/\\/\\*\\*\\/.*$/, '').replace(/\\*\\*\\/.*$/, '');\n const targetDir = dir ? path.resolve(projectDir, dir) : projectDir;\n if (fs.existsSync(targetDir)) {\n const relFiles = scanDirectory(targetDir, projectDir, excludes);\n filesToProcess.push(...relFiles.map((f) => path.join(projectDir, f)));\n }\n }\n }\n } else {\n logger.warn('No config file found. Use fnmap init to create config, or use --dir/--files to specify scope');\n logger.info('');\n logger.info('Supported config files: .fnmaprc, .fnmaprc.json, package.json#fnmap');\n return;\n }\n }\n\n if (filesToProcess.length === 0) {\n logger.info('No files found to process');\n return;\n }\n\n // 去重\n filesToProcess = [...new Set(filesToProcess)];\n\n logger.info('='.repeat(50));\n logger.title('fnmap - AI Code Indexing Tool');\n logger.info('='.repeat(50));\n\n let processed = 0;\n let failed = 0;\n const dirFilesMap = new Map<string, FileInfoEntry[]>();\n\n for (const filePath of filesToProcess) {\n const relativePath = path.relative(projectDir, filePath);\n logger.info(`\\nAnalyzing: ${relativePath}`);\n\n const result = processFile(filePath);\n\n if (result.success) {\n processed++;\n const info = result.info;\n logger.success(\n `Imports: ${info.imports.length}, Functions: ${info.functions.length}, Classes: ${info.classes.length}, Constants: ${info.constants.length}`\n );\n\n // 收集目录信息\n const dir = path.dirname(filePath);\n if (!dirFilesMap.has(dir)) {\n dirFilesMap.set(dir, []);\n }\n dirFilesMap.get(dir)!.push({ relativePath, info });\n } else {\n failed++;\n logger.error(result.error);\n }\n }\n\n // 生成.fnmap索引文件\n if (dirFilesMap.size > 0) {\n logger.info('\\nGenerating .fnmap index...');\n for (const [dir, filesInfo] of dirFilesMap) {\n const mapContent = generateAiMap(dir, filesInfo);\n const mapPath = path.join(dir, '.fnmap');\n fs.writeFileSync(mapPath, mapContent);\n logger.success(path.relative(projectDir, mapPath));\n }\n }\n\n // 生成Mermaid调用图\n if (options.mermaid && dirFilesMap.size > 0) {\n logger.info('\\nGenerating Mermaid call graphs...');\n\n if (options.mermaid === 'file' || options.mermaid === true) {\n // 文件级:每个文件生成一个mermaid图\n for (const [dir, filesInfo] of dirFilesMap) {\n for (const { relativePath, info } of filesInfo) {\n const mermaidContent = generateFileMermaid(relativePath, info);\n if (mermaidContent) {\n const baseName = path.basename(relativePath, path.extname(relativePath));\n const mermaidPath = path.join(dir, `${baseName}.mermaid`);\n fs.writeFileSync(mermaidPath, mermaidContent);\n logger.success(path.relative(projectDir, mermaidPath));\n }\n }\n }\n } else if (options.mermaid === 'project') {\n // 项目级:生成一个包含所有文件的mermaid图\n const allFilesInfo: FileInfoEntry[] = [];\n for (const [, filesInfo] of dirFilesMap) {\n allFilesInfo.push(...filesInfo);\n }\n const mermaidContent = generateProjectMermaid(projectDir, allFilesInfo);\n const mermaidPath = path.join(projectDir, '.fnmap.mermaid');\n fs.writeFileSync(mermaidPath, mermaidContent);\n logger.success(path.relative(projectDir, mermaidPath));\n }\n }\n\n logger.info('\\n' + '='.repeat(50));\n logger.info(\n `Complete! Analyzed: ${COLORS.green}${processed}${COLORS.reset}, Failed: ${failed > 0 ? COLORS.red : ''}${failed}${COLORS.reset}`\n );\n logger.info('='.repeat(50));\n}\n","#!/usr/bin/env node\n/**\n * fnmap - AI Code Indexing Tool\n * Analyzes JS/TS code structure and generates structured code maps to help AI understand code quickly\n */\n\n// 导出类型\nexport type {\n ErrorType,\n ValidationResult,\n ValidationSuccess,\n ValidationFailure,\n FnmapConfig,\n LoadedConfig,\n ImportInfo,\n FunctionInfo,\n MethodInfo,\n ClassInfo,\n ConstantInfo,\n CallGraph,\n FileInfo,\n ParseErrorResult,\n AnalyzeResult,\n ProcessResult,\n ProcessSuccess,\n ProcessFailure,\n CLIOptions,\n ErrorContext,\n FileInfoEntry\n} from './types';\n\n// 导出类型守卫\nexport { ErrorTypes, isParseError, isProcessSuccess, isProcessFailure, isValidationSuccess, isValidationFailure } from './types';\n\n// 导出常量\nexport { COLORS, SUPPORTED_EXTENSIONS, DEFAULT_EXCLUDES, DEFAULT_CONFIG, MAX_FILE_SIZE, MAX_DIR_DEPTH } from './constants';\n\n// 导出验证函数\nexport { validateFilePath, validateConfig, formatError } from './validation';\n\n// 导出配置函数\nexport { loadConfig, mergeConfig } from './config';\n\n// 导出 CLI\nexport { setupCLI, getVersion, logger, setQuietMode, isQuietMode, program } from './cli';\n\n// 导出扫描器\nexport { scanDirectory, getGitChangedFiles } from './scanner';\n\n// 导出分析器\nexport { analyzeFile, extractJSDocDescription } from './analyzer';\n\n// 导出生成器\nexport { generateHeader, removeExistingHeaders, generateAiMap, generateFileMermaid, generateProjectMermaid } from './generator';\n\n// 导出处理器\nexport { processFile } from './processor';\n\n// 导出主函数\nexport { main } from './main';\n\n// CLI 入口\nif (require.main === module) {\n const { main } = require('./main');\n main();\n}\n"],"names":["ErrorTypes","isParseError","result","isProcessSuccess","isProcessFailure","isValidationSuccess","isValidationFailure","COLORS","MAX_FILE_SIZE","MAX_DIR_DEPTH","SUPPORTED_EXTENSIONS","DEFAULT_EXCLUDES","DEFAULT_CONFIG","validateFilePath","filePath","fs","stats","e","validateConfig","config","cfg","formatError","_errorType","message","context","parts","quietMode","_program","logger","msg","setQuietMode","quiet","isQuietMode","getVersion","setupCLI","Command","val","f","getProgram","program","loadConfig","projectDir","configFiles","file","configPath","path","content","parseError","pe","errorMsg","validation","error","pkgPath","pkg","mergeConfig","userConfig","getGitChangedFiles","stagedOnly","files","output","child_process","staged","modified","untracked","changedFiles","ext","uniqueFiles","fullPath","scanDirectory","dir","baseDir","excludes","depth","visited","realPath","entries","entry","extractJSDocDescription","comment","lines","l","line","traverse","_traverse","analyzeFile","code","info","existingCommentMatch","firstLine","ast","isTS","parser","_a","_b","importsMap","localToModule","usageMap","getEnclosingFunctionName","nodePath","current","funcNode","methodNode","classPath","classNode","className","methodName","_c","parent","id","node","_d","moduleName","localName","prop","grandParent","_e","varDecl","_f","specifier","importedName","imported","name","enclosing","params","p","startLine","endLine","desc","comments","superClass","methods","_g","member","_h","methodParams","methodLine","_j","_i","methodDesc","methodComments","decl","members","usedIn","fn","definedFunctions","cls","method","importedNames","callGraph","calleeName","objName","propName","caller","isDefinedFunc","isImportedFunc","callees","generateHeader","fileName","headerLine","imp","clsLine","prefix","kindMark","fnLine","c","constLine","removeExistingHeaders","generateAiMap","dirPath","filesInfo","relativePath","fileLine","methodKey","calls","generateFileMermaid","safeId","escapeLabel","text","functions","classMethods","allFunctions","baseName","callee","generateProjectMermaid","_projectDir","allFilesInfo","fileToFunctions","allCallEdges","addedEdges","callerId","calleeId","matchingKey","k","fileData","edgeKey","processFile","main","options","args","defaultConfig","fileArgs","filesToProcess","targetDir","source","mergedConfig","pattern","relFiles","processed","failed","dirFilesMap","mapContent","mapPath","mermaidContent","mermaidPath"],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,69 @@
1
+ {
2
+ "name": "@didnhdj/fnmap",
3
+ "version": "0.1.0",
4
+ "description": "AI code indexing tool for analyzing JS/TS code structure and generating structured code maps to help AI understand code quickly",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "bin": {
8
+ "fnmap": "./dist/index.js"
9
+ },
10
+ "files": [
11
+ "dist",
12
+ "README.md",
13
+ "README_CN.md",
14
+ "LICENSE"
15
+ ],
16
+ "scripts": {
17
+ "build": "tsc && vite build",
18
+ "dev": "tsc --watch",
19
+ "typecheck": "tsc --noEmit",
20
+ "test": "vitest run",
21
+ "test:watch": "vitest",
22
+ "test:coverage": "vitest run --coverage",
23
+ "prepublishOnly": "npm test && npm run build",
24
+ "prepare": "npm run build"
25
+ },
26
+ "keywords": [
27
+ "ai",
28
+ "code-analysis",
29
+ "ast",
30
+ "javascript",
31
+ "typescript",
32
+ "code-index",
33
+ "code-map",
34
+ "fnmap",
35
+ "code-intelligence",
36
+ "static-analysis"
37
+ ],
38
+ "author": "gqfx <didnhdj2@gmail.com>",
39
+ "license": "MIT",
40
+ "repository": {
41
+ "type": "git",
42
+ "url": "git+https://github.com/gqfx/fnmap.git"
43
+ },
44
+ "bugs": {
45
+ "url": "https://github.com/gqfx/fnmap/issues"
46
+ },
47
+ "homepage": "https://github.com/gqfx/fnmap#readme",
48
+ "engines": {
49
+ "node": ">=14.0.0"
50
+ },
51
+ "dependencies": {
52
+ "@babel/generator": "^7.28.5",
53
+ "@babel/parser": "^7.28.5",
54
+ "@babel/traverse": "^7.28.5",
55
+ "@babel/types": "^7.28.5",
56
+ "commander": "^14.0.2",
57
+ "prettier": "^3.7.4"
58
+ },
59
+ "devDependencies": {
60
+ "@types/babel__traverse": "^7.28.0",
61
+ "@types/node": "^25.0.3",
62
+ "@vitest/coverage-v8": "^1.1.0",
63
+ "cross-env": "^10.1.0",
64
+ "esbuild": "^0.27.2",
65
+ "typescript": "^5.9.3",
66
+ "vite": "^5.0.0",
67
+ "vitest": "^1.1.0"
68
+ }
69
+ }