@justmpm/ai-tool 0.3.1 → 0.4.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/README.md +136 -79
- package/dist/{chunk-4HXWM7PK.js → chunk-33DHCY2H.js} +1416 -31
- package/dist/cli.js +42 -8
- package/dist/index.d.ts +249 -2
- package/dist/index.js +49 -3
- package/dist/{server-YV4BWISA.js → server-62WVGNJD.js} +246 -1
- package/package.json +2 -1
|
@@ -5,8 +5,8 @@ import skott from "skott";
|
|
|
5
5
|
function detectCategory(filePath) {
|
|
6
6
|
const normalized = filePath.replace(/\\/g, "/").toLowerCase();
|
|
7
7
|
const fileName = normalized.split("/").pop() || "";
|
|
8
|
-
const fileNameNoExt = fileName.replace(/\.(tsx?|jsx?|mjs|cjs)$/, "");
|
|
9
|
-
if (fileName.includes(".test.") || fileName.includes(".spec.") || normalized.includes("/__tests__/") || normalized.includes("/test/")) {
|
|
8
|
+
const fileNameNoExt = fileName.replace(/\.(tsx?|jsx?|mjs|cjs|vue|svelte|astro)$/, "");
|
|
9
|
+
if (fileName.includes(".test.") || fileName.includes(".spec.") || normalized.includes("/__tests__/") || normalized.includes("/test/") || normalized.includes("/tests/")) {
|
|
10
10
|
return "test";
|
|
11
11
|
}
|
|
12
12
|
if (isConfigFile(fileName)) {
|
|
@@ -20,23 +20,55 @@ function detectCategory(filePath) {
|
|
|
20
20
|
)) {
|
|
21
21
|
return "page";
|
|
22
22
|
}
|
|
23
|
-
if (normalized.includes("/
|
|
24
|
-
|
|
23
|
+
if (normalized.includes("/pages/") && !normalized.includes("/pages/api/")) {
|
|
24
|
+
return "page";
|
|
25
|
+
}
|
|
26
|
+
if (normalized.includes("/pages/") || normalized.includes("/views/") || normalized.includes("/screens/")) {
|
|
27
|
+
if (!normalized.includes("/components/")) {
|
|
28
|
+
return "page";
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
if (fileNameNoExt.endsWith("page") || fileNameNoExt.endsWith("view") || fileNameNoExt.endsWith("screen")) {
|
|
32
|
+
return "page";
|
|
33
|
+
}
|
|
34
|
+
if (normalized.includes("/routes/") && normalized.includes("/app/")) {
|
|
35
|
+
return "page";
|
|
36
|
+
}
|
|
37
|
+
if (normalized.includes("/layouts/")) {
|
|
38
|
+
return "layout";
|
|
39
|
+
}
|
|
40
|
+
if (fileNameNoExt === "+page" || fileNameNoExt === "+page.server") return "page";
|
|
41
|
+
if (fileNameNoExt === "+layout" || fileNameNoExt === "+layout.server") return "layout";
|
|
42
|
+
if (fileNameNoExt === "+server") return "route";
|
|
43
|
+
if (fileNameNoExt === "+error") return "page";
|
|
44
|
+
if (fileName.endsWith(".astro") && normalized.includes("/pages/")) {
|
|
45
|
+
return "page";
|
|
46
|
+
}
|
|
47
|
+
if (fileName.endsWith(".astro") && normalized.includes("/layouts/")) {
|
|
48
|
+
return "layout";
|
|
49
|
+
}
|
|
50
|
+
if (normalized.includes("/api/") || normalized.includes("/server/") || fileNameNoExt.endsWith(".server") || fileNameNoExt === "+server") {
|
|
51
|
+
return "route";
|
|
52
|
+
}
|
|
25
53
|
if (fileNameNoExt.startsWith("use") && fileNameNoExt.length > 3) return "hook";
|
|
26
54
|
if (normalized.includes("/hooks/")) return "hook";
|
|
27
|
-
if (normalized.includes("/
|
|
55
|
+
if (normalized.includes("/composables/")) return "hook";
|
|
56
|
+
if (normalized.includes("/stores/") && fileName.endsWith(".ts")) return "hook";
|
|
57
|
+
if (normalized.includes("/types/") || normalized.includes("/interfaces/") || fileName.endsWith(".d.ts") || fileNameNoExt === "types" || fileNameNoExt === "interfaces") {
|
|
28
58
|
return "type";
|
|
29
59
|
}
|
|
30
|
-
if (normalized.includes("/services/") || fileNameNoExt.endsWith("service")) {
|
|
60
|
+
if (normalized.includes("/services/") || normalized.includes("/api-client/") || fileNameNoExt.endsWith("service") || fileNameNoExt.endsWith("api")) {
|
|
31
61
|
return "service";
|
|
32
62
|
}
|
|
33
|
-
if (normalized.includes("/store/") || normalized.includes("/stores/") || normalized.includes("/context/") || normalized.includes("/contexts/") || normalized.includes("/providers/"))
|
|
63
|
+
if (normalized.includes("/store/") || normalized.includes("/stores/") || normalized.includes("/context/") || normalized.includes("/contexts/") || normalized.includes("/providers/") || normalized.includes("/state/") || // Zustand, Redux, Pinia, etc
|
|
64
|
+
fileNameNoExt.endsWith("store") || fileNameNoExt.endsWith("slice") || fileNameNoExt.endsWith("reducer")) {
|
|
34
65
|
return "store";
|
|
35
66
|
}
|
|
36
|
-
if (normalized.includes("/utils/") || normalized.includes("/lib/") || normalized.includes("/helpers/") || normalized.includes("/common/")) {
|
|
67
|
+
if (normalized.includes("/utils/") || normalized.includes("/lib/") || normalized.includes("/helpers/") || normalized.includes("/common/") || normalized.includes("/shared/") || fileNameNoExt.endsWith("utils") || fileNameNoExt.endsWith("helpers")) {
|
|
37
68
|
return "util";
|
|
38
69
|
}
|
|
39
|
-
if (normalized.includes("/components/") || normalized.includes("/ui/") || normalized.includes("/features/"))
|
|
70
|
+
if (normalized.includes("/components/") || normalized.includes("/ui/") || normalized.includes("/features/") || normalized.includes("/modules/") || // Vue/Svelte single file components
|
|
71
|
+
fileName.endsWith(".vue") || fileName.endsWith(".svelte")) {
|
|
40
72
|
return "component";
|
|
41
73
|
}
|
|
42
74
|
return "other";
|
|
@@ -252,6 +284,18 @@ function formatDeadText(result) {
|
|
|
252
284
|
`;
|
|
253
285
|
}
|
|
254
286
|
out += `
|
|
287
|
+
`;
|
|
288
|
+
}
|
|
289
|
+
if (result.filters?.firebase.detected) {
|
|
290
|
+
out += `\u{1F525} FIREBASE CLOUD FUNCTIONS
|
|
291
|
+
`;
|
|
292
|
+
out += ` Projeto Firebase detectado.
|
|
293
|
+
`;
|
|
294
|
+
if (result.filters.firebase.excludedCount > 0) {
|
|
295
|
+
out += ` ${result.filters.firebase.excludedCount} arquivo(s) filtrado(s) (exportados em functions/src/index.ts)
|
|
296
|
+
`;
|
|
297
|
+
}
|
|
298
|
+
out += `
|
|
255
299
|
`;
|
|
256
300
|
}
|
|
257
301
|
out += `\u{1F4A1} SUGEST\xC3O
|
|
@@ -585,10 +629,271 @@ function formatContextText(result) {
|
|
|
585
629
|
`;
|
|
586
630
|
return out;
|
|
587
631
|
}
|
|
632
|
+
function formatAreasText(result) {
|
|
633
|
+
let out = "";
|
|
634
|
+
out += `
|
|
635
|
+
`;
|
|
636
|
+
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
|
|
637
|
+
`;
|
|
638
|
+
out += `\u2551 \u{1F4E6} PROJECT AREAS \u2551
|
|
639
|
+
`;
|
|
640
|
+
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
|
|
641
|
+
|
|
642
|
+
`;
|
|
643
|
+
out += `\u{1F4CA} RESUMO
|
|
644
|
+
`;
|
|
645
|
+
out += ` \xC1reas: ${result.summary.totalAreas}
|
|
646
|
+
`;
|
|
647
|
+
out += ` Arquivos: ${result.summary.totalFiles}
|
|
648
|
+
`;
|
|
649
|
+
if (result.summary.unmappedCount > 0) {
|
|
650
|
+
out += ` \u26A0\uFE0F Sem \xE1rea: ${result.summary.unmappedCount}
|
|
651
|
+
`;
|
|
652
|
+
}
|
|
653
|
+
out += `
|
|
654
|
+
`;
|
|
655
|
+
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
|
|
656
|
+
|
|
657
|
+
`;
|
|
658
|
+
out += `\u{1F4E6} \xC1REAS DETECTADAS
|
|
659
|
+
|
|
660
|
+
`;
|
|
661
|
+
for (const area2 of result.areas) {
|
|
662
|
+
const autoTag = area2.isAutoDetected ? " (auto)" : "";
|
|
663
|
+
out += ` ${area2.name.padEnd(25)} ${String(area2.fileCount).padStart(4)} arquivos${autoTag}
|
|
664
|
+
`;
|
|
665
|
+
const catSummary = Object.entries(area2.categories).sort((a, b) => b[1] - a[1]).slice(0, 4).map(([cat, count]) => `${categoryIcons[cat]}${count}`).join(" ");
|
|
666
|
+
if (catSummary) {
|
|
667
|
+
out += ` ${catSummary}
|
|
668
|
+
`;
|
|
669
|
+
}
|
|
670
|
+
if (area2.description) {
|
|
671
|
+
out += ` ${area2.description}
|
|
672
|
+
`;
|
|
673
|
+
}
|
|
674
|
+
out += `
|
|
675
|
+
`;
|
|
676
|
+
}
|
|
677
|
+
if (result.unmapped.length > 0) {
|
|
678
|
+
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
|
|
679
|
+
|
|
680
|
+
`;
|
|
681
|
+
out += `\u26A0\uFE0F ARQUIVOS SEM \xC1REA (${result.unmapped.length})
|
|
682
|
+
|
|
683
|
+
`;
|
|
684
|
+
for (const file of result.unmapped.slice(0, 10)) {
|
|
685
|
+
const icon = categoryIcons[file.category];
|
|
686
|
+
out += ` ${icon} ${file.path}
|
|
687
|
+
`;
|
|
688
|
+
}
|
|
689
|
+
if (result.unmapped.length > 10) {
|
|
690
|
+
out += ` ... e mais ${result.unmapped.length - 10}
|
|
691
|
+
`;
|
|
692
|
+
}
|
|
693
|
+
out += `
|
|
694
|
+
\u{1F4A1} Adicione padr\xF5es em .analyze/areas.config.json
|
|
695
|
+
`;
|
|
696
|
+
out += ` ou execute 'ai-tool areas init' para gerar configura\xE7\xE3o
|
|
697
|
+
`;
|
|
698
|
+
}
|
|
699
|
+
out += `
|
|
700
|
+
\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
|
|
701
|
+
|
|
702
|
+
`;
|
|
703
|
+
out += `\u{1F4A1} Use 'ai-tool area <nome>' para ver detalhes de uma \xE1rea
|
|
704
|
+
`;
|
|
705
|
+
out += ` Exemplo: ai-tool area meus-pets
|
|
706
|
+
`;
|
|
707
|
+
return out;
|
|
708
|
+
}
|
|
709
|
+
function formatAreaDetailText(result, options = {}) {
|
|
710
|
+
const { full = false, filterType } = options;
|
|
711
|
+
const { area: area2, byCategory } = result;
|
|
712
|
+
let out = "";
|
|
713
|
+
out += `
|
|
714
|
+
`;
|
|
715
|
+
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
|
|
716
|
+
`;
|
|
717
|
+
out += `\u2551 \u{1F4E6} AREA DETAIL \u2551
|
|
718
|
+
`;
|
|
719
|
+
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
|
|
720
|
+
|
|
721
|
+
`;
|
|
722
|
+
out += `\u{1F4E6} ${area2.name}
|
|
723
|
+
`;
|
|
724
|
+
if (area2.description) {
|
|
725
|
+
out += ` ${area2.description}
|
|
726
|
+
`;
|
|
727
|
+
}
|
|
728
|
+
out += `
|
|
729
|
+
`;
|
|
730
|
+
out += `\u{1F4CA} Resumo: ${area2.fileCount} arquivos
|
|
731
|
+
`;
|
|
732
|
+
const catOrder = [
|
|
733
|
+
"page",
|
|
734
|
+
"layout",
|
|
735
|
+
"route",
|
|
736
|
+
"component",
|
|
737
|
+
"hook",
|
|
738
|
+
"service",
|
|
739
|
+
"store",
|
|
740
|
+
"util",
|
|
741
|
+
"type",
|
|
742
|
+
"config",
|
|
743
|
+
"test",
|
|
744
|
+
"other"
|
|
745
|
+
];
|
|
746
|
+
const catParts = [];
|
|
747
|
+
for (const cat of catOrder) {
|
|
748
|
+
const count = area2.categories[cat];
|
|
749
|
+
if (count) {
|
|
750
|
+
catParts.push(`${categoryIcons[cat]} ${cat}: ${count}`);
|
|
751
|
+
}
|
|
752
|
+
}
|
|
753
|
+
out += catParts.join(" ");
|
|
754
|
+
out += `
|
|
755
|
+
|
|
756
|
+
`;
|
|
757
|
+
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
|
|
758
|
+
|
|
759
|
+
`;
|
|
760
|
+
if (filterType) {
|
|
761
|
+
const files = byCategory[filterType] || [];
|
|
762
|
+
out += `${categoryIcons[filterType]} ${filterType.toUpperCase()} (${files.length})
|
|
763
|
+
|
|
764
|
+
`;
|
|
765
|
+
for (const file of files) {
|
|
766
|
+
const desc = file.description ? ` ${file.description}` : "";
|
|
767
|
+
out += ` ${file.path}${desc}
|
|
768
|
+
`;
|
|
769
|
+
}
|
|
770
|
+
return out;
|
|
771
|
+
}
|
|
772
|
+
const maxFilesPerCategory = full ? 100 : 8;
|
|
773
|
+
for (const cat of catOrder) {
|
|
774
|
+
const files = byCategory[cat];
|
|
775
|
+
if (!files || files.length === 0) continue;
|
|
776
|
+
out += `${categoryIcons[cat]} ${cat.toUpperCase()} (${files.length})
|
|
777
|
+
`;
|
|
778
|
+
const filesToShow = files.slice(0, maxFilesPerCategory);
|
|
779
|
+
const remaining = files.length - filesToShow.length;
|
|
780
|
+
for (const file of filesToShow) {
|
|
781
|
+
const maxPathLen = 50;
|
|
782
|
+
let displayPath = file.path;
|
|
783
|
+
if (displayPath.length > maxPathLen) {
|
|
784
|
+
displayPath = "..." + displayPath.slice(-maxPathLen + 3);
|
|
785
|
+
}
|
|
786
|
+
const desc = file.description || "";
|
|
787
|
+
out += ` ${displayPath.padEnd(maxPathLen + 2)} ${desc}
|
|
788
|
+
`;
|
|
789
|
+
}
|
|
790
|
+
if (remaining > 0) {
|
|
791
|
+
out += ` ... e mais ${remaining}
|
|
792
|
+
`;
|
|
793
|
+
}
|
|
794
|
+
out += `
|
|
795
|
+
`;
|
|
796
|
+
}
|
|
797
|
+
if (!full && area2.fileCount > 20) {
|
|
798
|
+
out += `\u{1F4A1} Use --full para ver todos os arquivos
|
|
799
|
+
`;
|
|
800
|
+
}
|
|
801
|
+
return out;
|
|
802
|
+
}
|
|
803
|
+
|
|
804
|
+
// src/cache/index.ts
|
|
805
|
+
import { existsSync as existsSync2, mkdirSync, readFileSync as readFileSync2, writeFileSync, statSync, readdirSync } from "fs";
|
|
806
|
+
import { join as join2, extname } from "path";
|
|
807
|
+
|
|
808
|
+
// src/utils/firebase.ts
|
|
809
|
+
import { existsSync, readFileSync } from "fs";
|
|
810
|
+
import { join, relative, normalize } from "path";
|
|
811
|
+
var indexContentCache = null;
|
|
812
|
+
function isFirebaseProject(cwd) {
|
|
813
|
+
const firebaserc = join(cwd, ".firebaserc");
|
|
814
|
+
const firebaseJson = join(cwd, "firebase.json");
|
|
815
|
+
return existsSync(firebaserc) || existsSync(firebaseJson);
|
|
816
|
+
}
|
|
817
|
+
function hasFirebaseFunctions(cwd) {
|
|
818
|
+
if (!isFirebaseProject(cwd)) {
|
|
819
|
+
return false;
|
|
820
|
+
}
|
|
821
|
+
const functionsIndexPath = join(cwd, "functions", "src", "index.ts");
|
|
822
|
+
return existsSync(functionsIndexPath);
|
|
823
|
+
}
|
|
824
|
+
function getFunctionsExports(cwd) {
|
|
825
|
+
const functionsIndexPath = join(cwd, "functions", "src", "index.ts");
|
|
826
|
+
if (indexContentCache && indexContentCache.cwd === cwd) {
|
|
827
|
+
return indexContentCache.exports;
|
|
828
|
+
}
|
|
829
|
+
if (!existsSync(functionsIndexPath)) {
|
|
830
|
+
return /* @__PURE__ */ new Set();
|
|
831
|
+
}
|
|
832
|
+
try {
|
|
833
|
+
const content = readFileSync(functionsIndexPath, "utf-8");
|
|
834
|
+
const exports = /* @__PURE__ */ new Set();
|
|
835
|
+
const namedExportRegex = /export\s*\{[^}]+\}\s*from\s*["']\.\/([^"']+)["']/g;
|
|
836
|
+
let match;
|
|
837
|
+
while ((match = namedExportRegex.exec(content)) !== null) {
|
|
838
|
+
const modulePath = match[1].replace(/\.js$/, "").replace(/\.ts$/, "");
|
|
839
|
+
exports.add(modulePath);
|
|
840
|
+
}
|
|
841
|
+
const starExportRegex = /export\s*\*\s*from\s*["']\.\/([^"']+)["']/g;
|
|
842
|
+
while ((match = starExportRegex.exec(content)) !== null) {
|
|
843
|
+
const modulePath = match[1].replace(/\.js$/, "").replace(/\.ts$/, "");
|
|
844
|
+
exports.add(modulePath);
|
|
845
|
+
}
|
|
846
|
+
const importRegex = /import\s*(?:\{[^}]+\}|\*\s+as\s+\w+)\s*from\s*["']\.\/([^"']+)["']/g;
|
|
847
|
+
while ((match = importRegex.exec(content)) !== null) {
|
|
848
|
+
const modulePath = match[1].replace(/\.js$/, "").replace(/\.ts$/, "");
|
|
849
|
+
exports.add(modulePath);
|
|
850
|
+
}
|
|
851
|
+
indexContentCache = { cwd, content, exports };
|
|
852
|
+
return exports;
|
|
853
|
+
} catch {
|
|
854
|
+
return /* @__PURE__ */ new Set();
|
|
855
|
+
}
|
|
856
|
+
}
|
|
857
|
+
function isExportedCloudFunction(filePath, cwd) {
|
|
858
|
+
const normalized = normalize(filePath).replace(/\\/g, "/");
|
|
859
|
+
if (!normalized.includes("functions/src/")) {
|
|
860
|
+
return false;
|
|
861
|
+
}
|
|
862
|
+
if (normalized.endsWith("functions/src/index.ts")) {
|
|
863
|
+
return false;
|
|
864
|
+
}
|
|
865
|
+
const functionsDir = join(cwd, "functions", "src");
|
|
866
|
+
const relativePath = relative(functionsDir, join(cwd, filePath)).replace(/\\/g, "/").replace(/\.ts$/, "").replace(/\.js$/, "");
|
|
867
|
+
const exports = getFunctionsExports(cwd);
|
|
868
|
+
if (exports.has(relativePath)) {
|
|
869
|
+
return true;
|
|
870
|
+
}
|
|
871
|
+
const parts = relativePath.split("/");
|
|
872
|
+
if (parts.length > 1 && exports.has(parts[0])) {
|
|
873
|
+
return true;
|
|
874
|
+
}
|
|
875
|
+
return false;
|
|
876
|
+
}
|
|
877
|
+
function filterCloudFunctionsFalsePositives(files, cwd) {
|
|
878
|
+
if (!hasFirebaseFunctions(cwd)) {
|
|
879
|
+
return { filtered: files, excluded: [] };
|
|
880
|
+
}
|
|
881
|
+
const filtered = [];
|
|
882
|
+
const excluded = [];
|
|
883
|
+
for (const file of files) {
|
|
884
|
+
if (isExportedCloudFunction(file, cwd)) {
|
|
885
|
+
excluded.push(file);
|
|
886
|
+
} else {
|
|
887
|
+
filtered.push(file);
|
|
888
|
+
}
|
|
889
|
+
}
|
|
890
|
+
return { filtered, excluded };
|
|
891
|
+
}
|
|
892
|
+
function clearFirebaseCache() {
|
|
893
|
+
indexContentCache = null;
|
|
894
|
+
}
|
|
588
895
|
|
|
589
896
|
// src/cache/index.ts
|
|
590
|
-
import { existsSync, mkdirSync, readFileSync, writeFileSync, statSync, readdirSync } from "fs";
|
|
591
|
-
import { join, extname } from "path";
|
|
592
897
|
var CACHE_DIR = ".analyze";
|
|
593
898
|
var META_FILE = "meta.json";
|
|
594
899
|
var GRAPH_FILE = "graph.json";
|
|
@@ -601,7 +906,7 @@ function calculateFilesHash(cwd) {
|
|
|
601
906
|
try {
|
|
602
907
|
const entries = readdirSync(dir, { withFileTypes: true });
|
|
603
908
|
for (const entry of entries) {
|
|
604
|
-
const fullPath =
|
|
909
|
+
const fullPath = join2(dir, entry.name);
|
|
605
910
|
if (entry.isDirectory()) {
|
|
606
911
|
if (entry.name === "node_modules" || entry.name === ".git" || entry.name === ".next" || entry.name === "dist" || entry.name === ".analyze") {
|
|
607
912
|
continue;
|
|
@@ -626,16 +931,16 @@ function calculateFilesHash(cwd) {
|
|
|
626
931
|
return `${timestamps.length}-${Math.floor(sum)}`;
|
|
627
932
|
}
|
|
628
933
|
function getCacheDir(cwd) {
|
|
629
|
-
return
|
|
934
|
+
return join2(cwd, CACHE_DIR);
|
|
630
935
|
}
|
|
631
936
|
function isCacheValid(cwd) {
|
|
632
937
|
const cacheDir = getCacheDir(cwd);
|
|
633
|
-
const metaPath =
|
|
634
|
-
if (!
|
|
938
|
+
const metaPath = join2(cacheDir, META_FILE);
|
|
939
|
+
if (!existsSync2(metaPath)) {
|
|
635
940
|
return false;
|
|
636
941
|
}
|
|
637
942
|
try {
|
|
638
|
-
const meta = JSON.parse(
|
|
943
|
+
const meta = JSON.parse(readFileSync2(metaPath, "utf-8"));
|
|
639
944
|
const currentHash = calculateFilesHash(cwd);
|
|
640
945
|
return meta.filesHash === currentHash;
|
|
641
946
|
} catch {
|
|
@@ -643,22 +948,22 @@ function isCacheValid(cwd) {
|
|
|
643
948
|
}
|
|
644
949
|
}
|
|
645
950
|
function readCache(cwd, file) {
|
|
646
|
-
const cachePath =
|
|
647
|
-
if (!
|
|
951
|
+
const cachePath = join2(getCacheDir(cwd), file);
|
|
952
|
+
if (!existsSync2(cachePath)) {
|
|
648
953
|
return null;
|
|
649
954
|
}
|
|
650
955
|
try {
|
|
651
|
-
return JSON.parse(
|
|
956
|
+
return JSON.parse(readFileSync2(cachePath, "utf-8"));
|
|
652
957
|
} catch {
|
|
653
958
|
return null;
|
|
654
959
|
}
|
|
655
960
|
}
|
|
656
961
|
function writeCache(cwd, file, data) {
|
|
657
962
|
const cacheDir = getCacheDir(cwd);
|
|
658
|
-
if (!
|
|
963
|
+
if (!existsSync2(cacheDir)) {
|
|
659
964
|
mkdirSync(cacheDir, { recursive: true });
|
|
660
965
|
}
|
|
661
|
-
const cachePath =
|
|
966
|
+
const cachePath = join2(cacheDir, file);
|
|
662
967
|
writeFileSync(cachePath, JSON.stringify(data, null, 2), "utf-8");
|
|
663
968
|
}
|
|
664
969
|
function updateCacheMeta(cwd) {
|
|
@@ -699,8 +1004,9 @@ function getCachedDeadResult(cwd) {
|
|
|
699
1004
|
return readCache(cwd, DEAD_FILE);
|
|
700
1005
|
}
|
|
701
1006
|
function invalidateCache(cwd) {
|
|
702
|
-
const metaPath =
|
|
703
|
-
|
|
1007
|
+
const metaPath = join2(getCacheDir(cwd), META_FILE);
|
|
1008
|
+
clearFirebaseCache();
|
|
1009
|
+
if (existsSync2(metaPath)) {
|
|
704
1010
|
try {
|
|
705
1011
|
writeFileSync(metaPath, "{}", "utf-8");
|
|
706
1012
|
} catch {
|
|
@@ -839,11 +1145,15 @@ async function dead(options = {}) {
|
|
|
839
1145
|
knipOutput = {};
|
|
840
1146
|
}
|
|
841
1147
|
}
|
|
842
|
-
const
|
|
1148
|
+
const rawFiles = knipOutput.files || [];
|
|
1149
|
+
const { filtered: filteredFiles, excluded: excludedFunctions } = filterCloudFunctionsFalsePositives(rawFiles, cwd);
|
|
1150
|
+
const deadFiles = filteredFiles.map((file) => ({
|
|
843
1151
|
path: file,
|
|
844
1152
|
category: detectCategory(file),
|
|
845
1153
|
type: "file"
|
|
846
1154
|
}));
|
|
1155
|
+
const hasFirebase = hasFirebaseFunctions(cwd);
|
|
1156
|
+
const firebaseInfo = hasFirebase ? { detected: true, excludedCount: excludedFunctions.length } : { detected: false, excludedCount: 0 };
|
|
847
1157
|
const deadExports = [];
|
|
848
1158
|
if (knipOutput.issues) {
|
|
849
1159
|
for (const issue of knipOutput.issues) {
|
|
@@ -873,7 +1183,12 @@ async function dead(options = {}) {
|
|
|
873
1183
|
},
|
|
874
1184
|
files: deadFiles,
|
|
875
1185
|
exports: deadExports,
|
|
876
|
-
dependencies: deadDependencies
|
|
1186
|
+
dependencies: deadDependencies,
|
|
1187
|
+
// Metadata sobre filtros aplicados
|
|
1188
|
+
filters: {
|
|
1189
|
+
firebase: firebaseInfo,
|
|
1190
|
+
excludedFiles: excludedFunctions
|
|
1191
|
+
}
|
|
877
1192
|
};
|
|
878
1193
|
if (useCache) {
|
|
879
1194
|
cacheDeadResult(cwd, result);
|
|
@@ -1456,8 +1771,8 @@ function levenshteinDistance2(a, b) {
|
|
|
1456
1771
|
}
|
|
1457
1772
|
|
|
1458
1773
|
// src/commands/context.ts
|
|
1459
|
-
import { existsSync as
|
|
1460
|
-
import { join as
|
|
1774
|
+
import { existsSync as existsSync3, readdirSync as readdirSync2, statSync as statSync2 } from "fs";
|
|
1775
|
+
import { join as join3, resolve, basename, extname as extname2 } from "path";
|
|
1461
1776
|
|
|
1462
1777
|
// src/ts/extractor.ts
|
|
1463
1778
|
import { Project, SyntaxKind } from "ts-morph";
|
|
@@ -1705,12 +2020,12 @@ var CODE_EXTENSIONS2 = /* @__PURE__ */ new Set([".ts", ".tsx", ".js", ".jsx", ".
|
|
|
1705
2020
|
function findTargetFile3(target, cwd) {
|
|
1706
2021
|
const normalizedTarget = target.replace(/\\/g, "/");
|
|
1707
2022
|
const directPath = resolve(cwd, normalizedTarget);
|
|
1708
|
-
if (
|
|
2023
|
+
if (existsSync3(directPath) && isCodeFile2(directPath)) {
|
|
1709
2024
|
return normalizedTarget;
|
|
1710
2025
|
}
|
|
1711
2026
|
for (const ext of CODE_EXTENSIONS2) {
|
|
1712
2027
|
const withExt = directPath + ext;
|
|
1713
|
-
if (
|
|
2028
|
+
if (existsSync3(withExt)) {
|
|
1714
2029
|
return normalizedTarget + ext;
|
|
1715
2030
|
}
|
|
1716
2031
|
}
|
|
@@ -1740,7 +2055,7 @@ function getAllCodeFiles(dir, files = [], baseDir = dir) {
|
|
|
1740
2055
|
try {
|
|
1741
2056
|
const entries = readdirSync2(dir);
|
|
1742
2057
|
for (const entry of entries) {
|
|
1743
|
-
const fullPath =
|
|
2058
|
+
const fullPath = join3(dir, entry);
|
|
1744
2059
|
if (shouldIgnore(entry)) {
|
|
1745
2060
|
continue;
|
|
1746
2061
|
}
|
|
@@ -1831,14 +2146,1066 @@ function levenshteinDistance3(a, b) {
|
|
|
1831
2146
|
return matrix[b.length][a.length];
|
|
1832
2147
|
}
|
|
1833
2148
|
|
|
2149
|
+
// src/commands/areas.ts
|
|
2150
|
+
import { readdirSync as readdirSync3, statSync as statSync3 } from "fs";
|
|
2151
|
+
import { join as join5, extname as extname3 } from "path";
|
|
2152
|
+
|
|
2153
|
+
// src/areas/config.ts
|
|
2154
|
+
import { existsSync as existsSync4, readFileSync as readFileSync3, writeFileSync as writeFileSync2, mkdirSync as mkdirSync2 } from "fs";
|
|
2155
|
+
import { join as join4 } from "path";
|
|
2156
|
+
var CONFIG_FILE = "areas.config.json";
|
|
2157
|
+
var DEFAULT_CONFIG = {
|
|
2158
|
+
$schema: "./areas.schema.json",
|
|
2159
|
+
version: "1.0.0",
|
|
2160
|
+
areas: {},
|
|
2161
|
+
descriptions: {},
|
|
2162
|
+
settings: {
|
|
2163
|
+
autoDetect: true,
|
|
2164
|
+
inferDescriptions: true,
|
|
2165
|
+
groupByCategory: true
|
|
2166
|
+
}
|
|
2167
|
+
};
|
|
2168
|
+
function getConfigPath(cwd) {
|
|
2169
|
+
return join4(cwd, CACHE_DIR, CONFIG_FILE);
|
|
2170
|
+
}
|
|
2171
|
+
function configExists(cwd) {
|
|
2172
|
+
return existsSync4(getConfigPath(cwd));
|
|
2173
|
+
}
|
|
2174
|
+
function readConfig(cwd) {
|
|
2175
|
+
const configPath = getConfigPath(cwd);
|
|
2176
|
+
if (!existsSync4(configPath)) {
|
|
2177
|
+
return DEFAULT_CONFIG;
|
|
2178
|
+
}
|
|
2179
|
+
try {
|
|
2180
|
+
const content = readFileSync3(configPath, "utf-8");
|
|
2181
|
+
const config = JSON.parse(content);
|
|
2182
|
+
return {
|
|
2183
|
+
...DEFAULT_CONFIG,
|
|
2184
|
+
...config,
|
|
2185
|
+
settings: {
|
|
2186
|
+
...DEFAULT_CONFIG.settings,
|
|
2187
|
+
...config.settings
|
|
2188
|
+
}
|
|
2189
|
+
};
|
|
2190
|
+
} catch {
|
|
2191
|
+
return DEFAULT_CONFIG;
|
|
2192
|
+
}
|
|
2193
|
+
}
|
|
2194
|
+
function writeConfig(cwd, config) {
|
|
2195
|
+
const cacheDir = join4(cwd, CACHE_DIR);
|
|
2196
|
+
if (!existsSync4(cacheDir)) {
|
|
2197
|
+
mkdirSync2(cacheDir, { recursive: true });
|
|
2198
|
+
}
|
|
2199
|
+
const configPath = getConfigPath(cwd);
|
|
2200
|
+
writeFileSync2(configPath, JSON.stringify(config, null, 2), "utf-8");
|
|
2201
|
+
}
|
|
2202
|
+
function setArea(cwd, id, area2) {
|
|
2203
|
+
const config = readConfig(cwd);
|
|
2204
|
+
config.areas[id] = area2;
|
|
2205
|
+
writeConfig(cwd, config);
|
|
2206
|
+
}
|
|
2207
|
+
function removeArea(cwd, id) {
|
|
2208
|
+
const config = readConfig(cwd);
|
|
2209
|
+
delete config.areas[id];
|
|
2210
|
+
writeConfig(cwd, config);
|
|
2211
|
+
}
|
|
2212
|
+
function setFileDescription(cwd, filePath, description) {
|
|
2213
|
+
const config = readConfig(cwd);
|
|
2214
|
+
if (!config.descriptions) {
|
|
2215
|
+
config.descriptions = {};
|
|
2216
|
+
}
|
|
2217
|
+
config.descriptions[filePath] = description;
|
|
2218
|
+
writeConfig(cwd, config);
|
|
2219
|
+
}
|
|
2220
|
+
function getFileDescription(cwd, filePath) {
|
|
2221
|
+
const config = readConfig(cwd);
|
|
2222
|
+
return config.descriptions?.[filePath];
|
|
2223
|
+
}
|
|
2224
|
+
|
|
2225
|
+
// src/areas/detector.ts
|
|
2226
|
+
import { minimatch } from "minimatch";
|
|
2227
|
+
|
|
2228
|
+
// src/areas/patterns.ts
|
|
2229
|
+
var FOLDER_PATTERNS = [
|
|
2230
|
+
// ============================================================================
|
|
2231
|
+
// NEXT.JS APP ROUTER - Rotas específicas (alta prioridade)
|
|
2232
|
+
// ============================================================================
|
|
2233
|
+
{ pattern: /app\/.*\/meus-pets\//, area: "meus-pets", priority: 100 },
|
|
2234
|
+
{ pattern: /app\/.*\/pets\//, area: "meus-pets", priority: 100 },
|
|
2235
|
+
{ pattern: /app\/.*\/consultas\//, area: "consultas-ia", priority: 100 },
|
|
2236
|
+
{ pattern: /app\/.*\/dashboard\//, area: "dashboard", priority: 100 },
|
|
2237
|
+
{ pattern: /app\/.*\/admin\//, area: "admin", priority: 100 },
|
|
2238
|
+
{ pattern: /app\/.*\/assinatura\//, area: "stripe", priority: 100 },
|
|
2239
|
+
{ pattern: /app\/.*\/guias\//, area: "training", priority: 100 },
|
|
2240
|
+
{ pattern: /app\/.*\/treino\//, area: "training", priority: 100 },
|
|
2241
|
+
{ pattern: /app\/.*\/login\//, area: "auth", priority: 100 },
|
|
2242
|
+
{ pattern: /app\/.*\/cadastro\//, area: "auth", priority: 100 },
|
|
2243
|
+
{ pattern: /app\/.*\/auth\//, area: "auth", priority: 100 },
|
|
2244
|
+
{ pattern: /app\/.*\/perfil\//, area: "profile", priority: 100 },
|
|
2245
|
+
{ pattern: /app\/.*\/configuracoes\//, area: "settings", priority: 100 },
|
|
2246
|
+
{ pattern: /app\/.*\/settings\//, area: "settings", priority: 100 },
|
|
2247
|
+
{ pattern: /app\/.*\/onboarding/, area: "onboarding", priority: 100 },
|
|
2248
|
+
{ pattern: /app\/.*\/beta\//, area: "beta", priority: 100 },
|
|
2249
|
+
{ pattern: /app\/.*\/precos\//, area: "pricing", priority: 100 },
|
|
2250
|
+
{ pattern: /app\/.*\/pricing\//, area: "pricing", priority: 100 },
|
|
2251
|
+
{ pattern: /app\/.*\/checkout\//, area: "checkout", priority: 100 },
|
|
2252
|
+
{ pattern: /app\/.*\/cart\//, area: "cart", priority: 100 },
|
|
2253
|
+
{ pattern: /app\/.*\/shop\//, area: "shop", priority: 100 },
|
|
2254
|
+
{ pattern: /app\/.*\/products\//, area: "products", priority: 100 },
|
|
2255
|
+
{ pattern: /app\/.*\/orders\//, area: "orders", priority: 100 },
|
|
2256
|
+
{ pattern: /app\/.*\/blog\//, area: "blog", priority: 100 },
|
|
2257
|
+
{ pattern: /app\/.*\/docs\//, area: "docs", priority: 100 },
|
|
2258
|
+
{ pattern: /app\/.*\/legal\//, area: "legal", priority: 100 },
|
|
2259
|
+
{ pattern: /app\/.*\/privacy\//, area: "legal", priority: 100 },
|
|
2260
|
+
{ pattern: /app\/.*\/terms\//, area: "legal", priority: 100 },
|
|
2261
|
+
{ pattern: /app\/.*\/about\//, area: "about", priority: 100 },
|
|
2262
|
+
{ pattern: /app\/.*\/contact\//, area: "contact", priority: 100 },
|
|
2263
|
+
{ pattern: /app\/.*\/faq\//, area: "faq", priority: 100 },
|
|
2264
|
+
{ pattern: /app\/.*\/help\//, area: "help", priority: 100 },
|
|
2265
|
+
{ pattern: /app\/.*\/support\//, area: "support", priority: 100 },
|
|
2266
|
+
{ pattern: /app\/.*\/feedback\//, area: "feedback", priority: 100 },
|
|
2267
|
+
// ============================================================================
|
|
2268
|
+
// VITE / CRA - src/pages/ ou src/views/ (alta prioridade)
|
|
2269
|
+
// ============================================================================
|
|
2270
|
+
{ pattern: /src\/pages\/[Dd]ashboard/, area: "dashboard", priority: 100 },
|
|
2271
|
+
{ pattern: /src\/pages\/[Aa]dmin/, area: "admin", priority: 100 },
|
|
2272
|
+
{ pattern: /src\/pages\/[Aa]uth/, area: "auth", priority: 100 },
|
|
2273
|
+
{ pattern: /src\/pages\/[Ll]ogin/, area: "auth", priority: 100 },
|
|
2274
|
+
{ pattern: /src\/pages\/[Rr]egister/, area: "auth", priority: 100 },
|
|
2275
|
+
{ pattern: /src\/pages\/[Ss]ignup/, area: "auth", priority: 100 },
|
|
2276
|
+
{ pattern: /src\/pages\/[Pp]rofile/, area: "profile", priority: 100 },
|
|
2277
|
+
{ pattern: /src\/pages\/[Ss]ettings/, area: "settings", priority: 100 },
|
|
2278
|
+
{ pattern: /src\/pages\/[Pp]ricing/, area: "pricing", priority: 100 },
|
|
2279
|
+
{ pattern: /src\/pages\/[Cc]heckout/, area: "checkout", priority: 100 },
|
|
2280
|
+
{ pattern: /src\/pages\/[Cc]art/, area: "cart", priority: 100 },
|
|
2281
|
+
{ pattern: /src\/pages\/[Ss]hop/, area: "shop", priority: 100 },
|
|
2282
|
+
{ pattern: /src\/pages\/[Pp]roducts?/, area: "products", priority: 100 },
|
|
2283
|
+
{ pattern: /src\/pages\/[Oo]rders?/, area: "orders", priority: 100 },
|
|
2284
|
+
{ pattern: /src\/pages\/[Bb]log/, area: "blog", priority: 100 },
|
|
2285
|
+
{ pattern: /src\/views\/[Dd]ashboard/, area: "dashboard", priority: 100 },
|
|
2286
|
+
{ pattern: /src\/views\/[Aa]dmin/, area: "admin", priority: 100 },
|
|
2287
|
+
{ pattern: /src\/views\/[Aa]uth/, area: "auth", priority: 100 },
|
|
2288
|
+
// ============================================================================
|
|
2289
|
+
// REMIX - app/routes/
|
|
2290
|
+
// ============================================================================
|
|
2291
|
+
{ pattern: /app\/routes\/dashboard/, area: "dashboard", priority: 100 },
|
|
2292
|
+
{ pattern: /app\/routes\/admin/, area: "admin", priority: 100 },
|
|
2293
|
+
{ pattern: /app\/routes\/auth/, area: "auth", priority: 100 },
|
|
2294
|
+
{ pattern: /app\/routes\/login/, area: "auth", priority: 100 },
|
|
2295
|
+
{ pattern: /app\/routes\/_auth/, area: "auth", priority: 100 },
|
|
2296
|
+
// ============================================================================
|
|
2297
|
+
// NUXT - pages/ (sem src/)
|
|
2298
|
+
// ============================================================================
|
|
2299
|
+
{ pattern: /^pages\/dashboard/, area: "dashboard", priority: 100 },
|
|
2300
|
+
{ pattern: /^pages\/admin/, area: "admin", priority: 100 },
|
|
2301
|
+
{ pattern: /^pages\/auth/, area: "auth", priority: 100 },
|
|
2302
|
+
// ============================================================================
|
|
2303
|
+
// SVELTEKIT - src/routes/
|
|
2304
|
+
// ============================================================================
|
|
2305
|
+
{ pattern: /src\/routes\/dashboard/, area: "dashboard", priority: 100 },
|
|
2306
|
+
{ pattern: /src\/routes\/admin/, area: "admin", priority: 100 },
|
|
2307
|
+
{ pattern: /src\/routes\/auth/, area: "auth", priority: 100 },
|
|
2308
|
+
{ pattern: /src\/routes\/\(auth\)/, area: "auth", priority: 100 },
|
|
2309
|
+
{ pattern: /src\/routes\/\(app\)/, area: "app", priority: 90 },
|
|
2310
|
+
// ============================================================================
|
|
2311
|
+
// ASTRO - src/pages/
|
|
2312
|
+
// ============================================================================
|
|
2313
|
+
{ pattern: /src\/pages\/blog/, area: "blog", priority: 100 },
|
|
2314
|
+
{ pattern: /src\/pages\/docs/, area: "docs", priority: 100 },
|
|
2315
|
+
// ============================================================================
|
|
2316
|
+
// COMPONENTS - Por subpasta (alta prioridade)
|
|
2317
|
+
// Funciona em qualquer framework (com ou sem src/)
|
|
2318
|
+
// ============================================================================
|
|
2319
|
+
{ pattern: /components\/pets\//, area: "meus-pets", priority: 90 },
|
|
2320
|
+
{ pattern: /components\/pet\//, area: "meus-pets", priority: 90 },
|
|
2321
|
+
{ pattern: /components\/consultation\//, area: "consultas-ia", priority: 90 },
|
|
2322
|
+
{ pattern: /components\/chat\//, area: "chat", priority: 90 },
|
|
2323
|
+
{ pattern: /components\/training\//, area: "training", priority: 90 },
|
|
2324
|
+
{ pattern: /components\/health\//, area: "health-tracking", priority: 90 },
|
|
2325
|
+
{ pattern: /components\/auth\//, area: "auth", priority: 90 },
|
|
2326
|
+
{ pattern: /components\/admin\//, area: "admin", priority: 90 },
|
|
2327
|
+
{ pattern: /components\/landing\//, area: "landing", priority: 90 },
|
|
2328
|
+
{ pattern: /components\/marketing\//, area: "landing", priority: 90 },
|
|
2329
|
+
{ pattern: /components\/dashboard\//, area: "dashboard", priority: 90 },
|
|
2330
|
+
{ pattern: /components\/subscription\//, area: "stripe", priority: 90 },
|
|
2331
|
+
{ pattern: /components\/stripe\//, area: "stripe", priority: 90 },
|
|
2332
|
+
{ pattern: /components\/payment\//, area: "payments", priority: 90 },
|
|
2333
|
+
{ pattern: /components\/checkout\//, area: "checkout", priority: 90 },
|
|
2334
|
+
{ pattern: /components\/cart\//, area: "cart", priority: 90 },
|
|
2335
|
+
{ pattern: /components\/notification\//, area: "notifications", priority: 90 },
|
|
2336
|
+
{ pattern: /components\/pdf\//, area: "pdf", priority: 90 },
|
|
2337
|
+
{ pattern: /components\/seo\//, area: "seo", priority: 90 },
|
|
2338
|
+
{ pattern: /components\/blog\//, area: "blog", priority: 90 },
|
|
2339
|
+
{ pattern: /components\/docs\//, area: "docs", priority: 90 },
|
|
2340
|
+
{ pattern: /components\/legal\//, area: "legal", priority: 90 },
|
|
2341
|
+
{ pattern: /components\/feedback\//, area: "feedback", priority: 90 },
|
|
2342
|
+
{ pattern: /components\/beta\//, area: "beta", priority: 90 },
|
|
2343
|
+
{ pattern: /components\/onboarding\//, area: "onboarding", priority: 90 },
|
|
2344
|
+
{ pattern: /components\/settings\//, area: "settings", priority: 90 },
|
|
2345
|
+
{ pattern: /components\/profile\//, area: "profile", priority: 90 },
|
|
2346
|
+
{ pattern: /components\/user\//, area: "user", priority: 90 },
|
|
2347
|
+
{ pattern: /components\/products?\//, area: "products", priority: 90 },
|
|
2348
|
+
{ pattern: /components\/orders?\//, area: "orders", priority: 90 },
|
|
2349
|
+
{ pattern: /components\/shop\//, area: "shop", priority: 90 },
|
|
2350
|
+
{ pattern: /components\/forms?\//, area: "forms", priority: 85 },
|
|
2351
|
+
{ pattern: /components\/tables?\//, area: "tables", priority: 85 },
|
|
2352
|
+
{ pattern: /components\/modals?\//, area: "modals", priority: 85 },
|
|
2353
|
+
{ pattern: /components\/dialogs?\//, area: "modals", priority: 85 },
|
|
2354
|
+
// Componentes compartilhados (baixa prioridade)
|
|
2355
|
+
{ pattern: /components\/ui\//, area: "shared-ui", priority: 30 },
|
|
2356
|
+
{ pattern: /components\/common\//, area: "shared-ui", priority: 30 },
|
|
2357
|
+
{ pattern: /components\/shared\//, area: "shared-ui", priority: 30 },
|
|
2358
|
+
{ pattern: /components\/base\//, area: "shared-ui", priority: 30 },
|
|
2359
|
+
{ pattern: /components\/core\//, area: "shared-ui", priority: 30 },
|
|
2360
|
+
{ pattern: /components\/primitives\//, area: "shared-ui", priority: 30 },
|
|
2361
|
+
{ pattern: /components\/providers\//, area: "core", priority: 40 },
|
|
2362
|
+
{ pattern: /components\/layout\//, area: "core", priority: 40 },
|
|
2363
|
+
{ pattern: /components\/layouts\//, area: "core", priority: 40 },
|
|
2364
|
+
// ============================================================================
|
|
2365
|
+
// FEATURES (padrão comum em projetos maiores)
|
|
2366
|
+
// ============================================================================
|
|
2367
|
+
{ pattern: /features\/auth\//, area: "auth", priority: 95 },
|
|
2368
|
+
{ pattern: /features\/dashboard\//, area: "dashboard", priority: 95 },
|
|
2369
|
+
{ pattern: /features\/admin\//, area: "admin", priority: 95 },
|
|
2370
|
+
{ pattern: /features\/checkout\//, area: "checkout", priority: 95 },
|
|
2371
|
+
{ pattern: /features\/cart\//, area: "cart", priority: 95 },
|
|
2372
|
+
{ pattern: /features\/products?\//, area: "products", priority: 95 },
|
|
2373
|
+
{ pattern: /features\/orders?\//, area: "orders", priority: 95 },
|
|
2374
|
+
{ pattern: /features\/user\//, area: "user", priority: 95 },
|
|
2375
|
+
{ pattern: /features\/settings\//, area: "settings", priority: 95 },
|
|
2376
|
+
{ pattern: /features\/notifications?\//, area: "notifications", priority: 95 },
|
|
2377
|
+
{ pattern: /features\/blog\//, area: "blog", priority: 95 },
|
|
2378
|
+
// ============================================================================
|
|
2379
|
+
// MODULES (outro padrão comum)
|
|
2380
|
+
// ============================================================================
|
|
2381
|
+
{ pattern: /modules\/auth\//, area: "auth", priority: 95 },
|
|
2382
|
+
{ pattern: /modules\/dashboard\//, area: "dashboard", priority: 95 },
|
|
2383
|
+
{ pattern: /modules\/admin\//, area: "admin", priority: 95 },
|
|
2384
|
+
{ pattern: /modules\/checkout\//, area: "checkout", priority: 95 },
|
|
2385
|
+
{ pattern: /modules\/products?\//, area: "products", priority: 95 },
|
|
2386
|
+
// ============================================================================
|
|
2387
|
+
// LIB - Módulos específicos
|
|
2388
|
+
// ============================================================================
|
|
2389
|
+
{ pattern: /lib\/firebase\/ai\//, area: "firebase-ai", priority: 85 },
|
|
2390
|
+
{ pattern: /lib\/firebase\/aiExtraction\//, area: "firebase-ai", priority: 85 },
|
|
2391
|
+
{ pattern: /lib\/firebase\/firestore\//, area: "firebase-firestore", priority: 85 },
|
|
2392
|
+
{ pattern: /lib\/firebase\/prompts\//, area: "firebase-ai", priority: 85 },
|
|
2393
|
+
{ pattern: /lib\/firebase\/analytics\//, area: "analytics", priority: 85 },
|
|
2394
|
+
{ pattern: /lib\/firebase\//, area: "firebase-core", priority: 80 },
|
|
2395
|
+
{ pattern: /lib\/stripe\//, area: "stripe", priority: 80 },
|
|
2396
|
+
{ pattern: /lib\/i18n\//, area: "i18n", priority: 80 },
|
|
2397
|
+
// ============================================================================
|
|
2398
|
+
// HOOKS - Por nome
|
|
2399
|
+
// ============================================================================
|
|
2400
|
+
{ pattern: /hooks\/.*[Pp]et/, area: "meus-pets", priority: 70 },
|
|
2401
|
+
{ pattern: /hooks\/.*[Aa]uth/, area: "auth", priority: 70 },
|
|
2402
|
+
{ pattern: /hooks\/.*[Ss]ubscription/, area: "stripe", priority: 70 },
|
|
2403
|
+
{ pattern: /hooks\/.*[Nn]otification/, area: "notifications", priority: 70 },
|
|
2404
|
+
{ pattern: /hooks\/.*[Hh]ealth/, area: "health-tracking", priority: 70 },
|
|
2405
|
+
{ pattern: /hooks\/.*[Tt]raining/, area: "training", priority: 70 },
|
|
2406
|
+
// ============================================================================
|
|
2407
|
+
// STORE - Por nome
|
|
2408
|
+
// ============================================================================
|
|
2409
|
+
{ pattern: /store\/.*[Pp]et/, area: "meus-pets", priority: 70 },
|
|
2410
|
+
{ pattern: /store\/.*[Aa]uth/, area: "auth", priority: 70 },
|
|
2411
|
+
{ pattern: /store\/.*[Uu]ser/, area: "auth", priority: 70 },
|
|
2412
|
+
// ============================================================================
|
|
2413
|
+
// SCHEMAS - Por nome
|
|
2414
|
+
// ============================================================================
|
|
2415
|
+
{ pattern: /schemas\/.*[Pp]et/, area: "meus-pets", priority: 70 },
|
|
2416
|
+
{ pattern: /schemas\/.*[Aa]uth/, area: "auth", priority: 70 },
|
|
2417
|
+
// ============================================================================
|
|
2418
|
+
// TYPES - Por nome
|
|
2419
|
+
// ============================================================================
|
|
2420
|
+
{ pattern: /types\/.*[Pp]et/, area: "meus-pets", priority: 70 },
|
|
2421
|
+
{ pattern: /types\/.*[Aa]uth/, area: "auth", priority: 70 },
|
|
2422
|
+
{ pattern: /types\/.*[Ss]tripe/, area: "stripe", priority: 70 },
|
|
2423
|
+
{ pattern: /types\/.*[Ss]ubscription/, area: "stripe", priority: 70 },
|
|
2424
|
+
// ============================================================================
|
|
2425
|
+
// CLOUD FUNCTIONS
|
|
2426
|
+
// ============================================================================
|
|
2427
|
+
{ pattern: /functions\/src\//, area: "cloud-functions", priority: 80 },
|
|
2428
|
+
// ============================================================================
|
|
2429
|
+
// OUTROS
|
|
2430
|
+
// ============================================================================
|
|
2431
|
+
{ pattern: /messages\//, area: "i18n", priority: 60 },
|
|
2432
|
+
{ pattern: /i18n\//, area: "i18n", priority: 60 },
|
|
2433
|
+
{ pattern: /public\//, area: "assets", priority: 50 },
|
|
2434
|
+
{ pattern: /scripts\//, area: "scripts", priority: 50 }
|
|
2435
|
+
];
|
|
2436
|
+
var KEYWORD_PATTERNS = [
|
|
2437
|
+
// Pets
|
|
2438
|
+
{ keyword: /[Pp]et/, area: "meus-pets", priority: 60 },
|
|
2439
|
+
{ keyword: /[Vv]accin/, area: "meus-pets", priority: 60 },
|
|
2440
|
+
{ keyword: /[Dd]eworm/, area: "meus-pets", priority: 60 },
|
|
2441
|
+
{ keyword: /[Mm]edication/, area: "meus-pets", priority: 60 },
|
|
2442
|
+
{ keyword: /[Ss]urgery/, area: "meus-pets", priority: 60 },
|
|
2443
|
+
{ keyword: /[Vv]eterinary/, area: "meus-pets", priority: 60 },
|
|
2444
|
+
// Consultas IA
|
|
2445
|
+
{ keyword: /[Cc]onsultation/, area: "consultas-ia", priority: 60 },
|
|
2446
|
+
{ keyword: /[Cc]hat/, area: "consultas-ia", priority: 50 },
|
|
2447
|
+
{ keyword: /[Gg]emini/, area: "firebase-ai", priority: 60 },
|
|
2448
|
+
// Health
|
|
2449
|
+
{ keyword: /[Hh]ealth[Tt]racking/, area: "health-tracking", priority: 65 },
|
|
2450
|
+
{ keyword: /[Hh]ome[Cc]are/, area: "health-tracking", priority: 65 },
|
|
2451
|
+
// Training
|
|
2452
|
+
{ keyword: /[Tt]raining/, area: "training", priority: 60 },
|
|
2453
|
+
{ keyword: /[Gg]uide/, area: "training", priority: 55 },
|
|
2454
|
+
{ keyword: /[Aa]destramento/, area: "training", priority: 60 },
|
|
2455
|
+
// Auth
|
|
2456
|
+
{ keyword: /[Aa]uth/, area: "auth", priority: 60 },
|
|
2457
|
+
{ keyword: /[Ll]ogin/, area: "auth", priority: 60 },
|
|
2458
|
+
{ keyword: /[Rr]egister/, area: "auth", priority: 60 },
|
|
2459
|
+
{ keyword: /[Ss]ignup/, area: "auth", priority: 60 },
|
|
2460
|
+
{ keyword: /[Ss]ignin/, area: "auth", priority: 60 },
|
|
2461
|
+
// Stripe
|
|
2462
|
+
{ keyword: /[Ss]tripe/, area: "stripe", priority: 65 },
|
|
2463
|
+
{ keyword: /[Ss]ubscription/, area: "stripe", priority: 60 },
|
|
2464
|
+
{ keyword: /[Pp]ayment/, area: "stripe", priority: 60 },
|
|
2465
|
+
{ keyword: /[Cc]heckout/, area: "stripe", priority: 60 },
|
|
2466
|
+
{ keyword: /[Pp]rice/, area: "pricing", priority: 55 },
|
|
2467
|
+
{ keyword: /[Pp]ricing/, area: "pricing", priority: 60 },
|
|
2468
|
+
// Notifications
|
|
2469
|
+
{ keyword: /[Nn]otification/, area: "notifications", priority: 60 },
|
|
2470
|
+
{ keyword: /[Ff][Cc][Mm]/, area: "notifications", priority: 65 },
|
|
2471
|
+
{ keyword: /[Pp]ush/, area: "notifications", priority: 55 },
|
|
2472
|
+
// i18n
|
|
2473
|
+
{ keyword: /[Ii]18n/, area: "i18n", priority: 60 },
|
|
2474
|
+
{ keyword: /[Ll]ocale/, area: "i18n", priority: 55 },
|
|
2475
|
+
{ keyword: /[Tt]ranslat/, area: "i18n", priority: 55 },
|
|
2476
|
+
// SEO
|
|
2477
|
+
{ keyword: /[Ss][Ee][Oo]/, area: "seo", priority: 60 },
|
|
2478
|
+
{ keyword: /[Ss]itemap/, area: "seo", priority: 60 },
|
|
2479
|
+
{ keyword: /[Mm]eta/, area: "seo", priority: 50 },
|
|
2480
|
+
// Analytics
|
|
2481
|
+
{ keyword: /[Aa]nalytics/, area: "analytics", priority: 60 },
|
|
2482
|
+
{ keyword: /[Uu]tm/, area: "analytics", priority: 55 },
|
|
2483
|
+
{ keyword: /[Tt]racking/, area: "analytics", priority: 50 },
|
|
2484
|
+
// Admin
|
|
2485
|
+
{ keyword: /[Aa]dmin/, area: "admin", priority: 60 },
|
|
2486
|
+
// PWA
|
|
2487
|
+
{ keyword: /[Pp][Ww][Aa]/, area: "pwa", priority: 60 },
|
|
2488
|
+
{ keyword: /[Ss]ervice[Ww]orker/, area: "pwa", priority: 60 },
|
|
2489
|
+
{ keyword: /[Mm]anifest/, area: "pwa", priority: 55 },
|
|
2490
|
+
{ keyword: /[Oo]ffline/, area: "pwa", priority: 55 },
|
|
2491
|
+
// PDF
|
|
2492
|
+
{ keyword: /[Pp]df/, area: "pdf", priority: 60 },
|
|
2493
|
+
{ keyword: /[Rr]eport/, area: "pdf", priority: 50 }
|
|
2494
|
+
];
|
|
2495
|
+
var AREA_NAMES = {
|
|
2496
|
+
// Áreas específicas de domínio
|
|
2497
|
+
"meus-pets": "Meus Pets",
|
|
2498
|
+
"consultas-ia": "Consultas IA",
|
|
2499
|
+
"health-tracking": "Health Tracking",
|
|
2500
|
+
training: "Adestramento",
|
|
2501
|
+
// Autenticação e usuário
|
|
2502
|
+
auth: "Autentica\xE7\xE3o",
|
|
2503
|
+
user: "Usu\xE1rio",
|
|
2504
|
+
profile: "Perfil",
|
|
2505
|
+
settings: "Configura\xE7\xF5es",
|
|
2506
|
+
onboarding: "Onboarding",
|
|
2507
|
+
// E-commerce
|
|
2508
|
+
stripe: "Pagamentos (Stripe)",
|
|
2509
|
+
payments: "Pagamentos",
|
|
2510
|
+
checkout: "Checkout",
|
|
2511
|
+
cart: "Carrinho",
|
|
2512
|
+
shop: "Loja",
|
|
2513
|
+
products: "Produtos",
|
|
2514
|
+
orders: "Pedidos",
|
|
2515
|
+
pricing: "Pre\xE7os",
|
|
2516
|
+
// Comunicação
|
|
2517
|
+
notifications: "Notifica\xE7\xF5es",
|
|
2518
|
+
chat: "Chat",
|
|
2519
|
+
feedback: "Feedback",
|
|
2520
|
+
support: "Suporte",
|
|
2521
|
+
help: "Ajuda",
|
|
2522
|
+
faq: "FAQ",
|
|
2523
|
+
contact: "Contato",
|
|
2524
|
+
// Firebase
|
|
2525
|
+
"firebase-core": "Firebase Core",
|
|
2526
|
+
"firebase-ai": "Firebase AI",
|
|
2527
|
+
"firebase-firestore": "Firestore",
|
|
2528
|
+
// Conteúdo
|
|
2529
|
+
blog: "Blog",
|
|
2530
|
+
docs: "Documenta\xE7\xE3o",
|
|
2531
|
+
legal: "P\xE1ginas Legais",
|
|
2532
|
+
about: "Sobre",
|
|
2533
|
+
// Marketing e SEO
|
|
2534
|
+
landing: "Landing Pages",
|
|
2535
|
+
seo: "SEO",
|
|
2536
|
+
analytics: "Analytics",
|
|
2537
|
+
beta: "Programa Beta",
|
|
2538
|
+
// Admin e Dashboard
|
|
2539
|
+
admin: "Admin",
|
|
2540
|
+
dashboard: "Dashboard",
|
|
2541
|
+
// Técnico
|
|
2542
|
+
i18n: "Internacionaliza\xE7\xE3o",
|
|
2543
|
+
pwa: "PWA",
|
|
2544
|
+
pdf: "PDF",
|
|
2545
|
+
core: "Core",
|
|
2546
|
+
"shared-ui": "UI Compartilhado",
|
|
2547
|
+
"cloud-functions": "Cloud Functions",
|
|
2548
|
+
assets: "Assets",
|
|
2549
|
+
scripts: "Scripts",
|
|
2550
|
+
// UI patterns
|
|
2551
|
+
forms: "Formul\xE1rios",
|
|
2552
|
+
tables: "Tabelas",
|
|
2553
|
+
modals: "Modais",
|
|
2554
|
+
// Genéricos
|
|
2555
|
+
app: "Aplica\xE7\xE3o"
|
|
2556
|
+
};
|
|
2557
|
+
var AREA_DESCRIPTIONS = {
|
|
2558
|
+
// Áreas específicas de domínio
|
|
2559
|
+
"meus-pets": "Gerenciamento completo de pets do usu\xE1rio",
|
|
2560
|
+
"consultas-ia": "Chat com IA para consultas veterin\xE1rias",
|
|
2561
|
+
"health-tracking": "Acompanhamento de sa\xFAde e sintomas",
|
|
2562
|
+
training: "Sistema de adestramento com guias e progresso",
|
|
2563
|
+
// Autenticação e usuário
|
|
2564
|
+
auth: "Autentica\xE7\xE3o e gerenciamento de sess\xE3o",
|
|
2565
|
+
user: "Gerenciamento de dados do usu\xE1rio",
|
|
2566
|
+
profile: "Perfil do usu\xE1rio",
|
|
2567
|
+
settings: "Configura\xE7\xF5es do usu\xE1rio",
|
|
2568
|
+
onboarding: "Fluxo de onboarding de novos usu\xE1rios",
|
|
2569
|
+
// E-commerce
|
|
2570
|
+
stripe: "Integra\xE7\xE3o Stripe para pagamentos e assinaturas",
|
|
2571
|
+
payments: "Sistema de pagamentos",
|
|
2572
|
+
checkout: "Fluxo de checkout e finaliza\xE7\xE3o de compra",
|
|
2573
|
+
cart: "Carrinho de compras",
|
|
2574
|
+
shop: "Loja e cat\xE1logo de produtos",
|
|
2575
|
+
products: "Gerenciamento de produtos",
|
|
2576
|
+
orders: "Gerenciamento de pedidos",
|
|
2577
|
+
pricing: "P\xE1gina de pre\xE7os e planos",
|
|
2578
|
+
// Comunicação
|
|
2579
|
+
notifications: "Sistema de notifica\xE7\xF5es push",
|
|
2580
|
+
chat: "Sistema de chat e mensagens",
|
|
2581
|
+
feedback: "Coleta de feedback dos usu\xE1rios",
|
|
2582
|
+
support: "Suporte ao cliente",
|
|
2583
|
+
help: "Central de ajuda",
|
|
2584
|
+
faq: "Perguntas frequentes",
|
|
2585
|
+
contact: "P\xE1gina de contato",
|
|
2586
|
+
// Firebase
|
|
2587
|
+
"firebase-core": "Configura\xE7\xE3o e servi\xE7os Firebase client-side",
|
|
2588
|
+
"firebase-ai": "Integra\xE7\xE3o com Firebase AI (Gemini)",
|
|
2589
|
+
"firebase-firestore": "Opera\xE7\xF5es CRUD no Firestore",
|
|
2590
|
+
// Conteúdo
|
|
2591
|
+
blog: "Blog e artigos",
|
|
2592
|
+
docs: "Documenta\xE7\xE3o e guias",
|
|
2593
|
+
legal: "Termos de uso, privacidade e pol\xEDticas",
|
|
2594
|
+
about: "P\xE1gina sobre a empresa",
|
|
2595
|
+
// Marketing e SEO
|
|
2596
|
+
landing: "Landing pages e marketing",
|
|
2597
|
+
seo: "SEO, meta tags e sitemaps",
|
|
2598
|
+
analytics: "Analytics e tracking de eventos",
|
|
2599
|
+
beta: "Programa de beta testers",
|
|
2600
|
+
// Admin e Dashboard
|
|
2601
|
+
admin: "Painel administrativo",
|
|
2602
|
+
dashboard: "Dashboard do usu\xE1rio",
|
|
2603
|
+
// Técnico
|
|
2604
|
+
i18n: "Internacionaliza\xE7\xE3o e tradu\xE7\xF5es",
|
|
2605
|
+
pwa: "Progressive Web App (offline, install)",
|
|
2606
|
+
pdf: "Gera\xE7\xE3o de relat\xF3rios PDF",
|
|
2607
|
+
core: "Providers e layout principal",
|
|
2608
|
+
"shared-ui": "Componentes de UI compartilhados",
|
|
2609
|
+
"cloud-functions": "Cloud Functions (serverless)",
|
|
2610
|
+
assets: "Assets p\xFAblicos (imagens, fontes)",
|
|
2611
|
+
scripts: "Scripts de automa\xE7\xE3o",
|
|
2612
|
+
// UI patterns
|
|
2613
|
+
forms: "Componentes de formul\xE1rio reutiliz\xE1veis",
|
|
2614
|
+
tables: "Componentes de tabela reutiliz\xE1veis",
|
|
2615
|
+
modals: "Modais e dialogs reutiliz\xE1veis",
|
|
2616
|
+
// Genéricos
|
|
2617
|
+
app: "\xC1rea principal da aplica\xE7\xE3o"
|
|
2618
|
+
};
|
|
2619
|
+
|
|
2620
|
+
// src/areas/detector.ts
|
|
2621
|
+
function detectFileAreas(filePath, config) {
|
|
2622
|
+
const normalizedPath = filePath.replace(/\\/g, "/");
|
|
2623
|
+
const matches = [];
|
|
2624
|
+
for (const [areaId, areaConfig] of Object.entries(config.areas)) {
|
|
2625
|
+
if (matchesAreaConfig(normalizedPath, areaConfig)) {
|
|
2626
|
+
matches.push({ area: areaId, priority: 200, source: "config" });
|
|
2627
|
+
}
|
|
2628
|
+
}
|
|
2629
|
+
for (const { pattern, area: area2, priority } of FOLDER_PATTERNS) {
|
|
2630
|
+
if (pattern.test(normalizedPath)) {
|
|
2631
|
+
if (!matches.some((m) => m.area === area2 && m.source === "config")) {
|
|
2632
|
+
matches.push({ area: area2, priority, source: "folder" });
|
|
2633
|
+
}
|
|
2634
|
+
}
|
|
2635
|
+
}
|
|
2636
|
+
const fileName = normalizedPath.split("/").pop() || "";
|
|
2637
|
+
for (const { keyword, area: area2, priority } of KEYWORD_PATTERNS) {
|
|
2638
|
+
if (keyword.test(fileName) || keyword.test(normalizedPath)) {
|
|
2639
|
+
if (!matches.some((m) => m.area === area2)) {
|
|
2640
|
+
matches.push({ area: area2, priority, source: "keyword" });
|
|
2641
|
+
}
|
|
2642
|
+
}
|
|
2643
|
+
}
|
|
2644
|
+
const sorted = matches.sort((a, b) => b.priority - a.priority);
|
|
2645
|
+
const unique = [...new Set(sorted.map((m) => m.area))];
|
|
2646
|
+
return unique;
|
|
2647
|
+
}
|
|
2648
|
+
function matchesAreaConfig(filePath, config) {
|
|
2649
|
+
if (config.exclude) {
|
|
2650
|
+
for (const pattern of config.exclude) {
|
|
2651
|
+
if (minimatch(filePath, pattern, { dot: true })) {
|
|
2652
|
+
return false;
|
|
2653
|
+
}
|
|
2654
|
+
}
|
|
2655
|
+
}
|
|
2656
|
+
for (const pattern of config.patterns) {
|
|
2657
|
+
if (minimatch(filePath, pattern, { dot: true })) {
|
|
2658
|
+
return true;
|
|
2659
|
+
}
|
|
2660
|
+
}
|
|
2661
|
+
if (config.keywords) {
|
|
2662
|
+
const lowerPath = filePath.toLowerCase();
|
|
2663
|
+
for (const keyword of config.keywords) {
|
|
2664
|
+
if (lowerPath.includes(keyword.toLowerCase())) {
|
|
2665
|
+
return true;
|
|
2666
|
+
}
|
|
2667
|
+
}
|
|
2668
|
+
}
|
|
2669
|
+
return false;
|
|
2670
|
+
}
|
|
2671
|
+
function getAreaName(areaId, config) {
|
|
2672
|
+
if (config.areas[areaId]?.name) {
|
|
2673
|
+
return config.areas[areaId].name;
|
|
2674
|
+
}
|
|
2675
|
+
if (AREA_NAMES[areaId]) {
|
|
2676
|
+
return AREA_NAMES[areaId];
|
|
2677
|
+
}
|
|
2678
|
+
return areaId.split("-").map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
|
|
2679
|
+
}
|
|
2680
|
+
function getAreaDescription(areaId, config) {
|
|
2681
|
+
if (config.areas[areaId]?.description) {
|
|
2682
|
+
return config.areas[areaId].description;
|
|
2683
|
+
}
|
|
2684
|
+
return AREA_DESCRIPTIONS[areaId];
|
|
2685
|
+
}
|
|
2686
|
+
function inferFileDescription(filePath, category) {
|
|
2687
|
+
const fileName = filePath.split("/").pop() || "";
|
|
2688
|
+
const fileNameNoExt = fileName.replace(/\.(tsx?|jsx?|mjs|cjs)$/, "");
|
|
2689
|
+
const patterns = [
|
|
2690
|
+
// Pages
|
|
2691
|
+
{ pattern: /^page$/, description: () => "P\xE1gina principal" },
|
|
2692
|
+
{ pattern: /^layout$/, description: () => "Layout" },
|
|
2693
|
+
{ pattern: /^loading$/, description: () => "Estado de loading" },
|
|
2694
|
+
{ pattern: /^error$/, description: () => "P\xE1gina de erro" },
|
|
2695
|
+
{ pattern: /^not-found$/, description: () => "P\xE1gina 404" },
|
|
2696
|
+
// Components com sufixo
|
|
2697
|
+
{ pattern: /(.+)PageClient$/, description: (m) => `Client component da p\xE1gina ${m[1]}` },
|
|
2698
|
+
{ pattern: /(.+)Dialog$/, description: (m) => `Dialog de ${m[1].toLowerCase()}` },
|
|
2699
|
+
{ pattern: /(.+)Modal$/, description: (m) => `Modal de ${m[1].toLowerCase()}` },
|
|
2700
|
+
{ pattern: /(.+)Form$/, description: (m) => `Formul\xE1rio de ${m[1].toLowerCase()}` },
|
|
2701
|
+
{ pattern: /(.+)Card$/, description: (m) => `Card de ${m[1].toLowerCase()}` },
|
|
2702
|
+
{ pattern: /(.+)List$/, description: (m) => `Lista de ${m[1].toLowerCase()}` },
|
|
2703
|
+
{ pattern: /(.+)Table$/, description: (m) => `Tabela de ${m[1].toLowerCase()}` },
|
|
2704
|
+
{ pattern: /(.+)Manager$/, description: (m) => `Gerenciador de ${m[1].toLowerCase()}` },
|
|
2705
|
+
{ pattern: /(.+)Provider$/, description: (m) => `Provider de ${m[1].toLowerCase()}` },
|
|
2706
|
+
{ pattern: /(.+)Context$/, description: (m) => `Context de ${m[1].toLowerCase()}` },
|
|
2707
|
+
{ pattern: /(.+)Step$/, description: (m) => `Step: ${m[1]}` },
|
|
2708
|
+
{ pattern: /(.+)Tab$/, description: (m) => `Aba: ${m[1]}` },
|
|
2709
|
+
{ pattern: /(.+)Section$/, description: (m) => `Se\xE7\xE3o: ${m[1]}` },
|
|
2710
|
+
{ pattern: /(.+)Header$/, description: (m) => `Header de ${m[1].toLowerCase()}` },
|
|
2711
|
+
{ pattern: /(.+)Footer$/, description: (m) => `Footer de ${m[1].toLowerCase()}` },
|
|
2712
|
+
{ pattern: /(.+)Skeleton$/, description: (m) => `Skeleton de ${m[1].toLowerCase()}` },
|
|
2713
|
+
{ pattern: /(.+)Chip$/, description: (m) => `Chip de ${m[1].toLowerCase()}` },
|
|
2714
|
+
{ pattern: /(.+)Badge$/, description: (m) => `Badge de ${m[1].toLowerCase()}` },
|
|
2715
|
+
{ pattern: /(.+)Button$/, description: (m) => `Bot\xE3o ${m[1].toLowerCase()}` },
|
|
2716
|
+
{ pattern: /(.+)Icon$/, description: (m) => `\xCDcone ${m[1].toLowerCase()}` },
|
|
2717
|
+
// Hooks
|
|
2718
|
+
{ pattern: /^use(.+)$/, description: (m) => `Hook de ${m[1].toLowerCase()}` },
|
|
2719
|
+
// Types/Schemas
|
|
2720
|
+
{ pattern: /(.+)\.types$/, description: (m) => `Tipos de ${m[1].toLowerCase()}` },
|
|
2721
|
+
{ pattern: /(.+)Schemas?$/, description: (m) => `Schema de ${m[1].toLowerCase()}` },
|
|
2722
|
+
// Utils
|
|
2723
|
+
{ pattern: /(.+)Helpers?$/, description: (m) => `Helpers de ${m[1].toLowerCase()}` },
|
|
2724
|
+
{ pattern: /(.+)Utils?$/, description: (m) => `Utilit\xE1rios de ${m[1].toLowerCase()}` },
|
|
2725
|
+
{ pattern: /(.+)Formatters?$/, description: (m) => `Formatador de ${m[1].toLowerCase()}` },
|
|
2726
|
+
{ pattern: /(.+)Validators?$/, description: (m) => `Validador de ${m[1].toLowerCase()}` },
|
|
2727
|
+
{ pattern: /(.+)Mappers?$/, description: (m) => `Mapper de ${m[1].toLowerCase()}` },
|
|
2728
|
+
{ pattern: /(.+)Converters?$/, description: (m) => `Conversor de ${m[1].toLowerCase()}` },
|
|
2729
|
+
// Services
|
|
2730
|
+
{ pattern: /(.+)Service$/, description: (m) => `Servi\xE7o de ${m[1].toLowerCase()}` },
|
|
2731
|
+
// Index
|
|
2732
|
+
{ pattern: /^index$/, description: () => "Export principal" }
|
|
2733
|
+
];
|
|
2734
|
+
for (const { pattern, description } of patterns) {
|
|
2735
|
+
const match = fileNameNoExt.match(pattern);
|
|
2736
|
+
if (match) {
|
|
2737
|
+
return description(match);
|
|
2738
|
+
}
|
|
2739
|
+
}
|
|
2740
|
+
const categoryDescriptions = {
|
|
2741
|
+
page: "P\xE1gina",
|
|
2742
|
+
layout: "Layout",
|
|
2743
|
+
route: "API Route",
|
|
2744
|
+
component: "Componente",
|
|
2745
|
+
hook: "Hook",
|
|
2746
|
+
service: "Servi\xE7o",
|
|
2747
|
+
store: "Store",
|
|
2748
|
+
util: "Utilit\xE1rio",
|
|
2749
|
+
type: "Tipos",
|
|
2750
|
+
config: "Configura\xE7\xE3o",
|
|
2751
|
+
test: "Teste"
|
|
2752
|
+
};
|
|
2753
|
+
return categoryDescriptions[category];
|
|
2754
|
+
}
|
|
2755
|
+
|
|
2756
|
+
// src/commands/areas.ts
|
|
2757
|
+
var CODE_EXTENSIONS3 = /* @__PURE__ */ new Set([".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"]);
|
|
2758
|
+
var IGNORED_DIRS = /* @__PURE__ */ new Set([
|
|
2759
|
+
"node_modules",
|
|
2760
|
+
"dist",
|
|
2761
|
+
"build",
|
|
2762
|
+
".git",
|
|
2763
|
+
".next",
|
|
2764
|
+
".cache",
|
|
2765
|
+
"coverage",
|
|
2766
|
+
".turbo",
|
|
2767
|
+
".vercel",
|
|
2768
|
+
".analyze"
|
|
2769
|
+
]);
|
|
2770
|
+
async function areas(options = {}) {
|
|
2771
|
+
const cwd = options.cwd || process.cwd();
|
|
2772
|
+
const format = options.format || "text";
|
|
2773
|
+
try {
|
|
2774
|
+
const config = readConfig(cwd);
|
|
2775
|
+
const allFiles = getAllCodeFiles2(cwd);
|
|
2776
|
+
const areaMap = /* @__PURE__ */ new Map();
|
|
2777
|
+
const unmapped = [];
|
|
2778
|
+
for (const filePath of allFiles) {
|
|
2779
|
+
const category = detectCategory(filePath);
|
|
2780
|
+
const areas2 = detectFileAreas(filePath, config);
|
|
2781
|
+
let description = getFileDescription(cwd, filePath);
|
|
2782
|
+
if (!description && config.settings?.inferDescriptions !== false) {
|
|
2783
|
+
description = inferFileDescription(filePath, category);
|
|
2784
|
+
}
|
|
2785
|
+
const areaFile = {
|
|
2786
|
+
path: filePath,
|
|
2787
|
+
category,
|
|
2788
|
+
description
|
|
2789
|
+
};
|
|
2790
|
+
if (areas2.length === 0) {
|
|
2791
|
+
unmapped.push(areaFile);
|
|
2792
|
+
} else {
|
|
2793
|
+
for (const areaId of areas2) {
|
|
2794
|
+
if (!areaMap.has(areaId)) {
|
|
2795
|
+
areaMap.set(areaId, []);
|
|
2796
|
+
}
|
|
2797
|
+
areaMap.get(areaId).push(areaFile);
|
|
2798
|
+
}
|
|
2799
|
+
}
|
|
2800
|
+
}
|
|
2801
|
+
const detectedAreas = [];
|
|
2802
|
+
for (const [areaId, files] of areaMap) {
|
|
2803
|
+
const categories = {};
|
|
2804
|
+
for (const file of files) {
|
|
2805
|
+
categories[file.category] = (categories[file.category] || 0) + 1;
|
|
2806
|
+
}
|
|
2807
|
+
detectedAreas.push({
|
|
2808
|
+
id: areaId,
|
|
2809
|
+
name: getAreaName(areaId, config),
|
|
2810
|
+
description: getAreaDescription(areaId, config),
|
|
2811
|
+
files,
|
|
2812
|
+
fileCount: files.length,
|
|
2813
|
+
categories,
|
|
2814
|
+
isAutoDetected: !config.areas[areaId]
|
|
2815
|
+
});
|
|
2816
|
+
}
|
|
2817
|
+
detectedAreas.sort((a, b) => b.fileCount - a.fileCount);
|
|
2818
|
+
const result = {
|
|
2819
|
+
version: "1.0.0",
|
|
2820
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2821
|
+
cwd,
|
|
2822
|
+
areas: detectedAreas,
|
|
2823
|
+
unmapped,
|
|
2824
|
+
summary: {
|
|
2825
|
+
totalAreas: detectedAreas.length,
|
|
2826
|
+
totalFiles: allFiles.length,
|
|
2827
|
+
unmappedCount: unmapped.length
|
|
2828
|
+
}
|
|
2829
|
+
};
|
|
2830
|
+
if (format === "json") {
|
|
2831
|
+
return JSON.stringify(result, null, 2);
|
|
2832
|
+
}
|
|
2833
|
+
return formatAreasText(result);
|
|
2834
|
+
} catch (error) {
|
|
2835
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
2836
|
+
throw new Error(`Erro ao executar areas: ${message}`);
|
|
2837
|
+
}
|
|
2838
|
+
}
|
|
2839
|
+
function getAllCodeFiles2(dir, files = [], baseDir = dir) {
|
|
2840
|
+
try {
|
|
2841
|
+
const entries = readdirSync3(dir);
|
|
2842
|
+
for (const entry of entries) {
|
|
2843
|
+
const fullPath = join5(dir, entry);
|
|
2844
|
+
if (IGNORED_DIRS.has(entry) || entry.startsWith(".")) {
|
|
2845
|
+
continue;
|
|
2846
|
+
}
|
|
2847
|
+
try {
|
|
2848
|
+
const stat = statSync3(fullPath);
|
|
2849
|
+
if (stat.isDirectory()) {
|
|
2850
|
+
getAllCodeFiles2(fullPath, files, baseDir);
|
|
2851
|
+
} else {
|
|
2852
|
+
const ext = extname3(entry).toLowerCase();
|
|
2853
|
+
if (CODE_EXTENSIONS3.has(ext)) {
|
|
2854
|
+
const relativePath = fullPath.slice(baseDir.length + 1).replace(/\\/g, "/");
|
|
2855
|
+
files.push(relativePath);
|
|
2856
|
+
}
|
|
2857
|
+
}
|
|
2858
|
+
} catch {
|
|
2859
|
+
}
|
|
2860
|
+
}
|
|
2861
|
+
} catch {
|
|
2862
|
+
}
|
|
2863
|
+
return files;
|
|
2864
|
+
}
|
|
2865
|
+
|
|
2866
|
+
// src/commands/area.ts
|
|
2867
|
+
import { readdirSync as readdirSync4, statSync as statSync4 } from "fs";
|
|
2868
|
+
import { join as join6, extname as extname4 } from "path";
|
|
2869
|
+
var CODE_EXTENSIONS4 = /* @__PURE__ */ new Set([".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"]);
|
|
2870
|
+
var IGNORED_DIRS2 = /* @__PURE__ */ new Set([
|
|
2871
|
+
"node_modules",
|
|
2872
|
+
"dist",
|
|
2873
|
+
"build",
|
|
2874
|
+
".git",
|
|
2875
|
+
".next",
|
|
2876
|
+
".cache",
|
|
2877
|
+
"coverage",
|
|
2878
|
+
".turbo",
|
|
2879
|
+
".vercel",
|
|
2880
|
+
".analyze"
|
|
2881
|
+
]);
|
|
2882
|
+
async function area(target, options = {}) {
|
|
2883
|
+
const cwd = options.cwd || process.cwd();
|
|
2884
|
+
const format = options.format || "text";
|
|
2885
|
+
const filterType = options.type;
|
|
2886
|
+
const full = options.full ?? false;
|
|
2887
|
+
if (!target) {
|
|
2888
|
+
throw new Error("Nome da \xE1rea \xE9 obrigat\xF3rio. Exemplo: ai-tool area meus-pets");
|
|
2889
|
+
}
|
|
2890
|
+
try {
|
|
2891
|
+
const config = readConfig(cwd);
|
|
2892
|
+
const allFiles = getAllCodeFiles3(cwd);
|
|
2893
|
+
const areaFiles = [];
|
|
2894
|
+
const targetLower = target.toLowerCase();
|
|
2895
|
+
for (const filePath of allFiles) {
|
|
2896
|
+
const fileAreas = detectFileAreas(filePath, config);
|
|
2897
|
+
const belongsToArea = fileAreas.some(
|
|
2898
|
+
(a) => a.toLowerCase() === targetLower || a.toLowerCase().includes(targetLower)
|
|
2899
|
+
);
|
|
2900
|
+
if (belongsToArea) {
|
|
2901
|
+
const category = detectCategory(filePath);
|
|
2902
|
+
if (filterType && category !== filterType) {
|
|
2903
|
+
continue;
|
|
2904
|
+
}
|
|
2905
|
+
let description = getFileDescription(cwd, filePath);
|
|
2906
|
+
if (!description && config.settings?.inferDescriptions !== false) {
|
|
2907
|
+
description = inferFileDescription(filePath, category);
|
|
2908
|
+
}
|
|
2909
|
+
areaFiles.push({
|
|
2910
|
+
path: filePath,
|
|
2911
|
+
category,
|
|
2912
|
+
description
|
|
2913
|
+
});
|
|
2914
|
+
}
|
|
2915
|
+
}
|
|
2916
|
+
if (areaFiles.length === 0) {
|
|
2917
|
+
const availableAreas = getAvailableAreas(allFiles, config);
|
|
2918
|
+
return formatAreaNotFound(target, availableAreas);
|
|
2919
|
+
}
|
|
2920
|
+
const byCategory = {};
|
|
2921
|
+
const categories = {};
|
|
2922
|
+
for (const file of areaFiles) {
|
|
2923
|
+
if (!byCategory[file.category]) {
|
|
2924
|
+
byCategory[file.category] = [];
|
|
2925
|
+
}
|
|
2926
|
+
byCategory[file.category].push(file);
|
|
2927
|
+
categories[file.category] = (categories[file.category] || 0) + 1;
|
|
2928
|
+
}
|
|
2929
|
+
for (const cat of Object.keys(byCategory)) {
|
|
2930
|
+
byCategory[cat].sort((a, b) => a.path.localeCompare(b.path));
|
|
2931
|
+
}
|
|
2932
|
+
const realAreaId = findRealAreaId(target, allFiles, config);
|
|
2933
|
+
const detectedArea = {
|
|
2934
|
+
id: realAreaId || target,
|
|
2935
|
+
name: getAreaName(realAreaId || target, config),
|
|
2936
|
+
description: getAreaDescription(realAreaId || target, config),
|
|
2937
|
+
files: areaFiles,
|
|
2938
|
+
fileCount: areaFiles.length,
|
|
2939
|
+
categories,
|
|
2940
|
+
isAutoDetected: !config.areas[realAreaId || target]
|
|
2941
|
+
};
|
|
2942
|
+
const result = {
|
|
2943
|
+
version: "1.0.0",
|
|
2944
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2945
|
+
area: detectedArea,
|
|
2946
|
+
byCategory
|
|
2947
|
+
};
|
|
2948
|
+
if (format === "json") {
|
|
2949
|
+
return JSON.stringify(result, null, 2);
|
|
2950
|
+
}
|
|
2951
|
+
return formatAreaDetailText(result, { full, filterType });
|
|
2952
|
+
} catch (error) {
|
|
2953
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
2954
|
+
throw new Error(`Erro ao executar area: ${message}`);
|
|
2955
|
+
}
|
|
2956
|
+
}
|
|
2957
|
+
function findRealAreaId(target, allFiles, config) {
|
|
2958
|
+
const targetLower = target.toLowerCase();
|
|
2959
|
+
for (const areaId of Object.keys(config.areas)) {
|
|
2960
|
+
if (areaId.toLowerCase() === targetLower || areaId.toLowerCase().includes(targetLower)) {
|
|
2961
|
+
return areaId;
|
|
2962
|
+
}
|
|
2963
|
+
}
|
|
2964
|
+
const detectedAreas = /* @__PURE__ */ new Set();
|
|
2965
|
+
for (const filePath of allFiles) {
|
|
2966
|
+
const areas2 = detectFileAreas(filePath, config);
|
|
2967
|
+
for (const areaId of areas2) {
|
|
2968
|
+
if (areaId.toLowerCase() === targetLower || areaId.toLowerCase().includes(targetLower)) {
|
|
2969
|
+
detectedAreas.add(areaId);
|
|
2970
|
+
}
|
|
2971
|
+
}
|
|
2972
|
+
}
|
|
2973
|
+
return detectedAreas.size > 0 ? [...detectedAreas][0] : null;
|
|
2974
|
+
}
|
|
2975
|
+
function getAvailableAreas(allFiles, config) {
|
|
2976
|
+
const areaCounts = /* @__PURE__ */ new Map();
|
|
2977
|
+
for (const filePath of allFiles) {
|
|
2978
|
+
const areas2 = detectFileAreas(filePath, config);
|
|
2979
|
+
for (const areaId of areas2) {
|
|
2980
|
+
areaCounts.set(areaId, (areaCounts.get(areaId) || 0) + 1);
|
|
2981
|
+
}
|
|
2982
|
+
}
|
|
2983
|
+
return [...areaCounts.entries()].map(([id, count]) => ({ id, count })).sort((a, b) => b.count - a.count);
|
|
2984
|
+
}
|
|
2985
|
+
function formatAreaNotFound(target, availableAreas) {
|
|
2986
|
+
let out = `
|
|
2987
|
+
\u274C \xC1rea n\xE3o encontrada: "${target}"
|
|
2988
|
+
|
|
2989
|
+
`;
|
|
2990
|
+
if (availableAreas.length > 0) {
|
|
2991
|
+
out += `\u{1F4E6} \xC1reas dispon\xEDveis:
|
|
2992
|
+
|
|
2993
|
+
`;
|
|
2994
|
+
for (const { id, count } of availableAreas.slice(0, 15)) {
|
|
2995
|
+
out += ` ${id.padEnd(25)} ${count} arquivos
|
|
2996
|
+
`;
|
|
2997
|
+
}
|
|
2998
|
+
if (availableAreas.length > 15) {
|
|
2999
|
+
out += ` ... e mais ${availableAreas.length - 15}
|
|
3000
|
+
`;
|
|
3001
|
+
}
|
|
3002
|
+
out += `
|
|
3003
|
+
`;
|
|
3004
|
+
}
|
|
3005
|
+
out += `\u{1F4A1} Dicas:
|
|
3006
|
+
`;
|
|
3007
|
+
out += ` - Use o ID exato da \xE1rea (ex: ai-tool area meus-pets)
|
|
3008
|
+
`;
|
|
3009
|
+
out += ` - Use 'ai-tool areas' para listar todas as \xE1reas
|
|
3010
|
+
`;
|
|
3011
|
+
return out;
|
|
3012
|
+
}
|
|
3013
|
+
function getAllCodeFiles3(dir, files = [], baseDir = dir) {
|
|
3014
|
+
try {
|
|
3015
|
+
const entries = readdirSync4(dir);
|
|
3016
|
+
for (const entry of entries) {
|
|
3017
|
+
const fullPath = join6(dir, entry);
|
|
3018
|
+
if (IGNORED_DIRS2.has(entry) || entry.startsWith(".")) {
|
|
3019
|
+
continue;
|
|
3020
|
+
}
|
|
3021
|
+
try {
|
|
3022
|
+
const stat = statSync4(fullPath);
|
|
3023
|
+
if (stat.isDirectory()) {
|
|
3024
|
+
getAllCodeFiles3(fullPath, files, baseDir);
|
|
3025
|
+
} else {
|
|
3026
|
+
const ext = extname4(entry).toLowerCase();
|
|
3027
|
+
if (CODE_EXTENSIONS4.has(ext)) {
|
|
3028
|
+
const relativePath = fullPath.slice(baseDir.length + 1).replace(/\\/g, "/");
|
|
3029
|
+
files.push(relativePath);
|
|
3030
|
+
}
|
|
3031
|
+
}
|
|
3032
|
+
} catch {
|
|
3033
|
+
}
|
|
3034
|
+
}
|
|
3035
|
+
} catch {
|
|
3036
|
+
}
|
|
3037
|
+
return files;
|
|
3038
|
+
}
|
|
3039
|
+
|
|
3040
|
+
// src/commands/areas-init.ts
|
|
3041
|
+
import { readdirSync as readdirSync5, statSync as statSync5 } from "fs";
|
|
3042
|
+
import { join as join7, extname as extname5 } from "path";
|
|
3043
|
+
var CODE_EXTENSIONS5 = /* @__PURE__ */ new Set([".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"]);
|
|
3044
|
+
var IGNORED_DIRS3 = /* @__PURE__ */ new Set([
|
|
3045
|
+
"node_modules",
|
|
3046
|
+
"dist",
|
|
3047
|
+
"build",
|
|
3048
|
+
".git",
|
|
3049
|
+
".next",
|
|
3050
|
+
".cache",
|
|
3051
|
+
"coverage",
|
|
3052
|
+
".turbo",
|
|
3053
|
+
".vercel",
|
|
3054
|
+
".analyze"
|
|
3055
|
+
]);
|
|
3056
|
+
async function areasInit(options = {}) {
|
|
3057
|
+
const cwd = options.cwd || process.cwd();
|
|
3058
|
+
const force = options.force ?? false;
|
|
3059
|
+
try {
|
|
3060
|
+
if (configExists(cwd) && !force) {
|
|
3061
|
+
return `
|
|
3062
|
+
\u26A0\uFE0F Arquivo de configura\xE7\xE3o j\xE1 existe: .analyze/areas.config.json
|
|
3063
|
+
|
|
3064
|
+
Use --force para sobrescrever:
|
|
3065
|
+
ai-tool areas init --force
|
|
3066
|
+
|
|
3067
|
+
Ou edite manualmente o arquivo existente.
|
|
3068
|
+
`.trim();
|
|
3069
|
+
}
|
|
3070
|
+
const allFiles = getAllCodeFiles4(cwd);
|
|
3071
|
+
const currentConfig = readConfig(cwd);
|
|
3072
|
+
const areaCounts = /* @__PURE__ */ new Map();
|
|
3073
|
+
for (const filePath of allFiles) {
|
|
3074
|
+
const areas2 = detectFileAreas(filePath, currentConfig);
|
|
3075
|
+
for (const areaId of areas2) {
|
|
3076
|
+
if (!areaCounts.has(areaId)) {
|
|
3077
|
+
areaCounts.set(areaId, /* @__PURE__ */ new Set());
|
|
3078
|
+
}
|
|
3079
|
+
areaCounts.get(areaId).add(filePath);
|
|
3080
|
+
}
|
|
3081
|
+
}
|
|
3082
|
+
const generatedAreas = {};
|
|
3083
|
+
for (const [areaId, files] of areaCounts) {
|
|
3084
|
+
const patterns = inferPatternsFromFiles([...files]);
|
|
3085
|
+
generatedAreas[areaId] = {
|
|
3086
|
+
name: getAreaName(areaId, currentConfig),
|
|
3087
|
+
description: getAreaDescription(areaId, currentConfig),
|
|
3088
|
+
patterns
|
|
3089
|
+
};
|
|
3090
|
+
}
|
|
3091
|
+
const newConfig = {
|
|
3092
|
+
$schema: "./areas.schema.json",
|
|
3093
|
+
version: "1.0.0",
|
|
3094
|
+
areas: generatedAreas,
|
|
3095
|
+
descriptions: {},
|
|
3096
|
+
settings: {
|
|
3097
|
+
autoDetect: true,
|
|
3098
|
+
inferDescriptions: true,
|
|
3099
|
+
groupByCategory: true
|
|
3100
|
+
}
|
|
3101
|
+
};
|
|
3102
|
+
writeConfig(cwd, newConfig);
|
|
3103
|
+
const sortedAreas = [...areaCounts.entries()].sort((a, b) => b[1].size - a[1].size);
|
|
3104
|
+
let out = `
|
|
3105
|
+
\u2705 Arquivo criado: .analyze/areas.config.json
|
|
3106
|
+
|
|
3107
|
+
\u{1F4E6} \xC1reas detectadas: ${sortedAreas.length}
|
|
3108
|
+
|
|
3109
|
+
`;
|
|
3110
|
+
for (const [areaId, files] of sortedAreas.slice(0, 15)) {
|
|
3111
|
+
const name = getAreaName(areaId, newConfig);
|
|
3112
|
+
out += ` ${name.padEnd(25)} ${files.size} arquivos
|
|
3113
|
+
`;
|
|
3114
|
+
}
|
|
3115
|
+
if (sortedAreas.length > 15) {
|
|
3116
|
+
out += ` ... e mais ${sortedAreas.length - 15}
|
|
3117
|
+
`;
|
|
3118
|
+
}
|
|
3119
|
+
const unmappedCount = allFiles.filter(
|
|
3120
|
+
(f) => detectFileAreas(f, currentConfig).length === 0
|
|
3121
|
+
).length;
|
|
3122
|
+
if (unmappedCount > 0) {
|
|
3123
|
+
out += `
|
|
3124
|
+
\u26A0\uFE0F ${unmappedCount} arquivos sem \xE1rea definida
|
|
3125
|
+
Use 'ai-tool areas' para ver detalhes
|
|
3126
|
+
`;
|
|
3127
|
+
}
|
|
3128
|
+
out += `
|
|
3129
|
+
\u{1F4A1} Edite o arquivo para:
|
|
3130
|
+
- Renomear \xE1reas (campo "name")
|
|
3131
|
+
- Adicionar descri\xE7\xF5es (campo "description")
|
|
3132
|
+
- Ajustar padr\xF5es (campo "patterns")
|
|
3133
|
+
- Adicionar/remover \xE1reas
|
|
3134
|
+
- Definir descri\xE7\xF5es espec\xEDficas de arquivos (campo "descriptions")
|
|
3135
|
+
`;
|
|
3136
|
+
return out.trim();
|
|
3137
|
+
} catch (error) {
|
|
3138
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
3139
|
+
throw new Error(`Erro ao executar areas init: ${message}`);
|
|
3140
|
+
}
|
|
3141
|
+
}
|
|
3142
|
+
function inferPatternsFromFiles(files) {
|
|
3143
|
+
const patterns = /* @__PURE__ */ new Set();
|
|
3144
|
+
const folderGroups = /* @__PURE__ */ new Map();
|
|
3145
|
+
for (const file of files) {
|
|
3146
|
+
const parts = file.split("/");
|
|
3147
|
+
if (parts.length > 1) {
|
|
3148
|
+
const folder = parts.slice(0, Math.min(3, parts.length - 1)).join("/");
|
|
3149
|
+
if (!folderGroups.has(folder)) {
|
|
3150
|
+
folderGroups.set(folder, []);
|
|
3151
|
+
}
|
|
3152
|
+
folderGroups.get(folder).push(file);
|
|
3153
|
+
}
|
|
3154
|
+
}
|
|
3155
|
+
for (const [folder, folderFiles] of folderGroups) {
|
|
3156
|
+
if (folderFiles.length >= 2) {
|
|
3157
|
+
patterns.add(`${folder}/**`);
|
|
3158
|
+
} else {
|
|
3159
|
+
patterns.add(folderFiles[0]);
|
|
3160
|
+
}
|
|
3161
|
+
}
|
|
3162
|
+
for (const file of files) {
|
|
3163
|
+
if (!file.includes("/")) {
|
|
3164
|
+
patterns.add(file);
|
|
3165
|
+
}
|
|
3166
|
+
}
|
|
3167
|
+
return [...patterns].sort();
|
|
3168
|
+
}
|
|
3169
|
+
function getAllCodeFiles4(dir, files = [], baseDir = dir) {
|
|
3170
|
+
try {
|
|
3171
|
+
const entries = readdirSync5(dir);
|
|
3172
|
+
for (const entry of entries) {
|
|
3173
|
+
const fullPath = join7(dir, entry);
|
|
3174
|
+
if (IGNORED_DIRS3.has(entry) || entry.startsWith(".")) {
|
|
3175
|
+
continue;
|
|
3176
|
+
}
|
|
3177
|
+
try {
|
|
3178
|
+
const stat = statSync5(fullPath);
|
|
3179
|
+
if (stat.isDirectory()) {
|
|
3180
|
+
getAllCodeFiles4(fullPath, files, baseDir);
|
|
3181
|
+
} else {
|
|
3182
|
+
const ext = extname5(entry).toLowerCase();
|
|
3183
|
+
if (CODE_EXTENSIONS5.has(ext)) {
|
|
3184
|
+
const relativePath = fullPath.slice(baseDir.length + 1).replace(/\\/g, "/");
|
|
3185
|
+
files.push(relativePath);
|
|
3186
|
+
}
|
|
3187
|
+
}
|
|
3188
|
+
} catch {
|
|
3189
|
+
}
|
|
3190
|
+
}
|
|
3191
|
+
} catch {
|
|
3192
|
+
}
|
|
3193
|
+
return files;
|
|
3194
|
+
}
|
|
3195
|
+
|
|
1834
3196
|
// src/index.ts
|
|
1835
|
-
var VERSION = "0.
|
|
3197
|
+
var VERSION = "0.4.0";
|
|
1836
3198
|
|
|
1837
3199
|
export {
|
|
1838
3200
|
detectCategory,
|
|
1839
3201
|
categoryIcons,
|
|
1840
3202
|
isEntryPoint,
|
|
1841
3203
|
isCodeFile,
|
|
3204
|
+
isFirebaseProject,
|
|
3205
|
+
hasFirebaseFunctions,
|
|
3206
|
+
isExportedCloudFunction,
|
|
3207
|
+
filterCloudFunctionsFalsePositives,
|
|
3208
|
+
clearFirebaseCache,
|
|
1842
3209
|
getCacheDir,
|
|
1843
3210
|
isCacheValid,
|
|
1844
3211
|
invalidateCache,
|
|
@@ -1848,5 +3215,23 @@ export {
|
|
|
1848
3215
|
impact,
|
|
1849
3216
|
suggest,
|
|
1850
3217
|
context,
|
|
3218
|
+
configExists,
|
|
3219
|
+
readConfig,
|
|
3220
|
+
writeConfig,
|
|
3221
|
+
setArea,
|
|
3222
|
+
removeArea,
|
|
3223
|
+
setFileDescription,
|
|
3224
|
+
getFileDescription,
|
|
3225
|
+
FOLDER_PATTERNS,
|
|
3226
|
+
KEYWORD_PATTERNS,
|
|
3227
|
+
AREA_NAMES,
|
|
3228
|
+
AREA_DESCRIPTIONS,
|
|
3229
|
+
detectFileAreas,
|
|
3230
|
+
getAreaName,
|
|
3231
|
+
getAreaDescription,
|
|
3232
|
+
inferFileDescription,
|
|
3233
|
+
areas,
|
|
3234
|
+
area,
|
|
3235
|
+
areasInit,
|
|
1851
3236
|
VERSION
|
|
1852
3237
|
};
|