@wtdlee/repomap 0.7.0 → 0.9.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 +17 -1
- package/dist/{chunk-JDM7Y7PX.js → chunk-LDX6WPHR.js} +1 -1
- package/dist/{chunk-OQAXO3X2.js → chunk-P7MX3M5U.js} +288 -16
- package/dist/{chunk-GCIRJGW3.js → chunk-SMN6XFMS.js} +3 -3
- package/dist/cli.js +16 -14
- package/dist/generators/assets/rails-map.css +6 -0
- package/dist/generators/index.js +1 -1
- package/dist/index.js +1 -1
- package/dist/page-map-generator-2XQB7RWO.js +1 -0
- package/dist/rails-map-generator-77ATUFMP.js +1 -0
- package/dist/server/index.js +1 -1
- package/package.json +1 -5
- package/dist/page-map-generator-3GO6GL2P.js +0 -1
- package/dist/rails-map-generator-CAQZUBI6.js +0 -1
package/README.md
CHANGED
|
@@ -101,13 +101,16 @@ npx @wtdlee/repomap serve
|
|
|
101
101
|
repomap serve [options]
|
|
102
102
|
-p, --port <number> Server port (default: 3030)
|
|
103
103
|
-c, --config <path> Path to config file
|
|
104
|
+
-o, --output <path> Output directory (default: .repomap)
|
|
104
105
|
--path <path> Path to repository to analyze
|
|
106
|
+
--temp Use OS temp directory (no files in repository)
|
|
105
107
|
--no-open Don't open browser automatically
|
|
106
108
|
|
|
107
109
|
# generate command options
|
|
108
110
|
repomap generate [options]
|
|
109
111
|
-c, --config <path> Path to config file
|
|
110
|
-
-o, --output <path> Output directory
|
|
112
|
+
-o, --output <path> Output directory (default: .repomap)
|
|
113
|
+
--temp Use OS temp directory (no files in repository)
|
|
111
114
|
--repo <name> Analyze specific repository only
|
|
112
115
|
--watch Watch for changes and regenerate
|
|
113
116
|
--static Generate standalone HTML files (for GitHub Pages)
|
|
@@ -120,6 +123,19 @@ repomap rails [options]
|
|
|
120
123
|
-o, --output <path> Output HTML file path
|
|
121
124
|
```
|
|
122
125
|
|
|
126
|
+
### Output Directory Options
|
|
127
|
+
|
|
128
|
+
```bash
|
|
129
|
+
# Default: creates .repomap in current directory
|
|
130
|
+
repomap serve
|
|
131
|
+
|
|
132
|
+
# Custom output directory
|
|
133
|
+
repomap serve -o ./docs
|
|
134
|
+
|
|
135
|
+
# Temporary directory (auto-cleaned on exit)
|
|
136
|
+
repomap serve --temp
|
|
137
|
+
```
|
|
138
|
+
|
|
123
139
|
## CI/CD Integration
|
|
124
140
|
|
|
125
141
|
### Deploy to GitHub Pages
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {a as a$2}from'./chunk-OQAXO3X2.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-GCIRJGW3.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-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=`
|
|
2
2
|
<h1>Page not found</h1>
|
|
3
3
|
<p>The requested path <code>${s}</code> does not exist.</p>
|
|
4
4
|
${r.length>0?`
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {k}from'./chunk-H7VVRHQZ.js';import*as
|
|
1
|
+
import {k}from'./chunk-H7VVRHQZ.js';import*as r from'fs';import*as c from'path';var n=class{constructor(t){this.rootPath=t;}result=null;async generate(t={}){if(!this.rootPath)throw new Error("Root path required for analysis");let{title:e="Rails Application Map"}=t;this.result=await k(this.rootPath);let s=this.generateHTML(e);return t.outputPath&&(r.writeFileSync(t.outputPath,s),console.log(`
|
|
2
2
|
\u{1F4C4} Generated: ${t.outputPath}`)),s}generateFromResult(t,e="Rails Application Map"){return this.result=t,this.generateHTML(e)}generateHTML(t){if(!this.result)throw new Error("Analysis not run");let{routes:e,controllers:s,models:a,grpc:l,summary:i}=this.result;return `<!DOCTYPE html>
|
|
3
3
|
<html lang="en">
|
|
4
4
|
<head>
|
|
@@ -524,10 +524,112 @@ import {k}from'./chunk-H7VVRHQZ.js';import*as d from'fs';import*as c from'path';
|
|
|
524
524
|
detailPanel.classList.add('open');
|
|
525
525
|
};
|
|
526
526
|
|
|
527
|
-
|
|
528
|
-
|
|
527
|
+
// Diagram state
|
|
528
|
+
let diagramModelCount = 15;
|
|
529
|
+
let diagramNamespace = 'all';
|
|
530
|
+
let diagramFocusModel = '';
|
|
531
|
+
let diagramDepth = 2;
|
|
532
|
+
|
|
533
|
+
function getNamespaces() {
|
|
534
|
+
const ns = new Set();
|
|
535
|
+
const prefixes = new Map(); // Count common prefixes
|
|
536
|
+
|
|
537
|
+
models.forEach(m => {
|
|
538
|
+
const name = m.name || m.className || '';
|
|
539
|
+
// Check for Ruby namespace (::)
|
|
540
|
+
if (name.includes('::')) {
|
|
541
|
+
ns.add(name.split('::')[0]);
|
|
542
|
+
}
|
|
543
|
+
// Also try to find common prefixes (e.g., User, UserProfile, UserSetting -> User)
|
|
544
|
+
const match = name.match(/^([A-Z][a-z]+)/);
|
|
545
|
+
if (match) {
|
|
546
|
+
const prefix = match[1];
|
|
547
|
+
prefixes.set(prefix, (prefixes.get(prefix) || 0) + 1);
|
|
548
|
+
}
|
|
549
|
+
});
|
|
550
|
+
|
|
551
|
+
// Add prefixes that have 3+ models as pseudo-namespaces
|
|
552
|
+
prefixes.forEach((count, prefix) => {
|
|
553
|
+
if (count >= 3 && !ns.has(prefix)) {
|
|
554
|
+
ns.add(prefix + '*'); // Mark as prefix-based filter
|
|
555
|
+
}
|
|
556
|
+
});
|
|
557
|
+
|
|
558
|
+
return ['all', ...Array.from(ns).sort()];
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
function getModelNames() {
|
|
562
|
+
return models.map(m => m.name || m.className).sort();
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
// Get related models up to specified depth
|
|
566
|
+
function getRelatedModels(centerModel, depth) {
|
|
567
|
+
const related = new Set([centerModel]);
|
|
568
|
+
const modelMap = new Map();
|
|
569
|
+
models.forEach(m => {
|
|
570
|
+
const name = m.name || m.className;
|
|
571
|
+
modelMap.set(name, m);
|
|
572
|
+
});
|
|
573
|
+
|
|
574
|
+
for (let d = 0; d < depth; d++) {
|
|
575
|
+
const currentModels = [...related];
|
|
576
|
+
currentModels.forEach(modelName => {
|
|
577
|
+
const model = modelMap.get(modelName);
|
|
578
|
+
if (!model) return;
|
|
579
|
+
|
|
580
|
+
model.associations.forEach(assoc => {
|
|
581
|
+
const targetName = assoc.className || capitalize(singularize(assoc.name));
|
|
582
|
+
if (modelMap.has(targetName)) {
|
|
583
|
+
related.add(targetName);
|
|
584
|
+
}
|
|
585
|
+
});
|
|
586
|
+
|
|
587
|
+
// Also find models that reference this model
|
|
588
|
+
models.forEach(m => {
|
|
589
|
+
const mName = m.name || m.className;
|
|
590
|
+
m.associations.forEach(assoc => {
|
|
591
|
+
const targetName = assoc.className || capitalize(singularize(assoc.name));
|
|
592
|
+
if (targetName === modelName) {
|
|
593
|
+
related.add(mName);
|
|
594
|
+
}
|
|
595
|
+
});
|
|
596
|
+
});
|
|
597
|
+
});
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
return related;
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
function generateMermaidCode(modelCount, namespace, focusModel, depth) {
|
|
604
|
+
let filteredModels = [...models];
|
|
605
|
+
|
|
606
|
+
// Filter by focus model (takes priority)
|
|
607
|
+
if (focusModel && focusModel !== '') {
|
|
608
|
+
const relatedNames = getRelatedModels(focusModel, depth);
|
|
609
|
+
filteredModels = filteredModels.filter(m => {
|
|
610
|
+
const name = m.name || m.className;
|
|
611
|
+
return relatedNames.has(name);
|
|
612
|
+
});
|
|
613
|
+
}
|
|
614
|
+
// Filter by namespace
|
|
615
|
+
else if (namespace !== 'all') {
|
|
616
|
+
filteredModels = filteredModels.filter(m => {
|
|
617
|
+
const name = m.name || m.className || '';
|
|
618
|
+
// Handle prefix-based filter (ends with *)
|
|
619
|
+
if (namespace.endsWith('*')) {
|
|
620
|
+
const prefix = namespace.slice(0, -1);
|
|
621
|
+
return name.startsWith(prefix);
|
|
622
|
+
}
|
|
623
|
+
// Handle Ruby namespace (::)
|
|
624
|
+
return name.startsWith(namespace + '::') || name === namespace;
|
|
625
|
+
});
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
// Sort and limit
|
|
629
|
+
const count = modelCount === 'all' ? filteredModels.length : parseInt(modelCount) || 15;
|
|
630
|
+
const topModels = filteredModels
|
|
529
631
|
.sort((a, b) => b.associations.length - a.associations.length)
|
|
530
|
-
.slice(0,
|
|
632
|
+
.slice(0, count);
|
|
531
633
|
|
|
532
634
|
const modelNames = new Set(topModels.map(m => m.name || m.className));
|
|
533
635
|
let mermaidCode = 'erDiagram\\n';
|
|
@@ -560,12 +662,163 @@ import {k}from'./chunk-H7VVRHQZ.js';import*as d from'fs';import*as c from'path';
|
|
|
560
662
|
});
|
|
561
663
|
}
|
|
562
664
|
|
|
665
|
+
return { mermaidCode, modelCount: topModels.length, totalModels: filteredModels.length };
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
window.toggleCustomInput = function() {
|
|
669
|
+
const countSelect = document.getElementById('model-count-select');
|
|
670
|
+
const customWrapper = document.getElementById('custom-input-wrapper');
|
|
671
|
+
if (countSelect.value === 'custom') {
|
|
672
|
+
customWrapper.style.display = 'flex';
|
|
673
|
+
document.getElementById('model-count-input').focus();
|
|
674
|
+
} else {
|
|
675
|
+
customWrapper.style.display = 'none';
|
|
676
|
+
document.getElementById('model-count-input').value = '';
|
|
677
|
+
updateDiagram();
|
|
678
|
+
}
|
|
679
|
+
};
|
|
680
|
+
|
|
681
|
+
window.clearFocusModel = function() {
|
|
682
|
+
document.getElementById('focus-model-select').value = '';
|
|
683
|
+
diagramFocusModel = '';
|
|
684
|
+
updateDiagram();
|
|
685
|
+
};
|
|
686
|
+
|
|
687
|
+
window.updateDiagram = function() {
|
|
688
|
+
const countInput = document.getElementById('model-count-input');
|
|
689
|
+
const countSelect = document.getElementById('model-count-select');
|
|
690
|
+
const nsSelect = document.getElementById('namespace-select');
|
|
691
|
+
const focusSelect = document.getElementById('focus-model-select');
|
|
692
|
+
const depthSelect = document.getElementById('depth-select');
|
|
693
|
+
|
|
694
|
+
// Get count from input or select
|
|
695
|
+
let count;
|
|
696
|
+
if (countSelect && countSelect.value === 'custom') {
|
|
697
|
+
count = countInput ? countInput.value.trim() || '15' : '15';
|
|
698
|
+
} else {
|
|
699
|
+
count = countSelect ? countSelect.value : '15';
|
|
700
|
+
}
|
|
701
|
+
diagramModelCount = count;
|
|
702
|
+
diagramNamespace = nsSelect ? nsSelect.value : 'all';
|
|
703
|
+
diagramFocusModel = focusSelect ? focusSelect.value : '';
|
|
704
|
+
diagramDepth = depthSelect ? parseInt(depthSelect.value) || 2 : 2;
|
|
705
|
+
|
|
706
|
+
// If focus model is set, disable namespace filter and enable depth
|
|
707
|
+
if (nsSelect) {
|
|
708
|
+
nsSelect.disabled = diagramFocusModel !== '';
|
|
709
|
+
nsSelect.style.opacity = diagramFocusModel !== '' ? '0.5' : '1';
|
|
710
|
+
}
|
|
711
|
+
if (depthSelect) {
|
|
712
|
+
depthSelect.disabled = diagramFocusModel === '';
|
|
713
|
+
depthSelect.style.opacity = diagramFocusModel !== '' ? '1' : '0.5';
|
|
714
|
+
const depthLabel = depthSelect.parentElement?.querySelector('span');
|
|
715
|
+
if (depthLabel) {
|
|
716
|
+
depthLabel.style.opacity = diagramFocusModel !== '' ? '1' : '0.5';
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
const { mermaidCode, modelCount, totalModels } = generateMermaidCode(count, diagramNamespace, diagramFocusModel, diagramDepth);
|
|
721
|
+
|
|
722
|
+
// Update diagram - need to recreate SVG
|
|
723
|
+
const container = document.getElementById('mermaid-container');
|
|
724
|
+
const diagram = document.getElementById('mermaid-diagram');
|
|
725
|
+
if (diagram && window.mermaid) {
|
|
726
|
+
// Remove old SVG
|
|
727
|
+
const oldSvg = container.querySelector('svg');
|
|
728
|
+
if (oldSvg) oldSvg.remove();
|
|
729
|
+
|
|
730
|
+
// Update mermaid code
|
|
731
|
+
diagram.textContent = mermaidCode;
|
|
732
|
+
diagram.removeAttribute('data-processed');
|
|
733
|
+
diagram.style.display = 'block';
|
|
734
|
+
|
|
735
|
+
// Re-render
|
|
736
|
+
window.mermaid.init(undefined, diagram);
|
|
737
|
+
setTimeout(() => {
|
|
738
|
+
initDiagramPanZoom();
|
|
739
|
+
}, 200);
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
// Update title
|
|
743
|
+
const title = document.querySelector('.diagram-title-text');
|
|
744
|
+
if (title) {
|
|
745
|
+
let filterText = '';
|
|
746
|
+
if (diagramFocusModel) {
|
|
747
|
+
filterText = \` around \${diagramFocusModel} (depth \${diagramDepth})\`;
|
|
748
|
+
} else if (diagramNamespace !== 'all') {
|
|
749
|
+
filterText = \` in \${diagramNamespace}\`;
|
|
750
|
+
}
|
|
751
|
+
title.textContent = \`Model Relationships (\${modelCount}/\${totalModels} models\${filterText})\`;
|
|
752
|
+
}
|
|
753
|
+
};
|
|
754
|
+
|
|
755
|
+
function renderDiagramView() {
|
|
756
|
+
const namespaces = getNamespaces();
|
|
757
|
+
const modelNames = getModelNames();
|
|
758
|
+
const { mermaidCode, modelCount, totalModels } = generateMermaidCode(diagramModelCount, diagramNamespace, diagramFocusModel, diagramDepth);
|
|
759
|
+
|
|
760
|
+
let filterText = '';
|
|
761
|
+
if (diagramFocusModel) {
|
|
762
|
+
filterText = \` around \${diagramFocusModel} (depth \${diagramDepth})\`;
|
|
763
|
+
} else if (diagramNamespace !== 'all') {
|
|
764
|
+
filterText = \` in \${diagramNamespace}\`;
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
const isCustom = !['15', '30', '50', '100', 'all'].includes(String(diagramModelCount));
|
|
768
|
+
|
|
563
769
|
return \`
|
|
564
|
-
<div class="
|
|
565
|
-
<div class="panel-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
770
|
+
<div class="diagram-view-wrapper" style="display:flex;flex-direction:column;height:100%;min-height:0;">
|
|
771
|
+
<div class="panel-header" style="flex-wrap:wrap;gap:8px;flex-shrink:0;">
|
|
772
|
+
<div class="panel-title diagram-title-text">Model Relationships (\${modelCount}/\${totalModels} models\${filterText})</div>
|
|
773
|
+
<div class="diagram-filters" style="display:flex;gap:12px;align-items:center;flex-wrap:wrap;font-size:12px;">
|
|
774
|
+
<label style="display:flex;align-items:center;gap:6px;">
|
|
775
|
+
<span>Limit:</span>
|
|
776
|
+
<select id="model-count-select" onchange="toggleCustomInput()" style="padding:6px 10px;border-radius:4px;background:#2d2d2d;color:#fff;border:1px solid #444;min-width:80px;">
|
|
777
|
+
<option value="15" \${diagramModelCount == 15 ? 'selected' : ''}>15</option>
|
|
778
|
+
<option value="30" \${diagramModelCount == 30 ? 'selected' : ''}>30</option>
|
|
779
|
+
<option value="50" \${diagramModelCount == 50 ? 'selected' : ''}>50</option>
|
|
780
|
+
<option value="100" \${diagramModelCount == 100 ? 'selected' : ''}>100</option>
|
|
781
|
+
<option value="all" \${diagramModelCount === 'all' ? 'selected' : ''}>All (\${models.length})</option>
|
|
782
|
+
<option value="custom" \${isCustom ? 'selected' : ''}>Custom...</option>
|
|
783
|
+
</select>
|
|
784
|
+
<div id="custom-input-wrapper" style="display:\${isCustom ? 'flex' : 'none'};align-items:center;gap:4px;">
|
|
785
|
+
<input type="number" id="model-count-input" placeholder="Enter number" min="1" max="\${models.length}"
|
|
786
|
+
value="\${isCustom ? diagramModelCount : ''}"
|
|
787
|
+
style="width:100px;padding:6px 10px;border-radius:4px;background:#2d2d2d;color:#fff;border:1px solid #444;"
|
|
788
|
+
onchange="updateDiagram()" onkeyup="if(event.key==='Enter')updateDiagram()">
|
|
789
|
+
<button onclick="updateDiagram()" style="padding:6px 12px;border-radius:4px;background:#3b82f6;color:#fff;border:none;cursor:pointer;">Apply</button>
|
|
790
|
+
</div>
|
|
791
|
+
</label>
|
|
792
|
+
<label style="display:flex;align-items:center;gap:6px;">
|
|
793
|
+
<span>Namespace:</span>
|
|
794
|
+
<select id="namespace-select" onchange="updateDiagram()" style="padding:6px 10px;border-radius:4px;background:#2d2d2d;color:#fff;border:1px solid #444;\${diagramFocusModel ? 'opacity:0.5;' : ''}" \${diagramFocusModel ? 'disabled' : ''}>
|
|
795
|
+
<option value="all" \${diagramNamespace === 'all' ? 'selected' : ''}>All</option>
|
|
796
|
+
\${namespaces.filter(ns => ns !== 'all').map(ns => \`<option value="\${ns}" \${diagramNamespace === ns ? 'selected' : ''}>\${ns}</option>\`).join('')}
|
|
797
|
+
</select>
|
|
798
|
+
</label>
|
|
799
|
+
<label style="display:flex;align-items:center;gap:6px;">
|
|
800
|
+
<span>Focus:</span>
|
|
801
|
+
<select id="focus-model-select" onchange="updateDiagram()" style="padding:6px 10px;border-radius:4px;background:#2d2d2d;color:#fff;border:1px solid #444;max-width:150px;">
|
|
802
|
+
<option value="">None</option>
|
|
803
|
+
\${modelNames.map(name => \`<option value="\${name}" \${diagramFocusModel === name ? 'selected' : ''}>\${name}</option>\`).join('')}
|
|
804
|
+
</select>
|
|
805
|
+
\${diagramFocusModel ? \`<button onclick="clearFocusModel()" style="padding:4px 8px;border-radius:4px;background:#666;color:#fff;border:none;cursor:pointer;" title="Clear focus">\u2715</button>\` : ''}
|
|
806
|
+
</label>
|
|
807
|
+
<label style="display:flex;align-items:center;gap:6px;">
|
|
808
|
+
<span style="opacity:\${diagramFocusModel ? 1 : 0.5}">Depth:</span>
|
|
809
|
+
<select id="depth-select" onchange="updateDiagram()" \${diagramFocusModel ? '' : 'disabled'} style="padding:6px 10px;border-radius:4px;background:#2d2d2d;color:#fff;border:1px solid #444;opacity:\${diagramFocusModel ? 1 : 0.5}">
|
|
810
|
+
<option value="1" \${diagramDepth === 1 ? 'selected' : ''}>1</option>
|
|
811
|
+
<option value="2" \${diagramDepth === 2 ? 'selected' : ''}>2</option>
|
|
812
|
+
<option value="3" \${diagramDepth === 3 ? 'selected' : ''}>3</option>
|
|
813
|
+
<option value="4" \${diagramDepth === 4 ? 'selected' : ''}>4</option>
|
|
814
|
+
<option value="5" \${diagramDepth === 5 ? 'selected' : ''}>5</option>
|
|
815
|
+
</select>
|
|
816
|
+
</label>
|
|
817
|
+
</div>
|
|
818
|
+
</div>
|
|
819
|
+
<div class="mermaid-container" id="mermaid-container" style="flex:1;min-height:0;">
|
|
820
|
+
<pre class="mermaid" id="mermaid-diagram">\${mermaidCode}</pre>
|
|
821
|
+
</div>
|
|
569
822
|
</div>
|
|
570
823
|
\`;
|
|
571
824
|
}
|
|
@@ -609,6 +862,20 @@ import {k}from'./chunk-H7VVRHQZ.js';import*as d from'fs';import*as c from'path';
|
|
|
609
862
|
const svg = container?.querySelector('svg');
|
|
610
863
|
if (!svg) return;
|
|
611
864
|
|
|
865
|
+
// Calculate dynamic max zoom based on SVG size
|
|
866
|
+
const svgRect = svg.getBoundingClientRect();
|
|
867
|
+
const containerRect = container.getBoundingClientRect();
|
|
868
|
+
const svgWidth = svgRect.width || 1000;
|
|
869
|
+
const svgHeight = svgRect.height || 500;
|
|
870
|
+
|
|
871
|
+
// Max zoom: allow reading small text clearly
|
|
872
|
+
// For very wide diagrams (many models), need much higher zoom
|
|
873
|
+
const minZoom = 0.01;
|
|
874
|
+
const maxZoom = Math.max(100, Math.ceil(svgWidth / 20)); // Very aggressive zoom
|
|
875
|
+
window.diagramMaxZoom = maxZoom;
|
|
876
|
+
window.diagramMinZoom = minZoom;
|
|
877
|
+
console.log('Diagram zoom range:', minZoom, '-', maxZoom, 'x (SVG width:', svgWidth, 'px)');
|
|
878
|
+
|
|
612
879
|
let scale = 1;
|
|
613
880
|
let translateX = 0;
|
|
614
881
|
let translateY = 0;
|
|
@@ -803,11 +1070,13 @@ import {k}from'./chunk-H7VVRHQZ.js';import*as d from'fs';import*as c from'path';
|
|
|
803
1070
|
svg.style.transform = \`translate(\${translateX}px, \${translateY}px) scale(\${scale})\`;
|
|
804
1071
|
}
|
|
805
1072
|
|
|
806
|
-
// Mouse wheel zoom
|
|
1073
|
+
// Mouse wheel zoom (extended range: 0.3x to 10x)
|
|
807
1074
|
container.addEventListener('wheel', (e) => {
|
|
808
1075
|
e.preventDefault();
|
|
809
|
-
|
|
810
|
-
|
|
1076
|
+
// Dynamic step: larger steps at higher zoom levels for faster navigation
|
|
1077
|
+
const step = Math.max(0.1, scale * 0.15);
|
|
1078
|
+
const delta = e.deltaY > 0 ? -step : step;
|
|
1079
|
+
scale = Math.max(minZoom, Math.min(maxZoom, scale + delta));
|
|
811
1080
|
updateTransform();
|
|
812
1081
|
}, { passive: false });
|
|
813
1082
|
|
|
@@ -834,7 +1103,7 @@ import {k}from'./chunk-H7VVRHQZ.js';import*as d from'fs';import*as c from'path';
|
|
|
834
1103
|
e.touches[0].clientY - e.touches[1].clientY
|
|
835
1104
|
);
|
|
836
1105
|
const delta = (dist - lastTouchDist) * 0.01;
|
|
837
|
-
scale = Math.max(
|
|
1106
|
+
scale = Math.max(minZoom, Math.min(maxZoom, scale + delta));
|
|
838
1107
|
lastTouchDist = dist;
|
|
839
1108
|
updateTransform();
|
|
840
1109
|
} else if (e.touches.length === 1 && isDragging) {
|
|
@@ -873,7 +1142,10 @@ import {k}from'./chunk-H7VVRHQZ.js';import*as d from'fs';import*as c from'path';
|
|
|
873
1142
|
|
|
874
1143
|
// Global functions for controls
|
|
875
1144
|
window.diagramZoom = (delta) => {
|
|
876
|
-
|
|
1145
|
+
// Dynamic step based on current scale
|
|
1146
|
+
const step = Math.max(0.2, scale * 0.2);
|
|
1147
|
+
const actualDelta = delta > 0 ? step : -step;
|
|
1148
|
+
scale = Math.max(minZoom, Math.min(maxZoom, scale + actualDelta));
|
|
877
1149
|
updateTransform();
|
|
878
1150
|
};
|
|
879
1151
|
|
|
@@ -1147,5 +1419,5 @@ import {k}from'./chunk-H7VVRHQZ.js';import*as d from'fs';import*as c from'path';
|
|
|
1147
1419
|
`).join("")}
|
|
1148
1420
|
</tbody>
|
|
1149
1421
|
</table>
|
|
1150
|
-
`}highlightParams(t){return t.replace(/:([a-zA-Z_]+)/g,'<span class="param">:$1</span>')}};async function m(){let
|
|
1151
|
-
export{
|
|
1422
|
+
`}highlightParams(t){return t.replace(/:([a-zA-Z_]+)/g,'<span class="param">:$1</span>')}};async function m(){let o=process.argv[2]||process.cwd(),t=process.argv[3]||c.join(o,"rails-map.html");await new n(o).generate({title:"Rails Application Map",outputPath:t});}var p=import.meta.url===`file://${process.argv[1]}`;p&&m().catch(console.error);
|
|
1423
|
+
export{n as a};
|
|
@@ -2208,7 +2208,7 @@ var P=class{graphqlOps=[];apiCalls=[];components=[];generatePageMapHtml(s,n){let
|
|
|
2208
2208
|
visited.add(comp.name);
|
|
2209
2209
|
|
|
2210
2210
|
// Check hooks for GraphQL queries (only match "Query: X" or "Mutation: X" format)
|
|
2211
|
-
if (comp.hooks && comp.hooks.some(h =>
|
|
2211
|
+
if (comp.hooks && comp.hooks.some(h =>
|
|
2212
2212
|
h.startsWith('Query: ') || h.startsWith('Mutation: ') || h.startsWith('Subscription: ')
|
|
2213
2213
|
)) {
|
|
2214
2214
|
return true;
|
|
@@ -2294,9 +2294,9 @@ var P=class{graphqlOps=[];apiCalls=[];components=[];generatePageMapHtml(s,n){let
|
|
|
2294
2294
|
]);
|
|
2295
2295
|
|
|
2296
2296
|
// Debug: log pagesWithGraphQL count
|
|
2297
|
-
console.log('\u{1F4CA} GraphQL Stats: totalComponents=' + components.length +
|
|
2297
|
+
console.log('\u{1F4CA} GraphQL Stats: totalComponents=' + components.length +
|
|
2298
2298
|
', componentsWithGraphQL=' + components.filter(c => c.hooks && c.hooks.some(h => h.startsWith('Query: ') || h.startsWith('Mutation: '))).length +
|
|
2299
|
-
', pagesWithGraphQL=' + pagesWithGraphQL.size +
|
|
2299
|
+
', pagesWithGraphQL=' + pagesWithGraphQL.size +
|
|
2300
2300
|
', totalPages=' + pages.length);
|
|
2301
2301
|
|
|
2302
2302
|
const pagesWithRestApi = new Set(pages.filter(p => {
|
package/dist/cli.js
CHANGED
|
@@ -1,23 +1,25 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {a,b}from'./chunk-
|
|
2
|
+
import {a,b}from'./chunk-LDX6WPHR.js';import'./chunk-P7MX3M5U.js';import'./chunk-VV3A3UE3.js';import'./chunk-TNUKDIO7.js';import'./chunk-HPBPEGHS.js';import'./chunk-SMN6XFMS.js';import'./chunk-H7VVRHQZ.js';import {Command}from'commander';import s from'chalk';import*as c from'path';import*as n from'fs/promises';import*as v from'os';function P(){let e=v.tmpdir();return c.join(e,`repomap-${Date.now()}`)}function C(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);}var d=new Command;d.name("repomap").description("Interactive documentation generator for code repositories").version("0.6.0");async function x(e){let t=c.basename(e),a=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");a=g.includes("gem 'rails'")||g.includes('gem "rails"');}catch{}let r=c.join(e,"package.json"),i=false,l=false,m={};try{let g=JSON.parse(await n.readFile(r,"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":a&&(y="rails"),!a&&!i&&!l?null:{name:t,displayName:t,description:a&&i?"Rails + React application":a?"Rails application":"",path:e,branch:"main",type:y,analyzers:h,settings:m}}async function E(e){let t=await x(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 $(e,t){let a=e?[e]:["repomap.config.ts","repomap.config.js","repomap.config.mjs"];for(let o of a){let p=c.resolve(t,o);try{await n.access(p);let r=await import(p);return r.config||r.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 a$1=process.cwd(),o=await
|
|
5
|
-
`)),
|
|
6
|
-
|
|
4
|
+
`));try{let a$1=process.cwd(),o=await $(e.config,a$1);e.temp&&(o.outputDir=P(),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(r=>r.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 F(p,o);else {let r=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(r,null,2)),t||console.log(s.gray(` \u2192 ${i}`));}if(e.static&&await N(o,r,t),!t)S(r);else {let i=r.repositories.reduce((l,m)=>l+m.summary.totalPages,0);console.log(`\u2705 Generated: ${i} pages, ${r.repositories.length} repos`);}}}catch(a){console.error(t?`Error: ${a.message}`:s.red("Error:"),a.message),process.exit(1);}});async function N(e,t,a){let{PageMapGenerator:o}=await import('./page-map-generator-2XQB7RWO.js'),{detectEnvironments:p}=await import('./env-detector-BIWJ7OYF.js'),r=e.outputDir;await n.mkdir(r,{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-3HNUFTQV.js');m=await f(i);}let y=new o().generatePageMapHtml(t,{envResult:l,railsAnalysis:m,staticMode:true});if(await n.writeFile(c.join(r,"index.html"),y),a||console.log(s.gray(` \u2192 ${c.join(r,"index.html")}`)),m){let{RailsMapGenerator:f}=await import('./rails-map-generator-77ATUFMP.js'),D=new f().generateFromResult(m);await n.writeFile(c.join(r,"rails-map.html"),D),a||console.log(s.gray(` \u2192 ${c.join(r,"rails-map.html")}`));}let g=["common.css","page-map.css","docs.css","rails-map.css"],w=c.join(r,"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{}a||console.log(s.green(`
|
|
7
|
+
\u2705 Static site generated: ${r}`));}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(`
|
|
7
8
|
\u{1F310} Repomap
|
|
8
|
-
`));try{let t=e.path||process.cwd(),a=await
|
|
9
|
+
`));try{let t=e.path||process.cwd(),a=await $(e.config,t);e.temp&&(a.outputDir=P(),console.log(s.cyan(`\u{1F4C1} Using temp directory: ${a.outputDir}
|
|
10
|
+
`)),C(a.outputDir)),e.output&&(a.outputDir=c.resolve(e.output)),await new b(a,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 x(process.cwd()),p=o?.name||"my-project",r=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";
|
|
9
11
|
|
|
10
12
|
export const config: DocGeneratorConfig = {
|
|
11
13
|
outputDir: "./.repomap",
|
|
12
14
|
site: {
|
|
13
|
-
title: "${
|
|
15
|
+
title: "${p} Documentation",
|
|
14
16
|
description: "Auto-generated documentation",
|
|
15
17
|
baseUrl: "/docs",
|
|
16
18
|
},
|
|
17
19
|
repositories: [
|
|
18
20
|
{
|
|
19
|
-
name: "${
|
|
20
|
-
displayName: "${
|
|
21
|
+
name: "${p}",
|
|
22
|
+
displayName: "${p}",
|
|
21
23
|
description: "Main repository",
|
|
22
24
|
path: ".",
|
|
23
25
|
branch: "main",
|
|
@@ -26,7 +28,7 @@ export const config: DocGeneratorConfig = {
|
|
|
26
28
|
settings: {
|
|
27
29
|
pagesDir: "${i}",
|
|
28
30
|
featuresDir: "${l}",
|
|
29
|
-
componentsDir: "${
|
|
31
|
+
componentsDir: "${m}",
|
|
30
32
|
},
|
|
31
33
|
},
|
|
32
34
|
],
|
|
@@ -54,10 +56,10 @@ export default config;
|
|
|
54
56
|
`;await n.writeFile(t,h,"utf-8"),console.log(s.green(`\u2705 Created ${t}`)),console.log(s.gray(`
|
|
55
57
|
Run 'npx repomap serve' to start the documentation server.`));}catch(a){console.error(s.red("Failed to create config:"),a.message);}});d.command("rails").description("Analyze a Rails application and generate interactive map").option("--path <path>","Path to Rails application").option("-o, --output <path>","Output HTML file path").action(async e=>{console.log(s.blue.bold(`
|
|
56
58
|
\u{1F6E4}\uFE0F Repomap Rails
|
|
57
|
-
`));try{let t=e.path||process.cwd();try{await n.access(
|
|
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:a}=await import('./rails-map-generator-77ATUFMP.js'),o=e.output||c.join(t,"rails-map.html");await new a(t).generate({title:`${c.basename(t)} - Rails Map`,outputPath:o}),console.log(s.green(`\u2705 Rails map generated: ${o}`));let{exec:r}=await import('child_process');r(`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(`
|
|
58
60
|
\u{1F4CA} Documentation Diff
|
|
59
|
-
`));try{let t=process.cwd(),a$1=await
|
|
60
|
-
\u{1F504} Change detected: ${i.filename}`)),await e.generate();},t.watch.debounce));}}function
|
|
61
|
+
`));try{let t=process.cwd(),a$1=await $(e.config,t),o=c.join(a$1.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 r=JSON.parse(await n.readFile(o,"utf-8")),l=await new a(a$1).generate();k(r,l);}catch(t){console.error(s.red("Failed to generate diff:"),t.message);}});async function F(e,t){await e.generate();let a=t.repositories.map(o=>o.path);for(let o of a){let p=n.watch(o,{recursive:true}),r=null;for await(let i of p)i.filename&&(i.filename.endsWith(".ts")||i.filename.endsWith(".tsx"))&&(r&&clearTimeout(r),r=setTimeout(async()=>{console.log(s.yellow(`
|
|
62
|
+
\u{1F504} Change detected: ${i.filename}`)),await e.generate();},t.watch.debounce));}}function S(e){console.log(s.green.bold(`
|
|
61
63
|
\u2705 Complete
|
|
62
|
-
`));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
|
|
63
|
-
`));for(let a of t.repositories){let o=e.repositories.find(l=>l.name===a.name);if(!o){console.log(s.green(` + New repository: ${a.displayName}`));continue}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 k(e,t){console.log(s.cyan(`Changes detected:
|
|
65
|
+
`));for(let a of t.repositories){let o=e.repositories.find(l=>l.name===a.name);if(!o){console.log(s.green(` + New repository: ${a.displayName}`));continue}let p=a.summary.totalPages-o.summary.totalPages,r=a.summary.totalComponents-o.summary.totalComponents,i=a.summary.totalGraphQLOperations-o.summary.totalGraphQLOperations;(p!==0||r!==0||i!==0)&&(console.log(s.yellow(` ~ ${a.displayName}:`)),p!==0&&console.log(` Pages: ${p>0?"+":""}${p}`),r!==0&&console.log(` Components: ${r>0?"+":""}${r}`),i!==0&&console.log(` GraphQL Ops: ${i>0?"+":""}${i}`));}}d.parse();
|
|
@@ -241,6 +241,8 @@ header h1 {
|
|
|
241
241
|
.main-panel {
|
|
242
242
|
overflow-y: auto;
|
|
243
243
|
padding: 20px;
|
|
244
|
+
display: flex;
|
|
245
|
+
flex-direction: column;
|
|
244
246
|
}
|
|
245
247
|
|
|
246
248
|
.panel-header {
|
|
@@ -572,6 +574,10 @@ header h1 {
|
|
|
572
574
|
position: relative;
|
|
573
575
|
min-height: 300px;
|
|
574
576
|
cursor: grab;
|
|
577
|
+
flex: 1;
|
|
578
|
+
display: flex;
|
|
579
|
+
align-items: center;
|
|
580
|
+
justify-content: center;
|
|
575
581
|
}
|
|
576
582
|
|
|
577
583
|
.mermaid-container:active {
|
package/dist/generators/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export{b as MarkdownGenerator,a as MermaidGenerator}from'../chunk-HPBPEGHS.js';export{a as PageMapGenerator}from'../chunk-
|
|
1
|
+
export{b as MarkdownGenerator,a as MermaidGenerator}from'../chunk-HPBPEGHS.js';export{a as PageMapGenerator}from'../chunk-SMN6XFMS.js';
|
package/dist/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import'./chunk-6F4PWJZI.js';export{a as DocGeneratorEngine,b as DocServer}from'./chunk-
|
|
1
|
+
import'./chunk-6F4PWJZI.js';export{a as DocGeneratorEngine,b as DocServer}from'./chunk-LDX6WPHR.js';import'./chunk-P7MX3M5U.js';import'./chunk-VV3A3UE3.js';export{a as BaseAnalyzer,A as DataFlowAnalyzer,z as GraphQLAnalyzer,y as PagesAnalyzer}from'./chunk-TNUKDIO7.js';export{b as MarkdownGenerator,a as MermaidGenerator}from'./chunk-HPBPEGHS.js';export{a as PageMapGenerator}from'./chunk-SMN6XFMS.js';import'./chunk-H7VVRHQZ.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export{a as PageMapGenerator}from'./chunk-SMN6XFMS.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export{a as RailsMapGenerator}from'./chunk-P7MX3M5U.js';import'./chunk-H7VVRHQZ.js';
|
package/dist/server/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export{b as DocServer}from'../chunk-
|
|
1
|
+
export{b as DocServer}from'../chunk-LDX6WPHR.js';import'../chunk-P7MX3M5U.js';import'../chunk-VV3A3UE3.js';import'../chunk-TNUKDIO7.js';import'../chunk-HPBPEGHS.js';import'../chunk-SMN6XFMS.js';import'../chunk-H7VVRHQZ.js';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wtdlee/repomap",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"description": "Interactive documentation generator for code repositories - visualize pages, components, GraphQL operations, and data flows",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -75,8 +75,6 @@
|
|
|
75
75
|
"node": ">=18.0.0"
|
|
76
76
|
},
|
|
77
77
|
"dependencies": {
|
|
78
|
-
"@babel/parser": "^7.23.0",
|
|
79
|
-
"@babel/traverse": "^7.23.0",
|
|
80
78
|
"@swc/core": "^1.15.3",
|
|
81
79
|
"chalk": "^5.3.0",
|
|
82
80
|
"commander": "^14.0.2",
|
|
@@ -89,14 +87,12 @@
|
|
|
89
87
|
"simple-git": "^3.21.0",
|
|
90
88
|
"socket.io": "^4.7.2",
|
|
91
89
|
"tree-sitter-wasms": "^0.1.13",
|
|
92
|
-
"ts-morph": "^27.0.2",
|
|
93
90
|
"web-tree-sitter": "^0.25.10",
|
|
94
91
|
"yaml": "^2.3.4"
|
|
95
92
|
},
|
|
96
93
|
"devDependencies": {
|
|
97
94
|
"@changesets/cli": "^2.27.0",
|
|
98
95
|
"@eslint/js": "^9.39.1",
|
|
99
|
-
"@types/babel__traverse": "^7.20.4",
|
|
100
96
|
"@types/express": "^5.0.6",
|
|
101
97
|
"@types/node": "^25.0.1",
|
|
102
98
|
"eslint": "^9.39.1",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export{a as PageMapGenerator}from'./chunk-GCIRJGW3.js';
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export{a as RailsMapGenerator}from'./chunk-OQAXO3X2.js';import'./chunk-H7VVRHQZ.js';
|