@wtdlee/repomap 0.9.1 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +12 -0
- package/dist/analyzers/index.d.ts +8 -8
- package/dist/analyzers/index.js +1 -1
- package/dist/{chunk-WZAAA7DS.js → chunk-ATRSGO6O.js} +175 -49
- package/dist/chunk-IGRCLYVZ.js +451 -0
- package/dist/{chunk-NQMJ3QRX.js → chunk-LHP2OKKA.js} +1 -1
- package/dist/{chunk-2XZSFAJF.js → chunk-QBSB6BIU.js} +6 -6
- package/dist/{chunk-6AZNHUOB.js → chunk-YKPXOHWZ.js} +52 -25
- package/dist/chunk-ZWRDP37E.js +1 -0
- package/dist/cli.js +14 -14
- package/dist/{dataflow-analyzer-CJ2T0cGS.d.ts → dataflow-analyzer-DIUsRpvv.d.ts} +18 -0
- package/dist/{env-detector-BIWJ7OYF.js → env-detector-RVGPBVNJ.js} +1 -1
- package/dist/generators/assets/docs.css +22 -0
- package/dist/generators/assets/rails-map.css +13 -0
- package/dist/generators/index.js +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/page-map-generator-LTVRHSDC.js +1 -0
- package/dist/{rails-3HNUFTQV.js → rails-57MNOGHR.js} +1 -1
- package/dist/rails-map-generator-JNU5QHX4.js +1 -0
- package/dist/server/index.js +1 -1
- package/dist/types.d.ts +34 -1
- package/package.json +1 -1
- package/dist/chunk-QDVE7MT3.js +0 -5
- package/dist/page-map-generator-HROGGVAQ.js +0 -1
- package/dist/rails-map-generator-DF2YAXW4.js +0 -1
|
@@ -813,7 +813,7 @@ import {k}from'./chunk-H7VVRHQZ.js';import*as c from'fs';import*as d from'path';
|
|
|
813
813
|
<div class="panel-header">
|
|
814
814
|
<div class="panel-title">gRPC Services <span class="panel-count">(\${Math.min(grpcDisplayCount, filteredGrpc.length)} / \${filteredGrpc.length})</span></div>
|
|
815
815
|
</div>
|
|
816
|
-
<div
|
|
816
|
+
<div class="model-card-grid">
|
|
817
817
|
\${displayedGrpc.map((svc, idx) => \`
|
|
818
818
|
<div class="model-card" onclick="showGrpcDetail(\${idx})">
|
|
819
819
|
<div class="model-name">
|
|
@@ -1,20 +1,20 @@
|
|
|
1
|
-
var
|
|
2
|
-
`),relatedFiles:s.pages.map(a=>a.filePath)}}generateDataFlowDiagram(s){let e=["flowchart LR"," %% Data Flow Diagram"],n=new Map,r=0,i=p=>{let l=`${p.type}:${p.name}`;if(!n.has(l)){let d=p.type.charAt(0).toUpperCase();n.set(l,`${d}${r++}`);}return n.get(l)??`N${r++}`},o=p=>p.replace(/[\u{1F4E1}\u{270F}\u{FE0F}\u{1F504}\u{1F4E6}]/gu,"").trim().substring(0,40),a=s.dataFlows.filter(p=>p.name.includes("\u{1F4E1}")||p.operations.some(l=>l.includes("Query"))),u=s.dataFlows.filter(p=>p.name.includes("\u270F\uFE0F")||p.operations.some(l=>l.includes("Mutation"))),c=s.dataFlows.filter(p=>p.name.includes("\u{1F504}")||p.source.type==="context"||p.operations.some(l=>l.includes("Context")));if(a.length>0){e.push(""),e.push(" subgraph Queries[\u{1F4E1} Queries]"),e.push(" direction TB");for(let p of a.slice(0,20)){let l=i(p.source),d=i(p.target),
|
|
1
|
+
var b=class{generateAll(s,e){let n=[];for(let r of s)n.push(this.generateNavigationDiagram(r)),n.push(this.generateDataFlowDiagram(r)),n.push(this.generateComponentDiagram(r)),n.push(this.generateGraphQLDiagram(r));return s.length>1&&n.push(this.generateCrossRepoDiagram(s,e)),n}generateNavigationDiagram(s){let e=["flowchart TB"," %% Page Navigation Flow - Grouped by Category"],n=new Map,r=0,i=new Map;for(let a of s.pages){let c=a.path.split("/").filter(Boolean)[0]||"root",p=i.get(c)||[];p.push(a),i.set(c,p);}for(let[a,u]of i){let c=a.replace(/[^a-zA-Z0-9]/g,"_"),p=a==="root"?"Root Pages":`/${a}`;e.push(""),e.push(` subgraph ${c}["${p}"]`),e.push(" direction TB");for(let l of u){let d=`P${r++}`;n.set(l.path,d);let q=l.path.split("/").filter(Boolean),m=q.length>1?q.slice(1).join("/"):l.path,f=l.authentication.required?" AUTH":"";e.push(` ${d}["${m}${f}"]`);}e.push(" end");}let t=0,o=30;e.push(""),e.push(" %% Navigation Links");for(let a of s.pages){if(t>=o)break;let u=n.get(a.path);for(let c of a.linkedPages.slice(0,2)){if(t>=o)break;let p=n.get(c);u&&p&&u!==p&&(e.push(` ${u} --> ${p}`),t++);}}e.push(""),e.push(" %% Styling"),e.push(" classDef authRequired fill:#fee2e2,stroke:#ef4444,color:#991b1b"),e.push(" classDef public fill:#dcfce7,stroke:#22c55e,color:#166534");for(let a of s.pages){let u=n.get(a.path);u&&(a.authentication.required?e.push(` class ${u} authRequired`):e.push(` class ${u} public`));}return {type:"flowchart",title:`${s.repository} - Page Navigation`,content:e.join(`
|
|
2
|
+
`),relatedFiles:s.pages.map(a=>a.filePath)}}generateDataFlowDiagram(s){let e=["flowchart LR"," %% Data Flow Diagram"],n=new Map,r=0,i=p=>{let l=`${p.type}:${p.name}`;if(!n.has(l)){let d=p.type.charAt(0).toUpperCase();n.set(l,`${d}${r++}`);}return n.get(l)??`N${r++}`},o=p=>p.replace(/[\u{1F4E1}\u{270F}\u{FE0F}\u{1F504}\u{1F4E6}]/gu,"").trim().substring(0,40),a=s.dataFlows.filter(p=>p.name.includes("\u{1F4E1}")||p.operations.some(l=>l.includes("Query"))),u=s.dataFlows.filter(p=>p.name.includes("\u270F\uFE0F")||p.operations.some(l=>l.includes("Mutation"))),c=s.dataFlows.filter(p=>p.name.includes("\u{1F504}")||p.source.type==="context"||p.operations.some(l=>l.includes("Context")));if(a.length>0){e.push(""),e.push(" subgraph Queries[\u{1F4E1} Queries]"),e.push(" direction TB");for(let p of a.slice(0,20)){let l=i(p.source),d=i(p.target),q=o(p.source.name),m=o(p.target.name);e.push(` ${l}(("${q}"))`),e.push(` ${d}["${m}"]`),e.push(` ${l} --> ${d}`);}e.push(" end");}if(u.length>0){e.push(""),e.push(" subgraph Mutations[\u270F\uFE0F Mutations]"),e.push(" direction TB");for(let p of u.slice(0,20)){let l=i(p.source),d=i(p.target),q=o(p.source.name),m=o(p.target.name);e.push(` ${l}["${q}"]`),e.push(` ${d}(("${m}"))`),e.push(` ${l} --> ${d}`);}e.push(" end");}if(c.length>0){e.push(""),e.push(" subgraph Context[\u{1F504} Context]"),e.push(" direction TB");for(let p of c.slice(0,15)){let l=i(p.source),d=i(p.target),q=o(p.source.name),m=o(p.target.name);e.push(` ${l}{{"${q}"}}`),e.push(` ${d}["${m}"]`),e.push(` ${l} -.-> ${d}`);}e.push(" end");}return e.push(""),e.push(" %% Styling"),e.push(" classDef query fill:#dbeafe,stroke:#3b82f6,color:#1e40af"),e.push(" classDef mutation fill:#fce7f3,stroke:#ec4899,color:#9d174d"),e.push(" classDef context fill:#d1fae5,stroke:#10b981,color:#065f46"),{type:"flowchart",title:`${s.repository} - Data Flow`,content:e.join(`
|
|
3
3
|
`),relatedFiles:s.dataFlows.map(p=>p.source.name)}}generateComponentDiagram(s){let e=["flowchart TB"," %% Component Hierarchy"],n=new Map;for(let t of s.components){let o=n.get(t.type)||[];o.push(t),n.set(t.type,o);}for(let[t,o]of n){e.push(` subgraph ${t.charAt(0).toUpperCase()+t.slice(1)}s`);for(let a of o.slice(0,20)){let u=a.name.replace(/[^a-zA-Z0-9]/g,"_");e.push(` ${u}["${a.name}"]`);}e.push(" end");}let r=0,i=50;for(let t of s.components){if(r>=i)break;let o=t.name.replace(/[^a-zA-Z0-9]/g,"_");for(let a of t.dependencies.slice(0,3)){if(r>=i)break;let u=a.replace(/[^a-zA-Z0-9]/g,"_");e.push(` ${o} --> ${u}`),r++;}}return {type:"flowchart",title:`${s.repository} - Component Hierarchy`,content:e.join(`
|
|
4
4
|
`),relatedFiles:s.components.map(t=>t.filePath)}}generateGraphQLDiagram(s){let e=["flowchart LR"," %% GraphQL Operations"],n=s.graphqlOperations.filter(t=>t.type==="query"),r=s.graphqlOperations.filter(t=>t.type==="mutation"),i=s.graphqlOperations.filter(t=>t.type==="fragment");if(e.push(' API[("GraphQL API")]'),n.length>0){e.push(" subgraph Queries");for(let t of n.slice(0,15)){let o=`Q_${t.name.replace(/[^a-zA-Z0-9]/g,"_")}`;e.push(` ${o}["${t.name}"]`),e.push(` ${o} --> API`);}e.push(" end");}if(r.length>0){e.push(" subgraph Mutations");for(let t of r.slice(0,15)){let o=`M_${t.name.replace(/[^a-zA-Z0-9]/g,"_")}`;e.push(` ${o}["${t.name}"]`),e.push(` ${o} --> API`);}e.push(" end");}if(i.length>0){e.push(" subgraph Fragments");for(let t of i.slice(0,10)){let o=`F_${t.name.replace(/[^a-zA-Z0-9]/g,"_")}`;e.push(` ${o}[/"${t.name}"/]`);}e.push(" end");}return {type:"flowchart",title:`${s.repository} - GraphQL Operations`,content:e.join(`
|
|
5
5
|
`),relatedFiles:s.graphqlOperations.map(t=>t.filePath)}}generateCrossRepoDiagram(s,e){let n=["flowchart TB"," %% Cross-Repository Architecture"];for(let r of s){let i=r.repository.replace(/[^a-zA-Z0-9]/g,"_");n.push(` subgraph ${i}["${r.repository}"]`),n.push(` ${i}_pages["\u{1F4C4} ${r.pages.length} Pages"]`),n.push(` ${i}_gql["\u{1F537} ${r.graphqlOperations.length} GraphQL Ops"]`),n.push(` ${i}_comp["\u{1F9E9} ${r.components.length} Components"]`),n.push(" end");}for(let r of e){let i=r.sourceRepo.replace(/[^a-zA-Z0-9]/g,"_"),t=r.targetRepo.replace(/[^a-zA-Z0-9]/g,"_"),o="-->";r.linkType==="api-call"?o="==>":r.linkType==="graphql-operation"&&(o="-..->"),n.push(` ${i}_gql ${o}|"${r.linkType}"| ${t}_gql`);}return {type:"flowchart",title:"Cross-Repository Architecture",content:n.join(`
|
|
6
6
|
`),relatedFiles:s.map(r=>r.repository)}}generateSequenceDiagram(s){let e=["sequenceDiagram",` %% ${s.name}`],n=[s.source,...s.via,s.target];for(let t of n)e.push(` participant ${t.name}`);let r=s.source;for(let t=0;t<s.via.length;t++){let o=s.via[t],a=s.operations[t]||"data";e.push(` ${r.name}->>+${o.name}: ${a}`),r=o;}let i=s.operations[s.operations.length-1]||"data";return e.push(` ${r.name}->>+${s.target.name}: ${i}`),e.push(` ${s.target.name}-->>-${s.source.name}: response`),{type:"sequence",title:s.name,content:e.join(`
|
|
7
7
|
`),relatedFiles:[]}}};var A=class{generateDocumentation(s){let e=new Map;e.set("index.md",this.generateIndex(s));for(let n of s.repositories)e.set(`repos/${n.name}/index.md`,this.generateRepoIndex(n)),e.set(`repos/${n.name}/pages.md`,this.generatePagesDoc(n)),e.set(`repos/${n.name}/components.md`,this.generateComponentsDoc(n)),e.set(`repos/${n.name}/graphql.md`,this.generateGraphQLDoc(n)),e.set(`repos/${n.name}/dataflow.md`,this.generateDataFlowDoc(n));return s.repositories.length>1&&e.set("cross-repo.md",this.generateCrossRepoDoc(s)),e.set("diagrams.md",this.generateDiagramsDoc(s.diagrams)),e}generateIndex(s){let n=[`# ${s.repositories.length===1?`${s.repositories[0].displayName} Documentation`:"Project Documentation"}`,"",`Generated: ${s.generatedAt}`,"",s.repositories.length>1?"## Repositories":"## Overview",""];for(let r of s.repositories)n.push(`### [${r.displayName}](/docs/repos/${r.name}/index)`),n.push(""),n.push(`- **Version**: ${r.version}`),n.push(`- **Commit**: \`${r.commitHash.substring(0,7)}\``),n.push(`- **Pages**: ${r.summary.totalPages}`),n.push(`- **Components**: ${r.summary.totalComponents}`),n.push(`- **GraphQL Ops**: ${r.summary.totalGraphQLOperations}`),n.push("");return n.push("## Quick Links"),n.push(""),s.repositories.length>1&&n.push("- [Cross Repository](/docs/cross-repo)"),n.push("- [Diagrams](/docs/diagrams)"),n.push("- [Page Map (Interactive)](/page-map)"),n.push(""),n.join(`
|
|
8
|
-
`)}generateRepoIndex(s){return [`# ${s.displayName}`,"",`Version: ${s.version} | Commit: \`${s.commitHash.substring(0,7)}\``,"","## Overview","","| Metric | Count |","|--------|-------|",`| Pages | ${s.summary.totalPages} |`,`| Components | ${s.summary.totalComponents} |`,`| GraphQL Operations | ${s.summary.totalGraphQLOperations} |`,`| Data Flows | ${s.summary.totalDataFlows} |`,`| Auth Required | ${s.summary.authRequiredPages} |`,`| Public | ${s.summary.publicPages} |`,"","## Documentation","",`- [Pages](/docs/repos/${s.name}/pages)`,`- [Components](/docs/repos/${s.name}/components)`,`- [GraphQL](/docs/repos/${s.name}/graphql)`,`- [Data Flow](/docs/repos/${s.name}/dataflow)`,"","## Quick Access","","- [Page Map](/page-map)","- [Diagrams](/docs/diagrams)",""].join(`
|
|
9
|
-
`)}generatePagesDoc(s){let e=[`# ${s.displayName} - Pages`,""],n=s.analysis.pages.filter(o=>o.authentication.required).length,r=s.analysis.pages.filter(o=>o.dataFetching.some(a=>!a.type.includes("Mutation"))).length,i=s.analysis.pages.filter(o=>o.dataFetching.some(a=>a.type.includes("Mutation"))).length;e.push("| Metric | Value |"),e.push("|--------|-------|"),e.push(`| Total | **${s.analysis.pages.length}** |`),e.push(`| Auth Required | ${n} |`),e.push(`| With Queries | ${r} |`),e.push(`| With Mutations | ${i} |`),e.push("");let t=new Map;for(let o of s.analysis.pages){let a=o.path.split("/")[1]||"root",u=t.get(a)||[];u.push(o),t.set(a,u);}for(let[o,a]of t){e.push(`## /${o}`),e.push(""),e.push("| Page | Auth | Layout |"),e.push("|------|------|--------|");for(let u of a){let c=u.path.replace(`/${o}`,"")||"/",p=u.authentication.required?"Required":"Public",l=u.layout||"-";e.push(`| \`${c}\` | ${p} | ${l} |`);}e.push("");for(let u of a){let c=u.dataFetching||[];if(c.length===0)continue;let p=new Map,l=(m,f,
|
|
8
|
+
`)}generateRepoIndex(s){return [`# ${s.displayName}`,"",`Version: ${s.version} | Commit: \`${s.commitHash.substring(0,7)}\``,"","## Overview","","| Metric | Count |","|--------|-------|",`| Pages | ${s.summary.totalPages} |`,`| Components | ${s.summary.totalComponents} |`,`| GraphQL Operations | ${s.summary.totalGraphQLOperations} |`,`| Data Flows | ${s.summary.totalDataFlows} |`,`| Auth Required | ${s.summary.authRequiredPages} |`,`| Public | ${s.summary.publicPages} |`,"","## Coverage","","| Metric | Value |","|--------|-------|",`| TS/JS Files Scanned | ${s.analysis.coverage?.tsFilesScanned??0} |`,`| TS Parse Failures | ${s.analysis.coverage?.tsParseFailures??0} |`,`| GraphQL Parse Failures | ${s.analysis.coverage?.graphqlParseFailures??0} |`,`| Codegen Files Detected | ${s.analysis.coverage?.codegenFilesDetected??0} |`,`| Codegen Files Parsed | ${s.analysis.coverage?.codegenFilesParsed??0} |`,`| Codegen Exports Found | ${s.analysis.coverage?.codegenExportsFound??0} |`,"","## Documentation","",`- [Pages](/docs/repos/${s.name}/pages)`,`- [Components](/docs/repos/${s.name}/components)`,`- [GraphQL](/docs/repos/${s.name}/graphql)`,`- [Data Flow](/docs/repos/${s.name}/dataflow)`,"","## Quick Access","","- [Page Map](/page-map)","- [Diagrams](/docs/diagrams)",""].join(`
|
|
9
|
+
`)}generatePagesDoc(s){let e=[`# ${s.displayName} - Pages`,""],n=s.analysis.pages.filter(o=>o.authentication.required).length,r=s.analysis.pages.filter(o=>o.dataFetching.some(a=>!a.type.includes("Mutation"))).length,i=s.analysis.pages.filter(o=>o.dataFetching.some(a=>a.type.includes("Mutation"))).length;e.push("| Metric | Value |"),e.push("|--------|-------|"),e.push(`| Total | **${s.analysis.pages.length}** |`),e.push(`| Auth Required | ${n} |`),e.push(`| With Queries | ${r} |`),e.push(`| With Mutations | ${i} |`),e.push("");let t=new Map;for(let o of s.analysis.pages){let a=o.path.split("/")[1]||"root",u=t.get(a)||[];u.push(o),t.set(a,u);}for(let[o,a]of t){e.push(`## /${o}`),e.push(""),e.push("| Page | Auth | Layout |"),e.push("|------|------|--------|");for(let u of a){let c=u.path.replace(`/${o}`,"")||"/",p=u.authentication.required?"Required":"Public",l=u.layout||"-";e.push(`| \`${c}\` | ${p} | ${l} |`);}e.push("");for(let u of a){let c=u.dataFetching||[];if(c.length===0)continue;let p=new Map,l=(m,f,g)=>{p.has(m)||p.set(m,{label:f,open:g,queries:new Set,mutations:new Set});let h=p.get(m);if(!h){let y={label:f,open:g,queries:new Set,mutations:new Set};return p.set(m,y),y}return h};for(let m of c){let g=(m.operationName||"").replace(/^[→\->\s]+/,"").trim();if(!g||g.length<2)continue;let h=m.type.includes("Mutation"),y=m.source||"",D="direct",$="Direct (this page)",C=true;y.startsWith("close:")?(D="close",$="Close (related)",C=true):y.startsWith("indirect:")||y.startsWith("usedIn:")||y.startsWith("import:")?(D="indirect",$="Indirect",C=false):y.startsWith("common:")?(D="common",$="Common (shared)",C=false):y.startsWith("hook:")?(D="hook",$="Hook",C=false):y.startsWith("component:")&&(D="component",$="Component",C=false);let P=l(D,$,C);h?P.mutations.add(g):P.queries.add(g);}let d=Array.from(p.values()).reduce((m,f)=>m+f.queries.size+f.mutations.size,0);if(d===0)continue;let q=["direct","close","component","hook","indirect","common"];e.push(`### ${u.path}`),e.push(""),e.push(`> ${u.filePath}`),e.push(""),e.push(`**Data Operations (${d})**`),e.push("");for(let m of q){let f=p.get(m);if(!f)continue;let g=f.queries.size+f.mutations.size;if(g!==0){if(e.push(`<details class="ops-group${f.open?" is-open":""}"${f.open?" open":""}>`),e.push(`<summary class="ops-group__summary"><span class="ops-group__title">${f.label}</span><span class="ops-group__count">${g}</span></summary>`),e.push(""),f.queries.size>0){e.push(`**Queries (${f.queries.size})**`),e.push(""),e.push('<div class="gql-ops-list">');for(let h of Array.from(f.queries).sort()){let D=c.some($=>($.operationName||"").replace(/^[→\->\s]+/,"").trim()===h&&$.confidence==="likely")?' data-confidence="likely"':"";e.push(`<span class="gql-op" data-op="${h}"${D}>${h}</span>`);}e.push("</div>"),e.push("");}if(f.mutations.size>0){e.push(`**Mutations (${f.mutations.size})**`),e.push(""),e.push('<div class="gql-ops-list">');for(let h of Array.from(f.mutations).sort()){let D=c.some($=>($.operationName||"").replace(/^[→\->\s]+/,"").trim()===h&&$.confidence==="likely")?' data-confidence="likely"':"";e.push(`<span class="gql-op mutation" data-op="${h}"${D}>${h}</span>`);}e.push("</div>"),e.push("");}e.push("</details>"),e.push("");}}e.push("");}}return e.join(`
|
|
10
10
|
`)}generateComponentsDoc(s){let e=[`# ${s.displayName} - Components`,""],n=new Map;for(let i of s.analysis.components){let t=n.get(i.type)||[];t.push(i),n.set(i.type,t);}e.push("| Type | Count |"),e.push("|------|-------|"),e.push(`| Container | ${n.get("container")?.length||0} |`),e.push(`| Presentational | ${n.get("presentational")?.length||0} |`),e.push(`| Layout | ${n.get("layout")?.length||0} |`),e.push(`| Hook | ${n.get("hook")?.length||0} |`),e.push(`| **Total** | **${s.analysis.components.length}** |`),e.push("");let r=new Map;for(let i of s.analysis.pages)r.set(i.path,[]);for(let i of s.analysis.components)for(let t of s.analysis.pages){let o=this.extractFeatureFromPage(t.filePath),a=this.extractFeatureFromComponent(i.filePath);o&&a&&o===a&&r.get(t.path)?.push(i);}e.push("## By Page"),e.push("");for(let[i,t]of r){if(t.length===0)continue;let o=t.filter(c=>c.type==="container"),a=t.filter(c=>c.type==="presentational"),u=t.filter(c=>c.type==="hook");e.push(`### ${i}`),e.push(""),e.push("| Component | Type | Data |"),e.push("|-----------|------|------|");for(let c of o){let p=this.formatComponentDataOps(c,s.analysis.graphqlOperations);e.push(`| ${c.name} | Container | ${p||"-"} |`);}for(let c of a.slice(0,10)){let p=this.formatComponentDataOps(c,s.analysis.graphqlOperations);e.push(`| ${c.name} | UI | ${p||"-"} |`);}for(let c of u){let p=this.formatComponentDataOps(c,s.analysis.graphqlOperations);e.push(`| ${c.name} | Hook | ${p||"-"} |`);}if(e.push(""),a.length>10){let c=a.slice(10),p=`more-ui-${i.replace(/[^a-zA-Z0-9]/g,"-")}`;e.push(`<details id="${p}">`),e.push(`<summary style="cursor:pointer;color:var(--accent);padding:8px 0">Show ${c.length} more UI components</summary>`),e.push(""),e.push("| Component | Type | Data |"),e.push("|-----------|------|------|");for(let l of c){let d=this.formatComponentDataOps(l,s.analysis.graphqlOperations);e.push(`| ${l.name} | UI | ${d||"-"} |`);}e.push(""),e.push("</details>"),e.push("");}}e.push("## By Type"),e.push("");for(let[i,t]of n){e.push(`### ${i.charAt(0).toUpperCase()+i.slice(1)} (${t.length})`),e.push(""),e.push("| Name | File | Data |"),e.push("|------|------|------|");for(let o of t.slice(0,25)){let a=o.filePath.replace("src/features/","").replace("src/",""),u=this.formatComponentDataOps(o,s.analysis.graphqlOperations);e.push(`| ${o.name} | ${a} | ${u||"-"} |`);}if(e.push(""),t.length>25){let o=t.slice(25),a=`more-${i}-components`;e.push(`<details id="${a}">`),e.push(`<summary style="cursor:pointer;color:var(--accent);padding:8px 0">Show ${o.length} more ${i} components</summary>`),e.push(""),e.push("| Name | File | Data |"),e.push("|------|------|------|");for(let u of o){let c=u.filePath.replace("src/features/","").replace("src/",""),p=this.formatComponentDataOps(u,s.analysis.graphqlOperations);e.push(`| ${u.name} | ${c} | ${p||"-"} |`);}e.push(""),e.push("</details>"),e.push("");}}return e.join(`
|
|
11
11
|
`)}extractFeatureFromPage(s){let e=s.split("/");return e.length>1?e[0]:null}extractFeatureFromComponent(s){let e=s.match(/src\/features\/([^/]+)/);return e?e[1]:null}formatComponentDataOps(s,e){let n=[],r=[];if(e&&s.filePath)for(let a of e)a.type!=="query"&&a.type!=="mutation"||!(a.filePath===s.filePath||a.usedIn&&a.usedIn.includes(s.filePath))||(a.type==="mutation"?r.push(a.name):n.push(a.name));for(let a of s.hooks){let u=a.match(/(?:useQuery|Query):\s*(\w+)/),c=a.match(/(?:useMutation|Mutation):\s*(\w+)/);u&&u[1]&&u[1].trim().length>=2?n.push(u[1]):c&&c[1]&&c[1].trim().length>=2&&r.push(c[1]);}let i=Array.from(new Set(n.filter(a=>a&&a.trim().length>=2))),t=Array.from(new Set(r.filter(a=>a&&a.trim().length>=2)));if(i.length===0&&t.length===0)return "";let o=[];for(let a of i.sort())o.push(`<span class="gql-op" data-op="${a}">${a}</span>`);for(let a of t.sort())o.push(`<span class="gql-op mutation" data-op="${a}">${a}</span>`);return `<div class="gql-ops-inline">${o.join(" ")}</div>`}generateGraphQLDoc(s){let e=[`# ${s.displayName} - GraphQL`,""],n=s.analysis.graphqlOperations.filter(t=>t.type==="query"),r=s.analysis.graphqlOperations.filter(t=>t.type==="mutation"),i=s.analysis.graphqlOperations.filter(t=>t.type==="fragment");if(e.push("| Type | Count |"),e.push("|------|-------|"),e.push(`| Query | ${n.length} |`),e.push(`| Mutation | ${r.length} |`),e.push(`| Fragment | ${i.length} |`),e.push(`| **Total** | **${s.analysis.graphqlOperations.length}** |`),e.push(""),n.length>0){e.push("## Queries"),e.push("");for(let t of n.slice(0,80)){e.push(`### ${t.name}`),e.push("");let o=t.returnType||"unknown",a=t.variables.length,u=t.usedIn.length;if(e.push(`> Return: \`${o}\` | Variables: ${a} | Used: ${u} files`),e.push(""),t.variables.length>0){e.push("| Variable | Type |"),e.push("|----------|------|");for(let c of t.variables)e.push(`| ${c.name} | \`${c.type}\` |`);e.push("");}t.fields&&t.fields.length>0&&(e.push("```graphql"),e.push(this.formatGraphQLFields(t.fields,0)),e.push("```"),e.push(""));}n.length>80&&e.push(`*+${n.length-80} more queries*
|
|
12
12
|
`);}if(r.length>0){e.push("## Mutations"),e.push("");for(let t of r.slice(0,80)){e.push(`### ${t.name}`),e.push("");let o=t.variables.length,a=t.usedIn.length;if(e.push(`> Variables: ${o} | Used: ${a} files`),e.push(""),t.variables.length>0){e.push("| Variable | Type |"),e.push("|----------|------|");for(let u of t.variables)e.push(`| ${u.name} | \`${u.type}\` |`);e.push("");}t.fields&&t.fields.length>0&&(e.push("```graphql"),e.push(this.formatGraphQLFields(t.fields,0)),e.push("```"),e.push(""));}r.length>80&&e.push(`*+${r.length-80} more mutations*
|
|
13
13
|
`);}if(i.length>0){e.push("## Fragments"),e.push(""),e.push("| Name | Type | Fields |"),e.push("|------|------|--------|");for(let t of i.slice(0,50)){let o=t.fields?.length||0;e.push(`| ${t.name} | ${t.returnType||"-"} | ${o} |`);}i.length>50&&e.push(`| *+${i.length-50} more* | | |`),e.push("");}return e.join(`
|
|
14
14
|
`)}formatGraphQLFields(s,e){if(!s||s.length===0)return "";let n=[];for(let r of s){let i=" ".repeat(e);r.fields&&r.fields.length>0?(n.push(`${i}${r.name} {`),n.push(this.formatGraphQLFields(r.fields,e+1)),n.push(`${i}}`)):n.push(`${i}${r.name}`);}return n.join(`
|
|
15
15
|
`)}generateDataFlowDoc(s){let e=[`# ${s.displayName} - Data Flow`,""],n=s.analysis.dataFlows.filter(o=>o.name.includes("\u{1F4E1}")||o.operations.some(a=>a.includes("Query"))),r=s.analysis.dataFlows.filter(o=>o.name.includes("\u270F\uFE0F")||o.operations.some(a=>a.includes("Mutation"))),i=s.analysis.dataFlows.filter(o=>o.source.type==="context");e.push("## Overview"),e.push(""),e.push("| Type | Count | Direction |"),e.push("|------|-------|-----------|"),e.push(`| \`QUERY\` | ${n.length} | Server \u2192 Component |`),e.push(`| \`MUTATION\` | ${r.length} | Component \u2192 Server |`),e.push(`| \`CONTEXT\` | ${i.length} | Provider \u2192 Consumer |`),e.push(`| **Total** | **${s.analysis.dataFlows.length}** | |`),e.push(""),e.push("## Architecture"),e.push(""),e.push("```mermaid"),e.push("flowchart LR"),e.push(' subgraph Server["GraphQL Server"]'),e.push(" API[(API)]"),e.push(" end"),e.push(' subgraph Client["React Application"]'),e.push(" Apollo[Apollo Client]"),e.push(" Container[Container Component]"),e.push(" View[View Component]"),e.push(" end"),e.push(" API -->|Query Response| Apollo"),e.push(" Apollo -->|Cache/Data| Container"),e.push(" Container -->|Props| View"),e.push(" View -->|User Action| Container"),e.push(" Container -->|Mutation| Apollo"),e.push(" Apollo -->|GraphQL Request| API"),e.push("```"),e.push(""),e.push("## Page Data Flows"),e.push(""),e.push('<div class="dataflow-page-flows">'),e.push(['<div class="ops-filters" data-filter-scope="dataflow">',' <span class="ops-filters__label">Show:</span>',' <label class="ops-toggle"><input type="checkbox" data-filter="direct" checked> Direct</label>',' <label class="ops-toggle"><input type="checkbox" data-filter="close" checked> Close</label>',' <label class="ops-toggle"><input type="checkbox" data-filter="indirect" checked> Indirect</label>',' <label class="ops-toggle"><input type="checkbox" data-filter="common"> Common</label>',"</div>"].join(`
|
|
16
|
-
`)),e.push("");for(let o of s.analysis.pages){let a=this.extractFeatureFromPage(o.filePath),u=s.analysis.components.filter(
|
|
16
|
+
`)),e.push("");for(let o of s.analysis.pages){let a=this.extractFeatureFromPage(o.filePath),u=s.analysis.components.filter(g=>this.extractFeatureFromComponent(g.filePath)===a);if(!(o.dataFetching.length>0||u.some(g=>g.stateManagement.some(h=>h.includes("Apollo")||h.includes("Context")))))continue;e.push(`### ${o.path}`),e.push(""),e.push(`\`FILE: ${o.filePath}\``),e.push("");let p=this.getPageOperationGroups(o),l=this.flattenGroups(p,"queries"),d=this.flattenGroups(p,"mutations");if(l.length>0||d.length>0){let g=o.path.replace(/[^a-zA-Z0-9]/g,"_"),h=o.path.replace(/"/g,"'");e.push(`<pre class="mermaid" data-mermaid-scope="dataflow" data-mermaid-page="${g}">`),e.push("flowchart LR"),e.push(` Page${g}["${h}"]`),e.push("");let y=10,D=["direct","close","indirect","common"];for(let $ of D){let C=p[$];C.queries.length===0&&C.mutations.length===0||(e.push(`%%DFG_GROUP:${$}:start%%`),C.queries.slice(0,y).forEach((P,F)=>{let w=`Q${g}_${$}_${F}`,v=P.replace(/"/g,"'").replace(/[<>]/g,"");e.push(` ${w}["${v}"]:::query --> Page${g}`);}),C.mutations.slice(0,y).forEach((P,F)=>{let w=`M${g}_${$}_${F}`,v=P.replace(/"/g,"'").replace(/[<>]/g,"");e.push(` Page${g} --> ${w}["${v}"]:::mutation`);}),e.push(`%%DFG_GROUP:${$}:end%%`),e.push(""));}e.push(" classDef query fill:#dbeafe,stroke:#1d4ed8,color:#1e40af"),e.push(" classDef mutation fill:#fce7f3,stroke:#be185d,color:#9d174d"),e.push("</pre>"),e.push("");}let q=["direct","close","indirect","common"],m={direct:{label:"Direct (this page)",open:true,defaultVisible:true},close:{label:"Close (related)",open:true,defaultVisible:true},indirect:{label:"Indirect (via imports)",open:false,defaultVisible:true},common:{label:"Common (shared)",open:false,defaultVisible:false}};if(q.some(g=>p[g].queries.length>0||p[g].mutations.length>0))for(let g of q){let h=p[g],y=m[g];if(h.queries.length===0&&h.mutations.length===0)continue;let D=y.open?" open":"",$=y.defaultVisible?"":' style="display:none"';if(e.push(`<details class="ops-group" data-ops-scope="dataflow" data-ops-group="${g}"${D}${$}>`),e.push(`<summary class="ops-group__summary"><span class="ops-group__title">${y.label}</span><span class="ops-group__count">${h.queries.length+h.mutations.length}</span></summary>`),h.queries.length>0){e.push(`<p><strong>Queries (${h.queries.length})</strong></p>`),e.push('<div class="gql-ops-list">');for(let C of h.queries)e.push(`<span class="gql-op" data-op="${C}">${C}</span>`);e.push("</div>");}if(h.mutations.length>0){e.push(`<p><strong>Mutations (${h.mutations.length})</strong></p>`),e.push('<div class="gql-ops-list">');for(let C of h.mutations)e.push(`<span class="gql-op mutation" data-op="${C}">${C}</span>`);e.push("</div>");}e.push("</details>"),e.push("");}e.push("---"),e.push("");}e.push("</div>");let t=new Set;for(let o of i)t.add(o.source.name);if(t.size>0){e.push("## Context Providers"),e.push(""),e.push("| Provider | Description |"),e.push("|----------|-------------|");for(let o of t)e.push(`| \`${o}\` | Provides shared state |`);e.push("");}return e.join(`
|
|
17
17
|
`)}getPageOperationGroups(s){let e={direct:{queries:new Set,mutations:new Set},close:{queries:new Set,mutations:new Set},indirect:{queries:new Set,mutations:new Set},common:{queries:new Set,mutations:new Set}},n=t=>{if(!t)return false;let o=t.trim();return o.length>=2&&/[a-zA-Z]/.test(o)},r=t=>{if(!t)return "direct";let o=t.trim();return o.startsWith("common:")?"common":o.startsWith("close:")?"close":o.startsWith("indirect:")?"indirect":"direct"};for(let t of s.dataFetching||[]){let a=(t.operationName?.replace(/^[→\->\s]+/,"")||"").replace(/Document$/g,"").trim();if(!n(a))continue;let u=r(t.source);t.type?.includes("Mutation")??false?e[u].mutations.add(a):e[u].queries.add(a);}let i=t=>Array.from(t).filter(n).sort((o,a)=>o.localeCompare(a));return {direct:{queries:i(e.direct.queries),mutations:i(e.direct.mutations)},close:{queries:i(e.close.queries),mutations:i(e.close.mutations)},indirect:{queries:i(e.indirect.queries),mutations:i(e.indirect.mutations)},common:{queries:i(e.common.queries),mutations:i(e.common.mutations)}}}flattenGroups(s,e){let n=new Set;for(let r of Object.values(s))for(let i of r[e])n.add(i);return Array.from(n).sort((r,i)=>r.localeCompare(i))}generateCrossRepoDoc(s){let e=["# Cross Repository Analysis","","## Architecture Overview","","```mermaid","flowchart TB"];for(let n of s.repositories){let r=n.name.replace(/[^a-zA-Z0-9]/g,"_");e.push(` subgraph ${r}["${n.displayName}"]`),e.push(` ${r}_core["Core"]`),e.push(" end");}e.push("```"),e.push(""),e.push("## API Connections"),e.push("");for(let n of s.crossRepoAnalysis.apiConnections)e.push(`- **${n.frontend}** \u2192 **${n.backend}**: \`${n.endpoint}\``);e.push(""),e.push("## Shared Types"),e.push("");for(let n of s.crossRepoAnalysis.sharedTypes)e.push(`- \`${n}\``);return e.push(""),e.join(`
|
|
18
18
|
`)}createPageDataFlowDiagram(s,e,n){let r=[],i=[],t=[],o=[];for(let a of e){a.type==="container"&&o.push(a.name);for(let u of a.hooks){if(u.includes("Query")||u.includes("\u{1F4E1}")){let c=u.includes(":")?u.split(":")[1].trim():u;i.includes(c)||i.push(c);}if(u.includes("Mutation")||u.includes("\u270F\uFE0F")){let c=u.includes(":")?u.split(":")[1].trim():u;t.includes(c)||t.push(c);}}}for(let a of s.dataFetching){let u=a.operationName.replace(/^→\s*/,"");a.type.includes("Query")&&!i.includes(u)?i.push(u):a.type.includes("Mutation")&&!t.includes(u)&&t.push(u);}if(r.push(`[Page: ${s.path}]`),r.push("\u2502"),i.length>0||t.length>0){r.push("\u251C\u2500 \u{1F4E1} Data Fetching (Query)");for(let a of i.slice(0,5))r.push(`\u2502 \u251C\u2500 ${a.substring(0,40)}`),r.push("\u2502 \u2502 \u2514\u2500 GraphQL Server \u2192 Apollo Cache \u2192 Component");if(i.length>5&&r.push(`\u2502 \u2514\u2500 ... and ${i.length-5} more`),t.length>0){r.push("\u2502"),r.push("\u251C\u2500 \u270F\uFE0F Data Mutation (Mutation)");for(let a of t.slice(0,5))r.push(`\u2502 \u251C\u2500 ${a.substring(0,40)}`),r.push("\u2502 \u2502 \u2514\u2500 Component \u2192 GraphQL Server \u2192 Apollo Cache");t.length>5&&r.push(`\u2502 \u2514\u2500 ... and ${t.length-5} more`);}}if(o.length>0){r.push("\u2502"),r.push("\u251C\u2500 \u{1F4E6} Container Components");for(let a of o.slice(0,5))r.push(`\u2502 \u2514\u2500 ${a}`);o.length>5&&r.push(`\u2502 \u2514\u2500 ... and ${o.length-5} more`);}return r.push("\u2502"),r.push("\u2514\u2500 [Render]"),r.join(`
|
|
19
19
|
`)}generateDiagramsDoc(s){let e=["# Diagrams",""];e.push("## Overview"),e.push(""),e.push("| Diagram | Type | Description |"),e.push("|---------|------|-------------|");for(let n of s)e.push(`| ${n.title} | \`${n.type.toUpperCase()}\` | Auto-generated |`);e.push("");for(let n of s)e.push(`## ${n.title}`),e.push(""),e.push(`\`TYPE: ${n.type.toUpperCase()}\``),e.push(""),e.push("```mermaid"),e.push(n.content),e.push("```"),e.push("");return e.join(`
|
|
20
|
-
`)}};export{
|
|
20
|
+
`)}};export{b as a,A as b};
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import {a as a$2}from'./chunk-NQMJ3QRX.js';import {a as a$3}from'./chunk-VV3A3UE3.js';import {B,A,z,y}from'./chunk-QDVE7MT3.js';import {a,b}from'./chunk-2XZSFAJF.js';import {a as a$1}from'./chunk-WZAAA7DS.js';import {k}from'./chunk-H7VVRHQZ.js';import {simpleGit}from'simple-git';import*as T from'fs/promises';import*as R from'path';import {parseSync}from'@swc/core';import me from'express';import {Server}from'socket.io';import*as fe from'http';import {marked}from'marked';import*as ce from'net';var U=class{config;mermaidGenerator;markdownGenerator;constructor(t){this.config=t,this.mermaidGenerator=new a,this.markdownGenerator=new b;}async generate(){let t=[];for(let w of this.config.repositories)try{let g=await this.analyzeRepository(w);t.push(g);}catch(g){console.error(`\u274C ${w.name}: ${g.message}`);}let r=this.analyzeCrossRepo(t),e=t.map(w=>w.analysis),n=this.extractCrossRepoLinks(e),s=this.mermaidGenerator.generateAll(e,n),i={generatedAt:new Date().toISOString(),repositories:t,crossRepoAnalysis:r,diagrams:s};return await this.writeDocumentation(i),i}async analyzeRepository(t){let{version:r,commitHash:e}=await this.getRepoInfo(t),n=t.analyzers.map(y=>this.createAnalyzer(y,t)).filter(y=>y!==null),s=Date.now(),i=await Promise.all(n.map(y=>y.analyze())),w=((Date.now()-s)/1e3).toFixed(1);console.log(` Analyzed ${t.displayName} in ${w}s`);let g=this.mergeAnalysisResults(i,t.name,r,e);await this.enrichPagesWithHookGraphQL(g,t.path);let k={totalPages:g.pages.length,totalComponents:g.components.length,totalGraphQLOperations:g.graphqlOperations.length,totalDataFlows:g.dataFlows.length,authRequiredPages:g.pages.filter(y=>y.authentication.required).length,publicPages:g.pages.filter(y=>!y.authentication.required).length};return {name:t.name,displayName:t.displayName,version:r,commitHash:e,analysis:g,summary:k}}async getRepoInfo(t){try{let n=(await simpleGit(t.path).log({n:1})).latest?.hash||"unknown",s="unknown";try{let i=R.join(t.path,"package.json");s=JSON.parse(await T.readFile(i,"utf-8")).version||"unknown";}catch{}return {version:s,commitHash:n}}catch{return {version:"unknown",commitHash:"unknown"}}}createAnalyzer(t,r){switch(t){case "pages":if(r.type==="nextjs"||r.type==="rails"||r.type==="generic")return new y(r);break;case "graphql":return new z(r);case "dataflow":case "components":return new A(r);case "rest-api":case "api":return new B(r)}return null}mergeAnalysisResults(t,r,e,n){let s={repository:r,timestamp:new Date().toISOString(),version:e,commitHash:n,pages:[],graphqlOperations:[],apiCalls:[],components:[],dataFlows:[],apiEndpoints:[],models:[],crossRepoLinks:[]};for(let i of t)i.pages&&s.pages.push(...i.pages),i.graphqlOperations&&s.graphqlOperations.push(...i.graphqlOperations),i.apiCalls&&s.apiCalls.push(...i.apiCalls),i.components&&s.components.push(...i.components),i.dataFlows&&s.dataFlows.push(...i.dataFlows),i.apiEndpoints&&s.apiEndpoints.push(...i.apiEndpoints),i.models&&s.models.push(...i.models),i.crossRepoLinks&&s.crossRepoLinks.push(...i.crossRepoLinks);return s}analyzeCrossRepo(t){let r=[],e=[],n=[],s=[],i=new Map;for(let k of t)for(let y of k.analysis.graphqlOperations){let $=i.get(y.name)||[];$.push(k.name),i.set(y.name,$);}for(let[k,y]of i)y.length>1&&r.push(k);let w=t.filter(k=>k.analysis.pages.length>0),g=t.filter(k=>k.analysis.apiEndpoints.length>0);for(let k of w)for(let y of g)for(let $ of y.analysis.apiEndpoints)e.push({frontend:k.name,backend:y.name,endpoint:$.path,operations:k.analysis.graphqlOperations.filter(A=>A.usedIn.length>0).map(A=>A.name)});return {sharedTypes:r,apiConnections:e,navigationFlows:n,dataFlowAcrossRepos:s}}extractCrossRepoLinks(t){let r=[],e=new Map;for(let n of t)for(let s of n.graphqlOperations){let i=e.get(s.name)||[];i.push(n),e.set(s.name,i);}for(let[n,s]of e)s.length>1&&r.push({sourceRepo:s[0].repository,sourcePath:`graphql/${n}`,targetRepo:s[1].repository,targetPath:`graphql/${n}`,linkType:"graphql-operation",description:`Shared GraphQL operation: ${n}`});return r}async enrichPagesWithHookGraphQL(t,r){let e=t.graphqlOperations.filter(a=>a.type==="query"||a.type==="mutation"||a.type==="subscription"),n=/\.(ts|tsx|js|jsx)$/,s=a=>R.normalize(a).replace(/\\/g,"/"),i=(this.config.analysis?.include||["**/*.ts","**/*.tsx"]).map(String),w=(this.config.analysis?.exclude||[]).map(String),g=(await import('fast-glob')).default,k=await g(i,{cwd:r,ignore:["**/node_modules/**","**/.next/**","**/dist/**","**/build/**","**/coverage/**",...w],onlyFiles:true,unique:true,dot:false}),y=new Set(k.map(s)),$=new Map;for(let a of y){let o=a.replace(n,"");$.has(o)||$.set(o,a);}let A=new Set(["src/"]);for(let a of y){let o=a.indexOf("/src/");o!==-1&&A.add(a.slice(0,o+5));}let N=a=>{let o=s(a).replace(n,""),c=$.get(o);if(c)return c;let l=$.get(o+"/index");return l||null},he=async()=>{let a=["tsconfig.json","jsconfig.json"];for(let o of a)try{let c=await T.readFile(R.join(r,o),"utf-8"),p=JSON.parse(c)?.compilerOptions||{},f=typeof p.baseUrl=="string"?p.baseUrl:void 0,q=typeof p.paths=="object"&&p.paths?p.paths:void 0;return {baseUrl:f,paths:q}}catch{}return {}},{baseUrl:O,paths:X}=await he(),ge=a=>{if(!X)return [];let o=[];for(let[c,l]of Object.entries(X)){if(!c.includes("*")){a===c&&o.push(...l);continue}let[p,f]=c.split("*");if(!a.startsWith(p)||!a.endsWith(f))continue;let q=a.slice(p.length,a.length-f.length);for(let d of l)d.includes("*")?o.push(d.replace("*",q)):o.push(d);}return o},B=(a,o)=>{if(!o)return null;if(o.startsWith(".")){let l=R.dirname(a);return N(R.join(l,o))}if(o.startsWith("@/")){let l=o.replace("@/","");if(O){let p=N(R.join(O,l));if(p)return p}for(let p of A){let f=N(p+l);if(f)return f}return null}let c=ge(o);if(c.length>0)for(let l of c){let p=N(O?R.join(O,l):l);if(p)return p}if(O){let l=N(R.join(O,o));if(l)return l}return null},j=new Map,W=new Map,Z=async a=>{let o=s(a),c=W.get(o);if(c!==void 0)return c;try{let l=R.join(r,o),p=await T.readFile(l,"utf-8");return W.set(o,p),p}catch{return W.set(o,""),null}},ye=async a=>{let o=s(a),c=j.get(o);if(c)return c.map(m=>({spec:m,names:null}));let l=await Z(o);if(!l)return j.set(o,[]),[];let p;try{let m=o.endsWith(".ts")||o.endsWith(".tsx"),h=o.endsWith(".tsx")||o.endsWith(".jsx");p=parseSync(l,{syntax:m?"typescript":"ecmascript",tsx:h,jsx:h,comments:!1});}catch{return j.set(o,[]),[]}let f=new Set,q=[],d=(m,h)=>{typeof m!="string"||m.length===0||(f.add(m),q.push({spec:m,names:h}));},b=m=>{if(!m||typeof m!="object")return;let h=m,v=h;if(v.type==="ImportDeclaration"){if(!v.typeOnly){let x=v.source?.value,u=[],E=v.specifiers||[];for(let S of E){let C=S,I=C.type;if(I==="ImportDefaultSpecifier"||I==="ImportNamespaceSpecifier"){u=null;break}if(I==="ImportSpecifier"){let G=C.imported?.value,P=C.local?.value,z=G||P;z&&u.push(z);}}Array.isArray(u)&&u.length===0&&(u=null),d(x,u);}}else if(v.type==="ExportAllDeclaration")d(v.source?.value,null);else if(v.type==="ExportNamedDeclaration"){let x=v.source?.value,u=v.specifiers||[],E=[];for(let S of u){let C=S;if(C.type==="ExportSpecifier"){let G=C.exported?.value,P=C.orig?.value,z=G||P;z&&E.push(z);}}d(x,E.length>0?E:null);}else if(v.type==="CallExpression"){let x=v.callee||null;if(x?.type==="Identifier"&&x.value==="require"){let u=v.arguments?.[0]?.expression;u?.type==="StringLiteral"&&d(u.value,null);}if(x?.type==="Import"){let u=v.arguments?.[0]?.expression;u?.type==="StringLiteral"&&d(u.value,null);}}for(let x of Object.keys(h)){let u=h[x];if(Array.isArray(u))for(let E of u)b(E);else u&&typeof u=="object"&&b(u);}};b(p);let D=Array.from(f);return j.set(o,D),q.filter(m=>typeof m.spec=="string"&&m.spec.length>0)},Q=new Map,_=async a=>{let o=s(a),c=Q.get(o);if(c)return c;let l=await Z(o);if(!l){let m={named:new Map,stars:[],isBarrel:false};return Q.set(o,m),m}let p;try{let m=o.endsWith(".ts")||o.endsWith(".tsx"),h=o.endsWith(".tsx")||o.endsWith(".jsx");p=parseSync(l,{syntax:m?"typescript":"ecmascript",tsx:h,jsx:h,comments:!1});}catch{let m={named:new Map,stars:[],isBarrel:false};return Q.set(o,m),m}let f=new Map,q=[],d=true,b=p?.body;if(Array.isArray(b))for(let m of b){let h=m,v=h.type;if(v&&v!=="ImportDeclaration"){if(v==="ExportAllDeclaration"){let x=h.source?.value;typeof x=="string"&&q.push(x);continue}if(v==="ExportNamedDeclaration"){let x=h.source?.value,u=h.specifiers||[];if(typeof x=="string"&&Array.isArray(u))for(let E of u){let S=E;if(S.type!=="ExportSpecifier")continue;let I=S.exported?.value,G=S.orig?.value,P=I||G;P&&f.set(P,x);}continue}d=false;}}else d=false;let D={named:f,stars:q,isBarrel:d};return Q.set(o,D),D},V=async(a,o,c)=>{let l=s(a);if(c.has(l))return null;c.add(l);let p=await _(l),f=p.named.get(o);if(f)return B(l,f);for(let q of p.stars){let d=B(l,q);if(!d)continue;let b=await V(d,o,c);if(b)return b}return null},Y=new Map;for(let a of e){let o=new Set;a.filePath&&o.add(a.filePath);for(let c of a.usedIn||[])o.add(c);for(let c of o){let l=Y.get(c)||[];l.push({opName:a.name,opType:a.type}),Y.set(c,l);}}let ve=a=>{if(!a)return null;let o=[`src/pages/${a}`,`pages/${a}`,`src/app/${a}`,`app/${a}`,`frontend/src/pages/${a}`,`frontend/src/app/${a}`,`app/javascript/pages/${a}`,`app/javascript/app/${a}`];for(let p of o){if(y.has(p))return p;let f=N(p);if(f)return f}let c=[],l=[`/pages/${a}`,`/app/${a}`,`/${a}`];for(let p of y)for(let f of l)if(p.endsWith(f)&&!(f.startsWith("/pages/")&&!p.includes("/pages/"))&&!(f.startsWith("/app/")&&!p.includes("/app/"))){c.push(p);break}return c.length===0?null:(c.sort((p,f)=>p.length-f.length),c[0])},J=new Map,H=new Map;for(let a of t.pages){let o=ve(a.filePath);if(!o)continue;let c=new Map,l=new Set,p=[{f:o,depth:0}],f=30,q=2e4;for(;p.length>0;){let d=p.shift();if(!d)break;if(l.has(d.f))continue;if(l.add(d.f),l.size>=q)break;let b=Y.get(d.f);if(b)for(let m of b){let h=c.get(m.opName);(!h||d.depth<h.distance)&&c.set(m.opName,{opName:m.opName,opType:m.opType,sourceFile:d.f,distance:d.depth});}if(d.depth>=f)continue;let D=await ye(d.f);for(let m of D){let h=B(d.f,m.spec);if(h){if(l.has(h)||p.push({f:h,depth:d.depth+1}),Array.isArray(m.names)&&m.names.length>0){if((await _(h)).isBarrel)for(let x of m.names){let u=await V(h,x,new Set);u&&!l.has(u)&&p.push({f:u,depth:d.depth+2});}}else if(m.names===null){let v=await _(h);if(v.isBarrel){for(let x of v.stars){let u=B(h,x);u&&!l.has(u)&&p.push({f:u,depth:d.depth+2});}for(let x of v.named.values()){let u=B(h,x);u&&!l.has(u)&&p.push({f:u,depth:d.depth+2});}}}}}}J.set(a.path,{page:a,entryFile:o,bestByOp:c});for(let d of l)H.set(d,(H.get(d)||0)+1);}let we=J.size,xe=Array.from(H.values()).sort((a,o)=>a-o),be=((a,o)=>{if(a.length===0)return 0;let c=Math.min(a.length-1,Math.max(0,Math.floor(a.length*o)));return a[c]})(xe,.9),ke=Math.max(10,be),qe=Math.max(2,Math.floor(we*.05));for(let{page:a,bestByOp:o}of J.values()){let c=new Set((a.dataFetching||[]).map(l=>l.operationName?.replace(/^[→\->\s]+/,"")||""));for(let{opName:l,opType:p,sourceFile:f,distance:q}of o.values()){if(c.has(l))continue;c.add(l);let d=H.get(f)||0,b;q===0?b=void 0:d>=ke?b=`common:${f}`:q<=2||d<=qe?b=`close:${f}`:b=`indirect:${f}`,a.dataFetching.push({type:p==="mutation"?"useMutation":p==="subscription"?"useSubscription":"useQuery",operationName:l,source:b});}}}async writeDocumentation(t){let r=this.config.outputDir;await T.mkdir(r,{recursive:true});let e=this.markdownGenerator.generateDocumentation(t);for(let[s,i]of e){let w=R.join(r,s),g=R.dirname(w);await T.mkdir(g,{recursive:true}),await T.writeFile(w,i,"utf-8");}let n=R.join(r,"report.json");await T.writeFile(n,JSON.stringify(t,null,2),"utf-8");}};function Me(L){return new Promise(t=>{let r=ce.createServer();r.once("error",e=>{e.code,t(false);}),r.once("listening",()=>{r.close(),t(true);}),r.listen(L);})}async function de(L,t=10){for(let r=0;r<t;r++){let e=L+r;if(await Me(e))return e}throw new Error(`No available port found between ${L} and ${L+t-1}`)}var ue=class{config;port;app;server;io;engine;currentReport=null;envResult=null;railsAnalysis=null;constructor(t,r=3030){this.config=t,this.port=r,this.app=me(),this.server=fe.createServer(this.app),this.io=new Server(this.server),this.engine=new U(t),this.setupRoutes(),this.setupSocketIO();}setupRoutes(){this.app.use("/assets",me.static(R.join(this.config.outputDir,"assets"))),["common.css","page-map.css","docs.css","rails-map.css"].forEach(e=>{this.app.get(`/${e}`,async(n,s)=>{let i=[R.join(R.dirname(new URL(import.meta.url).pathname),"generators","assets",e),R.join(R.dirname(new URL(import.meta.url).pathname),"..","generators","assets",e),R.join(process.cwd(),"dist","generators","assets",e),R.join(process.cwd(),"src","generators","assets",e)];for(let w of i)try{let g=await T.readFile(w,"utf-8");s.type("text/css").send(g);return}catch{}s.status(404).send("CSS not found");});}),["favicon.ico","favicon.svg","favicon-96x96.png","apple-touch-icon.png","site.webmanifest","web-app-manifest-192x192.png","web-app-manifest-512x512.png"].forEach(e=>{(e==="favicon.ico"?[`/${e}`,`/favicon/${e}`]:[`/favicon/${e}`]).forEach(s=>{this.app.get(s,async(i,w)=>{let g=[R.join(R.dirname(new URL(import.meta.url).pathname),"generators","assets","favicon",e),R.join(R.dirname(new URL(import.meta.url).pathname),"..","generators","assets","favicon",e),R.join(process.cwd(),"dist","generators","assets","favicon",e),R.join(process.cwd(),"src","generators","assets","favicon",e)];for(let k of g)try{let y=await T.readFile(k),$=e.split(".").pop(),A={ico:"image/x-icon",svg:"image/svg+xml",png:"image/png",webmanifest:"application/manifest+json"};w.type(A[$||""]||"application/octet-stream").send(y);return}catch{}w.status(404).send("File not found");});});}),this.app.get("/",(e,n)=>{n.redirect("/page-map");}),this.app.get("/page-map",(e,n)=>{if(!this.currentReport){n.status(503).send("Documentation not ready yet");return}let s=new a$1;n.send(s.generatePageMapHtml(this.currentReport,{envResult:this.envResult,railsAnalysis:this.railsAnalysis}));}),this.app.get("/rails-map",(e,n)=>{if(!this.railsAnalysis){n.status(404).send("No Rails environment detected");return}let s=new a$2;n.send(s.generateFromResult(this.railsAnalysis));}),this.app.get("/docs",async(e,n)=>{n.send(await this.renderPage("index"));}),this.app.get("/docs/*path",async(e,n)=>{let s=e.params.path,i=Array.isArray(s)?s.join("/"):s||"index";n.send(await this.renderPage(i));}),this.app.get("/api/report",(e,n)=>{n.json(this.currentReport);}),this.app.get("/api/env",(e,n)=>{n.json(this.envResult);}),this.app.get("/api/rails",(e,n)=>{this.railsAnalysis?n.json(this.railsAnalysis):n.status(404).json({error:"No Rails analysis available"});}),this.app.get("/api/diagram/:name",(e,n)=>{let s=this.currentReport?.diagrams.find(i=>i.title.toLowerCase().replace(/\s+/g,"-")===e.params.name);s?n.json(s):n.status(404).json({error:"Diagram not found"});}),this.app.post("/api/regenerate",async(e,n)=>{try{await this.regenerate(),n.json({success:!0});}catch(s){n.status(500).json({error:s.message});}});}setupSocketIO(){this.io.on("connection",t=>{t.on("disconnect",()=>{});});}async renderPage(t){let r=t.replace(/\.md$/,""),e=R.join(this.config.outputDir,`${r}.md`),n="";try{let s=await T.readFile(e,"utf-8"),i=await marked.parse(s);i=i.replace(/<pre><code class="language-mermaid">([\s\S]*?)<\/code><\/pre>/g,'<div class="mermaid">$1</div>'),i=i.replace(/<table>/g,'<div class="table-wrapper"><table>'),i=i.replace(/<\/table>/g,"</table></div>"),n=i;}catch(s){let i=s;if(i.code==="ENOENT"){let w=this.currentReport?.repositories.map(g=>g.name)||[];n=`
|
|
1
|
+
import {a as a$2}from'./chunk-LHP2OKKA.js';import {a as a$3}from'./chunk-VV3A3UE3.js';import {C,B,A,z,b as b$1}from'./chunk-IGRCLYVZ.js';import {a,b}from'./chunk-QBSB6BIU.js';import {a as a$1}from'./chunk-ATRSGO6O.js';import {k}from'./chunk-H7VVRHQZ.js';import {simpleGit}from'simple-git';import*as L from'fs/promises';import*as M from'path';import {parseSync}from'@swc/core';import fe from'express';import {Server}from'socket.io';import*as ge from'http';import {marked}from'marked';import*as me from'net';var W=class{config;mermaidGenerator;markdownGenerator;constructor(s){this.config=s,this.mermaidGenerator=new a,this.markdownGenerator=new b;}async generate(){let s=[];for(let k of this.config.repositories)try{let x=await this.analyzeRepository(k);s.push(x);}catch(x){console.error(`\u274C ${k.name}: ${x.message}`);}let r=this.analyzeCrossRepo(s),e=s.map(k=>k.analysis),a=this.extractCrossRepoLinks(e),o=this.mermaidGenerator.generateAll(e,a),i={generatedAt:new Date().toISOString(),repositories:s,crossRepoAnalysis:r,diagrams:o};return await this.writeDocumentation(i),i}async analyzeRepository(s){let{version:r,commitHash:e}=await this.getRepoInfo(s),a=s.analyzers.map(b=>this.createAnalyzer(b,s)).filter(b=>b!==null),o=Date.now(),i=await Promise.all(a.map(b=>b.analyze())),k=((Date.now()-o)/1e3).toFixed(1);console.log(` Analyzed ${s.displayName} in ${k}s`);let x=this.mergeAnalysisResults(i,s.name,r,e);await this.enrichPagesWithHookGraphQL(x,s.path);let $={totalPages:x.pages.length,totalComponents:x.components.length,totalGraphQLOperations:x.graphqlOperations.length,totalDataFlows:x.dataFlows.length,authRequiredPages:x.pages.filter(b=>b.authentication.required).length,publicPages:x.pages.filter(b=>!b.authentication.required).length};return {name:s.name,displayName:s.displayName,version:r,commitHash:e,analysis:x,summary:$}}async getRepoInfo(s){try{let a=(await simpleGit(s.path).log({n:1})).latest?.hash||"unknown",o="unknown";try{let i=M.join(s.path,"package.json");o=JSON.parse(await L.readFile(i,"utf-8")).version||"unknown";}catch{}return {version:o,commitHash:a}}catch{return {version:"unknown",commitHash:"unknown"}}}createAnalyzer(s,r){switch(s){case "pages":if(r.type==="nextjs"||r.type==="rails"||r.type==="generic")return new z(r);break;case "graphql":return new A(r);case "dataflow":case "components":return new B(r);case "rest-api":case "api":return new C(r)}return null}mergeAnalysisResults(s,r,e,a){let o={repository:r,timestamp:new Date().toISOString(),version:e,commitHash:a,coverage:{tsFilesScanned:0,tsParseFailures:0,graphqlParseFailures:0,codegenFilesDetected:0,codegenFilesParsed:0,codegenExportsFound:0},pages:[],graphqlOperations:[],apiCalls:[],components:[],dataFlows:[],apiEndpoints:[],models:[],crossRepoLinks:[]};for(let i of s)i.coverage&&o.coverage&&(o.coverage.tsFilesScanned+=i.coverage.tsFilesScanned||0,o.coverage.tsParseFailures+=i.coverage.tsParseFailures||0,o.coverage.graphqlParseFailures+=i.coverage.graphqlParseFailures||0,o.coverage.codegenFilesDetected+=i.coverage.codegenFilesDetected||0,o.coverage.codegenFilesParsed+=i.coverage.codegenFilesParsed||0,o.coverage.codegenExportsFound+=i.coverage.codegenExportsFound||0),i.pages&&o.pages.push(...i.pages),i.graphqlOperations&&o.graphqlOperations.push(...i.graphqlOperations),i.apiCalls&&o.apiCalls.push(...i.apiCalls),i.components&&o.components.push(...i.components),i.dataFlows&&o.dataFlows.push(...i.dataFlows),i.apiEndpoints&&o.apiEndpoints.push(...i.apiEndpoints),i.models&&o.models.push(...i.models),i.crossRepoLinks&&o.crossRepoLinks.push(...i.crossRepoLinks);return o}analyzeCrossRepo(s){let r=[],e=[],a=[],o=[],i=new Map;for(let $ of s)for(let b of $.analysis.graphqlOperations){let E=i.get(b.name)||[];E.push($.name),i.set(b.name,E);}for(let[$,b]of i)b.length>1&&r.push($);let k=s.filter($=>$.analysis.pages.length>0),x=s.filter($=>$.analysis.apiEndpoints.length>0);for(let $ of k)for(let b of x)for(let E of b.analysis.apiEndpoints)e.push({frontend:$.name,backend:b.name,endpoint:E.path,operations:$.analysis.graphqlOperations.filter(N=>N.usedIn.length>0).map(N=>N.name)});return {sharedTypes:r,apiConnections:e,navigationFlows:a,dataFlowAcrossRepos:o}}extractCrossRepoLinks(s){let r=[],e=new Map;for(let a of s)for(let o of a.graphqlOperations){let i=e.get(o.name)||[];i.push(a),e.set(o.name,i);}for(let[a,o]of e)o.length>1&&r.push({sourceRepo:o[0].repository,sourcePath:`graphql/${a}`,targetRepo:o[1].repository,targetPath:`graphql/${a}`,linkType:"graphql-operation",description:`Shared GraphQL operation: ${a}`});return r}async enrichPagesWithHookGraphQL(s,r){let e=s.graphqlOperations.filter(n=>n.type==="query"||n.type==="mutation"||n.type==="subscription"),a=/\.(ts|tsx|js|jsx)$/,o=n=>M.normalize(n).replace(/\\/g,"/"),i=(this.config.analysis?.include||["**/*.ts","**/*.tsx"]).map(String),k=(this.config.analysis?.exclude||[]).map(String),x=(await import('fast-glob')).default,$=await x(i,{cwd:r,ignore:["**/node_modules/**","**/.next/**","**/dist/**","**/build/**","**/coverage/**",...k],onlyFiles:true,unique:true,dot:false}),b=new Set($.map(o)),E=new Map;for(let n of b){let t=n.replace(a,"");E.has(t)||E.set(t,n);}let N=new b$1(r,b),Z=new Set(["src/"]);for(let n of b){let t=n.indexOf("/src/");t!==-1&&Z.add(n.slice(0,t+5));}let O=n=>{let t=o(n).replace(a,""),p=E.get(t);if(p)return p;let c=E.get(t+"/index");return c||null},ye=async()=>{let n=["tsconfig.json","jsconfig.json"];for(let t of n)try{let p=await L.readFile(M.join(r,t),"utf-8"),l=JSON.parse(p)?.compilerOptions||{},d=typeof l.baseUrl=="string"?l.baseUrl:void 0,R=typeof l.paths=="object"&&l.paths?l.paths:void 0;return {baseUrl:d,paths:R}}catch{}return {}},{baseUrl:F,paths:V}=await ye(),ve=n=>{if(!V)return [];let t=[];for(let[p,c]of Object.entries(V)){if(!p.includes("*")){n===p&&t.push(...c);continue}let[l,d]=p.split("*");if(!n.startsWith(l)||!n.endsWith(d))continue;let R=n.slice(l.length,n.length-d.length);for(let w of c)w.includes("*")?t.push(w.replace("*",R)):t.push(w);}return t},B=(n,t)=>{if(!t)return null;let p=N.resolve(n,t);if(p)return p.file;if(t.startsWith(".")){let l=M.dirname(n);return O(M.join(l,t))}if(t.startsWith("@/")){let l=t.replace("@/","");if(F){let d=O(M.join(F,l));if(d)return d}for(let d of Z){let R=O(d+l);if(R)return R}return null}let c=ve(t);if(c.length>0)for(let l of c){let d=O(F?M.join(F,l):l);if(d)return d}if(F){let l=O(M.join(F,t));if(l)return l}return null},j=new Map,Y=new Map,Q=async n=>{let t=o(n),p=Y.get(t);if(p!==void 0)return p;try{let c=M.join(r,t),l=await L.readFile(c,"utf-8");return Y.set(t,l),l}catch{return Y.set(t,""),null}},we=async n=>{let t=o(n),p=j.get(t);if(p)return p.map(h=>({spec:h,names:null}));let c=await Q(t);if(!c)return j.set(t,[]),[];let l;try{let h=t.endsWith(".ts")||t.endsWith(".tsx"),g=t.endsWith(".tsx")||t.endsWith(".jsx");l=parseSync(c,{syntax:h?"typescript":"ecmascript",tsx:g,jsx:g,comments:!1});}catch{return j.set(t,[]),[]}let d=new Set,R=[],w=(h,g,m)=>{typeof h!="string"||h.length===0||(d.add(h),R.push({spec:h,names:g,pos:m}));},f=h=>{if(!h||typeof h!="object")return;let g=h,m=g;if(m.type==="ImportDeclaration"){if(!m.typeOnly){let v=m.source?.value,u=[],y=m.specifiers||[];for(let C of y){let q=C,D=q.type;if(D==="ImportDefaultSpecifier"||D==="ImportNamespaceSpecifier"){u=null;break}if(D==="ImportSpecifier"){let G=q.imported?.value,P=q.local?.value,z=G||P;z&&u.push(z);}}Array.isArray(u)&&u.length===0&&(u=null),w(v,u,m.span?.start);}}else if(m.type==="ExportAllDeclaration")w(m.source?.value,null,m.span?.start);else if(m.type==="ExportNamedDeclaration"){let v=m.source?.value,u=m.specifiers||[],y=[];for(let C of u){let q=C;if(q.type==="ExportSpecifier"){let G=q.exported?.value,P=q.orig?.value,z=G||P;z&&y.push(z);}}w(v,y.length>0?y:null,m.span?.start);}else if(m.type==="CallExpression"){let v=m.callee||null;if(v?.type==="Identifier"&&v.value==="require"){let u=m.arguments?.[0]?.expression;u?.type==="StringLiteral"&&w(u.value,null,m.span?.start);}if(v?.type==="Import"){let u=m.arguments?.[0]?.expression;u?.type==="StringLiteral"&&w(u.value,null,m.span?.start);}}for(let v of Object.keys(g)){let u=g[v];if(Array.isArray(u))for(let y of u)f(y);else u&&typeof u=="object"&&f(u);}};f(l);let A=Array.from(d);return j.set(t,A),R.filter(h=>typeof h.spec=="string"&&h.spec.length>0)},xe=(n,t)=>{if(t===void 0||t<0)return;let p=1;for(let c=0;c<n.length&&c<t;c++)n.charCodeAt(c)===10&&p++;return p},be=(n,t)=>{if(t<0)return;let p=1;for(let c=0;c<n.length&&c<t;c++)n.charCodeAt(c)===10&&p++;return p},H=new Map,_=async n=>{let t=o(n),p=H.get(t);if(p)return p;let c=await Q(t);if(!c){let h={named:new Map,stars:[],isBarrel:false};return H.set(t,h),h}let l;try{let h=t.endsWith(".ts")||t.endsWith(".tsx"),g=t.endsWith(".tsx")||t.endsWith(".jsx");l=parseSync(c,{syntax:h?"typescript":"ecmascript",tsx:g,jsx:g,comments:!1});}catch{let h={named:new Map,stars:[],isBarrel:false};return H.set(t,h),h}let d=new Map,R=[],w=true,f=l?.body;if(Array.isArray(f))for(let h of f){let g=h,m=g.type;if(m&&m!=="ImportDeclaration"){if(m==="ExportAllDeclaration"){let v=g.source?.value;typeof v=="string"&&R.push(v);continue}if(m==="ExportNamedDeclaration"){let v=g.source?.value,u=g.specifiers||[];if(typeof v=="string"&&Array.isArray(u))for(let y of u){let C=y;if(C.type!=="ExportSpecifier")continue;let D=C.exported?.value,G=C.orig?.value,P=D||G;P&&d.set(P,v);}continue}w=false;}}else w=false;let A={named:d,stars:R,isBarrel:w};return H.set(t,A),A},ee=async(n,t,p)=>{let c=o(n);if(p.has(c))return null;p.add(c);let l=await _(c),d=l.named.get(t);if(d)return B(c,d);for(let R of l.stars){let w=B(c,R);if(!w)continue;let f=await ee(w,t,p);if(f)return f}return null},K=new Map;for(let n of e){let t=new Set;n.filePath&&t.add(n.filePath);for(let p of n.usedIn||[])t.add(p);for(let p of t){let c=K.get(p)||[];c.push({opName:n.name,opType:n.type}),K.set(p,c);}}let ke=n=>{if(!n)return null;let t=[`src/pages/${n}`,`pages/${n}`,`src/app/${n}`,`app/${n}`,`frontend/src/pages/${n}`,`frontend/src/app/${n}`,`app/javascript/pages/${n}`,`app/javascript/app/${n}`];for(let l of t){if(b.has(l))return l;let d=O(l);if(d)return d}let p=[],c=[`/pages/${n}`,`/app/${n}`,`/${n}`];for(let l of b)for(let d of c)if(l.endsWith(d)&&!(d.startsWith("/pages/")&&!l.includes("/pages/"))&&!(d.startsWith("/app/")&&!l.includes("/app/"))){p.push(l);break}return p.length===0?null:(p.sort((l,d)=>l.length-d.length),p[0])},J=new Map,U=new Map;for(let n of s.pages){let t=ke(n.filePath);if(!t)continue;let p=new Map,c=new Set,l=[{f:t,depth:0}],d=new Map,R=30,w=2e4;for(;l.length>0;){let f=l.shift();if(!f)break;if(c.has(f.f))continue;if(c.add(f.f),c.size>=w)break;let A=K.get(f.f);if(A)for(let g of A){let m=p.get(g.opName);(!m||f.depth<m.distance)&&p.set(g.opName,{opName:g.opName,opType:g.opType,sourceFile:f.f,distance:f.depth});}if(f.depth>=R)continue;let h=await we(f.f);for(let g of h){let m=B(f.f,g.spec);if(m){if(!c.has(m)&&(l.push({f:m,depth:f.depth+1}),!d.has(m))){let v=await Q(f.f),u=v?xe(v,g.pos):void 0,y=Array.isArray(g.names)&&g.names.length>0?`names:${g.names.join(",")}`:void 0;d.set(m,{from:f.f,spec:g.spec,line:u,detail:y});}if(Array.isArray(g.names)&&g.names.length>0){if((await _(m)).isBarrel)for(let u of g.names){let y=await ee(m,u,new Set);y&&!c.has(y)&&(l.push({f:y,depth:f.depth+2}),d.has(y)||d.set(y,{from:m,spec:`re-export:${u}`,line:void 0,detail:"barrel"}));}}else if(g.names===null){let v=await _(m);if(v.isBarrel){for(let u of v.stars){let y=B(m,u);y&&!c.has(y)&&l.push({f:y,depth:f.depth+2});}for(let u of v.named.values()){let y=B(m,u);y&&!c.has(y)&&l.push({f:y,depth:f.depth+2});}}}}}}J.set(n.path,{page:n,entryFile:t,parent:d,bestByOp:p});for(let f of c)U.set(f,(U.get(f)||0)+1);}let qe=J.size,Re=Array.from(U.values()).sort((n,t)=>n-t),$e=((n,t)=>{if(n.length===0)return 0;let p=Math.min(n.length-1,Math.max(0,Math.floor(n.length*t)));return n[p]})(Re,.9),Me=Math.max(10,$e),Te=Math.max(2,Math.floor(qe*.05));for(let{page:n,entryFile:t,parent:p,bestByOp:c}of J.values()){let l=new Set((n.dataFetching||[]).map(d=>d.operationName?.replace(/^[→\->\s]+/,"")||""));for(let{opName:d,opType:R,sourceFile:w,distance:f}of c.values()){if(l.has(d))continue;l.add(d);let A=U.get(w)||0,h;f===0?h=void 0:A>=Me?h=`common:${w}`:f<=2||A<=Te?h=`close:${w}`:h=`indirect:${w}`;let g=f===0||f<=2?"certain":h?.startsWith("common:")?"unknown":"likely",m=[];if(f>0&&w!==t){let u=[],y=w,C=new Set;for(;y!==t&&!C.has(y);){C.add(y);let q=p.get(y);if(!q)break;u.push({from:q.from,to:y,spec:q.spec,line:q.line,detail:q.detail}),y=q.from;}u.reverse();for(let q of u)m.push({kind:"import-edge",file:q.from,line:q.line,detail:`${q.spec} -> ${q.to}${q.detail?` (${q.detail})`:""}`});}let v=await Q(w);if(v){let u=v.indexOf(`${d}Document`)>=0?v.indexOf(`${d}Document`):v.indexOf(d),y=u>=0?be(v,u):void 0;m.push({kind:"operation-reference",file:w,line:y,detail:`ref:${d}`});}else m.push({kind:"operation-reference",file:w,detail:`ref:${d}`});n.dataFetching.push({type:R==="mutation"?"useMutation":R==="subscription"?"useSubscription":"useQuery",operationName:d,source:h,confidence:g,evidence:m.length>0?m:void 0});}}}async writeDocumentation(s){let r=this.config.outputDir;await L.mkdir(r,{recursive:true});let e=this.markdownGenerator.generateDocumentation(s);for(let[o,i]of e){let k=M.join(r,o),x=M.dirname(k);await L.mkdir(x,{recursive:true}),await L.writeFile(k,i,"utf-8");}let a=M.join(r,"report.json");await L.writeFile(a,JSON.stringify(s,null,2),"utf-8");}};function Ae(S){return new Promise(s=>{let r=me.createServer();r.once("error",e=>{e.code,s(false);}),r.once("listening",()=>{r.close(),s(true);}),r.listen(S);})}async function ue(S,s=10){for(let r=0;r<s;r++){let e=S+r;if(await Ae(e))return e}throw new Error(`No available port found between ${S} and ${S+s-1}`)}var he=class{config;port;app;server;io;engine;currentReport=null;envResult=null;railsAnalysis=null;constructor(s,r=3030){this.config=s,this.port=r,this.app=fe(),this.server=ge.createServer(this.app),this.io=new Server(this.server),this.engine=new W(s),this.setupRoutes(),this.setupSocketIO();}setupRoutes(){this.app.use("/assets",fe.static(M.join(this.config.outputDir,"assets"))),["common.css","page-map.css","docs.css","rails-map.css"].forEach(e=>{this.app.get(`/${e}`,async(a,o)=>{let i=[M.join(M.dirname(new URL(import.meta.url).pathname),"generators","assets",e),M.join(M.dirname(new URL(import.meta.url).pathname),"..","generators","assets",e),M.join(process.cwd(),"dist","generators","assets",e),M.join(process.cwd(),"src","generators","assets",e)];for(let k of i)try{let x=await L.readFile(k,"utf-8");o.type("text/css").send(x);return}catch{}o.status(404).send("CSS not found");});}),["favicon.ico","favicon.svg","favicon-96x96.png","apple-touch-icon.png","site.webmanifest","web-app-manifest-192x192.png","web-app-manifest-512x512.png"].forEach(e=>{(e==="favicon.ico"?[`/${e}`,`/favicon/${e}`]:[`/favicon/${e}`]).forEach(o=>{this.app.get(o,async(i,k)=>{let x=[M.join(M.dirname(new URL(import.meta.url).pathname),"generators","assets","favicon",e),M.join(M.dirname(new URL(import.meta.url).pathname),"..","generators","assets","favicon",e),M.join(process.cwd(),"dist","generators","assets","favicon",e),M.join(process.cwd(),"src","generators","assets","favicon",e)];for(let $ of x)try{let b=await L.readFile($),E=e.split(".").pop(),N={ico:"image/x-icon",svg:"image/svg+xml",png:"image/png",webmanifest:"application/manifest+json"};k.type(N[E||""]||"application/octet-stream").send(b);return}catch{}k.status(404).send("File not found");});});}),this.app.get("/",(e,a)=>{a.redirect("/page-map");}),this.app.get("/page-map",(e,a)=>{if(!this.currentReport){a.status(503).send("Documentation not ready yet");return}let o=new a$1;a.send(o.generatePageMapHtml(this.currentReport,{envResult:this.envResult,railsAnalysis:this.railsAnalysis}));}),this.app.get("/rails-map",(e,a)=>{if(!this.railsAnalysis){a.status(404).send("No Rails environment detected");return}let o=new a$2;a.send(o.generateFromResult(this.railsAnalysis));}),this.app.get("/docs",async(e,a)=>{a.send(await this.renderPage("index"));}),this.app.get("/docs/*path",async(e,a)=>{let o=e.params.path,i=Array.isArray(o)?o.join("/"):o||"index";a.send(await this.renderPage(i));}),this.app.get("/api/report",(e,a)=>{a.json(this.currentReport);}),this.app.get("/api/env",(e,a)=>{a.json(this.envResult);}),this.app.get("/api/rails",(e,a)=>{this.railsAnalysis?a.json(this.railsAnalysis):a.status(404).json({error:"No Rails analysis available"});}),this.app.get("/api/diagram/:name",(e,a)=>{let o=this.currentReport?.diagrams.find(i=>i.title.toLowerCase().replace(/\s+/g,"-")===e.params.name);o?a.json(o):a.status(404).json({error:"Diagram not found"});}),this.app.post("/api/regenerate",async(e,a)=>{try{await this.regenerate(),a.json({success:!0});}catch(o){a.status(500).json({error:o.message});}});}setupSocketIO(){this.io.on("connection",s=>{s.on("disconnect",()=>{});});}async renderPage(s){let r=s.replace(/\.md$/,""),e=M.join(this.config.outputDir,`${r}.md`),a="";try{let o=await L.readFile(e,"utf-8"),i=await marked.parse(o);i=i.replace(/<pre><code class="language-mermaid">([\s\S]*?)<\/code><\/pre>/g,'<div class="mermaid">$1</div>'),i=i.replace(/<table>/g,'<div class="table-wrapper"><table>'),i=i.replace(/<\/table>/g,"</table></div>"),a=i;}catch(o){let i=o;if(i.code==="ENOENT"){let k=this.currentReport?.repositories.map(x=>x.name)||[];a=`
|
|
2
2
|
<h1>Page not found</h1>
|
|
3
3
|
<p>The requested path <code>${r}</code> does not exist.</p>
|
|
4
|
-
${
|
|
4
|
+
${k.length>0?`
|
|
5
5
|
<p>Available repositories:</p>
|
|
6
|
-
<ul>${
|
|
6
|
+
<ul>${k.map(x=>`<li><a href="/docs/repos/${x}">${x}</a></li>`).join("")}</ul>
|
|
7
7
|
`:""}
|
|
8
8
|
<p><a href="/">\u2190 Back to home</a></p>
|
|
9
|
-
`;}else console.error(`\u26A0\uFE0F Error reading ${e}: ${i.message}`),
|
|
9
|
+
`;}else console.error(`\u26A0\uFE0F Error reading ${e}: ${i.message}`),a=`<h1>Error</h1><p>Failed to load page: ${i.message}</p>`;}return this.getHtmlTemplate(a)}getGraphQLData(){if(!this.currentReport)return "[]";let s=[];for(let r of this.currentReport.repositories)for(let e of r.analysis?.graphqlOperations||[])s.push({name:e.name,type:e.type,returnType:e.returnType,variables:e.variables,fields:e.fields,usedIn:e.usedIn});return JSON.stringify(s)}getApiCallsData(){if(!this.currentReport)return "[]";let s=[];for(let r of this.currentReport.repositories)for(let e of r.analysis?.apiCalls||[])s.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(s)}getHtmlTemplate(s){let r=this.getGraphQLData(),e=this.getApiCallsData();return `<!DOCTYPE html>
|
|
10
10
|
<html lang="en">
|
|
11
11
|
<head>
|
|
12
12
|
<meta charset="UTF-8">
|
|
@@ -104,11 +104,11 @@ import {a as a$2}from'./chunk-NQMJ3QRX.js';import {a as a$3}from'./chunk-VV3A3UE
|
|
|
104
104
|
<div class="nav-group">
|
|
105
105
|
<span class="nav-group-title">Documentation</span>
|
|
106
106
|
<div class="nav-subitems">
|
|
107
|
-
${this.config.repositories.map(
|
|
108
|
-
<a href="/docs/repos/${
|
|
109
|
-
<a href="/docs/repos/${
|
|
110
|
-
<a href="/docs/repos/${
|
|
111
|
-
<a href="/docs/repos/${
|
|
107
|
+
${this.config.repositories.map(a=>`
|
|
108
|
+
<a href="/docs/repos/${a.name}/pages">Pages</a>
|
|
109
|
+
<a href="/docs/repos/${a.name}/components">Components</a>
|
|
110
|
+
<a href="/docs/repos/${a.name}/graphql">GraphQL</a>
|
|
111
|
+
<a href="/docs/repos/${a.name}/dataflow">Data Flow</a>
|
|
112
112
|
`).join("")}
|
|
113
113
|
</div>
|
|
114
114
|
</div>
|
|
@@ -124,7 +124,7 @@ import {a as a$2}from'./chunk-NQMJ3QRX.js';import {a as a$3}from'./chunk-VV3A3UE
|
|
|
124
124
|
</aside>
|
|
125
125
|
<div class="content-area">
|
|
126
126
|
<div class="content">
|
|
127
|
-
${
|
|
127
|
+
${s}
|
|
128
128
|
</div>
|
|
129
129
|
</div>
|
|
130
130
|
</div>
|
|
@@ -469,7 +469,9 @@ import {a as a$2}from'./chunk-NQMJ3QRX.js';import {a as a$3}from'./chunk-VV3A3UE
|
|
|
469
469
|
|
|
470
470
|
if (op.usedIn?.length) {
|
|
471
471
|
html += '<div class="detail-section"><h4>Used In</h4><div style="max-height:100px;overflow-y:auto">';
|
|
472
|
-
op.usedIn.forEach(f => {
|
|
472
|
+
op.usedIn.forEach(f => {
|
|
473
|
+
html += \`<p><code>\${escapeHtml(f)}</code></p>\`;
|
|
474
|
+
});
|
|
473
475
|
html += '</div></div>';
|
|
474
476
|
}
|
|
475
477
|
} else {
|
|
@@ -508,7 +510,9 @@ import {a as a$2}from'./chunk-NQMJ3QRX.js';import {a as a$3}from'./chunk-VV3A3UE
|
|
|
508
510
|
|
|
509
511
|
if (partialMatch.usedIn?.length) {
|
|
510
512
|
html += '<div class="detail-section"><h4>Used In</h4><div style="max-height:100px;overflow-y:auto">';
|
|
511
|
-
partialMatch.usedIn.forEach(f => {
|
|
513
|
+
partialMatch.usedIn.forEach(f => {
|
|
514
|
+
html += \`<p><code>\${escapeHtml(f)}</code></p>\`;
|
|
515
|
+
});
|
|
512
516
|
html += '</div></div>';
|
|
513
517
|
}
|
|
514
518
|
} else {
|
|
@@ -590,6 +594,15 @@ import {a as a$2}from'./chunk-NQMJ3QRX.js';import {a as a$3}from'./chunk-VV3A3UE
|
|
|
590
594
|
return badges[type] || badges.unknown;
|
|
591
595
|
}
|
|
592
596
|
|
|
597
|
+
function escapeHtml(str) {
|
|
598
|
+
return String(str ?? '')
|
|
599
|
+
.replace(/&/g, '&')
|
|
600
|
+
.replace(/</g, '<')
|
|
601
|
+
.replace(/>/g, '>')
|
|
602
|
+
.replace(/"/g, '"')
|
|
603
|
+
.replace(/'/g, ''');
|
|
604
|
+
}
|
|
605
|
+
|
|
593
606
|
function closeModal() {
|
|
594
607
|
document.getElementById('detailModal').classList.remove('open');
|
|
595
608
|
modalHistory = [];
|
|
@@ -634,7 +647,8 @@ import {a as a$2}from'./chunk-NQMJ3QRX.js';import {a as a$3}from'./chunk-VV3A3UE
|
|
|
634
647
|
el.addEventListener('click', (e) => {
|
|
635
648
|
e.stopPropagation();
|
|
636
649
|
const opName = el.dataset.op || el.textContent?.replace(/^[QM]:\\s*/, '') || '';
|
|
637
|
-
|
|
650
|
+
const confidence = el.dataset.confidence || '';
|
|
651
|
+
if (opName) showGraphQLDetail(opName, el, confidence);
|
|
638
652
|
});
|
|
639
653
|
});
|
|
640
654
|
|
|
@@ -668,7 +682,7 @@ import {a as a$2}from'./chunk-NQMJ3QRX.js';import {a as a$3}from'./chunk-VV3A3UE
|
|
|
668
682
|
});
|
|
669
683
|
});
|
|
670
684
|
|
|
671
|
-
function showGraphQLDetail(name, el) {
|
|
685
|
+
function showGraphQLDetail(name, el, confidence) {
|
|
672
686
|
const modal = document.getElementById('detailModal');
|
|
673
687
|
const title = document.getElementById('modalTitle');
|
|
674
688
|
const body = document.getElementById('modalBody');
|
|
@@ -685,9 +699,16 @@ import {a as a$2}from'./chunk-NQMJ3QRX.js';import {a as a$3}from'./chunk-VV3A3UE
|
|
|
685
699
|
|
|
686
700
|
if (op) {
|
|
687
701
|
titleText = op.name;
|
|
702
|
+
const confBadge =
|
|
703
|
+
confidence === 'likely'
|
|
704
|
+
? '<span class="detail-badge confidence likely" title="Likely: reachable via the import graph, but indirect (3+ steps)">LIKELY</span>'
|
|
705
|
+
: '';
|
|
688
706
|
html = \`<div class="detail-section">
|
|
689
707
|
<h4>Type</h4>
|
|
690
|
-
<
|
|
708
|
+
<div class="detail-badges">
|
|
709
|
+
<span class="detail-badge \${op.type}">\${op.type.toUpperCase()}</span>
|
|
710
|
+
\${confBadge}
|
|
711
|
+
</div>
|
|
691
712
|
</div>\`;
|
|
692
713
|
|
|
693
714
|
if (op.returnType) {
|
|
@@ -716,8 +737,10 @@ import {a as a$2}from'./chunk-NQMJ3QRX.js';import {a as a$3}from'./chunk-VV3A3UE
|
|
|
716
737
|
}
|
|
717
738
|
|
|
718
739
|
if (op.usedIn?.length) {
|
|
719
|
-
html += '<div class="detail-section"><h4>Used In</h4><div style="
|
|
720
|
-
op.usedIn.forEach(f => {
|
|
740
|
+
html += '<div class="detail-section"><h4>Used In</h4><div style="max-height:100px;overflow-y:auto">';
|
|
741
|
+
op.usedIn.forEach(f => {
|
|
742
|
+
html += \`<p><code>\${escapeHtml(f)}</code></p>\`;
|
|
743
|
+
});
|
|
721
744
|
html += '</div></div>';
|
|
722
745
|
}
|
|
723
746
|
} else {
|
|
@@ -730,7 +753,9 @@ import {a as a$2}from'./chunk-NQMJ3QRX.js';import {a as a$3}from'./chunk-VV3A3UE
|
|
|
730
753
|
html = \`
|
|
731
754
|
<div class="detail-section">
|
|
732
755
|
<h4>Type</h4>
|
|
733
|
-
<
|
|
756
|
+
<div class="detail-badges">
|
|
757
|
+
<span class="detail-badge \${type}">\${type.toUpperCase()}</span>
|
|
758
|
+
</div>
|
|
734
759
|
</div>
|
|
735
760
|
<div class="detail-section">
|
|
736
761
|
<h4>Operation Name</h4>
|
|
@@ -856,8 +881,10 @@ import {a as a$2}from'./chunk-NQMJ3QRX.js';import {a as a$3}from'./chunk-VV3A3UE
|
|
|
856
881
|
}
|
|
857
882
|
|
|
858
883
|
if (op.usedIn?.length) {
|
|
859
|
-
html += '<div class="detail-section"><h4>Used In</h4><div style="
|
|
860
|
-
op.usedIn.forEach(f => {
|
|
884
|
+
html += '<div class="detail-section"><h4>Used In</h4><div style="max-height:100px;overflow-y:auto">';
|
|
885
|
+
op.usedIn.forEach(f => {
|
|
886
|
+
html += \`<p><code>\${escapeHtml(f)}</code></p>\`;
|
|
887
|
+
});
|
|
861
888
|
html += '</div></div>';
|
|
862
889
|
}
|
|
863
890
|
|
|
@@ -1085,11 +1112,11 @@ import {a as a$2}from'./chunk-NQMJ3QRX.js';import {a as a$3}from'./chunk-VV3A3UE
|
|
|
1085
1112
|
}
|
|
1086
1113
|
</script>
|
|
1087
1114
|
</body>
|
|
1088
|
-
</html>`}async start(
|
|
1089
|
-
Rails...`);try{this.railsAnalysis=await k(r);let e=this.railsAnalysis.summary;console.log(` ${e.totalRoutes} routes \xB7 ${e.totalControllers} controllers \xB7 ${e.totalModels} models \xB7 ${e.totalGrpcServices} gRPC`);}catch(e){console.error(" \u26A0\uFE0F Rails analysis failed:",e.message);}}try{let e=await
|
|
1115
|
+
</html>`}async start(s=true){let r=this.config.repositories[0]?.path||process.cwd();this.envResult=await a$3(r),this.currentReport=await this.engine.generate(),console.log();for(let e of this.currentReport.repositories){let a=e.summary;console.log(` \u2705 ${e.displayName}`),console.log(` ${a.totalPages} pages \xB7 ${a.totalComponents} components \xB7 ${a.totalGraphQLOperations} GraphQL \xB7 ${a.totalDataFlows} data flows`);}if(this.envResult.hasRails){console.log(`
|
|
1116
|
+
Rails...`);try{this.railsAnalysis=await k(r);let e=this.railsAnalysis.summary;console.log(` ${e.totalRoutes} routes \xB7 ${e.totalControllers} controllers \xB7 ${e.totalModels} models \xB7 ${e.totalGrpcServices} gRPC`);}catch(e){console.error(" \u26A0\uFE0F Rails analysis failed:",e.message);}}try{let e=await ue(this.port);e!==this.port&&console.log(`
|
|
1090
1117
|
\u26A0\uFE0F Port ${this.port} is in use, using port ${e} instead`),this.port=e;}catch(e){console.error(`
|
|
1091
1118
|
\u274C Failed to find available port: ${e.message}`),process.exit(1);}if(this.server.listen(this.port,()=>{console.log(`
|
|
1092
1119
|
\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
|
|
1093
|
-
`);}),
|
|
1094
|
-
\u{1F504} Regenerating...`),this.currentReport=await this.engine.generate(),this.envResult?.hasRails){let
|
|
1095
|
-
\u{1F44B} Server stopped`);}};export{
|
|
1120
|
+
`);}),s){let e=(await import('open')).default;await e(`http://localhost:${this.port}`);}this.config.watch.enabled&&this.watchForChanges();}async regenerate(){if(console.log(`
|
|
1121
|
+
\u{1F504} Regenerating...`),this.currentReport=await this.engine.generate(),this.envResult?.hasRails){let s=this.config.repositories[0]?.path||process.cwd();try{this.railsAnalysis=await k(s);}catch(r){console.error("\u26A0\uFE0F Rails re-analysis failed:",r.message);}}for(let s of this.currentReport.repositories)console.log(` ${s.displayName}: ${s.summary.totalPages} pages \xB7 ${s.summary.totalComponents} components \xB7 ${s.summary.totalGraphQLOperations} GraphQL`);this.io.emit("reload"),console.log("\u2705 Done");}async watchForChanges(){let s=this.config.repositories.map(e=>e.path),r=null;for(let e of s)try{let a=L.watch(e,{recursive:!0});(async()=>{for await(let o of a)o.filename&&(o.filename.endsWith(".ts")||o.filename.endsWith(".tsx"))&&(r&&clearTimeout(r),r=setTimeout(async()=>{await this.regenerate();},this.config.watch.debounce));})();}catch(a){console.warn(`Warning: Could not watch directory ${e}:`,a.message);}}stop(){this.server.close(),console.log(`
|
|
1122
|
+
\u{1F44B} Server stopped`);}};export{W as a,he as b};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var g=Object.create;var f=Object.defineProperty;var h=Object.getOwnPropertyDescriptor;var i=Object.getOwnPropertyNames;var j=Object.getPrototypeOf,k=Object.prototype.hasOwnProperty;var m=(a=>typeof require<"u"?require:typeof Proxy<"u"?new Proxy(a,{get:(b,c)=>(typeof require<"u"?require:b)[c]}):a)(function(a){if(typeof require<"u")return require.apply(this,arguments);throw Error('Dynamic require of "'+a+'" is not supported')});var n=(a,b)=>()=>(b||a((b={exports:{}}).exports,b),b.exports);var l=(a,b,c,e)=>{if(b&&typeof b=="object"||typeof b=="function")for(let d of i(b))!k.call(a,d)&&d!==c&&f(a,d,{get:()=>b[d],enumerable:!(e=h(b,d))||e.enumerable});return a};var o=(a,b,c)=>(c=a!=null?g(j(a)):{},l(b||!a||!a.__esModule?f(c,"default",{value:a,enumerable:true}):c,a));export{m as a,n as b,o as c};
|
package/dist/cli.js
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {a,b}from'./chunk-
|
|
2
|
+
import {a,b}from'./chunk-YKPXOHWZ.js';import'./chunk-LHP2OKKA.js';import'./chunk-VV3A3UE3.js';import'./chunk-IGRCLYVZ.js';import'./chunk-QBSB6BIU.js';import'./chunk-ATRSGO6O.js';import'./chunk-H7VVRHQZ.js';import'./chunk-ZWRDP37E.js';import {Command}from'commander';import s from'chalk';import*as c from'path';import*as $ from'fs';import*as n from'fs/promises';import*as P from'os';function x(){let e=P.tmpdir();return c.join(e,`repomap-${Date.now()}`)}function N(e){let t=async()=>{try{await n.rm(e,{recursive:!0,force:!0});}catch{}process.exit(0);};process.on("SIGINT",t),process.on("SIGTERM",t);}function S(){try{let e=new URL("../package.json",import.meta.url),t=$.readFileSync(e,"utf-8");return JSON.parse(t).version||"0.0.0"}catch{return "0.0.0"}}var d=new Command;d.name("repomap").description("Interactive documentation generator for code repositories").version(S());async function C(e){let t=c.basename(e),r=false,o=c.join(e,"Gemfile"),p=c.join(e,"config","routes.rb");try{await n.access(o),await n.access(p);let g=await n.readFile(o,"utf-8");r=g.includes("gem 'rails'")||g.includes('gem "rails"');}catch{}let a=c.join(e,"package.json"),i=false,l=false,m={};try{let g=JSON.parse(await n.readFile(a,"utf-8")),w={...g.dependencies,...g.devDependencies};i=!!w.react,l=!!w.next;let f=["src/pages","pages","app","src/app","frontend/src"];for(let u of f)try{await n.access(c.join(e,u)),m.pagesDir=u;break}catch{}let b=["src/features","features","src/modules","modules","frontend/src"];for(let u of b)try{await n.access(c.join(e,u)),m.featuresDir=u;break}catch{}let D=["src/components","components","src/common/components","frontend/src"];for(let u of D)try{await n.access(c.join(e,u)),m.componentsDir=u;break}catch{}}catch{}let h=[];(i||l)&&h.push("pages","graphql","dataflow","rest-api");let y="generic";return l?y="nextjs":r&&(y="rails"),!r&&!i&&!l?null:{name:t,displayName:t,description:r&&i?"Rails + React application":r?"Rails application":"",path:e,branch:"main",type:y,analyzers:h,settings:m}}async function E(e){let t=await C(e);if(!t)throw new Error("Could not detect project. Please create a repomap.config.ts file or run 'repomap init'.");return {outputDir:"./.repomap",site:{title:`${t.displayName} Documentation`,description:"Auto-generated documentation",baseUrl:"/docs"},repositories:[t],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 v(e,t){let r=e?[e]:["repomap.config.ts","repomap.config.js","repomap.config.mjs"];for(let o of r){let p=c.resolve(t,o);try{await n.access(p);let a=await import(p);return a.config||a.default}catch{}}return E(t)}d.command("generate").description("Generate documentation from source code").option("-c, --config <path>","Path to config file").option("-o, --output <path>","Output directory").option("--temp","Use temporary directory (no files in repository)").option("--repo <name>","Analyze specific repository only").option("--watch","Watch for changes and regenerate").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 t=e.ci||process.env.CI==="true";t||console.log(s.blue.bold(`
|
|
3
3
|
\u{1F4DA} Repomap
|
|
4
|
-
`));try{let
|
|
5
|
-
`))),e.output&&(o.outputDir=e.output),e.repo&&(o.repositories=o.repositories.filter(
|
|
6
|
-
`)),await
|
|
7
|
-
\u2705 Static site generated: ${
|
|
4
|
+
`));try{let r=process.cwd(),o=await v(e.config,r);e.temp&&(o.outputDir=x(),t||console.log(s.cyan(`\u{1F4C1} Using temp directory: ${o.outputDir}
|
|
5
|
+
`))),e.output&&(o.outputDir=e.output),e.repo&&(o.repositories=o.repositories.filter(a=>a.name===e.repo),o.repositories.length===0&&(console.error(s.red(`Repository "${e.repo}" not found in config`)),process.exit(1)));let p=new a(o);if(e.watch)console.log(s.yellow(`\u{1F440} Watch mode enabled. Press Ctrl+C to stop.
|
|
6
|
+
`)),await k(p,o);else {let a=await p.generate();if(e.format==="json"||e.static){let i=c.join(o.outputDir,"report.json");await n.mkdir(o.outputDir,{recursive:!0}),await n.writeFile(i,JSON.stringify(a,null,2)),t||console.log(s.gray(` \u2192 ${i}`));}if(e.static&&await F(o,a,t),!t)O(a);else {let i=a.repositories.reduce((l,m)=>l+m.summary.totalPages,0);console.log(`\u2705 Generated: ${i} pages, ${a.repositories.length} repos`);}}}catch(r){console.error(t?`Error: ${r.message}`:s.red("Error:"),r.message),process.exit(1);}});async function F(e,t,r){let{PageMapGenerator:o}=await import('./page-map-generator-LTVRHSDC.js'),{detectEnvironments:p}=await import('./env-detector-RVGPBVNJ.js'),a=e.outputDir;await n.mkdir(a,{recursive:true});let i=e.repositories[0]?.path||process.cwd(),l=await p(i),m=null;if(l.hasRails){let{analyzeRailsApp:f}=await import('./rails-57MNOGHR.js');m=await f(i);}let y=new o().generatePageMapHtml(t,{envResult:l,railsAnalysis:m,staticMode:true});if(await n.writeFile(c.join(a,"index.html"),y),r||console.log(s.gray(` \u2192 ${c.join(a,"index.html")}`)),m){let{RailsMapGenerator:f}=await import('./rails-map-generator-JNU5QHX4.js'),D=new f().generateFromResult(m);await n.writeFile(c.join(a,"rails-map.html"),D),r||console.log(s.gray(` \u2192 ${c.join(a,"rails-map.html")}`));}let g=["common.css","page-map.css","docs.css","rails-map.css"],w=c.join(a,"assets");await n.mkdir(w,{recursive:true});for(let f of g)try{let b=new URL(`./generators/assets/${f}`,import.meta.url),D=await n.readFile(b,"utf-8");await n.writeFile(c.join(w,f),D);}catch{}r||console.log(s.green(`
|
|
7
|
+
\u2705 Static site generated: ${a}`));}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("-o, --output <path>","Output directory (default: .repomap in target path)").option("-p, --port <number>","Server port","3030").option("--temp","Use temporary directory (no files in repository)").option("--no-open","Don't open browser automatically").action(async e=>{console.log(s.blue.bold(`
|
|
8
8
|
\u{1F310} Repomap
|
|
9
|
-
`));try{let t=e.path||process.cwd(),
|
|
10
|
-
`)),
|
|
9
|
+
`));try{let t=e.path||process.cwd(),r=await v(e.config,t);e.temp&&(r.outputDir=x(),console.log(s.cyan(`\u{1F4C1} Using temp directory: ${r.outputDir}
|
|
10
|
+
`)),N(r.outputDir)),e.output&&(r.outputDir=c.resolve(e.output)),await new b(r,parseInt(e.port)).start(!e.open);}catch(t){console.error(s.red("Error:"),t.message),process.exit(1);}});d.command("init").description("Initialize repomap configuration").option("-f, --force","Overwrite existing config").action(async e=>{let t="./repomap.config.ts";try{if(await n.access(t).then(()=>!0).catch(()=>!1)&&!e.force){console.log(s.yellow("Config file already exists. Use --force to overwrite."));return}let o=await C(process.cwd()),p=o?.name||"my-project",a=o?.type||"nextjs",i=o?.settings.pagesDir||"src/pages",l=o?.settings.featuresDir||"src/features",m=o?.settings.componentsDir||"src/components",h=`import type { DocGeneratorConfig } from "repomap";
|
|
11
11
|
|
|
12
12
|
export const config: DocGeneratorConfig = {
|
|
13
13
|
outputDir: "./.repomap",
|
|
@@ -23,7 +23,7 @@ export const config: DocGeneratorConfig = {
|
|
|
23
23
|
description: "Main repository",
|
|
24
24
|
path: ".",
|
|
25
25
|
branch: "main",
|
|
26
|
-
type: "${
|
|
26
|
+
type: "${a}",
|
|
27
27
|
analyzers: ["pages", "graphql", "components", "dataflow"],
|
|
28
28
|
settings: {
|
|
29
29
|
pagesDir: "${i}",
|
|
@@ -54,12 +54,12 @@ export const config: DocGeneratorConfig = {
|
|
|
54
54
|
|
|
55
55
|
export default config;
|
|
56
56
|
`;await n.writeFile(t,h,"utf-8"),console.log(s.green(`\u2705 Created ${t}`)),console.log(s.gray(`
|
|
57
|
-
Run 'npx repomap serve' to start the documentation server.`));}catch(
|
|
57
|
+
Run 'npx repomap serve' to start the documentation server.`));}catch(r){console.error(s.red("Failed to create config:"),r.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(s.blue.bold(`
|
|
58
58
|
\u{1F6E4}\uFE0F Repomap Rails
|
|
59
|
-
`));try{let t=e.path||process.cwd();try{await n.access(c.join(t,"config","routes.rb"));}catch{console.error(s.red("Not a Rails project (config/routes.rb not found)")),process.exit(1);}let{RailsMapGenerator:
|
|
59
|
+
`));try{let t=e.path||process.cwd();try{await n.access(c.join(t,"config","routes.rb"));}catch{console.error(s.red("Not a Rails project (config/routes.rb not found)")),process.exit(1);}let{RailsMapGenerator:r}=await import('./rails-map-generator-JNU5QHX4.js'),o=e.output||c.join(t,"rails-map.html");await new r(t).generate({title:`${c.basename(t)} - Rails Map`,outputPath:o}),console.log(s.green(`\u2705 Rails map generated: ${o}`));let{exec:a}=await import('child_process');a(`open "${o}"`);}catch(t){console.error(s.red("Error:"),t.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(s.blue.bold(`
|
|
60
60
|
\u{1F4CA} Documentation Diff
|
|
61
|
-
`));try{let t=process.cwd(),
|
|
62
|
-
\u{1F504} Change detected: ${i.filename}`)),await e.generate();},t.watch.debounce));}}function
|
|
61
|
+
`));try{let t=process.cwd(),r=await v(e.config,t),o=c.join(r.outputDir,"report.json");if(!await n.access(o).then(()=>!0).catch(()=>!1)){console.log(s.yellow("No previous report found. Run 'generate' first."));return}let a$1=JSON.parse(await n.readFile(o,"utf-8")),l=await new a(r).generate();M(a$1,l);}catch(t){console.error(s.red("Failed to generate diff:"),t.message);}});async function k(e,t){await e.generate();let r=t.repositories.map(o=>o.path);for(let o of r){let p=n.watch(o,{recursive:true}),a=null;for await(let i of p)i.filename&&(i.filename.endsWith(".ts")||i.filename.endsWith(".tsx"))&&(a&&clearTimeout(a),a=setTimeout(async()=>{console.log(s.yellow(`
|
|
62
|
+
\u{1F504} Change detected: ${i.filename}`)),await e.generate();},t.watch.debounce));}}function O(e){console.log(s.green.bold(`
|
|
63
63
|
\u2705 Complete
|
|
64
|
-
`));for(let t of e.repositories)console.log(s.white(` ${t.displayName}`)),console.log(s.gray(` ${t.summary.totalPages} pages \xB7 ${t.summary.totalComponents} components \xB7 ${t.summary.totalGraphQLOperations} GraphQL ops`));console.log();}function
|
|
65
|
-
`));for(let
|
|
64
|
+
`));for(let t of e.repositories)console.log(s.white(` ${t.displayName}`)),console.log(s.gray(` ${t.summary.totalPages} pages \xB7 ${t.summary.totalComponents} components \xB7 ${t.summary.totalGraphQLOperations} GraphQL ops`));console.log();}function M(e,t){console.log(s.cyan(`Changes detected:
|
|
65
|
+
`));for(let r of t.repositories){let o=e.repositories.find(l=>l.name===r.name);if(!o){console.log(s.green(` + New repository: ${r.displayName}`));continue}let p=r.summary.totalPages-o.summary.totalPages,a=r.summary.totalComponents-o.summary.totalComponents,i=r.summary.totalGraphQLOperations-o.summary.totalGraphQLOperations;(p!==0||a!==0||i!==0)&&(console.log(s.yellow(` ~ ${r.displayName}:`)),p!==0&&console.log(` Pages: ${p>0?"+":""}${p}`),a!==0&&console.log(` Components: ${a>0?"+":""}${a}`),i!==0&&console.log(` GraphQL Ops: ${i>0?"+":""}${i}`));}}d.parse();
|
|
@@ -23,6 +23,20 @@ declare abstract class BaseAnalyzer {
|
|
|
23
23
|
* Get setting value with fallback
|
|
24
24
|
*/
|
|
25
25
|
protected getSetting(key: string, defaultValue?: string): string;
|
|
26
|
+
/**
|
|
27
|
+
* Get list-like setting value (comma/newline separated).
|
|
28
|
+
* Example: "useMyQuery, useMyMutation" -> ["useMyQuery", "useMyMutation"]
|
|
29
|
+
*/
|
|
30
|
+
protected getListSetting(key: string, defaultValue?: string[]): string[];
|
|
31
|
+
/**
|
|
32
|
+
* Resolve effective GraphQL hook patterns by combining:
|
|
33
|
+
* - a preset selected by `settings.graphqlHookPreset` (default: "auto")
|
|
34
|
+
* - user-defined patterns from `settings.graphqlHookPatterns`
|
|
35
|
+
*
|
|
36
|
+
* Presets are meant to improve out-of-the-box support for common "production variants"
|
|
37
|
+
* (Relay/urql/custom wrappers) while keeping false positives low.
|
|
38
|
+
*/
|
|
39
|
+
protected getGraphQLHookPatterns(): string[];
|
|
26
40
|
/**
|
|
27
41
|
* Log analysis progress (silent by default, set REPOMAP_VERBOSE=1 to enable)
|
|
28
42
|
*/
|
|
@@ -42,6 +56,8 @@ declare abstract class BaseAnalyzer {
|
|
|
42
56
|
*/
|
|
43
57
|
declare class PagesAnalyzer extends BaseAnalyzer {
|
|
44
58
|
private codegenMap;
|
|
59
|
+
private tsResolver;
|
|
60
|
+
private coverage;
|
|
45
61
|
constructor(config: RepositoryConfig);
|
|
46
62
|
getName(): string;
|
|
47
63
|
analyze(): Promise<Partial<AnalysisResult>>;
|
|
@@ -190,6 +206,7 @@ declare class PagesAnalyzer extends BaseAnalyzer {
|
|
|
190
206
|
* Uses @swc/core for fast parsing
|
|
191
207
|
*/
|
|
192
208
|
declare class GraphQLAnalyzer extends BaseAnalyzer {
|
|
209
|
+
private coverage;
|
|
193
210
|
constructor(config: RepositoryConfig);
|
|
194
211
|
getName(): string;
|
|
195
212
|
analyze(): Promise<Partial<AnalysisResult>>;
|
|
@@ -279,6 +296,7 @@ declare class GraphQLAnalyzer extends BaseAnalyzer {
|
|
|
279
296
|
*/
|
|
280
297
|
declare class DataFlowAnalyzer extends BaseAnalyzer {
|
|
281
298
|
private componentCache;
|
|
299
|
+
private coverage;
|
|
282
300
|
constructor(config: RepositoryConfig);
|
|
283
301
|
getName(): string;
|
|
284
302
|
analyze(): Promise<Partial<AnalysisResult>>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export{a as detectEnvironments,b as getAnalyzersForEnvironments}from'./chunk-VV3A3UE3.js';
|
|
1
|
+
export{a as detectEnvironments,b as getAnalyzersForEnvironments}from'./chunk-VV3A3UE3.js';import'./chunk-ZWRDP37E.js';
|
|
@@ -597,6 +597,16 @@ body {
|
|
|
597
597
|
font-size: 13px;
|
|
598
598
|
}
|
|
599
599
|
|
|
600
|
+
.detail-badges {
|
|
601
|
+
padding: 8px 12px;
|
|
602
|
+
background: var(--docs-bg3);
|
|
603
|
+
border-radius: 4px;
|
|
604
|
+
display: flex;
|
|
605
|
+
flex-wrap: wrap;
|
|
606
|
+
gap: 6px;
|
|
607
|
+
align-items: center;
|
|
608
|
+
}
|
|
609
|
+
|
|
600
610
|
.detail-badge {
|
|
601
611
|
display: inline-block;
|
|
602
612
|
padding: 4px 10px;
|
|
@@ -605,6 +615,10 @@ body {
|
|
|
605
615
|
font-weight: 500;
|
|
606
616
|
margin-right: 6px;
|
|
607
617
|
}
|
|
618
|
+
|
|
619
|
+
.detail-badges .detail-badge {
|
|
620
|
+
margin-right: 0;
|
|
621
|
+
}
|
|
608
622
|
.detail-badge.query {
|
|
609
623
|
background: #dbeafe;
|
|
610
624
|
color: #1d4ed8;
|
|
@@ -630,6 +644,14 @@ body {
|
|
|
630
644
|
color: #475569;
|
|
631
645
|
}
|
|
632
646
|
|
|
647
|
+
.detail-badge.confidence {
|
|
648
|
+
cursor: help;
|
|
649
|
+
color: #fff;
|
|
650
|
+
}
|
|
651
|
+
.detail-badge.confidence.likely {
|
|
652
|
+
background: #f59e0b;
|
|
653
|
+
}
|
|
654
|
+
|
|
633
655
|
/* Regenerate Button */
|
|
634
656
|
.regenerate-btn {
|
|
635
657
|
background: var(--accent);
|