@wtdlee/repomap 0.9.0 → 0.9.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/analyzers/index.js +1 -1
- package/dist/chunk-2XZSFAJF.js +20 -0
- package/dist/{chunk-LDX6WPHR.js → chunk-6AZNHUOB.js} +154 -22
- package/dist/{chunk-P7MX3M5U.js → chunk-NQMJ3QRX.js} +427 -75
- package/dist/{chunk-TNUKDIO7.js → chunk-QDVE7MT3.js} +4 -4
- package/dist/{chunk-SMN6XFMS.js → chunk-WZAAA7DS.js} +123 -175
- package/dist/cli.js +3 -3
- package/dist/generators/assets/docs.css +154 -1
- package/dist/generators/assets/page-map.css +59 -0
- package/dist/generators/index.d.ts +2 -1
- package/dist/generators/index.js +1 -1
- package/dist/index.d.ts +9 -9
- package/dist/index.js +1 -1
- package/dist/page-map-generator-HROGGVAQ.js +1 -0
- package/dist/rails-map-generator-DF2YAXW4.js +1 -0
- package/dist/server/index.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-HPBPEGHS.js +0 -19
- package/dist/page-map-generator-2XQB7RWO.js +0 -1
- package/dist/rails-map-generator-77ATUFMP.js +0 -1
package/dist/analyzers/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export{e as ALL_GRAPHQL_HOOKS,a as BaseAnalyzer,A as DataFlowAnalyzer,g as GRAPHQL_INDICATORS,c as GRAPHQL_MUTATION_HOOKS,d as GRAPHQL_OTHER_HOOKS,b as GRAPHQL_QUERY_HOOKS,z as GraphQLAnalyzer,f as HOOK_TYPE_MAP,y as PagesAnalyzer,B as RestApiAnalyzer,n as cleanOperationName,r as extractGraphQLContext,w as extractGraphQLOperationsFromFile,s as extractOperationNameFromGqlCall,t as extractOperationNameFromTemplate,p as getCalleeName,x as getHookInfoString,l as getHookType,u as hasGraphQLArgument,m as hasGraphQLIndicators,k as isGraphQLHook,i as isMutationHook,h as isQueryHook,j as isSubscriptionHook,o as parseToAst,v as resolveOperationName,q as traverseAst}from'../chunk-
|
|
1
|
+
export{e as ALL_GRAPHQL_HOOKS,a as BaseAnalyzer,A as DataFlowAnalyzer,g as GRAPHQL_INDICATORS,c as GRAPHQL_MUTATION_HOOKS,d as GRAPHQL_OTHER_HOOKS,b as GRAPHQL_QUERY_HOOKS,z as GraphQLAnalyzer,f as HOOK_TYPE_MAP,y as PagesAnalyzer,B as RestApiAnalyzer,n as cleanOperationName,r as extractGraphQLContext,w as extractGraphQLOperationsFromFile,s as extractOperationNameFromGqlCall,t as extractOperationNameFromTemplate,p as getCalleeName,x as getHookInfoString,l as getHookType,u as hasGraphQLArgument,m as hasGraphQLIndicators,k as isGraphQLHook,i as isMutationHook,h as isQueryHook,j as isSubscriptionHook,o as parseToAst,v as resolveOperationName,q as traverseAst}from'../chunk-QDVE7MT3.js';
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
var v=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 D=l.path.split("/").filter(Boolean),m=D.length>1?D.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),D=o(p.source.name),m=o(p.target.name);e.push(` ${l}(("${D}"))`),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),D=o(p.source.name),m=o(p.target.name);e.push(` ${l}["${D}"]`),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),D=o(p.source.name),m=o(p.target.name);e.push(` ${l}{{"${D}"}}`),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
|
+
`),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
|
+
`),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
|
+
`),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
|
+
`),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
|
+
`),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,h)=>{p.has(m)||p.set(m,{label:f,open:h,queries:new Set,mutations:new Set});let g=p.get(m);if(!g){let $={label:f,open:h,queries:new Set,mutations:new Set};return p.set(m,$),$}return g};for(let m of c){let h=(m.operationName||"").replace(/^[→\->\s]+/,"").trim();if(!h||h.length<2)continue;let g=m.type.includes("Mutation"),$=m.source||"",q="direct",C="Direct (this page)",y=true;$.startsWith("close:")?(q="close",C="Close (related)",y=true):$.startsWith("indirect:")||$.startsWith("usedIn:")||$.startsWith("import:")?(q="indirect",C="Indirect",y=false):$.startsWith("common:")?(q="common",C="Common (shared)",y=false):$.startsWith("hook:")?(q="hook",C="Hook",y=false):$.startsWith("component:")&&(q="component",C="Component",y=false);let P=l(q,C,y);g?P.mutations.add(h):P.queries.add(h);}let d=Array.from(p.values()).reduce((m,f)=>m+f.queries.size+f.mutations.size,0);if(d===0)continue;let D=["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 D){let f=p.get(m);if(!f)continue;let h=f.queries.size+f.mutations.size;if(h!==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">${h}</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 g of Array.from(f.queries).sort())e.push(`<span class="gql-op" data-op="${g}">${g}</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 g of Array.from(f.mutations).sort())e.push(`<span class="gql-op mutation" data-op="${g}">${g}</span>`);e.push("</div>"),e.push("");}e.push("</details>"),e.push("");}}e.push("");}}return e.join(`
|
|
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
|
+
`)}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
|
+
`);}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
|
+
`);}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
|
+
`)}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
|
+
`)}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(h=>this.extractFeatureFromComponent(h.filePath)===a);if(!(o.dataFetching.length>0||u.some(h=>h.stateManagement.some(g=>g.includes("Apollo")||g.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 h=o.path.replace(/[^a-zA-Z0-9]/g,"_"),g=o.path.replace(/"/g,"'");e.push(`<pre class="mermaid" data-mermaid-scope="dataflow" data-mermaid-page="${h}">`),e.push("flowchart LR"),e.push(` Page${h}["${g}"]`),e.push("");let $=10,q=["direct","close","indirect","common"];for(let C of q){let y=p[C];y.queries.length===0&&y.mutations.length===0||(e.push(`%%DFG_GROUP:${C}:start%%`),y.queries.slice(0,$).forEach((P,w)=>{let b=`Q${h}_${C}_${w}`,F=P.replace(/"/g,"'").replace(/[<>]/g,"");e.push(` ${b}["${F}"]:::query --> Page${h}`);}),y.mutations.slice(0,$).forEach((P,w)=>{let b=`M${h}_${C}_${w}`,F=P.replace(/"/g,"'").replace(/[<>]/g,"");e.push(` Page${h} --> ${b}["${F}"]:::mutation`);}),e.push(`%%DFG_GROUP:${C}: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 D=["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(D.some(h=>p[h].queries.length>0||p[h].mutations.length>0))for(let h of D){let g=p[h],$=m[h];if(g.queries.length===0&&g.mutations.length===0)continue;let q=$.open?" open":"",C=$.defaultVisible?"":' style="display:none"';if(e.push(`<details class="ops-group" data-ops-scope="dataflow" data-ops-group="${h}"${q}${C}>`),e.push(`<summary class="ops-group__summary"><span class="ops-group__title">${$.label}</span><span class="ops-group__count">${g.queries.length+g.mutations.length}</span></summary>`),g.queries.length>0){e.push(`<p><strong>Queries (${g.queries.length})</strong></p>`),e.push('<div class="gql-ops-list">');for(let y of g.queries)e.push(`<span class="gql-op" data-op="${y}">${y}</span>`);e.push("</div>");}if(g.mutations.length>0){e.push(`<p><strong>Mutations (${g.mutations.length})</strong></p>`),e.push('<div class="gql-ops-list">');for(let y of g.mutations)e.push(`<span class="gql-op mutation" data-op="${y}">${y}</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
|
+
`)}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
|
+
`)}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
|
+
`)}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{v as a,A as b};
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import {a as a$2}from'./chunk-P7MX3M5U.js';import {a as a$3}from'./chunk-VV3A3UE3.js';import {B,A as A$1,z,y}from'./chunk-TNUKDIO7.js';import {a,b}from'./chunk-HPBPEGHS.js';import {a as a$1}from'./chunk-SMN6XFMS.js';import {k}from'./chunk-H7VVRHQZ.js';import {simpleGit}from'simple-git';import*as u from'fs/promises';import*as d from'path';import E from'express';import {Server}from'socket.io';import*as O from'http';import {marked}from'marked';import*as C from'net';var v=class{config;mermaidGenerator;markdownGenerator;constructor(a$1){this.config=a$1,this.mermaidGenerator=new a,this.markdownGenerator=new b;}async generate(){let a=[];for(let r of this.config.repositories)try{let i=await this.analyzeRepository(r);a.push(i);}catch(i){console.error(`\u274C ${r.name}: ${i.message}`);}let s=this.analyzeCrossRepo(a),e=a.map(r=>r.analysis),n=this.extractCrossRepoLinks(e),t=this.mermaidGenerator.generateAll(e,n),o={generatedAt:new Date().toISOString(),repositories:a,crossRepoAnalysis:s,diagrams:t};return await this.writeDocumentation(o),o}async analyzeRepository(a){let{version:s,commitHash:e}=await this.getRepoInfo(a),n=a.analyzers.map(p=>this.createAnalyzer(p,a)).filter(p=>p!==null),t=Date.now(),o=await Promise.all(n.map(p=>p.analyze())),r=((Date.now()-t)/1e3).toFixed(1);console.log(` Analyzed ${a.displayName} in ${r}s`);let i=this.mergeAnalysisResults(o,a.name,s,e);this.enrichPagesWithHookGraphQL(i);let l={totalPages:i.pages.length,totalComponents:i.components.length,totalGraphQLOperations:i.graphqlOperations.length,totalDataFlows:i.dataFlows.length,authRequiredPages:i.pages.filter(p=>p.authentication.required).length,publicPages:i.pages.filter(p=>!p.authentication.required).length};return {name:a.name,displayName:a.displayName,version:s,commitHash:e,analysis:i,summary:l}}async getRepoInfo(a){try{let n=(await simpleGit(a.path).log({n:1})).latest?.hash||"unknown",t="unknown";try{let o=d.join(a.path,"package.json");t=JSON.parse(await u.readFile(o,"utf-8")).version||"unknown";}catch{}return {version:t,commitHash:n}}catch{return {version:"unknown",commitHash:"unknown"}}}createAnalyzer(a,s){switch(a){case "pages":if(s.type==="nextjs"||s.type==="rails"||s.type==="generic")return new y(s);break;case "graphql":return new z(s);case "dataflow":case "components":return new A$1(s);case "rest-api":case "api":return new B(s)}return null}mergeAnalysisResults(a,s,e,n){let t={repository:s,timestamp:new Date().toISOString(),version:e,commitHash:n,pages:[],graphqlOperations:[],apiCalls:[],components:[],dataFlows:[],apiEndpoints:[],models:[],crossRepoLinks:[]};for(let o of a)o.pages&&t.pages.push(...o.pages),o.graphqlOperations&&t.graphqlOperations.push(...o.graphqlOperations),o.apiCalls&&t.apiCalls.push(...o.apiCalls),o.components&&t.components.push(...o.components),o.dataFlows&&t.dataFlows.push(...o.dataFlows),o.apiEndpoints&&t.apiEndpoints.push(...o.apiEndpoints),o.models&&t.models.push(...o.models),o.crossRepoLinks&&t.crossRepoLinks.push(...o.crossRepoLinks);return t}analyzeCrossRepo(a){let s=[],e=[],n=[],t=[],o=new Map;for(let l of a)for(let p of l.analysis.graphqlOperations){let c=o.get(p.name)||[];c.push(l.name),o.set(p.name,c);}for(let[l,p]of o)p.length>1&&s.push(l);let r=a.filter(l=>l.analysis.pages.length>0),i=a.filter(l=>l.analysis.apiEndpoints.length>0);for(let l of r)for(let p of i)for(let c of p.analysis.apiEndpoints)e.push({frontend:l.name,backend:p.name,endpoint:c.path,operations:l.analysis.graphqlOperations.filter(m=>m.usedIn.length>0).map(m=>m.name)});return {sharedTypes:s,apiConnections:e,navigationFlows:n,dataFlowAcrossRepos:t}}extractCrossRepoLinks(a){let s=[],e=new Map;for(let n of a)for(let t of n.graphqlOperations){let o=e.get(t.name)||[];o.push(n),e.set(t.name,o);}for(let[n,t]of e)t.length>1&&s.push({sourceRepo:t[0].repository,sourcePath:`graphql/${n}`,targetRepo:t[1].repository,targetPath:`graphql/${n}`,linkType:"graphql-operation",description:`Shared GraphQL operation: ${n}`});return s}enrichPagesWithHookGraphQL(a){let s=new Map;for(let t of a.graphqlOperations){if(!t.filePath)continue;let r=(t.filePath.split("/").pop()||"").replace(/\.(ts|tsx|js|jsx)$/,"");r.startsWith("use")&&(s.has(r)||s.set(r,new Set),s.get(r).add(t.name));}for(let t of a.components){if(t.type!=="hook")continue;let o=[];for(let r of t.hooks){let i=r.match(/^(Query|Mutation|Subscription):\s*(.+)$/);i&&o.push(i[2]);}if(o.length>0){s.has(t.name)||s.set(t.name,new Set);for(let r of o)s.get(t.name).add(r);}}let e=new Map;for(let t of a.graphqlOperations){if(t.type!=="query"&&t.type!=="mutation"&&t.type!=="subscription"||!t.filePath)continue;let o=t.filePath.replace(/\.(ts|tsx|js|jsx)$/,"");e.has(o)||e.set(o,[]),e.get(o).push({opName:t.name,opType:t.type});}let n=new Map;for(let t of a.graphqlOperations)(t.type==="query"||t.type==="mutation"||t.type==="subscription")&&n.set(t.name,t.type);for(let t of a.pages){let o=new Set(t.dataFetching.map(l=>l.operationName?.replace(/^[→\->\s]+/,"")||"")),r=a.components.find(l=>l.filePath===`src/pages/${t.filePath}`);if(!r)continue;let i=[];i.push(...r.hooks.filter(l=>l.startsWith("use")));for(let l of r.dependencies)l.startsWith("use")&&i.push(l);for(let l of i){let p=s.get(l);if(p)for(let c of p){if(o.has(c))continue;o.add(c);let m=n.get(c)||"query";t.dataFetching.push({type:m==="mutation"?"useMutation":"useQuery",operationName:c,source:`hook:${l}`});}}if(r.imports)for(let l of r.imports){let p=d.dirname(r.filePath),c=l.path;l.path.startsWith(".")?(c=d.join(p,l.path),c=d.normalize(c)):l.path.startsWith("@/")&&(c=l.path.replace("@/","src/")),c=c.replace(/\.(ts|tsx|js|jsx)$/,"");let m=e.get(c);if(m)for(let y of m)o.has(y.opName)||(o.add(y.opName),t.dataFetching.push({type:y.opType==="mutation"?"useMutation":"useQuery",operationName:y.opName,source:`import:${l.path}`}));}}for(let t of a.components){if(t.type!=="container"&&t.type!=="page")continue;let o=a.pages.find(i=>i.component===t.name||i.filePath?.includes(t.name));if(!o)continue;let r=new Set(o.dataFetching.map(i=>i.operationName?.replace(/^[→\->\s]+/,"")||""));for(let i of t.hooks){if(!i.startsWith("use"))continue;let l=s.get(i);if(l)for(let p of l){if(r.has(p))continue;r.add(p);let c=n.get(p)||"query";o.dataFetching.push({type:c==="mutation"?"useMutation":"useQuery",operationName:p,source:`component:${t.name}`});}}for(let i of t.dependencies){if(!i.startsWith("use"))continue;let l=s.get(i);if(l)for(let p of l){if(r.has(p))continue;r.add(p);let c=n.get(p)||"query";o.dataFetching.push({type:c==="mutation"?"useMutation":"useQuery",operationName:p,source:`component:${t.name}`});}}if(t.imports)for(let i of t.imports){let l=d.dirname(t.filePath),p=i.path;i.path.startsWith(".")?p=d.normalize(d.join(l,i.path)):i.path.startsWith("@/")&&(p=i.path.replace("@/","src/")),p=p.replace(/\.(ts|tsx|js|jsx)$/,"");let c=e.get(p);if(c)for(let m of c)r.has(m.opName)||(r.add(m.opName),o.dataFetching.push({type:m.opType==="mutation"?"useMutation":"useQuery",operationName:m.opName,source:`component:${t.name}`}));}}}async writeDocumentation(a){let s=this.config.outputDir;await u.mkdir(s,{recursive:true});let e=this.markdownGenerator.generateDocumentation(a);for(let[t,o]of e){let r=d.join(s,t),i=d.dirname(r);await u.mkdir(i,{recursive:true}),await u.writeFile(r,o,"utf-8");}let n=d.join(s,"report.json");await u.writeFile(n,JSON.stringify(a,null,2),"utf-8");}};function A(f){return new Promise(a=>{let s=C.createServer();s.once("error",e=>{e.code,a(false);}),s.once("listening",()=>{s.close(),a(true);}),s.listen(f);})}async function L(f,a=10){for(let s=0;s<a;s++){let e=f+s;if(await A(e))return e}throw new Error(`No available port found between ${f} and ${f+a-1}`)}var P=class{config;port;app;server;io;engine;currentReport=null;envResult=null;railsAnalysis=null;constructor(a,s=3030){this.config=a,this.port=s,this.app=E(),this.server=O.createServer(this.app),this.io=new Server(this.server),this.engine=new v(a),this.setupRoutes(),this.setupSocketIO();}setupRoutes(){this.app.use("/assets",E.static(d.join(this.config.outputDir,"assets"))),["common.css","page-map.css","docs.css","rails-map.css"].forEach(e=>{this.app.get(`/${e}`,async(n,t)=>{let o=[d.join(d.dirname(new URL(import.meta.url).pathname),"generators","assets",e),d.join(d.dirname(new URL(import.meta.url).pathname),"..","generators","assets",e),d.join(process.cwd(),"dist","generators","assets",e),d.join(process.cwd(),"src","generators","assets",e)];for(let r of o)try{let i=await u.readFile(r,"utf-8");t.type("text/css").send(i);return}catch{}t.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(t=>{this.app.get(t,async(o,r)=>{let i=[d.join(d.dirname(new URL(import.meta.url).pathname),"generators","assets","favicon",e),d.join(d.dirname(new URL(import.meta.url).pathname),"..","generators","assets","favicon",e),d.join(process.cwd(),"dist","generators","assets","favicon",e),d.join(process.cwd(),"src","generators","assets","favicon",e)];for(let l of i)try{let p=await u.readFile(l),c=e.split(".").pop(),m={ico:"image/x-icon",svg:"image/svg+xml",png:"image/png",webmanifest:"application/manifest+json"};r.type(m[c||""]||"application/octet-stream").send(p);return}catch{}r.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 t=new a$1;n.send(t.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 t=new a$2;n.send(t.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 t=e.params.path,o=Array.isArray(t)?t.join("/"):t||"index";n.send(await this.renderPage(o));}),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 t=this.currentReport?.diagrams.find(o=>o.title.toLowerCase().replace(/\s+/g,"-")===e.params.name);t?n.json(t):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(t){n.status(500).json({error:t.message});}});}setupSocketIO(){this.io.on("connection",a=>{a.on("disconnect",()=>{});});}async renderPage(a){let s=a.replace(/\.md$/,""),e=d.join(this.config.outputDir,`${s}.md`),n="";try{let t=await u.readFile(e,"utf-8"),o=await marked.parse(t);o=o.replace(/<pre><code class="language-mermaid">([\s\S]*?)<\/code><\/pre>/g,'<div class="mermaid">$1</div>'),o=o.replace(/<table>/g,'<div class="table-wrapper"><table>'),o=o.replace(/<\/table>/g,"</table></div>"),n=o;}catch(t){let o=t;if(o.code==="ENOENT"){let r=this.currentReport?.repositories.map(i=>i.name)||[];n=`
|
|
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=`
|
|
2
2
|
<h1>Page not found</h1>
|
|
3
|
-
<p>The requested path <code>${
|
|
4
|
-
${
|
|
3
|
+
<p>The requested path <code>${r}</code> does not exist.</p>
|
|
4
|
+
${w.length>0?`
|
|
5
5
|
<p>Available repositories:</p>
|
|
6
|
-
<ul>${
|
|
6
|
+
<ul>${w.map(g=>`<li><a href="/docs/repos/${g}">${g}</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}: ${
|
|
9
|
+
`;}else console.error(`\u26A0\uFE0F Error reading ${e}: ${i.message}`),n=`<h1>Error</h1><p>Failed to load page: ${i.message}</p>`;}return this.getHtmlTemplate(n)}getGraphQLData(){if(!this.currentReport)return "[]";let t=[];for(let r of this.currentReport.repositories)for(let e of r.analysis?.graphqlOperations||[])t.push({name:e.name,type:e.type,returnType:e.returnType,variables:e.variables,fields:e.fields,usedIn:e.usedIn});return JSON.stringify(t)}getApiCallsData(){if(!this.currentReport)return "[]";let t=[];for(let r of this.currentReport.repositories)for(let e of r.analysis?.apiCalls||[])t.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(t)}getHtmlTemplate(t){let r=this.getGraphQLData(),e=this.getApiCallsData();return `<!DOCTYPE html>
|
|
10
10
|
<html lang="en">
|
|
11
11
|
<head>
|
|
12
12
|
<meta charset="UTF-8">
|
|
@@ -20,7 +20,7 @@ import {a as a$2}from'./chunk-P7MX3M5U.js';import {a as a$3}from'./chunk-VV3A3UE
|
|
|
20
20
|
<script src="https://cdn.jsdelivr.net/npm/mermaid/dist/mermaid.min.js"></script>
|
|
21
21
|
<script src="/socket.io/socket.io.js"></script>
|
|
22
22
|
<script>
|
|
23
|
-
window.graphqlOps = ${
|
|
23
|
+
window.graphqlOps = ${r};
|
|
24
24
|
window.apiCalls = ${e};
|
|
25
25
|
// Create multiple lookup maps for different naming conventions
|
|
26
26
|
window.gqlMap = new Map();
|
|
@@ -124,7 +124,7 @@ import {a as a$2}from'./chunk-P7MX3M5U.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
|
+
${t}
|
|
128
128
|
</div>
|
|
129
129
|
</div>
|
|
130
130
|
</div>
|
|
@@ -158,6 +158,13 @@ import {a as a$2}from'./chunk-P7MX3M5U.js';import {a as a$3}from'./chunk-VV3A3UE
|
|
|
158
158
|
|
|
159
159
|
// Render all mermaid diagrams on page load
|
|
160
160
|
document.addEventListener('DOMContentLoaded', async () => {
|
|
161
|
+
// Snapshot raw dataflow mermaid source BEFORE first render (needed for filtering rerenders)
|
|
162
|
+
document.querySelectorAll('.mermaid[data-mermaid-scope="dataflow"]').forEach((el) => {
|
|
163
|
+
if (!el.dataset.mermaidRaw) {
|
|
164
|
+
el.dataset.mermaidRaw = el.textContent || '';
|
|
165
|
+
}
|
|
166
|
+
});
|
|
167
|
+
|
|
161
168
|
// Wrap mermaid divs with container and controls
|
|
162
169
|
document.querySelectorAll('.mermaid').forEach((el, idx) => {
|
|
163
170
|
const container = document.createElement('div');
|
|
@@ -183,21 +190,146 @@ import {a as a$2}from'./chunk-P7MX3M5U.js';import {a as a$3}from'./chunk-VV3A3UE
|
|
|
183
190
|
});
|
|
184
191
|
|
|
185
192
|
try {
|
|
186
|
-
await
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
const text = node.querySelector('span, text, .nodeLabel')?.textContent || '';
|
|
193
|
-
showNodeDetail(text, node);
|
|
194
|
-
});
|
|
195
|
-
});
|
|
193
|
+
await renderMermaidElements(document.querySelectorAll('.mermaid'));
|
|
194
|
+
|
|
195
|
+
attachMermaidNodeHandlers();
|
|
196
|
+
|
|
197
|
+
// Init toggle filters AFTER mermaid is ready (prevents double-run issues)
|
|
198
|
+
initOpsFilters();
|
|
196
199
|
} catch (e) {
|
|
197
200
|
console.error('Mermaid rendering error:', e);
|
|
198
201
|
}
|
|
199
202
|
});
|
|
200
203
|
|
|
204
|
+
function initOpsFilters() {
|
|
205
|
+
document.querySelectorAll('.ops-filters').forEach((container) => {
|
|
206
|
+
const scope = container.getAttribute('data-filter-scope') || 'default';
|
|
207
|
+
const inputs = Array.from(
|
|
208
|
+
container.querySelectorAll('input[type="checkbox"][data-filter]')
|
|
209
|
+
);
|
|
210
|
+
if (inputs.length === 0) return;
|
|
211
|
+
|
|
212
|
+
const apply = async () => {
|
|
213
|
+
const enabled = new Set(
|
|
214
|
+
inputs
|
|
215
|
+
.filter((i) => i.checked)
|
|
216
|
+
.map((i) => i.getAttribute('data-filter'))
|
|
217
|
+
.filter(Boolean)
|
|
218
|
+
);
|
|
219
|
+
document
|
|
220
|
+
.querySelectorAll('[data-ops-scope="' + scope + '"][data-ops-group]')
|
|
221
|
+
.forEach((el) => {
|
|
222
|
+
const key = el.getAttribute('data-ops-group');
|
|
223
|
+
// If key is missing, do nothing
|
|
224
|
+
if (!key) return;
|
|
225
|
+
el.style.display = enabled.has(key) ? '' : 'none';
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
// Update UI state for pill toggles
|
|
229
|
+
inputs.forEach((i) => {
|
|
230
|
+
const wrap = i.closest('.ops-toggle');
|
|
231
|
+
if (wrap) wrap.classList.toggle('is-on', i.checked);
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
// Apply to dataflow mermaid charts by re-rendering with filtered groups
|
|
235
|
+
if (scope === 'dataflow') {
|
|
236
|
+
await applyDataflowMermaidFilter(enabled);
|
|
237
|
+
}
|
|
238
|
+
};
|
|
239
|
+
|
|
240
|
+
inputs.forEach((i) => i.addEventListener('change', () => void apply()));
|
|
241
|
+
void apply();
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
async function applyDataflowMermaidFilter(enabledGroups) {
|
|
246
|
+
const els = Array.from(document.querySelectorAll('.mermaid[data-mermaid-scope="dataflow"]'));
|
|
247
|
+
if (els.length === 0) return;
|
|
248
|
+
|
|
249
|
+
// Store original source before the first render (or first filter)
|
|
250
|
+
els.forEach((el) => {
|
|
251
|
+
if (!el.dataset.mermaidRaw) {
|
|
252
|
+
el.dataset.mermaidRaw = el.textContent || '';
|
|
253
|
+
}
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
const buildFiltered = (raw) => {
|
|
257
|
+
// NOTE: This code is embedded inside an HTML template literal.
|
|
258
|
+
// Avoid using quotes/backslashes in comments here (they can be mangled by the outer template).
|
|
259
|
+
const lines = raw.split('\\n');
|
|
260
|
+
const out = [];
|
|
261
|
+
let current = null; // 'direct'|'close'|'indirect'|'common'|null
|
|
262
|
+
|
|
263
|
+
for (const line of lines) {
|
|
264
|
+
const start = line.match(/^%%DFG_GROUP:(direct|close|indirect|common):start%%$/);
|
|
265
|
+
if (start) {
|
|
266
|
+
current = start[1];
|
|
267
|
+
continue;
|
|
268
|
+
}
|
|
269
|
+
const end = line.match(/^%%DFG_GROUP:(direct|close|indirect|common):end%%$/);
|
|
270
|
+
if (end) {
|
|
271
|
+
current = null;
|
|
272
|
+
continue;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
if (!current) {
|
|
276
|
+
out.push(line);
|
|
277
|
+
continue;
|
|
278
|
+
}
|
|
279
|
+
if (enabledGroups.has(current)) out.push(line);
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
return out.join('\\n').trim();
|
|
283
|
+
};
|
|
284
|
+
|
|
285
|
+
// Update the mermaid source text and re-run mermaid renderer
|
|
286
|
+
for (const el of els) {
|
|
287
|
+
const raw = el.dataset.mermaidRaw || '';
|
|
288
|
+
const filtered = buildFiltered(raw);
|
|
289
|
+
el.removeAttribute('data-processed');
|
|
290
|
+
el.textContent = filtered;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
try {
|
|
294
|
+
await renderMermaidElements(
|
|
295
|
+
document.querySelectorAll('.mermaid[data-mermaid-scope="dataflow"]')
|
|
296
|
+
);
|
|
297
|
+
attachMermaidNodeHandlers();
|
|
298
|
+
} catch (e) {
|
|
299
|
+
console.error('Mermaid rendering error (dataflow filter):', e);
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
async function renderMermaidElements(nodeList) {
|
|
304
|
+
const elements = Array.from(nodeList || []);
|
|
305
|
+
for (let idx = 0; idx < elements.length; idx++) {
|
|
306
|
+
const el = elements[idx];
|
|
307
|
+
const marker = 'mermaid-single-run';
|
|
308
|
+
const unique = marker + '-' + idx;
|
|
309
|
+
el.classList.add(marker, unique);
|
|
310
|
+
try {
|
|
311
|
+
await mermaid.run({ querySelector: '.' + unique });
|
|
312
|
+
} catch (e) {
|
|
313
|
+
const page = el.getAttribute('data-mermaid-page') || '';
|
|
314
|
+
const scope = el.getAttribute('data-mermaid-scope') || '';
|
|
315
|
+
const preview = (el.textContent || '').slice(0, 260);
|
|
316
|
+
console.error('Mermaid rendering error (element)', { idx, scope, page, preview }, e);
|
|
317
|
+
} finally {
|
|
318
|
+
el.classList.remove(marker, unique);
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
function attachMermaidNodeHandlers() {
|
|
324
|
+
document.querySelectorAll('.mermaid .node').forEach(node => {
|
|
325
|
+
node.addEventListener('click', (e) => {
|
|
326
|
+
e.stopPropagation();
|
|
327
|
+
const text = node.querySelector('span, text, .nodeLabel')?.textContent || '';
|
|
328
|
+
showNodeDetail(text, node);
|
|
329
|
+
});
|
|
330
|
+
});
|
|
331
|
+
}
|
|
332
|
+
|
|
201
333
|
function setupDragHandlers(idx) {
|
|
202
334
|
const wrapper = document.getElementById(\`wrapper-\${idx}\`);
|
|
203
335
|
const inner = document.getElementById(\`inner-\${idx}\`);
|
|
@@ -953,11 +1085,11 @@ import {a as a$2}from'./chunk-P7MX3M5U.js';import {a as a$3}from'./chunk-VV3A3UE
|
|
|
953
1085
|
}
|
|
954
1086
|
</script>
|
|
955
1087
|
</body>
|
|
956
|
-
</html>`}async start(
|
|
957
|
-
Rails...`);try{this.railsAnalysis=await k(
|
|
1088
|
+
</html>`}async start(t=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 n=e.summary;console.log(` \u2705 ${e.displayName}`),console.log(` ${n.totalPages} pages \xB7 ${n.totalComponents} components \xB7 ${n.totalGraphQLOperations} GraphQL \xB7 ${n.totalDataFlows} data flows`);}if(this.envResult.hasRails){console.log(`
|
|
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 de(this.port);e!==this.port&&console.log(`
|
|
958
1090
|
\u26A0\uFE0F Port ${this.port} is in use, using port ${e} instead`),this.port=e;}catch(e){console.error(`
|
|
959
1091
|
\u274C Failed to find available port: ${e.message}`),process.exit(1);}if(this.server.listen(this.port,()=>{console.log(`
|
|
960
1092
|
\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
|
|
961
|
-
`);}),
|
|
962
|
-
\u{1F504} Regenerating...`),this.currentReport=await this.engine.generate(),this.envResult?.hasRails){let
|
|
963
|
-
\u{1F44B} Server stopped`);}};export{
|
|
1093
|
+
`);}),t){let e=(await import('open')).default;await e(`http://localhost:${this.port}`);}this.config.watch.enabled&&this.watchForChanges();}async regenerate(){if(console.log(`
|
|
1094
|
+
\u{1F504} Regenerating...`),this.currentReport=await this.engine.generate(),this.envResult?.hasRails){let t=this.config.repositories[0]?.path||process.cwd();try{this.railsAnalysis=await k(t);}catch(r){console.error("\u26A0\uFE0F Rails re-analysis failed:",r.message);}}for(let t of this.currentReport.repositories)console.log(` ${t.displayName}: ${t.summary.totalPages} pages \xB7 ${t.summary.totalComponents} components \xB7 ${t.summary.totalGraphQLOperations} GraphQL`);this.io.emit("reload"),console.log("\u2705 Done");}async watchForChanges(){let t=this.config.repositories.map(e=>e.path),r=null;for(let e of t)try{let n=T.watch(e,{recursive:!0});(async()=>{for await(let s of n)s.filename&&(s.filename.endsWith(".ts")||s.filename.endsWith(".tsx"))&&(r&&clearTimeout(r),r=setTimeout(async()=>{await this.regenerate();},this.config.watch.debounce));})();}catch(n){console.warn(`Warning: Could not watch directory ${e}:`,n.message);}}stop(){this.server.close(),console.log(`
|
|
1095
|
+
\u{1F44B} Server stopped`);}};export{U as a,ue as b};
|