@wtdlee/repomap 0.3.0 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/analyzers/index.d.ts +69 -5
- package/dist/analyzers/index.js +1 -5
- package/dist/chunk-3PWXDB7B.js +153 -0
- package/dist/{generators/page-map-generator.js → chunk-3YFXZAP7.js} +322 -358
- package/dist/chunk-6F4PWJZI.js +1 -0
- package/dist/{generators/rails-map-generator.js → chunk-E4WRODSI.js} +86 -94
- package/dist/chunk-GNBMJMET.js +2519 -0
- package/dist/{server/doc-server.js → chunk-M6YNU536.js} +702 -303
- package/dist/chunk-OWM6WNLE.js +2610 -0
- package/dist/chunk-SSU6QFTX.js +1058 -0
- package/dist/cli.d.ts +0 -1
- package/dist/cli.js +348 -452
- package/dist/dataflow-analyzer-BfAiqVUp.d.ts +180 -0
- package/dist/env-detector-EEMVUEIA.js +1 -0
- package/dist/generators/index.d.ts +431 -3
- package/dist/generators/index.js +2 -3
- package/dist/index.d.ts +53 -10
- package/dist/index.js +8 -11
- package/dist/page-map-generator-6MJGPBVA.js +1 -0
- package/dist/rails-UWSDRS33.js +1 -0
- package/dist/rails-map-generator-D2URLMVJ.js +2 -0
- package/dist/server/index.d.ts +33 -1
- package/dist/server/index.js +7 -1
- package/dist/types.d.ts +39 -37
- package/dist/types.js +1 -5
- package/package.json +4 -2
- package/dist/analyzers/base-analyzer.d.ts +0 -45
- package/dist/analyzers/base-analyzer.js +0 -47
- package/dist/analyzers/dataflow-analyzer.d.ts +0 -29
- package/dist/analyzers/dataflow-analyzer.js +0 -425
- package/dist/analyzers/graphql-analyzer.d.ts +0 -22
- package/dist/analyzers/graphql-analyzer.js +0 -386
- package/dist/analyzers/pages-analyzer.d.ts +0 -84
- package/dist/analyzers/pages-analyzer.js +0 -1695
- package/dist/analyzers/rails/index.d.ts +0 -46
- package/dist/analyzers/rails/index.js +0 -145
- package/dist/analyzers/rails/rails-controller-analyzer.d.ts +0 -82
- package/dist/analyzers/rails/rails-controller-analyzer.js +0 -478
- package/dist/analyzers/rails/rails-grpc-analyzer.d.ts +0 -44
- package/dist/analyzers/rails/rails-grpc-analyzer.js +0 -262
- package/dist/analyzers/rails/rails-model-analyzer.d.ts +0 -88
- package/dist/analyzers/rails/rails-model-analyzer.js +0 -493
- package/dist/analyzers/rails/rails-react-analyzer.d.ts +0 -41
- package/dist/analyzers/rails/rails-react-analyzer.js +0 -529
- package/dist/analyzers/rails/rails-routes-analyzer.d.ts +0 -62
- package/dist/analyzers/rails/rails-routes-analyzer.js +0 -540
- package/dist/analyzers/rails/rails-view-analyzer.d.ts +0 -49
- package/dist/analyzers/rails/rails-view-analyzer.js +0 -386
- package/dist/analyzers/rails/ruby-parser.d.ts +0 -63
- package/dist/analyzers/rails/ruby-parser.js +0 -212
- package/dist/analyzers/rest-api-analyzer.d.ts +0 -65
- package/dist/analyzers/rest-api-analyzer.js +0 -479
- package/dist/core/cache.d.ts +0 -47
- package/dist/core/cache.js +0 -151
- package/dist/core/engine.d.ts +0 -46
- package/dist/core/engine.js +0 -319
- package/dist/core/index.d.ts +0 -2
- package/dist/core/index.js +0 -2
- package/dist/generators/markdown-generator.d.ts +0 -25
- package/dist/generators/markdown-generator.js +0 -782
- package/dist/generators/mermaid-generator.d.ts +0 -35
- package/dist/generators/mermaid-generator.js +0 -364
- package/dist/generators/page-map-generator.d.ts +0 -22
- package/dist/generators/rails-map-generator.d.ts +0 -21
- package/dist/server/doc-server.d.ts +0 -30
- package/dist/utils/env-detector.d.ts +0 -31
- package/dist/utils/env-detector.js +0 -188
- package/dist/utils/parallel.d.ts +0 -23
- package/dist/utils/parallel.js +0 -70
- package/dist/utils/port.d.ts +0 -15
- package/dist/utils/port.js +0 -41
package/dist/cli.js
CHANGED
|
@@ -1,357 +1,283 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import { DocGeneratorEngine, DocServer } from './chunk-M6YNU536.js';
|
|
3
|
+
import './chunk-E4WRODSI.js';
|
|
4
|
+
import './chunk-3PWXDB7B.js';
|
|
5
|
+
import './chunk-GNBMJMET.js';
|
|
6
|
+
import './chunk-SSU6QFTX.js';
|
|
7
|
+
import './chunk-3YFXZAP7.js';
|
|
8
|
+
import './chunk-OWM6WNLE.js';
|
|
2
9
|
import { Command } from 'commander';
|
|
3
10
|
import chalk from 'chalk';
|
|
4
11
|
import * as path from 'path';
|
|
5
12
|
import * as fs from 'fs/promises';
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
program
|
|
10
|
-
.name('repomap')
|
|
11
|
-
.description('Interactive documentation generator for code repositories')
|
|
12
|
-
.version('0.1.0');
|
|
13
|
-
/**
|
|
14
|
-
* Auto-detect project type and settings
|
|
15
|
-
*/
|
|
13
|
+
|
|
14
|
+
var program = new Command();
|
|
15
|
+
program.name("repomap").description("Interactive documentation generator for code repositories").version("0.1.0");
|
|
16
16
|
async function detectProject(dir) {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
try {
|
|
46
|
-
await fs.access(path.join(dir, pagesDir));
|
|
47
|
-
settings.pagesDir = pagesDir;
|
|
48
|
-
break;
|
|
49
|
-
}
|
|
50
|
-
catch { }
|
|
51
|
-
}
|
|
52
|
-
// Check for features directory
|
|
53
|
-
const possibleFeaturesDirs = [
|
|
54
|
-
'src/features',
|
|
55
|
-
'features',
|
|
56
|
-
'src/modules',
|
|
57
|
-
'modules',
|
|
58
|
-
'frontend/src',
|
|
59
|
-
];
|
|
60
|
-
for (const featuresDir of possibleFeaturesDirs) {
|
|
61
|
-
try {
|
|
62
|
-
await fs.access(path.join(dir, featuresDir));
|
|
63
|
-
settings.featuresDir = featuresDir;
|
|
64
|
-
break;
|
|
65
|
-
}
|
|
66
|
-
catch { }
|
|
67
|
-
}
|
|
68
|
-
// Check for components directory
|
|
69
|
-
const possibleComponentsDirs = [
|
|
70
|
-
'src/components',
|
|
71
|
-
'components',
|
|
72
|
-
'src/common/components',
|
|
73
|
-
'frontend/src',
|
|
74
|
-
];
|
|
75
|
-
for (const componentsDir of possibleComponentsDirs) {
|
|
76
|
-
try {
|
|
77
|
-
await fs.access(path.join(dir, componentsDir));
|
|
78
|
-
settings.componentsDir = componentsDir;
|
|
79
|
-
break;
|
|
80
|
-
}
|
|
81
|
-
catch { }
|
|
82
|
-
}
|
|
17
|
+
const dirName = path.basename(dir);
|
|
18
|
+
let isRails = false;
|
|
19
|
+
const gemfilePath = path.join(dir, "Gemfile");
|
|
20
|
+
const routesPath = path.join(dir, "config", "routes.rb");
|
|
21
|
+
try {
|
|
22
|
+
await fs.access(gemfilePath);
|
|
23
|
+
await fs.access(routesPath);
|
|
24
|
+
const gemfile = await fs.readFile(gemfilePath, "utf-8");
|
|
25
|
+
isRails = gemfile.includes("gem 'rails'") || gemfile.includes('gem "rails"');
|
|
26
|
+
} catch {
|
|
27
|
+
}
|
|
28
|
+
const packageJsonPath = path.join(dir, "package.json");
|
|
29
|
+
let hasReact = false;
|
|
30
|
+
let hasNextjs = false;
|
|
31
|
+
const settings = {};
|
|
32
|
+
try {
|
|
33
|
+
const packageJson = JSON.parse(await fs.readFile(packageJsonPath, "utf-8"));
|
|
34
|
+
const deps = { ...packageJson.dependencies, ...packageJson.devDependencies };
|
|
35
|
+
hasReact = !!deps["react"];
|
|
36
|
+
hasNextjs = !!deps["next"];
|
|
37
|
+
const possiblePagesDirs = ["src/pages", "pages", "app", "src/app", "frontend/src"];
|
|
38
|
+
for (const pagesDir of possiblePagesDirs) {
|
|
39
|
+
try {
|
|
40
|
+
await fs.access(path.join(dir, pagesDir));
|
|
41
|
+
settings.pagesDir = pagesDir;
|
|
42
|
+
break;
|
|
43
|
+
} catch {
|
|
44
|
+
}
|
|
83
45
|
}
|
|
84
|
-
|
|
85
|
-
|
|
46
|
+
const possibleFeaturesDirs = [
|
|
47
|
+
"src/features",
|
|
48
|
+
"features",
|
|
49
|
+
"src/modules",
|
|
50
|
+
"modules",
|
|
51
|
+
"frontend/src"
|
|
52
|
+
];
|
|
53
|
+
for (const featuresDir of possibleFeaturesDirs) {
|
|
54
|
+
try {
|
|
55
|
+
await fs.access(path.join(dir, featuresDir));
|
|
56
|
+
settings.featuresDir = featuresDir;
|
|
57
|
+
break;
|
|
58
|
+
} catch {
|
|
59
|
+
}
|
|
86
60
|
}
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
61
|
+
const possibleComponentsDirs = [
|
|
62
|
+
"src/components",
|
|
63
|
+
"components",
|
|
64
|
+
"src/common/components",
|
|
65
|
+
"frontend/src"
|
|
66
|
+
];
|
|
67
|
+
for (const componentsDir of possibleComponentsDirs) {
|
|
68
|
+
try {
|
|
69
|
+
await fs.access(path.join(dir, componentsDir));
|
|
70
|
+
settings.componentsDir = componentsDir;
|
|
71
|
+
break;
|
|
72
|
+
} catch {
|
|
73
|
+
}
|
|
92
74
|
}
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
return
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
75
|
+
} catch {
|
|
76
|
+
}
|
|
77
|
+
const analyzers = [];
|
|
78
|
+
if (hasReact || hasNextjs) {
|
|
79
|
+
analyzers.push("pages", "graphql", "dataflow", "rest-api");
|
|
80
|
+
}
|
|
81
|
+
let type = "generic";
|
|
82
|
+
if (hasNextjs) {
|
|
83
|
+
type = "nextjs";
|
|
84
|
+
} else if (isRails) {
|
|
85
|
+
type = "rails";
|
|
86
|
+
}
|
|
87
|
+
if (!isRails && !hasReact && !hasNextjs) {
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
90
|
+
return {
|
|
91
|
+
name: dirName,
|
|
92
|
+
displayName: dirName,
|
|
93
|
+
description: isRails && hasReact ? "Rails + React application" : isRails ? "Rails application" : "",
|
|
94
|
+
path: dir,
|
|
95
|
+
branch: "main",
|
|
96
|
+
type,
|
|
97
|
+
analyzers,
|
|
98
|
+
settings
|
|
99
|
+
};
|
|
116
100
|
}
|
|
117
|
-
/**
|
|
118
|
-
* Create default config for current directory
|
|
119
|
-
*/
|
|
120
101
|
async function createDefaultConfig(cwd) {
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
102
|
+
const project = await detectProject(cwd);
|
|
103
|
+
if (!project) {
|
|
104
|
+
throw new Error(
|
|
105
|
+
"Could not detect project. Please create a repomap.config.ts file or run 'repomap init'."
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
return {
|
|
109
|
+
outputDir: "./.repomap",
|
|
110
|
+
site: {
|
|
111
|
+
title: `${project.displayName} Documentation`,
|
|
112
|
+
description: "Auto-generated documentation",
|
|
113
|
+
baseUrl: "/docs"
|
|
114
|
+
},
|
|
115
|
+
repositories: [project],
|
|
116
|
+
analysis: {
|
|
117
|
+
include: ["**/*.tsx", "**/*.ts"],
|
|
118
|
+
exclude: [
|
|
119
|
+
"**/node_modules/**",
|
|
120
|
+
"**/__tests__/**",
|
|
121
|
+
"**/*.test.*",
|
|
122
|
+
"**/*.spec.*",
|
|
123
|
+
"**/dist/**",
|
|
124
|
+
"**/.next/**"
|
|
125
|
+
],
|
|
126
|
+
maxDepth: 5
|
|
127
|
+
},
|
|
128
|
+
diagrams: {
|
|
129
|
+
enabled: true,
|
|
130
|
+
types: ["flowchart", "sequence"],
|
|
131
|
+
theme: "default"
|
|
132
|
+
},
|
|
133
|
+
watch: {
|
|
134
|
+
enabled: false,
|
|
135
|
+
debounce: 1e3
|
|
136
|
+
},
|
|
137
|
+
integrations: {
|
|
138
|
+
github: { enabled: false, organization: "" },
|
|
139
|
+
slack: { enabled: false }
|
|
124
140
|
}
|
|
125
|
-
|
|
126
|
-
outputDir: './.repomap',
|
|
127
|
-
site: {
|
|
128
|
-
title: `${project.displayName} Documentation`,
|
|
129
|
-
description: 'Auto-generated documentation',
|
|
130
|
-
baseUrl: '/docs',
|
|
131
|
-
},
|
|
132
|
-
repositories: [project],
|
|
133
|
-
analysis: {
|
|
134
|
-
include: ['**/*.tsx', '**/*.ts'],
|
|
135
|
-
exclude: [
|
|
136
|
-
'**/node_modules/**',
|
|
137
|
-
'**/__tests__/**',
|
|
138
|
-
'**/*.test.*',
|
|
139
|
-
'**/*.spec.*',
|
|
140
|
-
'**/dist/**',
|
|
141
|
-
'**/.next/**',
|
|
142
|
-
],
|
|
143
|
-
maxDepth: 5,
|
|
144
|
-
},
|
|
145
|
-
diagrams: {
|
|
146
|
-
enabled: true,
|
|
147
|
-
types: ['flowchart', 'sequence'],
|
|
148
|
-
theme: 'default',
|
|
149
|
-
},
|
|
150
|
-
watch: {
|
|
151
|
-
enabled: false,
|
|
152
|
-
debounce: 1000,
|
|
153
|
-
},
|
|
154
|
-
integrations: {
|
|
155
|
-
github: { enabled: false, organization: '' },
|
|
156
|
-
slack: { enabled: false },
|
|
157
|
-
},
|
|
158
|
-
};
|
|
141
|
+
};
|
|
159
142
|
}
|
|
160
|
-
/**
|
|
161
|
-
* Load config from file or auto-detect
|
|
162
|
-
*/
|
|
163
143
|
async function loadConfig(configPath, cwd) {
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
const module = await import(fullPath);
|
|
174
|
-
return module.config || module.default;
|
|
175
|
-
}
|
|
176
|
-
catch { }
|
|
144
|
+
const configFiles = configPath ? [configPath] : ["repomap.config.ts", "repomap.config.js", "repomap.config.mjs"];
|
|
145
|
+
for (const file of configFiles) {
|
|
146
|
+
const fullPath = path.resolve(cwd, file);
|
|
147
|
+
try {
|
|
148
|
+
await fs.access(fullPath);
|
|
149
|
+
console.log(chalk.gray(`Loading config from: ${fullPath}`));
|
|
150
|
+
const module = await import(fullPath);
|
|
151
|
+
return module.config || module.default;
|
|
152
|
+
} catch {
|
|
177
153
|
}
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
154
|
+
}
|
|
155
|
+
console.log(chalk.gray("No config file found, auto-detecting project..."));
|
|
156
|
+
return createDefaultConfig(cwd);
|
|
181
157
|
}
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
.
|
|
191
|
-
|
|
192
|
-
.option('--no-cache', 'Disable caching (always analyze from scratch)')
|
|
193
|
-
.option('--format <type>', 'Output format: json, html, markdown (default: all)', 'all')
|
|
194
|
-
.option('--ci', 'CI mode: minimal output, exit codes for errors')
|
|
195
|
-
.option('--static', 'Generate standalone HTML files (for GitHub Pages)')
|
|
196
|
-
.action(async (options) => {
|
|
197
|
-
const isCI = options.ci || process.env.CI === 'true';
|
|
198
|
-
if (!isCI) {
|
|
199
|
-
console.log(chalk.blue.bold('\n📚 Repomap - Documentation Generator\n'));
|
|
158
|
+
program.command("generate").description("Generate documentation from source code").option("-c, --config <path>", "Path to config file").option("-o, --output <path>", "Output directory").option("--repo <name>", "Analyze specific repository only").option("--watch", "Watch for changes and regenerate").option("--no-cache", "Disable caching (always analyze from scratch)").option("--format <type>", "Output format: json, html, markdown (default: all)", "all").option("--ci", "CI mode: minimal output, exit codes for errors").option("--static", "Generate standalone HTML files (for GitHub Pages)").action(async (options) => {
|
|
159
|
+
const isCI = options.ci || process.env.CI === "true";
|
|
160
|
+
if (!isCI) {
|
|
161
|
+
console.log(chalk.blue.bold("\n\u{1F4DA} Repomap - Documentation Generator\n"));
|
|
162
|
+
}
|
|
163
|
+
try {
|
|
164
|
+
const cwd = process.cwd();
|
|
165
|
+
const config = await loadConfig(options.config, cwd);
|
|
166
|
+
if (options.output) {
|
|
167
|
+
config.outputDir = options.output;
|
|
200
168
|
}
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
if (options.output) {
|
|
206
|
-
config.outputDir = options.output;
|
|
207
|
-
}
|
|
208
|
-
// Filter repositories if specified
|
|
209
|
-
if (options.repo) {
|
|
210
|
-
config.repositories = config.repositories.filter((r) => r.name === options.repo);
|
|
211
|
-
if (config.repositories.length === 0) {
|
|
212
|
-
console.error(chalk.red(`Repository "${options.repo}" not found in config`));
|
|
213
|
-
process.exit(1);
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
// Create engine and generate
|
|
217
|
-
const engine = new DocGeneratorEngine(config, { noCache: !options.cache });
|
|
218
|
-
if (options.watch) {
|
|
219
|
-
console.log(chalk.yellow('\n👀 Watch mode enabled. Press Ctrl+C to stop.\n'));
|
|
220
|
-
await watchAndGenerate(engine, config);
|
|
221
|
-
}
|
|
222
|
-
else {
|
|
223
|
-
const report = await engine.generate();
|
|
224
|
-
// Handle different output formats
|
|
225
|
-
if (options.format === 'json' || options.static) {
|
|
226
|
-
const jsonPath = path.join(config.outputDir, 'report.json');
|
|
227
|
-
await fs.mkdir(config.outputDir, { recursive: true });
|
|
228
|
-
await fs.writeFile(jsonPath, JSON.stringify(report, null, 2));
|
|
229
|
-
if (!isCI)
|
|
230
|
-
console.log(chalk.green(`📄 JSON report: ${jsonPath}`));
|
|
231
|
-
}
|
|
232
|
-
// Generate static HTML files for GitHub Pages
|
|
233
|
-
if (options.static) {
|
|
234
|
-
await generateStaticSite(config, report, isCI);
|
|
235
|
-
}
|
|
236
|
-
if (!isCI) {
|
|
237
|
-
printSummary(report);
|
|
238
|
-
}
|
|
239
|
-
else {
|
|
240
|
-
// CI mode: minimal output
|
|
241
|
-
const totalPages = report.repositories.reduce((sum, r) => sum + r.summary.totalPages, 0);
|
|
242
|
-
console.log(`✅ Generated: ${totalPages} pages, ${report.repositories.length} repos`);
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
|
-
catch (error) {
|
|
247
|
-
console.error(isCI ? `Error: ${error.message}` : chalk.red('\n❌ Error:'), error.message);
|
|
169
|
+
if (options.repo) {
|
|
170
|
+
config.repositories = config.repositories.filter((r) => r.name === options.repo);
|
|
171
|
+
if (config.repositories.length === 0) {
|
|
172
|
+
console.error(chalk.red(`Repository "${options.repo}" not found in config`));
|
|
248
173
|
process.exit(1);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
const engine = new DocGeneratorEngine(config, { noCache: !options.cache });
|
|
177
|
+
if (options.watch) {
|
|
178
|
+
console.log(chalk.yellow("\n\u{1F440} Watch mode enabled. Press Ctrl+C to stop.\n"));
|
|
179
|
+
await watchAndGenerate(engine, config);
|
|
180
|
+
} else {
|
|
181
|
+
const report = await engine.generate();
|
|
182
|
+
if (options.format === "json" || options.static) {
|
|
183
|
+
const jsonPath = path.join(config.outputDir, "report.json");
|
|
184
|
+
await fs.mkdir(config.outputDir, { recursive: true });
|
|
185
|
+
await fs.writeFile(jsonPath, JSON.stringify(report, null, 2));
|
|
186
|
+
if (!isCI) console.log(chalk.green(`\u{1F4C4} JSON report: ${jsonPath}`));
|
|
187
|
+
}
|
|
188
|
+
if (options.static) {
|
|
189
|
+
await generateStaticSite(config, report, isCI);
|
|
190
|
+
}
|
|
191
|
+
if (!isCI) {
|
|
192
|
+
printSummary(report);
|
|
193
|
+
} else {
|
|
194
|
+
const totalPages = report.repositories.reduce(
|
|
195
|
+
(sum, r) => sum + r.summary.totalPages,
|
|
196
|
+
0
|
|
197
|
+
);
|
|
198
|
+
console.log(`\u2705 Generated: ${totalPages} pages, ${report.repositories.length} repos`);
|
|
199
|
+
}
|
|
249
200
|
}
|
|
201
|
+
} catch (error) {
|
|
202
|
+
console.error(
|
|
203
|
+
isCI ? `Error: ${error.message}` : chalk.red("\n\u274C Error:"),
|
|
204
|
+
error.message
|
|
205
|
+
);
|
|
206
|
+
process.exit(1);
|
|
207
|
+
}
|
|
250
208
|
});
|
|
251
|
-
/**
|
|
252
|
-
* Generate static HTML site for GitHub Pages deployment
|
|
253
|
-
*/
|
|
254
209
|
async function generateStaticSite(config, report, isCI) {
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
210
|
+
const { PageMapGenerator } = await import('./page-map-generator-6MJGPBVA.js');
|
|
211
|
+
const { detectEnvironments } = await import('./env-detector-EEMVUEIA.js');
|
|
212
|
+
const outputDir = config.outputDir;
|
|
213
|
+
await fs.mkdir(outputDir, { recursive: true });
|
|
214
|
+
const rootPath = config.repositories[0]?.path || process.cwd();
|
|
215
|
+
const envResult = await detectEnvironments(rootPath);
|
|
216
|
+
let railsAnalysis = null;
|
|
217
|
+
if (envResult.hasRails) {
|
|
218
|
+
const { analyzeRailsApp } = await import('./rails-UWSDRS33.js');
|
|
219
|
+
railsAnalysis = await analyzeRailsApp(rootPath);
|
|
220
|
+
}
|
|
221
|
+
const pageMapGenerator = new PageMapGenerator();
|
|
222
|
+
const pageMapHtml = pageMapGenerator.generatePageMapHtml(report, {
|
|
223
|
+
envResult,
|
|
224
|
+
railsAnalysis,
|
|
225
|
+
staticMode: true
|
|
226
|
+
});
|
|
227
|
+
await fs.writeFile(path.join(outputDir, "index.html"), pageMapHtml);
|
|
228
|
+
if (!isCI) console.log(chalk.green(`\u{1F4C4} Static page map: ${path.join(outputDir, "index.html")}`));
|
|
229
|
+
if (railsAnalysis) {
|
|
230
|
+
const { RailsMapGenerator } = await import('./rails-map-generator-D2URLMVJ.js');
|
|
231
|
+
const railsGenerator = new RailsMapGenerator();
|
|
232
|
+
const railsHtml = railsGenerator.generateFromResult(railsAnalysis);
|
|
233
|
+
await fs.writeFile(path.join(outputDir, "rails-map.html"), railsHtml);
|
|
275
234
|
if (!isCI)
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
await fs.writeFile(path.join(outputDir, 'rails-map.html'), railsHtml);
|
|
283
|
-
if (!isCI)
|
|
284
|
-
console.log(chalk.green(`📄 Static Rails map: ${path.join(outputDir, 'rails-map.html')}`));
|
|
285
|
-
}
|
|
286
|
-
// Copy CSS assets
|
|
287
|
-
const cssFiles = ['common.css', 'page-map.css', 'docs.css', 'rails-map.css'];
|
|
288
|
-
const assetsDir = path.join(outputDir, 'assets');
|
|
289
|
-
await fs.mkdir(assetsDir, { recursive: true });
|
|
290
|
-
for (const cssFile of cssFiles) {
|
|
291
|
-
try {
|
|
292
|
-
const cssPath = new URL(`./generators/assets/${cssFile}`, import.meta.url);
|
|
293
|
-
const css = await fs.readFile(cssPath, 'utf-8');
|
|
294
|
-
await fs.writeFile(path.join(assetsDir, cssFile), css);
|
|
295
|
-
}
|
|
296
|
-
catch {
|
|
297
|
-
// CSS file not found, skip
|
|
298
|
-
}
|
|
299
|
-
}
|
|
300
|
-
if (!isCI) {
|
|
301
|
-
console.log(chalk.green(`\n✅ Static site generated in: ${outputDir}`));
|
|
302
|
-
console.log(chalk.gray(' Deploy to GitHub Pages or any static hosting'));
|
|
303
|
-
}
|
|
304
|
-
}
|
|
305
|
-
/**
|
|
306
|
-
* Serve command - starts documentation server
|
|
307
|
-
*/
|
|
308
|
-
program
|
|
309
|
-
.command('serve')
|
|
310
|
-
.description('Start local documentation server with live reload')
|
|
311
|
-
.option('-c, --config <path>', 'Path to config file')
|
|
312
|
-
.option('--path <path>', 'Path to repository to analyze (auto-detect if no config)')
|
|
313
|
-
.option('-p, --port <number>', 'Server port', '3030')
|
|
314
|
-
.option('--no-open', "Don't open browser automatically")
|
|
315
|
-
.option('--no-cache', 'Disable caching (always analyze from scratch)')
|
|
316
|
-
.action(async (options) => {
|
|
317
|
-
console.log(chalk.blue.bold('\n🌐 Repomap - Documentation Server\n'));
|
|
235
|
+
console.log(chalk.green(`\u{1F4C4} Static Rails map: ${path.join(outputDir, "rails-map.html")}`));
|
|
236
|
+
}
|
|
237
|
+
const cssFiles = ["common.css", "page-map.css", "docs.css", "rails-map.css"];
|
|
238
|
+
const assetsDir = path.join(outputDir, "assets");
|
|
239
|
+
await fs.mkdir(assetsDir, { recursive: true });
|
|
240
|
+
for (const cssFile of cssFiles) {
|
|
318
241
|
try {
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
}
|
|
324
|
-
catch (error) {
|
|
325
|
-
console.error(chalk.red('\n❌ Error:'), error.message);
|
|
326
|
-
process.exit(1);
|
|
242
|
+
const cssPath = new URL(`./generators/assets/${cssFile}`, import.meta.url);
|
|
243
|
+
const css = await fs.readFile(cssPath, "utf-8");
|
|
244
|
+
await fs.writeFile(path.join(assetsDir, cssFile), css);
|
|
245
|
+
} catch {
|
|
327
246
|
}
|
|
247
|
+
}
|
|
248
|
+
if (!isCI) {
|
|
249
|
+
console.log(chalk.green(`
|
|
250
|
+
\u2705 Static site generated in: ${outputDir}`));
|
|
251
|
+
console.log(chalk.gray(" Deploy to GitHub Pages or any static hosting"));
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
program.command("serve").description("Start local documentation server with live reload").option("-c, --config <path>", "Path to config file").option("--path <path>", "Path to repository to analyze (auto-detect if no config)").option("-p, --port <number>", "Server port", "3030").option("--no-open", "Don't open browser automatically").option("--no-cache", "Disable caching (always analyze from scratch)").action(async (options) => {
|
|
255
|
+
console.log(chalk.blue.bold("\n\u{1F310} Repomap - Documentation Server\n"));
|
|
256
|
+
try {
|
|
257
|
+
const targetPath = options.path || process.cwd();
|
|
258
|
+
const config = await loadConfig(options.config, targetPath);
|
|
259
|
+
const server = new DocServer(config, parseInt(options.port), { noCache: !options.cache });
|
|
260
|
+
await server.start(!options.open);
|
|
261
|
+
} catch (error) {
|
|
262
|
+
console.error(chalk.red("\n\u274C Error:"), error.message);
|
|
263
|
+
process.exit(1);
|
|
264
|
+
}
|
|
328
265
|
});
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
.
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
const
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
console.log(chalk.yellow('Config file already exists. Use --force to overwrite.'));
|
|
345
|
-
return;
|
|
346
|
-
}
|
|
347
|
-
// Detect current project
|
|
348
|
-
const project = await detectProject(process.cwd());
|
|
349
|
-
const projectName = project?.name || 'my-project';
|
|
350
|
-
const projectType = project?.type || 'nextjs';
|
|
351
|
-
const pagesDir = project?.settings.pagesDir || 'src/pages';
|
|
352
|
-
const featuresDir = project?.settings.featuresDir || 'src/features';
|
|
353
|
-
const componentsDir = project?.settings.componentsDir || 'src/components';
|
|
354
|
-
const templateConfig = `import type { DocGeneratorConfig } from "repomap";
|
|
266
|
+
program.command("init").description("Initialize repomap configuration").option("-f, --force", "Overwrite existing config").action(async (options) => {
|
|
267
|
+
const configPath = "./repomap.config.ts";
|
|
268
|
+
try {
|
|
269
|
+
const exists = await fs.access(configPath).then(() => true).catch(() => false);
|
|
270
|
+
if (exists && !options.force) {
|
|
271
|
+
console.log(chalk.yellow("Config file already exists. Use --force to overwrite."));
|
|
272
|
+
return;
|
|
273
|
+
}
|
|
274
|
+
const project = await detectProject(process.cwd());
|
|
275
|
+
const projectName = project?.name || "my-project";
|
|
276
|
+
const projectType = project?.type || "nextjs";
|
|
277
|
+
const pagesDir = project?.settings.pagesDir || "src/pages";
|
|
278
|
+
const featuresDir = project?.settings.featuresDir || "src/features";
|
|
279
|
+
const componentsDir = project?.settings.componentsDir || "src/components";
|
|
280
|
+
const templateConfig = `import type { DocGeneratorConfig } from "repomap";
|
|
355
281
|
|
|
356
282
|
export const config: DocGeneratorConfig = {
|
|
357
283
|
outputDir: "./.repomap",
|
|
@@ -409,141 +335,111 @@ export const config: DocGeneratorConfig = {
|
|
|
409
335
|
|
|
410
336
|
export default config;
|
|
411
337
|
`;
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
}
|
|
338
|
+
await fs.writeFile(configPath, templateConfig, "utf-8");
|
|
339
|
+
console.log(chalk.green(`\u2705 Created ${configPath}`));
|
|
340
|
+
console.log(chalk.gray("\nRun 'npx repomap serve' to start the documentation server."));
|
|
341
|
+
} catch (error) {
|
|
342
|
+
console.error(chalk.red("Failed to create config:"), error.message);
|
|
343
|
+
}
|
|
419
344
|
});
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
.command('rails')
|
|
425
|
-
.description('Analyze a Rails application and generate interactive map')
|
|
426
|
-
.option('--path <path>', 'Path to Rails application')
|
|
427
|
-
.option('-o, --output <path>', 'Output HTML file path')
|
|
428
|
-
.action(async (options) => {
|
|
429
|
-
console.log(chalk.blue.bold('\n🛤️ Repomap - Rails Analyzer\n'));
|
|
345
|
+
program.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 (options) => {
|
|
346
|
+
console.log(chalk.blue.bold("\n\u{1F6E4}\uFE0F Repomap - Rails Analyzer\n"));
|
|
347
|
+
try {
|
|
348
|
+
const targetPath = options.path || process.cwd();
|
|
430
349
|
try {
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
}
|
|
436
|
-
catch {
|
|
437
|
-
console.error(chalk.red('Not a Rails project (config/routes.rb not found)'));
|
|
438
|
-
process.exit(1);
|
|
439
|
-
}
|
|
440
|
-
// Dynamically import Rails analyzer
|
|
441
|
-
const { RailsMapGenerator } = await import('./generators/rails-map-generator.js');
|
|
442
|
-
// Generate map
|
|
443
|
-
const outputPath = options.output || path.join(targetPath, 'rails-map.html');
|
|
444
|
-
const generator = new RailsMapGenerator(targetPath);
|
|
445
|
-
await generator.generate({
|
|
446
|
-
title: `${path.basename(targetPath)} - Rails Map`,
|
|
447
|
-
outputPath,
|
|
448
|
-
});
|
|
449
|
-
console.log(chalk.green(`\n✅ Rails map generated: ${outputPath}`));
|
|
450
|
-
// Open in browser
|
|
451
|
-
const { exec } = await import('child_process');
|
|
452
|
-
exec(`open "${outputPath}"`);
|
|
453
|
-
}
|
|
454
|
-
catch (error) {
|
|
455
|
-
console.error(chalk.red('\n❌ Error:'), error.message);
|
|
456
|
-
process.exit(1);
|
|
350
|
+
await fs.access(path.join(targetPath, "config", "routes.rb"));
|
|
351
|
+
} catch {
|
|
352
|
+
console.error(chalk.red("Not a Rails project (config/routes.rb not found)"));
|
|
353
|
+
process.exit(1);
|
|
457
354
|
}
|
|
355
|
+
const { RailsMapGenerator } = await import('./rails-map-generator-D2URLMVJ.js');
|
|
356
|
+
const outputPath = options.output || path.join(targetPath, "rails-map.html");
|
|
357
|
+
const generator = new RailsMapGenerator(targetPath);
|
|
358
|
+
await generator.generate({
|
|
359
|
+
title: `${path.basename(targetPath)} - Rails Map`,
|
|
360
|
+
outputPath
|
|
361
|
+
});
|
|
362
|
+
console.log(chalk.green(`
|
|
363
|
+
\u2705 Rails map generated: ${outputPath}`));
|
|
364
|
+
const { exec } = await import('child_process');
|
|
365
|
+
exec(`open "${outputPath}"`);
|
|
366
|
+
} catch (error) {
|
|
367
|
+
console.error(chalk.red("\n\u274C Error:"), error.message);
|
|
368
|
+
process.exit(1);
|
|
369
|
+
}
|
|
458
370
|
});
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
.
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
const cwd = process.cwd();
|
|
470
|
-
const config = await loadConfig(options.config, cwd);
|
|
471
|
-
const reportPath = path.join(config.outputDir, 'report.json');
|
|
472
|
-
const reportExists = await fs
|
|
473
|
-
.access(reportPath)
|
|
474
|
-
.then(() => true)
|
|
475
|
-
.catch(() => false);
|
|
476
|
-
if (!reportExists) {
|
|
477
|
-
console.log(chalk.yellow("No previous report found. Run 'generate' first."));
|
|
478
|
-
return;
|
|
479
|
-
}
|
|
480
|
-
const previousReport = JSON.parse(await fs.readFile(reportPath, 'utf-8'));
|
|
481
|
-
// Generate new report without writing
|
|
482
|
-
const engine = new DocGeneratorEngine(config);
|
|
483
|
-
const currentReport = await engine.generate();
|
|
484
|
-
// Compare
|
|
485
|
-
showDiff(previousReport, currentReport);
|
|
486
|
-
}
|
|
487
|
-
catch (error) {
|
|
488
|
-
console.error(chalk.red('Failed to generate diff:'), error.message);
|
|
371
|
+
program.command("diff").description("Show documentation changes since last generation").option("-c, --config <path>", "Path to config file").action(async (options) => {
|
|
372
|
+
console.log(chalk.blue.bold("\n\u{1F4CA} Documentation Diff\n"));
|
|
373
|
+
try {
|
|
374
|
+
const cwd = process.cwd();
|
|
375
|
+
const config = await loadConfig(options.config, cwd);
|
|
376
|
+
const reportPath = path.join(config.outputDir, "report.json");
|
|
377
|
+
const reportExists = await fs.access(reportPath).then(() => true).catch(() => false);
|
|
378
|
+
if (!reportExists) {
|
|
379
|
+
console.log(chalk.yellow("No previous report found. Run 'generate' first."));
|
|
380
|
+
return;
|
|
489
381
|
}
|
|
382
|
+
const previousReport = JSON.parse(await fs.readFile(reportPath, "utf-8"));
|
|
383
|
+
const engine = new DocGeneratorEngine(config);
|
|
384
|
+
const currentReport = await engine.generate();
|
|
385
|
+
showDiff(previousReport, currentReport);
|
|
386
|
+
} catch (error) {
|
|
387
|
+
console.error(chalk.red("Failed to generate diff:"), error.message);
|
|
388
|
+
}
|
|
490
389
|
});
|
|
491
|
-
// Helper functions
|
|
492
390
|
async function watchAndGenerate(engine, config) {
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
const
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
}, config.watch.debounce);
|
|
508
|
-
}
|
|
509
|
-
}
|
|
391
|
+
await engine.generate();
|
|
392
|
+
const watchDirs = config.repositories.map((r) => r.path);
|
|
393
|
+
for (const dir of watchDirs) {
|
|
394
|
+
const watcher = fs.watch(dir, { recursive: true });
|
|
395
|
+
let timeout = null;
|
|
396
|
+
for await (const event of watcher) {
|
|
397
|
+
if (event.filename && (event.filename.endsWith(".ts") || event.filename.endsWith(".tsx"))) {
|
|
398
|
+
if (timeout) clearTimeout(timeout);
|
|
399
|
+
timeout = setTimeout(async () => {
|
|
400
|
+
console.log(chalk.yellow(`
|
|
401
|
+
\u{1F504} Change detected: ${event.filename}`));
|
|
402
|
+
await engine.generate();
|
|
403
|
+
}, config.watch.debounce);
|
|
404
|
+
}
|
|
510
405
|
}
|
|
406
|
+
}
|
|
511
407
|
}
|
|
512
408
|
function printSummary(report) {
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
409
|
+
console.log(chalk.green.bold("\n\u{1F4C8} Generation Summary\n"));
|
|
410
|
+
for (const repo of report.repositories) {
|
|
411
|
+
console.log(chalk.cyan(` ${repo.displayName}:`));
|
|
412
|
+
console.log(` Pages: ${repo.summary.totalPages}`);
|
|
413
|
+
console.log(` Components: ${repo.summary.totalComponents}`);
|
|
414
|
+
console.log(` GraphQL Operations: ${repo.summary.totalGraphQLOperations}`);
|
|
415
|
+
console.log(` Data Flows: ${repo.summary.totalDataFlows}`);
|
|
416
|
+
console.log();
|
|
417
|
+
}
|
|
418
|
+
console.log(chalk.gray(` Generated at: ${report.generatedAt}`));
|
|
523
419
|
}
|
|
524
420
|
function showDiff(previous, current) {
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
}
|
|
421
|
+
console.log(chalk.cyan("Changes detected:\n"));
|
|
422
|
+
for (const repo of current.repositories) {
|
|
423
|
+
const prevRepo = previous.repositories.find((r) => r.name === repo.name);
|
|
424
|
+
if (!prevRepo) {
|
|
425
|
+
console.log(chalk.green(` + New repository: ${repo.displayName}`));
|
|
426
|
+
continue;
|
|
427
|
+
}
|
|
428
|
+
const pagesDiff = repo.summary.totalPages - prevRepo.summary.totalPages;
|
|
429
|
+
const compDiff = repo.summary.totalComponents - prevRepo.summary.totalComponents;
|
|
430
|
+
const gqlDiff = repo.summary.totalGraphQLOperations - prevRepo.summary.totalGraphQLOperations;
|
|
431
|
+
if (pagesDiff !== 0 || compDiff !== 0 || gqlDiff !== 0) {
|
|
432
|
+
console.log(chalk.yellow(` ~ ${repo.displayName}:`));
|
|
433
|
+
if (pagesDiff !== 0) {
|
|
434
|
+
console.log(` Pages: ${pagesDiff > 0 ? "+" : ""}${pagesDiff}`);
|
|
435
|
+
}
|
|
436
|
+
if (compDiff !== 0) {
|
|
437
|
+
console.log(` Components: ${compDiff > 0 ? "+" : ""}${compDiff}`);
|
|
438
|
+
}
|
|
439
|
+
if (gqlDiff !== 0) {
|
|
440
|
+
console.log(` GraphQL Ops: ${gqlDiff > 0 ? "+" : ""}${gqlDiff}`);
|
|
441
|
+
}
|
|
547
442
|
}
|
|
443
|
+
}
|
|
548
444
|
}
|
|
549
445
|
program.parse();
|