@didnhdj/fnmap 0.1.4 → 0.1.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +16 -16
  2. package/package.json +1 -1
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 v=require("fs"),I=require("path"),Ie=require("commander"),U=require("child_process"),Se=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 Fe(e){return e.success===!0}function _e(e){return e.success===!1}function ve(e){return e.valid===!0}function Ce(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 Oe(){return j}function ce(){try{return require("../../package.json").version}catch{return"0.1.0"}}function Y(){return G=new Ie.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",`
2
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const v=require("fs"),S=require("path"),Re=require("commander"),k=require("child_process"),Ie=require("@babel/parser"),J=require("@babel/traverse"),x={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 Fe(e){return e.success===!0}function Ce(e){return e.success===!1}function ve(e){return e.valid===!0}function _e(e){return e.valid===!1}const _={reset:"\x1B[0m",red:"\x1B[31m",green:"\x1B[32m",yellow:"\x1B[33m",blue:"\x1B[34m",gray:"\x1B[90m",bold:"\x1B[1m"},X=10*1024*1024,H=50,B=[".js",".ts",".jsx",".tsx",".mjs"],K=["node_modules",".git","dist","build",".next","coverage","__pycache__",".cache"],Q={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:x.VALIDATION_ERROR};if(!v.existsSync(e))return{valid:!1,error:`File not found: ${e} / 文件不存在: ${e}`,errorType:x.FILE_NOT_FOUND};try{const i=v.statSync(e);if(!i.isFile())return{valid:!1,error:`Path is not a file: ${e} / 路径不是文件: ${e}`,errorType:x.VALIDATION_ERROR};if(i.size>X)return{valid:!1,error:`File too large (${(i.size/1024/1024).toFixed(2)}MB > ${X/1024/1024}MB): ${e} / 文件过大`,errorType:x.FILE_TOO_LARGE}}catch(i){return{valid:!1,error:`Cannot access file: ${e}. Reason: ${i.message} / 无法访问文件`,errorType:x.PERMISSION_ERROR}}return{valid:!0}}function Z(e){if(!e||typeof e!="object")return{valid:!1,error:"Config must be an object / 配置必须是对象"};const i=e;return i.enable!==void 0&&typeof i.enable!="boolean"?{valid:!1,error:"Config.enable must be a boolean / enable 字段必须是布尔值"}:i.include!==void 0&&!Array.isArray(i.include)?{valid:!1,error:"Config.include must be an array / include 字段必须是数组"}:i.exclude!==void 0&&!Array.isArray(i.exclude)?{valid:!1,error:"Config.exclude must be an array / exclude 字段必须是数组"}:{valid:!0}}function U(e,i,o={}){const t=[i];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 L=!1,q=null;const R={error:e=>{L||console.error(`${_.red}✗${_.reset} ${e}`)},success:e=>{L||console.log(`${_.green}✓${_.reset} ${e}`)},info:e=>{L||console.log(e)},warn:e=>{L||console.warn(`${_.yellow}!${_.reset} ${e}`)},title:e=>{L||console.log(`${_.bold}${e}${_.reset}`)}};function ae(e){L=e}function xe(){return L}function ce(){try{return require("../../package.json").version}catch{return"0.1.0"}}function Y(){return q=new Re.Command,q.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(i=>i.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
4
  Configuration files (by priority):
5
5
  .fnmaprc JSON config file
6
6
  .fnmaprc.json JSON config file
@@ -19,17 +19,17 @@ Examples:
19
19
  $ fnmap --mermaid file --dir src Generate file-level call graphs
20
20
  $ fnmap --mermaid project Generate project-level call graph
21
21
  $ fnmap --init Create config file
22
- `),G}function P(){return G||Y()}const be={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=Se.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 $e=((ee=L.key)==null?void 0:ee.type)==="Identifier"?L.key.name:"[computed]",Ee=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+"?":"?"}),Re=((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:$e,params:Ee.join(","),line:Re,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 xe(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 Ae(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 ye(){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))}require.main===module&&ye();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=xe;exports.generateProjectMermaid=ge;exports.getGitChangedFiles=de;exports.getVersion=ce;exports.isParseError=oe;exports.isProcessFailure=_e;exports.isProcessSuccess=Fe;exports.isQuietMode=Oe;exports.isValidationFailure=Ce;exports.isValidationSuccess=ve;exports.loadConfig=le;exports.logger=E;exports.main=ye;exports.mergeConfig=fe;exports.processFile=he;exports.program=be;exports.removeExistingHeaders=Ae;exports.scanDirectory=z;exports.setQuietMode=ae;exports.setupCLI=Y;exports.validateConfig=Q;exports.validateFilePath=ie;
22
+ `),q}function P(){return q||Y()}const be={get opts(){return P().opts.bind(P())},get args(){return P().args},get parse(){return P().parse.bind(P())}};function le(e){const i=[".fnmaprc",".fnmaprc.json"];for(const t of i){const h=S.join(e,t);if(v.existsSync(h))try{const r=v.readFileSync(h,"utf-8");if(!r.trim()){R.warn(`Config file is empty: ${t}. Using default config / 配置文件为空,使用默认配置`);continue}let m;try{m=JSON.parse(r)}catch(d){const a=d,s=U(x.CONFIG_ERROR,`Failed to parse config file: ${t} / 配置文件解析失败`,{file:h,line:a.lineNumber,column:a.columnNumber,suggestion:"Check JSON syntax, ensure proper quotes and commas / 检查 JSON 语法,确保引号和逗号正确"});R.warn(s);continue}const l=Z(m);if(!l.valid){R.warn(`Invalid config in ${t}: ${l.error}`);continue}return{config:m,source:t}}catch(r){const m=r,l=U(x.FILE_READ_ERROR,`Failed to read config file: ${t} / 配置文件读取失败`,{file:h,suggestion:m.message});R.warn(l)}}const o=S.join(e,"package.json");if(v.existsSync(o))try{const t=JSON.parse(v.readFileSync(o,"utf-8"));if(t.fnmap){const h=Z(t.fnmap);if(!h.valid)R.warn(`Invalid fnmap config in package.json: ${h.error}`);else return{config:t.fnmap,source:"package.json#fnmap"}}}catch{}return{config:null,source:null}}function fe(e){return e?{...Q,...e,exclude:[...e.exclude??[]]}:Q}function Ae(e){try{return k.execSync("git rev-parse --show-toplevel",{cwd:e,encoding:"utf-8"}).trim()}catch{return null}}function de(e,i=!1){const o=[];try{const t=Ae(e);if(!t)return[];let h;if(i)h=k.execSync("git diff --cached --name-only --diff-filter=ACMR",{cwd:e,encoding:"utf-8"});else{const l=k.execSync("git diff --cached --name-only --diff-filter=ACMR",{cwd:e,encoding:"utf-8"}),d=k.execSync("git diff --name-only --diff-filter=ACMR",{cwd:e,encoding:"utf-8"}),a=k.execSync("git ls-files --others --exclude-standard",{cwd:e,encoding:"utf-8"});h=`${l}
23
+ ${d}
24
+ ${a}`}const r=h.split(`
25
+ `).map(l=>l.trim()).filter(Boolean).filter(l=>{const d=S.extname(l);return B.includes(d)}),m=[...new Set(r)];for(const l of m){const d=S.resolve(t,l);v.existsSync(d)&&d.startsWith(S.resolve(e))&&o.push(d)}}catch{return[]}return o}function Ne(e){const i=[];if(!v.existsSync(e))return i;try{const o=v.readdirSync(e,{withFileTypes:!0});for(const t of o)if(t.isFile()){const h=S.extname(t.name);B.includes(h)&&i.push(S.join(e,t.name))}}catch{return i}return i}function z(e,i=e,o=K,t=0,h=new Set){const r=[];if(!v.existsSync(e))return R.warn(`Directory does not exist: ${e} / 目录不存在`),r;if(t>H)return R.warn(`Max directory depth (${H}) exceeded: ${e} / 超过最大目录深度`),r;let m;try{m=v.realpathSync(e)}catch(d){const a=d;return R.warn(`Cannot resolve real path: ${e}. Reason: ${a.message} / 无法解析真实路径`),r}if(h.has(m))return R.warn(`Circular reference detected, skipping: ${e} / 检测到循环引用`),r;h.add(m);let l;try{l=v.readdirSync(e,{withFileTypes:!0})}catch(d){const a=d;return a.code==="EACCES"||a.code==="EPERM"?R.warn(`Permission denied: ${e} / 权限不足`):R.warn(`Failed to read directory: ${e}. Reason: ${a.message} / 读取目录失败`),r}for(const d of l)try{const a=S.join(e,d.name);if(d.isDirectory())o.includes(d.name)||r.push(...z(a,i,o,t+1,h));else if(d.isFile()){const s=S.extname(d.name);B.includes(s)&&r.push(S.relative(i,a))}}catch(a){const s=a;R.warn(`Error processing entry: ${d.name}. Reason: ${s.message} / 处理条目出错`)}return r}function G(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 W=typeof J=="function"?J:J.default;function pe(e,i){var u,F;if(e==null)return{parseError:"Code content is null or undefined / 代码内容为空",errorType:x.VALIDATION_ERROR};if(typeof e!="string")return{parseError:"Code must be a string / 代码必须是字符串类型",errorType:x.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(c=>c.replace(/^\s*\*\s?/,"").trim()).filter(c=>c&&!c.startsWith("/")&&!c.startsWith("@ai"));for(const c of n)if(c.startsWith("@description ")){o.description=c.slice(13).trim();break}if(!o.description&&n.length>0){const c=n.find(g=>!g.startsWith("@"));c&&(o.description=c)}}let h;try{const f=i&&(i.endsWith(".ts")||i.endsWith(".tsx"));h=Ie.parse(e,{sourceType:"unambiguous",plugins:["jsx","classPrivateProperties","classPrivateMethods",...f?["typescript"]:[]]})}catch(f){const n=f;return{parseError:U(x.PARSE_ERROR,`Syntax error: ${n.message} / 语法错误`,{file:i??void 0,line:(u=n.loc)==null?void 0:u.line,column:(F=n.loc)==null?void 0:F.column,suggestion:"Check syntax errors in the file / 检查文件中的语法错误"}),loc:n.loc,errorType:x.PARSE_ERROR}}const r=new Map,m=new Map,l=new Map;function d(f){var c,g,I;let n=f;for(;n;){if(n.node.type==="FunctionDeclaration"){const y=n.node;if(y.id)return y.id.name}if(n.node.type==="ClassMethod"){const y=n.node,E=(c=n.parentPath)==null?void 0:c.parentPath,$=E==null?void 0:E.node,C=((g=$==null?void 0:$.id)==null?void 0:g.name)??"",b=((I=y.key)==null?void 0:I.name)??"";return C?`${C}.${b}`:b}if(n.node.type==="ArrowFunctionExpression"||n.node.type==="FunctionExpression"){const y=n.parent;if((y==null?void 0:y.type)==="VariableDeclarator"){const $=y.id;if($!=null&&$.name)return $.name}}n=n.parentPath}return null}W(h,{VariableDeclarator(f){var c,g,I,y;const n=f.node;if(((c=n.init)==null?void 0:c.type)==="CallExpression"&&((g=n.init.callee)==null?void 0:g.type)==="Identifier"&&n.init.callee.name==="require"&&((y=(I=n.init.arguments)==null?void 0:I[0])==null?void 0:y.type)==="StringLiteral"){const E=n.init.arguments[0].value;if(r.has(E)||r.set(E,new Set),n.id.type==="Identifier"){const $=n.id.name;r.get(E).add($),m.set($,E),l.set($,new Set)}else if(n.id.type==="ObjectPattern"){for(const $ of n.id.properties)if($.type==="ObjectProperty"&&$.key.type==="Identifier"){const C=$.value.type==="Identifier"?$.value.name:$.key.name;r.get(E).add($.key.name),m.set(C,E),l.set(C,new Set)}}}},CallExpression(f){var c,g,I,y,E,$;const n=f.node;if(((c=n.callee)==null?void 0:c.type)==="Identifier"&&n.callee.name==="require"&&((I=(g=n.arguments)==null?void 0:g[0])==null?void 0:I.type)==="StringLiteral"){const C=f.parent;if((C==null?void 0:C.type)==="MemberExpression"&&((y=C.property)==null?void 0:y.type)==="Identifier"){const b=n.arguments[0].value;r.has(b)||r.set(b,new Set),r.get(b).add(C.property.name);const N=(E=f.parentPath)==null?void 0:E.parent;if((N==null?void 0:N.type)==="VariableDeclarator"){const O=N;(($=O.id)==null?void 0:$.type)==="Identifier"&&(m.set(O.id.name,b),l.set(O.id.name,new Set))}}}},ImportDeclaration(f){const n=f.node,c=n.source.value;r.has(c)||r.set(c,new Set);for(const g of n.specifiers){let I,y;if(g.type==="ImportDefaultSpecifier")I="default",y=g.local.name;else if(g.type==="ImportNamespaceSpecifier")I="*",y=g.local.name;else if(g.type==="ImportSpecifier"){const E=g.imported;I=E.type==="Identifier"?E.name:E.value,y=g.local.name}I&&y&&(r.get(c).add(I),m.set(y,c),l.set(y,new Set))}}}),W(h,{Identifier(f){const n=f.node.name;if(l.has(n)){const c=f.parent;if((c==null?void 0:c.type)==="VariableDeclarator"&&c.id===f.node||(c==null?void 0:c.type)==="ImportSpecifier"||(c==null?void 0:c.type)==="ImportDefaultSpecifier")return;const g=d(f);g&&l.get(n).add(g)}},FunctionDeclaration(f){var C,b,N,O,j;const n=f.node,c=((C=n.id)==null?void 0:C.name)??"[anonymous]",g=n.params.map(A=>{var M,T;return A.type==="Identifier"?A.name:A.type==="AssignmentPattern"&&((M=A.left)==null?void 0:M.type)==="Identifier"?A.left.name+"?":A.type==="RestElement"&&((T=A.argument)==null?void 0:T.type)==="Identifier"?"..."+A.argument.name:"?"}),I=((N=(b=n.loc)==null?void 0:b.start)==null?void 0:N.line)??0,y=((j=(O=n.loc)==null?void 0:O.end)==null?void 0:j.line)??0;let E="";const $=n.leadingComments;$&&$.length>0&&(E=G($[$.length-1])),o.functions.push({name:c,params:g.join(","),startLine:I,endLine:y,description:E})},ClassDeclaration(f){var b,N,O,j,A,M,T,ee,ne,te;const n=f.node,c=((b=n.id)==null?void 0:b.name)??"[anonymous]",g=((O=(N=n.loc)==null?void 0:N.start)==null?void 0:O.line)??0,I=((A=(j=n.loc)==null?void 0:j.end)==null?void 0:A.line)??0,y=((M=n.superClass)==null?void 0:M.type)==="Identifier"?n.superClass.name:null;let E="";const $=n.leadingComments;$&&$.length>0&&(E=G($[$.length-1]));const C=[];if((T=n.body)!=null&&T.body){for(const w of n.body.body)if(w.type==="ClassMethod"){const $e=((ee=w.key)==null?void 0:ee.type)==="Identifier"?w.key.name:"[computed]",Se=w.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=w.loc)==null?void 0:ne.start)==null?void 0:te.line)??0;let se="";const V=w.leadingComments;V&&V.length>0&&(se=G(V[V.length-1])),C.push({name:$e,params:Se.join(","),line:Ee,static:w.static,kind:w.kind,description:se})}}o.classes.push({name:c,superClass:y,startLine:g,endLine:I,methods:C,description:E})},VariableDeclaration(f){var c,g,I;if(f.parent.type!=="Program")return;const n=f.node;if(n.kind==="const"){let y="";const E=n.leadingComments;E&&E.length>0&&(y=G(E[E.length-1]));for(const $ of n.declarations){const C=((c=$.id)==null?void 0:c.type)==="Identifier"?$.id.name:void 0;if(C&&C===C.toUpperCase()&&C.length>2){const b=((I=(g=n.loc)==null?void 0:g.start)==null?void 0:I.line)??0;o.constants.push({name:C,line:b,description:y})}}}}});for(const[f,n]of r){const c=new Set;for(const g of m.keys())if(m.get(g)===f&&l.has(g))for(const I of l.get(g))c.add(I);o.imports.push({module:f,members:Array.from(n),usedIn:Array.from(c)})}const a=new Set;for(const f of o.functions)a.add(f.name);for(const f of o.classes)for(const n of f.methods)a.add(n.name),a.add(`${f.name}.${n.name}`);const s=new Set(m.keys()),p=new Map;W(h,{CallExpression(f){var g,I;const n=f.node;let c=null;if(n.callee.type==="Identifier")c=n.callee.name;else if(n.callee.type==="MemberExpression"&&((g=n.callee.property)==null?void 0:g.type)==="Identifier"){const y=((I=n.callee.object)==null?void 0:I.type)==="Identifier"?n.callee.object.name:void 0,E=n.callee.property.name;y&&s.has(y)?c=`${y}.${E}`:c=E}if(c){const y=d(f);if(y&&y!==c){const E=a.has(c),$=s.has(c)||c.includes(".")&&s.has(c.split(".")[0]);(E||$)&&(p.has(y)||p.set(y,new Set),p.get(y).add(c))}}}}),o.callGraph={};for(const[f,n]of p)o.callGraph[f]=Array.from(n);return o}function Oe(e,i){var h;const o=[];let t=`/*@AI ${i}`;e.description&&(t+=` - ${e.description.slice(0,50)}`),o.push(t);for(const r of e.imports){const m=r.members.join(",");let l=`<${r.module}:${m}`;((h=r.usedIn)==null?void 0:h.length)>0&&(l+=` ->${r.usedIn.join(",")}`),o.push(l)}for(const r of e.classes){let m=r.name;r.superClass&&(m+=`:${r.superClass}`),m+=` ${r.startLine}-${r.endLine}`,r.description&&(m+=` ${r.description}`),o.push(m);for(const l of r.methods){const d=l.static?" +":" .",a=l.kind==="get"?"get:":l.kind==="set"?"set:":"";let s=`${d}${a}${l.name}(${l.params}) ${l.line}`;l.description&&(s+=` ${l.description}`),o.push(s)}}for(const r of e.functions){let m=`${r.name}(${r.params}) ${r.startLine}-${r.endLine}`;r.description&&(m+=` ${r.description}`),o.push(m)}for(const r of e.constants){let m=`${r.name} ${r.line}`;r.description&&(m+=` ${r.description}`),o.push(m)}return o.push("@AI*/"),o.join(`
28
+ `)}function we(e){let i=e;return i=i.replace(/\/\*@AI[\s\S]*?@AI\*\/\s*/g,""),i=i.replace(/\/\*\*[\s\S]*?@ai-context-end[\s\S]*?\*\/\s*/g,""),i=i.replace(/^\/\*\*[\s\S]*?\*\/\s*\n?/,""),i}function ue(e,i){var t,h,r;const o=[`@FNMAP ${S.basename(e)}/`];for(const{relativePath:m,info:l}of i){let a=`#${S.basename(m)}`;l.description&&(a+=` ${l.description.slice(0,50)}`),o.push(a);for(const s of l.imports){const p=s.members.join(",");o.push(` <${s.module}:${p}`)}for(const s of l.classes){let p=` ${s.name}`;s.superClass&&(p+=`:${s.superClass}`),p+=` ${s.startLine}-${s.endLine}`,s.description&&(p+=` ${s.description}`),o.push(p);for(const u of s.methods){const F=u.static?" +":" .",f=u.kind==="get"?"get:":u.kind==="set"?"set:":"";let n=`${F}${f}${u.name}(${u.params}) ${u.line}`;u.description&&(n+=` ${u.description}`);const c=`${s.name}.${u.name}`,g=((t=l.callGraph)==null?void 0:t[c])??((h=l.callGraph)==null?void 0:h[u.name]);g&&g.length>0&&(n+=` →${g.join(",")}`),o.push(n)}}for(const s of l.functions){let p=` ${s.name}(${s.params}) ${s.startLine}-${s.endLine}`;s.description&&(p+=` ${s.description}`);const u=(r=l.callGraph)==null?void 0:r[s.name];u&&u.length>0&&(p+=` →${u.join(",")}`),o.push(p)}for(const s of l.constants){let p=` ${s.name} ${s.line}`;s.description&&(p+=` ${s.description}`),o.push(p)}}return o.push("@FNMAP"),o.join(`
29
+ `)}function me(e,i){const o=["flowchart TD"],t=s=>"id_"+s.replace(/[^a-zA-Z0-9]/g,p=>`_${p.charCodeAt(0)}_`),h=s=>s.replace(/"/g,"#quot;"),r=i.functions.map(s=>s.name),m=[];for(const s of i.classes)for(const p of s.methods)m.push(`${s.name}.${p.name}`);const l=[...r,...m];if(l.length===0)return null;const d=S.basename(e,S.extname(e));o.push(` subgraph ${t(d)}["${d}"]`);for(const s of l)o.push(` ${t(s)}["${h(s)}"]`);o.push(" end");const a=i.callGraph??{};for(const[s,p]of Object.entries(a))for(const u of p)if(l.includes(u)||u.includes(".")){const F=l.includes(u)?u:u.split(".").pop();(l.includes(u)||l.some(f=>f.endsWith(F)))&&o.push(` ${t(s)} --> ${t(u)}`)}return o.join(`
30
+ `)}function ge(e,i){const o=["flowchart TD"],t=d=>"id_"+d.replace(/[^a-zA-Z0-9]/g,a=>`_${a.charCodeAt(0)}_`),h=d=>d.replace(/"/g,"#quot;"),r=new Map,m=[];for(const{relativePath:d,info:a}of i){const s=S.basename(d,S.extname(d)),p=a.functions.map(n=>n.name),u=[];for(const n of a.classes)for(const c of n.methods)u.push(`${n.name}.${c.name}`);const F=[...p,...u];r.set(d,{fileName:s,functions:F});const f=a.callGraph??{};for(const[n,c]of Object.entries(f))for(const g of c)m.push({file:d,fileName:s,caller:n,callee:g})}for(const[,{fileName:d,functions:a}]of r)if(a.length!==0){o.push(` subgraph ${t(d)}["${h(d)}"]`);for(const s of a)o.push(` ${t(d)}_${t(s)}["${h(s)}"]`);o.push(" end")}const l=new Set;for(const{fileName:d,caller:a,callee:s}of m){const p=`${t(d)}_${t(a)}`;let u=null;for(const[,{fileName:F,functions:f}]of r)if(f.includes(s)){u=`${t(F)}_${t(s)}`;break}if(!u){const F=[...r.keys()].find(f=>{var n;return((n=r.get(f))==null?void 0:n.fileName)===d});if(F){const f=r.get(F);f!=null&&f.functions.includes(s)&&(u=`${t(d)}_${t(s)}`)}}if(u){const F=`${p}-->${u}`;l.has(F)||(o.push(` ${p} --> ${u}`),l.add(F))}}return o.join(`
31
+ `)}function he(e){const i=ie(e);if(!i.valid)return{success:!1,error:i.error,errorType:i.errorType??x.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:x.PARSE_ERROR}}catch(o){const t=o;return{success:!1,error:U(x.FILE_READ_ERROR,"Failed to read or process file / 读取或处理文件失败",{file:e,suggestion:t.message}),errorType:x.FILE_READ_ERROR}}}const Le="\n\n## .fnmap Code Index Format\n\nThe `.fnmap` file provides a structured code index for quick navigation. Read it before exploring code to locate target files and function line numbers.\n\n**Format Reference:**\n\n- `#filename.js` - Filename followed by file description\n- `<module:members` - Imported module and its members\n- `funcName(params) 10-20 description →callee1,callee2` - Function signature, line range, description, call graph\n- `ClassName:SuperClass 30-100` - Class definition with inheritance\n- ` .method(params) 35 →callee` - Instance method (`.` prefix)\n- ` +staticMethod(params) 40` - Static method (`+` prefix)\n- `CONST_NAME 5` - Constant definition with line number\n\n**Call Graph:** The `→` at the end of function/method lines indicates which functions are called (both local and imported), helping you understand code execution flow.\n\n**Note:** `.fnmap` files are auto-maintained by scripts and should not be manually updated.\n";function je(e){const i=["CLAUDE.md","AGENTS.md"],o=v.readdirSync(e);for(const t of i){const h=o.find(r=>r.toLowerCase()===t.toLowerCase());if(h){const r=S.join(e,h);if(v.readFileSync(r,"utf-8").includes(".fnmap Code Index Format")){console.log(`${_.yellow}!${_.reset} ${h} already contains fnmap documentation`);continue}v.appendFileSync(r,Le),console.log(`${_.green}✓${_.reset} Appended fnmap documentation to ${h}`)}}}function ye(){const e=Y();e.parse(process.argv);const i=e.opts(),o=e.args;i.quiet&&ae(!0);const t=S.resolve(i.project);if(i.init){const a=S.join(t,".fnmaprc");if(v.existsSync(a)){console.log(`${_.yellow}!${_.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(a,JSON.stringify(s,null,2)),console.log(`${_.green}✓${_.reset} Created config file: .fnmaprc`),je(t);return}const h=[...i.files??[],...o].filter(a=>v.existsSync(a));let r=[];if(i.changed||i.staged){const a=de(t,i.staged);if(a.length===0){R.info("No git changed code files detected");return}const s=new Set;for(const p of a)s.add(S.dirname(p));for(const p of s){const u=Ne(p);r.push(...u)}}else if(h.length>0)r=h.map(a=>S.isAbsolute(a)?a:S.resolve(t,a));else if(i.dir){const a=S.resolve(t,i.dir);r=z(a,t).map(p=>S.join(t,p))}else{const{config:a,source:s}=le(t);if(a){if(R.info(`Using config: ${s}`),a.enable===!1){R.info("Config file has enable set to false, skipping processing");return}const p=fe(a),u=[...K,...p.exclude];if(p.include)for(const F of p.include){const f=F.replace(/\/\*\*\/.*$/,"").replace(/\*\*\/.*$/,""),n=f?S.resolve(t,f):t;if(v.existsSync(n)){const c=z(n,t,u);r.push(...c.map(g=>S.join(t,g)))}}}else{R.warn("No config file found. Use fnmap init to create config, or use --dir/--files to specify scope"),R.info(""),R.info("Supported config files: .fnmaprc, .fnmaprc.json, package.json#fnmap");return}}if(r.length===0){R.info("No files found to process");return}r=[...new Set(r)],R.info("=".repeat(50)),R.title("fnmap - AI Code Indexing Tool"),R.info("=".repeat(50));let m=0,l=0;const d=new Map;for(const a of r){const s=S.relative(t,a);R.info(`
32
+ Analyzing: ${s}`);const p=he(a);if(p.success){m++;const u=p.info;R.success(`Imports: ${u.imports.length}, Functions: ${u.functions.length}, Classes: ${u.classes.length}, Constants: ${u.constants.length}`);const F=S.dirname(a);d.has(F)||d.set(F,[]),d.get(F).push({relativePath:s,info:u})}else l++,R.error(p.error)}if(d.size>0){R.info(`
33
+ Generating .fnmap index...`);for(const[a,s]of d){const p=ue(a,s),u=S.join(a,".fnmap");v.writeFileSync(u,p),R.success(S.relative(t,u))}}if(i.mermaid&&d.size>0){if(R.info(`
34
+ Generating Mermaid call graphs...`),i.mermaid==="file"||i.mermaid===!0)for(const[a,s]of d)for(const{relativePath:p,info:u}of s){const F=me(p,u);if(F){const f=S.basename(p,S.extname(p)),n=S.join(a,`${f}.mermaid`);v.writeFileSync(n,F),R.success(S.relative(t,n))}}else if(i.mermaid==="project"){const a=[];for(const[,u]of d)a.push(...u);const s=ge(t,a),p=S.join(t,".fnmap.mermaid");v.writeFileSync(p,s),R.success(S.relative(t,p))}}R.info(`
35
+ `+"=".repeat(50)),R.info(`Complete! Analyzed: ${_.green}${m}${_.reset}, Failed: ${l>0?_.red:""}${l}${_.reset}`),R.info("=".repeat(50))}require.main===module&&ye();exports.COLORS=_;exports.DEFAULT_CONFIG=Q;exports.DEFAULT_EXCLUDES=K;exports.ErrorTypes=x;exports.MAX_DIR_DEPTH=H;exports.MAX_FILE_SIZE=X;exports.SUPPORTED_EXTENSIONS=B;exports.analyzeFile=pe;exports.extractJSDocDescription=G;exports.formatError=U;exports.generateAiMap=ue;exports.generateFileMermaid=me;exports.generateHeader=Oe;exports.generateProjectMermaid=ge;exports.getGitChangedFiles=de;exports.getVersion=ce;exports.isParseError=oe;exports.isProcessFailure=Ce;exports.isProcessSuccess=Fe;exports.isQuietMode=xe;exports.isValidationFailure=_e;exports.isValidationSuccess=ve;exports.loadConfig=le;exports.logger=R;exports.main=ye;exports.mergeConfig=fe;exports.processFile=he;exports.program=be;exports.removeExistingHeaders=we;exports.scanDirectory=z;exports.setQuietMode=ae;exports.setupCLI=Y;exports.validateConfig=Z;exports.validateFilePath=ie;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@didnhdj/fnmap",
3
- "version": "0.1.4",
3
+ "version": "0.1.6",
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",