@justmpm/ai-tool 0.6.1 → 0.7.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/README.md +53 -3
- package/dist/{chunk-XHYBVOXY.js → chunk-SVY7A47I.js} +954 -40
- package/dist/cli.js +17 -6
- package/dist/index.d.ts +229 -4
- package/dist/index.js +5 -1
- package/dist/{server-J2J5WZST.js → server-URRJAR75.js} +44 -4
- package/package.json +1 -1
- package/dist/chunk-JTVYOIBK.js +0 -219
|
@@ -47,6 +47,16 @@ function detectCategory(filePath) {
|
|
|
47
47
|
if (fileName.endsWith(".astro") && normalized.includes("/layouts/")) {
|
|
48
48
|
return "layout";
|
|
49
49
|
}
|
|
50
|
+
if (normalized.includes("functions/src/") && !normalized.includes("/__tests__/")) {
|
|
51
|
+
if (!fileName.includes("index") && !normalized.includes("/types/")) {
|
|
52
|
+
return "cloud-function";
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
if (normalized.includes("/triggers/") || normalized.includes("/callable/") || normalized.includes("/scheduled/")) {
|
|
56
|
+
if (normalized.includes("functions/") || normalized.includes("firebase/")) {
|
|
57
|
+
return "cloud-function";
|
|
58
|
+
}
|
|
59
|
+
}
|
|
50
60
|
if (normalized.includes("/api/") || normalized.includes("/server/") || fileNameNoExt.endsWith(".server") || fileNameNoExt === "+server") {
|
|
51
61
|
return "route";
|
|
52
62
|
}
|
|
@@ -106,6 +116,7 @@ var categoryIcons = {
|
|
|
106
116
|
type: "\u{1F4DD}",
|
|
107
117
|
config: "\u2699\uFE0F",
|
|
108
118
|
test: "\u{1F9EA}",
|
|
119
|
+
"cloud-function": "\u26A1",
|
|
109
120
|
other: "\u{1F4C1}"
|
|
110
121
|
};
|
|
111
122
|
function isEntryPoint(filePath) {
|
|
@@ -149,6 +160,7 @@ function formatMapSummary(result, areasInfo) {
|
|
|
149
160
|
"layout",
|
|
150
161
|
"route",
|
|
151
162
|
"store",
|
|
163
|
+
"cloud-function",
|
|
152
164
|
"other"
|
|
153
165
|
];
|
|
154
166
|
const catParts = [];
|
|
@@ -167,6 +179,16 @@ function formatMapSummary(result, areasInfo) {
|
|
|
167
179
|
}
|
|
168
180
|
out += `
|
|
169
181
|
`;
|
|
182
|
+
const cloudFunctionCount = result.summary.categories["cloud-function"] || 0;
|
|
183
|
+
if (cloudFunctionCount > 0) {
|
|
184
|
+
out += `\u{1F525} Firebase Cloud Functions detectado
|
|
185
|
+
`;
|
|
186
|
+
out += ` ${cloudFunctionCount} function(s) em functions/src/
|
|
187
|
+
`;
|
|
188
|
+
out += ` \u2192 Use 'ai-tool functions' para listar triggers
|
|
189
|
+
|
|
190
|
+
`;
|
|
191
|
+
}
|
|
170
192
|
if (result.circularDependencies.length > 0) {
|
|
171
193
|
out += `\u26A0\uFE0F ${result.circularDependencies.length} depend\xEAncia(s) circular(es) detectada(s)
|
|
172
194
|
`;
|
|
@@ -225,13 +247,14 @@ function formatMapText(result) {
|
|
|
225
247
|
"type",
|
|
226
248
|
"config",
|
|
227
249
|
"test",
|
|
250
|
+
"cloud-function",
|
|
228
251
|
"other"
|
|
229
252
|
];
|
|
230
253
|
for (const cat of catOrder) {
|
|
231
254
|
const count = result.summary.categories[cat];
|
|
232
255
|
if (count) {
|
|
233
256
|
const icon = categoryIcons[cat];
|
|
234
|
-
out += ` ${icon} ${cat.padEnd(
|
|
257
|
+
out += ` ${icon} ${cat.padEnd(14)} ${count}
|
|
235
258
|
`;
|
|
236
259
|
}
|
|
237
260
|
}
|
|
@@ -356,12 +379,93 @@ function formatDeadText(result) {
|
|
|
356
379
|
out += `
|
|
357
380
|
`;
|
|
358
381
|
}
|
|
359
|
-
out += `\u{1F4A1}
|
|
382
|
+
out += `\u{1F4A1} COMO RESOLVER
|
|
383
|
+
|
|
384
|
+
`;
|
|
385
|
+
out += ` 1. Falsos positivos? Adicione ao .analyze/areas.config.json:
|
|
386
|
+
`;
|
|
387
|
+
out += ` { "ignore": ["functions/lib/**", "**/*.test.ts"] }
|
|
388
|
+
|
|
389
|
+
`;
|
|
390
|
+
out += ` 2. Remover automaticamente:
|
|
391
|
+
`;
|
|
392
|
+
out += ` npx knip --fix
|
|
393
|
+
|
|
394
|
+
`;
|
|
395
|
+
out += ` 3. Ver detalhes em JSON:
|
|
396
|
+
`;
|
|
397
|
+
out += ` ai-tool dead --format=json
|
|
398
|
+
`;
|
|
399
|
+
const suggestions = generateIgnoreSuggestions(result);
|
|
400
|
+
if (suggestions.length > 0) {
|
|
401
|
+
out += `
|
|
402
|
+
\u{1F3AF} SUGEST\xD5ES INTELIGENTES
|
|
403
|
+
|
|
360
404
|
`;
|
|
361
|
-
|
|
405
|
+
for (const suggestion of suggestions) {
|
|
406
|
+
out += ` ${suggestion.icon} ${suggestion.pattern}
|
|
362
407
|
`;
|
|
408
|
+
out += ` Motivo: ${suggestion.reason}
|
|
409
|
+
`;
|
|
410
|
+
out += ` Arquivos afetados: ${suggestion.count}
|
|
411
|
+
|
|
412
|
+
`;
|
|
413
|
+
}
|
|
414
|
+
}
|
|
363
415
|
return out;
|
|
364
416
|
}
|
|
417
|
+
function generateIgnoreSuggestions(result) {
|
|
418
|
+
const suggestions = [];
|
|
419
|
+
const files = result.files.map((f) => f.path);
|
|
420
|
+
const libFiles = files.filter((f) => f.includes("functions/lib/"));
|
|
421
|
+
if (libFiles.length > 0) {
|
|
422
|
+
suggestions.push({
|
|
423
|
+
icon: "\u{1F4E6}",
|
|
424
|
+
pattern: "functions/lib/**",
|
|
425
|
+
reason: "Build compilado do Firebase Functions",
|
|
426
|
+
count: libFiles.length
|
|
427
|
+
});
|
|
428
|
+
}
|
|
429
|
+
const testFiles = files.filter((f) => /\.(test|spec)\.(ts|tsx|js|jsx)$/.test(f));
|
|
430
|
+
if (testFiles.length > 3) {
|
|
431
|
+
suggestions.push({
|
|
432
|
+
icon: "\u{1F9EA}",
|
|
433
|
+
pattern: "**/*.(test|spec).(ts|tsx|js|jsx)",
|
|
434
|
+
reason: "Arquivos de teste geralmente s\xE3o entry points pr\xF3prios",
|
|
435
|
+
count: testFiles.length
|
|
436
|
+
});
|
|
437
|
+
}
|
|
438
|
+
const configFiles = files.filter(
|
|
439
|
+
(f) => f.includes("vite.config") || f.includes("next.config") || f.includes("tailwind.config") || f.includes("jest.config") || f.includes("eslint.config")
|
|
440
|
+
);
|
|
441
|
+
if (configFiles.length > 0) {
|
|
442
|
+
suggestions.push({
|
|
443
|
+
icon: "\u2699\uFE0F",
|
|
444
|
+
pattern: "**/*.config.(ts|js|mjs|cjs)",
|
|
445
|
+
reason: "Arquivos de configura\xE7\xE3o s\xE3o entry points",
|
|
446
|
+
count: configFiles.length
|
|
447
|
+
});
|
|
448
|
+
}
|
|
449
|
+
const dtsFiles = files.filter((f) => f.endsWith(".d.ts"));
|
|
450
|
+
if (dtsFiles.length > 0) {
|
|
451
|
+
suggestions.push({
|
|
452
|
+
icon: "\u{1F4D8}",
|
|
453
|
+
pattern: "**/*.d.ts",
|
|
454
|
+
reason: "Arquivos de defini\xE7\xE3o TypeScript",
|
|
455
|
+
count: dtsFiles.length
|
|
456
|
+
});
|
|
457
|
+
}
|
|
458
|
+
const scriptFiles = files.filter((f) => f.startsWith("scripts/") || f.includes("/scripts/"));
|
|
459
|
+
if (scriptFiles.length > 0) {
|
|
460
|
+
suggestions.push({
|
|
461
|
+
icon: "\u{1F4DC}",
|
|
462
|
+
pattern: "scripts/**",
|
|
463
|
+
reason: "Scripts de automa\xE7\xE3o s\xE3o entry points",
|
|
464
|
+
count: scriptFiles.length
|
|
465
|
+
});
|
|
466
|
+
}
|
|
467
|
+
return suggestions;
|
|
468
|
+
}
|
|
365
469
|
function formatImpactText(result) {
|
|
366
470
|
let out = "";
|
|
367
471
|
out += `
|
|
@@ -799,6 +903,7 @@ function formatAreaDetailText(result, options = {}) {
|
|
|
799
903
|
"type",
|
|
800
904
|
"config",
|
|
801
905
|
"test",
|
|
906
|
+
"cloud-function",
|
|
802
907
|
"other"
|
|
803
908
|
];
|
|
804
909
|
const catParts = [];
|
|
@@ -869,20 +974,32 @@ function formatFindText(result) {
|
|
|
869
974
|
out += `\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D
|
|
870
975
|
|
|
871
976
|
`;
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
977
|
+
if (result.query) {
|
|
978
|
+
out += `\u{1F50D} "${result.query}"`;
|
|
979
|
+
if (result.filters.type) {
|
|
980
|
+
out += ` [type: ${result.filters.type}]`;
|
|
981
|
+
}
|
|
982
|
+
if (result.filters.area) {
|
|
983
|
+
out += ` [area: ${result.filters.area}]`;
|
|
984
|
+
}
|
|
985
|
+
out += `
|
|
986
|
+
|
|
987
|
+
`;
|
|
988
|
+
} else {
|
|
989
|
+
out += `\u{1F4CB} Listando todos os s\xEDmbolos do tipo: ${result.filters.type || "all"}
|
|
990
|
+
|
|
991
|
+
`;
|
|
878
992
|
}
|
|
879
|
-
|
|
993
|
+
if (!result.definition && result.references.length === 0 && result.summary.definitions === 0) {
|
|
994
|
+
if (result.query) {
|
|
995
|
+
out += `\u274C Nenhum resultado encontrado para "${result.query}"
|
|
880
996
|
|
|
881
997
|
`;
|
|
882
|
-
|
|
883
|
-
|
|
998
|
+
} else {
|
|
999
|
+
out += `\u274C Nenhum s\xEDmbolo do tipo "${result.filters.type}" encontrado
|
|
884
1000
|
|
|
885
1001
|
`;
|
|
1002
|
+
}
|
|
886
1003
|
out += `\u{1F4A1} Dicas:
|
|
887
1004
|
`;
|
|
888
1005
|
out += ` \u2022 Verifique a ortografia
|
|
@@ -1057,6 +1174,26 @@ function formatAreaContextText(result) {
|
|
|
1057
1174
|
`;
|
|
1058
1175
|
out += ` state: ${st.state}
|
|
1059
1176
|
|
|
1177
|
+
`;
|
|
1178
|
+
}
|
|
1179
|
+
}
|
|
1180
|
+
if (result.triggers && result.triggers.length > 0) {
|
|
1181
|
+
out += `\u26A1 TRIGGERS (${result.triggers.length})
|
|
1182
|
+
|
|
1183
|
+
`;
|
|
1184
|
+
for (const t of result.triggers) {
|
|
1185
|
+
const filePart = t.file.split("/").pop() || t.file;
|
|
1186
|
+
out += ` ${t.name}`.padEnd(35) + `${t.triggerType.padEnd(25)}[${filePart}:${t.line}]
|
|
1187
|
+
`;
|
|
1188
|
+
if (t.triggerPath) {
|
|
1189
|
+
out += ` path: ${t.triggerPath}
|
|
1190
|
+
`;
|
|
1191
|
+
}
|
|
1192
|
+
if (t.triggerSchedule) {
|
|
1193
|
+
out += ` schedule: ${t.triggerSchedule}
|
|
1194
|
+
`;
|
|
1195
|
+
}
|
|
1196
|
+
out += `
|
|
1060
1197
|
`;
|
|
1061
1198
|
}
|
|
1062
1199
|
}
|
|
@@ -1077,6 +1214,10 @@ function formatAreaContextText(result) {
|
|
|
1077
1214
|
`;
|
|
1078
1215
|
out += ` Stores: ${result.stores.length}
|
|
1079
1216
|
`;
|
|
1217
|
+
if (result.triggers && result.triggers.length > 0) {
|
|
1218
|
+
out += ` Triggers: ${result.triggers.length}
|
|
1219
|
+
`;
|
|
1220
|
+
}
|
|
1080
1221
|
return out;
|
|
1081
1222
|
}
|
|
1082
1223
|
|
|
@@ -1559,9 +1700,17 @@ var FOLDER_PATTERNS = [
|
|
|
1559
1700
|
{ pattern: /store\/.*[Aa]uth/, area: "auth", priority: 70 },
|
|
1560
1701
|
{ pattern: /store\/.*[Uu]ser/, area: "user", priority: 70 },
|
|
1561
1702
|
// ============================================================================
|
|
1562
|
-
// CLOUD FUNCTIONS
|
|
1703
|
+
// FIREBASE CLOUD FUNCTIONS (expandido)
|
|
1563
1704
|
// ============================================================================
|
|
1564
1705
|
{ pattern: /functions\/src\//, area: "cloud-functions", priority: 80 },
|
|
1706
|
+
{ pattern: /functions\/src\/triggers\//, area: "triggers", priority: 95 },
|
|
1707
|
+
{ pattern: /functions\/src\/callable\//, area: "callable", priority: 95 },
|
|
1708
|
+
{ pattern: /functions\/src\/scheduled\//, area: "scheduled", priority: 95 },
|
|
1709
|
+
{ pattern: /functions\/src\/firestore\//, area: "firestore-triggers", priority: 95 },
|
|
1710
|
+
{ pattern: /functions\/src\/auth\//, area: "auth-triggers", priority: 95 },
|
|
1711
|
+
{ pattern: /functions\/src\/storage\//, area: "storage-triggers", priority: 95 },
|
|
1712
|
+
{ pattern: /functions\/src\/pubsub\//, area: "pubsub", priority: 95 },
|
|
1713
|
+
{ pattern: /functions\/src\/https\//, area: "callable", priority: 95 },
|
|
1565
1714
|
// ============================================================================
|
|
1566
1715
|
// OUTROS
|
|
1567
1716
|
// ============================================================================
|
|
@@ -1611,7 +1760,17 @@ var KEYWORD_PATTERNS = [
|
|
|
1611
1760
|
{ keyword: /[Mm]anifest/, area: "pwa", priority: 55 },
|
|
1612
1761
|
// PDF (genérico)
|
|
1613
1762
|
{ keyword: /[Pp]df[Ee]xport/, area: "export", priority: 60 },
|
|
1614
|
-
{ keyword: /[Dd]ocx[Ee]xport/, area: "export", priority: 60 }
|
|
1763
|
+
{ keyword: /[Dd]ocx[Ee]xport/, area: "export", priority: 60 },
|
|
1764
|
+
// Firebase Cloud Functions triggers
|
|
1765
|
+
{ keyword: /[Oo]nCall/, area: "callable", priority: 70 },
|
|
1766
|
+
{ keyword: /[Oo]nRequest/, area: "callable", priority: 70 },
|
|
1767
|
+
{ keyword: /[Oo]nSchedule/, area: "scheduled", priority: 70 },
|
|
1768
|
+
{ keyword: /[Oo]n[A-Z].*Created/, area: "triggers", priority: 70 },
|
|
1769
|
+
{ keyword: /[Oo]n[A-Z].*Updated/, area: "triggers", priority: 70 },
|
|
1770
|
+
{ keyword: /[Oo]n[A-Z].*Deleted/, area: "triggers", priority: 70 },
|
|
1771
|
+
{ keyword: /[Oo]n[A-Z].*Written/, area: "triggers", priority: 70 },
|
|
1772
|
+
{ keyword: /[Oo]nObject/, area: "storage-triggers", priority: 70 },
|
|
1773
|
+
{ keyword: /[Oo]nMessage/, area: "pubsub", priority: 70 }
|
|
1615
1774
|
];
|
|
1616
1775
|
var AREA_NAMES = {
|
|
1617
1776
|
// Autenticação e usuário
|
|
@@ -1658,6 +1817,13 @@ var AREA_NAMES = {
|
|
|
1658
1817
|
layout: "Layout",
|
|
1659
1818
|
"shared-ui": "UI Compartilhada",
|
|
1660
1819
|
"cloud-functions": "Cloud Functions",
|
|
1820
|
+
triggers: "Triggers Firebase",
|
|
1821
|
+
callable: "Callable Functions",
|
|
1822
|
+
scheduled: "Scheduled Functions",
|
|
1823
|
+
"firestore-triggers": "Firestore Triggers",
|
|
1824
|
+
"auth-triggers": "Auth Triggers",
|
|
1825
|
+
"storage-triggers": "Storage Triggers",
|
|
1826
|
+
pubsub: "Pub/Sub",
|
|
1661
1827
|
assets: "Assets",
|
|
1662
1828
|
scripts: "Scripts",
|
|
1663
1829
|
// UI patterns
|
|
@@ -1711,7 +1877,14 @@ var AREA_DESCRIPTIONS = {
|
|
|
1711
1877
|
core: "Providers e configura\xE7\xE3o principal",
|
|
1712
1878
|
layout: "Layout e navega\xE7\xE3o",
|
|
1713
1879
|
"shared-ui": "Componentes de UI compartilhados",
|
|
1714
|
-
"cloud-functions": "Cloud Functions (serverless)",
|
|
1880
|
+
"cloud-functions": "Firebase Cloud Functions (serverless)",
|
|
1881
|
+
triggers: "Event-driven Firebase triggers",
|
|
1882
|
+
callable: "HTTPS callable functions (frontend SDK)",
|
|
1883
|
+
scheduled: "Scheduled/cron functions",
|
|
1884
|
+
"firestore-triggers": "Triggers para eventos do Firestore",
|
|
1885
|
+
"auth-triggers": "Triggers para eventos de autentica\xE7\xE3o",
|
|
1886
|
+
"storage-triggers": "Triggers para eventos do Storage",
|
|
1887
|
+
pubsub: "Triggers para mensagens Pub/Sub",
|
|
1715
1888
|
assets: "Assets p\xFAblicos",
|
|
1716
1889
|
scripts: "Scripts de automa\xE7\xE3o",
|
|
1717
1890
|
// UI patterns
|
|
@@ -2748,6 +2921,12 @@ function collectSuggestions(targetPath, graph, allFiles, limit) {
|
|
|
2748
2921
|
priority: "low"
|
|
2749
2922
|
});
|
|
2750
2923
|
}
|
|
2924
|
+
const cloudFunctionSuggestions = suggestFirebaseRules(targetPath, allFiles);
|
|
2925
|
+
for (const suggestion of cloudFunctionSuggestions) {
|
|
2926
|
+
if (addedPaths.has(suggestion.path)) continue;
|
|
2927
|
+
addedPaths.add(suggestion.path);
|
|
2928
|
+
suggestions.push(suggestion);
|
|
2929
|
+
}
|
|
2751
2930
|
const priorityOrder = {
|
|
2752
2931
|
critical: 0,
|
|
2753
2932
|
high: 1,
|
|
@@ -2806,6 +2985,63 @@ function findTargetFile2(target, allFiles) {
|
|
|
2806
2985
|
}
|
|
2807
2986
|
return null;
|
|
2808
2987
|
}
|
|
2988
|
+
function suggestFirebaseRules(targetPath, allFiles) {
|
|
2989
|
+
const suggestions = [];
|
|
2990
|
+
if (!targetPath.includes("functions/src/")) {
|
|
2991
|
+
return suggestions;
|
|
2992
|
+
}
|
|
2993
|
+
const isFirestoreTrigger = targetPath.toLowerCase().includes("firestore") || targetPath.toLowerCase().includes("document");
|
|
2994
|
+
const isStorageTrigger = targetPath.toLowerCase().includes("storage");
|
|
2995
|
+
for (const file of allFiles) {
|
|
2996
|
+
const fileName = file.split("/").pop()?.toLowerCase() || "";
|
|
2997
|
+
if (fileName === "firestore.rules" || file.endsWith("firestore.rules")) {
|
|
2998
|
+
if (isFirestoreTrigger) {
|
|
2999
|
+
suggestions.push({
|
|
3000
|
+
path: file,
|
|
3001
|
+
category: "config",
|
|
3002
|
+
reason: "Regras Firestore (trigger relacionado)",
|
|
3003
|
+
priority: "high"
|
|
3004
|
+
});
|
|
3005
|
+
} else {
|
|
3006
|
+
suggestions.push({
|
|
3007
|
+
path: file,
|
|
3008
|
+
category: "config",
|
|
3009
|
+
reason: "Regras Firestore (Cloud Function)",
|
|
3010
|
+
priority: "medium"
|
|
3011
|
+
});
|
|
3012
|
+
}
|
|
3013
|
+
}
|
|
3014
|
+
if (fileName === "storage.rules" || file.endsWith("storage.rules")) {
|
|
3015
|
+
if (isStorageTrigger) {
|
|
3016
|
+
suggestions.push({
|
|
3017
|
+
path: file,
|
|
3018
|
+
category: "config",
|
|
3019
|
+
reason: "Regras Storage (trigger relacionado)",
|
|
3020
|
+
priority: "high"
|
|
3021
|
+
});
|
|
3022
|
+
} else {
|
|
3023
|
+
suggestions.push({
|
|
3024
|
+
path: file,
|
|
3025
|
+
category: "config",
|
|
3026
|
+
reason: "Regras Storage (Cloud Function)",
|
|
3027
|
+
priority: "low"
|
|
3028
|
+
});
|
|
3029
|
+
}
|
|
3030
|
+
}
|
|
3031
|
+
}
|
|
3032
|
+
if (!targetPath.endsWith("index.ts")) {
|
|
3033
|
+
const indexFile = allFiles.find((f) => f.includes("functions/src/index"));
|
|
3034
|
+
if (indexFile) {
|
|
3035
|
+
suggestions.push({
|
|
3036
|
+
path: indexFile,
|
|
3037
|
+
category: "config",
|
|
3038
|
+
reason: "Exports de Cloud Functions",
|
|
3039
|
+
priority: "high"
|
|
3040
|
+
});
|
|
3041
|
+
}
|
|
3042
|
+
}
|
|
3043
|
+
return suggestions;
|
|
3044
|
+
}
|
|
2809
3045
|
function formatNotFound2(target, allFiles) {
|
|
2810
3046
|
return formatFileNotFound({ target, allFiles, command: "suggest" });
|
|
2811
3047
|
}
|
|
@@ -2881,11 +3117,11 @@ function simplifyType(typeText) {
|
|
|
2881
3117
|
return simplified;
|
|
2882
3118
|
}
|
|
2883
3119
|
function extractFunctions(sourceFile) {
|
|
2884
|
-
const
|
|
3120
|
+
const functions2 = [];
|
|
2885
3121
|
for (const func of sourceFile.getFunctions()) {
|
|
2886
3122
|
const name = func.getName();
|
|
2887
3123
|
if (!name) continue;
|
|
2888
|
-
|
|
3124
|
+
functions2.push({
|
|
2889
3125
|
name,
|
|
2890
3126
|
params: extractParams(func.getParameters()),
|
|
2891
3127
|
returnType: simplifyType(func.getReturnType().getText()),
|
|
@@ -2903,7 +3139,7 @@ function extractFunctions(sourceFile) {
|
|
|
2903
3139
|
if (init.getKind() === SyntaxKind.ArrowFunction) {
|
|
2904
3140
|
const arrowFunc = init.asKind(SyntaxKind.ArrowFunction);
|
|
2905
3141
|
if (!arrowFunc) continue;
|
|
2906
|
-
|
|
3142
|
+
functions2.push({
|
|
2907
3143
|
name: varDecl.getName(),
|
|
2908
3144
|
params: extractParams(arrowFunc.getParameters()),
|
|
2909
3145
|
returnType: simplifyType(arrowFunc.getReturnType().getText()),
|
|
@@ -2916,7 +3152,7 @@ function extractFunctions(sourceFile) {
|
|
|
2916
3152
|
if (init.getKind() === SyntaxKind.FunctionExpression) {
|
|
2917
3153
|
const funcExpr = init.asKind(SyntaxKind.FunctionExpression);
|
|
2918
3154
|
if (!funcExpr) continue;
|
|
2919
|
-
|
|
3155
|
+
functions2.push({
|
|
2920
3156
|
name: varDecl.getName(),
|
|
2921
3157
|
params: extractParams(funcExpr.getParameters()),
|
|
2922
3158
|
returnType: simplifyType(funcExpr.getReturnType().getText()),
|
|
@@ -2928,7 +3164,7 @@ function extractFunctions(sourceFile) {
|
|
|
2928
3164
|
}
|
|
2929
3165
|
}
|
|
2930
3166
|
}
|
|
2931
|
-
return
|
|
3167
|
+
return functions2;
|
|
2932
3168
|
}
|
|
2933
3169
|
function extractTypes(sourceFile) {
|
|
2934
3170
|
const types = [];
|
|
@@ -3023,6 +3259,12 @@ import { readdirSync as readdirSync2, statSync as statSync2 } from "fs";
|
|
|
3023
3259
|
import { join as join4, extname as extname2, resolve } from "path";
|
|
3024
3260
|
import { Project as Project2, SyntaxKind as SyntaxKind2 } from "ts-morph";
|
|
3025
3261
|
var CODE_EXTENSIONS2 = /* @__PURE__ */ new Set([".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"]);
|
|
3262
|
+
var DEBUG = process.env.DEBUG_ANALYZE === "true";
|
|
3263
|
+
function debugLog(...args) {
|
|
3264
|
+
if (DEBUG) {
|
|
3265
|
+
console.error("[analyze:debug]", ...args);
|
|
3266
|
+
}
|
|
3267
|
+
}
|
|
3026
3268
|
var IGNORED_DIRS = /* @__PURE__ */ new Set([
|
|
3027
3269
|
"node_modules",
|
|
3028
3270
|
"dist",
|
|
@@ -3035,8 +3277,69 @@ var IGNORED_DIRS = /* @__PURE__ */ new Set([
|
|
|
3035
3277
|
".vercel",
|
|
3036
3278
|
".analyze"
|
|
3037
3279
|
]);
|
|
3280
|
+
var FIREBASE_V2_TRIGGERS = /* @__PURE__ */ new Set([
|
|
3281
|
+
// HTTPS (firebase-functions/v2/https)
|
|
3282
|
+
"onCall",
|
|
3283
|
+
"onRequest",
|
|
3284
|
+
// Firestore (firebase-functions/v2/firestore)
|
|
3285
|
+
"onDocumentCreated",
|
|
3286
|
+
"onDocumentCreatedWithAuthContext",
|
|
3287
|
+
"onDocumentUpdated",
|
|
3288
|
+
"onDocumentUpdatedWithAuthContext",
|
|
3289
|
+
"onDocumentDeleted",
|
|
3290
|
+
"onDocumentDeletedWithAuthContext",
|
|
3291
|
+
"onDocumentWritten",
|
|
3292
|
+
"onDocumentWrittenWithAuthContext",
|
|
3293
|
+
// Realtime Database (firebase-functions/v2/database)
|
|
3294
|
+
"onValueCreated",
|
|
3295
|
+
"onValueUpdated",
|
|
3296
|
+
"onValueDeleted",
|
|
3297
|
+
"onValueWritten",
|
|
3298
|
+
// Scheduler (firebase-functions/v2/scheduler)
|
|
3299
|
+
"onSchedule",
|
|
3300
|
+
// Storage (firebase-functions/v2/storage)
|
|
3301
|
+
"onObjectFinalized",
|
|
3302
|
+
"onObjectArchived",
|
|
3303
|
+
"onObjectDeleted",
|
|
3304
|
+
"onMetadataUpdated",
|
|
3305
|
+
// Pub/Sub (firebase-functions/v2/pubsub)
|
|
3306
|
+
"onMessagePublished",
|
|
3307
|
+
// Identity (firebase-functions/v2/identity)
|
|
3308
|
+
"beforeUserCreated",
|
|
3309
|
+
"beforeUserSignedIn",
|
|
3310
|
+
"beforeEmailSent",
|
|
3311
|
+
"beforeSmsSent",
|
|
3312
|
+
// Alerts - Crashlytics (firebase-functions/v2/alerts/crashlytics)
|
|
3313
|
+
"onNewFatalIssuePublished",
|
|
3314
|
+
"onNewNonfatalIssuePublished",
|
|
3315
|
+
"onNewAnrIssuePublished",
|
|
3316
|
+
"onRegressionAlertPublished",
|
|
3317
|
+
"onStabilityDigestPublished",
|
|
3318
|
+
"onVelocityAlertPublished",
|
|
3319
|
+
// Alerts - App Distribution (firebase-functions/v2/alerts/appDistribution)
|
|
3320
|
+
"onNewTesterIosDevicePublished",
|
|
3321
|
+
"onInAppFeedbackPublished",
|
|
3322
|
+
// Alerts - Performance (firebase-functions/v2/alerts/performance)
|
|
3323
|
+
"onThresholdAlertPublished",
|
|
3324
|
+
// Alerts - Billing (firebase-functions/v2/alerts/billing)
|
|
3325
|
+
"onPlanUpdatePublished",
|
|
3326
|
+
"onPlanAutomatedUpdatePublished",
|
|
3327
|
+
// Remote Config (firebase-functions/v2/remoteConfig)
|
|
3328
|
+
"onConfigUpdated",
|
|
3329
|
+
// Eventarc (firebase-functions/v2/eventarc)
|
|
3330
|
+
"onCustomEventPublished",
|
|
3331
|
+
// Tasks (firebase-functions/v2/tasks)
|
|
3332
|
+
"onTaskDispatched",
|
|
3333
|
+
// Test Lab (firebase-functions/v2/testLab)
|
|
3334
|
+
"onTestMatrixCompleted"
|
|
3335
|
+
]);
|
|
3038
3336
|
function indexProject(cwd) {
|
|
3039
3337
|
const allFiles = getAllCodeFiles(cwd);
|
|
3338
|
+
debugLog(`Indexando ${allFiles.length} arquivos em ${cwd}`);
|
|
3339
|
+
const functionFiles = allFiles.filter((f) => f.includes("functions/src/"));
|
|
3340
|
+
if (functionFiles.length > 0) {
|
|
3341
|
+
debugLog(`Encontrados ${functionFiles.length} arquivos em functions/src/:`, functionFiles);
|
|
3342
|
+
}
|
|
3040
3343
|
const project = createProject2(cwd);
|
|
3041
3344
|
for (const file of allFiles) {
|
|
3042
3345
|
try {
|
|
@@ -3136,6 +3439,55 @@ function indexProject(cwd) {
|
|
|
3136
3439
|
if (isExported) {
|
|
3137
3440
|
exports.push(name);
|
|
3138
3441
|
}
|
|
3442
|
+
} else if (initKind === SyntaxKind2.CallExpression) {
|
|
3443
|
+
const triggerName = extractFirebaseTriggerName(init);
|
|
3444
|
+
if (DEBUG && filePath.includes("functions/src/")) {
|
|
3445
|
+
const initText = init.getText().slice(0, 50);
|
|
3446
|
+
debugLog(`[CF] Analisando: ${name} = ${initText}...`);
|
|
3447
|
+
if (triggerName) {
|
|
3448
|
+
debugLog(`[CF] \u2713 Trigger detectado: ${triggerName}`);
|
|
3449
|
+
}
|
|
3450
|
+
}
|
|
3451
|
+
if (triggerName && FIREBASE_V2_TRIGGERS.has(triggerName)) {
|
|
3452
|
+
const triggerInfo = extractTriggerInfo(init, triggerName);
|
|
3453
|
+
const symbol = {
|
|
3454
|
+
name,
|
|
3455
|
+
file: filePath,
|
|
3456
|
+
line: varDecl.getStartLineNumber(),
|
|
3457
|
+
kind: "trigger",
|
|
3458
|
+
signature: `${isExported ? "export " : ""}const ${name} = ${triggerName}(...)`,
|
|
3459
|
+
isExported,
|
|
3460
|
+
triggerInfo
|
|
3461
|
+
};
|
|
3462
|
+
symbols.push(symbol);
|
|
3463
|
+
if (!symbolsByName[name]) {
|
|
3464
|
+
symbolsByName[name] = [];
|
|
3465
|
+
}
|
|
3466
|
+
symbolsByName[name].push(symbol);
|
|
3467
|
+
if (isExported) {
|
|
3468
|
+
exports.push(name);
|
|
3469
|
+
}
|
|
3470
|
+
} else {
|
|
3471
|
+
const declKind = varStatement.getDeclarationKind();
|
|
3472
|
+
if (declKind.toString() === "const") {
|
|
3473
|
+
const symbol = {
|
|
3474
|
+
name,
|
|
3475
|
+
file: filePath,
|
|
3476
|
+
line: varDecl.getStartLineNumber(),
|
|
3477
|
+
kind: "const",
|
|
3478
|
+
signature: `${isExported ? "export " : ""}const ${name} = ${truncateCode(init.getText(), 40)}`,
|
|
3479
|
+
isExported
|
|
3480
|
+
};
|
|
3481
|
+
symbols.push(symbol);
|
|
3482
|
+
if (!symbolsByName[name]) {
|
|
3483
|
+
symbolsByName[name] = [];
|
|
3484
|
+
}
|
|
3485
|
+
symbolsByName[name].push(symbol);
|
|
3486
|
+
if (isExported) {
|
|
3487
|
+
exports.push(name);
|
|
3488
|
+
}
|
|
3489
|
+
}
|
|
3490
|
+
}
|
|
3139
3491
|
} else {
|
|
3140
3492
|
const declKind = varStatement.getDeclarationKind();
|
|
3141
3493
|
if (declKind.toString() !== "const") continue;
|
|
@@ -3290,6 +3642,44 @@ function inferSymbolKind(name, context2) {
|
|
|
3290
3642
|
}
|
|
3291
3643
|
return context2 === "function" ? "function" : "const";
|
|
3292
3644
|
}
|
|
3645
|
+
function extractFirebaseTriggerName(init) {
|
|
3646
|
+
const text = init.getText();
|
|
3647
|
+
for (const trigger of FIREBASE_V2_TRIGGERS) {
|
|
3648
|
+
const pattern = new RegExp(`(?:^|\\.|\\s)${trigger}(?:<[^>]*>)?\\s*\\(`);
|
|
3649
|
+
if (pattern.test(text)) {
|
|
3650
|
+
return trigger;
|
|
3651
|
+
}
|
|
3652
|
+
}
|
|
3653
|
+
return null;
|
|
3654
|
+
}
|
|
3655
|
+
function extractTriggerInfo(init, triggerName) {
|
|
3656
|
+
const text = init.getText();
|
|
3657
|
+
const info = { triggerType: triggerName };
|
|
3658
|
+
if (triggerName.startsWith("onDocument") || triggerName.startsWith("onValue")) {
|
|
3659
|
+
const pathMatch = text.match(/\(\s*["'`]([^"'`]+)["'`]/);
|
|
3660
|
+
if (pathMatch) {
|
|
3661
|
+
info.triggerPath = pathMatch[1];
|
|
3662
|
+
}
|
|
3663
|
+
}
|
|
3664
|
+
if (triggerName === "onSchedule") {
|
|
3665
|
+
const scheduleMatch = text.match(/onSchedule\s*\(\s*["'`]([^"'`]+)["'`]/);
|
|
3666
|
+
if (scheduleMatch) {
|
|
3667
|
+
info.triggerSchedule = scheduleMatch[1];
|
|
3668
|
+
} else {
|
|
3669
|
+
const objectScheduleMatch = text.match(/schedule\s*:\s*["'`]([^"'`]+)["'`]/);
|
|
3670
|
+
if (objectScheduleMatch) {
|
|
3671
|
+
info.triggerSchedule = objectScheduleMatch[1];
|
|
3672
|
+
}
|
|
3673
|
+
}
|
|
3674
|
+
}
|
|
3675
|
+
if (triggerName.startsWith("onObject") || triggerName === "onMetadataUpdated") {
|
|
3676
|
+
const bucketMatch = text.match(/bucket\s*:\s*["'`]([^"'`]+)["'`]/);
|
|
3677
|
+
if (bucketMatch) {
|
|
3678
|
+
info.triggerPath = bucketMatch[1];
|
|
3679
|
+
}
|
|
3680
|
+
}
|
|
3681
|
+
return info;
|
|
3682
|
+
}
|
|
3293
3683
|
function simplifyType2(typeText) {
|
|
3294
3684
|
if (!typeText) return "unknown";
|
|
3295
3685
|
let simplified = typeText.replace(/import\([^)]+\)\./g, "");
|
|
@@ -3354,7 +3744,7 @@ async function context(target, options = {}) {
|
|
|
3354
3744
|
const absolutePath = resolve2(cwd, targetPath);
|
|
3355
3745
|
const sourceFile = addSourceFile(project, absolutePath);
|
|
3356
3746
|
const imports = extractImports(sourceFile);
|
|
3357
|
-
const
|
|
3747
|
+
const functions2 = extractFunctions(sourceFile);
|
|
3358
3748
|
const types = extractTypes(sourceFile);
|
|
3359
3749
|
const exports = extractExports(sourceFile);
|
|
3360
3750
|
const result = {
|
|
@@ -3364,7 +3754,7 @@ async function context(target, options = {}) {
|
|
|
3364
3754
|
category: detectCategory(targetPath),
|
|
3365
3755
|
imports,
|
|
3366
3756
|
exports,
|
|
3367
|
-
functions,
|
|
3757
|
+
functions: functions2,
|
|
3368
3758
|
types
|
|
3369
3759
|
};
|
|
3370
3760
|
if (format === "json") {
|
|
@@ -3497,10 +3887,11 @@ async function areaContext(areaName, options = {}) {
|
|
|
3497
3887
|
}
|
|
3498
3888
|
const types = [];
|
|
3499
3889
|
const hooks = [];
|
|
3500
|
-
const
|
|
3890
|
+
const functions2 = [];
|
|
3501
3891
|
const components = [];
|
|
3502
3892
|
const services = [];
|
|
3503
3893
|
const stores = [];
|
|
3894
|
+
const triggers = [];
|
|
3504
3895
|
for (const filePath of areaFiles) {
|
|
3505
3896
|
const fileData = index.files[filePath];
|
|
3506
3897
|
if (!fileData) continue;
|
|
@@ -3545,7 +3936,7 @@ async function areaContext(areaName, options = {}) {
|
|
|
3545
3936
|
returns: symbol.returnType || "unknown"
|
|
3546
3937
|
});
|
|
3547
3938
|
} else {
|
|
3548
|
-
|
|
3939
|
+
functions2.push({
|
|
3549
3940
|
name: symbol.name,
|
|
3550
3941
|
file: filePath,
|
|
3551
3942
|
line: symbol.line,
|
|
@@ -3564,6 +3955,16 @@ async function areaContext(areaName, options = {}) {
|
|
|
3564
3955
|
});
|
|
3565
3956
|
}
|
|
3566
3957
|
break;
|
|
3958
|
+
case "trigger":
|
|
3959
|
+
triggers.push({
|
|
3960
|
+
name: symbol.name,
|
|
3961
|
+
file: filePath,
|
|
3962
|
+
line: symbol.line,
|
|
3963
|
+
triggerType: symbol.triggerInfo?.triggerType || "unknown",
|
|
3964
|
+
triggerPath: symbol.triggerInfo?.triggerPath,
|
|
3965
|
+
triggerSchedule: symbol.triggerInfo?.triggerSchedule
|
|
3966
|
+
});
|
|
3967
|
+
break;
|
|
3567
3968
|
}
|
|
3568
3969
|
}
|
|
3569
3970
|
}
|
|
@@ -3579,10 +3980,11 @@ async function areaContext(areaName, options = {}) {
|
|
|
3579
3980
|
},
|
|
3580
3981
|
types,
|
|
3581
3982
|
hooks,
|
|
3582
|
-
functions,
|
|
3983
|
+
functions: functions2,
|
|
3583
3984
|
components,
|
|
3584
3985
|
services,
|
|
3585
|
-
stores
|
|
3986
|
+
stores,
|
|
3987
|
+
triggers
|
|
3586
3988
|
};
|
|
3587
3989
|
if (format === "json") {
|
|
3588
3990
|
return JSON.stringify(result, null, 2);
|
|
@@ -3741,6 +4143,42 @@ var IGNORED_DIRS3 = /* @__PURE__ */ new Set([
|
|
|
3741
4143
|
".vercel",
|
|
3742
4144
|
".analyze"
|
|
3743
4145
|
]);
|
|
4146
|
+
function resolveAreaId(target, config, allFiles) {
|
|
4147
|
+
const targetLower = target.toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g, "");
|
|
4148
|
+
if (config.areas[target]) {
|
|
4149
|
+
return target;
|
|
4150
|
+
}
|
|
4151
|
+
for (const [id, areaConfig] of Object.entries(config.areas)) {
|
|
4152
|
+
const name = areaConfig.name?.toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g, "");
|
|
4153
|
+
if (name === targetLower) {
|
|
4154
|
+
return id;
|
|
4155
|
+
}
|
|
4156
|
+
}
|
|
4157
|
+
for (const [id, name] of Object.entries(AREA_NAMES)) {
|
|
4158
|
+
const nameNormalized = name.toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g, "");
|
|
4159
|
+
if (nameNormalized === targetLower) {
|
|
4160
|
+
return id;
|
|
4161
|
+
}
|
|
4162
|
+
}
|
|
4163
|
+
for (const id of Object.keys(config.areas)) {
|
|
4164
|
+
if (id.toLowerCase().includes(targetLower)) {
|
|
4165
|
+
return id;
|
|
4166
|
+
}
|
|
4167
|
+
}
|
|
4168
|
+
const detectedAreas = /* @__PURE__ */ new Set();
|
|
4169
|
+
for (const filePath of allFiles) {
|
|
4170
|
+
const areas2 = detectFileAreas(filePath, config);
|
|
4171
|
+
for (const areaId of areas2) {
|
|
4172
|
+
if (areaId.toLowerCase().includes(targetLower)) {
|
|
4173
|
+
detectedAreas.add(areaId);
|
|
4174
|
+
}
|
|
4175
|
+
}
|
|
4176
|
+
}
|
|
4177
|
+
if (detectedAreas.size > 0) {
|
|
4178
|
+
return [...detectedAreas][0];
|
|
4179
|
+
}
|
|
4180
|
+
return target;
|
|
4181
|
+
}
|
|
3744
4182
|
async function area(target, options = {}) {
|
|
3745
4183
|
const cwd = options.cwd || process.cwd();
|
|
3746
4184
|
const format = options.format || "text";
|
|
@@ -3752,9 +4190,10 @@ async function area(target, options = {}) {
|
|
|
3752
4190
|
try {
|
|
3753
4191
|
const config = readConfig(cwd);
|
|
3754
4192
|
const allFiles = getAllCodeFiles4(cwd);
|
|
4193
|
+
const resolvedTarget = resolveAreaId(target, config, allFiles);
|
|
3755
4194
|
const filteredFiles = allFiles.filter((filePath) => !isFileIgnored(filePath, config));
|
|
3756
4195
|
const areaFiles = [];
|
|
3757
|
-
const targetLower =
|
|
4196
|
+
const targetLower = resolvedTarget.toLowerCase();
|
|
3758
4197
|
for (const filePath of filteredFiles) {
|
|
3759
4198
|
const fileAreas = detectFileAreas(filePath, config);
|
|
3760
4199
|
const belongsToArea = fileAreas.some(
|
|
@@ -3792,15 +4231,19 @@ async function area(target, options = {}) {
|
|
|
3792
4231
|
for (const cat of Object.keys(byCategory)) {
|
|
3793
4232
|
byCategory[cat].sort((a, b) => a.path.localeCompare(b.path));
|
|
3794
4233
|
}
|
|
3795
|
-
const realAreaId = findRealAreaId(target, filteredFiles, config);
|
|
4234
|
+
const realAreaId = resolvedTarget !== target ? resolvedTarget : findRealAreaId(target, filteredFiles, config);
|
|
4235
|
+
const finalAreaId = realAreaId || resolvedTarget;
|
|
4236
|
+
const nameConversionMsg = resolvedTarget !== target ? `
|
|
4237
|
+
\u{1F4A1} Buscando \xE1rea "${getAreaName(finalAreaId, config)}" (ID: ${finalAreaId})
|
|
4238
|
+
` : "";
|
|
3796
4239
|
const detectedArea = {
|
|
3797
|
-
id:
|
|
3798
|
-
name: getAreaName(
|
|
3799
|
-
description: getAreaDescription(
|
|
4240
|
+
id: finalAreaId,
|
|
4241
|
+
name: getAreaName(finalAreaId, config),
|
|
4242
|
+
description: getAreaDescription(finalAreaId, config),
|
|
3800
4243
|
files: areaFiles,
|
|
3801
4244
|
fileCount: areaFiles.length,
|
|
3802
4245
|
categories,
|
|
3803
|
-
isAutoDetected: !config.areas[
|
|
4246
|
+
isAutoDetected: !config.areas[finalAreaId]
|
|
3804
4247
|
};
|
|
3805
4248
|
const result = {
|
|
3806
4249
|
version: "1.0.0",
|
|
@@ -3811,7 +4254,8 @@ async function area(target, options = {}) {
|
|
|
3811
4254
|
if (format === "json") {
|
|
3812
4255
|
return JSON.stringify(result, null, 2);
|
|
3813
4256
|
}
|
|
3814
|
-
|
|
4257
|
+
const output = formatAreaDetailText(result, { full, filterType });
|
|
4258
|
+
return nameConversionMsg + output;
|
|
3815
4259
|
} catch (error) {
|
|
3816
4260
|
const message = error instanceof Error ? error.message : String(error);
|
|
3817
4261
|
throw new Error(`Erro ao executar area: ${message}`);
|
|
@@ -3926,9 +4370,11 @@ Ou edite manualmente o arquivo existente.
|
|
|
3926
4370
|
patterns
|
|
3927
4371
|
};
|
|
3928
4372
|
}
|
|
4373
|
+
const suggestedIgnore = detectSuggestedIgnorePatterns(allFiles);
|
|
3929
4374
|
const newConfig = {
|
|
3930
4375
|
$schema: "./areas.schema.json",
|
|
3931
4376
|
version: "1.0.0",
|
|
4377
|
+
ignore: suggestedIgnore,
|
|
3932
4378
|
areas: generatedAreas,
|
|
3933
4379
|
descriptions: {},
|
|
3934
4380
|
settings: {
|
|
@@ -3943,7 +4389,12 @@ Ou edite manualmente o arquivo existente.
|
|
|
3943
4389
|
\u2705 Arquivo criado: .analyze/areas.config.json
|
|
3944
4390
|
|
|
3945
4391
|
\u{1F4E6} \xC1reas detectadas: ${sortedAreas.length}
|
|
3946
|
-
|
|
4392
|
+
`;
|
|
4393
|
+
if (suggestedIgnore.length > 0) {
|
|
4394
|
+
out += `\u{1F6AB} Padr\xF5es ignorados: ${suggestedIgnore.length}
|
|
4395
|
+
`;
|
|
4396
|
+
}
|
|
4397
|
+
out += `
|
|
3947
4398
|
`;
|
|
3948
4399
|
for (const [areaId, files] of sortedAreas.slice(0, 15)) {
|
|
3949
4400
|
const name = getAreaName(areaId, newConfig);
|
|
@@ -3963,6 +4414,15 @@ Ou edite manualmente o arquivo existente.
|
|
|
3963
4414
|
Use 'ai-tool areas' para ver detalhes
|
|
3964
4415
|
`;
|
|
3965
4416
|
}
|
|
4417
|
+
if (suggestedIgnore.length > 0) {
|
|
4418
|
+
out += `
|
|
4419
|
+
\u{1F4CB} Padr\xF5es adicionados ao ignore:
|
|
4420
|
+
`;
|
|
4421
|
+
for (const pattern of suggestedIgnore) {
|
|
4422
|
+
out += ` \u2022 ${pattern}
|
|
4423
|
+
`;
|
|
4424
|
+
}
|
|
4425
|
+
}
|
|
3966
4426
|
out += `
|
|
3967
4427
|
\u{1F4A1} Edite o arquivo para:
|
|
3968
4428
|
- Renomear \xE1reas (campo "name")
|
|
@@ -4004,6 +4464,28 @@ function inferPatternsFromFiles(files) {
|
|
|
4004
4464
|
}
|
|
4005
4465
|
return [...patterns].sort();
|
|
4006
4466
|
}
|
|
4467
|
+
function detectSuggestedIgnorePatterns(files) {
|
|
4468
|
+
const patterns = [];
|
|
4469
|
+
if (files.some((f) => f.includes("functions/lib/"))) {
|
|
4470
|
+
patterns.push("functions/lib/**");
|
|
4471
|
+
}
|
|
4472
|
+
const testCount = files.filter((f) => /\.(test|spec)\.(ts|tsx|js|jsx)$/.test(f)).length;
|
|
4473
|
+
if (testCount > 3) {
|
|
4474
|
+
patterns.push("**/*.test.{ts,tsx,js,jsx}");
|
|
4475
|
+
patterns.push("**/*.spec.{ts,tsx,js,jsx}");
|
|
4476
|
+
}
|
|
4477
|
+
const dtsCount = files.filter((f) => f.endsWith(".d.ts")).length;
|
|
4478
|
+
if (dtsCount > 2) {
|
|
4479
|
+
patterns.push("**/*.d.ts");
|
|
4480
|
+
}
|
|
4481
|
+
const configCount = files.filter(
|
|
4482
|
+
(f) => /\.(config|conf)\.(ts|js|mjs|cjs)$/.test(f)
|
|
4483
|
+
).length;
|
|
4484
|
+
if (configCount > 2) {
|
|
4485
|
+
patterns.push("**/*.config.{ts,js,mjs,cjs}");
|
|
4486
|
+
}
|
|
4487
|
+
return patterns;
|
|
4488
|
+
}
|
|
4007
4489
|
function getAllCodeFiles5(dir, files = [], baseDir = dir) {
|
|
4008
4490
|
try {
|
|
4009
4491
|
const entries = readdirSync6(dir);
|
|
@@ -4031,6 +4513,442 @@ function getAllCodeFiles5(dir, files = [], baseDir = dir) {
|
|
|
4031
4513
|
return files;
|
|
4032
4514
|
}
|
|
4033
4515
|
|
|
4516
|
+
// src/commands/functions.ts
|
|
4517
|
+
async function functions(options = {}) {
|
|
4518
|
+
const cwd = options.cwd || process.cwd();
|
|
4519
|
+
const format = options.format || "text";
|
|
4520
|
+
const useCache = options.cache !== false;
|
|
4521
|
+
const filterTrigger = options.trigger;
|
|
4522
|
+
if (!isFirebaseProject(cwd)) {
|
|
4523
|
+
const errorMsg = "Este n\xE3o \xE9 um projeto Firebase.\nN\xE3o foi encontrado .firebaserc ou firebase.json";
|
|
4524
|
+
return format === "json" ? JSON.stringify({ error: errorMsg }) : `\u274C ${errorMsg}`;
|
|
4525
|
+
}
|
|
4526
|
+
if (!hasFirebaseFunctions(cwd)) {
|
|
4527
|
+
const errorMsg = "Projeto Firebase sem Cloud Functions.\nN\xE3o foi encontrado functions/src/index.ts";
|
|
4528
|
+
return format === "json" ? JSON.stringify({ error: errorMsg }) : `\u274C ${errorMsg}`;
|
|
4529
|
+
}
|
|
4530
|
+
try {
|
|
4531
|
+
let index;
|
|
4532
|
+
if (useCache && isCacheValid(cwd)) {
|
|
4533
|
+
const cached = getCachedSymbolsIndex(cwd);
|
|
4534
|
+
if (cached && cached.symbolsByName) {
|
|
4535
|
+
index = cached;
|
|
4536
|
+
} else {
|
|
4537
|
+
index = indexProject(cwd);
|
|
4538
|
+
cacheSymbolsIndex(cwd, index);
|
|
4539
|
+
updateCacheMeta(cwd);
|
|
4540
|
+
}
|
|
4541
|
+
} else {
|
|
4542
|
+
index = indexProject(cwd);
|
|
4543
|
+
if (useCache) {
|
|
4544
|
+
cacheSymbolsIndex(cwd, index);
|
|
4545
|
+
updateCacheMeta(cwd);
|
|
4546
|
+
}
|
|
4547
|
+
}
|
|
4548
|
+
const funcs = [];
|
|
4549
|
+
for (const fileData of Object.values(index.files)) {
|
|
4550
|
+
if (!fileData.path.includes("functions/src/")) continue;
|
|
4551
|
+
for (const symbol of fileData.symbols) {
|
|
4552
|
+
if (symbol.kind === "trigger") {
|
|
4553
|
+
funcs.push({
|
|
4554
|
+
name: symbol.name,
|
|
4555
|
+
file: symbol.file,
|
|
4556
|
+
line: symbol.line,
|
|
4557
|
+
triggerType: symbol.triggerInfo?.triggerType || "unknown",
|
|
4558
|
+
triggerPath: symbol.triggerInfo?.triggerPath,
|
|
4559
|
+
triggerSchedule: symbol.triggerInfo?.triggerSchedule,
|
|
4560
|
+
isExported: symbol.isExported
|
|
4561
|
+
});
|
|
4562
|
+
}
|
|
4563
|
+
}
|
|
4564
|
+
}
|
|
4565
|
+
const filtered = filterTrigger ? funcs.filter(
|
|
4566
|
+
(f) => f.triggerType.toLowerCase().includes(filterTrigger.toLowerCase())
|
|
4567
|
+
) : funcs;
|
|
4568
|
+
const byTrigger = {};
|
|
4569
|
+
const triggerCounts = {};
|
|
4570
|
+
for (const func of filtered) {
|
|
4571
|
+
if (!byTrigger[func.triggerType]) {
|
|
4572
|
+
byTrigger[func.triggerType] = [];
|
|
4573
|
+
triggerCounts[func.triggerType] = 0;
|
|
4574
|
+
}
|
|
4575
|
+
byTrigger[func.triggerType].push(func);
|
|
4576
|
+
triggerCounts[func.triggerType]++;
|
|
4577
|
+
}
|
|
4578
|
+
const result = {
|
|
4579
|
+
version: "1.0.0",
|
|
4580
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
4581
|
+
cwd,
|
|
4582
|
+
isFirebaseProject: true,
|
|
4583
|
+
hasFunctions: true,
|
|
4584
|
+
functions: filtered,
|
|
4585
|
+
byTrigger,
|
|
4586
|
+
summary: {
|
|
4587
|
+
total: filtered.length,
|
|
4588
|
+
exported: filtered.filter((f) => f.isExported).length,
|
|
4589
|
+
byTrigger: triggerCounts
|
|
4590
|
+
}
|
|
4591
|
+
};
|
|
4592
|
+
if (format === "json") {
|
|
4593
|
+
return JSON.stringify(result, null, 2);
|
|
4594
|
+
}
|
|
4595
|
+
return formatFunctionsText(result);
|
|
4596
|
+
} catch (error) {
|
|
4597
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
4598
|
+
throw new Error(`Erro ao executar functions: ${message}`);
|
|
4599
|
+
}
|
|
4600
|
+
}
|
|
4601
|
+
function formatFunctionsText(result) {
|
|
4602
|
+
let out = "";
|
|
4603
|
+
out += `
|
|
4604
|
+
`;
|
|
4605
|
+
out += `\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557
|
|
4606
|
+
`;
|
|
4607
|
+
out += `\u2551 \u26A1 CLOUD FUNCTIONS \u2551
|
|
4608
|
+
`;
|
|
4609
|
+
out += `\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D
|
|
4610
|
+
|
|
4611
|
+
`;
|
|
4612
|
+
out += `\u{1F4CA} RESUMO
|
|
4613
|
+
`;
|
|
4614
|
+
out += ` Total: ${result.summary.total} functions
|
|
4615
|
+
`;
|
|
4616
|
+
out += ` Exportadas: ${result.summary.exported}
|
|
4617
|
+
`;
|
|
4618
|
+
if (result.summary.total > 0) {
|
|
4619
|
+
out += `
|
|
4620
|
+
\u{1F4A1} Filtros dispon\xEDveis:
|
|
4621
|
+
`;
|
|
4622
|
+
out += ` ai-tool functions --trigger=onCall
|
|
4623
|
+
`;
|
|
4624
|
+
out += ` ai-tool functions --trigger=onDocumentCreated
|
|
4625
|
+
`;
|
|
4626
|
+
}
|
|
4627
|
+
out += `
|
|
4628
|
+
`;
|
|
4629
|
+
if (result.summary.total === 0) {
|
|
4630
|
+
out += ` \u26A0\uFE0F NENHUMA CLOUD FUNCTION DETECTADA
|
|
4631
|
+
|
|
4632
|
+
`;
|
|
4633
|
+
out += ` Poss\xEDveis causas:
|
|
4634
|
+
`;
|
|
4635
|
+
out += ` 1. O projeto n\xE3o \xE9 Firebase (n\xE3o encontrou .firebaserc ou firebase.json)
|
|
4636
|
+
`;
|
|
4637
|
+
out += ` 2. N\xE3o h\xE1 arquivo functions/src/index.ts
|
|
4638
|
+
`;
|
|
4639
|
+
out += ` 3. Os triggers n\xE3o usam padr\xF5es v2 (onCall, onDocumentCreated, etc)
|
|
4640
|
+
`;
|
|
4641
|
+
out += ` 4. O cache est\xE1 desatualizado \u2192 tente: ai-tool functions --no-cache
|
|
4642
|
+
`;
|
|
4643
|
+
out += ` 5. Para debug: DEBUG_ANALYZE=true ai-tool functions
|
|
4644
|
+
|
|
4645
|
+
`;
|
|
4646
|
+
out += ` Padr\xF5es suportados:
|
|
4647
|
+
`;
|
|
4648
|
+
out += ` export const minhaFunc = onCall((request) => { ... })
|
|
4649
|
+
`;
|
|
4650
|
+
out += ` export const minhaFunc = onDocumentCreated("path", (event) => { ... })
|
|
4651
|
+
|
|
4652
|
+
`;
|
|
4653
|
+
out += ` Documenta\xE7\xE3o: https://firebase.google.com/docs/functions
|
|
4654
|
+
`;
|
|
4655
|
+
return out;
|
|
4656
|
+
}
|
|
4657
|
+
out += `\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
|
|
4658
|
+
|
|
4659
|
+
`;
|
|
4660
|
+
const triggerOrder = [
|
|
4661
|
+
// HTTPS
|
|
4662
|
+
"onCall",
|
|
4663
|
+
"onRequest",
|
|
4664
|
+
// Firestore
|
|
4665
|
+
"onDocumentCreated",
|
|
4666
|
+
"onDocumentCreatedWithAuthContext",
|
|
4667
|
+
"onDocumentUpdated",
|
|
4668
|
+
"onDocumentUpdatedWithAuthContext",
|
|
4669
|
+
"onDocumentDeleted",
|
|
4670
|
+
"onDocumentDeletedWithAuthContext",
|
|
4671
|
+
"onDocumentWritten",
|
|
4672
|
+
"onDocumentWrittenWithAuthContext",
|
|
4673
|
+
// Realtime Database
|
|
4674
|
+
"onValueCreated",
|
|
4675
|
+
"onValueUpdated",
|
|
4676
|
+
"onValueDeleted",
|
|
4677
|
+
"onValueWritten",
|
|
4678
|
+
// Scheduler
|
|
4679
|
+
"onSchedule",
|
|
4680
|
+
// Storage
|
|
4681
|
+
"onObjectFinalized",
|
|
4682
|
+
"onObjectArchived",
|
|
4683
|
+
"onObjectDeleted",
|
|
4684
|
+
"onMetadataUpdated",
|
|
4685
|
+
// Pub/Sub
|
|
4686
|
+
"onMessagePublished",
|
|
4687
|
+
// Identity
|
|
4688
|
+
"beforeUserCreated",
|
|
4689
|
+
"beforeUserSignedIn"
|
|
4690
|
+
];
|
|
4691
|
+
const sortedTriggers = Object.keys(result.byTrigger).sort((a, b) => {
|
|
4692
|
+
const aIdx = triggerOrder.indexOf(a);
|
|
4693
|
+
const bIdx = triggerOrder.indexOf(b);
|
|
4694
|
+
if (aIdx === -1 && bIdx === -1) return a.localeCompare(b);
|
|
4695
|
+
if (aIdx === -1) return 1;
|
|
4696
|
+
if (bIdx === -1) return -1;
|
|
4697
|
+
return aIdx - bIdx;
|
|
4698
|
+
});
|
|
4699
|
+
for (const trigger of sortedTriggers) {
|
|
4700
|
+
const funcs = result.byTrigger[trigger];
|
|
4701
|
+
const icon = getTriggerIcon(trigger);
|
|
4702
|
+
out += `${icon} ${trigger} (${funcs.length})
|
|
4703
|
+
|
|
4704
|
+
`;
|
|
4705
|
+
for (const func of funcs) {
|
|
4706
|
+
const exportTag = func.isExported ? "" : " [n\xE3o exportada]";
|
|
4707
|
+
out += ` ${func.name}${exportTag}
|
|
4708
|
+
`;
|
|
4709
|
+
out += ` \u{1F4C1} ${func.file}:${func.line}
|
|
4710
|
+
`;
|
|
4711
|
+
if (func.triggerPath) {
|
|
4712
|
+
out += ` \u{1F4CD} path: ${func.triggerPath}
|
|
4713
|
+
`;
|
|
4714
|
+
}
|
|
4715
|
+
if (func.triggerSchedule) {
|
|
4716
|
+
out += ` \u23F0 schedule: ${func.triggerSchedule}
|
|
4717
|
+
`;
|
|
4718
|
+
}
|
|
4719
|
+
}
|
|
4720
|
+
out += `
|
|
4721
|
+
`;
|
|
4722
|
+
}
|
|
4723
|
+
return out;
|
|
4724
|
+
}
|
|
4725
|
+
function getTriggerIcon(trigger) {
|
|
4726
|
+
if (trigger.includes("Call") || trigger.includes("Request")) return "\u{1F310}";
|
|
4727
|
+
if (trigger.includes("Document") || trigger.includes("Value")) return "\u{1F525}";
|
|
4728
|
+
if (trigger.includes("Schedule")) return "\u23F0";
|
|
4729
|
+
if (trigger.includes("Object") || trigger.includes("Metadata")) return "\u{1F4E6}";
|
|
4730
|
+
if (trigger.includes("Message") || trigger.includes("Pub")) return "\u{1F4E8}";
|
|
4731
|
+
if (trigger.includes("User") || trigger.includes("before")) return "\u{1F464}";
|
|
4732
|
+
if (trigger.includes("Alert") || trigger.includes("Issue")) return "\u{1F6A8}";
|
|
4733
|
+
if (trigger.includes("Config")) return "\u2699\uFE0F";
|
|
4734
|
+
if (trigger.includes("Task")) return "\u{1F4CB}";
|
|
4735
|
+
if (trigger.includes("Test")) return "\u{1F9EA}";
|
|
4736
|
+
return "\u26A1";
|
|
4737
|
+
}
|
|
4738
|
+
|
|
4739
|
+
// src/commands/find.ts
|
|
4740
|
+
async function find(query, options = {}) {
|
|
4741
|
+
const cwd = options.cwd || process.cwd();
|
|
4742
|
+
const format = options.format || "text";
|
|
4743
|
+
const filterType = options.type || "all";
|
|
4744
|
+
const filterArea = options.area;
|
|
4745
|
+
const defOnly = options.def ?? false;
|
|
4746
|
+
const refsOnly = options.refs ?? false;
|
|
4747
|
+
const useCache = options.cache !== false;
|
|
4748
|
+
const listAllMode = !query && defOnly && filterType && filterType !== "all";
|
|
4749
|
+
if (!query && !listAllMode) {
|
|
4750
|
+
throw new Error("Query \xE9 obrigat\xF3ria. Exemplo: ai-tool find useAuth\n Ou use --def para listar todos de um tipo: ai-tool find --type=trigger --def");
|
|
4751
|
+
}
|
|
4752
|
+
try {
|
|
4753
|
+
let index;
|
|
4754
|
+
let fromCache = false;
|
|
4755
|
+
if (useCache && isCacheValid(cwd)) {
|
|
4756
|
+
const cached = getCachedSymbolsIndex(cwd);
|
|
4757
|
+
if (cached && cached.symbolsByName) {
|
|
4758
|
+
index = cached;
|
|
4759
|
+
fromCache = true;
|
|
4760
|
+
} else {
|
|
4761
|
+
index = indexProject(cwd);
|
|
4762
|
+
cacheSymbolsIndex(cwd, index);
|
|
4763
|
+
updateCacheMeta(cwd);
|
|
4764
|
+
}
|
|
4765
|
+
} else {
|
|
4766
|
+
index = indexProject(cwd);
|
|
4767
|
+
if (useCache) {
|
|
4768
|
+
cacheSymbolsIndex(cwd, index);
|
|
4769
|
+
updateCacheMeta(cwd);
|
|
4770
|
+
}
|
|
4771
|
+
}
|
|
4772
|
+
let allowedFiles = null;
|
|
4773
|
+
if (filterArea) {
|
|
4774
|
+
const config = readConfig(cwd);
|
|
4775
|
+
const areaLower = filterArea.toLowerCase();
|
|
4776
|
+
allowedFiles = /* @__PURE__ */ new Set();
|
|
4777
|
+
for (const filePath of Object.keys(index.files)) {
|
|
4778
|
+
if (isFileIgnored(filePath, config)) continue;
|
|
4779
|
+
const fileAreas = detectFileAreas(filePath, config);
|
|
4780
|
+
const belongsToArea = fileAreas.some(
|
|
4781
|
+
(a) => a.toLowerCase() === areaLower || a.toLowerCase().includes(areaLower)
|
|
4782
|
+
);
|
|
4783
|
+
if (belongsToArea) {
|
|
4784
|
+
allowedFiles.add(filePath);
|
|
4785
|
+
}
|
|
4786
|
+
}
|
|
4787
|
+
if (allowedFiles.size === 0) {
|
|
4788
|
+
return format === "json" ? JSON.stringify({ error: `Nenhum arquivo encontrado na \xE1rea "${filterArea}"` }) : `\u274C Nenhum arquivo encontrado na \xE1rea "${filterArea}"`;
|
|
4789
|
+
}
|
|
4790
|
+
}
|
|
4791
|
+
const matches = searchInIndex(index, query, filterType, allowedFiles);
|
|
4792
|
+
let definition = null;
|
|
4793
|
+
let references = [];
|
|
4794
|
+
for (const match of matches) {
|
|
4795
|
+
if (match.matchType === "definition") {
|
|
4796
|
+
if (!definition) {
|
|
4797
|
+
definition = match;
|
|
4798
|
+
}
|
|
4799
|
+
} else {
|
|
4800
|
+
references.push(match);
|
|
4801
|
+
}
|
|
4802
|
+
}
|
|
4803
|
+
if (defOnly) {
|
|
4804
|
+
references = [];
|
|
4805
|
+
}
|
|
4806
|
+
if (refsOnly) {
|
|
4807
|
+
definition = null;
|
|
4808
|
+
}
|
|
4809
|
+
const uniqueFiles = /* @__PURE__ */ new Set();
|
|
4810
|
+
if (definition) uniqueFiles.add(definition.file);
|
|
4811
|
+
for (const ref of references) {
|
|
4812
|
+
uniqueFiles.add(ref.file);
|
|
4813
|
+
}
|
|
4814
|
+
const result = {
|
|
4815
|
+
version: "1.0.0",
|
|
4816
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
4817
|
+
query,
|
|
4818
|
+
filters: {
|
|
4819
|
+
type: filterType !== "all" ? filterType : void 0,
|
|
4820
|
+
area: filterArea,
|
|
4821
|
+
defOnly,
|
|
4822
|
+
refsOnly
|
|
4823
|
+
},
|
|
4824
|
+
definition,
|
|
4825
|
+
references,
|
|
4826
|
+
summary: {
|
|
4827
|
+
definitions: definition ? 1 : 0,
|
|
4828
|
+
references: references.length,
|
|
4829
|
+
files: uniqueFiles.size
|
|
4830
|
+
},
|
|
4831
|
+
fromCache
|
|
4832
|
+
};
|
|
4833
|
+
if (format === "json") {
|
|
4834
|
+
return JSON.stringify(result, null, 2);
|
|
4835
|
+
}
|
|
4836
|
+
return formatFindText(result);
|
|
4837
|
+
} catch (error) {
|
|
4838
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
4839
|
+
throw new Error(`Erro ao executar find: ${message}`);
|
|
4840
|
+
}
|
|
4841
|
+
}
|
|
4842
|
+
function searchInIndex(index, query, filterType, allowedFiles) {
|
|
4843
|
+
const matches = [];
|
|
4844
|
+
const queryLower = query?.toLowerCase() || "";
|
|
4845
|
+
const processedSymbols = /* @__PURE__ */ new Set();
|
|
4846
|
+
const listAllMode = !query && filterType && filterType !== "all";
|
|
4847
|
+
for (const [name, symbols] of Object.entries(index.symbolsByName)) {
|
|
4848
|
+
const nameLower = name.toLowerCase();
|
|
4849
|
+
const shouldInclude = listAllMode || nameLower === queryLower || nameLower.includes(queryLower);
|
|
4850
|
+
if (shouldInclude) {
|
|
4851
|
+
for (const symbol of symbols) {
|
|
4852
|
+
if (allowedFiles && !allowedFiles.has(symbol.file)) continue;
|
|
4853
|
+
if (!matchesType(symbol.kind, filterType)) continue;
|
|
4854
|
+
const key = `${symbol.file}:${symbol.line}:${symbol.name}`;
|
|
4855
|
+
if (processedSymbols.has(key)) continue;
|
|
4856
|
+
processedSymbols.add(key);
|
|
4857
|
+
const fileData = index.files[symbol.file];
|
|
4858
|
+
matches.push({
|
|
4859
|
+
file: symbol.file,
|
|
4860
|
+
line: symbol.line,
|
|
4861
|
+
column: 0,
|
|
4862
|
+
code: symbol.signature,
|
|
4863
|
+
matchType: "definition",
|
|
4864
|
+
symbolType: mapKindToSymbolType(symbol.kind),
|
|
4865
|
+
category: fileData?.category || "other"
|
|
4866
|
+
});
|
|
4867
|
+
}
|
|
4868
|
+
}
|
|
4869
|
+
}
|
|
4870
|
+
if (!listAllMode) {
|
|
4871
|
+
for (const [filePath, fileData] of Object.entries(index.files)) {
|
|
4872
|
+
if (allowedFiles && !allowedFiles.has(filePath)) continue;
|
|
4873
|
+
for (const imp of fileData.imports) {
|
|
4874
|
+
for (const spec of imp.specifiers) {
|
|
4875
|
+
const specLower = spec.toLowerCase();
|
|
4876
|
+
if (specLower === queryLower || specLower.includes(queryLower)) {
|
|
4877
|
+
const key = `import:${filePath}:${spec}:${imp.source}`;
|
|
4878
|
+
if (processedSymbols.has(key)) continue;
|
|
4879
|
+
processedSymbols.add(key);
|
|
4880
|
+
matches.push({
|
|
4881
|
+
file: filePath,
|
|
4882
|
+
line: 1,
|
|
4883
|
+
// Imports geralmente estão no topo
|
|
4884
|
+
column: 0,
|
|
4885
|
+
code: `import { ${spec} } from '${imp.source}'`,
|
|
4886
|
+
matchType: "import",
|
|
4887
|
+
symbolType: inferSymbolTypeFromName(spec),
|
|
4888
|
+
category: fileData.category
|
|
4889
|
+
});
|
|
4890
|
+
}
|
|
4891
|
+
}
|
|
4892
|
+
}
|
|
4893
|
+
}
|
|
4894
|
+
}
|
|
4895
|
+
matches.sort((a, b) => {
|
|
4896
|
+
const order = { definition: 0, import: 1, usage: 2 };
|
|
4897
|
+
const orderDiff = order[a.matchType] - order[b.matchType];
|
|
4898
|
+
if (orderDiff !== 0) return orderDiff;
|
|
4899
|
+
return a.file.localeCompare(b.file) || a.line - b.line;
|
|
4900
|
+
});
|
|
4901
|
+
return matches;
|
|
4902
|
+
}
|
|
4903
|
+
function matchesType(kind, filter) {
|
|
4904
|
+
if (filter === "all") return true;
|
|
4905
|
+
switch (filter) {
|
|
4906
|
+
case "function":
|
|
4907
|
+
return kind === "function" || kind === "trigger";
|
|
4908
|
+
case "type":
|
|
4909
|
+
return kind === "type" || kind === "interface" || kind === "enum";
|
|
4910
|
+
case "const":
|
|
4911
|
+
return kind === "const";
|
|
4912
|
+
case "component":
|
|
4913
|
+
return kind === "component";
|
|
4914
|
+
case "hook":
|
|
4915
|
+
return kind === "hook";
|
|
4916
|
+
case "trigger":
|
|
4917
|
+
return kind === "trigger";
|
|
4918
|
+
default:
|
|
4919
|
+
return true;
|
|
4920
|
+
}
|
|
4921
|
+
}
|
|
4922
|
+
function mapKindToSymbolType(kind) {
|
|
4923
|
+
switch (kind) {
|
|
4924
|
+
case "function":
|
|
4925
|
+
return "function";
|
|
4926
|
+
case "hook":
|
|
4927
|
+
return "hook";
|
|
4928
|
+
case "component":
|
|
4929
|
+
return "component";
|
|
4930
|
+
case "type":
|
|
4931
|
+
case "interface":
|
|
4932
|
+
case "enum":
|
|
4933
|
+
return "type";
|
|
4934
|
+
case "const":
|
|
4935
|
+
return "const";
|
|
4936
|
+
case "trigger":
|
|
4937
|
+
return "trigger";
|
|
4938
|
+
default:
|
|
4939
|
+
return "function";
|
|
4940
|
+
}
|
|
4941
|
+
}
|
|
4942
|
+
function inferSymbolTypeFromName(name) {
|
|
4943
|
+
if (name.startsWith("use") && name.length > 3 && name[3] === name[3].toUpperCase()) {
|
|
4944
|
+
return "hook";
|
|
4945
|
+
}
|
|
4946
|
+
if (name[0] === name[0].toUpperCase()) {
|
|
4947
|
+
return "type";
|
|
4948
|
+
}
|
|
4949
|
+
return "function";
|
|
4950
|
+
}
|
|
4951
|
+
|
|
4034
4952
|
// src/index.ts
|
|
4035
4953
|
import { createRequire } from "module";
|
|
4036
4954
|
var require2 = createRequire(import.meta.url);
|
|
@@ -4042,7 +4960,6 @@ export {
|
|
|
4042
4960
|
categoryIcons,
|
|
4043
4961
|
isEntryPoint,
|
|
4044
4962
|
isCodeFile,
|
|
4045
|
-
formatFindText,
|
|
4046
4963
|
isFirebaseProject,
|
|
4047
4964
|
hasFirebaseFunctions,
|
|
4048
4965
|
isExportedCloudFunction,
|
|
@@ -4050,10 +4967,7 @@ export {
|
|
|
4050
4967
|
clearFirebaseCache,
|
|
4051
4968
|
getCacheDir,
|
|
4052
4969
|
isCacheValid,
|
|
4053
|
-
updateCacheMeta,
|
|
4054
4970
|
invalidateCache,
|
|
4055
|
-
cacheSymbolsIndex,
|
|
4056
|
-
getCachedSymbolsIndex,
|
|
4057
4971
|
configExists,
|
|
4058
4972
|
readConfig,
|
|
4059
4973
|
writeConfig,
|
|
@@ -4065,7 +4979,6 @@ export {
|
|
|
4065
4979
|
KEYWORD_PATTERNS,
|
|
4066
4980
|
AREA_NAMES,
|
|
4067
4981
|
AREA_DESCRIPTIONS,
|
|
4068
|
-
isFileIgnored,
|
|
4069
4982
|
detectFileAreas,
|
|
4070
4983
|
getAreaName,
|
|
4071
4984
|
getAreaDescription,
|
|
@@ -4084,11 +4997,12 @@ export {
|
|
|
4084
4997
|
formatInvalidCommand,
|
|
4085
4998
|
impact,
|
|
4086
4999
|
suggest,
|
|
4087
|
-
indexProject,
|
|
4088
5000
|
context,
|
|
4089
5001
|
areaContext,
|
|
4090
5002
|
areas,
|
|
4091
5003
|
area,
|
|
4092
5004
|
areasInit,
|
|
5005
|
+
find,
|
|
5006
|
+
functions,
|
|
4093
5007
|
VERSION
|
|
4094
5008
|
};
|