@wtdlee/repomap 0.3.0 → 0.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (72) hide show
  1. package/dist/analyzers/index.d.ts +69 -5
  2. package/dist/analyzers/index.js +1 -5
  3. package/dist/{server/doc-server.js → chunk-4K4MGTPV.js} +41 -329
  4. package/dist/chunk-6F4PWJZI.js +0 -0
  5. package/dist/chunk-J2CM7T7U.js +1 -0
  6. package/dist/{generators/page-map-generator.js → chunk-MOEA75XK.js} +278 -503
  7. package/dist/{generators/rails-map-generator.js → chunk-SL2GMDBN.js} +48 -129
  8. package/dist/chunk-UJT5KTVK.js +36 -0
  9. package/dist/chunk-VV3A3UE3.js +1 -0
  10. package/dist/chunk-XWZH2RDG.js +19 -0
  11. package/dist/cli.d.ts +0 -1
  12. package/dist/cli.js +29 -499
  13. package/dist/dataflow-analyzer-BfAiqVUp.d.ts +180 -0
  14. package/dist/env-detector-BIWJ7OYF.js +1 -0
  15. package/dist/generators/assets/common.css +564 -23
  16. package/dist/generators/index.d.ts +431 -3
  17. package/dist/generators/index.js +1 -3
  18. package/dist/index.d.ts +53 -10
  19. package/dist/index.js +1 -11
  20. package/dist/page-map-generator-XNZ4TDJT.js +1 -0
  21. package/dist/rails-TJCDGBBF.js +1 -0
  22. package/dist/rails-map-generator-JL5PKHYP.js +1 -0
  23. package/dist/server/index.d.ts +33 -1
  24. package/dist/server/index.js +1 -1
  25. package/dist/types.d.ts +39 -37
  26. package/dist/types.js +1 -5
  27. package/package.json +4 -2
  28. package/dist/analyzers/base-analyzer.d.ts +0 -45
  29. package/dist/analyzers/base-analyzer.js +0 -47
  30. package/dist/analyzers/dataflow-analyzer.d.ts +0 -29
  31. package/dist/analyzers/dataflow-analyzer.js +0 -425
  32. package/dist/analyzers/graphql-analyzer.d.ts +0 -22
  33. package/dist/analyzers/graphql-analyzer.js +0 -386
  34. package/dist/analyzers/pages-analyzer.d.ts +0 -84
  35. package/dist/analyzers/pages-analyzer.js +0 -1695
  36. package/dist/analyzers/rails/index.d.ts +0 -46
  37. package/dist/analyzers/rails/index.js +0 -145
  38. package/dist/analyzers/rails/rails-controller-analyzer.d.ts +0 -82
  39. package/dist/analyzers/rails/rails-controller-analyzer.js +0 -478
  40. package/dist/analyzers/rails/rails-grpc-analyzer.d.ts +0 -44
  41. package/dist/analyzers/rails/rails-grpc-analyzer.js +0 -262
  42. package/dist/analyzers/rails/rails-model-analyzer.d.ts +0 -88
  43. package/dist/analyzers/rails/rails-model-analyzer.js +0 -493
  44. package/dist/analyzers/rails/rails-react-analyzer.d.ts +0 -41
  45. package/dist/analyzers/rails/rails-react-analyzer.js +0 -529
  46. package/dist/analyzers/rails/rails-routes-analyzer.d.ts +0 -62
  47. package/dist/analyzers/rails/rails-routes-analyzer.js +0 -540
  48. package/dist/analyzers/rails/rails-view-analyzer.d.ts +0 -49
  49. package/dist/analyzers/rails/rails-view-analyzer.js +0 -386
  50. package/dist/analyzers/rails/ruby-parser.d.ts +0 -63
  51. package/dist/analyzers/rails/ruby-parser.js +0 -212
  52. package/dist/analyzers/rest-api-analyzer.d.ts +0 -65
  53. package/dist/analyzers/rest-api-analyzer.js +0 -479
  54. package/dist/core/cache.d.ts +0 -47
  55. package/dist/core/cache.js +0 -151
  56. package/dist/core/engine.d.ts +0 -46
  57. package/dist/core/engine.js +0 -319
  58. package/dist/core/index.d.ts +0 -2
  59. package/dist/core/index.js +0 -2
  60. package/dist/generators/markdown-generator.d.ts +0 -25
  61. package/dist/generators/markdown-generator.js +0 -782
  62. package/dist/generators/mermaid-generator.d.ts +0 -35
  63. package/dist/generators/mermaid-generator.js +0 -364
  64. package/dist/generators/page-map-generator.d.ts +0 -22
  65. package/dist/generators/rails-map-generator.d.ts +0 -21
  66. package/dist/server/doc-server.d.ts +0 -30
  67. package/dist/utils/env-detector.d.ts +0 -31
  68. package/dist/utils/env-detector.js +0 -188
  69. package/dist/utils/parallel.d.ts +0 -23
  70. package/dist/utils/parallel.js +0 -70
  71. package/dist/utils/port.d.ts +0 -15
  72. package/dist/utils/port.js +0 -41
