magector 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +627 -0
- package/config/mcp-config.json +13 -0
- package/package.json +53 -0
- package/src/binary.js +66 -0
- package/src/cli.js +203 -0
- package/src/init.js +293 -0
- package/src/magento-patterns.js +563 -0
- package/src/mcp-server.js +915 -0
- package/src/model.js +127 -0
- package/src/templates/claude-md.js +47 -0
- package/src/templates/cursorrules.js +45 -0
- package/src/validation/accuracy-calculator.js +397 -0
- package/src/validation/benchmark.js +355 -0
- package/src/validation/test-data-generator.js +672 -0
- package/src/validation/test-queries.js +326 -0
- package/src/validation/validator.js +302 -0
|
@@ -0,0 +1,563 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Magento-specific code patterns for high-precision indexing
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
// Magento file type detection based on path
|
|
6
|
+
export const MAGENTO_FILE_TYPES = {
|
|
7
|
+
// PHP patterns
|
|
8
|
+
'Controller': /\/Controller\/.*\.php$/,
|
|
9
|
+
'Block': /\/Block\/.*\.php$/,
|
|
10
|
+
'Model': /\/Model\/(?!ResourceModel).*\.php$/,
|
|
11
|
+
'ResourceModel': /\/Model\/ResourceModel\/.*\.php$/,
|
|
12
|
+
'Collection': /\/Collection\.php$/,
|
|
13
|
+
'Repository': /Repository\.php$/,
|
|
14
|
+
'RepositoryInterface': /RepositoryInterface\.php$/,
|
|
15
|
+
'Api': /\/Api\/.*Interface\.php$/,
|
|
16
|
+
'ApiData': /\/Api\/Data\/.*\.php$/,
|
|
17
|
+
'Plugin': /\/Plugin\/.*\.php$/,
|
|
18
|
+
'Observer': /\/Observer\/.*\.php$/,
|
|
19
|
+
'Helper': /\/Helper\/.*\.php$/,
|
|
20
|
+
'Setup': /\/Setup\/.*\.php$/,
|
|
21
|
+
'Console': /\/Console\/Command\/.*\.php$/,
|
|
22
|
+
'Cron': /\/Cron\/.*\.php$/,
|
|
23
|
+
'ViewModel': /\/ViewModel\/.*\.php$/,
|
|
24
|
+
'GraphQlResolver': /\/Model\/Resolver\/.*\.php$/,
|
|
25
|
+
'DataProvider': /\/DataProvider\/.*\.php$/,
|
|
26
|
+
'UiComponent': /\/Ui\/.*\.php$/,
|
|
27
|
+
|
|
28
|
+
// XML config patterns
|
|
29
|
+
'di.xml': /\/etc\/.*di\.xml$/,
|
|
30
|
+
'events.xml': /\/etc\/.*events\.xml$/,
|
|
31
|
+
'routes.xml': /\/etc\/.*routes\.xml$/,
|
|
32
|
+
'webapi.xml': /\/etc\/webapi\.xml$/,
|
|
33
|
+
'system.xml': /\/etc\/adminhtml\/system\.xml$/,
|
|
34
|
+
'config.xml': /\/etc\/config\.xml$/,
|
|
35
|
+
'acl.xml': /\/etc\/acl\.xml$/,
|
|
36
|
+
'crontab.xml': /\/etc\/crontab\.xml$/,
|
|
37
|
+
'module.xml': /\/etc\/module\.xml$/,
|
|
38
|
+
'db_schema.xml': /\/etc\/db_schema\.xml$/,
|
|
39
|
+
'indexer.xml': /\/etc\/indexer\.xml$/,
|
|
40
|
+
'mview.xml': /\/etc\/mview\.xml$/,
|
|
41
|
+
'widget.xml': /\/etc\/widget\.xml$/,
|
|
42
|
+
'layout.xml': /\/layout\/.*\.xml$/,
|
|
43
|
+
'ui_component.xml': /\/ui_component\/.*\.xml$/,
|
|
44
|
+
|
|
45
|
+
// Frontend
|
|
46
|
+
'template': /\.phtml$/,
|
|
47
|
+
'less': /\.less$/,
|
|
48
|
+
'requirejs': /requirejs-config\.js$/,
|
|
49
|
+
'knockout': /\/view\/.*\.html$/,
|
|
50
|
+
|
|
51
|
+
// GraphQL
|
|
52
|
+
'graphql_schema': /\.graphqls$/
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
// Magento-specific PHP patterns
|
|
56
|
+
export const PHP_PATTERNS = {
|
|
57
|
+
// Plugin methods
|
|
58
|
+
plugin: {
|
|
59
|
+
before: /public\s+function\s+(before\w+)\s*\(/g,
|
|
60
|
+
after: /public\s+function\s+(after\w+)\s*\(/g,
|
|
61
|
+
around: /public\s+function\s+(around\w+)\s*\(/g
|
|
62
|
+
},
|
|
63
|
+
|
|
64
|
+
// Controller actions
|
|
65
|
+
controller: {
|
|
66
|
+
execute: /public\s+function\s+execute\s*\(\s*\)/,
|
|
67
|
+
resultFactory: /\$this->resultFactory->create\s*\(\s*ResultFactory::TYPE_(\w+)/g
|
|
68
|
+
},
|
|
69
|
+
|
|
70
|
+
// Repository patterns
|
|
71
|
+
repository: {
|
|
72
|
+
getById: /public\s+function\s+getById\s*\(/,
|
|
73
|
+
getList: /public\s+function\s+getList\s*\(/,
|
|
74
|
+
save: /public\s+function\s+save\s*\(/,
|
|
75
|
+
delete: /public\s+function\s+delete\s*\(/,
|
|
76
|
+
deleteById: /public\s+function\s+deleteById\s*\(/
|
|
77
|
+
},
|
|
78
|
+
|
|
79
|
+
// Model patterns
|
|
80
|
+
model: {
|
|
81
|
+
beforeSave: /protected\s+function\s+_beforeSave\s*\(/,
|
|
82
|
+
afterSave: /protected\s+function\s+_afterSave\s*\(/,
|
|
83
|
+
beforeLoad: /protected\s+function\s+_beforeLoad\s*\(/,
|
|
84
|
+
afterLoad: /protected\s+function\s+_afterLoad\s*\(/,
|
|
85
|
+
construct: /protected\s+function\s+_construct\s*\(/
|
|
86
|
+
},
|
|
87
|
+
|
|
88
|
+
// Observer
|
|
89
|
+
observer: {
|
|
90
|
+
execute: /public\s+function\s+execute\s*\(\s*Observer\s+\$observer\s*\)/
|
|
91
|
+
},
|
|
92
|
+
|
|
93
|
+
// Block patterns
|
|
94
|
+
block: {
|
|
95
|
+
toHtml: /protected\s+function\s+_toHtml\s*\(/,
|
|
96
|
+
prepareLayout: /protected\s+function\s+_prepareLayout\s*\(/,
|
|
97
|
+
beforeToHtml: /protected\s+function\s+_beforeToHtml\s*\(/
|
|
98
|
+
},
|
|
99
|
+
|
|
100
|
+
// GraphQL Resolver
|
|
101
|
+
resolver: {
|
|
102
|
+
resolve: /public\s+function\s+resolve\s*\(\s*Field\s+\$field/
|
|
103
|
+
},
|
|
104
|
+
|
|
105
|
+
// Setup scripts
|
|
106
|
+
setup: {
|
|
107
|
+
install: /public\s+function\s+install\s*\(/,
|
|
108
|
+
upgrade: /public\s+function\s+upgrade\s*\(/,
|
|
109
|
+
apply: /public\s+function\s+apply\s*\(/
|
|
110
|
+
},
|
|
111
|
+
|
|
112
|
+
// Console command
|
|
113
|
+
console: {
|
|
114
|
+
configure: /protected\s+function\s+configure\s*\(/,
|
|
115
|
+
execute: /protected\s+function\s+execute\s*\(\s*InputInterface/
|
|
116
|
+
},
|
|
117
|
+
|
|
118
|
+
// Data provider
|
|
119
|
+
dataProvider: {
|
|
120
|
+
getData: /public\s+function\s+getData\s*\(/,
|
|
121
|
+
getMeta: /public\s+function\s+getMeta\s*\(/
|
|
122
|
+
}
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
// XML config patterns
|
|
126
|
+
export const XML_PATTERNS = {
|
|
127
|
+
// di.xml
|
|
128
|
+
di: {
|
|
129
|
+
preference: /<preference\s+for="([^"]+)"\s+type="([^"]+)"/g,
|
|
130
|
+
virtualType: /<virtualType\s+name="([^"]+)"\s+type="([^"]+)"/g,
|
|
131
|
+
plugin: /<plugin\s+name="([^"]+)"[^>]*type="([^"]+)"/g,
|
|
132
|
+
type: /<type\s+name="([^"]+)"/g,
|
|
133
|
+
argument: /<argument\s+name="([^"]+)"\s+xsi:type="([^"]+)"/g
|
|
134
|
+
},
|
|
135
|
+
|
|
136
|
+
// events.xml
|
|
137
|
+
events: {
|
|
138
|
+
event: /<event\s+name="([^"]+)"/g,
|
|
139
|
+
observer: /<observer\s+name="([^"]+)"[^>]*instance="([^"]+)"/g
|
|
140
|
+
},
|
|
141
|
+
|
|
142
|
+
// layout.xml
|
|
143
|
+
layout: {
|
|
144
|
+
block: /<block\s+[^>]*class="([^"]+)"/g,
|
|
145
|
+
container: /<container\s+name="([^"]+)"/g,
|
|
146
|
+
referenceBlock: /<referenceBlock\s+name="([^"]+)"/g,
|
|
147
|
+
referenceContainer: /<referenceContainer\s+name="([^"]+)"/g,
|
|
148
|
+
uiComponent: /<uiComponent\s+name="([^"]+)"/g
|
|
149
|
+
},
|
|
150
|
+
|
|
151
|
+
// webapi.xml
|
|
152
|
+
webapi: {
|
|
153
|
+
route: /<route\s+url="([^"]+)"\s+method="([^"]+)"/g,
|
|
154
|
+
service: /<service\s+class="([^"]+)"\s+method="([^"]+)"/g,
|
|
155
|
+
resource: /<resource\s+ref="([^"]+)"/g
|
|
156
|
+
},
|
|
157
|
+
|
|
158
|
+
// system.xml
|
|
159
|
+
system: {
|
|
160
|
+
section: /<section\s+id="([^"]+)"/g,
|
|
161
|
+
group: /<group\s+id="([^"]+)"/g,
|
|
162
|
+
field: /<field\s+id="([^"]+)"/g,
|
|
163
|
+
sourceModel: /<source_model>([^<]+)<\/source_model>/g,
|
|
164
|
+
backendModel: /<backend_model>([^<]+)<\/backend_model>/g
|
|
165
|
+
},
|
|
166
|
+
|
|
167
|
+
// db_schema.xml
|
|
168
|
+
dbSchema: {
|
|
169
|
+
table: /<table\s+name="([^"]+)"/g,
|
|
170
|
+
column: /<column\s+[^>]*name="([^"]+)"/g,
|
|
171
|
+
constraint: /<constraint\s+[^>]*referenceId="([^"]+)"/g,
|
|
172
|
+
index: /<index\s+[^>]*referenceId="([^"]+)"/g
|
|
173
|
+
},
|
|
174
|
+
|
|
175
|
+
// acl.xml
|
|
176
|
+
acl: {
|
|
177
|
+
resource: /<resource\s+id="([^"]+)"/g
|
|
178
|
+
},
|
|
179
|
+
|
|
180
|
+
// crontab.xml
|
|
181
|
+
crontab: {
|
|
182
|
+
job: /<job\s+name="([^"]+)"[^>]*instance="([^"]+)"/g,
|
|
183
|
+
schedule: /<schedule>([^<]+)<\/schedule>/g
|
|
184
|
+
}
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
// GraphQL schema patterns
|
|
188
|
+
export const GRAPHQL_PATTERNS = {
|
|
189
|
+
type: /type\s+(\w+)\s*(?:implements\s+[\w\s,&]+)?\s*\{/g,
|
|
190
|
+
interface: /interface\s+(\w+)\s*\{/g,
|
|
191
|
+
input: /input\s+(\w+)\s*\{/g,
|
|
192
|
+
enum: /enum\s+(\w+)\s*\{/g,
|
|
193
|
+
query: /(\w+)\s*\([^)]*\)\s*:\s*([\w\[\]!]+)/g,
|
|
194
|
+
mutation: /(\w+)\s*\([^)]*\)\s*:\s*([\w\[\]!]+)/g,
|
|
195
|
+
resolver: /@resolver\s*\(\s*class:\s*"([^"]+)"/g
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
// Magento module areas
|
|
199
|
+
export const AREAS = ['frontend', 'adminhtml', 'webapi_rest', 'webapi_soap', 'graphql', 'crontab', 'global'];
|
|
200
|
+
|
|
201
|
+
// Common Magento interfaces
|
|
202
|
+
export const CORE_INTERFACES = {
|
|
203
|
+
'Magento\\Framework\\App\\ActionInterface': 'Controller',
|
|
204
|
+
'Magento\\Framework\\View\\Element\\BlockInterface': 'Block',
|
|
205
|
+
'Magento\\Framework\\Model\\AbstractModel': 'Model',
|
|
206
|
+
'Magento\\Framework\\Model\\ResourceModel\\AbstractResource': 'ResourceModel',
|
|
207
|
+
'Magento\\Framework\\Data\\Collection\\AbstractDb': 'Collection',
|
|
208
|
+
'Magento\\Framework\\Event\\ObserverInterface': 'Observer',
|
|
209
|
+
'Magento\\Framework\\Interception\\InterceptorInterface': 'Plugin',
|
|
210
|
+
'Magento\\Framework\\Api\\SearchCriteriaInterface': 'SearchCriteria',
|
|
211
|
+
'Magento\\Framework\\Api\\ExtensibleDataInterface': 'DataInterface',
|
|
212
|
+
'Magento\\Framework\\GraphQl\\Query\\ResolverInterface': 'GraphQlResolver',
|
|
213
|
+
'Magento\\Framework\\Setup\\InstallSchemaInterface': 'InstallSchema',
|
|
214
|
+
'Magento\\Framework\\Setup\\UpgradeSchemaInterface': 'UpgradeSchema',
|
|
215
|
+
'Magento\\Framework\\Setup\\Patch\\DataPatchInterface': 'DataPatch',
|
|
216
|
+
'Magento\\Framework\\Setup\\Patch\\SchemaPatchInterface': 'SchemaPatch',
|
|
217
|
+
'Magento\\Framework\\Console\\Cli': 'ConsoleCommand',
|
|
218
|
+
'Magento\\Ui\\DataProvider\\AbstractDataProvider': 'UiDataProvider'
|
|
219
|
+
};
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* Detect Magento-specific file type from path
|
|
223
|
+
*/
|
|
224
|
+
export function detectMagentoFileType(filePath) {
|
|
225
|
+
for (const [type, pattern] of Object.entries(MAGENTO_FILE_TYPES)) {
|
|
226
|
+
if (pattern.test(filePath)) {
|
|
227
|
+
return type;
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
return null;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* Extract Magento-specific metadata from PHP content
|
|
235
|
+
*/
|
|
236
|
+
export function extractPhpMagentoMetadata(content, filePath) {
|
|
237
|
+
const metadata = {
|
|
238
|
+
magentoType: detectMagentoFileType(filePath),
|
|
239
|
+
patterns: []
|
|
240
|
+
};
|
|
241
|
+
|
|
242
|
+
// Detect plugin methods
|
|
243
|
+
for (const [method, pattern] of Object.entries(PHP_PATTERNS.plugin)) {
|
|
244
|
+
const matches = [...content.matchAll(pattern)];
|
|
245
|
+
if (matches.length > 0) {
|
|
246
|
+
metadata.isPlugin = true;
|
|
247
|
+
metadata.pluginMethods = matches.map(m => ({ type: method, name: m[1] }));
|
|
248
|
+
metadata.patterns.push('plugin');
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// Detect controller
|
|
253
|
+
if (PHP_PATTERNS.controller.execute.test(content)) {
|
|
254
|
+
metadata.isController = true;
|
|
255
|
+
metadata.patterns.push('controller');
|
|
256
|
+
|
|
257
|
+
const resultTypes = [...content.matchAll(PHP_PATTERNS.controller.resultFactory)];
|
|
258
|
+
if (resultTypes.length > 0) {
|
|
259
|
+
metadata.resultTypes = resultTypes.map(m => m[1]);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
// Detect repository pattern
|
|
264
|
+
const repoMethods = [];
|
|
265
|
+
for (const [method, pattern] of Object.entries(PHP_PATTERNS.repository)) {
|
|
266
|
+
if (pattern.test(content)) {
|
|
267
|
+
repoMethods.push(method);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
if (repoMethods.length >= 2) {
|
|
271
|
+
metadata.isRepository = true;
|
|
272
|
+
metadata.repositoryMethods = repoMethods;
|
|
273
|
+
metadata.patterns.push('repository');
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
// Detect model hooks
|
|
277
|
+
const modelHooks = [];
|
|
278
|
+
for (const [hook, pattern] of Object.entries(PHP_PATTERNS.model)) {
|
|
279
|
+
if (pattern.test(content)) {
|
|
280
|
+
modelHooks.push(hook);
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
if (modelHooks.length > 0) {
|
|
284
|
+
metadata.isModel = true;
|
|
285
|
+
metadata.modelHooks = modelHooks;
|
|
286
|
+
metadata.patterns.push('model');
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
// Detect observer
|
|
290
|
+
if (PHP_PATTERNS.observer.execute.test(content)) {
|
|
291
|
+
metadata.isObserver = true;
|
|
292
|
+
metadata.patterns.push('observer');
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
// Detect block
|
|
296
|
+
for (const [method, pattern] of Object.entries(PHP_PATTERNS.block)) {
|
|
297
|
+
if (pattern.test(content)) {
|
|
298
|
+
metadata.isBlock = true;
|
|
299
|
+
metadata.patterns.push('block');
|
|
300
|
+
break;
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
// Detect GraphQL resolver
|
|
305
|
+
if (PHP_PATTERNS.resolver.resolve.test(content)) {
|
|
306
|
+
metadata.isResolver = true;
|
|
307
|
+
metadata.patterns.push('graphql_resolver');
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
// Detect setup script
|
|
311
|
+
for (const [type, pattern] of Object.entries(PHP_PATTERNS.setup)) {
|
|
312
|
+
if (pattern.test(content)) {
|
|
313
|
+
metadata.isSetup = true;
|
|
314
|
+
metadata.setupType = type;
|
|
315
|
+
metadata.patterns.push('setup');
|
|
316
|
+
break;
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
// Detect console command
|
|
321
|
+
if (PHP_PATTERNS.console.configure.test(content)) {
|
|
322
|
+
metadata.isConsoleCommand = true;
|
|
323
|
+
metadata.patterns.push('console');
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
// Detect data provider
|
|
327
|
+
if (PHP_PATTERNS.dataProvider.getData.test(content)) {
|
|
328
|
+
metadata.isDataProvider = true;
|
|
329
|
+
metadata.patterns.push('data_provider');
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
// Extract injected dependencies
|
|
333
|
+
const constructorMatch = content.match(/function\s+__construct\s*\(([^)]*)\)/s);
|
|
334
|
+
if (constructorMatch) {
|
|
335
|
+
const deps = [];
|
|
336
|
+
const depRegex = /([\w\\]+(?:Interface)?)\s+\$(\w+)/g;
|
|
337
|
+
let match;
|
|
338
|
+
while ((match = depRegex.exec(constructorMatch[1])) !== null) {
|
|
339
|
+
const fullType = match[1];
|
|
340
|
+
const shortType = fullType.split('\\').pop();
|
|
341
|
+
deps.push({
|
|
342
|
+
type: shortType,
|
|
343
|
+
fullType: fullType.includes('\\') ? fullType : null,
|
|
344
|
+
variable: match[2]
|
|
345
|
+
});
|
|
346
|
+
}
|
|
347
|
+
if (deps.length > 0) {
|
|
348
|
+
metadata.dependencies = deps;
|
|
349
|
+
metadata.dependencyCount = deps.length;
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
return metadata;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
/**
|
|
357
|
+
* Extract Magento-specific metadata from XML content
|
|
358
|
+
*/
|
|
359
|
+
export function extractXmlMagentoMetadata(content, filePath) {
|
|
360
|
+
const metadata = {
|
|
361
|
+
magentoType: detectMagentoFileType(filePath),
|
|
362
|
+
configItems: []
|
|
363
|
+
};
|
|
364
|
+
|
|
365
|
+
// di.xml parsing
|
|
366
|
+
if (filePath.includes('di.xml')) {
|
|
367
|
+
const preferences = [...content.matchAll(XML_PATTERNS.di.preference)];
|
|
368
|
+
if (preferences.length > 0) {
|
|
369
|
+
metadata.preferences = preferences.map(m => ({ for: m[1], type: m[2] }));
|
|
370
|
+
metadata.configItems.push(...metadata.preferences.map(p => `preference:${p.for}`));
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
const virtualTypes = [...content.matchAll(XML_PATTERNS.di.virtualType)];
|
|
374
|
+
if (virtualTypes.length > 0) {
|
|
375
|
+
metadata.virtualTypes = virtualTypes.map(m => ({ name: m[1], type: m[2] }));
|
|
376
|
+
metadata.configItems.push(...metadata.virtualTypes.map(v => `virtualType:${v.name}`));
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
const plugins = [...content.matchAll(XML_PATTERNS.di.plugin)];
|
|
380
|
+
if (plugins.length > 0) {
|
|
381
|
+
metadata.plugins = plugins.map(m => ({ name: m[1], type: m[2] }));
|
|
382
|
+
metadata.configItems.push(...metadata.plugins.map(p => `plugin:${p.name}`));
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
const types = [...content.matchAll(XML_PATTERNS.di.type)];
|
|
386
|
+
if (types.length > 0) {
|
|
387
|
+
metadata.types = types.map(m => m[1]);
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
// events.xml parsing
|
|
392
|
+
if (filePath.includes('events.xml')) {
|
|
393
|
+
const events = [...content.matchAll(XML_PATTERNS.events.event)];
|
|
394
|
+
if (events.length > 0) {
|
|
395
|
+
metadata.events = events.map(m => m[1]);
|
|
396
|
+
metadata.configItems.push(...metadata.events.map(e => `event:${e}`));
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
const observers = [...content.matchAll(XML_PATTERNS.events.observer)];
|
|
400
|
+
if (observers.length > 0) {
|
|
401
|
+
metadata.observers = observers.map(m => ({ name: m[1], instance: m[2] }));
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
// webapi.xml parsing
|
|
406
|
+
if (filePath.includes('webapi.xml')) {
|
|
407
|
+
const routes = [...content.matchAll(XML_PATTERNS.webapi.route)];
|
|
408
|
+
if (routes.length > 0) {
|
|
409
|
+
metadata.apiRoutes = routes.map(m => ({ url: m[1], method: m[2] }));
|
|
410
|
+
metadata.configItems.push(...metadata.apiRoutes.map(r => `api:${r.method}:${r.url}`));
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
const services = [...content.matchAll(XML_PATTERNS.webapi.service)];
|
|
414
|
+
if (services.length > 0) {
|
|
415
|
+
metadata.apiServices = services.map(m => ({ class: m[1], method: m[2] }));
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
// layout.xml parsing
|
|
420
|
+
if (filePath.includes('/layout/')) {
|
|
421
|
+
const blocks = [...content.matchAll(XML_PATTERNS.layout.block)];
|
|
422
|
+
if (blocks.length > 0) {
|
|
423
|
+
metadata.blocks = blocks.map(m => m[1]);
|
|
424
|
+
metadata.configItems.push(...metadata.blocks.map(b => `block:${b.split('\\').pop()}`));
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
const uiComponents = [...content.matchAll(XML_PATTERNS.layout.uiComponent)];
|
|
428
|
+
if (uiComponents.length > 0) {
|
|
429
|
+
metadata.uiComponents = uiComponents.map(m => m[1]);
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
// system.xml parsing
|
|
434
|
+
if (filePath.includes('system.xml')) {
|
|
435
|
+
const sections = [...content.matchAll(XML_PATTERNS.system.section)];
|
|
436
|
+
const groups = [...content.matchAll(XML_PATTERNS.system.group)];
|
|
437
|
+
const fields = [...content.matchAll(XML_PATTERNS.system.field)];
|
|
438
|
+
|
|
439
|
+
if (sections.length > 0) metadata.configSections = sections.map(m => m[1]);
|
|
440
|
+
if (groups.length > 0) metadata.configGroups = groups.map(m => m[1]);
|
|
441
|
+
if (fields.length > 0) {
|
|
442
|
+
metadata.configFields = fields.map(m => m[1]);
|
|
443
|
+
metadata.configItems.push(...metadata.configFields.map(f => `config:${f}`));
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
// db_schema.xml parsing
|
|
448
|
+
if (filePath.includes('db_schema.xml')) {
|
|
449
|
+
const tables = [...content.matchAll(XML_PATTERNS.dbSchema.table)];
|
|
450
|
+
if (tables.length > 0) {
|
|
451
|
+
metadata.tables = tables.map(m => m[1]);
|
|
452
|
+
metadata.configItems.push(...metadata.tables.map(t => `table:${t}`));
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
const columns = [...content.matchAll(XML_PATTERNS.dbSchema.column)];
|
|
456
|
+
if (columns.length > 0) {
|
|
457
|
+
metadata.columns = columns.map(m => m[1]);
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
// crontab.xml parsing
|
|
462
|
+
if (filePath.includes('crontab.xml')) {
|
|
463
|
+
const jobs = [...content.matchAll(XML_PATTERNS.crontab.job)];
|
|
464
|
+
if (jobs.length > 0) {
|
|
465
|
+
metadata.cronJobs = jobs.map(m => ({ name: m[1], instance: m[2] }));
|
|
466
|
+
metadata.configItems.push(...metadata.cronJobs.map(j => `cron:${j.name}`));
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
// acl.xml parsing
|
|
471
|
+
if (filePath.includes('acl.xml')) {
|
|
472
|
+
const resources = [...content.matchAll(XML_PATTERNS.acl.resource)];
|
|
473
|
+
if (resources.length > 0) {
|
|
474
|
+
metadata.aclResources = resources.map(m => m[1]);
|
|
475
|
+
metadata.configItems.push(...metadata.aclResources.map(r => `acl:${r}`));
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
return metadata;
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
/**
|
|
483
|
+
* Extract GraphQL schema metadata
|
|
484
|
+
*/
|
|
485
|
+
export function extractGraphqlMetadata(content, filePath) {
|
|
486
|
+
const metadata = {
|
|
487
|
+
magentoType: 'graphql_schema',
|
|
488
|
+
types: [],
|
|
489
|
+
interfaces: [],
|
|
490
|
+
inputs: [],
|
|
491
|
+
enums: [],
|
|
492
|
+
queries: [],
|
|
493
|
+
mutations: [],
|
|
494
|
+
resolvers: []
|
|
495
|
+
};
|
|
496
|
+
|
|
497
|
+
const types = [...content.matchAll(GRAPHQL_PATTERNS.type)];
|
|
498
|
+
metadata.types = types.map(m => m[1]);
|
|
499
|
+
|
|
500
|
+
const interfaces = [...content.matchAll(GRAPHQL_PATTERNS.interface)];
|
|
501
|
+
metadata.interfaces = interfaces.map(m => m[1]);
|
|
502
|
+
|
|
503
|
+
const inputs = [...content.matchAll(GRAPHQL_PATTERNS.input)];
|
|
504
|
+
metadata.inputs = inputs.map(m => m[1]);
|
|
505
|
+
|
|
506
|
+
const enums = [...content.matchAll(GRAPHQL_PATTERNS.enum)];
|
|
507
|
+
metadata.enums = enums.map(m => m[1]);
|
|
508
|
+
|
|
509
|
+
const resolvers = [...content.matchAll(GRAPHQL_PATTERNS.resolver)];
|
|
510
|
+
metadata.resolvers = resolvers.map(m => m[1]);
|
|
511
|
+
|
|
512
|
+
// Extract queries from Query type
|
|
513
|
+
const queryBlock = content.match(/type\s+Query\s*\{([^}]+)\}/s);
|
|
514
|
+
if (queryBlock) {
|
|
515
|
+
const queries = [...queryBlock[1].matchAll(/(\w+)\s*\(/g)];
|
|
516
|
+
metadata.queries = queries.map(m => m[1]);
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
// Extract mutations from Mutation type
|
|
520
|
+
const mutationBlock = content.match(/type\s+Mutation\s*\{([^}]+)\}/s);
|
|
521
|
+
if (mutationBlock) {
|
|
522
|
+
const mutations = [...mutationBlock[1].matchAll(/(\w+)\s*\(/g)];
|
|
523
|
+
metadata.mutations = mutations.map(m => m[1]);
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
return metadata;
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
/**
|
|
530
|
+
* Get Magento area from file path
|
|
531
|
+
*/
|
|
532
|
+
export function detectArea(filePath) {
|
|
533
|
+
if (filePath.includes('/adminhtml/')) return 'adminhtml';
|
|
534
|
+
if (filePath.includes('/frontend/')) return 'frontend';
|
|
535
|
+
if (filePath.includes('/webapi_rest/')) return 'webapi_rest';
|
|
536
|
+
if (filePath.includes('/webapi_soap/')) return 'webapi_soap';
|
|
537
|
+
if (filePath.includes('/graphql/')) return 'graphql';
|
|
538
|
+
if (filePath.includes('/crontab/')) return 'crontab';
|
|
539
|
+
return 'global';
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
/**
|
|
543
|
+
* Extract module vendor and name from path
|
|
544
|
+
*/
|
|
545
|
+
export function extractModuleInfo(filePath) {
|
|
546
|
+
// app/code/Vendor/Module
|
|
547
|
+
const appMatch = filePath.match(/app\/code\/(\w+)\/(\w+)/);
|
|
548
|
+
if (appMatch) {
|
|
549
|
+
return { vendor: appMatch[1], module: appMatch[2], full: `${appMatch[1]}_${appMatch[2]}` };
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
// vendor/vendor/module-name
|
|
553
|
+
const vendorMatch = filePath.match(/vendor\/([\w-]+)\/(module-[\w-]+)/);
|
|
554
|
+
if (vendorMatch) {
|
|
555
|
+
const vendor = vendorMatch[1].replace(/-/g, '');
|
|
556
|
+
const module = vendorMatch[2].replace('module-', '').split('-').map(
|
|
557
|
+
s => s.charAt(0).toUpperCase() + s.slice(1)
|
|
558
|
+
).join('');
|
|
559
|
+
return { vendor, module, full: `${vendor}_${module}` };
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
return null;
|
|
563
|
+
}
|