@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,478 +0,0 @@
1
- /**
2
- * Rails Controller Analyzer using tree-sitter
3
- * tree-sitterを使用してコントローラファイルを解析する
4
- */
5
- import * as fs from 'fs';
6
- import * as path from 'path';
7
- import { glob } from 'glob';
8
- import { parseRubyFile, findNodes, getClassName, getSuperclass, getMethodName, getMethodParameters, getChildrenByType, } from './ruby-parser.js';
9
- export class RailsControllerAnalyzer {
10
- rootPath;
11
- controllersDir;
12
- controllers = [];
13
- errors = [];
14
- constructor(rootPath) {
15
- this.rootPath = rootPath;
16
- this.controllersDir = path.join(rootPath, 'app', 'controllers');
17
- }
18
- async analyze() {
19
- if (!fs.existsSync(this.controllersDir)) {
20
- return {
21
- controllers: [],
22
- totalActions: 0,
23
- namespaces: [],
24
- concerns: [],
25
- errors: [`Controllers directory not found at ${this.controllersDir}`],
26
- };
27
- }
28
- const controllerFiles = await glob('**/*_controller.rb', {
29
- cwd: this.controllersDir,
30
- ignore: ['concerns/**'],
31
- });
32
- for (const file of controllerFiles) {
33
- const fullPath = path.join(this.controllersDir, file);
34
- try {
35
- const controller = await this.parseControllerFile(fullPath, file);
36
- if (controller) {
37
- this.controllers.push(controller);
38
- }
39
- }
40
- catch (error) {
41
- this.errors.push(`Error parsing ${file}: ${error}`);
42
- }
43
- }
44
- const namespaces = [
45
- ...new Set(this.controllers.filter((c) => c.namespace).map((c) => c.namespace)),
46
- ];
47
- const concerns = [...new Set(this.controllers.flatMap((c) => c.concerns))];
48
- const totalActions = this.controllers.reduce((sum, c) => sum + c.actions.length, 0);
49
- return {
50
- controllers: this.controllers,
51
- totalActions,
52
- namespaces,
53
- concerns,
54
- errors: this.errors,
55
- };
56
- }
57
- async parseControllerFile(filePath, relativePath) {
58
- const tree = await parseRubyFile(filePath);
59
- const rootNode = tree.rootNode;
60
- // Extract namespace from path
61
- const pathParts = relativePath.replace(/_controller\.rb$/, '').split('/');
62
- const namespace = pathParts.length > 1 ? pathParts.slice(0, -1).join('/') : undefined;
63
- const controllerName = pathParts[pathParts.length - 1];
64
- // Find class definition
65
- const classNodes = findNodes(rootNode, 'class');
66
- if (classNodes.length === 0)
67
- return null;
68
- const classNode = classNodes[0];
69
- const className = getClassName(classNode);
70
- const parentClass = getSuperclass(classNode);
71
- if (!className)
72
- return null;
73
- const controller = {
74
- name: controllerName,
75
- filePath: relativePath,
76
- className,
77
- parentClass: parentClass || 'ApplicationController',
78
- namespace,
79
- actions: [],
80
- beforeActions: [],
81
- afterActions: [],
82
- aroundActions: [],
83
- skipBeforeActions: [],
84
- concerns: [],
85
- helpers: [],
86
- rescueFrom: [],
87
- line: classNode.startPosition.row + 1,
88
- };
89
- // Find all method calls for filters, concerns, etc.
90
- const calls = findNodes(classNode, 'call');
91
- for (const call of calls) {
92
- const methodNode = call.childForFieldName('method');
93
- if (!methodNode)
94
- continue;
95
- const methodName = methodNode.text;
96
- const line = call.startPosition.row + 1;
97
- switch (methodName) {
98
- case 'before_action':
99
- case 'before_filter': // Legacy
100
- this.parseFilter(call, controller.beforeActions, line);
101
- break;
102
- case 'after_action':
103
- case 'after_filter':
104
- this.parseFilter(call, controller.afterActions, line);
105
- break;
106
- case 'around_action':
107
- case 'around_filter':
108
- this.parseFilter(call, controller.aroundActions, line);
109
- break;
110
- case 'skip_before_action':
111
- case 'skip_before_filter':
112
- this.parseFilter(call, controller.skipBeforeActions, line);
113
- break;
114
- case 'include':
115
- this.parseInclude(call, controller.concerns);
116
- break;
117
- case 'helper':
118
- this.parseHelper(call, controller.helpers);
119
- break;
120
- case 'layout':
121
- controller.layoutInfo = this.parseLayout(call);
122
- break;
123
- case 'rescue_from':
124
- this.parseRescueFrom(call, controller.rescueFrom, line);
125
- break;
126
- }
127
- }
128
- // Find all method definitions
129
- const _methods = findNodes(classNode, 'method');
130
- let currentVisibility = 'public';
131
- // Track visibility changes through identifiers
132
- const bodyStatement = classNode.childForFieldName('body');
133
- if (bodyStatement) {
134
- for (let i = 0; i < bodyStatement.childCount; i++) {
135
- const child = bodyStatement.child(i);
136
- if (!child)
137
- continue;
138
- if (child.type === 'identifier') {
139
- const text = child.text;
140
- if (text === 'private')
141
- currentVisibility = 'private';
142
- else if (text === 'protected')
143
- currentVisibility = 'protected';
144
- else if (text === 'public')
145
- currentVisibility = 'public';
146
- }
147
- else if (child.type === 'method') {
148
- const action = this.parseMethod(child, currentVisibility);
149
- if (action) {
150
- controller.actions.push(action);
151
- }
152
- }
153
- }
154
- }
155
- return controller;
156
- }
157
- parseFilter(call, filters, line) {
158
- const args = this.getCallArguments(call);
159
- if (args.length === 0)
160
- return;
161
- // First argument is the filter name (symbol)
162
- const nameArg = args[0];
163
- const filterName = nameArg.text.replace(/^:/, '');
164
- const filter = {
165
- name: filterName,
166
- line,
167
- };
168
- // Parse options
169
- for (const arg of args.slice(1)) {
170
- if (arg.type === 'hash') {
171
- const pairs = getChildrenByType(arg, 'pair');
172
- for (const pair of pairs) {
173
- const key = pair.child(0)?.text?.replace(/^:/, '');
174
- const value = pair.child(2);
175
- if (!key || !value)
176
- continue;
177
- switch (key) {
178
- case 'only':
179
- filter.only = this.extractArrayValues(value);
180
- break;
181
- case 'except':
182
- filter.except = this.extractArrayValues(value);
183
- break;
184
- case 'if':
185
- filter.if = value.text;
186
- break;
187
- case 'unless':
188
- filter.unless = value.text;
189
- break;
190
- }
191
- }
192
- }
193
- }
194
- filters.push(filter);
195
- }
196
- parseInclude(call, concerns) {
197
- const args = this.getCallArguments(call);
198
- for (const arg of args) {
199
- if (arg.type === 'constant' || arg.type === 'scope_resolution') {
200
- concerns.push(arg.text);
201
- }
202
- }
203
- }
204
- parseHelper(call, helpers) {
205
- const args = this.getCallArguments(call);
206
- for (const arg of args) {
207
- const value = arg.text.replace(/^:/, '');
208
- helpers.push(value);
209
- }
210
- }
211
- parseLayout(call) {
212
- const args = this.getCallArguments(call);
213
- if (args.length === 0)
214
- return undefined;
215
- const nameArg = args[0];
216
- let layoutName = nameArg.text.replace(/^["']|["']$/g, '');
217
- // Handle symbol
218
- if (layoutName.startsWith(':')) {
219
- layoutName = layoutName.substring(1);
220
- }
221
- // Handle proc/lambda (return false or dynamic)
222
- if (nameArg.type === 'lambda' || nameArg.type === 'proc') {
223
- layoutName = '(dynamic)';
224
- }
225
- const layout = { name: layoutName };
226
- // Parse conditions
227
- for (const arg of args.slice(1)) {
228
- if (arg.type === 'hash') {
229
- layout.conditions = arg.text;
230
- }
231
- }
232
- return layout;
233
- }
234
- parseRescueFrom(call, rescues, line) {
235
- const args = this.getCallArguments(call);
236
- if (args.length === 0)
237
- return;
238
- const exception = args[0].text;
239
- let handler = 'unknown';
240
- // Look for with: option
241
- for (const arg of args.slice(1)) {
242
- if (arg.type === 'hash' || arg.type === 'pair') {
243
- const pairs = arg.type === 'hash' ? getChildrenByType(arg, 'pair') : [arg];
244
- for (const pair of pairs) {
245
- const key = pair.child(0)?.text?.replace(/^:/, '');
246
- const value = pair.child(2);
247
- if (key === 'with' && value) {
248
- handler = value.text.replace(/^:/, '');
249
- }
250
- }
251
- }
252
- }
253
- rescues.push({ exception, handler, line });
254
- }
255
- parseMethod(methodNode, visibility) {
256
- const name = getMethodName(methodNode);
257
- if (!name)
258
- return null;
259
- // Skip singleton methods (def self.xxx)
260
- if (methodNode.text.includes('def self.'))
261
- return null;
262
- const action = {
263
- name,
264
- line: methodNode.startPosition.row + 1,
265
- visibility,
266
- parameters: getMethodParameters(methodNode),
267
- servicesCalled: [],
268
- modelsCalled: [],
269
- methodCalls: [],
270
- instanceVarAssignments: [],
271
- };
272
- // Analyze method body
273
- const bodyContent = methodNode.text;
274
- // Extract instance variable assignments: @var = value
275
- const ivarRegex = /@([a-z_][a-z0-9_]*)\s*=\s*([^\n]+)/gi;
276
- let ivarMatch;
277
- while ((ivarMatch = ivarRegex.exec(bodyContent)) !== null) {
278
- const varName = ivarMatch[1];
279
- const assignedValue = ivarMatch[2].trim().slice(0, 100); // Truncate long values
280
- // Try to detect model type from assignment
281
- let assignedType;
282
- // Pattern: Model.find/where/new/create/etc
283
- const modelMatch = assignedValue.match(/^([A-Z][a-zA-Z0-9]+)\.(find|find_by|find_by!|where|all|first|last|new|create|create!|build)/);
284
- if (modelMatch) {
285
- assignedType = modelMatch[1];
286
- }
287
- // Pattern: @parent.association (e.g., @company.users)
288
- const assocMatch = assignedValue.match(/^@([a-z_]+)\.([a-z_]+)/);
289
- if (assocMatch && !assignedType) {
290
- assignedType = `${assocMatch[1]}.${assocMatch[2]}`;
291
- }
292
- // Pattern: current_user, current_company, etc.
293
- const currentMatch = assignedValue.match(/^current_([a-z_]+)/);
294
- if (currentMatch && !assignedType) {
295
- assignedType = currentMatch[1].charAt(0).toUpperCase() + currentMatch[1].slice(1);
296
- }
297
- // Pattern: SomeService.call (service result)
298
- const serviceMatch = assignedValue.match(/^([A-Z][a-zA-Z0-9]+Service)\.(call|new|perform)/);
299
- if (serviceMatch && !assignedType) {
300
- assignedType = `Service:${serviceMatch[1]}`;
301
- }
302
- if (action.instanceVarAssignments) {
303
- action.instanceVarAssignments.push({
304
- name: varName,
305
- assignedType,
306
- assignedValue: assignedValue.length > 60 ? assignedValue.slice(0, 57) + '...' : assignedValue,
307
- });
308
- }
309
- }
310
- // Check render types
311
- if (bodyContent.includes('render json:') || bodyContent.includes('render :json')) {
312
- action.rendersJson = true;
313
- }
314
- if (bodyContent.includes('render') && !action.rendersJson) {
315
- action.rendersHtml = true;
316
- }
317
- // Extract redirects
318
- const redirectMatch = bodyContent.match(/redirect_to\s+([^,\n]+)/);
319
- if (redirectMatch) {
320
- action.redirectsTo = redirectMatch[1].trim();
321
- }
322
- // Check respond_to formats
323
- if (bodyContent.includes('respond_to')) {
324
- const formats = [];
325
- if (bodyContent.includes('format.html'))
326
- formats.push('html');
327
- if (bodyContent.includes('format.json'))
328
- formats.push('json');
329
- if (bodyContent.includes('format.xml'))
330
- formats.push('xml');
331
- if (bodyContent.includes('format.js'))
332
- formats.push('js');
333
- if (bodyContent.includes('format.csv'))
334
- formats.push('csv');
335
- if (bodyContent.includes('format.pdf'))
336
- formats.push('pdf');
337
- if (formats.length > 0) {
338
- action.respondsTo = formats;
339
- }
340
- }
341
- // Find service calls
342
- const serviceCalls = findNodes(methodNode, 'call');
343
- for (const call of serviceCalls) {
344
- const receiver = call.childForFieldName('receiver');
345
- const method = call.childForFieldName('method');
346
- if (receiver && method) {
347
- const receiverText = receiver.text;
348
- const methodText = method.text;
349
- // Service pattern: SomeService.call/new/perform
350
- if (receiverText.endsWith('Service') &&
351
- ['call', 'new', 'perform', 'execute'].includes(methodText)) {
352
- if (!action.servicesCalled.includes(receiverText)) {
353
- action.servicesCalled.push(receiverText);
354
- }
355
- }
356
- // Model pattern: User.find/where/create etc.
357
- const arMethods = [
358
- 'find',
359
- 'find_by',
360
- 'find_by!',
361
- 'where',
362
- 'all',
363
- 'first',
364
- 'last',
365
- 'create',
366
- 'create!',
367
- 'new',
368
- 'update',
369
- 'update!',
370
- 'destroy',
371
- 'delete',
372
- ];
373
- if (/^[A-Z][a-zA-Z]+$/.test(receiverText) && arMethods.includes(methodText)) {
374
- if (!['Rails', 'ActiveRecord', 'ActionController', 'ApplicationRecord'].includes(receiverText)) {
375
- if (!action.modelsCalled.includes(receiverText)) {
376
- action.modelsCalled.push(receiverText);
377
- }
378
- }
379
- }
380
- // Track all method calls
381
- action.methodCalls.push(`${receiverText}.${methodText}`);
382
- }
383
- else if (method && !receiver) {
384
- // Method call without receiver
385
- action.methodCalls.push(method.text);
386
- }
387
- }
388
- return action;
389
- }
390
- getCallArguments(call) {
391
- const args = call.childForFieldName('arguments');
392
- if (!args) {
393
- // Arguments might be direct children without parentheses
394
- const results = [];
395
- for (let i = 0; i < call.childCount; i++) {
396
- const child = call.child(i);
397
- if (child && !['identifier', '(', ')', ',', 'call'].includes(child.type)) {
398
- // Skip the method name and receiver
399
- if (child !== call.childForFieldName('method') &&
400
- child !== call.childForFieldName('receiver')) {
401
- results.push(child);
402
- }
403
- }
404
- }
405
- return results;
406
- }
407
- const results = [];
408
- for (let i = 0; i < args.childCount; i++) {
409
- const child = args.child(i);
410
- if (child && child.type !== '(' && child.type !== ')' && child.type !== ',') {
411
- results.push(child);
412
- }
413
- }
414
- return results;
415
- }
416
- extractArrayValues(node) {
417
- const values = [];
418
- if (node.type === 'array') {
419
- for (let i = 0; i < node.childCount; i++) {
420
- const child = node.child(i);
421
- if (child && child.type !== '[' && child.type !== ']' && child.type !== ',') {
422
- values.push(child.text.replace(/^:/, ''));
423
- }
424
- }
425
- }
426
- else {
427
- values.push(node.text.replace(/^:/, ''));
428
- }
429
- return values;
430
- }
431
- }
432
- // Standalone execution for testing
433
- async function main() {
434
- const targetPath = process.argv[2] || process.cwd();
435
- console.log(`Analyzing controllers in: ${targetPath}`);
436
- const analyzer = new RailsControllerAnalyzer(targetPath);
437
- const result = await analyzer.analyze();
438
- console.log('\n=== Rails Controllers Analysis ===\n');
439
- console.log(`Total controllers: ${result.controllers.length}`);
440
- console.log(`Total actions: ${result.totalActions}`);
441
- console.log(`Namespaces: ${result.namespaces.join(', ') || '(none)'}`);
442
- console.log(`Shared concerns: ${result.concerns.length}`);
443
- if (result.errors.length > 0) {
444
- console.log(`\n--- Errors (${result.errors.length}) ---`);
445
- for (const error of result.errors.slice(0, 5)) {
446
- console.log(` ❌ ${error}`);
447
- }
448
- if (result.errors.length > 5) {
449
- console.log(` ... and ${result.errors.length - 5} more`);
450
- }
451
- }
452
- console.log('\n--- Sample Controllers (first 10) ---');
453
- for (const controller of result.controllers.slice(0, 10)) {
454
- console.log(`\n 📁 ${controller.className} (${controller.filePath})`);
455
- console.log(` Parent: ${controller.parentClass}`);
456
- console.log(` Actions (${controller.actions.length}): ${controller.actions
457
- .map((a) => a.name)
458
- .slice(0, 5)
459
- .join(', ')}${controller.actions.length > 5 ? '...' : ''}`);
460
- if (controller.beforeActions.length > 0) {
461
- console.log(` Before: ${controller.beforeActions.map((f) => f.name).join(', ')}`);
462
- }
463
- if (controller.concerns.length > 0) {
464
- console.log(` Concerns: ${controller.concerns.join(', ')}`);
465
- }
466
- }
467
- // Summary of actions by visibility
468
- const publicActions = result.controllers.flatMap((c) => c.actions.filter((a) => a.visibility === 'public'));
469
- const privateActions = result.controllers.flatMap((c) => c.actions.filter((a) => a.visibility === 'private'));
470
- console.log('\n--- Action Visibility Summary ---');
471
- console.log(` Public: ${publicActions.length}`);
472
- console.log(` Private: ${privateActions.length}`);
473
- }
474
- // Run if executed directly
475
- const isMainModule = import.meta.url === `file://${process.argv[1]}`;
476
- if (isMainModule) {
477
- main().catch(console.error);
478
- }
@@ -1,44 +0,0 @@
1
- /**
2
- * Rails gRPC Service Analyzer using tree-sitter
3
- * tree-sitterを使用してgRPCサービスを解析する
4
- */
5
- export interface GrpcServiceInfo {
6
- name: string;
7
- filePath: string;
8
- className: string;
9
- parentClass: string;
10
- namespace?: string;
11
- protoService?: string;
12
- rpcs: RpcMethodInfo[];
13
- policies: string[];
14
- serializers: string[];
15
- concerns: string[];
16
- line: number;
17
- }
18
- export interface RpcMethodInfo {
19
- name: string;
20
- requestType?: string;
21
- responseType?: string;
22
- streaming?: 'none' | 'server' | 'client' | 'bidirectional';
23
- policyMethod?: string;
24
- modelsUsed: string[];
25
- servicesUsed: string[];
26
- line: number;
27
- }
28
- export interface RailsGrpcResult {
29
- services: GrpcServiceInfo[];
30
- totalRpcs: number;
31
- namespaces: string[];
32
- errors: string[];
33
- }
34
- export declare class RailsGrpcAnalyzer {
35
- private rootPath;
36
- private grpcDir;
37
- private services;
38
- private errors;
39
- constructor(rootPath: string);
40
- analyze(): Promise<RailsGrpcResult>;
41
- private parseServiceFile;
42
- private parseRpcMethod;
43
- private getCallArguments;
44
- }