@@ -1,5 +1,69 @@
1
- export * from './base-analyzer.js';
2
- export * from './pages-analyzer.js';
3
- export * from './graphql-analyzer.js';
4
- export * from './dataflow-analyzer.js';
5
- export * from './rest-api-analyzer.js';
1
+ import { B as BaseAnalyzer } from '../dataflow-analyzer-BfAiqVUp.js';
2
+ export { D as DataFlowAnalyzer, G as GraphQLAnalyzer, P as PagesAnalyzer } from '../dataflow-analyzer-BfAiqVUp.js';
3
+ import { RepositoryConfig, AnalysisResult } from '../types.js';
4
+
5
+ /**
6
+ * Analyzer for REST API calls (fetch, axios, useSWR, etc.)
7
+ * REST API呼び出しの分析器
8
+ */
9
+ declare class RestApiAnalyzer extends BaseAnalyzer {
10
+ private project;
11
+ private apiCallCounter;
12
+ constructor(config: RepositoryConfig);
13
+ getName(): string;
14
+ analyze(): Promise<Partial<AnalysisResult>>;
15
+ /**
16
+ * Find fetch() calls
17
+ */
18
+ private findFetchCalls;
19
+ /**
20
+ * Find axios calls (axios.get, axios.post, etc.)
21
+ */
22
+ private findAxiosCalls;
23
+ /**
24
+ * Find useSWR calls
25
+ */
26
+ private findSwrCalls;
27
+ /**
28
+ * Extract API call info from fetch()
29
+ */
30
+ private extractFetchCall;
31
+ /**
32
+ * Extract API call info from axios.method()
33
+ */
34
+ private extractAxiosCall;
35
+ /**
36
+ * Extract API call info from axios() direct call
37
+ */
38
+ private extractAxiosDirectCall;
39
+ /**
40
+ * Extract API call info from useSWR()
41
+ */
42
+ private extractSwrCall;
43
+ /**
44
+ * Get the name of the containing function/component
45
+ */
46
+ private getContainingFunctionName;
47
+ /**
48
+ * Extract URL from argument (handles literals, variables, function calls)
49
+ */
50
+ private extractUrlFromArg;
51
+ /**
52
+ * Clean string literal (remove quotes)
53
+ */
54
+ private cleanStringLiteral;
55
+ /**
56
+ * Check if URL looks like an API endpoint
57
+ */
58
+ private isApiUrl;
59
+ /**
60
+ * Categorize API by URL pattern
61
+ */
62
+ private categorizeApi;
63
+ /**
64
+ * Normalize HTTP method
65
+ */
66
+ private normalizeMethod;
67
+ }
68
+
69
+ export { BaseAnalyzer, RestApiAnalyzer };
@@ -1,5 +1 @@
1
- export * from './base-analyzer.js';
2
- export * from './pages-analyzer.js';
3
- export * from './graphql-analyzer.js';
4
- export * from './dataflow-analyzer.js';
5
- export * from './rest-api-analyzer.js';
1
+ export{a as BaseAnalyzer,d as DataFlowAnalyzer,c as GraphQLAnalyzer,b as PagesAnalyzer,e as RestApiAnalyzer}from'../chunk-J2CM7T7U.js';
@@ -1,201 +1,10 @@
1
- import express from 'express';
2
- import { Server } from 'socket.io';
3
- import * as http from 'http';
4
- import * as fs from 'fs/promises';
5
- import * as path from 'path';
6
- import { marked } from 'marked';
7
- import { DocGeneratorEngine } from '../core/engine.js';
8
- import { PageMapGenerator } from '../generators/page-map-generator.js';
9
- import { RailsMapGenerator } from '../generators/rails-map-generator.js';
10
- import { detectEnvironments } from '../utils/env-detector.js';
11
- import { analyzeRailsApp } from '../analyzers/rails/index.js';
12
- import { findAvailablePort } from '../utils/port.js';
13
- /**
14
- * Documentation server with live reload
15
- * ライブリロード機能付きドキュメントサーバー
16
- */
17
- export class DocServer {
18
- config;
19
- port;
20
- app;
21
- server;
22
- io;
23
- engine;
24
- currentReport = null;
25
- envResult = null;
26
- railsAnalysis = null;
27
- constructor(config, port = 3030, options) {
28
- this.config = config;
29
- this.port = port;
30
- this.app = express();
31
- this.server = http.createServer(this.app);
32
- this.io = new Server(this.server);
33
- this.engine = new DocGeneratorEngine(config, { noCache: options?.noCache });
34
- this.setupRoutes();
35
- this.setupSocketIO();
36
- }
37
- setupRoutes() {
38
- // Serve static assets
39
- this.app.use('/assets', express.static(path.join(this.config.outputDir, 'assets')));
40
- // Serve CSS files from generators/assets
41
- const cssFiles = ['common.css', 'page-map.css', 'docs.css', 'rails-map.css'];
42
- cssFiles.forEach((file) => {
43
- this.app.get(`/${file}`, async (req, res) => {
44
- try {
45
- const cssPath = new URL(`../generators/assets/${file}`, import.meta.url);
46
- const css = await fs.readFile(cssPath, 'utf-8');
47
- res.type('text/css').send(css);
48
- }
49
- catch {
50
- res.status(404).send('CSS not found');
51
- }
52
- });
53
- });
54
- // Main page - redirect to page-map
55
- this.app.get('/', (req, res) => {
56
- res.redirect('/page-map');
57
- });
58
- // Interactive page map (main view) - now with environment awareness
59
- this.app.get('/page-map', (req, res) => {
60
- if (!this.currentReport) {
61
- res.status(503).send('Documentation not ready yet');
62
- return;
63
- }
64
- const generator = new PageMapGenerator();
65
- res.send(generator.generatePageMapHtml(this.currentReport, {
66
- envResult: this.envResult,
67
- railsAnalysis: this.railsAnalysis,
68
- }));
69
- });
70
- // Rails map (standalone view)
71
- this.app.get('/rails-map', (req, res) => {
72
- if (!this.railsAnalysis) {
73
- res.status(404).send('No Rails environment detected');
74
- return;
75
- }
76
- const generator = new RailsMapGenerator();
77
- res.send(generator.generateFromResult(this.railsAnalysis));
78
- });
79
- // Markdown pages - index
80
- this.app.get('/docs', async (req, res) => {
81
- res.send(await this.renderPage('index'));
82
- });
83
- // Markdown pages - specific path
84
- this.app.get('/docs/*', async (req, res) => {
85
- const pagePath = req.params[0] || 'index';
86
- res.send(await this.renderPage(pagePath));
87
- });
88
- // API endpoints
89
- this.app.get('/api/report', (req, res) => {
90
- res.json(this.currentReport);
91
- });
92
- // Environment detection result
93
- this.app.get('/api/env', (req, res) => {
94
- res.json(this.envResult);
95
- });
96
- // Rails analysis result
97
- this.app.get('/api/rails', (req, res) => {
98
- if (this.railsAnalysis) {
99
- res.json(this.railsAnalysis);
100
- }
101
- else {
102
- res.status(404).json({ error: 'No Rails analysis available' });
103
- }
104
- });
105
- this.app.get('/api/diagram/:name', (req, res) => {
106
- const diagram = this.currentReport?.diagrams.find((d) => d.title.toLowerCase().replace(/\s+/g, '-') === req.params.name);
107
- if (diagram) {
108
- res.json(diagram);
109
- }
110
- else {
111
- res.status(404).json({ error: 'Diagram not found' });
112
- }
113
- });
114
- // Regenerate endpoint
115
- this.app.post('/api/regenerate', async (req, res) => {
116
- try {
117
- await this.regenerate();
118
- res.json({ success: true });
119
- }
120
- catch (error) {
121
- res.status(500).json({ error: error.message });
122
- }
123
- });
124
- }
125
- setupSocketIO() {
126
- this.io.on('connection', (socket) => {
127
- console.log('Client connected');
128
- socket.on('disconnect', () => {
129
- console.log('Client disconnected');
130
- });
131
- });
132
- }
133
- async renderPage(pagePath) {
134
- // Remove .md extension if present
135
- const cleanPath = pagePath.replace(/\.md$/, '');
136
- const mdPath = path.join(this.config.outputDir, `${cleanPath}.md`);
137
- let content = '';
138
- try {
139
- const markdown = await fs.readFile(mdPath, 'utf-8');
140
- // Parse markdown to HTML
141
- let html = await marked.parse(markdown);
142
- // Convert mermaid code blocks to mermaid divs
143
- // marked renders: <pre><code class="language-mermaid">...</code></pre>
144
- // mermaid expects: <div class="mermaid">...</div>
145
- html = html.replace(/<pre><code class="language-mermaid">([\s\S]*?)<\/code><\/pre>/g, '<div class="mermaid">$1</div>');
146
- // Wrap tables for horizontal scroll
147
- html = html.replace(/<table>/g, '<div class="table-wrapper"><table>');
148
- html = html.replace(/<\/table>/g, '</table></div>');
149
- content = html;
150
- }
151
- catch (e) {
152
- console.error(`Failed to render page: ${mdPath}`, e);
153
- content = `<h1>Page not found</h1><p>Path: ${cleanPath}</p>`;
154
- }
155
- return this.getHtmlTemplate(content);
156
- }
157
- getGraphQLData() {
158
- if (!this.currentReport)
159
- return '[]';
160
- const ops = [];
161
- for (const repo of this.currentReport.repositories) {
162
- for (const op of repo.analysis?.graphqlOperations || []) {
163
- ops.push({
164
- name: op.name,
165
- type: op.type,
166
- returnType: op.returnType,
167
- variables: op.variables,
168
- fields: op.fields,
169
- usedIn: op.usedIn,
170
- });
171
- }
172
- }
173
- return JSON.stringify(ops);
174
- }
175
- getApiCallsData() {
176
- if (!this.currentReport)
177
- return '[]';
178
- const calls = [];
179
- for (const repo of this.currentReport.repositories) {
180
- for (const call of repo.analysis?.apiCalls || []) {
181
- calls.push({
182
- id: call.id,
183
- method: call.method,
184
- url: call.url,
185
- callType: call.callType,
186
- filePath: call.filePath,
187
- line: call.line,
188
- containingFunction: call.containingFunction,
189
- requiresAuth: call.requiresAuth,
190
- });
191
- }
192
- }
193
- return JSON.stringify(calls);
194
- }
195
- getHtmlTemplate(content) {
196
- const graphqlData = this.getGraphQLData();
197
- const apiCallsData = this.getApiCallsData();
198
- return `<!DOCTYPE html>
1
+ import {a as a$2}from'./chunk-SL2GMDBN.js';import {a as a$3}from'./chunk-VV3A3UE3.js';import {e,d,c,b as b$2}from'./chunk-J2CM7T7U.js';import {a,b as b$1}from'./chunk-XWZH2RDG.js';import {a as a$1}from'./chunk-MOEA75XK.js';import {k}from'./chunk-UJT5KTVK.js';import {simpleGit}from'simple-git';import*as m from'fs/promises';import*as l from'path';import H from'fast-glob';import*as w from'crypto';import F from'express';import {Server}from'socket.io';import*as z from'http';import {marked}from'marked';import*as A from'net';var C="1.1",x=class{cacheDir;manifest;manifestPath;dirty=false;constructor(t){this.cacheDir=l.join(t,".repomap-cache"),this.manifestPath=l.join(this.cacheDir,"manifest.json"),this.manifest={version:C,entries:{}};}async init(){try{await m.mkdir(this.cacheDir,{recursive:!0});}catch(t){console.warn(` Warning: Could not create cache directory: ${t.message}`);return}try{let t=await m.readFile(this.manifestPath,"utf-8"),a=JSON.parse(t);a.version===C?this.manifest=a:(console.log(" Cache version mismatch, clearing cache..."),await this.clear());}catch{}}async computeFileHash(t){try{let a=await m.readFile(t,"utf-8");return w.createHash("md5").update(a).digest("hex")}catch{return ""}}async computeFilesHash(t){let a=[...t].sort(),e=50,n;a.length<=e*2?n=a:n=[...a.slice(0,e),...a.slice(-e)];let o=await Promise.all(n.map(i=>this.computeFileHash(i))),s=w.createHash("md5").update(String(a.length)).digest("hex");return w.createHash("md5").update(o.join("")+s).digest("hex")}get(t,a){let e=this.manifest.entries[t];return e&&e.hash===a?e.data:null}set(t,a,e){this.manifest.entries[t]={hash:a,timestamp:Date.now(),data:e},this.dirty=true;}async save(){if(this.dirty)try{await m.mkdir(this.cacheDir,{recursive:!0}),await m.writeFile(this.manifestPath,JSON.stringify(this.manifest,null,2)),this.dirty=!1;}catch(t){console.warn(" Warning: Failed to save cache:",t.message);}}async clear(){this.manifest={version:C,entries:{}},this.dirty=true;try{await m.rm(this.cacheDir,{recursive:!0,force:!0}),await m.mkdir(this.cacheDir,{recursive:!0});}catch{}}getStats(){let t=Object.keys(this.manifest.entries).length,a=JSON.stringify(this.manifest).length;return {entries:t,size:a>1024*1024?`${(a/1024/1024).toFixed(1)}MB`:`${(a/1024).toFixed(1)}KB`}}};var b=class{config;mermaidGenerator;markdownGenerator;noCache;constructor(t,a$1){this.config=t,this.mermaidGenerator=new a,this.markdownGenerator=new b$1,this.noCache=a$1?.noCache??false;}async generate(){console.log(`\u{1F680} Starting documentation generation...
2
+ `);let t=[];for(let i of this.config.repositories)try{console.log(`
3
+ \u{1F4E6} Analyzing ${i.displayName}...`);let r=await this.analyzeRepository(i);t.push(r),console.log(`\u2705 Completed ${i.displayName}`);}catch(r){console.error(`\u274C Failed to analyze ${i.name}:`,r.message);}console.log(`
4
+ \u{1F517} Running cross-repository analysis...`);let a=this.analyzeCrossRepo(t);console.log(`
5
+ \u{1F4CA} Generating diagrams...`);let e=t.map(i=>i.analysis),n=this.extractCrossRepoLinks(e),o=this.mermaidGenerator.generateAll(e,n),s={generatedAt:new Date().toISOString(),repositories:t,crossRepoAnalysis:a,diagrams:o};return console.log(`
6
+ \u{1F4DD} Writing documentation...`),await this.writeDocumentation(s),console.log(`
7
+ \u2728 Documentation generation complete!`),console.log(`\u{1F4C1} Output: ${this.config.outputDir}`),s}async analyzeRepository(t){let a=new x(t.path);await a.init();let{version:e,commitHash:n}=await this.getRepoInfo(t),o=await H(["**/*.{ts,tsx,graphql}"],{cwd:t.path,ignore:["**/node_modules/**","**/.next/**","**/dist/**","**/build/**"],absolute:true}),s=await a.computeFilesHash(o),i=`analysis_v${e}_${t.name}_${n}`;this.noCache&&console.log(" \u{1F504} Cache disabled, analyzing from scratch...");let r=this.noCache?null:a.get(i,s);if(r){console.log(` \u26A1 Using cached analysis (hash: ${s.slice(0,8)}...)`);let d={totalPages:r.pages.length,totalComponents:r.components.length,totalGraphQLOperations:r.graphqlOperations.length,totalDataFlows:r.dataFlows.length,authRequiredPages:r.pages.filter(q=>q.authentication.required).length,publicPages:r.pages.filter(q=>!q.authentication.required).length};return {name:t.name,displayName:t.displayName,version:e,commitHash:n,analysis:r,summary:d}}let p=t.analyzers.map(d=>this.createAnalyzer(d,t)).filter(d=>d!==null);console.log(` Running ${p.length} analyzers in parallel...`);let h=Date.now(),f=await Promise.all(p.map(d=>d.analyze()));console.log(` Analysis completed in ${((Date.now()-h)/1e3).toFixed(1)}s`);let c=this.mergeAnalysisResults(f,t.name,e,n);a.set(i,s,c),await a.save(),console.log(` \u{1F4BE} Analysis cached (hash: ${s.slice(0,8)}...)`);let B={totalPages:c.pages.length,totalComponents:c.components.length,totalGraphQLOperations:c.graphqlOperations.length,totalDataFlows:c.dataFlows.length,authRequiredPages:c.pages.filter(d=>d.authentication.required).length,publicPages:c.pages.filter(d=>!d.authentication.required).length};return {name:t.name,displayName:t.displayName,version:e,commitHash:n,analysis:c,summary:B}}async getRepoInfo(t){try{let n=(await simpleGit(t.path).log({n:1})).latest?.hash||"unknown",o="unknown";try{let s=l.join(t.path,"package.json");o=JSON.parse(await m.readFile(s,"utf-8")).version||"unknown";}catch{}return {version:o,commitHash:n}}catch{return {version:"unknown",commitHash:"unknown"}}}createAnalyzer(t,a){switch(t){case "pages":if(a.type==="nextjs"||a.type==="rails"||a.type==="generic")return new b$2(a);break;case "graphql":return new c(a);case "dataflow":case "components":return new d(a);case "rest-api":case "api":return new e(a)}return null}mergeAnalysisResults(t,a,e,n){let o={repository:a,timestamp:new Date().toISOString(),version:e,commitHash:n,pages:[],graphqlOperations:[],apiCalls:[],components:[],dataFlows:[],apiEndpoints:[],models:[],crossRepoLinks:[]};for(let s of t)s.pages&&o.pages.push(...s.pages),s.graphqlOperations&&o.graphqlOperations.push(...s.graphqlOperations),s.apiCalls&&o.apiCalls.push(...s.apiCalls),s.components&&o.components.push(...s.components),s.dataFlows&&o.dataFlows.push(...s.dataFlows),s.apiEndpoints&&o.apiEndpoints.push(...s.apiEndpoints),s.models&&o.models.push(...s.models),s.crossRepoLinks&&o.crossRepoLinks.push(...s.crossRepoLinks);return o}analyzeCrossRepo(t){let a=[],e=[],n=[],o=[],s=new Map;for(let p of t)for(let h of p.analysis.graphqlOperations){let f=s.get(h.name)||[];f.push(p.name),s.set(h.name,f);}for(let[p,h]of s)h.length>1&&a.push(p);let i=t.filter(p=>p.analysis.pages.length>0),r=t.filter(p=>p.analysis.apiEndpoints.length>0);for(let p of i)for(let h of r)for(let f of h.analysis.apiEndpoints)e.push({frontend:p.name,backend:h.name,endpoint:f.path,operations:p.analysis.graphqlOperations.filter(c=>c.usedIn.length>0).map(c=>c.name)});return {sharedTypes:a,apiConnections:e,navigationFlows:n,dataFlowAcrossRepos:o}}extractCrossRepoLinks(t){let a=[],e=new Map;for(let n of t)for(let o of n.graphqlOperations){let s=e.get(o.name)||[];s.push(n),e.set(o.name,s);}for(let[n,o]of e)o.length>1&&a.push({sourceRepo:o[0].repository,sourcePath:`graphql/${n}`,targetRepo:o[1].repository,targetPath:`graphql/${n}`,linkType:"graphql-operation",description:`Shared GraphQL operation: ${n}`});return a}async writeDocumentation(t){let a=this.config.outputDir;await m.mkdir(a,{recursive:true});let e=this.markdownGenerator.generateDocumentation(t);for(let[o,s]of e){let i=l.join(a,o),r=l.dirname(i);await m.mkdir(r,{recursive:true}),await m.writeFile(i,s,"utf-8"),console.log(` \u{1F4C4} ${o}`);}let n=l.join(a,"report.json");await m.writeFile(n,JSON.stringify(t,null,2),"utf-8"),console.log(" \u{1F4CB} report.json");}};function Q(u){return new Promise(t=>{let a=A.createServer();a.once("error",e=>{e.code,t(false);}),a.once("listening",()=>{a.close(),t(true);}),a.listen(u);})}async function O(u,t=10){for(let a=0;a<t;a++){let e=u+a;if(await Q(e))return e}throw new Error(`No available port found between ${u} and ${u+t-1}`)}var I=class{config;port;app;server;io;engine;currentReport=null;envResult=null;railsAnalysis=null;constructor(t,a=3030,e){this.config=t,this.port=a,this.app=F(),this.server=z.createServer(this.app),this.io=new Server(this.server),this.engine=new b(t,{noCache:e?.noCache}),this.setupRoutes(),this.setupSocketIO();}setupRoutes(){this.app.use("/assets",F.static(l.join(this.config.outputDir,"assets")));["common.css","page-map.css","docs.css","rails-map.css"].forEach(e=>{this.app.get(`/${e}`,async(n,o)=>{let s=[l.join(l.dirname(new URL(import.meta.url).pathname),"generators","assets",e),l.join(l.dirname(new URL(import.meta.url).pathname),"..","generators","assets",e),l.join(process.cwd(),"dist","generators","assets",e),l.join(process.cwd(),"src","generators","assets",e)];for(let i of s)try{let r=await m.readFile(i,"utf-8");o.type("text/css").send(r);return}catch{}o.status(404).send("CSS 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 o=new a$1;n.send(o.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 o=new a$2;n.send(o.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 o=e.params.path||"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 o=this.currentReport?.diagrams.find(s=>s.title.toLowerCase().replace(/\s+/g,"-")===e.params.name);o?n.json(o):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(o){n.status(500).json({error:o.message});}});}setupSocketIO(){this.io.on("connection",t=>{console.log("Client connected"),t.on("disconnect",()=>{console.log("Client disconnected");});});}async renderPage(t){let a=t.replace(/\.md$/,""),e=l.join(this.config.outputDir,`${a}.md`),n="";try{let o=await m.readFile(e,"utf-8"),s=await marked.parse(o);s=s.replace(/<pre><code class="language-mermaid">([\s\S]*?)<\/code><\/pre>/g,'<div class="mermaid">$1</div>'),s=s.replace(/<table>/g,'<div class="table-wrapper"><table>'),s=s.replace(/<\/table>/g,"</table></div>"),n=s;}catch(o){console.error(`Failed to render page: ${e}`,o),n=`<h1>Page not found</h1><p>Path: ${a}</p>`;}return this.getHtmlTemplate(n)}getGraphQLData(){if(!this.currentReport)return "[]";let t=[];for(let a of this.currentReport.repositories)for(let e of a.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 a of this.currentReport.repositories)for(let e of a.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 a=this.getGraphQLData(),e=this.getApiCallsData();return `<!DOCTYPE html>
199
8
  <html lang="ja">
200
9
  <head>
201
10
  <meta charset="UTF-8">
@@ -204,8 +13,8 @@ export class DocServer {
204
13
  <script src="https://cdn.jsdelivr.net/npm/mermaid/dist/mermaid.min.js"></script>
205
14
  <script src="/socket.io/socket.io.js"></script>
206
15
  <script>
207
- window.graphqlOps = ${graphqlData};
208
- window.apiCalls = ${apiCallsData};
16
+ window.graphqlOps = ${a};
17
+ window.apiCalls = ${e};
209
18
  // Create multiple lookup maps for different naming conventions
210
19
  window.gqlMap = new Map();
211
20
  window.gqlMapNormalized = new Map();
@@ -273,10 +82,10 @@ export class DocServer {
273
82
  <body>
274
83
  <header class="header">
275
84
  <div style="display:flex;align-items:center;gap:24px">
276
- <h1 style="cursor:pointer" onclick="location.href='/'">📊 ${this.config.repositories[0]?.displayName || this.config.repositories[0]?.name || 'Repository'}</h1>
85
+ <h1 style="cursor:pointer" onclick="location.href='/'">\u{1F4CA} ${this.config.repositories[0]?.displayName||this.config.repositories[0]?.name||"Repository"}</h1>
277
86
  <nav style="display:flex;gap:4px">
278
87
  <a href="/page-map" class="nav-link">Page Map</a>
279
- ${this.railsAnalysis ? '<a href="/rails-map" class="nav-link">Rails Map</a>' : ''}
88
+ ${this.railsAnalysis?'<a href="/rails-map" class="nav-link">Rails Map</a>':""}
280
89
  <a href="/docs" class="nav-link active">Docs</a>
281
90
  <a href="/api/report" class="nav-link" target="_blank">API</a>
282
91
  </nav>
@@ -288,14 +97,12 @@ export class DocServer {
288
97
  <div class="nav-group">
289
98
  <span class="nav-group-title">Documentation</span>
290
99
  <div class="nav-subitems">
291
- ${this.config.repositories
292
- .map((repo) => `
293
- <a href="/docs/repos/${repo.name}/pages">Pages</a>
294
- <a href="/docs/repos/${repo.name}/components">Components</a>
295
- <a href="/docs/repos/${repo.name}/graphql">GraphQL</a>
296
- <a href="/docs/repos/${repo.name}/dataflow">Data Flow</a>
297
- `)
298
- .join('')}
100
+ ${this.config.repositories.map(n=>`
101
+ <a href="/docs/repos/${n.name}/pages">Pages</a>
102
+ <a href="/docs/repos/${n.name}/components">Components</a>
103
+ <a href="/docs/repos/${n.name}/graphql">GraphQL</a>
104
+ <a href="/docs/repos/${n.name}/dataflow">Data Flow</a>
105
+ `).join("")}
299
106
  </div>
300
107
  </div>
301
108
  <div class="nav-group">
@@ -310,7 +117,7 @@ export class DocServer {
310
117
  </aside>
311
118
  <div class="content-area">
312
119
  <div class="content">
313
- ${content}
120
+ ${t}
314
121
  </div>
315
122
  </div>
316
123
  </div>
@@ -321,10 +128,10 @@ export class DocServer {
321
128
  <div class="detail-modal-content">
322
129
  <div class="detail-modal-header">
323
130
  <div style="display:flex;align-items:center;gap:8px">
324
- <button id="modalBackBtn" onclick="modalBack()" style="display:none;background:#f1f5f9;border:1px solid #e2e8f0;border-radius:4px;padding:4px 8px;cursor:pointer;font-size:14px">← Back</button>
131
+ <button id="modalBackBtn" onclick="modalBack()" style="display:none;background:#f1f5f9;border:1px solid #e2e8f0;border-radius:4px;padding:4px 8px;cursor:pointer;font-size:14px">\u2190 Back</button>
325
132
  <h3 id="modalTitle">Details</h3>
326
133
  </div>
327
- <button class="detail-modal-close" onclick="closeModal()">×</button>
134
+ <button class="detail-modal-close" onclick="closeModal()">\xD7</button>
328
135
  </div>
329
136
  <div id="modalBody"></div>
330
137
  </div>
@@ -350,10 +157,10 @@ export class DocServer {
350
157
  container.className = 'mermaid-container';
351
158
  container.innerHTML = \`
352
159
  <div class="mermaid-controls">
353
- <button onclick="zoomDiagram(\${idx}, 0.8)" title="縮小">➖</button>
354
- <button onclick="zoomDiagram(\${idx}, 1.25)" title="拡大">➕</button>
355
- <button onclick="zoomDiagram(\${idx}, 'reset')" title="リセット">🔄</button>
356
- <button onclick="toggleFullscreen(\${idx})" title="全画面">⛶</button>
160
+ <button onclick="zoomDiagram(\${idx}, 0.8)" title="\u7E2E\u5C0F">\u2796</button>
161
+ <button onclick="zoomDiagram(\${idx}, 1.25)" title="\u62E1\u5927">\u2795</button>
162
+ <button onclick="zoomDiagram(\${idx}, 'reset')" title="\u30EA\u30BB\u30C3\u30C8">\u{1F504}</button>
163
+ <button onclick="toggleFullscreen(\${idx})" title="\u5168\u753B\u9762">\u26F6</button>
357
164
  </div>
358
165
  <div class="mermaid-wrapper" id="wrapper-\${idx}">
359
166
  <div class="mermaid-inner" id="inner-\${idx}"></div>
@@ -481,17 +288,17 @@ export class DocServer {
481
288
  modalHistory = [];
482
289
 
483
290
  // Clean name: remove icons and extract operation name from patterns like "GraphQL: OPERATION_NAME"
484
- let cleanName = text.replace(/[\u{1F512}\u{1F4E1}\u{270F}\u{FE0F}\u{1F504}]/gu, '').trim();
291
+ let cleanName = text.replace(/[\u{1F512}\u{1F4E1}\u270F\uFE0F\u{1F504}]/gu, '').trim();
485
292
  // Handle "GraphQL: OPERATION_NAME" pattern
486
293
  if (cleanName.includes('GraphQL:')) {
487
- cleanName = cleanName.replace(/^.*GraphQL:\s*/, '').trim();
294
+ cleanName = cleanName.replace(/^.*GraphQL:s*/, '').trim();
488
295
  }
489
296
  // Handle "API: OPERATION_NAME" pattern
490
297
  if (cleanName.includes('API:')) {
491
- cleanName = cleanName.replace(/^.*API:\s*/, '').trim();
298
+ cleanName = cleanName.replace(/^.*API:s*/, '').trim();
492
299
  }
493
300
  // Remove any remaining prefixes like "Query:", "Mutation:"
494
- cleanName = cleanName.replace(/^(Query|Mutation|Fragment):\s*/i, '').trim();
301
+ cleanName = cleanName.replace(/^(Query|Mutation|Fragment):s*/i, '').trim();
495
302
 
496
303
  const op = window.findGraphQLOp?.(cleanName);
497
304
 
@@ -1118,7 +925,7 @@ export class DocServer {
1118
925
  async function regenerate() {
1119
926
  try {
1120
927
  const btn = document.querySelector('.regenerate-btn');
1121
- btn.textContent = ' 生成中...';
928
+ btn.textContent = '\u23F3 \u751F\u6210\u4E2D...';
1122
929
  btn.disabled = true;
1123
930
 
1124
931
  const res = await fetch('/api/regenerate', { method: 'POST' });
@@ -1127,119 +934,24 @@ export class DocServer {
1127
934
  if (data.success) {
1128
935
  window.location.reload();
1129
936
  } else {
1130
- alert('生成に失敗しました: ' + data.error);
937
+ alert('\u751F\u6210\u306B\u5931\u6557\u3057\u307E\u3057\u305F: ' + data.error);
1131
938
  }
1132
939
  } catch (e) {
1133
- alert('エラー: ' + e.message);
940
+ alert('\u30A8\u30E9\u30FC: ' + e.message);
1134
941
  } finally {
1135
942
  const btn = document.querySelector('.regenerate-btn');
1136
- btn.textContent = '🔄 再生成';
943
+ btn.textContent = '\u{1F504} \u518D\u751F\u6210';
1137
944
  btn.disabled = false;
1138
945
  }
1139
946
  }
1140
947
  </script>
1141
948
  </body>
1142
- </html>`;
1143
- }
1144
- async start(openBrowser = true) {
1145
- // Detect environments first
1146
- const rootPath = this.config.repositories[0]?.path || process.cwd();
1147
- console.log('🔍 Detecting project environments...');
1148
- this.envResult = await detectEnvironments(rootPath);
1149
- if (this.envResult.environments.length > 0) {
1150
- console.log(` Found: ${this.envResult.environments.map((e) => e.type).join(', ')}`);
1151
- for (const env of this.envResult.environments) {
1152
- if (env.features.length > 0) {
1153
- console.log(` ${env.type} features: ${env.features.join(', ')}`);
1154
- }
1155
- }
1156
- }
1157
- // Generate initial documentation for frontend
1158
- console.log('\n📚 Generating documentation...');
1159
- this.currentReport = await this.engine.generate();
1160
- // If Rails is detected, also analyze Rails
1161
- if (this.envResult.hasRails) {
1162
- console.log('\n🛤️ Analyzing Rails application...');
1163
- try {
1164
- this.railsAnalysis = await analyzeRailsApp(rootPath);
1165
- console.log(` ✅ Rails analysis complete`);
1166
- }
1167
- catch (error) {
1168
- console.error(` ⚠️ Rails analysis failed:`, error.message);
1169
- }
1170
- }
1171
- // Find available port (auto-detect if requested port is in use)
1172
- try {
1173
- const availablePort = await findAvailablePort(this.port);
1174
- if (availablePort !== this.port) {
1175
- console.log(`\n⚠️ Port ${this.port} is in use, using port ${availablePort} instead`);
1176
- }
1177
- this.port = availablePort;
1178
- }
1179
- catch (error) {
1180
- console.error(`\n❌ Failed to find available port: ${error.message}`);
1181
- process.exit(1);
1182
- }
1183
- // Start server
1184
- this.server.listen(this.port, () => {
1185
- console.log(`\n🌐 Documentation server running at http://localhost:${this.port}`);
1186
- if (this.envResult?.hasRails && this.envResult?.hasNextjs) {
1187
- console.log(' 📊 Multiple environments detected - use tabs to switch views');
1188
- }
1189
- console.log(' Press Ctrl+C to stop\n');
1190
- });
1191
- // Open browser
1192
- if (openBrowser) {
1193
- const open = (await import('open')).default;
1194
- await open(`http://localhost:${this.port}`);
1195
- }
1196
- // Watch for changes
1197
- if (this.config.watch.enabled) {
1198
- this.watchForChanges();
1199
- }
1200
- }
1201
- async regenerate() {
1202
- console.log('\n🔄 Regenerating documentation...');
1203
- this.currentReport = await this.engine.generate();
1204
- // Re-analyze Rails if detected
1205
- if (this.envResult?.hasRails) {
1206
- const rootPath = this.config.repositories[0]?.path || process.cwd();
1207
- try {
1208
- this.railsAnalysis = await analyzeRailsApp(rootPath);
1209
- }
1210
- catch (error) {
1211
- console.error(`⚠️ Rails re-analysis failed:`, error.message);
1212
- }
1213
- }
1214
- this.io.emit('reload');
1215
- console.log('✅ Documentation regenerated');
1216
- }
1217
- async watchForChanges() {
1218
- const watchDirs = this.config.repositories.map((r) => r.path);
1219
- let timeout = null;
1220
- for (const dir of watchDirs) {
1221
- try {
1222
- const watcher = fs.watch(dir, { recursive: true });
1223
- (async () => {
1224
- for await (const event of watcher) {
1225
- if (event.filename &&
1226
- (event.filename.endsWith('.ts') || event.filename.endsWith('.tsx'))) {
1227
- if (timeout)
1228
- clearTimeout(timeout);
1229
- timeout = setTimeout(async () => {
1230
- await this.regenerate();
1231
- }, this.config.watch.debounce);
1232
- }
1233
- }
1234
- })();
1235
- }
1236
- catch (error) {
1237
- console.warn(`Warning: Could not watch directory ${dir}:`, error.message);
1238
- }
1239
- }
1240
- }
1241
- stop() {
1242
- this.server.close();
1243
- console.log('\n👋 Server stopped');
1244
- }
1245
- }
949
+ </html>`}async start(t=true){let a=this.config.repositories[0]?.path||process.cwd();if(console.log("\u{1F50D} Detecting project environments..."),this.envResult=await a$3(a),this.envResult.environments.length>0){console.log(` Found: ${this.envResult.environments.map(e=>e.type).join(", ")}`);for(let e of this.envResult.environments)e.features.length>0&&console.log(` ${e.type} features: ${e.features.join(", ")}`);}if(console.log(`
950
+ \u{1F4DA} Generating documentation...`),this.currentReport=await this.engine.generate(),this.envResult.hasRails){console.log(`
951
+ \u{1F6E4}\uFE0F Analyzing Rails application...`);try{this.railsAnalysis=await k(a),console.log(" \u2705 Rails analysis complete");}catch(e){console.error(" \u26A0\uFE0F Rails analysis failed:",e.message);}}try{let e=await O(this.port);e!==this.port&&console.log(`
952
+ \u26A0\uFE0F Port ${this.port} is in use, using port ${e} instead`),this.port=e;}catch(e){console.error(`
953
+ \u274C Failed to find available port: ${e.message}`),process.exit(1);}if(this.server.listen(this.port,()=>{console.log(`
954
+ \u{1F310} Documentation server running at http://localhost:${this.port}`),this.envResult?.hasRails&&this.envResult?.hasNextjs&&console.log(" \u{1F4CA} Multiple environments detected - use tabs to switch views"),console.log(` Press Ctrl+C to stop
955
+ `);}),t){let e=(await import('open')).default;await e(`http://localhost:${this.port}`);}this.config.watch.enabled&&this.watchForChanges();}async regenerate(){if(console.log(`
956
+ \u{1F504} Regenerating documentation...`),this.currentReport=await this.engine.generate(),this.envResult?.hasRails){let t=this.config.repositories[0]?.path||process.cwd();try{this.railsAnalysis=await k(t);}catch(a){console.error("\u26A0\uFE0F Rails re-analysis failed:",a.message);}}this.io.emit("reload"),console.log("\u2705 Documentation regenerated");}async watchForChanges(){let t=this.config.repositories.map(e=>e.path),a=null;for(let e of t)try{let n=m.watch(e,{recursive:!0});(async()=>{for await(let o of n)o.filename&&(o.filename.endsWith(".ts")||o.filename.endsWith(".tsx"))&&(a&&clearTimeout(a),a=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(`
957
+ \u{1F44B} Server stopped`);}};export{b as a,I as b};
File without changes