@wtdlee/repomap 0.3.2 → 0.4.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/dist/analyzers/index.d.ts +2 -2
- package/dist/analyzers/index.js +1 -1
- package/dist/{chunk-SL2GMDBN.js → chunk-H4YGP3GL.js} +1 -1
- package/dist/{chunk-J2CM7T7U.js → chunk-IAO54U6X.js} +2 -1
- package/dist/{chunk-UJT5KTVK.js → chunk-PTR5IROV.js} +30 -30
- package/dist/{chunk-4K4MGTPV.js → chunk-RFKIKTGM.js} +18 -18
- package/dist/cli.js +3 -3
- package/dist/{dataflow-analyzer-BfAiqVUp.d.ts → dataflow-analyzer-LeE44O3x.d.ts} +25 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/{rails-TJCDGBBF.js → rails-FFISZ4AE.js} +1 -1
- package/dist/rails-map-generator-UFLCMFAT.js +1 -0
- package/dist/server/index.js +1 -1
- package/package.json +1 -1
- package/dist/rails-map-generator-JL5PKHYP.js +0 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { B as BaseAnalyzer } from '../dataflow-analyzer-
|
|
2
|
-
export { D as DataFlowAnalyzer, G as GraphQLAnalyzer, P as PagesAnalyzer } from '../dataflow-analyzer-
|
|
1
|
+
import { B as BaseAnalyzer } from '../dataflow-analyzer-LeE44O3x.js';
|
|
2
|
+
export { D as DataFlowAnalyzer, G as GraphQLAnalyzer, P as PagesAnalyzer } from '../dataflow-analyzer-LeE44O3x.js';
|
|
3
3
|
import { RepositoryConfig, AnalysisResult } from '../types.js';
|
|
4
4
|
|
|
5
5
|
/**
|
package/dist/analyzers/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export{a as BaseAnalyzer,d as DataFlowAnalyzer,c as GraphQLAnalyzer,b as PagesAnalyzer,e as RestApiAnalyzer}from'../chunk-
|
|
1
|
+
export{a as BaseAnalyzer,d as DataFlowAnalyzer,c as GraphQLAnalyzer,b as PagesAnalyzer,e as RestApiAnalyzer}from'../chunk-IAO54U6X.js';
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {k}from'./chunk-
|
|
1
|
+
import {k}from'./chunk-PTR5IROV.js';import*as r from'fs';import*as c from'path';var o=class{constructor(t){this.rootPath=t;}result=null;async generate(t={}){if(!this.rootPath)throw new Error("Root path required for analysis");let{title:e="Rails Application Map"}=t;this.result=await k(this.rootPath);let a=this.generateHTML(e);return t.outputPath&&(r.writeFileSync(t.outputPath,a),console.log(`
|
|
2
2
|
\u{1F4C4} Generated: ${t.outputPath}`)),a}generateFromResult(t,e="Rails Application Map"){return this.result=t,this.generateHTML(e)}generateHTML(t){if(!this.result)throw new Error("Analysis not run");let{routes:e,controllers:a,models:s,grpc:i,summary:l}=this.result;return `<!DOCTYPE html>
|
|
3
3
|
<html lang="en">
|
|
4
4
|
<head>
|
|
@@ -1 +1,2 @@
|
|
|
1
|
-
import {Project,Node,SyntaxKind}from'ts-morph';import z from'fast-glob';import*as N from'path';import*as W from'fs';import*as L from'fs/promises';import {parse}from'graphql';var F=class{config;basePath;constructor(e){this.config=e,this.basePath=e.path;}resolvePath(e){return `${this.basePath}/${e}`}getSetting(e,t=""){return this.config.settings[e]??t}log(e){console.log(`[${this.getName()}] ${e}`);}warn(e){console.warn(`[${this.getName()}] \u26A0\uFE0F ${e}`);}error(e,t){console.error(`[${this.getName()}] \u274C ${e}`,t?.message||"");}};async function E(D,e,t=8){let n=new Array(D.length).fill(null),o=0;async function i(){for(;o<D.length;){let s=o++;if(s<D.length)try{n[s]=await e(D[s],s);}catch{n[s]=null;}}}let a=Array(Math.min(t,D.length)).fill(null).map(()=>i());return await Promise.all(a),n.filter(s=>s!==null)}var q=class extends F{project;codegenMap=new Map;constructor(e){super(e),this.project=new Project({tsConfigFilePath:this.resolvePath("tsconfig.json"),skipAddingFilesFromTsConfig:true});}extractOperationNameFromGql(e){let t=e.match(/(?:query|mutation|subscription)\s+(\w+)/);if(t&&t[1])return t[1];let n=e.match(/`\s*(?:query|mutation|subscription)\s+(\w+)/);if(n&&n[1])return n[1];let o=e.match(/GraphQL[^`]*`\s*(?:\n\s*)?(?:query|mutation|subscription)\s+(\w+)/);return o&&o[1]?o[1]:null}findOperationNameFromVariable(e,t){let n=e.getVariableDeclaration(t);if(!n){let s=e.getExportedDeclarations().get(t);if(s&&s.length>0){let r=s[0];Node.isVariableDeclaration(r)&&(n=r);}}if(n||(n=e.getVariableDeclarations().find(s=>s.getName()===t)),!n){let a=e.getFullText(),s=[new RegExp(`(?:const|let|var)\\s+${t}\\s*=\\s*gql\\s*\\(\\s*/\\*[^*]*\\*/\\s*\`\\s*(?:query|mutation|subscription)\\s+(\\w+)`,"s"),new RegExp(`(?:const|let|var)\\s+${t}\\s*=\\s*gql\`\\s*(?:query|mutation|subscription)\\s+(\\w+)`,"s"),new RegExp(`(?:const|let|var)\\s+${t}\\s*=\\s*gql\\s*\\(\`\\s*(?:query|mutation|subscription)\\s+(\\w+)`,"s"),new RegExp(`(?:const|let|var)\\s+${t}\\s*=\\s*graphql\\s*\\(\`\\s*(?:query|mutation|subscription)\\s+(\\w+)`,"s")];for(let r of s){let c=a.match(r);if(c&&c[1])return c[1]}return null}let o=n.getInitializer();if(!o)return null;let i=o.getText();return this.extractOperationNameFromGql(i)}getName(){return "PagesAnalyzer"}async analyze(){this.log("Starting page analysis..."),await this.loadCodegenMapping(),await this.analyzeAppFile();let e=await this.findPageFiles();this.log(`Found ${e.length} page files`);for(let o of e)try{this.project.addSourceFileAtPath(o);}catch{}let n=(await E(e,async o=>{let i=this.detectPagesRoot(o);return this.analyzePageFile(o,i)},4)).filter(o=>o!==null);return this.log(`Analyzed ${n.length} pages successfully`),{pages:n}}async analyzePageFile(e,t){let n=this.project.getSourceFile(e);if(!n)return null;let o=N.relative(t,e),i=this.filePathToRoutePath(o),a=this.findPageComponent(n);if(!a)return null;let s=this.extractRouteParams(i),r=this.extractLayout(n),c=this.extractAuthRequirement(n),l=this.extractPermissions(n),g=this.extractDataFetching(n),u=this.extractNavigation(n),d=this.extractLinkedPages(n),C=this.extractSteps(n);return {path:i,filePath:o,component:a,params:s,layout:r,authentication:c,permissions:l,dataFetching:g,navigation:u,linkedPages:d,steps:C.length>0?C:void 0}}detectPagesRoot(e){let t=["/src/pages/","/pages/","/src/app/","/app/","/frontend/src/pages/","/app/javascript/pages/"];for(let n of t){let o=e.indexOf(n);if(o!==-1)return e.substring(0,o+n.length-1)}return this.basePath}filePathToRoutePath(e){return "/"+e.replace(/\.tsx?$/,"").replace(/\/index$/,"").replace(/\[\.\.\.(\w+)\]/g,"*").replace(/\[(\w+)\]/g,":$1")}extractRouteParams(e){let t=[],n=/:(\w+)/g,o;for(;(o=n.exec(e))!==null;)t.push(o[1]);return t}findPageComponent(e){let t=e.getDefaultExportSymbol();if(t){let s=t.getName();if(s!=="default")return s}let n=e.getExportAssignment(s=>!s.isExportEquals());if(n){let s=n.getExpression();if(s){let r=s.getText();if(/^[A-Z][a-zA-Z0-9]*$/.test(r))return r;if(Node.isFunctionExpression(s)||Node.isArrowFunction(s))return "default"}}let o=e.getFunctions();for(let s of o)if(s.isDefaultExport())return s.getName()||"default";if(e.getVariableDeclaration("Page"))return "Page";let a=e.getVariableDeclarations();for(let s of a){let r=s.getTypeNode();if(r){let c=r.getText();if(c.includes("NextPage")||c.includes("FC")||c.includes("React.FC"))return s.getName()}}for(let s of a){let r=s.getName();if(/^[A-Z][a-zA-Z0-9]*$/.test(r)){let c=s.getInitializer();if(c&&(Node.isArrowFunction(c)||Node.isFunctionExpression(c))){let l=c.getText();if(l.includes("return")&&(l.includes("<")||l.includes("jsx")))return r}}}return null}extractLayout(e){let t=e.getDescendantsOfKind(SyntaxKind.PropertyAccessExpression).find(n=>n.getName()==="getLayout");if(t){let n=t.getParent();if(Node.isBinaryExpression(n)){let i=n.getRight().getDescendantsOfKind(SyntaxKind.JsxOpeningElement);if(i.length>0)return i[0].getTagNameNode().getText()}}}extractAuthRequirement(e){let n=e.getFilePath().split("/").pop()||"",a={required:!["404.tsx","permission-denied.tsx","_app.tsx","_document.tsx","_error.tsx"].some(s=>n===s)};try{let s=["RequiredCondition","ProtectedRoute","AuthGuard","PrivateRoute","WithAuth","RequireAuth","Authenticated","Authorized"],r=e.getDescendantsOfKind(SyntaxKind.JsxOpeningElement).find(c=>{let l=c.getTagNameNode().getText();return s.some(g=>l.includes(g))});if(r){a.condition="Additional permissions required";let c=r.getAttributes();for(let l of c)if(l.isKind(SyntaxKind.JsxAttribute))try{let g=l.getNameNode().getText();if(["condition","roles","permissions","requiredRoles","allowedRoles"].includes(g)){let u=l.getInitializer();if(u){a.condition=u.getText();let d=this.extractRolesFromCondition(u.getText());d.length>0&&(a.roles=d);}}}catch{}}}catch{}return a}extractRolesFromCondition(e){let t=[],n=/(\w+Role|\w+Permission)\.(\w+)/g,o;for(;(o=n.exec(e))!==null;)t.push(o[2]);let i=/['"]([a-zA-Z_-]+)['"]/g;for(;(o=i.exec(e))!==null;){let s=o[1];/admin|user|owner|member|guest|manager|editor|viewer/i.test(s)&&t.push(s);}let a=/\b(ROLE_\w+|[A-Z]+_ROLE)\b/g;for(;(o=a.exec(e))!==null;)t.push(o[1]);return [...new Set(t)]}extractPermissions(e){let t=[],n=e.getDescendantsOfKind(SyntaxKind.PropertyAccessExpression).filter(o=>{let i=o.getText();return i.includes("Permission")||i.includes("Role")||i.includes("isAdmin")});for(let o of n){let i=o.getText();t.includes(i)||t.push(i);}return t}extractDataFetching(e){let t=[],n=new Map,o=["useQuery","useMutation","useLazyQuery","useSubscription"];for(let S of e.getImportDeclarations()){let p=S.getModuleSpecifierValue();if(p.includes("@apollo/client")||p.includes("apollo"))for(let f of S.getNamedImports()){let x=f.getName(),m=f.getAliasNode()?.getText()||x;o.includes(x)&&n.set(m,x);}}let i=n.size>0||e.getImportDeclarations().some(S=>{let p=S.getModuleSpecifierValue();return p.includes("@apollo/client")||p.includes("apollo")}),a=e.getDescendantsOfKind(SyntaxKind.CallExpression).filter(S=>{let p=S.getExpression().getText();return !!(n.has(p)||o.includes(p)||/^use[A-Z].*Query$/.test(p)&&!p.includes("Params")&&!p.includes("String")||/^use[A-Z].*Mutation$/.test(p))});for(let S of a){let p=S.getExpression().getText(),f;n.has(p)?f=n.get(p):p.includes("Mutation")?f="useMutation":p.includes("Lazy")?f="useLazyQuery":f="useQuery";let x=S.getArguments();if(x.length===0){if(/^use[A-Z]/.test(p)){let T=p.replace(/^use/,"").replace(/Query$|Mutation$/,"");t.push({type:f,operationName:T,variables:[]});}continue}let h=x[0].getText();if(h.startsWith("[")||h.startsWith("{")||h.startsWith("'")||h.startsWith('"')||h.startsWith("`")||!(i||h.endsWith("Document")||h.endsWith("Query")||h.endsWith("Mutation")||h.includes("gql")||/^[A-Z_]+$/.test(h)))continue;let y=h.replace(/Document$/,"").replace(/Query$|Mutation$/,""),A=[];if(x.length>1){let w=x[1].getDescendantsOfKind(SyntaxKind.PropertyAssignment).find(b=>{try{return b.getName()==="variables"}catch{return false}});if(w){let b=w.getInitializer();if(b){let R=b.getDescendantsOfKind(SyntaxKind.PropertyAssignment);for(let Z of R)try{A.push(Z.getName());}catch{}}}}t.push({type:f,operationName:y,variables:A});}let s=e.getVariableDeclaration("getServerSideProps"),r=e.getFunction("getServerSideProps"),c=s||r;if(c){let S=e.getImportDeclarations();for(let x of S){let m=x.getNamedImports();for(let h of m){let v=h.getName();if(v.endsWith("Document")&&e.getDescendantsOfKind(SyntaxKind.Identifier).filter(A=>A.getText()===v).length>0){let A=v.replace(/Document$/,"");t.push({type:"getServerSideProps",operationName:`\u2192 ${A}`});}}}let f=c.getText().match(/query:\s*(\w+)/g);if(f)for(let x of f){let m=x.replace(/query:\s*/,"");t.some(h=>h.operationName?.includes(m.replace(/Document$/,"")))||t.push({type:"getServerSideProps",operationName:`\u2192 ${m.replace(/Document$/,"")}`});}}let l=e.getVariableDeclaration("getStaticProps"),g=e.getFunction("getStaticProps");(l||g)&&t.push({type:"getStaticProps",operationName:"getStaticProps"});let u=e.getImportDeclarations(),d=e.getFilePath(),C=N.dirname(d);for(let S of u){let p=S.getModuleSpecifierValue(),f=p.startsWith(".")||p.startsWith("/"),x=!p.includes("node_modules")&&!p.startsWith("@types/")&&p.startsWith("@")===false;if(f||x){if(p.includes("__generated__")||p.includes("/generated/"))continue;let m=[],h=S.getNamedImports();for(let y of h){let A=y.getName();this.isComponentName(A)&&m.push(A);}let v=S.getDefaultImport();if(v){let y=v.getText();this.isComponentName(y)&&m.push(y);}for(let y of m){let A=this.analyzeImportedComponent(C,p,y);if(A.length>0)for(let T of A)t.push({type:T.type,operationName:T.operationName.startsWith("\u2192")?`\u2192 ${T.operationName} (${y})`:`\u2192 ${T.operationName} (${y})`,variables:T.variables});else t.push({type:"component",operationName:y,variables:[]});}}}return t}symbolTraceCache=new Map;analyzeImportedComponent(e,t,n,o=new Set,i=0){if(i>10)return [];let s=[];try{let r=N.resolve(e,t),c=`${r}:${n}`;if(o.has(c))return [];o.add(c);let l=this.symbolTraceCache.get(c);if(l!==void 0)return l;let g=[`${r}.tsx`,`${r}.ts`,`${r}/index.tsx`,`${r}/index.ts`,`${r}/${n}.tsx`,`${r}/${n}.ts`],u,d;for(let p of g)try{if(u=this.project.addSourceFileAtPath(p),u){d=p;break}}catch{}if(!u||!d)return s;if(d.endsWith("index.tsx")||d.endsWith("index.ts")){let p=this.followReExport(u,n,N.dirname(d));p&&(u=p);}let C=u.getImportDeclarations().some(p=>{let f=p.getModuleSpecifierValue();return f.includes("@apollo/client")||f.includes("apollo")||f.includes("gql")||f.includes("graphql")||f.includes("__generated__")}),S=u.getImportDeclarations().filter(p=>{let f=p.getModuleSpecifierValue();return f.startsWith("./")||f.startsWith("../")});for(let p of S){let f=p.getModuleSpecifierValue(),x=p.getNamedImports().map(m=>m.getName()).filter(m=>/^use[A-Z]/.test(m));for(let m of x){let h=this.analyzeCustomHook(N.dirname(u.getFilePath()),f,m,o,i+1);s.push(...h);}}for(let p of S){let f=p.getModuleSpecifierValue(),x=p.getNamedImports().map(v=>v.getName()),m=p.getDefaultImport()?.getText(),h=x.filter(v=>/^[A-Z]/.test(v)&&this.isComponentName(v));for(let v of h){let y=this.analyzeImportedComponent(N.dirname(u.getFilePath()),f,v,o,i+1);s.push(...y);}if(m&&/^[A-Z]/.test(m)&&this.isComponentName(m)){let v=this.analyzeImportedComponent(N.dirname(u.getFilePath()),f,m,o,i+1);s.push(...v);}}if(C){let p=u.getDescendantsOfKind(SyntaxKind.CallExpression).filter(f=>{let x=f.getExpression().getText();return ["useQuery","useMutation","useLazyQuery","useSubscription"].includes(x)});for(let f of p){let x=f.getExpression().getText(),m=f.getArguments();if(m.length===0)continue;let v=m[0].getText(),y=v,A=null,T=this.resolveDocumentName(v);if(T)y=T.operationName,A=T.operationType;else if(v.endsWith("Document"))y=v.replace(/Document$/,"");else if(/^[A-Za-z]/.test(v)){let b=this.findOperationNameFromVariable(u,v);if(b&&(y=b),y===v&&v!=="Query"&&v!=="Mutation"){let R=v.match(/^(.+?)(Query|Mutation|Subscription)$/);R&&(y=R[1]);}}y!=="Query"&&y!=="Mutation"&&(y=y.replace(/Document$/,"").replace(/Query$|Mutation$/,"")||y),(y==="Query"||y==="Mutation"||y==="")&&y===""&&(y=v||"Unknown");let w=A?A==="mutation"?"useMutation":A==="subscription"?"useSubscription":x.includes("Lazy")?"useLazyQuery":"useQuery":x.includes("Mutation")?"useMutation":x.includes("Lazy")?"useLazyQuery":"useQuery";s.push({type:w,operationName:y,variables:[]});}}this.symbolTraceCache.set(c,s);}catch{}return s}analyzeCustomHook(e,t,n,o=new Set,i=0){if(i>10)return [];let s=[];try{let r=N.resolve(e,t),c=`hook:${r}:${n}`;if(o.has(c))return [];o.add(c);let l=this.symbolTraceCache.get(c);if(l!==void 0)return l;let g=[`${r}.tsx`,`${r}.ts`,`${r}/${n}.tsx`,`${r}/${n}.ts`,`${r}/index.tsx`,`${r}/index.ts`],u;for(let p of g)try{if(u=this.project.addSourceFileAtPath(p),u)break}catch{}if(!u)return s;let d=u.getImportDeclarations().some(p=>{let f=p.getModuleSpecifierValue();return f.includes("@apollo/client")||f.includes("apollo")||f.includes("graphql")||f.includes("__generated__")}),C=u.getImportDeclarations().filter(p=>{let f=p.getModuleSpecifierValue();return f.startsWith("./")||f.startsWith("../")});for(let p of C){let f=p.getModuleSpecifierValue(),x=p.getNamedImports().map(m=>m.getName()).filter(m=>/^use[A-Z]/.test(m));for(let m of x){let h=this.analyzeCustomHook(N.dirname(u.getFilePath()),f,m,o,i+1);s.push(...h);}}if(!d&&s.length===0)return s;let S=u.getDescendantsOfKind(SyntaxKind.CallExpression).filter(p=>{let f=p.getExpression().getText();return ["useQuery","useMutation","useLazyQuery","useSubscription"].includes(f)});for(let p of S){let f=p.getExpression().getText(),x=p.getArguments();if(x.length===0)continue;let m=x[0].getText(),h=m,v=null,y=this.resolveDocumentName(m);if(y)h=y.operationName,v=y.operationType;else if(m.endsWith("Document"))h=m.replace(/Document$/,"");else if(/^[A-Za-z]/.test(m)){let T=this.findOperationNameFromVariable(u,m);if(T&&(h=T),h===m&&m!=="Query"&&m!=="Mutation"){let w=m.match(/^(.+?)(Query|Mutation|Subscription)$/);w&&(h=w[1]);}}h!=="Query"&&h!=="Mutation"&&(h=h.replace(/Document$/,"").replace(/Query$|Mutation$/,"")||h),h===""&&(h=m||"Unknown");let A=v?v==="mutation"?"useMutation":v==="subscription"?"useSubscription":f.includes("Lazy")?"useLazyQuery":"useQuery":f.includes("Mutation")?"useMutation":f.includes("Lazy")?"useLazyQuery":"useQuery";s.push({type:A,operationName:`\u2192 ${h} (via ${n})`,variables:[]});}this.symbolTraceCache.set(c,s);}catch{}return s}globalContextQueries=[];async findPageFiles(){let e=this.getSetting("pagesDir","src/pages"),t=[],o=[...new Set([e,"pages","src/pages","app","src/app"])];for(let s of o){if(s==="app"||s==="src/app"){let c=["controllers","models","views","helpers"],l=this.resolvePath(s);if(c.some(u=>{try{return W.existsSync(N.join(l,u))}catch{return false}}))continue}let r=this.resolvePath(s);try{let c=await z(["**/*.tsx","**/*.ts","**/*.jsx","**/*.js"],{cwd:r,ignore:["_app.tsx","_app.ts","_app.jsx","_app.js","_document.tsx","_document.ts","_document.jsx","_document.js","_error.tsx","_error.ts","_error.jsx","_error.js","api/**","**/*.test.*","**/*.spec.*","**/node_modules/**","**/components/pages/**"],absolute:!0});t.push(...c),c.length>0&&this.log(`Found ${c.length} pages in ${s}`);}catch{}}let i=["frontend/src/**/pages","app/javascript/**/pages"];for(let s of i)try{let r=await z([`${s}/**/*.tsx`,`${s}/**/*.ts`,`${s}/**/*.jsx`,`${s}/**/*.js`],{cwd:this.basePath,ignore:["**/*.test.*","**/*.spec.*","**/node_modules/**","**/vendor/**","**/components/pages/**","**/stories/**"],absolute:!0});t.push(...r),r.length>0&&this.log(`Found ${r.length} React pages in ${s}`);}catch{}let a=["frontend/src/**/index.tsx","frontend/src/**/App.tsx","app/javascript/packs/*.tsx","app/javascript/packs/*.jsx"];for(let s of a)try{let r=await z([s],{cwd:this.basePath,ignore:["**/node_modules/**","**/vendor/**"],absolute:!0});for(let c of r)t.includes(c)||t.push(c);}catch{}return [...new Set(t)]}async analyzeAppFile(){let e=this.getSetting("pagesDir","src/pages"),t=[this.resolvePath(`${e}/_app.tsx`),this.resolvePath(`${e}/_app.ts`)];for(let n of t)try{let o=this.project.addSourceFileAtPath(n);if(!o)continue;let i=o.getDescendantsOfKind(SyntaxKind.JsxElement),a=o.getDescendantsOfKind(SyntaxKind.JsxSelfClosingElement),s=new Set;for(let r of [...i,...a]){let c=r.getFirstDescendantByKind(SyntaxKind.Identifier)?.getText();c&&(c.includes("Provider")||c.includes("Context"))&&s.add(c);}for(let r of o.getImportDeclarations()){let c=r.getModuleSpecifierValue();if(!c.startsWith("./")&&!c.startsWith("../"))continue;let l=r.getNamedImports().map(u=>u.getName()),g=r.getDefaultImport()?.getText();for(let u of s)if(l.includes(u)||g===u){let d=this.analyzeImportedComponent(N.dirname(n),c,u,new Set,0);for(let C of d)this.globalContextQueries.push({...C,operationName:`[Global] ${C.operationName}`});}}this.globalContextQueries.length>0&&this.log(`Found ${this.globalContextQueries.length} global context queries from _app`);return}catch{}}async loadCodegenMapping(){let e=["__generated__","src/__generated__","src/__generated__/gql-graphql-gateway","generated","src/generated"];for(let t of e){let n=this.resolvePath(t);try{let o=await z(["**/*.ts","**/*.tsx"],{cwd:n,absolute:!0,onlyFiles:!0});for(let i of o)try{let a=this.project.addSourceFileAtPath(i),s=a.getVariableDeclarations();for(let c of s){let l=c.getName();if(l.endsWith("Document")){let g=c.getInitializer()?.getText()??"",u=g.match(/(?:query|mutation|subscription)\s+(\w+)/),d=g.match(/(query|mutation|subscription)\s+/);u&&this.codegenMap.set(l,{operationName:u[1],operationType:d?d[1]:"query"});}}let r=a.getTypeAliases();for(let c of r){let l=c.getName();if((l.endsWith("Query")||l.endsWith("Mutation")||l.endsWith("Subscription"))&&!l.endsWith("Variables")){let g=l+"Document";if(!this.codegenMap.has(g)){let u=l.endsWith("Mutation")?"mutation":l.endsWith("Subscription")?"subscription":"query";this.codegenMap.set(g,{operationName:l,operationType:u});}}}}catch{}if(this.codegenMap.size>0){this.log(`Loaded ${this.codegenMap.size} codegen mappings from ${t}`);return}}catch{}}}resolveDocumentName(e){if(new Set(["Query","Mutation","Subscription"]).has(e))return null;let n=this.codegenMap.get(e);if(n!==void 0)return n;let o=e.endsWith("Document")?e:e+"Document",i=this.codegenMap.get(o);return i!==void 0?i:null}followReExport(e,t,n){try{let o=e.getExportDeclarations(),i=null;for(let a of o){let s=a.getNamedExports();for(let r of s){let c=a.getModuleSpecifierValue();if(!c)continue;let l=N.resolve(n,c),g=[`${l}.tsx`,`${l}.ts`,`${l}/index.tsx`,`${l}/index.ts`],u;for(let d of g)try{if(u=this.project.addSourceFileAtPath(d),u)break}catch{}if(u&&(i||(i=u),r.getName()===t||r.getAliasNode()?.getText()===t))return u}}if(i)return i;for(let a of e.getExportDeclarations())if(a.isNamespaceExport()){let s=a.getModuleSpecifierValue();if(s){let r=N.resolve(n,s),c=[`${r}.tsx`,`${r}.ts`];for(let l of c)try{let g=this.project.addSourceFileAtPath(l);if(g&&g.getExportedDeclarations().has(t))return g}catch{}}}}catch{}return null}isComponentName(e){return !/^[A-Z]/.test(e)||e.endsWith("Query")||e.endsWith("Mutation")||e.endsWith("Subscription")||e.endsWith("Fragment")||e.endsWith("Document")||e.endsWith("Variables")||e==="Query"||e==="Mutation"||e==="Subscription"||new Set(["NextPage","NextPageContext","NextApiRequest","NextApiResponse","GetServerSideProps","GetStaticProps","GetStaticPaths","InferGetServerSidePropsType","InferGetStaticPropsType","FC","FunctionComponent","VFC","Component","PureComponent","ReactNode","ReactElement","PropsWithChildren","ComponentProps","ComponentType","ElementType","RefObject","MutableRefObject","Dispatch","SetStateAction","ChangeEvent","MouseEvent","KeyboardEvent","FormEvent","SyntheticEvent"]).has(e)?false:!!(["Container","Page","Screen","View","Form","Modal","Dialog","Panel","Root","Provider","Wrapper"].some(o=>e.endsWith(o))||/Page[A-Z]?\w*$/.test(e)||/Container[A-Z]?\w*$/.test(e)||/^[A-Z][a-z]+[A-Z][a-z]+/.test(e))}extractNavigation(e){let t={visible:true,currentNavItem:null};try{let n=e.getDescendantsOfKind(SyntaxKind.PropertyAccessExpression).find(o=>{try{return o.getName()==="globalNavigationStyle"}catch{return !1}});if(n){let o=n.getParent();if(Node.isBinaryExpression(o)){let i=o.getRight(),a=i.getDescendantsOfKind(SyntaxKind.PropertyAssignment).find(c=>{try{return c.getName()==="visible"}catch{return !1}});a&&(t.visible=a.getInitializer()?.getText()==="true");let s=i.getDescendantsOfKind(SyntaxKind.PropertyAssignment).find(c=>{try{return c.getName()==="currentNavItem"}catch{return !1}});if(s){let c=s.getInitializer()?.getText();t.currentNavItem=c&&c!=="null"?c.replace(/['"]/g,""):null;}let r=i.getDescendantsOfKind(SyntaxKind.PropertyAssignment).find(c=>{try{return c.getName()==="mini"}catch{return !1}});r&&(t.mini=r.getInitializer()?.getText()==="true");}}}catch{}return t}extractSteps(e){let t=[],n=e.getDescendantsOfKind(SyntaxKind.CallExpression).filter(a=>a.getExpression().getText()==="useState");for(let a of n){let s=a.getParent();if(!s)continue;let c=s.getText().match(/\[\s*(step|currentStep|activeStep|page|currentPage|phase|stage)\s*,/i);if(c){let l=c[1],g=e.getDescendantsOfKind(SyntaxKind.SwitchStatement);for(let d of g)d.getExpression().getText().includes(l)&&d.getClauses().forEach((p,f)=>{if(p.isKind(SyntaxKind.CaseClause)){let x=p.getExpression()?.getText()||String(f),m=p.getDescendantsOfKind(SyntaxKind.JsxOpeningElement),h=m.length>0?m[0].getTagNameNode().getText():void 0;t.push({id:x.replace(/['"]/g,""),name:`Step ${x.replace(/['"]/g,"")}`,component:h});}});let u=e.getDescendantsOfKind(SyntaxKind.ArrayLiteralExpression);for(let d of u){let C=d.getParent();C&&C.getText().match(/steps|pages|screens|views|components/i)&&d.getElements().forEach((p,f)=>{let x=p.getText();if(x.startsWith("{")){let m=x.match(/(?:name|label|title)\s*:\s*['"]([^'"]+)['"]/),h=x.match(/(?:component|content)\s*:\s*<?\s*(\w+)/);t.push({id:f+1,name:m?m[1]:`Step ${f+1}`,component:h?h[1]:void 0});}else /^[A-Z]/.test(x)&&t.push({id:f+1,name:x,component:x});});}}}let o=e.getDescendantsOfKind(SyntaxKind.JsxOpeningElement);for(let a of o)if(a.getTagNameNode().getText().match(/Stepper|Wizard|Steps|TabPanel|FormStep/i)){let r=a.getParent();r&&r.isKind(SyntaxKind.JsxElement)&&r.getJsxChildren().forEach((l,g)=>{if(l.isKind(SyntaxKind.JsxElement)||l.isKind(SyntaxKind.JsxSelfClosingElement)){let u=l.isKind(SyntaxKind.JsxElement)?l.getOpeningElement().getTagNameNode().getText():l.getTagNameNode().getText(),d=l.isKind(SyntaxKind.JsxElement)?l.getOpeningElement().getAttributes():l.getAttributes(),C=u;for(let S of d)if(S.isKind(SyntaxKind.JsxAttribute)){let p=S.getNameNode().getText();if(p==="label"||p==="title"||p==="name"){let f=S.getInitializer()?.getText();if(f){C=f.replace(/['"{}]/g,"");break}}}t.push({id:g+1,name:C,component:u});}});}let i=e.getDescendantsOfKind(SyntaxKind.ConditionalExpression);for(let a of i){let s=a.getCondition().getText();if(s.match(/step\s*===?\s*\d+|currentStep|activeStep/i)){let r=a.getWhenTrue(),c=s.match(/===?\s*(\d+)/);if(c&&t.length===0){let l=r.getDescendantsOfKind(SyntaxKind.JsxOpeningElement);l.length>0&&t.push({id:parseInt(c[1]),component:l[0].getTagNameNode().getText()});}}}return t}extractLinkedPages(e){let t=[],n=e.getDescendantsOfKind(SyntaxKind.CallExpression).filter(i=>{let a=i.getExpression().getText();return a.includes("router.push")||a.includes("router.replace")||a.includes("Link")});for(let i of n){let a=i.getArguments();if(a.length>0){let r=a[0].getText().match(/['"`]([^'"`]+)['"`]/);r&&!t.includes(r[1])&&t.push(r[1]);}}let o=e.getDescendantsOfKind(SyntaxKind.JsxOpeningElement).filter(i=>i.getTagNameNode().getText()==="Link");for(let i of o)try{let a=i.getAttributes();for(let s of a)if(s.isKind(SyntaxKind.JsxAttribute)&&s.getNameNode().getText()==="href"){let l=s.getInitializer()?.getText();if(l){let g=l.match(/['"`]([^'"`]+)['"`]/);g&&!t.includes(g[1])&&t.push(g[1]);}}}catch{}return t}};var K=class extends F{project;constructor(e){super(e),this.project=new Project({tsConfigFilePath:this.resolvePath("tsconfig.json"),skipAddingFilesFromTsConfig:true});}getName(){return "GraphQLAnalyzer"}async analyze(){this.log("Starting GraphQL analysis...");let e=[],t=await this.analyzeGraphQLFiles();e.push(...t);let n=await this.analyzeInlineGraphQL();return e.push(...n),await this.findOperationUsage(e),this.log(`Found ${e.length} GraphQL operations`),{graphqlOperations:e}}async analyzeGraphQLFiles(){let e=await z(["**/*.graphql"],{cwd:this.basePath,ignore:["**/node_modules/**","**/.next/**"],absolute:true});return (await E(e,async n=>{let o=await L.readFile(n,"utf-8"),i=parse(o);return this.extractOperationsFromDocument(i,N.relative(this.basePath,n))})).flat()}async analyzeInlineGraphQL(){let e=[],t=await z(["**/*.ts","**/*.tsx"],{cwd:this.basePath,ignore:["**/node_modules/**","**/.next/**","**/__tests__/**","**/*.test.*","**/*.spec.*"],absolute:true});for(let n of t)try{let o=this.project.addSourceFileAtPath(n),i=N.relative(this.basePath,n),a=o.getImportDeclarations().some(c=>{let l=c.getModuleSpecifierValue(),g=c.getNamedImports().map(d=>d.getName()),u=c.getDefaultImport()?.getText();return (g.includes("gql")||g.includes("graphql")||u==="gql")&&(l.includes("graphql")||l.includes("apollo")||l.includes("gql")||l.includes("__generated__"))}),s=o.getDescendantsOfKind(SyntaxKind.TaggedTemplateExpression);for(let c of s){let l=c.getTag().getText();if(l==="gql"||l==="graphql")try{let g=c.getTemplate(),u="";if(g.isKind(SyntaxKind.NoSubstitutionTemplateLiteral)?u=g.getLiteralValue():g.isKind(SyntaxKind.TemplateExpression)&&(u=g.getText().slice(1,-1).replace(/\$\{[^}]*\}/g,"")),u&&u.trim())try{let d=parse(u),C=this.extractOperationsFromDocument(d,i);e.push(...C);}catch{}}catch{}}let r=o.getDescendantsOfKind(SyntaxKind.CallExpression);for(let c of r)try{let g=c.getExpression().getText();if(g==="gql"||g==="graphql"){let u=c.getArguments();if(u.length>0){let d=u[0],C="";if(d.isKind(SyntaxKind.NoSubstitutionTemplateLiteral))C=d.getLiteralValue();else if(d.isKind(SyntaxKind.TemplateExpression))C=d.getText().slice(1,-1).replace(/\$\{[^}]*\}/g,"");else {let S=d.getText();if(S.includes("`")){let p=S.match(/\/\*\s*GraphQL\s*\*\/\s*`([^`]*)`/);if(p)C=p[1];else {let f=S.match(/`([^`]*)`/);f&&(C=f[1]);}}}if(C&&C.trim())try{let S=parse(C),p=this.extractOperationsFromDocument(S,i);e.push(...p);}catch{}}}}catch{}if(a){let c=o.getVariableDeclarations();for(let l of c){let g=l.getName();if(g.includes("QUERY")||g.includes("MUTATION")||g.includes("FRAGMENT")||g.includes("Query")||g.includes("Mutation")||g.includes("Subscription")||/^[A-Z_]+_(QUERY|MUTATION|FRAGMENT|SUBSCRIPTION)$/.test(g)||/Query$|Mutation$|Fragment$|Subscription$/.test(g)){let d=l.getInitializer();d&&d.isKind(SyntaxKind.CallExpression);}}}}catch(o){this.warn(`Failed to analyze ${n}: ${o.message}`);}return e}extractOperationsFromDocument(e,t){let n=[];for(let o of e.definitions){let i=this.extractOperation(o,t);i&&n.push(i);}return n}extractOperation(e,t){if(e.kind==="OperationDefinition"){let n=e.name?.value||"anonymous",o=e.operation,i=this.extractVariables(e),a=this.extractFragmentReferences(e),s=this.extractFields(e);return {name:n,type:o,filePath:t,usedIn:[],variables:i,returnType:this.inferReturnType(e),fragments:a,fields:s}}return e.kind==="FragmentDefinition"?{name:e.name.value,type:"fragment",filePath:t,usedIn:[],variables:[],returnType:e.typeCondition.name.value,fragments:this.extractFragmentReferences(e),fields:this.extractFields(e)}:null}extractFields(e){let t=[],n=(o,i=0)=>{if(!o||!o.selections||i>5)return [];let a=[];for(let s of o.selections)if(s.kind==="Field"){let r={name:s.name.value};if(s.arguments&&s.arguments.length>0){let c=s.arguments.map(l=>l.name.value).join(", ");r.type=`(${c})`;}s.selectionSet&&(r.fields=n(s.selectionSet,i+1)),a.push(r);}else if(s.kind==="FragmentSpread")a.push({name:`...${s.name.value}`,type:"fragment"});else if(s.kind==="InlineFragment"&&s.selectionSet){let r=s.typeCondition?.name?.value||"inline";a.push({name:`... on ${r}`,type:"inline-fragment",fields:n(s.selectionSet,i+1)});}return a};return e.selectionSet?n(e.selectionSet):t}extractVariables(e){let t=[];if(e.variableDefinitions)for(let n of e.variableDefinitions){let o=n.variable.name.value,i=this.typeNodeToString(n.type),a=n.type.kind==="NonNullType";t.push({name:o,type:i,required:a});}return t}typeNodeToString(e){return e.kind==="NonNullType"?`${this.typeNodeToString(e.type)}!`:e.kind==="ListType"?`[${this.typeNodeToString(e.type)}]`:e.kind==="NamedType"?e.name.value:"unknown"}extractFragmentReferences(e){let t=[],n=o=>{if(o&&(o.kind==="FragmentSpread"&&t.push(o.name.value),o.selectionSet))for(let i of o.selectionSet.selections)n(i);};return n(e),t}inferReturnType(e){if(e.selectionSet&&e.selectionSet.selections.length>0){let t=e.selectionSet.selections[0];if(t.kind==="Field")return t.name.value}return "unknown"}async findOperationUsage(e){let t=await z(["**/*.ts","**/*.tsx"],{cwd:this.basePath,ignore:["**/node_modules/**","**/.next/**"],absolute:true}),n=new Map;for(let o of e)n.set(o.name,o);for(let o of t)try{let i=await L.readFile(o,"utf-8"),a=N.relative(this.basePath,o);for(let[s,r]of n)(i.includes(`useQuery<${s}`)||i.includes(`useMutation<${s}`)||i.includes(`useLazyQuery<${s}`)||i.includes(`${s}Query`)||i.includes(`${s}Mutation`)||i.includes(`${s}Variables`))&&(r.usedIn.includes(a)||r.usedIn.push(a));}catch{}}};var G=class extends F{project;componentCache=new Map;constructor(e){super(e),this.project=new Project({tsConfigFilePath:this.resolvePath("tsconfig.json"),skipAddingFilesFromTsConfig:true});}getName(){return "DataFlowAnalyzer"}async analyze(){this.log("Starting data flow analysis...");let e=await this.analyzeComponents(),t=await this.analyzeDataFlows(e);return this.log(`Analyzed ${e.length} components and ${t.length} data flows`),{components:e,dataFlows:t}}async analyzeComponents(){let e=[],t=[this.getSetting("featuresDir",""),this.getSetting("componentsDir",""),this.getSetting("pagesDir","")].filter(Boolean),n=["src/features","src/components","src/common/components","src/common","src/pages","src/app","src/modules","src/views","src/screens","components","pages","app"],o=[...new Set([...t,...n])];for(let i of o){let a=await z(["**/*.tsx"],{cwd:this.resolvePath(i),ignore:["**/*.test.*","**/*.spec.*","**/*.stories.*"],absolute:true});for(let s of a)try{let r=this.project.addSourceFileAtPath(s),c=N.relative(this.basePath,s),l=this.analyzeComponentFile(r,c);e.push(...l);}catch(r){this.warn(`Failed to analyze ${s}: ${r.message}`);}}return this.buildDependencyGraph(e),e}analyzeComponentFile(e,t){let n=[],o=e.getFunctions();for(let s of o){let r=s.getName();if(r&&this.isComponentName(r)){let c=this.extractComponentInfo(s,r,t);n.push(c),this.componentCache.set(r,c);}}let i=e.getVariableDeclarations();for(let s of i){let r=s.getName();if(this.isComponentName(r)){let c=s.getInitializer();if(c&&(c.isKind(SyntaxKind.ArrowFunction)||c.isKind(SyntaxKind.FunctionExpression))){let l=this.extractComponentInfo(c,r,t);n.push(l),this.componentCache.set(r,l);}}}let a=e.getFunctions().filter(s=>{let r=s.getName();return r&&r.startsWith("use")});for(let s of a){let r=s.getName()??"",c=this.extractHookInfo(s,r,t);n.push(c),this.componentCache.set(r,c);}return n}isComponentName(e){return /^[A-Z][a-zA-Z0-9]*$/.test(e)}extractComponentInfo(e,t,n){let o=e.getSourceFile(),i="presentational";["/pages/","/app/","/routes/","/views/","/screens/"].some(g=>n.includes(g))?i="page":t.includes("Container")||t.includes("Provider")?i="container":(t.includes("Layout")||t.includes("Shell")||t.includes("Wrapper")||t.includes("Frame")||t.includes("Scaffold")||n.includes("/layouts/")||n.includes("/layout/"))&&(i="layout");let s=this.extractProps(e),r=this.extractHooksUsed(e),c=this.extractDependencies(o),l=this.extractStateManagement(e);return {name:t,filePath:n,type:i,props:s,dependencies:c,dependents:[],hooks:r,stateManagement:l}}extractHookInfo(e,t,n){let o=e.getSourceFile(),i=this.extractProps(e),a=this.extractHooksUsed(e),s=this.extractDependencies(o),r=this.extractStateManagement(e);return {name:t,filePath:n,type:"hook",props:i,dependencies:s,dependents:[],hooks:a,stateManagement:r}}extractProps(e){let t=[],n=e.getParameters?.()||[];if(n.length>0){let i=n[0].getTypeNode?.();if(i){let a=i.getDescendantsOfKind?.(SyntaxKind.PropertySignature)||[];for(let s of a)t.push({name:s.getName(),type:s.getType().getText(),required:!s.hasQuestionToken()});}}return t}extractHooksUsed(e){let t=[],n=e.getDescendantsOfKind?.(SyntaxKind.CallExpression)||[];for(let o of n)try{let i=o.getExpression().getText();if(i.startsWith("use"))if(i==="useQuery"||i==="useMutation"||i==="useLazyQuery"){let a=this.extractOperationName(o,i);t.includes(a)||t.push(a);}else if(i==="useContext"){let a=this.extractContextName(o);t.includes(a)||t.push(a);}else t.includes(i)||t.push(i);}catch{}return t}extractOperationName(e,t){try{let n=e.getArguments?.()||[];if(n.length>0){let i=n[0].getText().replace(/^(GET_|FETCH_|CREATE_|UPDATE_|DELETE_)/,"").replace(/_QUERY$|_MUTATION$/,"").replace(/Document$/,"").replace(/Query$|Mutation$/,"");return `${t==="useMutation"?"\u270F\uFE0F":"\u{1F4E1}"} ${t==="useMutation"?"Mutation":"Query"}: ${i}`}}catch{}return t}extractContextName(e){try{let t=e.getArguments?.()||[];if(t.length>0)return `\u{1F504} Context: ${t[0].getText().replace(/Context$/,"").replace(/^Session|^Token|^Apollo/,o=>o)}`}catch{}return "useContext"}extractDependencies(e){let t=[],n=e.getImportDeclarations();for(let o of n){let i=o.getModuleSpecifierValue();if(i.startsWith(".")||i.startsWith("@/")){let a=o.getNamedImports();for(let r of a){let c=r.getName();(this.isComponentName(c)||c.startsWith("use"))&&t.push(c);}let s=o.getDefaultImport();if(s){let r=s.getText();this.isComponentName(r)&&t.push(r);}}}return t}extractStateManagement(e){let t=[],n=e.getText?.()||"";return n.includes("useState")&&t.push("useState"),n.includes("useReducer")&&t.push("useReducer"),n.includes("useContext")&&t.push("useContext"),n.includes("useQuery")&&t.push("Apollo Query"),n.includes("useMutation")&&t.push("Apollo Mutation"),n.includes("useRecoil")&&t.push("Recoil"),(n.includes("useSelector")||n.includes("useDispatch"))&&t.push("Redux"),t}buildDependencyGraph(e){let t=new Map;for(let n of e)t.set(n.name,n);for(let n of e)for(let o of n.dependencies){let i=t.get(o);i&&!i.dependents.includes(n.name)&&i.dependents.push(n.name);}}async analyzeDataFlows(e){let t=[],n=1,o=this.analyzeContextFlows(e);t.push(...o.map(s=>({...s,id:`flow-${n++}`})));let i=this.analyzeApolloFlows(e);t.push(...i.map(s=>({...s,id:`flow-${n++}`})));let a=this.analyzePropDrilling(e);return t.push(...a.map(s=>({...s,id:`flow-${n++}`}))),t}analyzeContextFlows(e){let t=[],n=e.filter(i=>i.name.includes("Provider")||i.name.includes("Context")),o=e.filter(i=>i.hooks.some(a=>a.includes("Context")));for(let i of n){let a=i.name.replace("Provider","").replace("Context","");for(let s of o){let r=s.hooks.find(c=>c.includes("Context")&&c.includes(a));(r||s.hooks.some(c=>c.includes(a)))&&t.push({name:`\u{1F504} ${a} Context`,description:`Data flows from ${i.name} to ${s.name} via Context`,source:{type:"context",name:i.name},target:{type:"component",name:s.name},via:[],operations:[r||`useContext(${a})`]});}}return t}analyzeApolloFlows(e){let t=[];for(let n of e){let o=n.hooks.filter(a=>a.includes("Query:")||a==="useQuery"||a==="useLazyQuery");for(let a of o){let s=a.includes(":")?a.split(":")[1].trim():n.name;t.push({name:`\u{1F4E1} ${s}`,description:`${n.name} fetches ${s} via Apollo`,source:{type:"api",name:`GraphQL: ${s}`},target:{type:"component",name:n.name},via:[{type:"cache",name:"Apollo Cache"}],operations:[a]});}let i=n.hooks.filter(a=>a.includes("Mutation:")||a==="useMutation");for(let a of i){let s=a.includes(":")?a.split(":")[1].trim():n.name;t.push({name:`\u270F\uFE0F ${s}`,description:`${n.name} mutates ${s} via Apollo`,source:{type:"component",name:n.name},target:{type:"api",name:`GraphQL: ${s}`},via:[],operations:[a]});}}return t}analyzePropDrilling(e){let t=[];for(let n of e)n.props.length>5&&n.dependents.length>0&&t.push({name:`Prop Drilling through ${n.name}`,description:`${n.name} passes ${n.props.length} props to children`,source:{type:"component",name:n.name},target:{type:"component",name:n.dependents[0]},via:[],operations:["props"]});return t}};var H=class extends F{project;apiCallCounter=0;constructor(e){super(e),this.project=new Project({tsConfigFilePath:this.resolvePath("tsconfig.json"),skipAddingFilesFromTsConfig:true});}getName(){return "RestApiAnalyzer"}async analyze(){this.log("Starting REST API analysis...");let e=await z(["**/*.ts","**/*.tsx"],{cwd:this.basePath,ignore:["**/node_modules/**","**/.next/**","**/__tests__/**","**/*.test.*","**/*.spec.*","**/*.stories.*","**/__generated__/**","**/dist/**","**/build/**"],absolute:true}),t=new Map;for(let i of e)try{t.set(i,this.project.addSourceFileAtPath(i));}catch{}let o=(await E(Array.from(t.entries()),async([i,a])=>{let s=N.relative(this.basePath,i),r=[];return r.push(...this.findFetchCalls(a,s)),r.push(...this.findAxiosCalls(a,s)),r.push(...this.findSwrCalls(a,s)),r},8)).flat();return this.log(`Found ${o.length} REST API calls`),{apiCalls:o}}findFetchCalls(e,t){let n=[],o=e.getDescendantsOfKind(SyntaxKind.CallExpression);for(let i of o)try{let s=i.getExpression().getText();if(s==="fetch"||s==="window.fetch"){let r=this.extractFetchCall(i,t);r&&n.push(r);}}catch{}return n}findAxiosCalls(e,t){let n=[],o=e.getDescendantsOfKind(SyntaxKind.CallExpression);for(let i of o)try{let s=i.getExpression().getText(),r=s.match(/^axios\.(get|post|put|delete|patch)$/i);if(r){let c=this.extractAxiosCall(i,t,r[1].toUpperCase());c&&n.push(c);}if(s==="axios"){let c=this.extractAxiosDirectCall(i,t);c&&n.push(c);}}catch{}return n}findSwrCalls(e,t){let n=[],o=e.getDescendantsOfKind(SyntaxKind.CallExpression);for(let i of o)try{let s=i.getExpression().getText();if(s==="useSWR"||s==="useSWRImmutable"){let r=this.extractSwrCall(i,t);r&&n.push(r);}}catch{}return n}extractFetchCall(e,t){let n=e.getArguments();if(n.length===0)return null;let o=n[0].getText(),i=this.extractUrlFromArg(o);if(!i.url||!i.isPlaceholder&&!this.isApiUrl(i.url))return null;let a="GET",s=false;if(n.length>1){let l=n[1].getText(),g=l.match(/method:\s*["'](\w+)["']/i);g&&(a=this.normalizeMethod(g[1])),s=l.includes("credentials")||l.includes("Authorization")||l.includes("withCredentials");}let r=this.getContainingFunctionName(e),c=e.getStartLineNumber();return {id:`api-${++this.apiCallCounter}`,method:a,url:i.url,callType:"fetch",filePath:t,line:c,containingFunction:r,usedIn:[],requiresAuth:s,category:this.categorizeApi(i.url)}}extractAxiosCall(e,t,n){let o=e.getArguments();if(o.length===0)return null;let i=o[0].getText(),a=this.extractUrlFromArg(i);if(!a.url)return null;let s=false;if(o.length>1){let l=o[o.length-1].getText();s=l.includes("withCredentials")||l.includes("Authorization");}let r=this.getContainingFunctionName(e),c=e.getStartLineNumber();return {id:`api-${++this.apiCallCounter}`,method:this.normalizeMethod(n),url:a.url,callType:"axios",filePath:t,line:c,containingFunction:r,usedIn:[],requiresAuth:s,category:this.categorizeApi(a.url)}}extractAxiosDirectCall(e,t){let n=e.getArguments();if(n.length===0)return null;let o=n[0].getText(),i=o.match(/url:\s*["'`]([^"'`]+)["'`]/),a=o.match(/method:\s*["'](\w+)["']/i);if(!i)return null;let s=i[1],r=a?this.normalizeMethod(a[1]):"GET",c=o.includes("withCredentials")||o.includes("Authorization"),l=this.getContainingFunctionName(e),g=e.getStartLineNumber();return {id:`api-${++this.apiCallCounter}`,method:r,url:s,callType:"axios",filePath:t,line:g,containingFunction:l,usedIn:[],requiresAuth:c,category:this.categorizeApi(s)}}extractSwrCall(e,t){let n=e.getArguments();if(n.length===0)return null;let o=n[0].getText(),i=null;if(o.startsWith('"')||o.startsWith("'")||o.startsWith("`"))i=this.cleanStringLiteral(o);else if(o.includes("?")&&o.includes(":")){let r=o.match(/\?\s*["'`]([^"'`]+)["'`]/);r?i=r[1]:(r=o.match(/:\s*["'`]([^"'`]+)["'`]/),r&&(i=r[1])),i||(r=o.match(/\?\s*`([^`]+)`/),r&&(i=r[1].replace(/\$\{[^}]+\}/g,":param")));}else {let r=this.extractUrlFromArg(o);r.url&&!o.includes("null")&&!o.includes("undefined")&&(i=r.url);}if(!i)return null;let a=this.getContainingFunctionName(e),s=e.getStartLineNumber();return {id:`api-${++this.apiCallCounter}`,method:"GET",url:i,callType:"useSWR",filePath:t,line:s,containingFunction:a,usedIn:[],requiresAuth:false,category:this.categorizeApi(i)}}getContainingFunctionName(e){let t=e;for(;t;){if(Node.isFunctionDeclaration(t))return t.getName()||"anonymous";if(Node.isVariableDeclaration(t)||Node.isMethodDeclaration(t))return t.getName();if(Node.isArrowFunction(t)){let n=t.getParent();if(n&&Node.isVariableDeclaration(n))return n.getName()}t=t.getParent();}return "unknown"}extractUrlFromArg(e){if(/^["'`]/.test(e))return {url:this.cleanStringLiteral(e),isPlaceholder:false};let t=e.match(/^(\w+)\s*\(\s*["'`]([^"'`]+)["'`]/);if(t)return {url:`[${t[1]}] ${t[2]}`,isPlaceholder:true};let n=e.match(/^(\w+)\s*\(\s*`([^`]+)`/);if(n){let o=n[2].replace(/\$\{[^}]+\}/g,":param");return {url:`[${n[1]}] ${o}`,isPlaceholder:true}}return /^\w+(\.\w+)*$/.test(e)?{url:`[${e}]`,isPlaceholder:true}:e.includes(".")?{url:`[${e}]`,isPlaceholder:true}:{url:null,isPlaceholder:false}}cleanStringLiteral(e){let t=e.replace(/^["'`]|["'`]$/g,"").trim();return t.includes("${")?t.replace(/\$\{[^}]+\}/g,":param"):t||null}isApiUrl(e){return e.startsWith("data:")||e.startsWith("blob:")||/\.(css|js|png|jpg|jpeg|gif|svg|ico|woff|woff2|ttf|eot|html)$/i.test(e)?false:e.startsWith("/")||e.startsWith("http")||e.includes("/api/")||e.includes(".json")||e.includes("api.")||e.includes("github.io")||e.includes("hsforms.com")||e.includes("hubspot")||e.includes("amazonaws.com")||e.includes("s3.")||e.includes("googleapis.com")||e.includes("stripe.com")||e.includes("graph.facebook.com")||e.includes("api.twitter.com")||e.includes("slack.com")||e.includes("discord.com")||e.includes("sendgrid.com")||e.includes("twilio.com")||e.includes("firebase")||e.includes("supabase")||e.includes("auth0.com")||e.includes("okta.com")||e.includes("cloudflare.com")||e.includes("vercel.com")||e.includes("netlify.com")}categorizeApi(e){if(e.includes("hsforms.com")||e.includes("hubspot"))return "HubSpot";if(e.includes("amazonaws.com")||e.includes("s3."))return "AWS S3";if(e.includes("googleapis.com"))return "Google API";if(e.includes("stripe.com"))return "Stripe";if(e.includes("graph.facebook.com"))return "Facebook";if(e.includes("api.twitter.com"))return "Twitter";if(e.includes("slack.com"))return "Slack";if(e.includes("discord.com"))return "Discord";if(e.includes("sendgrid.com"))return "SendGrid";if(e.includes("twilio.com"))return "Twilio";if(e.includes("firebase"))return "Firebase";if(e.includes("supabase"))return "Supabase";if(e.includes("auth0.com"))return "Auth0";if(e.includes("okta.com"))return "Okta";if(e.includes("github.io"))return "GitHub Pages API";if(e.startsWith("/api/"))return "Internal API";if(e.startsWith("/"))return "Internal Route";if(e.startsWith("["))return "Dynamic URL"}normalizeMethod(e){let t=e.toUpperCase();return ["GET","POST","PUT","DELETE","PATCH"].includes(t)?t:"unknown"}};export{F as a,q as b,K as c,G as d,H as e};
|
|
1
|
+
import {Project,Node,SyntaxKind}from'ts-morph';import z from'fast-glob';import*as F from'path';import*as W from'fs';import*as O from'fs/promises';import {parse}from'graphql';var w=class{config;basePath;constructor(e){this.config=e,this.basePath=e.path;}resolvePath(e){return `${this.basePath}/${e}`}getSetting(e,t=""){return this.config.settings[e]??t}log(e){console.log(`[${this.getName()}] ${e}`);}warn(e){console.warn(`[${this.getName()}] \u26A0\uFE0F ${e}`);}error(e,t){console.error(`[${this.getName()}] \u274C ${e}`,t?.message||"");}};async function E(N,e,t=8){let n=new Array(N.length).fill(null),o=0;async function i(){for(;o<N.length;){let s=o++;if(s<N.length)try{n[s]=await e(N[s],s);}catch{n[s]=null;}}}let a=Array(Math.min(t,N.length)).fill(null).map(()=>i());return await Promise.all(a),n.filter(s=>s!==null)}var j=class extends w{project;codegenMap=new Map;constructor(e){super(e),this.project=new Project({tsConfigFilePath:this.resolvePath("tsconfig.json"),skipAddingFilesFromTsConfig:true});}extractOperationNameFromGql(e){let t=e.match(/(?:query|mutation|subscription)\s+(\w+)/);if(t&&t[1])return t[1];let n=e.match(/`\s*(?:query|mutation|subscription)\s+(\w+)/);if(n&&n[1])return n[1];let o=e.match(/GraphQL[^`]*`\s*(?:\n\s*)?(?:query|mutation|subscription)\s+(\w+)/);return o&&o[1]?o[1]:null}findOperationNameFromVariable(e,t){let n=e.getVariableDeclaration(t);if(!n){let s=e.getExportedDeclarations().get(t);if(s&&s.length>0){let r=s[0];Node.isVariableDeclaration(r)&&(n=r);}}if(n||(n=e.getVariableDeclarations().find(s=>s.getName()===t)),!n){let a=e.getFullText(),s=[new RegExp(`(?:const|let|var)\\s+${t}\\s*=\\s*gql\\s*\\(\\s*/\\*[^*]*\\*/\\s*\`\\s*(?:query|mutation|subscription)\\s+(\\w+)`,"s"),new RegExp(`(?:const|let|var)\\s+${t}\\s*=\\s*gql\`\\s*(?:query|mutation|subscription)\\s+(\\w+)`,"s"),new RegExp(`(?:const|let|var)\\s+${t}\\s*=\\s*gql\\s*\\(\`\\s*(?:query|mutation|subscription)\\s+(\\w+)`,"s"),new RegExp(`(?:const|let|var)\\s+${t}\\s*=\\s*graphql\\s*\\(\`\\s*(?:query|mutation|subscription)\\s+(\\w+)`,"s")];for(let r of s){let c=a.match(r);if(c&&c[1])return c[1]}return null}let o=n.getInitializer();if(!o)return null;let i=o.getText();return this.extractOperationNameFromGql(i)}getName(){return "PagesAnalyzer"}async analyze(){this.log("Starting page analysis..."),await this.loadCodegenMapping(),await this.analyzeAppFile();let e=await this.findPageFiles();this.log(`Found ${e.length} page files`);for(let o of e)try{this.project.addSourceFileAtPath(o);}catch{}let n=(await E(e,async o=>{let i=this.detectPagesRoot(o);return this.analyzePageFile(o,i)},4)).filter(o=>o!==null);return this.log(`Analyzed ${n.length} pages successfully`),{pages:n}}async analyzePageFile(e,t){let n=this.project.getSourceFile(e);if(!n)return null;let o=F.relative(t,e),i=this.filePathToRoutePath(o),a=this.findPageComponent(n);if(!a)return null;let s=this.extractRouteParams(i),r=this.extractLayout(n),c=this.extractAuthRequirement(n),l=this.extractPermissions(n),g=this.extractDataFetching(n),u=this.extractNavigation(n),m=this.extractLinkedPages(n),A=this.extractSteps(n);return {path:i,filePath:o,component:a,params:s,layout:r,authentication:c,permissions:l,dataFetching:g,navigation:u,linkedPages:m,steps:A.length>0?A:void 0}}detectPagesRoot(e){let t=["/src/pages/","/pages/","/src/app/","/app/","/frontend/src/pages/","/app/javascript/pages/"];for(let n of t){let o=e.indexOf(n);if(o!==-1)return e.substring(0,o+n.length-1)}return this.basePath}filePathToRoutePath(e){return "/"+e.replace(/\.tsx?$/,"").replace(/\/index$/,"").replace(/\[\.\.\.(\w+)\]/g,"*").replace(/\[(\w+)\]/g,":$1")}extractRouteParams(e){let t=[],n=/:(\w+)/g,o;for(;(o=n.exec(e))!==null;)t.push(o[1]);return t}findPageComponent(e){let t=e.getDefaultExportSymbol();if(t){let s=t.getName();if(s!=="default")return s}let n=e.getExportAssignment(s=>!s.isExportEquals());if(n){let s=n.getExpression();if(s){let r=s.getText();if(/^[A-Z][a-zA-Z0-9]*$/.test(r))return r;if(Node.isFunctionExpression(s)||Node.isArrowFunction(s))return "default"}}let o=e.getFunctions();for(let s of o)if(s.isDefaultExport())return s.getName()||"default";if(e.getVariableDeclaration("Page"))return "Page";let a=e.getVariableDeclarations();for(let s of a){let r=s.getTypeNode();if(r){let c=r.getText();if(c.includes("NextPage")||c.includes("FC")||c.includes("React.FC"))return s.getName()}}for(let s of a){let r=s.getName();if(/^[A-Z][a-zA-Z0-9]*$/.test(r)){let c=s.getInitializer();if(c&&(Node.isArrowFunction(c)||Node.isFunctionExpression(c))){let l=c.getText();if(l.includes("return")&&(l.includes("<")||l.includes("jsx")))return r}}}return null}extractLayout(e){let t=e.getDescendantsOfKind(SyntaxKind.PropertyAccessExpression).find(n=>n.getName()==="getLayout");if(t){let n=t.getParent();if(Node.isBinaryExpression(n)){let i=n.getRight().getDescendantsOfKind(SyntaxKind.JsxOpeningElement);if(i.length>0)return i[0].getTagNameNode().getText()}}}extractAuthRequirement(e){let n=e.getFilePath().split("/").pop()||"",a={required:!["404.tsx","permission-denied.tsx","_app.tsx","_document.tsx","_error.tsx"].some(s=>n===s)};try{let s=["RequiredCondition","ProtectedRoute","AuthGuard","PrivateRoute","WithAuth","RequireAuth","Authenticated","Authorized"],r=e.getDescendantsOfKind(SyntaxKind.JsxOpeningElement).find(c=>{let l=c.getTagNameNode().getText();return s.some(g=>l.includes(g))});if(r){a.condition="Additional permissions required";let c=r.getAttributes();for(let l of c)if(l.isKind(SyntaxKind.JsxAttribute))try{let g=l.getNameNode().getText();if(["condition","roles","permissions","requiredRoles","allowedRoles"].includes(g)){let u=l.getInitializer();if(u){a.condition=u.getText();let m=this.extractRolesFromCondition(u.getText());m.length>0&&(a.roles=m);}}}catch{}}}catch{}return a}extractRolesFromCondition(e){let t=[],n=/(\w+Role|\w+Permission)\.(\w+)/g,o;for(;(o=n.exec(e))!==null;)t.push(o[2]);let i=/['"]([a-zA-Z_-]+)['"]/g;for(;(o=i.exec(e))!==null;){let s=o[1];/admin|user|owner|member|guest|manager|editor|viewer/i.test(s)&&t.push(s);}let a=/\b(ROLE_\w+|[A-Z]+_ROLE)\b/g;for(;(o=a.exec(e))!==null;)t.push(o[1]);return [...new Set(t)]}extractPermissions(e){let t=[],n=e.getDescendantsOfKind(SyntaxKind.PropertyAccessExpression).filter(o=>{let i=o.getText();return i.includes("Permission")||i.includes("Role")||i.includes("isAdmin")});for(let o of n){let i=o.getText();t.includes(i)||t.push(i);}return t}extractDataFetching(e){let t=[],n=new Map,o=["useQuery","useMutation","useLazyQuery","useSubscription"];for(let y of e.getImportDeclarations()){let p=y.getModuleSpecifierValue();if(p.includes("@apollo/client")||p.includes("apollo"))for(let f of y.getNamedImports()){let x=f.getName(),d=f.getAliasNode()?.getText()||x;o.includes(x)&&n.set(d,x);}}let i=n.size>0||e.getImportDeclarations().some(y=>{let p=y.getModuleSpecifierValue();return p.includes("@apollo/client")||p.includes("apollo")}),a=e.getDescendantsOfKind(SyntaxKind.CallExpression).filter(y=>{let p=y.getExpression().getText();return !!(n.has(p)||o.includes(p)||/^use[A-Z].*Query$/.test(p)&&!p.includes("Params")&&!p.includes("String")||/^use[A-Z].*Mutation$/.test(p))});for(let y of a){let p=y.getExpression().getText(),f;n.has(p)?f=n.get(p):p.includes("Mutation")?f="useMutation":p.includes("Lazy")?f="useLazyQuery":f="useQuery";let x=y.getArguments();if(x.length===0){if(/^use[A-Z]/.test(p)){let T=p.replace(/^use/,"").replace(/Query$|Mutation$/,"");t.push({type:f,operationName:T,variables:[]});}continue}let h=x[0].getText();if(h.startsWith("[")||h.startsWith("{")||h.startsWith("'")||h.startsWith('"')||h.startsWith("`")||!(i||h.endsWith("Document")||h.endsWith("Query")||h.endsWith("Mutation")||h.includes("gql")||/^[A-Z_]+$/.test(h)))continue;let P=h.replace(/Document$/,"").replace(/Query$|Mutation$/,""),C=[];if(x.length>1){let b=x[1].getDescendantsOfKind(SyntaxKind.PropertyAssignment).find(D=>{try{return D.getName()==="variables"}catch{return false}});if(b){let D=b.getInitializer();if(D){let k=D.getDescendantsOfKind(SyntaxKind.PropertyAssignment);for(let Z of k)try{C.push(Z.getName());}catch{}}}}t.push({type:f,operationName:P,variables:C});}let s=e.getVariableDeclaration("getServerSideProps"),r=e.getFunction("getServerSideProps"),c=s||r;if(c){let y=e.getImportDeclarations();for(let x of y){let d=x.getNamedImports();for(let h of d){let S=h.getName();if(S.endsWith("Document")&&e.getDescendantsOfKind(SyntaxKind.Identifier).filter(C=>C.getText()===S).length>0){let C=S.replace(/Document$/,"");t.push({type:"getServerSideProps",operationName:`\u2192 ${C}`});}}}let f=c.getText().match(/query:\s*(\w+)/g);if(f)for(let x of f){let d=x.replace(/query:\s*/,"");t.some(h=>h.operationName?.includes(d.replace(/Document$/,"")))||t.push({type:"getServerSideProps",operationName:`\u2192 ${d.replace(/Document$/,"")}`});}}let l=e.getVariableDeclaration("getStaticProps"),g=e.getFunction("getStaticProps");(l||g)&&t.push({type:"getStaticProps",operationName:"getStaticProps"});let u=e.getImportDeclarations(),m=e.getFilePath(),A=F.dirname(m);for(let y of u){let p=y.getModuleSpecifierValue(),f=p.startsWith(".")||p.startsWith("/"),x=!p.includes("node_modules")&&!p.startsWith("@types/")&&p.startsWith("@")===false;if(f||x){if(p.includes("__generated__")||p.includes("/generated/"))continue;let d=[],h=y.getNamedImports();for(let P of h){let C=P.getName();this.isComponentName(C)&&d.push(C);}let S=y.getDefaultImport();if(S){let P=S.getText();this.isComponentName(P)&&d.push(P);}for(let P of d){let C=this.analyzeImportedComponent(A,p,P);if(C.length>0)for(let T of C)t.push({type:T.type,operationName:T.operationName.startsWith("\u2192")?`\u2192 ${T.operationName} (${P})`:`\u2192 ${T.operationName} (${P})`,variables:T.variables});else t.push({type:"component",operationName:P,variables:[]});}}}return t}symbolTraceCache=new Map;analyzeImportedComponent(e,t,n,o=new Set,i=0){if(i>10)return [];let s=[];try{let r=F.resolve(e,t),c=`${r}:${n}`;if(o.has(c))return [];o.add(c);let l=this.symbolTraceCache.get(c);if(l!==void 0)return l;let g=[`${r}.tsx`,`${r}.ts`,`${r}/index.tsx`,`${r}/index.ts`,`${r}/${n}.tsx`,`${r}/${n}.ts`],u,m;for(let p of g)try{if(u=this.project.addSourceFileAtPath(p),u){m=p;break}}catch{}if(!u||!m)return s;if(m.endsWith("index.tsx")||m.endsWith("index.ts")){let p=this.followReExport(u,n,F.dirname(m));p&&(u=p);}let A=u.getImportDeclarations().some(p=>{let f=p.getModuleSpecifierValue();return f.includes("@apollo/client")||f.includes("apollo")||f.includes("gql")||f.includes("graphql")||f.includes("__generated__")}),y=u.getImportDeclarations().filter(p=>{let f=p.getModuleSpecifierValue();return f.startsWith("./")||f.startsWith("../")});for(let p of y){let f=p.getModuleSpecifierValue(),x=p.getNamedImports().map(d=>d.getName()).filter(d=>/^use[A-Z]/.test(d));for(let d of x){let h=this.analyzeCustomHook(F.dirname(u.getFilePath()),f,d,o,i+1);s.push(...h);}}for(let p of y){let f=p.getModuleSpecifierValue(),x=p.getNamedImports().map(S=>S.getName()),d=p.getDefaultImport()?.getText(),h=x.filter(S=>/^[A-Z]/.test(S)&&this.isComponentName(S));for(let S of h){let P=this.analyzeImportedComponent(F.dirname(u.getFilePath()),f,S,o,i+1);s.push(...P);}if(d&&/^[A-Z]/.test(d)&&this.isComponentName(d)){let S=this.analyzeImportedComponent(F.dirname(u.getFilePath()),f,d,o,i+1);s.push(...S);}}if(A){let p=u.getDescendantsOfKind(SyntaxKind.CallExpression).filter(f=>{let x=f.getExpression().getText();return ["useQuery","useMutation","useLazyQuery","useSubscription"].includes(x)});for(let f of p){let x=f.getExpression().getText(),d=f.getArguments();if(d.length===0)continue;let S=d[0].getText(),P=S,C=null,T=this.resolveDocumentName(S);if(T)P=T.operationName,C=T.operationType;else if(S.endsWith("Document"))P=S.replace(/Document$/,"");else if(/^[A-Za-z]/.test(S)){let D=this.findOperationNameFromVariable(u,S);if(D&&(P=D),P===S&&S!=="Query"&&S!=="Mutation"){let k=S.match(/^(.+?)(Query|Mutation|Subscription)$/);k&&(P=k[1]);}}P!=="Query"&&P!=="Mutation"&&(P=P.replace(/Document$/,"").replace(/Query$|Mutation$/,"")||P),(P==="Query"||P==="Mutation"||P==="")&&P===""&&(P=S||"Unknown");let b=C?C==="mutation"?"useMutation":C==="subscription"?"useSubscription":x.includes("Lazy")?"useLazyQuery":"useQuery":x.includes("Mutation")?"useMutation":x.includes("Lazy")?"useLazyQuery":"useQuery";s.push({type:b,operationName:P,variables:[]});}}this.symbolTraceCache.set(c,s);}catch{}return s}analyzeCustomHook(e,t,n,o=new Set,i=0){if(i>10)return [];let s=[];try{let r=F.resolve(e,t),c=`hook:${r}:${n}`;if(o.has(c))return [];o.add(c);let l=this.symbolTraceCache.get(c);if(l!==void 0)return l;let g=[`${r}.tsx`,`${r}.ts`,`${r}/${n}.tsx`,`${r}/${n}.ts`,`${r}/index.tsx`,`${r}/index.ts`],u;for(let p of g)try{if(u=this.project.addSourceFileAtPath(p),u)break}catch{}if(!u)return s;let m=u.getImportDeclarations().some(p=>{let f=p.getModuleSpecifierValue();return f.includes("@apollo/client")||f.includes("apollo")||f.includes("graphql")||f.includes("__generated__")}),A=u.getImportDeclarations().filter(p=>{let f=p.getModuleSpecifierValue();return f.startsWith("./")||f.startsWith("../")});for(let p of A){let f=p.getModuleSpecifierValue(),x=p.getNamedImports().map(d=>d.getName()).filter(d=>/^use[A-Z]/.test(d));for(let d of x){let h=this.analyzeCustomHook(F.dirname(u.getFilePath()),f,d,o,i+1);s.push(...h);}}if(!m&&s.length===0)return s;let y=u.getDescendantsOfKind(SyntaxKind.CallExpression).filter(p=>{let f=p.getExpression().getText();return ["useQuery","useMutation","useLazyQuery","useSubscription"].includes(f)});for(let p of y){let f=p.getExpression().getText(),x=p.getArguments();if(x.length===0)continue;let d=x[0].getText(),h=d,S=null,P=this.resolveDocumentName(d);if(P)h=P.operationName,S=P.operationType;else if(d.endsWith("Document"))h=d.replace(/Document$/,"");else if(/^[A-Za-z]/.test(d)){let T=this.findOperationNameFromVariable(u,d);if(T&&(h=T),h===d&&d!=="Query"&&d!=="Mutation"){let b=d.match(/^(.+?)(Query|Mutation|Subscription)$/);b&&(h=b[1]);}}h!=="Query"&&h!=="Mutation"&&(h=h.replace(/Document$/,"").replace(/Query$|Mutation$/,"")||h),h===""&&(h=d||"Unknown");let C=S?S==="mutation"?"useMutation":S==="subscription"?"useSubscription":f.includes("Lazy")?"useLazyQuery":"useQuery":f.includes("Mutation")?"useMutation":f.includes("Lazy")?"useLazyQuery":"useQuery";s.push({type:C,operationName:`\u2192 ${h} (via ${n})`,variables:[]});}this.symbolTraceCache.set(c,s);}catch{}return s}globalContextQueries=[];async findPageFiles(){let e=this.getSetting("pagesDir","src/pages"),t=[],o=[...new Set([e,"pages","src/pages","app","src/app"])];for(let s of o){if(s==="app"||s==="src/app"){let c=["controllers","models","views","helpers"],l=this.resolvePath(s);if(c.some(u=>{try{return W.existsSync(F.join(l,u))}catch{return false}}))continue}let r=this.resolvePath(s);try{let c=await z(["**/*.tsx","**/*.ts","**/*.jsx","**/*.js"],{cwd:r,ignore:["_app.tsx","_app.ts","_app.jsx","_app.js","_document.tsx","_document.ts","_document.jsx","_document.js","_error.tsx","_error.ts","_error.jsx","_error.js","api/**","**/*.test.*","**/*.spec.*","**/node_modules/**","**/components/pages/**"],absolute:!0});t.push(...c),c.length>0&&this.log(`Found ${c.length} pages in ${s}`);}catch{}}let i=["frontend/src/**/pages","app/javascript/**/pages"];for(let s of i)try{let r=await z([`${s}/**/*.tsx`,`${s}/**/*.ts`,`${s}/**/*.jsx`,`${s}/**/*.js`],{cwd:this.basePath,ignore:["**/*.test.*","**/*.spec.*","**/node_modules/**","**/vendor/**","**/components/pages/**","**/stories/**"],absolute:!0});t.push(...r),r.length>0&&this.log(`Found ${r.length} React pages in ${s}`);}catch{}let a=["frontend/src/**/index.tsx","frontend/src/**/App.tsx","app/javascript/packs/*.tsx","app/javascript/packs/*.jsx"];for(let s of a)try{let r=await z([s],{cwd:this.basePath,ignore:["**/node_modules/**","**/vendor/**"],absolute:!0});for(let c of r)t.includes(c)||t.push(c);}catch{}return [...new Set(t)]}async analyzeAppFile(){let e=this.getSetting("pagesDir","src/pages"),t=[this.resolvePath(`${e}/_app.tsx`),this.resolvePath(`${e}/_app.ts`)];for(let n of t)try{let o=this.project.addSourceFileAtPath(n);if(!o)continue;let i=o.getDescendantsOfKind(SyntaxKind.JsxElement),a=o.getDescendantsOfKind(SyntaxKind.JsxSelfClosingElement),s=new Set;for(let r of [...i,...a]){let c=r.getFirstDescendantByKind(SyntaxKind.Identifier)?.getText();c&&(c.includes("Provider")||c.includes("Context"))&&s.add(c);}for(let r of o.getImportDeclarations()){let c=r.getModuleSpecifierValue();if(!c.startsWith("./")&&!c.startsWith("../"))continue;let l=r.getNamedImports().map(u=>u.getName()),g=r.getDefaultImport()?.getText();for(let u of s)if(l.includes(u)||g===u){let m=this.analyzeImportedComponent(F.dirname(n),c,u,new Set,0);for(let A of m)this.globalContextQueries.push({...A,operationName:`[Global] ${A.operationName}`});}}this.globalContextQueries.length>0&&this.log(`Found ${this.globalContextQueries.length} global context queries from _app`);return}catch{}}async loadCodegenMapping(){let e=["__generated__","src/__generated__","src/__generated__/gql-graphql-gateway","generated","src/generated"];for(let t of e){let n=this.resolvePath(t);try{let o=await z(["**/*.ts","**/*.tsx"],{cwd:n,absolute:!0,onlyFiles:!0});for(let i of o)try{let a=this.project.addSourceFileAtPath(i),s=a.getVariableDeclarations();for(let c of s){let l=c.getName();if(l.endsWith("Document")){let g=c.getInitializer()?.getText()??"",u=g.match(/(?:query|mutation|subscription)\s+(\w+)/),m=g.match(/(query|mutation|subscription)\s+/);u&&this.codegenMap.set(l,{operationName:u[1],operationType:m?m[1]:"query"});}}let r=a.getTypeAliases();for(let c of r){let l=c.getName();if((l.endsWith("Query")||l.endsWith("Mutation")||l.endsWith("Subscription"))&&!l.endsWith("Variables")){let g=l+"Document";if(!this.codegenMap.has(g)){let u=l.endsWith("Mutation")?"mutation":l.endsWith("Subscription")?"subscription":"query";this.codegenMap.set(g,{operationName:l,operationType:u});}}}}catch{}if(this.codegenMap.size>0){this.log(`Loaded ${this.codegenMap.size} codegen mappings from ${t}`);return}}catch{}}}resolveDocumentName(e){if(new Set(["Query","Mutation","Subscription"]).has(e))return null;let n=this.codegenMap.get(e);if(n!==void 0)return n;let o=e.endsWith("Document")?e:e+"Document",i=this.codegenMap.get(o);return i!==void 0?i:null}followReExport(e,t,n){try{let o=e.getExportDeclarations(),i=null;for(let a of o){let s=a.getNamedExports();for(let r of s){let c=a.getModuleSpecifierValue();if(!c)continue;let l=F.resolve(n,c),g=[`${l}.tsx`,`${l}.ts`,`${l}/index.tsx`,`${l}/index.ts`],u;for(let m of g)try{if(u=this.project.addSourceFileAtPath(m),u)break}catch{}if(u&&(i||(i=u),r.getName()===t||r.getAliasNode()?.getText()===t))return u}}if(i)return i;for(let a of e.getExportDeclarations())if(a.isNamespaceExport()){let s=a.getModuleSpecifierValue();if(s){let r=F.resolve(n,s),c=[`${r}.tsx`,`${r}.ts`];for(let l of c)try{let g=this.project.addSourceFileAtPath(l);if(g&&g.getExportedDeclarations().has(t))return g}catch{}}}}catch{}return null}isComponentName(e){return !/^[A-Z]/.test(e)||e.endsWith("Query")||e.endsWith("Mutation")||e.endsWith("Subscription")||e.endsWith("Fragment")||e.endsWith("Document")||e.endsWith("Variables")||e==="Query"||e==="Mutation"||e==="Subscription"||new Set(["NextPage","NextPageContext","NextApiRequest","NextApiResponse","GetServerSideProps","GetStaticProps","GetStaticPaths","InferGetServerSidePropsType","InferGetStaticPropsType","FC","FunctionComponent","VFC","Component","PureComponent","ReactNode","ReactElement","PropsWithChildren","ComponentProps","ComponentType","ElementType","RefObject","MutableRefObject","Dispatch","SetStateAction","ChangeEvent","MouseEvent","KeyboardEvent","FormEvent","SyntheticEvent"]).has(e)?false:!!(["Container","Page","Screen","View","Form","Modal","Dialog","Panel","Root","Provider","Wrapper"].some(o=>e.endsWith(o))||/Page[A-Z]?\w*$/.test(e)||/Container[A-Z]?\w*$/.test(e)||/^[A-Z][a-z]+[A-Z][a-z]+/.test(e))}extractNavigation(e){let t={visible:true,currentNavItem:null};try{let n=e.getDescendantsOfKind(SyntaxKind.PropertyAccessExpression).find(o=>{try{return o.getName()==="globalNavigationStyle"}catch{return !1}});if(n){let o=n.getParent();if(Node.isBinaryExpression(o)){let i=o.getRight(),a=i.getDescendantsOfKind(SyntaxKind.PropertyAssignment).find(c=>{try{return c.getName()==="visible"}catch{return !1}});a&&(t.visible=a.getInitializer()?.getText()==="true");let s=i.getDescendantsOfKind(SyntaxKind.PropertyAssignment).find(c=>{try{return c.getName()==="currentNavItem"}catch{return !1}});if(s){let c=s.getInitializer()?.getText();t.currentNavItem=c&&c!=="null"?c.replace(/['"]/g,""):null;}let r=i.getDescendantsOfKind(SyntaxKind.PropertyAssignment).find(c=>{try{return c.getName()==="mini"}catch{return !1}});r&&(t.mini=r.getInitializer()?.getText()==="true");}}}catch{}return t}extractSteps(e){let t=[],n=e.getDescendantsOfKind(SyntaxKind.CallExpression).filter(a=>a.getExpression().getText()==="useState");for(let a of n){let s=a.getParent();if(!s)continue;let c=s.getText().match(/\[\s*(step|currentStep|activeStep|page|currentPage|phase|stage)\s*,/i);if(c){let l=c[1],g=e.getDescendantsOfKind(SyntaxKind.SwitchStatement);for(let m of g)m.getExpression().getText().includes(l)&&m.getClauses().forEach((p,f)=>{if(p.isKind(SyntaxKind.CaseClause)){let x=p.getExpression()?.getText()||String(f),d=p.getDescendantsOfKind(SyntaxKind.JsxOpeningElement),h=d.length>0?d[0].getTagNameNode().getText():void 0;t.push({id:x.replace(/['"]/g,""),name:`Step ${x.replace(/['"]/g,"")}`,component:h});}});let u=e.getDescendantsOfKind(SyntaxKind.ArrayLiteralExpression);for(let m of u){let A=m.getParent();A&&A.getText().match(/steps|pages|screens|views|components/i)&&m.getElements().forEach((p,f)=>{let x=p.getText();if(x.startsWith("{")){let d=x.match(/(?:name|label|title)\s*:\s*['"]([^'"]+)['"]/),h=x.match(/(?:component|content)\s*:\s*<?\s*(\w+)/);t.push({id:f+1,name:d?d[1]:`Step ${f+1}`,component:h?h[1]:void 0});}else /^[A-Z]/.test(x)&&t.push({id:f+1,name:x,component:x});});}}}let o=e.getDescendantsOfKind(SyntaxKind.JsxOpeningElement);for(let a of o)if(a.getTagNameNode().getText().match(/Stepper|Wizard|Steps|TabPanel|FormStep/i)){let r=a.getParent();r&&r.isKind(SyntaxKind.JsxElement)&&r.getJsxChildren().forEach((l,g)=>{if(l.isKind(SyntaxKind.JsxElement)||l.isKind(SyntaxKind.JsxSelfClosingElement)){let u=l.isKind(SyntaxKind.JsxElement)?l.getOpeningElement().getTagNameNode().getText():l.getTagNameNode().getText(),m=l.isKind(SyntaxKind.JsxElement)?l.getOpeningElement().getAttributes():l.getAttributes(),A=u;for(let y of m)if(y.isKind(SyntaxKind.JsxAttribute)){let p=y.getNameNode().getText();if(p==="label"||p==="title"||p==="name"){let f=y.getInitializer()?.getText();if(f){A=f.replace(/['"{}]/g,"");break}}}t.push({id:g+1,name:A,component:u});}});}let i=e.getDescendantsOfKind(SyntaxKind.ConditionalExpression);for(let a of i){let s=a.getCondition().getText();if(s.match(/step\s*===?\s*\d+|currentStep|activeStep/i)){let r=a.getWhenTrue(),c=s.match(/===?\s*(\d+)/);if(c&&t.length===0){let l=r.getDescendantsOfKind(SyntaxKind.JsxOpeningElement);l.length>0&&t.push({id:parseInt(c[1]),component:l[0].getTagNameNode().getText()});}}}return t}extractLinkedPages(e){let t=[],n=e.getDescendantsOfKind(SyntaxKind.CallExpression).filter(i=>{let a=i.getExpression().getText();return a.includes("router.push")||a.includes("router.replace")||a.includes("Link")});for(let i of n){let a=i.getArguments();if(a.length>0){let r=a[0].getText().match(/['"`]([^'"`]+)['"`]/);r&&!t.includes(r[1])&&t.push(r[1]);}}let o=e.getDescendantsOfKind(SyntaxKind.JsxOpeningElement).filter(i=>i.getTagNameNode().getText()==="Link");for(let i of o)try{let a=i.getAttributes();for(let s of a)if(s.isKind(SyntaxKind.JsxAttribute)&&s.getNameNode().getText()==="href"){let l=s.getInitializer()?.getText();if(l){let g=l.match(/['"`]([^'"`]+)['"`]/);g&&!t.includes(g[1])&&t.push(g[1]);}}}catch{}return t}};var G=class extends w{project;constructor(e){super(e),this.project=new Project({tsConfigFilePath:this.resolvePath("tsconfig.json"),skipAddingFilesFromTsConfig:true});}getName(){return "GraphQLAnalyzer"}async analyze(){this.log("Starting GraphQL analysis...");let e=[],t=await this.analyzeGraphQLFiles();e.push(...t);let n=await this.analyzeInlineGraphQL();e.push(...n);let o=await this.analyzeCodegenGenerated();e.push(...o);let i=this.deduplicateOperations(e);return await this.findOperationUsage(i),this.log(`Found ${i.length} GraphQL operations`),{graphqlOperations:i}}deduplicateOperations(e){let t=new Map;for(let n of e)if(!t.has(n.name))t.set(n.name,n);else {let o=t.get(n.name);if(o)for(let i of n.usedIn)o.usedIn.includes(i)||o.usedIn.push(i);}return Array.from(t.values())}async analyzeCodegenGenerated(){let e=[],t=await z(["**/__generated__/graphql.ts","**/__generated__/gql.ts","**/generated/graphql.ts"],{cwd:this.basePath,ignore:["**/node_modules/**","**/.next/**"],absolute:true});for(let n of t)try{let o=await O.readFile(n,"utf-8"),i=F.relative(this.basePath,n),a=o.split(`
|
|
2
|
+
`);for(let s of a){if(!s.includes("Document =")||!s.includes("DocumentNode"))continue;let r=s.match(/export\s+const\s+(\w+Document)\s*=\s*(\{"kind":"Document".+\})\s*as\s+unknown\s+as\s+DocumentNode/);if(!r)continue;let c=r[1],l=r[2];try{let g=JSON.parse(l);if(g.kind==="Document"&&g.definitions){let u=g.definitions[0];if(u?.kind==="OperationDefinition"){let m=u.name?.value||c.replace(/Document$/,""),A=u.operation,y=(u.variableDefinitions||[]).map(p=>({name:p.variable?.name?.value||"unknown",type:this.extractTypeFromAst(p.type),required:p.type?.kind==="NonNullType"}));e.push({name:m,type:A,filePath:i,usedIn:[],variables:y,returnType:this.inferReturnTypeFromAst(u),fragments:[],fields:[]});}}}catch{}}this.log(`Found ${e.length} operations in codegen output: ${i}`);}catch(o){this.warn(`Failed to analyze codegen file ${n}: ${o.message}`);}return e}extractTypeFromAst(e){return e?e.kind==="NonNullType"?`${this.extractTypeFromAst(e.type)}!`:e.kind==="ListType"?`[${this.extractTypeFromAst(e.type)}]`:e.kind==="NamedType"&&e.name?.value||"unknown":"unknown"}extractFieldsFromAst(e,t=0){if(!e?.selections||t>5)return [];let n=[];for(let o of e.selections)if(o.kind==="Field"){let i={name:o.name?.value||"unknown"};if(o.arguments?.length>0){let a=o.arguments.map(s=>s.name?.value).join(", ");i.type=`(${a})`;}o.selectionSet&&(i.fields=this.extractFieldsFromAst(o.selectionSet,t+1)),n.push(i);}else if(o.kind==="FragmentSpread")n.push({name:`...${o.name?.value}`,type:"fragment"});else if(o.kind==="InlineFragment"){let i=o.typeCondition?.name?.value||"inline";n.push({name:`... on ${i}`,type:"inline-fragment",fields:this.extractFieldsFromAst(o.selectionSet,t+1)});}return n}extractFragmentReferencesFromAst(e){let t=[],n=o=>{if(o&&(o.kind==="FragmentSpread"&&t.push(o.name?.value),o.selectionSet?.selections))for(let i of o.selectionSet.selections)n(i);};return n(e),t.filter(Boolean)}inferReturnTypeFromAst(e){if(e.selectionSet?.selections?.length>0){let t=e.selectionSet.selections[0];if(t.kind==="Field")return t.name?.value||"unknown"}return "unknown"}async analyzeGraphQLFiles(){let e=await z(["**/*.graphql"],{cwd:this.basePath,ignore:["**/node_modules/**","**/.next/**"],absolute:true});return (await E(e,async n=>{let o=await O.readFile(n,"utf-8"),i=parse(o);return this.extractOperationsFromDocument(i,F.relative(this.basePath,n))})).flat()}async analyzeInlineGraphQL(){let e=[],t=await z(["**/*.ts","**/*.tsx"],{cwd:this.basePath,ignore:["**/node_modules/**","**/.next/**","**/__tests__/**","**/*.test.*","**/*.spec.*"],absolute:true});for(let n of t)try{let o=this.project.addSourceFileAtPath(n),i=F.relative(this.basePath,n),a=o.getImportDeclarations().some(c=>{let l=c.getModuleSpecifierValue(),g=c.getNamedImports().map(m=>m.getName()),u=c.getDefaultImport()?.getText();return (g.includes("gql")||g.includes("graphql")||u==="gql")&&(l.includes("graphql")||l.includes("apollo")||l.includes("gql")||l.includes("__generated__"))}),s=o.getDescendantsOfKind(SyntaxKind.TaggedTemplateExpression);for(let c of s){let l=c.getTag().getText();if(l==="gql"||l==="graphql")try{let g=c.getTemplate(),u="";if(g.isKind(SyntaxKind.NoSubstitutionTemplateLiteral)?u=g.getLiteralValue():g.isKind(SyntaxKind.TemplateExpression)&&(u=g.getText().slice(1,-1).replace(/\$\{[^}]*\}/g,"")),u&&u.trim())try{let m=parse(u),A=this.extractOperationsFromDocument(m,i);e.push(...A);}catch{}}catch{}}let r=o.getDescendantsOfKind(SyntaxKind.CallExpression);for(let c of r)try{let g=c.getExpression().getText();if(g==="gql"||g==="graphql"){let u=c.getArguments();if(u.length>0){let m=u[0],A="";if(m.isKind(SyntaxKind.NoSubstitutionTemplateLiteral))A=m.getLiteralValue();else if(m.isKind(SyntaxKind.TemplateExpression))A=m.getText().slice(1,-1).replace(/\$\{[^}]*\}/g,"");else {let y=m.getText();if(y.includes("`")){let p=y.match(/\/\*\s*GraphQL\s*\*\/\s*`([^`]*)`/);if(p)A=p[1];else {let f=y.match(/`([^`]*)`/);f&&(A=f[1]);}}}if(A&&A.trim())try{let y=parse(A),p=this.extractOperationsFromDocument(y,i);e.push(...p);}catch{}}}}catch{}if(a){let c=o.getVariableDeclarations();for(let l of c){let g=l.getName();if(g.includes("QUERY")||g.includes("MUTATION")||g.includes("FRAGMENT")||g.includes("Query")||g.includes("Mutation")||g.includes("Subscription")||/^[A-Z_]+_(QUERY|MUTATION|FRAGMENT|SUBSCRIPTION)$/.test(g)||/Query$|Mutation$|Fragment$|Subscription$/.test(g)){let m=l.getInitializer();m&&m.isKind(SyntaxKind.CallExpression);}}}}catch(o){this.warn(`Failed to analyze ${n}: ${o.message}`);}return e}extractOperationsFromDocument(e,t){let n=[];for(let o of e.definitions){let i=this.extractOperation(o,t);i&&n.push(i);}return n}extractOperation(e,t){if(e.kind==="OperationDefinition"){let n=e.name?.value||"anonymous",o=e.operation,i=this.extractVariables(e),a=this.extractFragmentReferences(e),s=this.extractFields(e);return {name:n,type:o,filePath:t,usedIn:[],variables:i,returnType:this.inferReturnType(e),fragments:a,fields:s}}return e.kind==="FragmentDefinition"?{name:e.name.value,type:"fragment",filePath:t,usedIn:[],variables:[],returnType:e.typeCondition.name.value,fragments:this.extractFragmentReferences(e),fields:this.extractFields(e)}:null}extractFields(e){let t=[],n=(o,i=0)=>{if(!o||!o.selections||i>5)return [];let a=[];for(let s of o.selections)if(s.kind==="Field"){let r={name:s.name.value};if(s.arguments&&s.arguments.length>0){let c=s.arguments.map(l=>l.name.value).join(", ");r.type=`(${c})`;}s.selectionSet&&(r.fields=n(s.selectionSet,i+1)),a.push(r);}else if(s.kind==="FragmentSpread")a.push({name:`...${s.name.value}`,type:"fragment"});else if(s.kind==="InlineFragment"&&s.selectionSet){let r=s.typeCondition?.name?.value||"inline";a.push({name:`... on ${r}`,type:"inline-fragment",fields:n(s.selectionSet,i+1)});}return a};return e.selectionSet?n(e.selectionSet):t}extractVariables(e){let t=[];if(e.variableDefinitions)for(let n of e.variableDefinitions){let o=n.variable.name.value,i=this.typeNodeToString(n.type),a=n.type.kind==="NonNullType";t.push({name:o,type:i,required:a});}return t}typeNodeToString(e){return e.kind==="NonNullType"?`${this.typeNodeToString(e.type)}!`:e.kind==="ListType"?`[${this.typeNodeToString(e.type)}]`:e.kind==="NamedType"?e.name.value:"unknown"}extractFragmentReferences(e){let t=[],n=o=>{if(o&&(o.kind==="FragmentSpread"&&t.push(o.name.value),o.selectionSet))for(let i of o.selectionSet.selections)n(i);};return n(e),t}inferReturnType(e){if(e.selectionSet&&e.selectionSet.selections.length>0){let t=e.selectionSet.selections[0];if(t.kind==="Field")return t.name.value}return "unknown"}async findOperationUsage(e){let t=await z(["**/*.ts","**/*.tsx"],{cwd:this.basePath,ignore:["**/node_modules/**","**/.next/**","**/__generated__/**"],absolute:true}),n=new Map;for(let o of e)n.set(o.name,o),n.set(`${o.name}Document`,o);for(let o of t)try{let i=await O.readFile(o,"utf-8"),a=F.relative(this.basePath,o);for(let[s,r]of n){if(a===r.filePath)continue;let c=[`useQuery<${s}`,`useMutation<${s}`,`useLazyQuery<${s}`,`useSubscription<${s}`,`${s}Query`,`${s}Mutation`,`${s}Variables`],l=[new RegExp(`import\\s*\\{[^}]*\\b${s}\\b[^}]*\\}\\s*from`),new RegExp(`useQuery\\s*\\(\\s*${s}`),new RegExp(`useMutation\\s*\\(\\s*${s}`),new RegExp(`useLazyQuery\\s*\\(\\s*${s}`),new RegExp(`useSubscription\\s*\\(\\s*${s}`),new RegExp(`useSuspenseQuery\\s*\\(\\s*${s}`),new RegExp(`query\\s*:\\s*${s}`),new RegExp(`mutation\\s*:\\s*${s}`)],g=c.some(m=>i.includes(m)),u=l.some(m=>m.test(i));(g||u)&&(r.usedIn.includes(a)||r.usedIn.push(a));}}catch{}}};var K=class extends w{project;componentCache=new Map;constructor(e){super(e),this.project=new Project({tsConfigFilePath:this.resolvePath("tsconfig.json"),skipAddingFilesFromTsConfig:true});}getName(){return "DataFlowAnalyzer"}async analyze(){this.log("Starting data flow analysis...");let e=await this.analyzeComponents(),t=await this.analyzeDataFlows(e);return this.log(`Analyzed ${e.length} components and ${t.length} data flows`),{components:e,dataFlows:t}}async analyzeComponents(){let e=[],t=[this.getSetting("featuresDir",""),this.getSetting("componentsDir",""),this.getSetting("pagesDir","")].filter(Boolean),n=["src/features","src/components","src/common/components","src/common","src/pages","src/app","src/modules","src/views","src/screens","components","pages","app"],o=[...new Set([...t,...n])];for(let i of o){let a=await z(["**/*.tsx"],{cwd:this.resolvePath(i),ignore:["**/*.test.*","**/*.spec.*","**/*.stories.*"],absolute:true});for(let s of a)try{let r=this.project.addSourceFileAtPath(s),c=F.relative(this.basePath,s),l=this.analyzeComponentFile(r,c);e.push(...l);}catch(r){this.warn(`Failed to analyze ${s}: ${r.message}`);}}return this.buildDependencyGraph(e),e}analyzeComponentFile(e,t){let n=[],o=e.getFunctions();for(let s of o){let r=s.getName();if(r&&this.isComponentName(r)){let c=this.extractComponentInfo(s,r,t);n.push(c),this.componentCache.set(r,c);}}let i=e.getVariableDeclarations();for(let s of i){let r=s.getName();if(this.isComponentName(r)){let c=s.getInitializer();if(c&&(c.isKind(SyntaxKind.ArrowFunction)||c.isKind(SyntaxKind.FunctionExpression))){let l=this.extractComponentInfo(c,r,t);n.push(l),this.componentCache.set(r,l);}}}let a=e.getFunctions().filter(s=>{let r=s.getName();return r&&r.startsWith("use")});for(let s of a){let r=s.getName()??"",c=this.extractHookInfo(s,r,t);n.push(c),this.componentCache.set(r,c);}return n}isComponentName(e){return /^[A-Z][a-zA-Z0-9]*$/.test(e)}extractComponentInfo(e,t,n){let o=e.getSourceFile(),i="presentational";["/pages/","/app/","/routes/","/views/","/screens/"].some(g=>n.includes(g))?i="page":t.includes("Container")||t.includes("Provider")?i="container":(t.includes("Layout")||t.includes("Shell")||t.includes("Wrapper")||t.includes("Frame")||t.includes("Scaffold")||n.includes("/layouts/")||n.includes("/layout/"))&&(i="layout");let s=this.extractProps(e),r=this.extractHooksUsed(e),c=this.extractDependencies(o),l=this.extractStateManagement(e);return {name:t,filePath:n,type:i,props:s,dependencies:c,dependents:[],hooks:r,stateManagement:l}}extractHookInfo(e,t,n){let o=e.getSourceFile(),i=this.extractProps(e),a=this.extractHooksUsed(e),s=this.extractDependencies(o),r=this.extractStateManagement(e);return {name:t,filePath:n,type:"hook",props:i,dependencies:s,dependents:[],hooks:a,stateManagement:r}}extractProps(e){let t=[],n=e.getParameters?.()||[];if(n.length>0){let i=n[0].getTypeNode?.();if(i){let a=i.getDescendantsOfKind?.(SyntaxKind.PropertySignature)||[];for(let s of a)t.push({name:s.getName(),type:s.getType().getText(),required:!s.hasQuestionToken()});}}return t}extractHooksUsed(e){let t=[],n=e.getDescendantsOfKind?.(SyntaxKind.CallExpression)||[];for(let o of n)try{let i=o.getExpression().getText();if(i.startsWith("use"))if(i==="useQuery"||i==="useMutation"||i==="useLazyQuery"){let a=this.extractOperationName(o,i);t.includes(a)||t.push(a);}else if(i==="useContext"){let a=this.extractContextName(o);t.includes(a)||t.push(a);}else t.includes(i)||t.push(i);}catch{}return t}extractOperationName(e,t){try{let n=e.getArguments?.()||[];if(n.length>0){let i=n[0].getText().replace(/^(GET_|FETCH_|CREATE_|UPDATE_|DELETE_)/,"").replace(/_QUERY$|_MUTATION$/,"").replace(/Document$/,"").replace(/Query$|Mutation$/,"");return `${t==="useMutation"?"\u270F\uFE0F":"\u{1F4E1}"} ${t==="useMutation"?"Mutation":"Query"}: ${i}`}}catch{}return t}extractContextName(e){try{let t=e.getArguments?.()||[];if(t.length>0)return `\u{1F504} Context: ${t[0].getText().replace(/Context$/,"").replace(/^Session|^Token|^Apollo/,o=>o)}`}catch{}return "useContext"}extractDependencies(e){let t=[],n=e.getImportDeclarations();for(let o of n){let i=o.getModuleSpecifierValue();if(i.startsWith(".")||i.startsWith("@/")){let a=o.getNamedImports();for(let r of a){let c=r.getName();(this.isComponentName(c)||c.startsWith("use"))&&t.push(c);}let s=o.getDefaultImport();if(s){let r=s.getText();this.isComponentName(r)&&t.push(r);}}}return t}extractStateManagement(e){let t=[],n=e.getText?.()||"";return n.includes("useState")&&t.push("useState"),n.includes("useReducer")&&t.push("useReducer"),n.includes("useContext")&&t.push("useContext"),n.includes("useQuery")&&t.push("Apollo Query"),n.includes("useMutation")&&t.push("Apollo Mutation"),n.includes("useRecoil")&&t.push("Recoil"),(n.includes("useSelector")||n.includes("useDispatch"))&&t.push("Redux"),t}buildDependencyGraph(e){let t=new Map;for(let n of e)t.set(n.name,n);for(let n of e)for(let o of n.dependencies){let i=t.get(o);i&&!i.dependents.includes(n.name)&&i.dependents.push(n.name);}}async analyzeDataFlows(e){let t=[],n=1,o=this.analyzeContextFlows(e);t.push(...o.map(s=>({...s,id:`flow-${n++}`})));let i=this.analyzeApolloFlows(e);t.push(...i.map(s=>({...s,id:`flow-${n++}`})));let a=this.analyzePropDrilling(e);return t.push(...a.map(s=>({...s,id:`flow-${n++}`}))),t}analyzeContextFlows(e){let t=[],n=e.filter(i=>i.name.includes("Provider")||i.name.includes("Context")),o=e.filter(i=>i.hooks.some(a=>a.includes("Context")));for(let i of n){let a=i.name.replace("Provider","").replace("Context","");for(let s of o){let r=s.hooks.find(c=>c.includes("Context")&&c.includes(a));(r||s.hooks.some(c=>c.includes(a)))&&t.push({name:`\u{1F504} ${a} Context`,description:`Data flows from ${i.name} to ${s.name} via Context`,source:{type:"context",name:i.name},target:{type:"component",name:s.name},via:[],operations:[r||`useContext(${a})`]});}}return t}analyzeApolloFlows(e){let t=[];for(let n of e){let o=n.hooks.filter(a=>a.includes("Query:")||a==="useQuery"||a==="useLazyQuery");for(let a of o){let s=a.includes(":")?a.split(":")[1].trim():n.name;t.push({name:`\u{1F4E1} ${s}`,description:`${n.name} fetches ${s} via Apollo`,source:{type:"api",name:`GraphQL: ${s}`},target:{type:"component",name:n.name},via:[{type:"cache",name:"Apollo Cache"}],operations:[a]});}let i=n.hooks.filter(a=>a.includes("Mutation:")||a==="useMutation");for(let a of i){let s=a.includes(":")?a.split(":")[1].trim():n.name;t.push({name:`\u270F\uFE0F ${s}`,description:`${n.name} mutates ${s} via Apollo`,source:{type:"component",name:n.name},target:{type:"api",name:`GraphQL: ${s}`},via:[],operations:[a]});}}return t}analyzePropDrilling(e){let t=[];for(let n of e)n.props.length>5&&n.dependents.length>0&&t.push({name:`Prop Drilling through ${n.name}`,description:`${n.name} passes ${n.props.length} props to children`,source:{type:"component",name:n.name},target:{type:"component",name:n.dependents[0]},via:[],operations:["props"]});return t}};var H=class extends w{project;apiCallCounter=0;constructor(e){super(e),this.project=new Project({tsConfigFilePath:this.resolvePath("tsconfig.json"),skipAddingFilesFromTsConfig:true});}getName(){return "RestApiAnalyzer"}async analyze(){this.log("Starting REST API analysis...");let e=await z(["**/*.ts","**/*.tsx"],{cwd:this.basePath,ignore:["**/node_modules/**","**/.next/**","**/__tests__/**","**/*.test.*","**/*.spec.*","**/*.stories.*","**/__generated__/**","**/dist/**","**/build/**"],absolute:true}),t=new Map;for(let i of e)try{t.set(i,this.project.addSourceFileAtPath(i));}catch{}let o=(await E(Array.from(t.entries()),async([i,a])=>{let s=F.relative(this.basePath,i),r=[];return r.push(...this.findFetchCalls(a,s)),r.push(...this.findAxiosCalls(a,s)),r.push(...this.findSwrCalls(a,s)),r},8)).flat();return this.log(`Found ${o.length} REST API calls`),{apiCalls:o}}findFetchCalls(e,t){let n=[],o=e.getDescendantsOfKind(SyntaxKind.CallExpression);for(let i of o)try{let s=i.getExpression().getText();if(s==="fetch"||s==="window.fetch"){let r=this.extractFetchCall(i,t);r&&n.push(r);}}catch{}return n}findAxiosCalls(e,t){let n=[],o=e.getDescendantsOfKind(SyntaxKind.CallExpression);for(let i of o)try{let s=i.getExpression().getText(),r=s.match(/^axios\.(get|post|put|delete|patch)$/i);if(r){let c=this.extractAxiosCall(i,t,r[1].toUpperCase());c&&n.push(c);}if(s==="axios"){let c=this.extractAxiosDirectCall(i,t);c&&n.push(c);}}catch{}return n}findSwrCalls(e,t){let n=[],o=e.getDescendantsOfKind(SyntaxKind.CallExpression);for(let i of o)try{let s=i.getExpression().getText();if(s==="useSWR"||s==="useSWRImmutable"){let r=this.extractSwrCall(i,t);r&&n.push(r);}}catch{}return n}extractFetchCall(e,t){let n=e.getArguments();if(n.length===0)return null;let o=n[0].getText(),i=this.extractUrlFromArg(o);if(!i.url||!i.isPlaceholder&&!this.isApiUrl(i.url))return null;let a="GET",s=false;if(n.length>1){let l=n[1].getText(),g=l.match(/method:\s*["'](\w+)["']/i);g&&(a=this.normalizeMethod(g[1])),s=l.includes("credentials")||l.includes("Authorization")||l.includes("withCredentials");}let r=this.getContainingFunctionName(e),c=e.getStartLineNumber();return {id:`api-${++this.apiCallCounter}`,method:a,url:i.url,callType:"fetch",filePath:t,line:c,containingFunction:r,usedIn:[],requiresAuth:s,category:this.categorizeApi(i.url)}}extractAxiosCall(e,t,n){let o=e.getArguments();if(o.length===0)return null;let i=o[0].getText(),a=this.extractUrlFromArg(i);if(!a.url)return null;let s=false;if(o.length>1){let l=o[o.length-1].getText();s=l.includes("withCredentials")||l.includes("Authorization");}let r=this.getContainingFunctionName(e),c=e.getStartLineNumber();return {id:`api-${++this.apiCallCounter}`,method:this.normalizeMethod(n),url:a.url,callType:"axios",filePath:t,line:c,containingFunction:r,usedIn:[],requiresAuth:s,category:this.categorizeApi(a.url)}}extractAxiosDirectCall(e,t){let n=e.getArguments();if(n.length===0)return null;let o=n[0].getText(),i=o.match(/url:\s*["'`]([^"'`]+)["'`]/),a=o.match(/method:\s*["'](\w+)["']/i);if(!i)return null;let s=i[1],r=a?this.normalizeMethod(a[1]):"GET",c=o.includes("withCredentials")||o.includes("Authorization"),l=this.getContainingFunctionName(e),g=e.getStartLineNumber();return {id:`api-${++this.apiCallCounter}`,method:r,url:s,callType:"axios",filePath:t,line:g,containingFunction:l,usedIn:[],requiresAuth:c,category:this.categorizeApi(s)}}extractSwrCall(e,t){let n=e.getArguments();if(n.length===0)return null;let o=n[0].getText(),i=null;if(o.startsWith('"')||o.startsWith("'")||o.startsWith("`"))i=this.cleanStringLiteral(o);else if(o.includes("?")&&o.includes(":")){let r=o.match(/\?\s*["'`]([^"'`]+)["'`]/);r?i=r[1]:(r=o.match(/:\s*["'`]([^"'`]+)["'`]/),r&&(i=r[1])),i||(r=o.match(/\?\s*`([^`]+)`/),r&&(i=r[1].replace(/\$\{[^}]+\}/g,":param")));}else {let r=this.extractUrlFromArg(o);r.url&&!o.includes("null")&&!o.includes("undefined")&&(i=r.url);}if(!i)return null;let a=this.getContainingFunctionName(e),s=e.getStartLineNumber();return {id:`api-${++this.apiCallCounter}`,method:"GET",url:i,callType:"useSWR",filePath:t,line:s,containingFunction:a,usedIn:[],requiresAuth:false,category:this.categorizeApi(i)}}getContainingFunctionName(e){let t=e;for(;t;){if(Node.isFunctionDeclaration(t))return t.getName()||"anonymous";if(Node.isVariableDeclaration(t)||Node.isMethodDeclaration(t))return t.getName();if(Node.isArrowFunction(t)){let n=t.getParent();if(n&&Node.isVariableDeclaration(n))return n.getName()}t=t.getParent();}return "unknown"}extractUrlFromArg(e){if(/^["'`]/.test(e))return {url:this.cleanStringLiteral(e),isPlaceholder:false};let t=e.match(/^(\w+)\s*\(\s*["'`]([^"'`]+)["'`]/);if(t)return {url:`[${t[1]}] ${t[2]}`,isPlaceholder:true};let n=e.match(/^(\w+)\s*\(\s*`([^`]+)`/);if(n){let o=n[2].replace(/\$\{[^}]+\}/g,":param");return {url:`[${n[1]}] ${o}`,isPlaceholder:true}}return /^\w+(\.\w+)*$/.test(e)?{url:`[${e}]`,isPlaceholder:true}:e.includes(".")?{url:`[${e}]`,isPlaceholder:true}:{url:null,isPlaceholder:false}}cleanStringLiteral(e){let t=e.replace(/^["'`]|["'`]$/g,"").trim();return t.includes("${")?t.replace(/\$\{[^}]+\}/g,":param"):t||null}isApiUrl(e){return e.startsWith("data:")||e.startsWith("blob:")||/\.(css|js|png|jpg|jpeg|gif|svg|ico|woff|woff2|ttf|eot|html)$/i.test(e)?false:e.startsWith("/")||e.startsWith("http")||e.includes("/api/")||e.includes(".json")||e.includes("api.")||e.includes("github.io")||e.includes("hsforms.com")||e.includes("hubspot")||e.includes("amazonaws.com")||e.includes("s3.")||e.includes("googleapis.com")||e.includes("stripe.com")||e.includes("graph.facebook.com")||e.includes("api.twitter.com")||e.includes("slack.com")||e.includes("discord.com")||e.includes("sendgrid.com")||e.includes("twilio.com")||e.includes("firebase")||e.includes("supabase")||e.includes("auth0.com")||e.includes("okta.com")||e.includes("cloudflare.com")||e.includes("vercel.com")||e.includes("netlify.com")}categorizeApi(e){if(e.includes("hsforms.com")||e.includes("hubspot"))return "HubSpot";if(e.includes("amazonaws.com")||e.includes("s3."))return "AWS S3";if(e.includes("googleapis.com"))return "Google API";if(e.includes("stripe.com"))return "Stripe";if(e.includes("graph.facebook.com"))return "Facebook";if(e.includes("api.twitter.com"))return "Twitter";if(e.includes("slack.com"))return "Slack";if(e.includes("discord.com"))return "Discord";if(e.includes("sendgrid.com"))return "SendGrid";if(e.includes("twilio.com"))return "Twilio";if(e.includes("firebase"))return "Firebase";if(e.includes("supabase"))return "Supabase";if(e.includes("auth0.com"))return "Auth0";if(e.includes("okta.com"))return "Okta";if(e.includes("github.io"))return "GitHub Pages API";if(e.startsWith("/api/"))return "Internal API";if(e.startsWith("/"))return "Internal Route";if(e.startsWith("["))return "Dynamic URL"}normalizeMethod(e){let t=e.toUpperCase();return ["GET","POST","PUT","DELETE","PATCH"].includes(t)?t:"unknown"}};export{w as a,j as b,G as c,K as d,H as e};
|
|
@@ -1,36 +1,36 @@
|
|
|
1
|
-
import*as
|
|
1
|
+
import*as G from'fs';import*as x from'path';import {Parser,Language}from'web-tree-sitter';import {fileURLToPath}from'url';import {createRequire}from'module';import {glob}from'glob';import*as A from'fs/promises';var ue=createRequire(import.meta.url);var me=fileURLToPath(import.meta.url);x.dirname(me);var te=false,O=null,D=null;async function se(){if(D&&O)return D;te||(await Parser.init(),te=true),D=new Parser;let l=null;try{let e=ue.resolve("tree-sitter-wasms/package.json");l=x.join(x.dirname(e),"out/tree-sitter-ruby.wasm");}catch{let e=x.join(process.cwd(),"node_modules/tree-sitter-wasms/out/tree-sitter-ruby.wasm");G.existsSync(e)&&(l=e);}if(!l||!G.existsSync(l))throw new Error("tree-sitter-ruby.wasm not found. Please ensure tree-sitter-wasms package is installed.");return O=await Language.load(l),D.setLanguage(O),D}async function ne(l){let t=(await se()).parse(l);if(!t)throw new Error("Failed to parse Ruby code");return t}async function N(l){let e=G.readFileSync(l,"utf-8");return ne(e)}function R(l,e){let t=[];l.type===e&&t.push(l);for(let o=0;o<l.childCount;o++){let s=l.child(o);s&&t.push(...R(s,e));}return t}function L(l,e){for(let t=0;t<l.childCount;t++){let o=l.child(t);if(o&&o.type===e)return o}return null}function w(l,e){let t=[];for(let o=0;o<l.childCount;o++){let s=l.child(o);s&&s.type===e&&t.push(s);}return t}function S(l){let e=l.childForFieldName("arguments");if(!e)return [];let t=[];for(let o=0;o<e.childCount;o++){let s=e.child(o);s&&s.type!=="("&&s.type!==")"&&s.type!==","&&t.push(s);}return t}function F(l){let e=l.childForFieldName("name");return e?e.text:null}function M(l){let e=l.childForFieldName("superclass");if(!e)return null;let t=L(e,"constant")||L(e,"scope_resolution");return t?t.text:null}function U(l){let e=l.childForFieldName("name");return e?e.text:null}function Z(l){let e=l.childForFieldName("parameters");if(!e)return [];let t=[];for(let o=0;o<e.childCount;o++){let s=e.child(o);if(s&&(s.type==="identifier"||s.type==="keyword_parameter"||s.type==="optional_parameter"||s.type==="splat_parameter")){let n=s.childForFieldName("name")||s;n.type==="identifier"?t.push(n.text):t.push(s.text);}}return t}var k=class{constructor(e){this.rootPath=e;this.routesDir=x.join(e,"config","routes");}routesDir;routes=[];namespaces=[];resources=[];mountedEngines=[];drawnFiles=[];errors=[];async analyze(){let e=x.join(this.rootPath,"config","routes.rb");if(!G.existsSync(e))return {routes:[],namespaces:[],resources:[],mountedEngines:[],drawnFiles:[],errors:[`routes.rb not found at ${e}`]};try{await this.parseRoutesFile(e,[]);}catch(t){this.errors.push(`Error parsing ${e}: ${t}`);}return {routes:this.routes,namespaces:[...new Set(this.namespaces)],resources:this.resources,mountedEngines:this.mountedEngines,drawnFiles:this.drawnFiles,errors:this.errors}}async parseRoutesFile(e,t){let s=(await N(e)).rootNode,n=R(s,"call");for(let r of n){let a=r.childForFieldName("method");if(!a)continue;let c=a.text,i=r.startPosition.row+1;switch(c){case "get":case "post":case "put":case "patch":case "delete":case "match":this.parseHttpRoute(r,c,t,i);break;case "resources":case "resource":await this.parseResources(r,t,i,c==="resource");break;case "namespace":await this.parseNamespace(r,t,e);break;case "mount":this.parseMount(r,i);break;case "draw":await this.parseDraw(r,t);break;case "devise_for":this.parseDeviseFor(r,t,i);break;case "root":this.parseRoot(r,t,i);break}}}parseHttpRoute(e,t,o,s){let n=S(e);if(n.length===0)return;let r=n[0],a=this.extractStringValue(r);if(!a)return;let c="",i="";for(let u of n)if(u.type==="hash"||u.type==="pair"){let m=u.type==="hash"?w(u,"pair"):[u];for(let d of m){let y=d.child(0)?.text?.replace(/^:/,""),f=d.child(2);if(y==="to"&&f){let g=this.extractStringValue(f);g&&g.includes("#")&&([c,i]=g.split("#"));}}}else if(u.type==="string"||u.type==="string_content"){let m=this.extractStringValue(u);m&&m.includes("#")&&!c&&([c,i]=m.split("#"));}if(!c&&!i){let u=a.replace(/^\//,"").split("/");c=u[0]||"",i=u[1]||"index";}o.length>0&&c&&!c.includes("/")&&(c=`${o.join("/")}/${c}`);let p=this.buildPath(o,a);this.routes.push({method:t==="match"?"ALL":t.toUpperCase(),path:p,controller:c,action:i,namespace:o.join("/")||void 0,line:s});}async parseResources(e,t,o,s){let n=S(e);if(n.length===0)return;let a=n[0].text.replace(/^:/,""),c={name:a,controller:t.length>0?`${t.join("/")}/${a}`:a,nested:[],memberRoutes:[],collectionRoutes:[],line:o};for(let p of n)if(p.type==="hash"){let u=w(p,"pair");for(let m of u){let d=m.child(0)?.text?.replace(/^:/,""),y=m.child(2);d==="only"&&y?c.only=this.extractArrayValues(y):d==="except"&&y&&(c.except=this.extractArrayValues(y));}}this.generateResourceRoutes(c,t,s),this.resources.push(c);let i=e.childForFieldName("block");if(i){let p=R(i,"call");for(let u of p){let m=u.childForFieldName("method")?.text;if(m==="member"){let d=u.childForFieldName("block");d&&this.parseMemberCollectionRoutes(d,c,t,"member");}else if(m==="collection"){let d=u.childForFieldName("block");d&&this.parseMemberCollectionRoutes(d,c,t,"collection");}}}}parseMemberCollectionRoutes(e,t,o,s){let n=R(e,"call");for(let r of n){let a=r.childForFieldName("method");if(!a)continue;let c=a.text;if(!["get","post","put","patch","delete"].includes(c))continue;let i=S(r);if(i.length===0)continue;let p=i[0].text.replace(/^:/,""),u=s==="member"?`/${t.name}/:id/${p}`:`/${t.name}/${p}`,m={method:c.toUpperCase(),path:this.buildPath(o,u),controller:t.controller,action:p,namespace:o.join("/")||void 0,line:r.startPosition.row+1};s==="member"?t.memberRoutes.push(m):t.collectionRoutes.push(m),this.routes.push(m);}}async parseNamespace(e,t,o){let s=S(e);if(s.length===0)return;let n=s[0].text.replace(/^:/,"");this.namespaces.push(n);let r=[...t,n],a=e.childForFieldName("block");if(a){let c=R(a,"call");for(let i of c){let p=i.childForFieldName("method");if(!p)continue;let u=p.text,m=i.startPosition.row+1;switch(u){case "get":case "post":case "put":case "patch":case "delete":case "match":this.parseHttpRoute(i,u,r,m);break;case "resources":case "resource":await this.parseResources(i,r,m,u==="resource");break;case "draw":await this.parseDraw(i,r);break}}}}parseMount(e,t){let o=S(e);if(o.length===0)return;let n=o[0].text,r="/";for(let a of o)if(a.type==="hash"||a.type==="pair"){let c=a.type==="hash"?w(a,"pair"):[a];for(let i of c){let p=i.child(0)?.text?.replace(/^:/,""),u=i.child(2);p==="at"&&u&&(r=this.extractStringValue(u)||r);}}else if(a.type==="string"){let c=this.extractStringValue(a);c&&c.startsWith("/")&&(r=c);}this.mountedEngines.push({engine:n,mountPath:r,line:t});}async parseDraw(e,t){let o=S(e);if(o.length===0)return;let s=o[0].text.replace(/^:/,""),n=x.join(this.routesDir,`${s}.rb`);if(G.existsSync(n)){this.drawnFiles.push(n);try{await this.parseRoutesFile(n,t);}catch(r){this.errors.push(`Error parsing drawn file ${n}: ${r}`);}}}parseDeviseFor(e,t,o){let s=S(e);if(s.length===0)return;let n=s[0].text.replace(/^:/,""),r=[{method:"GET",path:`/${n}/sign_in`,action:"new",controller:"devise/sessions"},{method:"POST",path:`/${n}/sign_in`,action:"create",controller:"devise/sessions"},{method:"DELETE",path:`/${n}/sign_out`,action:"destroy",controller:"devise/sessions"},{method:"GET",path:`/${n}/password/new`,action:"new",controller:"devise/passwords"},{method:"POST",path:`/${n}/password`,action:"create",controller:"devise/passwords"},{method:"GET",path:`/${n}/sign_up`,action:"new",controller:"devise/registrations"},{method:"POST",path:`/${n}`,action:"create",controller:"devise/registrations"}];for(let a of r)this.routes.push({method:a.method,path:this.buildPath(t,a.path),controller:a.controller,action:a.action,namespace:t.join("/")||void 0,line:o,authenticated:false});}parseRoot(e,t,o){let s=S(e),n="",r="index";for(let a of s)if(a.type==="string"){let c=this.extractStringValue(a);c&&c.includes("#")&&([n,r]=c.split("#"));}else if(a.type==="hash"||a.type==="pair"){let c=a.type==="hash"?w(a,"pair"):[a];for(let i of c){let p=i.child(0)?.text?.replace(/^:/,""),u=i.child(2);if(p==="to"&&u){let m=this.extractStringValue(u);m&&m.includes("#")&&([n,r]=m.split("#"));}}}n&&this.routes.push({method:"GET",path:this.buildPath(t,"/"),controller:n,action:r,namespace:t.join("/")||void 0,line:o});}generateResourceRoutes(e,t,o){let s=this.buildPath(t,`/${e.name}`),n=o?["show","new","create","edit","update","destroy"]:["index","show","new","create","edit","update","destroy"],r=e.only||(e.except?n.filter(i=>!e.except.includes(i)):n),a=[];o||r.includes("index")&&a.push({method:"GET",path:s,action:"index"}),r.includes("new")&&a.push({method:"GET",path:`${s}/new`,action:"new"}),r.includes("create")&&a.push({method:"POST",path:s,action:"create"});let c=o?s:`${s}/:id`;r.includes("show")&&a.push({method:"GET",path:c,action:"show"}),r.includes("edit")&&a.push({method:"GET",path:`${c}/edit`,action:"edit"}),r.includes("update")&&(a.push({method:"PUT",path:c,action:"update"}),a.push({method:"PATCH",path:c,action:"update"})),r.includes("destroy")&&a.push({method:"DELETE",path:c,action:"destroy"});for(let i of a)this.routes.push({method:i.method,path:i.path,controller:e.controller,action:i.action,namespace:t.join("/")||void 0,line:e.line});}buildPath(e,t){return t.startsWith("/")?t:`${e.length>0?`/${e.join("/")}`:""}/${t}`}extractStringValue(e){if(e.type==="string"){let t=L(e,"string_content");return t?t.text:e.text.replace(/^["']|["']$/g,"")}return e.type==="string_content"?e.text:e.type==="simple_symbol"||e.type==="symbol"?e.text.replace(/^:/,""):e.text.replace(/^["']|["']$/g,"")}extractArrayValues(e){let t=[];if(e.type==="array")for(let o=0;o<e.childCount;o++){let s=e.child(o);if(s&&s.type!=="["&&s.type!=="]"&&s.type!==","){let n=this.extractStringValue(s);n&&t.push(n);}}else {let o=this.extractStringValue(e);o&&t.push(o);}return t}};async function de(){let l=process.argv[2]||process.cwd();console.log(`Analyzing routes in: ${l}`);let t=await new k(l).analyze();if(console.log(`
|
|
2
2
|
=== Rails Routes Analysis ===
|
|
3
|
-
`),console.log(`Total routes: ${
|
|
4
|
-
--- Errors ---`);for(let o of
|
|
5
|
-
--- Sample Routes (first 30) ---`);for(let o of
|
|
6
|
-
--- Mounted Engines ---`);for(let o of
|
|
7
|
-
--- External Route Files ---`);for(let o of
|
|
3
|
+
`),console.log(`Total routes: ${t.routes.length}`),console.log(`Namespaces: ${t.namespaces.join(", ")||"(none)"}`),console.log(`Resources: ${t.resources.length}`),console.log(`Mounted engines: ${t.mountedEngines.length}`),console.log(`External route files: ${t.drawnFiles.length}`),t.errors.length>0){console.log(`
|
|
4
|
+
--- Errors ---`);for(let o of t.errors)console.log(` \u274C ${o}`);}console.log(`
|
|
5
|
+
--- Sample Routes (first 30) ---`);for(let o of t.routes.slice(0,30))console.log(` ${o.method.padEnd(7)} ${o.path.padEnd(50)} => ${o.controller}#${o.action}`);console.log(`
|
|
6
|
+
--- Mounted Engines ---`);for(let o of t.mountedEngines)console.log(` ${o.engine} => ${o.mountPath}`);console.log(`
|
|
7
|
+
--- External Route Files ---`);for(let o of t.drawnFiles)console.log(` ${x.basename(o)}`);}var fe=import.meta.url===`file://${process.argv[1]}`;fe&&de().catch(console.error);var V=class{constructor(e){this.rootPath=e;this.controllersDir=x.join(e,"app","controllers");}controllersDir;controllers=[];errors=[];async analyze(){if(!G.existsSync(this.controllersDir))return {controllers:[],totalActions:0,namespaces:[],concerns:[],errors:[`Controllers directory not found at ${this.controllersDir}`]};let e=await glob("**/*_controller.rb",{cwd:this.controllersDir,ignore:["concerns/**"]});for(let n of e){let r=x.join(this.controllersDir,n);try{let a=await this.parseControllerFile(r,n);a&&this.controllers.push(a);}catch(a){this.errors.push(`Error parsing ${n}: ${a}`);}}let t=[...new Set(this.controllers.filter(n=>n.namespace).map(n=>n.namespace))],o=[...new Set(this.controllers.flatMap(n=>n.concerns))],s=this.controllers.reduce((n,r)=>n+r.actions.length,0);return {controllers:this.controllers,totalActions:s,namespaces:t,concerns:o,errors:this.errors}}async parseControllerFile(e,t){let s=(await N(e)).rootNode,n=t.replace(/_controller\.rb$/,"").split("/"),r=n.length>1?n.slice(0,-1).join("/"):void 0,a=n[n.length-1],c=R(s,"class");if(c.length===0)return null;let i=c[0],p=F(i),u=M(i);if(!p)return null;let m={name:a,filePath:t,className:p,parentClass:u||"ApplicationController",namespace:r,actions:[],beforeActions:[],afterActions:[],aroundActions:[],skipBeforeActions:[],concerns:[],helpers:[],rescueFrom:[],line:i.startPosition.row+1},d=R(i,"call");for(let h of d){let b=h.childForFieldName("method");if(!b)continue;let v=b.text,C=h.startPosition.row+1;switch(v){case "before_action":case "before_filter":this.parseFilter(h,m.beforeActions,C);break;case "after_action":case "after_filter":this.parseFilter(h,m.afterActions,C);break;case "around_action":case "around_filter":this.parseFilter(h,m.aroundActions,C);break;case "skip_before_action":case "skip_before_filter":this.parseFilter(h,m.skipBeforeActions,C);break;case "include":this.parseInclude(h,m.concerns);break;case "helper":this.parseHelper(h,m.helpers);break;case "layout":m.layoutInfo=this.parseLayout(h);break;case "rescue_from":this.parseRescueFrom(h,m.rescueFrom,C);break}}R(i,"method");let f="public",g=i.childForFieldName("body");if(g)for(let h=0;h<g.childCount;h++){let b=g.child(h);if(b){if(b.type==="identifier"){let v=b.text;v==="private"?f="private":v==="protected"?f="protected":v==="public"&&(f="public");}else if(b.type==="method"){let v=this.parseMethod(b,f);v&&m.actions.push(v);}}}return m}parseFilter(e,t,o){let s=this.getCallArguments(e);if(s.length===0)return;let a={name:s[0].text.replace(/^:/,""),line:o};for(let c of s.slice(1))if(c.type==="hash"){let i=w(c,"pair");for(let p of i){let u=p.child(0)?.text?.replace(/^:/,""),m=p.child(2);if(!(!u||!m))switch(u){case "only":a.only=this.extractArrayValues(m);break;case "except":a.except=this.extractArrayValues(m);break;case "if":a.if=m.text;break;case "unless":a.unless=m.text;break}}}t.push(a);}parseInclude(e,t){let o=this.getCallArguments(e);for(let s of o)(s.type==="constant"||s.type==="scope_resolution")&&t.push(s.text);}parseHelper(e,t){let o=this.getCallArguments(e);for(let s of o){let n=s.text.replace(/^:/,"");t.push(n);}}parseLayout(e){let t=this.getCallArguments(e);if(t.length===0)return;let o=t[0],s=o.text.replace(/^["']|["']$/g,"");s.startsWith(":")&&(s=s.substring(1)),(o.type==="lambda"||o.type==="proc")&&(s="(dynamic)");let n={name:s};for(let r of t.slice(1))r.type==="hash"&&(n.conditions=r.text);return n}parseRescueFrom(e,t,o){let s=this.getCallArguments(e);if(s.length===0)return;let n=s[0].text,r="unknown";for(let a of s.slice(1))if(a.type==="hash"||a.type==="pair"){let c=a.type==="hash"?w(a,"pair"):[a];for(let i of c){let p=i.child(0)?.text?.replace(/^:/,""),u=i.child(2);p==="with"&&u&&(r=u.text.replace(/^:/,""));}}t.push({exception:n,handler:r,line:o});}parseMethod(e,t){let o=U(e);if(!o||e.text.includes("def self."))return null;let s={name:o,line:e.startPosition.row+1,visibility:t,parameters:Z(e),servicesCalled:[],modelsCalled:[],methodCalls:[],instanceVarAssignments:[]},n=e.text,r=/@([a-z_][a-z0-9_]*)\s*=\s*([^\n]+)/gi,a;for(;(a=r.exec(n))!==null;){let p=a[1],u=a[2].trim().slice(0,100),m,d=u.match(/^([A-Z][a-zA-Z0-9]+)\.(find|find_by|find_by!|where|all|first|last|new|create|create!|build)/);d&&(m=d[1]);let y=u.match(/^@([a-z_]+)\.([a-z_]+)/);y&&!m&&(m=`${y[1]}.${y[2]}`);let f=u.match(/^current_([a-z_]+)/);f&&!m&&(m=f[1].charAt(0).toUpperCase()+f[1].slice(1));let g=u.match(/^([A-Z][a-zA-Z0-9]+Service)\.(call|new|perform)/);g&&!m&&(m=`Service:${g[1]}`),s.instanceVarAssignments&&s.instanceVarAssignments.push({name:p,assignedType:m,assignedValue:u.length>60?u.slice(0,57)+"...":u});}(n.includes("render json:")||n.includes("render :json"))&&(s.rendersJson=true),n.includes("render")&&!s.rendersJson&&(s.rendersHtml=true);let c=n.match(/redirect_to\s+([^,\n]+)/);if(c&&(s.redirectsTo=c[1].trim()),n.includes("respond_to")){let p=[];n.includes("format.html")&&p.push("html"),n.includes("format.json")&&p.push("json"),n.includes("format.xml")&&p.push("xml"),n.includes("format.js")&&p.push("js"),n.includes("format.csv")&&p.push("csv"),n.includes("format.pdf")&&p.push("pdf"),p.length>0&&(s.respondsTo=p);}let i=R(e,"call");for(let p of i){let u=p.childForFieldName("receiver"),m=p.childForFieldName("method");if(u&&m){let d=u.text,y=m.text;d.endsWith("Service")&&["call","new","perform","execute"].includes(y)&&(s.servicesCalled.includes(d)||s.servicesCalled.push(d));let f=["find","find_by","find_by!","where","all","first","last","create","create!","new","update","update!","destroy","delete"];/^[A-Z][a-zA-Z]+$/.test(d)&&f.includes(y)&&(["Rails","ActiveRecord","ActionController","ApplicationRecord"].includes(d)||s.modelsCalled.includes(d)||s.modelsCalled.push(d)),s.methodCalls.push(`${d}.${y}`);}else m&&!u&&s.methodCalls.push(m.text);}return s}getCallArguments(e){let t=e.childForFieldName("arguments");if(!t){let s=[];for(let n=0;n<e.childCount;n++){let r=e.child(n);r&&!["identifier","(",")",",","call"].includes(r.type)&&r!==e.childForFieldName("method")&&r!==e.childForFieldName("receiver")&&s.push(r);}return s}let o=[];for(let s=0;s<t.childCount;s++){let n=t.child(s);n&&n.type!=="("&&n.type!==")"&&n.type!==","&&o.push(n);}return o}extractArrayValues(e){let t=[];if(e.type==="array")for(let o=0;o<e.childCount;o++){let s=e.child(o);s&&s.type!=="["&&s.type!=="]"&&s.type!==","&&t.push(s.text.replace(/^:/,""));}else t.push(e.text.replace(/^:/,""));return t}};async function ge(){let l=process.argv[2]||process.cwd();console.log(`Analyzing controllers in: ${l}`);let t=await new V(l).analyze();if(console.log(`
|
|
8
8
|
=== Rails Controllers Analysis ===
|
|
9
|
-
`),console.log(`Total controllers: ${
|
|
10
|
-
--- Errors (${
|
|
11
|
-
--- Sample Controllers (first 10) ---`);for(let n of
|
|
12
|
-
\u{1F4C1} ${n.className} (${n.filePath})`),console.log(` Parent: ${n.parentClass}`),console.log(` Actions (${n.actions.length}): ${n.actions.map(r=>r.name).slice(0,5).join(", ")}${n.actions.length>5?"...":""}`),n.beforeActions.length>0&&console.log(` Before: ${n.beforeActions.map(r=>r.name).join(", ")}`),n.concerns.length>0&&console.log(` Concerns: ${n.concerns.join(", ")}`);let o=
|
|
13
|
-
--- Action Visibility Summary ---`),console.log(` Public: ${o.length}`),console.log(` Private: ${s.length}`);}var
|
|
9
|
+
`),console.log(`Total controllers: ${t.controllers.length}`),console.log(`Total actions: ${t.totalActions}`),console.log(`Namespaces: ${t.namespaces.join(", ")||"(none)"}`),console.log(`Shared concerns: ${t.concerns.length}`),t.errors.length>0){console.log(`
|
|
10
|
+
--- Errors (${t.errors.length}) ---`);for(let n of t.errors.slice(0,5))console.log(` \u274C ${n}`);t.errors.length>5&&console.log(` ... and ${t.errors.length-5} more`);}console.log(`
|
|
11
|
+
--- Sample Controllers (first 10) ---`);for(let n of t.controllers.slice(0,10))console.log(`
|
|
12
|
+
\u{1F4C1} ${n.className} (${n.filePath})`),console.log(` Parent: ${n.parentClass}`),console.log(` Actions (${n.actions.length}): ${n.actions.map(r=>r.name).slice(0,5).join(", ")}${n.actions.length>5?"...":""}`),n.beforeActions.length>0&&console.log(` Before: ${n.beforeActions.map(r=>r.name).join(", ")}`),n.concerns.length>0&&console.log(` Concerns: ${n.concerns.join(", ")}`);let o=t.controllers.flatMap(n=>n.actions.filter(r=>r.visibility==="public")),s=t.controllers.flatMap(n=>n.actions.filter(r=>r.visibility==="private"));console.log(`
|
|
13
|
+
--- Action Visibility Summary ---`),console.log(` Public: ${o.length}`),console.log(` Private: ${s.length}`);}var ye=import.meta.url===`file://${process.argv[1]}`;ye&&ge().catch(console.error);var j=class{constructor(e){this.rootPath=e;this.modelsDir=x.join(e,"app","models");}modelsDir;models=[];errors=[];async analyze(){if(!G.existsSync(this.modelsDir))return {models:[],totalAssociations:0,totalValidations:0,concerns:[],namespaces:[],errors:[`Models directory not found at ${this.modelsDir}`]};let e=await glob("**/*.rb",{cwd:this.modelsDir,ignore:["concerns/**","application_record.rb"]});for(let r of e){let a=x.join(this.modelsDir,r);try{let c=await this.parseModelFile(a,r);c&&this.models.push(c);}catch(c){this.errors.push(`Error parsing ${r}: ${c}`);}}let t=[...new Set(this.models.flatMap(r=>r.concerns))],o=[...new Set(this.models.map(r=>{let a=r.filePath.split("/");return a.length>1?a.slice(0,-1).join("/"):null}).filter(r=>r!==null))],s=this.models.reduce((r,a)=>r+a.associations.length,0),n=this.models.reduce((r,a)=>r+a.validations.length,0);return {models:this.models,totalAssociations:s,totalValidations:n,concerns:t,namespaces:o,errors:this.errors}}async parseModelFile(e,t){let s=(await N(e)).rootNode,n=R(s,"class");if(n.length===0)return null;let r=n[0],a=F(r),c=M(r);if(!a)return null;c&&this.isActiveRecordModel(c);let i={name:a.replace(/.*::/,""),filePath:t,className:a,parentClass:c||"ApplicationRecord",associations:[],validations:[],callbacks:[],scopes:[],concerns:[],enums:[],attributes:[],classMethodsCount:0,instanceMethodsCount:0,line:r.startPosition.row+1};i.tableName=this.parseTableName(r);let p=R(r,"call");for(let d of p){let y=d.childForFieldName("method");if(!y)continue;let f=y.text,g=d.startPosition.row+1;["belongs_to","has_one","has_many","has_and_belongs_to_many"].includes(f)?this.parseAssociation(d,f,i.associations,g):f.startsWith("validates")||f==="validate"?this.parseValidation(d,f,i.validations,g):this.isCallback(f)?this.parseCallback(d,f,i.callbacks,g):f==="scope"?this.parseScope(d,i.scopes,g):f==="include"?this.parseInclude(d,i.concerns):f==="enum"?this.parseEnum(d,i.enums,g):f==="attribute"&&this.parseAttribute(d,i.attributes,g);}let u=R(r,"method"),m=R(r,"singleton_method");return i.instanceMethodsCount=u.length,i.classMethodsCount=m.length,i}isActiveRecordModel(e){return ["ApplicationRecord","ActiveRecord::Base","ActiveRecord"].some(o=>e.includes(o))}parseTableName(e){let t=R(e,"call");for(let s of t)if(s.childForFieldName("method")?.text==="table_name="){let r=this.getCallArguments(s);if(r.length>0)return r[0].text.replace(/^["']|["']$/g,"")}let o=R(e,"assignment");for(let s of o)if(s.child(0)?.text?.includes("table_name")){let r=s.child(2);if(r)return r.text.replace(/^["']|["']$/g,"")}}parseAssociation(e,t,o,s){let n=this.getCallArguments(e);if(n.length===0)return;let a=n[0].text.replace(/^:/,""),c={type:t,name:a,line:s};for(let i of n.slice(1))if(i.type==="hash"){let p=w(i,"pair");for(let u of p){let m=u.child(0)?.text?.replace(/^:/,""),d=u.child(2);if(!(!m||!d))switch(m){case "class_name":c.className=d.text.replace(/^["']|["']$/g,"");break;case "foreign_key":c.foreignKey=d.text.replace(/^["']|["']$/g,"").replace(/^:/,"");break;case "through":c.through=d.text.replace(/^:/,"");break;case "polymorphic":c.polymorphic=d.text==="true";break;case "dependent":c.dependent=d.text.replace(/^:/,"");break;case "optional":c.optional=d.text==="true";break}}}o.push(c);}parseValidation(e,t,o,s){let n=this.getCallArguments(e);if(n.length===0)return;let r=[],a={},c=t;for(let i of n)if(i.type==="simple_symbol"||i.type==="symbol")r.push(i.text.replace(/^:/,""));else if(i.type==="hash"){let p=w(i,"pair");for(let u of p){let m=u.child(0)?.text?.replace(/^:/,""),d=u.child(2);m&&d&&(["presence","uniqueness","numericality","length","format","inclusion","exclusion","acceptance","confirmation"].includes(m)&&(c=m),a[m]=d.text);}}(r.length>0||t==="validate")&&o.push({type:c,attributes:r,options:Object.keys(a).length>0?a:void 0,line:s});}isCallback(e){return ["before_validation","after_validation","before_save","around_save","after_save","before_create","around_create","after_create","before_update","around_update","after_update","before_destroy","around_destroy","after_destroy","after_commit","after_rollback","after_initialize","after_find","after_touch"].includes(e)}parseCallback(e,t,o,s){let n=this.getCallArguments(e);if(n.length===0)return;let a=n[0].text.replace(/^:/,""),c={type:t,method:a,line:s};for(let i of n.slice(1))if(i.type==="hash"){let p=w(i,"pair");for(let u of p){let m=u.child(0)?.text?.replace(/^:/,""),d=u.child(2);m&&d&&["if","unless"].includes(m)&&(c.conditions=`${m}: ${d.text}`);}}o.push(c);}parseScope(e,t,o){let s=this.getCallArguments(e);if(s.length===0)return;let r=s[0].text.replace(/^:/,""),a=s.length>1&&(s[1].type==="lambda"||s[1].text.includes("->"));t.push({name:r,lambda:a,line:o});}parseInclude(e,t){let o=this.getCallArguments(e);for(let s of o)(s.type==="constant"||s.type==="scope_resolution")&&t.push(s.text);}parseEnum(e,t,o){let s=this.getCallArguments(e);if(s.length!==0){for(let n of s)if(n.type==="hash"){let r=w(n,"pair");for(let a of r){let c=a.child(0)?.text?.replace(/^:/,""),i=a.child(2);if(c&&i&&i.type==="hash"){let p=[],u=w(i,"pair");for(let m of u){let d=m.child(0)?.text?.replace(/^:/,"");d&&p.push(d);}t.push({name:c,values:p,line:o});}else if(c&&i&&i.type==="array"){let p=[];for(let u=0;u<i.childCount;u++){let m=i.child(u);m&&m.type!=="["&&m.type!=="]"&&m.type!==","&&p.push(m.text.replace(/^:/,""));}t.push({name:c,values:p,line:o});}}}}}parseAttribute(e,t,o){let s=this.getCallArguments(e);if(s.length===0)return;let a={name:s[0].text.replace(/^:/,""),line:o};if(s.length>1){let c=s[1];a.type=c.text.replace(/^:/,"");}for(let c of s)if(c.type==="hash"){let i=w(c,"pair");for(let p of i){let u=p.child(0)?.text?.replace(/^:/,""),m=p.child(2);u==="default"&&m&&(a.default=m.text);}}t.push(a);}getCallArguments(e){let t=e.childForFieldName("arguments");if(!t){let s=[];for(let n=0;n<e.childCount;n++){let r=e.child(n);r&&!["identifier","(",")",",","call"].includes(r.type)&&r!==e.childForFieldName("method")&&r!==e.childForFieldName("receiver")&&s.push(r);}return s}let o=[];for(let s=0;s<t.childCount;s++){let n=t.child(s);n&&n.type!=="("&&n.type!==")"&&n.type!==","&&o.push(n);}return o}};async function xe(){let l=process.argv[2]||process.cwd();console.log(`Analyzing models in: ${l}`);let t=await new j(l).analyze();if(console.log(`
|
|
14
14
|
=== Rails Models Analysis ===
|
|
15
|
-
`),console.log(`Total models: ${
|
|
16
|
-
--- Errors (${
|
|
17
|
-
--- Sample Models (first 15) ---`);for(let a of
|
|
18
|
-
\u{1F4E6} ${a.className} (${a.filePath})`),console.log(` Parent: ${a.parentClass}`),a.associations.length>0){let c=a.associations.slice(0,3).map(i=>`${i.type} :${i.name}`);console.log(` Associations: ${c.join(", ")}${a.associations.length>3?"...":""}`);}if(a.validations.length>0&&console.log(` Validations: ${a.validations.length}`),a.scopes.length>0){let c=a.scopes.slice(0,3).map(i=>i.name);console.log(` Scopes: ${c.join(", ")}${a.scopes.length>3?"...":""}`);}if(a.enums.length>0){let c=a.enums.map(i=>`${i.name}(${i.values.length})`);console.log(` Enums: ${c.join(", ")}`);}a.concerns.length>0&&console.log(` Concerns: ${a.concerns.slice(0,3).join(", ")}${a.concerns.length>3?"...":""}`);}let o=
|
|
19
|
-
--- Association Summary ---`),console.log(` belongs_to: ${s}`),console.log(` has_many: ${n}`),console.log(` has_one: ${r}`);}var
|
|
15
|
+
`),console.log(`Total models: ${t.models.length}`),console.log(`Total associations: ${t.totalAssociations}`),console.log(`Total validations: ${t.totalValidations}`),console.log(`Shared concerns: ${t.concerns.length}`),console.log(`Namespaces: ${t.namespaces.join(", ")||"(none)"}`),t.errors.length>0){console.log(`
|
|
16
|
+
--- Errors (${t.errors.length}) ---`);for(let a of t.errors.slice(0,5))console.log(` \u274C ${a}`);t.errors.length>5&&console.log(` ... and ${t.errors.length-5} more`);}console.log(`
|
|
17
|
+
--- Sample Models (first 15) ---`);for(let a of t.models.slice(0,15)){if(console.log(`
|
|
18
|
+
\u{1F4E6} ${a.className} (${a.filePath})`),console.log(` Parent: ${a.parentClass}`),a.associations.length>0){let c=a.associations.slice(0,3).map(i=>`${i.type} :${i.name}`);console.log(` Associations: ${c.join(", ")}${a.associations.length>3?"...":""}`);}if(a.validations.length>0&&console.log(` Validations: ${a.validations.length}`),a.scopes.length>0){let c=a.scopes.slice(0,3).map(i=>i.name);console.log(` Scopes: ${c.join(", ")}${a.scopes.length>3?"...":""}`);}if(a.enums.length>0){let c=a.enums.map(i=>`${i.name}(${i.values.length})`);console.log(` Enums: ${c.join(", ")}`);}a.concerns.length>0&&console.log(` Concerns: ${a.concerns.slice(0,3).join(", ")}${a.concerns.length>3?"...":""}`);}let o=t.models.flatMap(a=>a.associations),s=o.filter(a=>a.type==="belongs_to").length,n=o.filter(a=>a.type==="has_many").length,r=o.filter(a=>a.type==="has_one").length;console.log(`
|
|
19
|
+
--- Association Summary ---`),console.log(` belongs_to: ${s}`),console.log(` has_many: ${n}`),console.log(` has_one: ${r}`);}var Re=import.meta.url===`file://${process.argv[1]}`;Re&&xe().catch(console.error);var T=class{constructor(e){this.rootPath=e;this.grpcDir=x.join(e,"app","grpc_services");}grpcDir;services=[];errors=[];async analyze(){if(!G.existsSync(this.grpcDir))return {services:[],totalRpcs:0,namespaces:[],errors:[`gRPC services directory not found at ${this.grpcDir}`]};let e=await glob("**/*_grpc_service.rb",{cwd:this.grpcDir});for(let s of e){let n=x.join(this.grpcDir,s);try{let r=await this.parseServiceFile(n,s);r&&this.services.push(r);}catch(r){this.errors.push(`Error parsing ${s}: ${r}`);}}let t=[...new Set(this.services.filter(s=>s.namespace).map(s=>s.namespace))],o=this.services.reduce((s,n)=>s+n.rpcs.length,0);return {services:this.services,totalRpcs:o,namespaces:t,errors:this.errors}}async parseServiceFile(e,t){let s=(await N(e)).rootNode,n=t.replace(/_grpc_service\.rb$/,"").split("/"),r=n.length>1?n.slice(0,-1).join("/"):void 0,a=n[n.length-1],c=R(s,"class");if(c.length===0)return null;let i=c[0],p=F(i),u=M(i);if(!p)return null;let m={name:a,filePath:t,className:p,parentClass:u||"Unknown",namespace:r,rpcs:[],policies:[],serializers:[],concerns:[],line:i.startPosition.row+1};u&&u.match(/(\w+)::Service$/)&&(m.protoService=u.replace("::Service",""));let d=R(i,"call");for(let h of d)if(h.childForFieldName("method")?.text==="include"){let v=this.getCallArguments(h);for(let C of v)(C.type==="constant"||C.type==="scope_resolution")&&m.concerns.push(C.text);}R(i,"method");let f="public",g=i.childForFieldName("body");if(g)for(let h=0;h<g.childCount;h++){let b=g.child(h);if(b){if(b.type==="identifier"){let v=b.text;v==="private"?f="private":v==="protected"?f="protected":v==="public"&&(f="public");}else if(b.type==="method"&&f==="public"){let v=this.parseRpcMethod(b);if(v){let C=R(b,"call");for(let X of C){let B=X.childForFieldName("method")?.text,P=X.childForFieldName("receiver")?.text;(B==="authorize!"||B==="new")&&P?.includes("Policy")&&(m.policies.includes(P)||m.policies.push(P),v.policyMethod="authorize!"),P?.includes("Serializer")&&B==="new"&&(m.serializers.includes(P)||m.serializers.push(P));}m.rpcs.push(v);}}}}return m}parseRpcMethod(e){let t=U(e);if(!t||["initialize","to_s","inspect","call","perform","execute"].includes(t))return null;Z(e);let n=e.text,r={name:t,line:e.startPosition.row+1,streaming:"none",modelsUsed:[],servicesUsed:[]},a=n.match(/@param\s+\[([^\]]+)\]\s+req/);a&&(r.requestType=a[1]);let c=n.match(/@return\s+\[([^\]]+)\]/);c&&(r.responseType=c[1]);let i=n.matchAll(/\b([A-Z][a-zA-Z]+)\.(find|find_by|where|all|first|last|create|joins|includes)\b/g);for(let u of i){let m=u[1];!["Rails","ActiveRecord","GRPC","Visit","Google"].includes(m)&&!r.modelsUsed.includes(m)&&r.modelsUsed.push(m);}let p=n.matchAll(/\b(\w+Service)\.(call|new|perform)\b/g);for(let u of p){let m=u[1];r.servicesUsed.includes(m)||r.servicesUsed.push(m);}return r}getCallArguments(e){let t=e.childForFieldName("arguments");if(!t){let s=[];for(let n=0;n<e.childCount;n++){let r=e.child(n);r&&!["identifier","(",")",",","call"].includes(r.type)&&r!==e.childForFieldName("method")&&r!==e.childForFieldName("receiver")&&s.push(r);}return s}let o=[];for(let s=0;s<t.childCount;s++){let n=t.child(s);n&&n.type!=="("&&n.type!==")"&&n.type!==","&&o.push(n);}return o}};async function we(){let l=process.argv[2]||process.cwd();console.log(`Analyzing gRPC services in: ${l}`);let t=await new T(l).analyze();if(console.log(`
|
|
20
20
|
=== Rails gRPC Services Analysis ===
|
|
21
|
-
`),console.log(`Total services: ${
|
|
22
|
-
--- Errors (${
|
|
23
|
-
--- Sample Services (first 15) ---`);for(let n of
|
|
24
|
-
\u{1F4E1} ${n.className} (${n.filePath})`),console.log(` Proto: ${n.protoService||"unknown"}`),console.log(` RPCs (${n.rpcs.length}): ${n.rpcs.map(r=>r.name).join(", ")}`),n.policies.length>0&&console.log(` Policies: ${n.policies.join(", ")}`),n.serializers.length>0&&console.log(` Serializers: ${n.serializers.join(", ")}`);let o=
|
|
25
|
-
--- RPC Summary ---`),console.log(` Total RPCs: ${o.length}`),console.log(` RPCs using models: ${s.length}`);}var
|
|
26
|
-
`);for(let o of
|
|
27
|
-
`,n)+1||l.length);if(r.match(/^\s*(def|class|module|if|unless|case|while|until|for|begin|do)\b/)&&(
|
|
28
|
-
`,n),n===-1))break}return s}function
|
|
29
|
-
`),n=
|
|
21
|
+
`),console.log(`Total services: ${t.services.length}`),console.log(`Total RPCs: ${t.totalRpcs}`),console.log(`Namespaces: ${t.namespaces.join(", ")||"(none)"}`),t.errors.length>0){console.log(`
|
|
22
|
+
--- Errors (${t.errors.length}) ---`);for(let n of t.errors.slice(0,5))console.log(` \u274C ${n}`);}console.log(`
|
|
23
|
+
--- Sample Services (first 15) ---`);for(let n of t.services.slice(0,15))console.log(`
|
|
24
|
+
\u{1F4E1} ${n.className} (${n.filePath})`),console.log(` Proto: ${n.protoService||"unknown"}`),console.log(` RPCs (${n.rpcs.length}): ${n.rpcs.map(r=>r.name).join(", ")}`),n.policies.length>0&&console.log(` Policies: ${n.policies.join(", ")}`),n.serializers.length>0&&console.log(` Serializers: ${n.serializers.join(", ")}`);let o=t.services.flatMap(n=>n.rpcs),s=o.filter(n=>n.modelsUsed.length>0);console.log(`
|
|
25
|
+
--- RPC Summary ---`),console.log(` Total RPCs: ${o.length}`),console.log(` RPCs using models: ${s.length}`);}var Ae=import.meta.url===`file://${process.argv[1]}`;Ae&&we().catch(console.error);async function K(l){let e=x.join(l,"app/views"),t=x.join(l,"app/controllers");try{await A.access(e);}catch{return {views:[],pages:[],summary:{totalViews:0,totalPages:0,byController:{},byTemplate:{}}}}let o=await glob("**/*.{haml,erb,html.haml,html.erb,yml}",{cwd:e,nodir:true}),s=[],n={},r={};for(let c of o){let i=await Ce(e,c);i&&(s.push(i),n[i.controller]=(n[i.controller]||0)+1,r[i.template]=(r[i.template]||0)+1);}let a=await Fe(t,s,l);return {views:s,pages:a,summary:{totalViews:s.length,totalPages:a.length,byController:n,byTemplate:r}}}async function Ce(l,e){let t=x.join(l,e);try{let o=await A.readFile(t,"utf-8"),s=e.split("/");if(s.some(g=>g.endsWith("_mailer")||g==="layouts"||g==="shared"||g==="devise"))return null;let n=s.pop()||"",r=s.join("/")||"application",a=n.split("."),c=a[0].replace(/^_/,""),i=n.startsWith("_"),p;n.endsWith(".yml")?p="yml":n.endsWith(".haml")?p="haml":n.endsWith(".erb")?p="erb":p="other";let u=a.length>2?a[1]:"html";if(i)return null;let m=[],d=[],y=[],f=[];return p==="yml"?(y=Pe(o),f=[{name:"App",ssr:!0,propsVar:"@yml_data"}]):(m=Ne(o,p),d=Se(o,p),y=_e(o),f=$e(o,p)),{name:c,path:e,controller:r,action:c,format:u,template:p,partials:m,helpers:d,instanceVars:y,reactComponents:f}}catch{return null}}function Ne(l,e){let t=[];if(e==="haml"){let o=l.matchAll(/=\s*render\s+(?:partial:\s*)?['"]([^'"]+)['"]/g);for(let s of o)t.push(s[1]);}else if(e==="erb"){let o=l.matchAll(/<%=?\s*render\s+(?:partial:\s*)?['"]([^'"]+)['"]/g);for(let s of o)t.push(s[1]);}return [...new Set(t)]}function Se(l,e){let t=[],o=/\b(link_to|form_for|form_with|image_tag|content_for|yield|render|t|l|raw|html_safe|simple_form_for)\b/g,s=l.matchAll(o);for(let n of s)t.push(n[1]);return [...new Set(t)]}function $e(l,e){let t=[],o=/render_react_component\s*\(?\s*["']([^"']+)["'](?:,\s*\{[^}]*\})?\s*(?:,\s*ssr:\s*(true|false))?\)?/g,s;for(;(s=o.exec(l))!==null;)t.push({name:s[1],ssr:s[2]==="true"});let n=/data:\s*\{\s*react_component:\s*["']([^"']+)["'](?:,\s*react_component_props:\s*(@?\w+)(?:\.to_json)?)?/g;for(;(s=n.exec(l))!==null;)t.push({name:s[1],propsVar:s[2],ssr:false});let r=/ReactComponent\s+name=["']([^"']+)["']/g;for(;(s=r.exec(l))!==null;)t.push({name:s[1],ssr:false});let a=new Set;return t.filter(c=>a.has(c.name)?false:(a.add(c.name),true))}function Pe(l){let e=[],t=l.split(`
|
|
26
|
+
`);for(let o of t){let s=o.match(/^([a-z_]+):/);s&&!s[1].startsWith("_")&&e.push(s[1]);let n=o.match(/^\s+-\s+(\w+)/);n&&e.push(n[1]);}return [...new Set(e)].slice(0,50)}function _e(l){let e=[],t=l.matchAll(/@(\w+)/g);for(let o of t)["import","media","keyframes","charset"].includes(o[1])||e.push(o[1]);return [...new Set(e)]}async function Fe(l,e,t){let o=[];try{await A.access(l);}catch{return o}let s=await glob("**/*_controller.rb",{cwd:l,nodir:true}),n=await Me(t);for(let r of s){let a=x.join(l,r),c=await A.readFile(a,"utf-8"),i=r.replace(/_controller\.rb$/,"").replace(/\//g,"/"),p=Ie(c,i,r,e,n);o.push(...p);}return o}async function Me(l){let e=new Map;try{let t=x.join(l,".repomap","rails-routes.json"),o=await A.readFile(t,"utf-8"),s=JSON.parse(o);for(let n of s){let r=`${n.controller}#${n.action}`;e.set(r,{path:n.path,method:n.method});}}catch{}return e}function Ie(l,e,t,o,s){let n=[],r=l.split(/\n\s*(private|protected)\b/)[0],a=/def\s+(\w+)/g,c;for(;(c=a.exec(r))!==null;){let i=c[1];["initialize","new","create","update","destroy","index","show","edit"].includes(i)||i.startsWith("set_")||i.startsWith("_");let p=c.index,u=ke(r,p),m=Ve(u,t),d=je(u),y=Te(u),f=ze(u),g=o.find(v=>v.controller===e&&v.action===i),h=`${e}#${i}`,b=s.get(h);n.push({route:b?.path||`/${e}/${i}`,method:b?.method||"GET",controller:e,action:i,view:g,apis:m,services:d,grpcCalls:y,modelAccess:f});}return n}function ke(l,e){let t=0,o=false,s="";for(let n=e;n<l.length;n++){let r=l.slice(n,l.indexOf(`
|
|
27
|
+
`,n)+1||l.length);if(r.match(/^\s*(def|class|module|if|unless|case|while|until|for|begin|do)\b/)&&(t++,o=true),r.match(/^\s*end\b/)&&(t--,o&&t===0)||(s+=r,n=l.indexOf(`
|
|
28
|
+
`,n),n===-1))break}return s}function Ve(l,e){let t=[],o=[/HTTPClient\.(get|post|put|patch|delete)\s*\(\s*['"]([^'"]+)['"]/gi,/RestClient\.(get|post|put|patch|delete)\s*\(\s*['"]([^'"]+)['"]/gi,/Faraday\.(get|post|put|patch|delete)\s*\(\s*['"]([^'"]+)['"]/gi,/Net::HTTP\.(get|post)\s*\(/gi];for(let s of o){let n;for(;(n=s.exec(l))!==null;)t.push({type:"http",name:n[2]||"HTTP call",method:n[1]?.toUpperCase(),source:e});}return t}function je(l){let e=[],t=/(\w+(?:::\w+)*Service)\.(?:call!?|new)/g,o;for(;(o=t.exec(l))!==null;)e.push(o[1]);return [...new Set(e)]}function Te(l){let e=[],t=[/(\w+(?:::\w+)*Grpc(?:::\w+)?)\./g,/Grpc::(\w+(?:::\w+)*)/g,/(\w+GrpcService)\./g];for(let o of t){let s;for(;(s=o.exec(l))!==null;)e.push(s[1]);}return [...new Set(e)]}function ze(l){let e=[],t=/([A-Z][a-zA-Z0-9]+)\.(?:find|where|find_by|first|last|all|create|update|destroy|new)/g,o;for(;(o=t.exec(l))!==null;)["File","Dir","Time","Date","DateTime","JSON","YAML","CSV","Logger"].includes(o[1])||e.push(o[1]);return [...new Set(e)]}var Ee=["app/javascript/packs","app/javascript/entrypoints","app/frontend/entrypoints","app/javascript/application","frontend/assets/javascripts/entries","frontend/entries","app/assets/javascripts/entries","client/entries","src/entries"],De=["app/javascript/components","app/javascript/react","app/javascript/src/components","app/javascript/bundles","app/frontend/components","app/frontend/react","frontend/assets/javascripts/react","frontend/assets/javascripts/components","frontend/src","frontend/src/components","frontend/components","client/components","src/components"],E={dataReactComponent:/react_component:\s*["']([A-Za-z0-9_/]+)["']/g,renderReactComponent:/render_react_component\s*\(?\s*["']([A-Za-z0-9_/]+)["']/g,reactComponent:/<%=?\s*react_component\s*\(\s*["']([A-Za-z0-9_/]+)["']/g,reduxStore:/<%=?\s*redux_store\s*\(\s*["']([A-Za-z0-9_/]+)["']/g,dataComponent:/data-component\s*[=:]\s*["']([A-Za-z0-9_/]+)["']/g,dataReactClass:/data-react-class\s*[=:]\s*["']([A-Za-z0-9_/]+)["']/g};async function Q(l){let e=new Map,t=[],o=await Ge(l);console.log(` \u{1F4C2} Detected paths: ${o.entryDirs.length} entry dirs, ${o.componentDirs.length} component dirs (${o.integrationPattern})`);for(let a of o.entryDirs){let c=x.join(l,a);try{await A.access(c);let i=await glob("**/*.{tsx,ts,jsx,js}",{cwd:c,nodir:!0,ignore:["**/*.d.ts","**/*.test.*","**/*.spec.*"]});for(let p of i){let u=await Ze(x.join(c,p),p,a);if(u&&(t.push(u),u.componentName)){let m=e.get(u.componentName);m?(m.entryFile=x.join(a,p),m.importPath=u.imports[0]):e.set(u.componentName,{name:u.componentName,entryFile:x.join(a,p),importPath:u.imports[0],ssr:!1,usedIn:[]});}}}catch{}}let s=x.join(l,"app/views");try{await A.access(s);let a=await glob("**/*.{haml,erb,html.haml,html.erb,slim}",{cwd:s,nodir:!0});for(let c of a){let i=await Be(x.join(s,c),c);for(let p of i){let u=e.get(p.componentName);u?(u.usedIn.push({viewPath:c,controller:p.controller,action:p.action,propsVar:p.propsVar,line:p.line,pattern:p.pattern}),p.ssr&&(u.ssr=!0)):e.set(p.componentName,{name:p.componentName,ssr:p.ssr,usedIn:[{viewPath:c,controller:p.controller,action:p.action,propsVar:p.propsVar,line:p.line,pattern:p.pattern}]});}}}catch{}await Oe(l,e,o.componentDirs);let n=Array.from(e.values()),r=n.filter(a=>a.ssr).length;return {components:n,entryPoints:t,detectedPaths:o,summary:{totalComponents:n.length,totalEntryPoints:t.length,ssrComponents:r,clientComponents:n.length-r}}}async function Ge(l){let e=[],t=[],o="unknown";for(let s of Ee){let n=x.join(l,s);try{(await A.stat(n)).isDirectory()&&e.push(s);}catch{}}for(let s of De){let n=x.join(l,s);try{(await A.stat(n)).isDirectory()&&t.push(s);}catch{}}if(o=await Le(l),e.length===0){let s=await Ue(l);e.push(...s);}return t.length===0&&e.length>0&&t.push(...e),{entryDirs:e,componentDirs:t,integrationPattern:o}}async function Le(l){try{let e=x.join(l,"Gemfile"),t=await A.readFile(e,"utf-8");if(t.includes("react_on_rails"))return "react_on_rails";if(t.includes("react-rails"))return "react-rails";if(t.includes("vite_rails")||t.includes("vite_ruby"))return "vite";if(t.includes("webpacker"))return "webpacker";try{return await A.access(x.join(l,"vite.config.ts")),"vite"}catch{}try{return await A.access(x.join(l,"vite.config.js")),"vite"}catch{}return "custom"}catch{return "unknown"}}async function Ue(l){let e=[],t=["app/**/entries","app/**/packs","frontend/**/entries","client/**/entries","src/**/entries"];for(let o of t)try{let s=await glob(o,{cwd:l,nodir:!1});for(let n of s){let r=x.join(l,n);try{(await A.stat(r)).isDirectory()&&e.push(n);}catch{}}}catch{}return e}async function Ze(l,e,t){try{let o=await A.readFile(l,"utf-8"),s=[/\[data-react-component[=:][\s]*["']?([A-Za-z0-9_]+)["']?\]/,/\[data-component[=:][\s]*["']?([A-Za-z0-9_]+)["']?\]/,/\[data-react-class[=:][\s]*["']?([A-Za-z0-9_]+)["']?\]/,/getElementById\s*\(\s*["']([A-Za-z0-9_-]+)["']\s*\)/,/querySelector\s*\(\s*["']#([A-Za-z0-9_-]+)["']\s*\)/],n=null,r;for(let p of s){let u=o.match(p);if(u){n=u[1],r=u[0];break}}n||(n=x.basename(e,x.extname(e)).split(/[-_]/).map(u=>u.charAt(0).toUpperCase()+u.slice(1)).join(""));let a=[],c=o.matchAll(/import\s+(?:\{[^}]+\}|\*\s+as\s+\w+|\w+)\s+from\s+["']([^"']+)["']/g);for(let p of c){let u=p[1];(u.includes("/react/")||u.includes("/components/")||u.includes("/containers/")||u.includes("/bundles/")||u.includes("/pages/")||u.match(/\/[A-Z][a-zA-Z0-9]*/))&&a.push(u);}let i=o.matchAll(/require\s*\(\s*["']([^"']+)["']\s*\)/g);for(let p of i){let u=p[1];(u.includes("/react/")||u.includes("/components/")||u.includes("/containers/"))&&a.push(u);}return !n&&a.length===0?null:{file:e,fullPath:x.join(t,e),componentName:n||"",imports:a,selector:r}}catch{return null}}async function Be(l,e){let t=[];try{let s=(await A.readFile(l,"utf-8")).split(`
|
|
29
|
+
`),n=e.split("/"),r=n.pop()||"",a=n.join("/")||"application",c=r.split(".")[0].replace(/^_/,""),i=0;for(let p of s){i++;let u=p.matchAll(E.dataReactComponent);for(let h of u){let b=p.match(/react_component_props:\s*(@?\w+(?:\.\w+)*)/);t.push({componentName:h[1],controller:a,action:c,propsVar:b?b[1]:void 0,line:i,pattern:"data-react-component",ssr:!1});}let m=p.match(E.renderReactComponent);if(m){let h=p.match(/ssr:\s*(true|false|@\w+)/);t.push({componentName:m[1],controller:a,action:c,line:i,pattern:"render_react_component",ssr:h?h[1]==="true"||h[1].startsWith("@"):!1});}let d=p.match(E.reactComponent);if(d){let h=p.match(/props:\s*(@?\w+(?:\.\w+)*)/),b=p.match(/prerender:\s*(true|false)/);t.push({componentName:d[1],controller:a,action:c,propsVar:h?h[1]:void 0,line:i,pattern:"react_component",ssr:b?b[1]==="true":!1});}let y=p.match(E.reduxStore);y&&t.push({componentName:y[1],controller:a,action:c,line:i,pattern:"redux_store",ssr:!1});let f=p.match(E.dataComponent);f&&t.push({componentName:f[1],controller:a,action:c,line:i,pattern:"data-react-component",ssr:!1});let g=p.match(E.dataReactClass);g&&t.push({componentName:g[1],controller:a,action:c,line:i,pattern:"data-react-component",ssr:!1});}}catch{}return t}async function Oe(l,e,t){for(let[o,s]of e)if(!(!o||typeof o!="string")){if(s.importPath&&typeof s.importPath=="string"){let n=s.importPath.replace(/\.js$/,"").replace(/\.tsx?$/,"").replace(/^\.\.\//,"").replace(/^\.\//,"");s.sourceFile=n;}else if(!s.sourceFile){let n=[".tsx",".ts",".jsx",".js"],r=[o,We(o),He(o)].filter(Boolean),a=false;for(let c of t){if(a)break;for(let i of r){if(a)break;for(let p of n){let u=[x.join(l,c,i,`index${p}`),x.join(l,c,i,`${i}${p}`),x.join(l,c,`${i}${p}`),x.join(l,c,"components",`${i}${p}`),x.join(l,c,"containers",`${i}${p}`)];for(let m of u)try{await A.access(m),s.sourceFile=x.relative(l,m),a=!0;break}catch{}}}}}}}function We(l){return l?l.replace(/([A-Z])/g,"_$1").toLowerCase().replace(/^_/,""):""}function He(l){return l?l.replace(/([A-Z])/g,"-$1").toLowerCase().replace(/^-/,""):""}async function qe(l){console.log(`
|
|
30
30
|
\u{1F4E6} Analyzing Rails application at: ${l}
|
|
31
|
-
`),console.log("\u{1F504} Analyzing routes...");let
|
|
31
|
+
`),console.log("\u{1F504} Analyzing routes...");let t=await new k(l).analyze();console.log(` \u2705 Found ${t.routes.length} routes`),console.log("\u{1F504} Analyzing controllers...");let s=await new V(l).analyze();console.log(` \u2705 Found ${s.controllers.length} controllers with ${s.totalActions} actions`),console.log("\u{1F504} Analyzing models...");let r=await new j(l).analyze();console.log(` \u2705 Found ${r.models.length} models with ${r.totalAssociations} associations`),console.log("\u{1F504} Analyzing gRPC services...");let c=await new T(l).analyze();console.log(` \u2705 Found ${c.services.length} gRPC services with ${c.totalRpcs} RPCs`),console.log("\u{1F504} Analyzing views...");let i=await K(l);console.log(` \u2705 Found ${i.summary.totalViews} views and ${i.summary.totalPages} pages`),console.log("\u{1F504} Analyzing React components...");let p=await Q(l);console.log(` \u2705 Found ${p.summary.totalComponents} React components (${p.summary.ssrComponents} SSR, ${p.summary.clientComponents} client)`);let u=[...new Set([...t.namespaces,...s.namespaces,...r.namespaces,...c.namespaces])],m=[...new Set([...s.concerns,...r.concerns])],d={totalRoutes:t.routes.length,totalControllers:s.controllers.length,totalActions:s.totalActions,totalModels:r.models.length,totalAssociations:r.totalAssociations,totalValidations:r.totalValidations,totalGrpcServices:c.services.length,totalRpcs:c.totalRpcs,totalViews:i.summary.totalViews,totalPages:i.summary.totalPages,totalReactComponents:p.summary.totalComponents,ssrReactComponents:p.summary.ssrComponents,namespaces:u,concerns:m};return {routes:t,controllers:s,models:r,grpc:c,views:i,react:p,summary:d}}async function Je(){let l=process.argv[2]||process.cwd(),e=await qe(l);console.log(`
|
|
32
32
|
`+"=".repeat(60)),console.log("\u{1F4CA} RAILS APPLICATION ANALYSIS SUMMARY"),console.log("=".repeat(60)+`
|
|
33
|
-
`),console.log("\u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510"),console.log("\u2502 Routes \u2502"),console.log("\u251C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524"),console.log(`\u2502 Total routes: ${String(
|
|
34
|
-
\u26A0\uFE0F Total errors: ${
|
|
35
|
-
\u2705 Analysis completed without errors!`);}var
|
|
36
|
-
export{
|
|
33
|
+
`),console.log("\u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510"),console.log("\u2502 Routes \u2502"),console.log("\u251C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524"),console.log(`\u2502 Total routes: ${String(e.summary.totalRoutes).padStart(6)} \u2502`),console.log(`\u2502 Resources: ${String(e.routes.resources.length).padStart(6)} \u2502`),console.log(`\u2502 Mounted engines: ${String(e.routes.mountedEngines.length).padStart(6)} \u2502`),console.log(`\u2502 External files: ${String(e.routes.drawnFiles.length).padStart(6)} \u2502`),console.log("\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518"),console.log("\u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510"),console.log("\u2502 Controllers \u2502"),console.log("\u251C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524"),console.log(`\u2502 Total controllers: ${String(e.summary.totalControllers).padStart(6)} \u2502`),console.log(`\u2502 Total actions: ${String(e.summary.totalActions).padStart(6)} \u2502`),console.log(`\u2502 Namespaces: ${String(e.controllers.namespaces.length).padStart(6)} \u2502`),console.log("\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518"),console.log("\u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510"),console.log("\u2502 Models \u2502"),console.log("\u251C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524"),console.log(`\u2502 Total models: ${String(e.summary.totalModels).padStart(6)} \u2502`),console.log(`\u2502 Associations: ${String(e.summary.totalAssociations).padStart(6)} \u2502`),console.log(`\u2502 Validations: ${String(e.summary.totalValidations).padStart(6)} \u2502`),console.log("\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518"),console.log("\u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510"),console.log("\u2502 gRPC Services \u2502"),console.log("\u251C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524"),console.log(`\u2502 Total services: ${String(e.summary.totalGrpcServices).padStart(6)} \u2502`),console.log(`\u2502 Total RPCs: ${String(e.summary.totalRpcs).padStart(6)} \u2502`),console.log("\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518"),console.log("\u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510"),console.log("\u2502 Shared \u2502"),console.log("\u251C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524"),console.log(`\u2502 Total namespaces: ${String(e.summary.namespaces.length).padStart(6)} \u2502`),console.log(`\u2502 Total concerns: ${String(e.summary.concerns.length).padStart(6)} \u2502`),console.log("\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518");let t=e.routes.errors.length+e.controllers.errors.length+e.models.errors.length+e.grpc.errors.length;t>0?console.log(`
|
|
34
|
+
\u26A0\uFE0F Total errors: ${t}`):console.log(`
|
|
35
|
+
\u2705 Analysis completed without errors!`);}var Ke=import.meta.url===`file://${process.argv[1]}`;Ke&&Je().catch(console.error);
|
|
36
|
+
export{se as a,ne as b,N as c,R as d,k as e,V as f,j as g,T as h,K as i,Q as j,qe as k};
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import {a as a$2}from'./chunk-
|
|
2
|
-
`);let
|
|
3
|
-
\u{1F4E6} Analyzing ${i.displayName}...`);let r=await this.analyzeRepository(i);
|
|
4
|
-
\u{1F517} Running cross-repository analysis...`);let
|
|
5
|
-
\u{1F4CA} Generating diagrams...`);let e=
|
|
6
|
-
\u{1F4DD} Writing documentation...`),await this.writeDocumentation(
|
|
7
|
-
\u2728 Documentation generation complete!`),console.log(`\u{1F4C1} Output: ${this.config.outputDir}`),
|
|
1
|
+
import {a as a$2}from'./chunk-H4YGP3GL.js';import {a as a$3}from'./chunk-VV3A3UE3.js';import {e,d as d$1,c,b as b$2}from'./chunk-IAO54U6X.js';import {a,b as b$1}from'./chunk-XWZH2RDG.js';import {a as a$1}from'./chunk-MOEA75XK.js';import {k}from'./chunk-PTR5IROV.js';import {simpleGit}from'simple-git';import*as d from'fs/promises';import*as m from'path';import H from'fast-glob';import*as w from'crypto';import F from'express';import {Server}from'socket.io';import*as z from'http';import {marked}from'marked';import*as P from'net';var C="1.1",x=class{cacheDir;manifest;manifestPath;dirty=false;constructor(a){this.cacheDir=m.join(a,".repomap-cache"),this.manifestPath=m.join(this.cacheDir,"manifest.json"),this.manifest={version:C,entries:{}};}async init(){try{await d.mkdir(this.cacheDir,{recursive:!0});}catch(a){console.warn(` Warning: Could not create cache directory: ${a.message}`);return}try{let a=await d.readFile(this.manifestPath,"utf-8"),t=JSON.parse(a);t.version===C?this.manifest=t:(console.log(" Cache version mismatch, clearing cache..."),await this.clear());}catch{}}async computeFileHash(a){try{let t=await d.readFile(a,"utf-8");return w.createHash("md5").update(t).digest("hex")}catch{return ""}}async computeFilesHash(a){let t=[...a].sort(),e=50,o;t.length<=e*2?o=t:o=[...t.slice(0,e),...t.slice(-e)];let s=await Promise.all(o.map(i=>this.computeFileHash(i))),n=w.createHash("md5").update(String(t.length)).digest("hex");return w.createHash("md5").update(s.join("")+n).digest("hex")}get(a,t){let e=this.manifest.entries[a];return e&&e.hash===t?e.data:null}set(a,t,e){this.manifest.entries[a]={hash:t,timestamp:Date.now(),data:e},this.dirty=true;}async save(){if(this.dirty)try{await d.mkdir(this.cacheDir,{recursive:!0}),await d.writeFile(this.manifestPath,JSON.stringify(this.manifest,null,2)),this.dirty=!1;}catch(a){console.warn(" Warning: Failed to save cache:",a.message);}}async clear(){this.manifest={version:C,entries:{}},this.dirty=true;try{await d.rm(this.cacheDir,{recursive:!0,force:!0}),await d.mkdir(this.cacheDir,{recursive:!0});}catch{}}getStats(){let a=Object.keys(this.manifest.entries).length,t=JSON.stringify(this.manifest).length;return {entries:a,size:t>1024*1024?`${(t/1024/1024).toFixed(1)}MB`:`${(t/1024).toFixed(1)}KB`}}};var b=class{config;mermaidGenerator;markdownGenerator;noCache;constructor(a$1,t){this.config=a$1,this.mermaidGenerator=new a,this.markdownGenerator=new b$1,this.noCache=t?.noCache??false;}async generate(){console.log(`\u{1F680} Starting documentation generation...
|
|
2
|
+
`);let a=[];for(let i of this.config.repositories)try{console.log(`
|
|
3
|
+
\u{1F4E6} Analyzing ${i.displayName}...`);let r=await this.analyzeRepository(i);a.push(r),console.log(`\u2705 Completed ${i.displayName}`);}catch(r){console.error(`\u274C Failed to analyze ${i.name}:`,r.message);}console.log(`
|
|
4
|
+
\u{1F517} Running cross-repository analysis...`);let t=this.analyzeCrossRepo(a);console.log(`
|
|
5
|
+
\u{1F4CA} Generating diagrams...`);let e=a.map(i=>i.analysis),o=this.extractCrossRepoLinks(e),s=this.mermaidGenerator.generateAll(e,o),n={generatedAt:new Date().toISOString(),repositories:a,crossRepoAnalysis:t,diagrams:s};return console.log(`
|
|
6
|
+
\u{1F4DD} Writing documentation...`),await this.writeDocumentation(n),console.log(`
|
|
7
|
+
\u2728 Documentation generation complete!`),console.log(`\u{1F4C1} Output: ${this.config.outputDir}`),n}async analyzeRepository(a){let t=new x(a.path);await t.init();let{version:e,commitHash:o}=await this.getRepoInfo(a),s=await H(["**/*.{ts,tsx,graphql}"],{cwd:a.path,ignore:["**/node_modules/**","**/.next/**","**/dist/**","**/build/**"],absolute:true}),n=await t.computeFilesHash(s),i=`analysis_v${e}_${a.name}_${o}`;this.noCache&&console.log(" \u{1F504} Cache disabled, analyzing from scratch...");let r=this.noCache?null:t.get(i,n);if(r){console.log(` \u26A1 Using cached analysis (hash: ${n.slice(0,8)}...)`);let c={totalPages:r.pages.length,totalComponents:r.components.length,totalGraphQLOperations:r.graphqlOperations.length,totalDataFlows:r.dataFlows.length,authRequiredPages:r.pages.filter(q=>q.authentication.required).length,publicPages:r.pages.filter(q=>!q.authentication.required).length};return {name:a.name,displayName:a.displayName,version:e,commitHash:o,analysis:r,summary:c}}let l=a.analyzers.map(c=>this.createAnalyzer(c,a)).filter(c=>c!==null);console.log(` Running ${l.length} analyzers in parallel...`);let h=Date.now(),g=await Promise.all(l.map(c=>c.analyze()));console.log(` Analysis completed in ${((Date.now()-h)/1e3).toFixed(1)}s`);let p=this.mergeAnalysisResults(g,a.name,e,o);t.set(i,n,p),await t.save(),console.log(` \u{1F4BE} Analysis cached (hash: ${n.slice(0,8)}...)`);let B={totalPages:p.pages.length,totalComponents:p.components.length,totalGraphQLOperations:p.graphqlOperations.length,totalDataFlows:p.dataFlows.length,authRequiredPages:p.pages.filter(c=>c.authentication.required).length,publicPages:p.pages.filter(c=>!c.authentication.required).length};return {name:a.name,displayName:a.displayName,version:e,commitHash:o,analysis:p,summary:B}}async getRepoInfo(a){try{let o=(await simpleGit(a.path).log({n:1})).latest?.hash||"unknown",s="unknown";try{let n=m.join(a.path,"package.json");s=JSON.parse(await d.readFile(n,"utf-8")).version||"unknown";}catch{}return {version:s,commitHash:o}}catch{return {version:"unknown",commitHash:"unknown"}}}createAnalyzer(a,t){switch(a){case "pages":if(t.type==="nextjs"||t.type==="rails"||t.type==="generic")return new b$2(t);break;case "graphql":return new c(t);case "dataflow":case "components":return new d$1(t);case "rest-api":case "api":return new e(t)}return null}mergeAnalysisResults(a,t,e,o){let s={repository:t,timestamp:new Date().toISOString(),version:e,commitHash:o,pages:[],graphqlOperations:[],apiCalls:[],components:[],dataFlows:[],apiEndpoints:[],models:[],crossRepoLinks:[]};for(let n of a)n.pages&&s.pages.push(...n.pages),n.graphqlOperations&&s.graphqlOperations.push(...n.graphqlOperations),n.apiCalls&&s.apiCalls.push(...n.apiCalls),n.components&&s.components.push(...n.components),n.dataFlows&&s.dataFlows.push(...n.dataFlows),n.apiEndpoints&&s.apiEndpoints.push(...n.apiEndpoints),n.models&&s.models.push(...n.models),n.crossRepoLinks&&s.crossRepoLinks.push(...n.crossRepoLinks);return s}analyzeCrossRepo(a){let t=[],e=[],o=[],s=[],n=new Map;for(let l of a)for(let h of l.analysis.graphqlOperations){let g=n.get(h.name)||[];g.push(l.name),n.set(h.name,g);}for(let[l,h]of n)h.length>1&&t.push(l);let i=a.filter(l=>l.analysis.pages.length>0),r=a.filter(l=>l.analysis.apiEndpoints.length>0);for(let l of i)for(let h of r)for(let g of h.analysis.apiEndpoints)e.push({frontend:l.name,backend:h.name,endpoint:g.path,operations:l.analysis.graphqlOperations.filter(p=>p.usedIn.length>0).map(p=>p.name)});return {sharedTypes:t,apiConnections:e,navigationFlows:o,dataFlowAcrossRepos:s}}extractCrossRepoLinks(a){let t=[],e=new Map;for(let o of a)for(let s of o.graphqlOperations){let n=e.get(s.name)||[];n.push(o),e.set(s.name,n);}for(let[o,s]of e)s.length>1&&t.push({sourceRepo:s[0].repository,sourcePath:`graphql/${o}`,targetRepo:s[1].repository,targetPath:`graphql/${o}`,linkType:"graphql-operation",description:`Shared GraphQL operation: ${o}`});return t}async writeDocumentation(a){let t=this.config.outputDir;await d.mkdir(t,{recursive:true});let e=this.markdownGenerator.generateDocumentation(a);for(let[s,n]of e){let i=m.join(t,s),r=m.dirname(i);await d.mkdir(r,{recursive:true}),await d.writeFile(i,n,"utf-8"),console.log(` \u{1F4C4} ${s}`);}let o=m.join(t,"report.json");await d.writeFile(o,JSON.stringify(a,null,2),"utf-8"),console.log(" \u{1F4CB} report.json");}};function Q(u){return new Promise(a=>{let t=P.createServer();t.once("error",e=>{e.code,a(false);}),t.once("listening",()=>{t.close(),a(true);}),t.listen(u);})}async function O(u,a=10){for(let t=0;t<a;t++){let e=u+t;if(await Q(e))return e}throw new Error(`No available port found between ${u} and ${u+a-1}`)}var I=class{config;port;app;server;io;engine;currentReport=null;envResult=null;railsAnalysis=null;constructor(a,t=3030,e){this.config=a,this.port=t,this.app=F(),this.server=z.createServer(this.app),this.io=new Server(this.server),this.engine=new b(a,{noCache:e?.noCache}),this.setupRoutes(),this.setupSocketIO();}setupRoutes(){this.app.use("/assets",F.static(m.join(this.config.outputDir,"assets"))),["common.css","page-map.css","docs.css","rails-map.css"].forEach(t=>{this.app.get(`/${t}`,async(e,o)=>{let s=[m.join(m.dirname(new URL(import.meta.url).pathname),"generators","assets",t),m.join(m.dirname(new URL(import.meta.url).pathname),"..","generators","assets",t),m.join(process.cwd(),"dist","generators","assets",t),m.join(process.cwd(),"src","generators","assets",t)];for(let n of s)try{let i=await d.readFile(n,"utf-8");o.type("text/css").send(i);return}catch{}o.status(404).send("CSS not found");});}),this.app.get("/",(t,e)=>{e.redirect("/page-map");}),this.app.get("/page-map",(t,e)=>{if(!this.currentReport){e.status(503).send("Documentation not ready yet");return}let o=new a$1;e.send(o.generatePageMapHtml(this.currentReport,{envResult:this.envResult,railsAnalysis:this.railsAnalysis}));}),this.app.get("/rails-map",(t,e)=>{if(!this.railsAnalysis){e.status(404).send("No Rails environment detected");return}let o=new a$2;e.send(o.generateFromResult(this.railsAnalysis));}),this.app.get("/docs",async(t,e)=>{e.send(await this.renderPage("index"));}),this.app.get("/docs/*path",async(t,e)=>{let o=t.params.path,s=Array.isArray(o)?o.join("/"):o||"index";e.send(await this.renderPage(s));}),this.app.get("/api/report",(t,e)=>{e.json(this.currentReport);}),this.app.get("/api/env",(t,e)=>{e.json(this.envResult);}),this.app.get("/api/rails",(t,e)=>{this.railsAnalysis?e.json(this.railsAnalysis):e.status(404).json({error:"No Rails analysis available"});}),this.app.get("/api/diagram/:name",(t,e)=>{let o=this.currentReport?.diagrams.find(s=>s.title.toLowerCase().replace(/\s+/g,"-")===t.params.name);o?e.json(o):e.status(404).json({error:"Diagram not found"});}),this.app.post("/api/regenerate",async(t,e)=>{try{await this.regenerate(),e.json({success:!0});}catch(o){e.status(500).json({error:o.message});}});}setupSocketIO(){this.io.on("connection",a=>{console.log("Client connected"),a.on("disconnect",()=>{console.log("Client disconnected");});});}async renderPage(a){let t=a.replace(/\.md$/,""),e=m.join(this.config.outputDir,`${t}.md`),o="";try{let s=await d.readFile(e,"utf-8"),n=await marked.parse(s);n=n.replace(/<pre><code class="language-mermaid">([\s\S]*?)<\/code><\/pre>/g,'<div class="mermaid">$1</div>'),n=n.replace(/<table>/g,'<div class="table-wrapper"><table>'),n=n.replace(/<\/table>/g,"</table></div>"),o=n;}catch(s){console.error(`Failed to render page: ${e}`,s),o=`<h1>Page not found</h1><p>Path: ${t}</p>`;}return this.getHtmlTemplate(o)}getGraphQLData(){if(!this.currentReport)return "[]";let a=[];for(let t of this.currentReport.repositories)for(let e of t.analysis?.graphqlOperations||[])a.push({name:e.name,type:e.type,returnType:e.returnType,variables:e.variables,fields:e.fields,usedIn:e.usedIn});return JSON.stringify(a)}getApiCallsData(){if(!this.currentReport)return "[]";let a=[];for(let t of this.currentReport.repositories)for(let e of t.analysis?.apiCalls||[])a.push({id:e.id,method:e.method,url:e.url,callType:e.callType,filePath:e.filePath,line:e.line,containingFunction:e.containingFunction,requiresAuth:e.requiresAuth});return JSON.stringify(a)}getHtmlTemplate(a){let t=this.getGraphQLData(),e=this.getApiCallsData();return `<!DOCTYPE html>
|
|
8
8
|
<html lang="ja">
|
|
9
9
|
<head>
|
|
10
10
|
<meta charset="UTF-8">
|
|
@@ -13,7 +13,7 @@ import {a as a$2}from'./chunk-SL2GMDBN.js';import {a as a$3}from'./chunk-VV3A3UE
|
|
|
13
13
|
<script src="https://cdn.jsdelivr.net/npm/mermaid/dist/mermaid.min.js"></script>
|
|
14
14
|
<script src="/socket.io/socket.io.js"></script>
|
|
15
15
|
<script>
|
|
16
|
-
window.graphqlOps = ${
|
|
16
|
+
window.graphqlOps = ${t};
|
|
17
17
|
window.apiCalls = ${e};
|
|
18
18
|
// Create multiple lookup maps for different naming conventions
|
|
19
19
|
window.gqlMap = new Map();
|
|
@@ -97,11 +97,11 @@ import {a as a$2}from'./chunk-SL2GMDBN.js';import {a as a$3}from'./chunk-VV3A3UE
|
|
|
97
97
|
<div class="nav-group">
|
|
98
98
|
<span class="nav-group-title">Documentation</span>
|
|
99
99
|
<div class="nav-subitems">
|
|
100
|
-
${this.config.repositories.map(
|
|
101
|
-
<a href="/docs/repos/${
|
|
102
|
-
<a href="/docs/repos/${
|
|
103
|
-
<a href="/docs/repos/${
|
|
104
|
-
<a href="/docs/repos/${
|
|
100
|
+
${this.config.repositories.map(o=>`
|
|
101
|
+
<a href="/docs/repos/${o.name}/pages">Pages</a>
|
|
102
|
+
<a href="/docs/repos/${o.name}/components">Components</a>
|
|
103
|
+
<a href="/docs/repos/${o.name}/graphql">GraphQL</a>
|
|
104
|
+
<a href="/docs/repos/${o.name}/dataflow">Data Flow</a>
|
|
105
105
|
`).join("")}
|
|
106
106
|
</div>
|
|
107
107
|
</div>
|
|
@@ -117,7 +117,7 @@ import {a as a$2}from'./chunk-SL2GMDBN.js';import {a as a$3}from'./chunk-VV3A3UE
|
|
|
117
117
|
</aside>
|
|
118
118
|
<div class="content-area">
|
|
119
119
|
<div class="content">
|
|
120
|
-
${
|
|
120
|
+
${a}
|
|
121
121
|
</div>
|
|
122
122
|
</div>
|
|
123
123
|
</div>
|
|
@@ -946,12 +946,12 @@ import {a as a$2}from'./chunk-SL2GMDBN.js';import {a as a$3}from'./chunk-VV3A3UE
|
|
|
946
946
|
}
|
|
947
947
|
</script>
|
|
948
948
|
</body>
|
|
949
|
-
</html>`}async start(
|
|
949
|
+
</html>`}async start(a=true){let t=this.config.repositories[0]?.path||process.cwd();if(console.log("\u{1F50D} Detecting project environments..."),this.envResult=await a$3(t),this.envResult.environments.length>0){console.log(` Found: ${this.envResult.environments.map(e=>e.type).join(", ")}`);for(let e of this.envResult.environments)e.features.length>0&&console.log(` ${e.type} features: ${e.features.join(", ")}`);}if(console.log(`
|
|
950
950
|
\u{1F4DA} Generating documentation...`),this.currentReport=await this.engine.generate(),this.envResult.hasRails){console.log(`
|
|
951
|
-
\u{1F6E4}\uFE0F Analyzing Rails application...`);try{this.railsAnalysis=await k(
|
|
951
|
+
\u{1F6E4}\uFE0F Analyzing Rails application...`);try{this.railsAnalysis=await k(t),console.log(" \u2705 Rails analysis complete");}catch(e){console.error(" \u26A0\uFE0F Rails analysis failed:",e.message);}}try{let e=await O(this.port);e!==this.port&&console.log(`
|
|
952
952
|
\u26A0\uFE0F Port ${this.port} is in use, using port ${e} instead`),this.port=e;}catch(e){console.error(`
|
|
953
953
|
\u274C Failed to find available port: ${e.message}`),process.exit(1);}if(this.server.listen(this.port,()=>{console.log(`
|
|
954
954
|
\u{1F310} Documentation server running at http://localhost:${this.port}`),this.envResult?.hasRails&&this.envResult?.hasNextjs&&console.log(" \u{1F4CA} Multiple environments detected - use tabs to switch views"),console.log(` Press Ctrl+C to stop
|
|
955
|
-
`);}),
|
|
956
|
-
\u{1F504} Regenerating documentation...`),this.currentReport=await this.engine.generate(),this.envResult?.hasRails){let
|
|
955
|
+
`);}),a){let e=(await import('open')).default;await e(`http://localhost:${this.port}`);}this.config.watch.enabled&&this.watchForChanges();}async regenerate(){if(console.log(`
|
|
956
|
+
\u{1F504} Regenerating documentation...`),this.currentReport=await this.engine.generate(),this.envResult?.hasRails){let a=this.config.repositories[0]?.path||process.cwd();try{this.railsAnalysis=await k(a);}catch(t){console.error("\u26A0\uFE0F Rails re-analysis failed:",t.message);}}this.io.emit("reload"),console.log("\u2705 Documentation regenerated");}async watchForChanges(){let a=this.config.repositories.map(e=>e.path),t=null;for(let e of a)try{let o=d.watch(e,{recursive:!0});(async()=>{for await(let s of o)s.filename&&(s.filename.endsWith(".ts")||s.filename.endsWith(".tsx"))&&(t&&clearTimeout(t),t=setTimeout(async()=>{await this.regenerate();},this.config.watch.debounce));})();}catch(o){console.warn(`Warning: Could not watch directory ${e}:`,o.message);}}stop(){this.server.close(),console.log(`
|
|
957
957
|
\u{1F44B} Server stopped`);}};export{b as a,I as b};
|
package/dist/cli.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {a,b}from'./chunk-
|
|
2
|
+
import {a,b}from'./chunk-RFKIKTGM.js';import'./chunk-H4YGP3GL.js';import'./chunk-VV3A3UE3.js';import'./chunk-IAO54U6X.js';import'./chunk-XWZH2RDG.js';import'./chunk-MOEA75XK.js';import'./chunk-PTR5IROV.js';import {Command}from'commander';import n from'chalk';import*as p from'path';import*as s from'fs/promises';var d=new Command;d.name("repomap").description("Interactive documentation generator for code repositories").version("0.1.0");async function $(e){let o=p.basename(e),a=false,t=p.join(e,"Gemfile"),c=p.join(e,"config","routes.rb");try{await s.access(t),await s.access(c);let u=await s.readFile(t,"utf-8");a=u.includes("gem 'rails'")||u.includes('gem "rails"');}catch{}let r=p.join(e,"package.json"),i=false,l=false,m={};try{let u=JSON.parse(await s.readFile(r,"utf-8")),w={...u.dependencies,...u.devDependencies};i=!!w.react,l=!!w.next;let f=["src/pages","pages","app","src/app","frontend/src"];for(let g of f)try{await s.access(p.join(e,g)),m.pagesDir=g;break}catch{}let b=["src/features","features","src/modules","modules","frontend/src"];for(let g of b)try{await s.access(p.join(e,g)),m.featuresDir=g;break}catch{}let D=["src/components","components","src/common/components","frontend/src"];for(let g of D)try{await s.access(p.join(e,g)),m.componentsDir=g;break}catch{}}catch{}let h=[];(i||l)&&h.push("pages","graphql","dataflow","rest-api");let y="generic";return l?y="nextjs":a&&(y="rails"),!a&&!i&&!l?null:{name:o,displayName:o,description:a&&i?"Rails + React application":a?"Rails application":"",path:e,branch:"main",type:y,analyzers:h,settings:m}}async function x(e){let o=await $(e);if(!o)throw new Error("Could not detect project. Please create a repomap.config.ts file or run 'repomap init'.");return {outputDir:"./.repomap",site:{title:`${o.displayName} Documentation`,description:"Auto-generated documentation",baseUrl:"/docs"},repositories:[o],analysis:{include:["**/*.tsx","**/*.ts"],exclude:["**/node_modules/**","**/__tests__/**","**/*.test.*","**/*.spec.*","**/dist/**","**/.next/**"],maxDepth:5},diagrams:{enabled:true,types:["flowchart","sequence"],theme:"default"},watch:{enabled:false,debounce:1e3},integrations:{github:{enabled:false,organization:""},slack:{enabled:false}}}}async function P(e,o){let a=e?[e]:["repomap.config.ts","repomap.config.js","repomap.config.mjs"];for(let t of a){let c=p.resolve(o,t);try{await s.access(c),console.log(n.gray(`Loading config from: ${c}`));let r=await import(c);return r.config||r.default}catch{}}return console.log(n.gray("No config file found, auto-detecting project...")),x(o)}d.command("generate").description("Generate documentation from source code").option("-c, --config <path>","Path to config file").option("-o, --output <path>","Output directory").option("--repo <name>","Analyze specific repository only").option("--watch","Watch for changes and regenerate").option("--no-cache","Disable caching (always analyze from scratch)").option("--format <type>","Output format: json, html, markdown (default: all)","all").option("--ci","CI mode: minimal output, exit codes for errors").option("--static","Generate standalone HTML files (for GitHub Pages)").action(async e=>{let o=e.ci||process.env.CI==="true";o||console.log(n.blue.bold(`
|
|
3
3
|
\u{1F4DA} Repomap - Documentation Generator
|
|
4
4
|
`));try{let a$1=process.cwd(),t=await P(e.config,a$1);e.output&&(t.outputDir=e.output),e.repo&&(t.repositories=t.repositories.filter(r=>r.name===e.repo),t.repositories.length===0&&(console.error(n.red(`Repository "${e.repo}" not found in config`)),process.exit(1)));let c=new a(t,{noCache:!e.cache});if(e.watch)console.log(n.yellow(`
|
|
5
5
|
\u{1F440} Watch mode enabled. Press Ctrl+C to stop.
|
|
6
6
|
`)),await C(c,t);else {let r=await c.generate();if(e.format==="json"||e.static){let i=p.join(t.outputDir,"report.json");await s.mkdir(t.outputDir,{recursive:!0}),await s.writeFile(i,JSON.stringify(r,null,2)),o||console.log(n.green(`\u{1F4C4} JSON report: ${i}`));}if(e.static&&await G(t,r,o),!o)N(r);else {let i=r.repositories.reduce((l,m)=>l+m.summary.totalPages,0);console.log(`\u2705 Generated: ${i} pages, ${r.repositories.length} repos`);}}}catch(a){console.error(o?`Error: ${a.message}`:n.red(`
|
|
7
|
-
\u274C Error:`),a.message),process.exit(1);}});async function G(e,o,a){let{PageMapGenerator:t}=await import('./page-map-generator-XNZ4TDJT.js'),{detectEnvironments:c}=await import('./env-detector-BIWJ7OYF.js'),r=e.outputDir;await s.mkdir(r,{recursive:true});let i=e.repositories[0]?.path||process.cwd(),l=await c(i),m=null;if(l.hasRails){let{analyzeRailsApp:f}=await import('./rails-
|
|
7
|
+
\u274C Error:`),a.message),process.exit(1);}});async function G(e,o,a){let{PageMapGenerator:t}=await import('./page-map-generator-XNZ4TDJT.js'),{detectEnvironments:c}=await import('./env-detector-BIWJ7OYF.js'),r=e.outputDir;await s.mkdir(r,{recursive:true});let i=e.repositories[0]?.path||process.cwd(),l=await c(i),m=null;if(l.hasRails){let{analyzeRailsApp:f}=await import('./rails-FFISZ4AE.js');m=await f(i);}let y=new t().generatePageMapHtml(o,{envResult:l,railsAnalysis:m,staticMode:true});if(await s.writeFile(p.join(r,"index.html"),y),a||console.log(n.green(`\u{1F4C4} Static page map: ${p.join(r,"index.html")}`)),m){let{RailsMapGenerator:f}=await import('./rails-map-generator-UFLCMFAT.js'),D=new f().generateFromResult(m);await s.writeFile(p.join(r,"rails-map.html"),D),a||console.log(n.green(`\u{1F4C4} Static Rails map: ${p.join(r,"rails-map.html")}`));}let u=["common.css","page-map.css","docs.css","rails-map.css"],w=p.join(r,"assets");await s.mkdir(w,{recursive:true});for(let f of u)try{let b=new URL(`./generators/assets/${f}`,import.meta.url),D=await s.readFile(b,"utf-8");await s.writeFile(p.join(w,f),D);}catch{}a||(console.log(n.green(`
|
|
8
8
|
\u2705 Static site generated in: ${r}`)),console.log(n.gray(" Deploy to GitHub Pages or any static hosting")));}d.command("serve").description("Start local documentation server with live reload").option("-c, --config <path>","Path to config file").option("--path <path>","Path to repository to analyze (auto-detect if no config)").option("-p, --port <number>","Server port","3030").option("--no-open","Don't open browser automatically").option("--no-cache","Disable caching (always analyze from scratch)").action(async e=>{console.log(n.blue.bold(`
|
|
9
9
|
\u{1F310} Repomap - Documentation Server
|
|
10
10
|
`));try{let o=e.path||process.cwd(),a=await P(e.config,o);await new b(a,parseInt(e.port),{noCache:!e.cache}).start(!e.open);}catch(o){console.error(n.red(`
|
|
@@ -68,7 +68,7 @@ export default config;
|
|
|
68
68
|
`;await s.writeFile(o,h,"utf-8"),console.log(n.green(`\u2705 Created ${o}`)),console.log(n.gray(`
|
|
69
69
|
Run 'npx repomap serve' to start the documentation server.`));}catch(a){console.error(n.red("Failed to create config:"),a.message);}});d.command("rails").description("Analyze a Rails application and generate interactive map").option("--path <path>","Path to Rails application").option("-o, --output <path>","Output HTML file path").action(async e=>{console.log(n.blue.bold(`
|
|
70
70
|
\u{1F6E4}\uFE0F Repomap - Rails Analyzer
|
|
71
|
-
`));try{let o=e.path||process.cwd();try{await s.access(p.join(o,"config","routes.rb"));}catch{console.error(n.red("Not a Rails project (config/routes.rb not found)")),process.exit(1);}let{RailsMapGenerator:a}=await import('./rails-map-generator-
|
|
71
|
+
`));try{let o=e.path||process.cwd();try{await s.access(p.join(o,"config","routes.rb"));}catch{console.error(n.red("Not a Rails project (config/routes.rb not found)")),process.exit(1);}let{RailsMapGenerator:a}=await import('./rails-map-generator-UFLCMFAT.js'),t=e.output||p.join(o,"rails-map.html");await new a(o).generate({title:`${p.basename(o)} - Rails Map`,outputPath:t}),console.log(n.green(`
|
|
72
72
|
\u2705 Rails map generated: ${t}`));let{exec:r}=await import('child_process');r(`open "${t}"`);}catch(o){console.error(n.red(`
|
|
73
73
|
\u274C Error:`),o.message),process.exit(1);}});d.command("diff").description("Show documentation changes since last generation").option("-c, --config <path>","Path to config file").action(async e=>{console.log(n.blue.bold(`
|
|
74
74
|
\u{1F4CA} Documentation Diff
|
|
@@ -137,6 +137,31 @@ declare class GraphQLAnalyzer extends BaseAnalyzer {
|
|
|
137
137
|
constructor(config: RepositoryConfig);
|
|
138
138
|
getName(): string;
|
|
139
139
|
analyze(): Promise<Partial<AnalysisResult>>;
|
|
140
|
+
/**
|
|
141
|
+
* Deduplicate operations by name, keeping the first occurrence
|
|
142
|
+
*/
|
|
143
|
+
private deduplicateOperations;
|
|
144
|
+
/**
|
|
145
|
+
* Analyze GraphQL Code Generator output files
|
|
146
|
+
* Supports client preset pattern: __generated__/graphql.ts
|
|
147
|
+
*/
|
|
148
|
+
private analyzeCodegenGenerated;
|
|
149
|
+
/**
|
|
150
|
+
* Extract type string from AST type node
|
|
151
|
+
*/
|
|
152
|
+
private extractTypeFromAst;
|
|
153
|
+
/**
|
|
154
|
+
* Extract fields from AST selection set
|
|
155
|
+
*/
|
|
156
|
+
private extractFieldsFromAst;
|
|
157
|
+
/**
|
|
158
|
+
* Extract fragment references from AST
|
|
159
|
+
*/
|
|
160
|
+
private extractFragmentReferencesFromAst;
|
|
161
|
+
/**
|
|
162
|
+
* Infer return type from AST definition
|
|
163
|
+
*/
|
|
164
|
+
private inferReturnTypeFromAst;
|
|
140
165
|
private analyzeGraphQLFiles;
|
|
141
166
|
private analyzeInlineGraphQL;
|
|
142
167
|
private extractOperationsFromDocument;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { DocGeneratorConfig, DocumentationReport } from './types.js';
|
|
2
2
|
export { APICall, APIConnection, APIEndpoint, AnalysisConfig, AnalysisResult, AnalyzerType, AssociationInfo, AttributeInfo, AuthRequirement, ComponentInfo, CrossRepoAnalysis, CrossRepoLink, DataFetchingInfo, DataFlow, DataFlowNode, DiagramConfig, DiagramType, GraphQLField, GraphQLOperation, IntegrationsConfig, MermaidDiagram, ModelInfo, NavigationFlow, NavigationInfo, PageInfo, ParameterInfo, PropInfo, RepositoryConfig, RepositoryReport, RepositorySummary, ResponseInfo, SiteConfig, StepInfo, VariableInfo, WatchConfig } from './types.js';
|
|
3
|
-
export { B as BaseAnalyzer, D as DataFlowAnalyzer, G as GraphQLAnalyzer, P as PagesAnalyzer } from './dataflow-analyzer-
|
|
3
|
+
export { B as BaseAnalyzer, D as DataFlowAnalyzer, G as GraphQLAnalyzer, P as PagesAnalyzer } from './dataflow-analyzer-LeE44O3x.js';
|
|
4
4
|
export { MarkdownGenerator, MermaidGenerator, PageMapGenerator, PageMapOptions } from './generators/index.js';
|
|
5
5
|
export { DocServer, DocServerOptions } from './server/index.js';
|
|
6
6
|
|
package/dist/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import'./chunk-6F4PWJZI.js';export{a as DocGeneratorEngine,b as DocServer}from'./chunk-
|
|
1
|
+
import'./chunk-6F4PWJZI.js';export{a as DocGeneratorEngine,b as DocServer}from'./chunk-RFKIKTGM.js';import'./chunk-H4YGP3GL.js';import'./chunk-VV3A3UE3.js';export{a as BaseAnalyzer,d as DataFlowAnalyzer,c as GraphQLAnalyzer,b as PagesAnalyzer}from'./chunk-IAO54U6X.js';export{b as MarkdownGenerator,a as MermaidGenerator}from'./chunk-XWZH2RDG.js';export{a as PageMapGenerator}from'./chunk-MOEA75XK.js';import'./chunk-PTR5IROV.js';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export{f as RailsControllerAnalyzer,h as RailsGrpcAnalyzer,g as RailsModelAnalyzer,e as RailsRoutesAnalyzer,k as analyzeRailsApp,i as analyzeRailsViews,j as analyzeReactComponents,d as findNodes,a as initRubyParser,b as parseRuby,c as parseRubyFile}from'./chunk-
|
|
1
|
+
export{f as RailsControllerAnalyzer,h as RailsGrpcAnalyzer,g as RailsModelAnalyzer,e as RailsRoutesAnalyzer,k as analyzeRailsApp,i as analyzeRailsViews,j as analyzeReactComponents,d as findNodes,a as initRubyParser,b as parseRuby,c as parseRubyFile}from'./chunk-PTR5IROV.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export{a as RailsMapGenerator}from'./chunk-H4YGP3GL.js';import'./chunk-PTR5IROV.js';
|
package/dist/server/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export{b as DocServer}from'../chunk-
|
|
1
|
+
export{b as DocServer}from'../chunk-RFKIKTGM.js';import'../chunk-H4YGP3GL.js';import'../chunk-VV3A3UE3.js';import'../chunk-IAO54U6X.js';import'../chunk-XWZH2RDG.js';import'../chunk-MOEA75XK.js';import'../chunk-PTR5IROV.js';
|
package/package.json
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export{a as RailsMapGenerator}from'./chunk-SL2GMDBN.js';import'./chunk-UJT5KTVK.js';
|