@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,493 +0,0 @@
1
- /**
2
- * Rails Model 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, getChildrenByType, } from './ruby-parser.js';
9
- export class RailsModelAnalyzer {
10
- rootPath;
11
- modelsDir;
12
- models = [];
13
- errors = [];
14
- constructor(rootPath) {
15
- this.rootPath = rootPath;
16
- this.modelsDir = path.join(rootPath, 'app', 'models');
17
- }
18
- async analyze() {
19
- if (!fs.existsSync(this.modelsDir)) {
20
- return {
21
- models: [],
22
- totalAssociations: 0,
23
- totalValidations: 0,
24
- concerns: [],
25
- namespaces: [],
26
- errors: [`Models directory not found at ${this.modelsDir}`],
27
- };
28
- }
29
- const modelFiles = await glob('**/*.rb', {
30
- cwd: this.modelsDir,
31
- ignore: ['concerns/**', 'application_record.rb'],
32
- });
33
- for (const file of modelFiles) {
34
- const fullPath = path.join(this.modelsDir, file);
35
- try {
36
- const model = await this.parseModelFile(fullPath, file);
37
- if (model) {
38
- this.models.push(model);
39
- }
40
- }
41
- catch (error) {
42
- this.errors.push(`Error parsing ${file}: ${error}`);
43
- }
44
- }
45
- const concerns = [...new Set(this.models.flatMap((m) => m.concerns))];
46
- const namespaces = [
47
- ...new Set(this.models
48
- .map((m) => {
49
- const parts = m.filePath.split('/');
50
- return parts.length > 1 ? parts.slice(0, -1).join('/') : null;
51
- })
52
- .filter((n) => n !== null)),
53
- ];
54
- const totalAssociations = this.models.reduce((sum, m) => sum + m.associations.length, 0);
55
- const totalValidations = this.models.reduce((sum, m) => sum + m.validations.length, 0);
56
- return {
57
- models: this.models,
58
- totalAssociations,
59
- totalValidations,
60
- concerns,
61
- namespaces,
62
- errors: this.errors,
63
- };
64
- }
65
- async parseModelFile(filePath, relativePath) {
66
- const tree = await parseRubyFile(filePath);
67
- const rootNode = tree.rootNode;
68
- // Find class definition
69
- const classNodes = findNodes(rootNode, 'class');
70
- if (classNodes.length === 0)
71
- return null;
72
- const classNode = classNodes[0];
73
- const className = getClassName(classNode);
74
- const parentClass = getSuperclass(classNode);
75
- if (!className)
76
- return null;
77
- // Skip non-ActiveRecord models
78
- if (parentClass && !this.isActiveRecordModel(parentClass)) {
79
- // Still include it but mark as non-AR
80
- }
81
- const model = {
82
- name: className.replace(/.*::/, ''), // Remove namespace prefix
83
- filePath: relativePath,
84
- className,
85
- parentClass: parentClass || 'ApplicationRecord',
86
- associations: [],
87
- validations: [],
88
- callbacks: [],
89
- scopes: [],
90
- concerns: [],
91
- enums: [],
92
- attributes: [],
93
- classMethodsCount: 0,
94
- instanceMethodsCount: 0,
95
- line: classNode.startPosition.row + 1,
96
- };
97
- // Parse table name if explicitly set
98
- model.tableName = this.parseTableName(classNode);
99
- // Find all method calls for associations, validations, etc.
100
- const calls = findNodes(classNode, 'call');
101
- for (const call of calls) {
102
- const methodNode = call.childForFieldName('method');
103
- if (!methodNode)
104
- continue;
105
- const methodName = methodNode.text;
106
- const line = call.startPosition.row + 1;
107
- // Associations
108
- if (['belongs_to', 'has_one', 'has_many', 'has_and_belongs_to_many'].includes(methodName)) {
109
- this.parseAssociation(call, methodName, model.associations, line);
110
- }
111
- // Validations
112
- else if (methodName.startsWith('validates') || methodName === 'validate') {
113
- this.parseValidation(call, methodName, model.validations, line);
114
- }
115
- // Callbacks
116
- else if (this.isCallback(methodName)) {
117
- this.parseCallback(call, methodName, model.callbacks, line);
118
- }
119
- // Scopes
120
- else if (methodName === 'scope') {
121
- this.parseScope(call, model.scopes, line);
122
- }
123
- // Concerns
124
- else if (methodName === 'include') {
125
- this.parseInclude(call, model.concerns);
126
- }
127
- // Enums
128
- else if (methodName === 'enum') {
129
- this.parseEnum(call, model.enums, line);
130
- }
131
- // Attributes
132
- else if (methodName === 'attribute') {
133
- this.parseAttribute(call, model.attributes, line);
134
- }
135
- }
136
- // Count methods
137
- const methods = findNodes(classNode, 'method');
138
- const singletonMethods = findNodes(classNode, 'singleton_method');
139
- model.instanceMethodsCount = methods.length;
140
- model.classMethodsCount = singletonMethods.length;
141
- return model;
142
- }
143
- isActiveRecordModel(parentClass) {
144
- const arBases = ['ApplicationRecord', 'ActiveRecord::Base', 'ActiveRecord'];
145
- return arBases.some((base) => parentClass.includes(base));
146
- }
147
- parseTableName(classNode) {
148
- const calls = findNodes(classNode, 'call');
149
- for (const call of calls) {
150
- const methodNode = call.childForFieldName('method');
151
- if (methodNode?.text === 'table_name=') {
152
- const args = this.getCallArguments(call);
153
- if (args.length > 0) {
154
- return args[0].text.replace(/^["']|["']$/g, '');
155
- }
156
- }
157
- }
158
- // Also check for self.table_name =
159
- const assignments = findNodes(classNode, 'assignment');
160
- for (const assignment of assignments) {
161
- const left = assignment.child(0);
162
- if (left?.text?.includes('table_name')) {
163
- const right = assignment.child(2);
164
- if (right) {
165
- return right.text.replace(/^["']|["']$/g, '');
166
- }
167
- }
168
- }
169
- return undefined;
170
- }
171
- parseAssociation(call, type, associations, line) {
172
- const args = this.getCallArguments(call);
173
- if (args.length === 0)
174
- return;
175
- const nameArg = args[0];
176
- const assocName = nameArg.text.replace(/^:/, '');
177
- const association = {
178
- type,
179
- name: assocName,
180
- line,
181
- };
182
- // Parse options
183
- for (const arg of args.slice(1)) {
184
- if (arg.type === 'hash') {
185
- const pairs = getChildrenByType(arg, 'pair');
186
- for (const pair of pairs) {
187
- const key = pair.child(0)?.text?.replace(/^:/, '');
188
- const value = pair.child(2);
189
- if (!key || !value)
190
- continue;
191
- switch (key) {
192
- case 'class_name':
193
- association.className = value.text.replace(/^["']|["']$/g, '');
194
- break;
195
- case 'foreign_key':
196
- association.foreignKey = value.text.replace(/^["']|["']$/g, '').replace(/^:/, '');
197
- break;
198
- case 'through':
199
- association.through = value.text.replace(/^:/, '');
200
- break;
201
- case 'polymorphic':
202
- association.polymorphic = value.text === 'true';
203
- break;
204
- case 'dependent':
205
- association.dependent = value.text.replace(/^:/, '');
206
- break;
207
- case 'optional':
208
- association.optional = value.text === 'true';
209
- break;
210
- }
211
- }
212
- }
213
- }
214
- associations.push(association);
215
- }
216
- parseValidation(call, methodName, validations, line) {
217
- const args = this.getCallArguments(call);
218
- if (args.length === 0)
219
- return;
220
- // Extract attributes being validated
221
- const attributes = [];
222
- const options = {};
223
- let validationType = methodName;
224
- for (const arg of args) {
225
- if (arg.type === 'simple_symbol' || arg.type === 'symbol') {
226
- attributes.push(arg.text.replace(/^:/, ''));
227
- }
228
- else if (arg.type === 'hash') {
229
- const pairs = getChildrenByType(arg, 'pair');
230
- for (const pair of pairs) {
231
- const key = pair.child(0)?.text?.replace(/^:/, '');
232
- const value = pair.child(2);
233
- if (key && value) {
234
- // Validation type is usually the key (presence, uniqueness, etc.)
235
- if ([
236
- 'presence',
237
- 'uniqueness',
238
- 'numericality',
239
- 'length',
240
- 'format',
241
- 'inclusion',
242
- 'exclusion',
243
- 'acceptance',
244
- 'confirmation',
245
- ].includes(key)) {
246
- validationType = key;
247
- options[key] = value.text;
248
- }
249
- else {
250
- options[key] = value.text;
251
- }
252
- }
253
- }
254
- }
255
- }
256
- if (attributes.length > 0 || methodName === 'validate') {
257
- validations.push({
258
- type: validationType,
259
- attributes,
260
- options: Object.keys(options).length > 0 ? options : undefined,
261
- line,
262
- });
263
- }
264
- }
265
- isCallback(methodName) {
266
- const callbacks = [
267
- 'before_validation',
268
- 'after_validation',
269
- 'before_save',
270
- 'around_save',
271
- 'after_save',
272
- 'before_create',
273
- 'around_create',
274
- 'after_create',
275
- 'before_update',
276
- 'around_update',
277
- 'after_update',
278
- 'before_destroy',
279
- 'around_destroy',
280
- 'after_destroy',
281
- 'after_commit',
282
- 'after_rollback',
283
- 'after_initialize',
284
- 'after_find',
285
- 'after_touch',
286
- ];
287
- return callbacks.includes(methodName);
288
- }
289
- parseCallback(call, type, callbacks, line) {
290
- const args = this.getCallArguments(call);
291
- if (args.length === 0)
292
- return;
293
- const methodArg = args[0];
294
- const methodName = methodArg.text.replace(/^:/, '');
295
- const callback = {
296
- type,
297
- method: methodName,
298
- line,
299
- };
300
- // Check for conditions
301
- for (const arg of args.slice(1)) {
302
- if (arg.type === 'hash') {
303
- const pairs = getChildrenByType(arg, 'pair');
304
- for (const pair of pairs) {
305
- const key = pair.child(0)?.text?.replace(/^:/, '');
306
- const value = pair.child(2);
307
- if (key && value && ['if', 'unless'].includes(key)) {
308
- callback.conditions = `${key}: ${value.text}`;
309
- }
310
- }
311
- }
312
- }
313
- callbacks.push(callback);
314
- }
315
- parseScope(call, scopes, line) {
316
- const args = this.getCallArguments(call);
317
- if (args.length === 0)
318
- return;
319
- const nameArg = args[0];
320
- const scopeName = nameArg.text.replace(/^:/, '');
321
- const isLambda = args.length > 1 && (args[1].type === 'lambda' || args[1].text.includes('->'));
322
- scopes.push({
323
- name: scopeName,
324
- lambda: isLambda,
325
- line,
326
- });
327
- }
328
- parseInclude(call, concerns) {
329
- const args = this.getCallArguments(call);
330
- for (const arg of args) {
331
- if (arg.type === 'constant' || arg.type === 'scope_resolution') {
332
- concerns.push(arg.text);
333
- }
334
- }
335
- }
336
- parseEnum(call, enums, line) {
337
- const args = this.getCallArguments(call);
338
- if (args.length === 0)
339
- return;
340
- // enum status: { draft: 0, published: 1 }
341
- // or enum :status, { draft: 0, published: 1 }
342
- for (const arg of args) {
343
- if (arg.type === 'hash') {
344
- const pairs = getChildrenByType(arg, 'pair');
345
- for (const pair of pairs) {
346
- const key = pair.child(0)?.text?.replace(/^:/, '');
347
- const value = pair.child(2);
348
- if (key && value && value.type === 'hash') {
349
- const enumValues = [];
350
- const valuePairs = getChildrenByType(value, 'pair');
351
- for (const vp of valuePairs) {
352
- const vKey = vp.child(0)?.text?.replace(/^:/, '');
353
- if (vKey)
354
- enumValues.push(vKey);
355
- }
356
- enums.push({
357
- name: key,
358
- values: enumValues,
359
- line,
360
- });
361
- }
362
- else if (key && value && value.type === 'array') {
363
- // enum status: [:draft, :published]
364
- const enumValues = [];
365
- for (let i = 0; i < value.childCount; i++) {
366
- const child = value.child(i);
367
- if (child && child.type !== '[' && child.type !== ']' && child.type !== ',') {
368
- enumValues.push(child.text.replace(/^:/, ''));
369
- }
370
- }
371
- enums.push({
372
- name: key,
373
- values: enumValues,
374
- line,
375
- });
376
- }
377
- }
378
- }
379
- }
380
- }
381
- parseAttribute(call, attributes, line) {
382
- const args = this.getCallArguments(call);
383
- if (args.length === 0)
384
- return;
385
- const nameArg = args[0];
386
- const attrName = nameArg.text.replace(/^:/, '');
387
- const attr = {
388
- name: attrName,
389
- line,
390
- };
391
- // Parse type and default
392
- if (args.length > 1) {
393
- const typeArg = args[1];
394
- attr.type = typeArg.text.replace(/^:/, '');
395
- }
396
- for (const arg of args) {
397
- if (arg.type === 'hash') {
398
- const pairs = getChildrenByType(arg, 'pair');
399
- for (const pair of pairs) {
400
- const key = pair.child(0)?.text?.replace(/^:/, '');
401
- const value = pair.child(2);
402
- if (key === 'default' && value) {
403
- attr.default = value.text;
404
- }
405
- }
406
- }
407
- }
408
- attributes.push(attr);
409
- }
410
- getCallArguments(call) {
411
- const args = call.childForFieldName('arguments');
412
- if (!args) {
413
- const results = [];
414
- for (let i = 0; i < call.childCount; i++) {
415
- const child = call.child(i);
416
- if (child && !['identifier', '(', ')', ',', 'call'].includes(child.type)) {
417
- if (child !== call.childForFieldName('method') &&
418
- child !== call.childForFieldName('receiver')) {
419
- results.push(child);
420
- }
421
- }
422
- }
423
- return results;
424
- }
425
- const results = [];
426
- for (let i = 0; i < args.childCount; i++) {
427
- const child = args.child(i);
428
- if (child && child.type !== '(' && child.type !== ')' && child.type !== ',') {
429
- results.push(child);
430
- }
431
- }
432
- return results;
433
- }
434
- }
435
- // Standalone execution for testing
436
- async function main() {
437
- const targetPath = process.argv[2] || process.cwd();
438
- console.log(`Analyzing models in: ${targetPath}`);
439
- const analyzer = new RailsModelAnalyzer(targetPath);
440
- const result = await analyzer.analyze();
441
- console.log('\n=== Rails Models Analysis ===\n');
442
- console.log(`Total models: ${result.models.length}`);
443
- console.log(`Total associations: ${result.totalAssociations}`);
444
- console.log(`Total validations: ${result.totalValidations}`);
445
- console.log(`Shared concerns: ${result.concerns.length}`);
446
- console.log(`Namespaces: ${result.namespaces.join(', ') || '(none)'}`);
447
- if (result.errors.length > 0) {
448
- console.log(`\n--- Errors (${result.errors.length}) ---`);
449
- for (const error of result.errors.slice(0, 5)) {
450
- console.log(` ❌ ${error}`);
451
- }
452
- if (result.errors.length > 5) {
453
- console.log(` ... and ${result.errors.length - 5} more`);
454
- }
455
- }
456
- console.log('\n--- Sample Models (first 15) ---');
457
- for (const model of result.models.slice(0, 15)) {
458
- console.log(`\n 📦 ${model.className} (${model.filePath})`);
459
- console.log(` Parent: ${model.parentClass}`);
460
- if (model.associations.length > 0) {
461
- const assocs = model.associations.slice(0, 3).map((a) => `${a.type} :${a.name}`);
462
- console.log(` Associations: ${assocs.join(', ')}${model.associations.length > 3 ? '...' : ''}`);
463
- }
464
- if (model.validations.length > 0) {
465
- console.log(` Validations: ${model.validations.length}`);
466
- }
467
- if (model.scopes.length > 0) {
468
- const scopeNames = model.scopes.slice(0, 3).map((s) => s.name);
469
- console.log(` Scopes: ${scopeNames.join(', ')}${model.scopes.length > 3 ? '...' : ''}`);
470
- }
471
- if (model.enums.length > 0) {
472
- const enumInfo = model.enums.map((e) => `${e.name}(${e.values.length})`);
473
- console.log(` Enums: ${enumInfo.join(', ')}`);
474
- }
475
- if (model.concerns.length > 0) {
476
- console.log(` Concerns: ${model.concerns.slice(0, 3).join(', ')}${model.concerns.length > 3 ? '...' : ''}`);
477
- }
478
- }
479
- // Association type summary
480
- const allAssociations = result.models.flatMap((m) => m.associations);
481
- const belongsTo = allAssociations.filter((a) => a.type === 'belongs_to').length;
482
- const hasMany = allAssociations.filter((a) => a.type === 'has_many').length;
483
- const hasOne = allAssociations.filter((a) => a.type === 'has_one').length;
484
- console.log('\n--- Association Summary ---');
485
- console.log(` belongs_to: ${belongsTo}`);
486
- console.log(` has_many: ${hasMany}`);
487
- console.log(` has_one: ${hasOne}`);
488
- }
489
- // Run if executed directly
490
- const isMainModule = import.meta.url === `file://${process.argv[1]}`;
491
- if (isMainModule) {
492
- main().catch(console.error);
493
- }
@@ -1,41 +0,0 @@
1
- export interface ReactComponentMapping {
2
- name: string;
3
- entryFile?: string;
4
- sourceFile?: string;
5
- importPath?: string;
6
- ssr: boolean;
7
- usedIn: ReactComponentUsage[];
8
- }
9
- export interface ReactComponentUsage {
10
- viewPath: string;
11
- controller: string;
12
- action: string;
13
- propsVar?: string;
14
- line?: number;
15
- pattern: ReactMountPattern;
16
- }
17
- export type ReactMountPattern = 'data-react-component' | 'render_react_component' | 'react_component' | 'redux_store' | 'stimulus-reflex' | 'turbo-frame-react';
18
- export interface ReactAnalysisResult {
19
- components: ReactComponentMapping[];
20
- entryPoints: EntryPointInfo[];
21
- detectedPaths: DetectedPaths;
22
- summary: {
23
- totalComponents: number;
24
- totalEntryPoints: number;
25
- ssrComponents: number;
26
- clientComponents: number;
27
- };
28
- }
29
- export interface EntryPointInfo {
30
- file: string;
31
- fullPath: string;
32
- componentName: string;
33
- imports: string[];
34
- selector?: string;
35
- }
36
- export interface DetectedPaths {
37
- entryDirs: string[];
38
- componentDirs: string[];
39
- integrationPattern: 'react-rails' | 'react_on_rails' | 'webpacker' | 'vite' | 'custom' | 'unknown';
40
- }
41
- export declare function analyzeReactComponents(rootPath: string): Promise<ReactAnalysisResult>;