@semacode/cli 1.0.0 → 1.2.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/AGENTS.md +50 -0
- package/README.md +15 -2
- package/SEMA_BRIEF.curto.txt +9 -0
- package/SEMA_BRIEF.md +49 -0
- package/SEMA_BRIEF.micro.txt +7 -0
- package/SEMA_INDEX.json +546 -0
- package/dist/drift.d.ts +17 -2
- package/dist/drift.js +516 -5
- package/dist/drift.js.map +1 -1
- package/dist/importador.d.ts +1 -1
- package/dist/importador.js +741 -3
- package/dist/importador.js.map +1 -1
- package/dist/index.js +962 -86
- package/dist/index.js.map +1 -1
- package/dist/lua-symbols.d.ts +8 -0
- package/dist/lua-symbols.js +68 -0
- package/dist/lua-symbols.js.map +1 -0
- package/dist/projeto.js +56 -2
- package/dist/projeto.js.map +1 -1
- package/dist/tipos.d.ts +1 -1
- package/docs/AGENT_STARTER.md +34 -6
- package/docs/instalacao-e-primeiro-uso.md +18 -9
- package/llms-full.txt +34 -0
- package/llms.txt +17 -0
- package/node_modules/@sema/gerador-dart/package.json +1 -1
- package/node_modules/@sema/gerador-lua/dist/index.d.ts +3 -0
- package/node_modules/@sema/gerador-lua/dist/index.js +360 -0
- package/node_modules/@sema/gerador-lua/dist/index.js.map +1 -0
- package/node_modules/@sema/gerador-lua/package.json +7 -0
- package/node_modules/@sema/gerador-python/dist/index.js +92 -10
- package/node_modules/@sema/gerador-python/dist/index.js.map +1 -1
- package/node_modules/@sema/gerador-python/package.json +1 -1
- package/node_modules/@sema/gerador-typescript/package.json +1 -1
- package/node_modules/@sema/nucleo/dist/ast/tipos.d.ts +1 -1
- package/node_modules/@sema/nucleo/dist/ir/conversor.js +4 -0
- package/node_modules/@sema/nucleo/dist/ir/conversor.js.map +1 -1
- package/node_modules/@sema/nucleo/dist/ir/modelos.d.ts +3 -3
- package/node_modules/@sema/nucleo/dist/parser/parser.js +2 -0
- package/node_modules/@sema/nucleo/dist/parser/parser.js.map +1 -1
- package/node_modules/@sema/nucleo/dist/semantico/analisador.d.ts +2 -2
- package/node_modules/@sema/nucleo/dist/semantico/analisador.js +3 -1
- package/node_modules/@sema/nucleo/dist/semantico/analisador.js.map +1 -1
- package/node_modules/@sema/nucleo/package.json +1 -1
- package/node_modules/@sema/padroes/dist/index.d.ts +2 -1
- package/node_modules/@sema/padroes/dist/index.js +64 -1
- package/node_modules/@sema/padroes/dist/index.js.map +1 -1
- package/node_modules/@sema/padroes/package.json +1 -1
- package/package.json +16 -7
package/dist/importador.js
CHANGED
|
@@ -9,6 +9,7 @@ import { extrairRotasGo, extrairSimbolosGo } from "./go-http.js";
|
|
|
9
9
|
import { extrairRotasJava, extrairSimbolosJava } from "./java-http.js";
|
|
10
10
|
import { extrairParametrosCaminhoFlask, extrairRotasFlaskDecoradas } from "./python-http.js";
|
|
11
11
|
import { extrairRotasRust, extrairSimbolosRust } from "./rust-http.js";
|
|
12
|
+
import { extrairSimbolosLua } from "./lua-symbols.js";
|
|
12
13
|
import { extrairRotasTypeScriptHttp, inferirSemanticaHandlerTypeScriptHttp, localizarExportacaoTypeScriptHttp, } from "./typescript-http.js";
|
|
13
14
|
const DIRETORIOS_IGNORADOS = new Set([
|
|
14
15
|
".git",
|
|
@@ -190,6 +191,7 @@ function mapearTipoPrimitivo(tipo) {
|
|
|
190
191
|
const limpo = tipo.trim().replace(/\s+/g, "");
|
|
191
192
|
const base = limpo
|
|
192
193
|
.replace(/^Promise<(.*)>$/, "$1")
|
|
194
|
+
.replace(/^Future<(.*)>$/, "$1")
|
|
193
195
|
.replace(/\|undefined/g, "")
|
|
194
196
|
.replace(/\|null/g, "")
|
|
195
197
|
.replace(/\bundefined\|/g, "")
|
|
@@ -217,10 +219,22 @@ function mapearTipoPrimitivo(tipo) {
|
|
|
217
219
|
if (minusculo === "id" || minusculo.endsWith("id")) {
|
|
218
220
|
return "Id";
|
|
219
221
|
}
|
|
220
|
-
if (minusculo.includes("[]")
|
|
222
|
+
if (minusculo.includes("[]")
|
|
223
|
+
|| minusculo.startsWith("array<")
|
|
224
|
+
|| minusculo.startsWith("record<")
|
|
225
|
+
|| minusculo.startsWith("map<")
|
|
226
|
+
|| minusculo.startsWith("list<")
|
|
227
|
+
|| minusculo.startsWith("list[")
|
|
228
|
+
|| minusculo.startsWith("dict[")) {
|
|
221
229
|
return "Json";
|
|
222
230
|
}
|
|
223
|
-
if (minusculo === "json"
|
|
231
|
+
if (minusculo === "json"
|
|
232
|
+
|| minusculo === "object"
|
|
233
|
+
|| minusculo === "unknown"
|
|
234
|
+
|| minusculo === "any"
|
|
235
|
+
|| minusculo === "dynamic"
|
|
236
|
+
|| minusculo === "void"
|
|
237
|
+
|| minusculo === "none") {
|
|
224
238
|
return minusculo === "void" || minusculo === "none" ? "Vazio" : "Json";
|
|
225
239
|
}
|
|
226
240
|
return tipo.trim();
|
|
@@ -568,6 +582,624 @@ function resolverEscopoImportacaoNextJs(diretorioEntrada) {
|
|
|
568
582
|
diretorioEscopo: resolvido,
|
|
569
583
|
};
|
|
570
584
|
}
|
|
585
|
+
function normalizarCaminhoImportado(caminhoArquivo) {
|
|
586
|
+
return caminhoArquivo.replace(/\\/g, "/");
|
|
587
|
+
}
|
|
588
|
+
function normalizarSegmentoRotaConsumer(segmento) {
|
|
589
|
+
const opcionalCatchAll = segmento.match(/^\[\[\.\.\.([A-Za-z_]\w*)\]\]$/);
|
|
590
|
+
if (opcionalCatchAll) {
|
|
591
|
+
return `{${opcionalCatchAll[1]}}`;
|
|
592
|
+
}
|
|
593
|
+
const catchAll = segmento.match(/^\[\.\.\.([A-Za-z_]\w*)\]$/);
|
|
594
|
+
if (catchAll) {
|
|
595
|
+
return `{${catchAll[1]}}`;
|
|
596
|
+
}
|
|
597
|
+
const dinamico = segmento.match(/^\[([A-Za-z_]\w*)\]$/);
|
|
598
|
+
if (dinamico) {
|
|
599
|
+
return `{${dinamico[1]}}`;
|
|
600
|
+
}
|
|
601
|
+
return segmento;
|
|
602
|
+
}
|
|
603
|
+
function montarCaminhoRotaConsumer(partes) {
|
|
604
|
+
const filtradas = partes
|
|
605
|
+
.filter((segmento) => segmento && segmento !== "index" && !/^\(.*\)$/.test(segmento) && !segmento.startsWith("@"))
|
|
606
|
+
.map(normalizarSegmentoRotaConsumer);
|
|
607
|
+
return filtradas.length > 0 ? `/${filtradas.join("/")}`.replace(/\/+/g, "/") : "/";
|
|
608
|
+
}
|
|
609
|
+
function resolverEscopoImportacaoFrontendConsumer(diretorioEntrada) {
|
|
610
|
+
const resolvido = path.resolve(diretorioEntrada);
|
|
611
|
+
const partes = path.parse(resolvido);
|
|
612
|
+
const segmentos = resolvido.slice(partes.root.length).split(path.sep).filter(Boolean);
|
|
613
|
+
const procurarSequencia = (sequencia) => segmentos.findIndex((segmento, indice) => sequencia.every((item, deslocamento) => segmentos[indice + deslocamento]?.toLowerCase() === item));
|
|
614
|
+
const montarBase = (indice) => indice <= 0
|
|
615
|
+
? partes.root
|
|
616
|
+
: path.join(partes.root, ...segmentos.slice(0, indice));
|
|
617
|
+
for (const sequencia of [
|
|
618
|
+
["src", "pages"],
|
|
619
|
+
["pages"],
|
|
620
|
+
["src", "app", "api"],
|
|
621
|
+
["app", "api"],
|
|
622
|
+
["src", "app"],
|
|
623
|
+
["app"],
|
|
624
|
+
["src", "lib"],
|
|
625
|
+
["lib"],
|
|
626
|
+
]) {
|
|
627
|
+
const indice = procurarSequencia(sequencia);
|
|
628
|
+
if (indice >= 0) {
|
|
629
|
+
return {
|
|
630
|
+
baseProjeto: montarBase(indice),
|
|
631
|
+
diretorioEscopo: resolvido,
|
|
632
|
+
};
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
return {
|
|
636
|
+
baseProjeto: resolvido,
|
|
637
|
+
diretorioEscopo: resolvido,
|
|
638
|
+
};
|
|
639
|
+
}
|
|
640
|
+
function arquivoEhBridgeNextJsConsumer(relacaoArquivo) {
|
|
641
|
+
const relacao = normalizarCaminhoImportado(relacaoArquivo);
|
|
642
|
+
return /(?:^|\/)(?:src\/)?lib\/(?:sema_consumer_bridge|sema\/.+)\.(?:ts|tsx|js|jsx)$/i.test(relacao);
|
|
643
|
+
}
|
|
644
|
+
function arquivoEhBridgeReactViteConsumer(relacaoArquivo) {
|
|
645
|
+
const relacao = normalizarCaminhoImportado(relacaoArquivo);
|
|
646
|
+
return /(?:^|\/)(?:src\/)?lib\/(?:sema_consumer_bridge|sema\/.+)\.(?:ts|tsx|js|jsx)$/i.test(relacao);
|
|
647
|
+
}
|
|
648
|
+
function arquivoEhBridgeAngularConsumer(relacaoArquivo) {
|
|
649
|
+
const relacao = normalizarCaminhoImportado(relacaoArquivo);
|
|
650
|
+
return /(?:^|\/)(?:src\/)?app\/(?:sema_consumer_bridge|sema\/.+)\.(?:ts|js)$/i.test(relacao);
|
|
651
|
+
}
|
|
652
|
+
function arquivoEhSuperficieNextJsConsumer(relacaoArquivo) {
|
|
653
|
+
const relacao = normalizarCaminhoImportado(relacaoArquivo);
|
|
654
|
+
return /(?:^|\/)(?:src\/)?app\/(?:(?!api\/).)*?(?:page|layout|loading|error)\.(?:ts|tsx|js|jsx)$/i.test(relacao);
|
|
655
|
+
}
|
|
656
|
+
function arquivoEhSuperficieReactViteConsumer(relacaoArquivo) {
|
|
657
|
+
const relacao = normalizarCaminhoImportado(relacaoArquivo);
|
|
658
|
+
return /^(?:src\/)?pages\/.+\.(?:ts|tsx|js|jsx)$/i.test(relacao)
|
|
659
|
+
|| /^(?:src\/)?App\.(?:ts|tsx|js|jsx)$/i.test(relacao);
|
|
660
|
+
}
|
|
661
|
+
function arquivoEhRotasReactViteConsumer(relacaoArquivo, codigo) {
|
|
662
|
+
const relacao = normalizarCaminhoImportado(relacaoArquivo);
|
|
663
|
+
return /(?:^|\/)(?:src\/)?(?:app\/)?(?:router|routes)\.(?:ts|tsx|js|jsx)$/i.test(relacao)
|
|
664
|
+
|| /from\s+["']react-router-dom["']|createBrowserRouter|RouterProvider|useRoutes\s*\(|<Routes\b|<Route\b/.test(codigo ?? "");
|
|
665
|
+
}
|
|
666
|
+
function arquivoEhRotasAngularConsumer(relacaoArquivo) {
|
|
667
|
+
const relacao = normalizarCaminhoImportado(relacaoArquivo);
|
|
668
|
+
return /(?:^|\/)(?:src\/)?app(?:\/.+)?\/[^/]+\.routes\.(?:ts|js)$/i.test(relacao);
|
|
669
|
+
}
|
|
670
|
+
function arquivoEhRotasAngularConsumerRaiz(relacaoArquivo) {
|
|
671
|
+
const relacao = normalizarCaminhoImportado(relacaoArquivo);
|
|
672
|
+
return /(?:^|\/)(?:src\/)?app\/[^/]+\.routes\.(?:ts|js)$/i.test(relacao);
|
|
673
|
+
}
|
|
674
|
+
function arquivoEhBridgeFlutterConsumer(relacaoArquivo) {
|
|
675
|
+
const relacao = normalizarCaminhoImportado(relacaoArquivo);
|
|
676
|
+
return /(?:^|\/)(?:lib\/)?(?:sema_consumer_bridge|api\/sema_contract_bridge|sema\/.+)\.dart$/i.test(relacao);
|
|
677
|
+
}
|
|
678
|
+
function arquivoEhSuperficieFlutterConsumer(relacaoArquivo) {
|
|
679
|
+
const relacao = normalizarCaminhoImportado(relacaoArquivo);
|
|
680
|
+
return /(?:^|\/)(?:lib\/)?(?:screens|pages)\/.+\.dart$/i.test(relacao)
|
|
681
|
+
|| /(?:^|\/)(?:lib\/)?main\.dart$/i.test(relacao);
|
|
682
|
+
}
|
|
683
|
+
function arquivoEhRotasFlutterConsumer(relacaoArquivo, codigo) {
|
|
684
|
+
const relacao = normalizarCaminhoImportado(relacaoArquivo);
|
|
685
|
+
return /(?:^|\/)(?:lib\/)?(?:router|app_router|routes)\.dart$/i.test(relacao)
|
|
686
|
+
|| /MaterialApp(?:\.router)?\s*\(|CupertinoApp(?:\.router)?\s*\(|GoRouter\s*\(/.test(codigo ?? "");
|
|
687
|
+
}
|
|
688
|
+
function inferirCaminhoNextJsConsumer(relacaoArquivo) {
|
|
689
|
+
const relacao = normalizarCaminhoImportado(relacaoArquivo);
|
|
690
|
+
const segmentos = relacao.split("/");
|
|
691
|
+
const indiceSrcApp = segmentos.findIndex((segmento, indice) => segmento === "src" && segmentos[indice + 1] === "app");
|
|
692
|
+
const indiceApp = segmentos.findIndex((segmento) => segmento === "app");
|
|
693
|
+
const inicioApp = indiceSrcApp >= 0 ? indiceSrcApp + 2 : indiceApp >= 0 ? indiceApp + 1 : -1;
|
|
694
|
+
if (inicioApp < 0) {
|
|
695
|
+
return undefined;
|
|
696
|
+
}
|
|
697
|
+
const arquivoFinal = segmentos.at(-1) ?? "";
|
|
698
|
+
const tipoArquivo = arquivoFinal.match(/^(page|layout|loading|error)\.(?:ts|tsx|js|jsx)$/)?.[1];
|
|
699
|
+
if (!tipoArquivo) {
|
|
700
|
+
return undefined;
|
|
701
|
+
}
|
|
702
|
+
const caminhoAteArquivo = segmentos.slice(inicioApp, -1);
|
|
703
|
+
if (caminhoAteArquivo[0] === "api") {
|
|
704
|
+
return undefined;
|
|
705
|
+
}
|
|
706
|
+
const partes = caminhoAteArquivo
|
|
707
|
+
.filter((segmento) => segmento);
|
|
708
|
+
const caminho = montarCaminhoRotaConsumer(partes);
|
|
709
|
+
return {
|
|
710
|
+
caminho,
|
|
711
|
+
arquivo: relacao,
|
|
712
|
+
tipoArquivo,
|
|
713
|
+
};
|
|
714
|
+
}
|
|
715
|
+
function inferirCaminhoReactViteConsumer(relacaoArquivo) {
|
|
716
|
+
const relacao = normalizarCaminhoImportado(relacaoArquivo);
|
|
717
|
+
if (!arquivoEhSuperficieReactViteConsumer(relacao)) {
|
|
718
|
+
return undefined;
|
|
719
|
+
}
|
|
720
|
+
if (/(?:^|\/)(?:src\/)?App\.(?:ts|tsx|js|jsx)$/i.test(relacao)) {
|
|
721
|
+
return {
|
|
722
|
+
caminho: "/",
|
|
723
|
+
arquivo: relacao,
|
|
724
|
+
tipoArquivo: "app",
|
|
725
|
+
};
|
|
726
|
+
}
|
|
727
|
+
const segmentos = relacao.split("/");
|
|
728
|
+
const indiceSrcPages = segmentos.findIndex((segmento, indice) => segmento === "src" && segmentos[indice + 1] === "pages");
|
|
729
|
+
const indicePages = segmentos.findIndex((segmento) => segmento === "pages");
|
|
730
|
+
const inicioPages = indiceSrcPages >= 0 ? indiceSrcPages + 2 : indicePages >= 0 ? indicePages + 1 : -1;
|
|
731
|
+
if (inicioPages < 0) {
|
|
732
|
+
return undefined;
|
|
733
|
+
}
|
|
734
|
+
const arquivoFinal = segmentos.at(-1) ?? "";
|
|
735
|
+
const nomeBase = arquivoFinal.replace(/\.(?:ts|tsx|js|jsx)$/i, "");
|
|
736
|
+
const caminho = montarCaminhoRotaConsumer([...segmentos.slice(inicioPages, -1), nomeBase]);
|
|
737
|
+
return {
|
|
738
|
+
caminho,
|
|
739
|
+
arquivo: relacao,
|
|
740
|
+
tipoArquivo: "page",
|
|
741
|
+
};
|
|
742
|
+
}
|
|
743
|
+
function inferirCaminhoFlutterConsumer(relacaoArquivo) {
|
|
744
|
+
const relacao = normalizarCaminhoImportado(relacaoArquivo);
|
|
745
|
+
if (!arquivoEhSuperficieFlutterConsumer(relacao)) {
|
|
746
|
+
return undefined;
|
|
747
|
+
}
|
|
748
|
+
if (/(?:^|\/)(?:lib\/)?main\.dart$/i.test(relacao)) {
|
|
749
|
+
return {
|
|
750
|
+
caminho: "/",
|
|
751
|
+
arquivo: relacao,
|
|
752
|
+
tipoArquivo: "app",
|
|
753
|
+
};
|
|
754
|
+
}
|
|
755
|
+
const segmentos = relacao.split("/");
|
|
756
|
+
const indiceLibScreens = segmentos.findIndex((segmento, indice) => segmento === "lib" && ["screens", "pages"].includes(segmentos[indice + 1] ?? ""));
|
|
757
|
+
const indiceScreens = segmentos.findIndex((segmento) => segmento === "screens" || segmento === "pages");
|
|
758
|
+
const inicio = indiceLibScreens >= 0 ? indiceLibScreens + 2 : indiceScreens >= 0 ? indiceScreens + 1 : -1;
|
|
759
|
+
if (inicio < 0) {
|
|
760
|
+
return undefined;
|
|
761
|
+
}
|
|
762
|
+
const arquivoFinal = segmentos.at(-1) ?? "";
|
|
763
|
+
const nomeBase = arquivoFinal
|
|
764
|
+
.replace(/\.(?:dart)$/i, "")
|
|
765
|
+
.replace(/_(screen|page)$/i, "");
|
|
766
|
+
return {
|
|
767
|
+
caminho: montarCaminhoRotaConsumer([...segmentos.slice(inicio, -1), nomeBase]),
|
|
768
|
+
arquivo: relacao,
|
|
769
|
+
tipoArquivo: "screen",
|
|
770
|
+
};
|
|
771
|
+
}
|
|
772
|
+
function normalizarRotaDeclaradaConsumer(caminhoCru, prefixo = "/") {
|
|
773
|
+
const partesPrefixo = prefixo.replace(/^\/+|\/+$/g, "").split("/").filter(Boolean);
|
|
774
|
+
const partesCaminho = (caminhoCru ?? "").trim().replace(/^\/+|\/+$/g, "").split("/").filter(Boolean);
|
|
775
|
+
return montarCaminhoRotaConsumer([...partesPrefixo, ...partesCaminho]);
|
|
776
|
+
}
|
|
777
|
+
function resolverImportRelativoTypeScript(relacaoArquivoBase, especificador) {
|
|
778
|
+
if (!especificador.startsWith(".")) {
|
|
779
|
+
return undefined;
|
|
780
|
+
}
|
|
781
|
+
const baseDir = path.posix.dirname(normalizarCaminhoImportado(relacaoArquivoBase));
|
|
782
|
+
for (const sufixo of ["", ".ts", ".tsx", ".js", ".jsx", "/index.ts", "/index.tsx", "/index.js", "/index.jsx"]) {
|
|
783
|
+
const candidato = path.posix.normalize(path.posix.join(baseDir, `${especificador}${sufixo}`));
|
|
784
|
+
if (!/\.(?:ts|tsx|js|jsx)$/i.test(candidato)) {
|
|
785
|
+
continue;
|
|
786
|
+
}
|
|
787
|
+
return candidato;
|
|
788
|
+
}
|
|
789
|
+
return undefined;
|
|
790
|
+
}
|
|
791
|
+
function extrairImportsTypeScriptConsumer(relacaoArquivo, codigo) {
|
|
792
|
+
const imports = new Map();
|
|
793
|
+
for (const match of codigo.matchAll(/import\s*\{\s*([^}]+)\s*\}\s*from\s*["']([^"']+)["']/g)) {
|
|
794
|
+
const moduloImportado = match[2];
|
|
795
|
+
const relacaoImportada = resolverImportRelativoTypeScript(relacaoArquivo, moduloImportado);
|
|
796
|
+
if (!relacaoImportada) {
|
|
797
|
+
continue;
|
|
798
|
+
}
|
|
799
|
+
for (const bruto of match[1].split(",")) {
|
|
800
|
+
const normalizado = bruto.trim();
|
|
801
|
+
if (!normalizado) {
|
|
802
|
+
continue;
|
|
803
|
+
}
|
|
804
|
+
const local = normalizado.split(/\s+as\s+/i).at(-1)?.trim();
|
|
805
|
+
if (local) {
|
|
806
|
+
imports.set(local, relacaoImportada);
|
|
807
|
+
}
|
|
808
|
+
}
|
|
809
|
+
}
|
|
810
|
+
for (const match of codigo.matchAll(/import\s+([A-Za-z_]\w*)\s+from\s*["']([^"']+)["']/g)) {
|
|
811
|
+
const relacaoImportada = resolverImportRelativoTypeScript(relacaoArquivo, match[2]);
|
|
812
|
+
const local = match[1]?.trim();
|
|
813
|
+
if (relacaoImportada && local) {
|
|
814
|
+
imports.set(local, relacaoImportada);
|
|
815
|
+
}
|
|
816
|
+
}
|
|
817
|
+
return imports;
|
|
818
|
+
}
|
|
819
|
+
function extrairRotasReactViteConsumer(relacaoArquivo, codigo) {
|
|
820
|
+
const imports = extrairImportsTypeScriptConsumer(relacaoArquivo, codigo);
|
|
821
|
+
const rotas = new Map();
|
|
822
|
+
const registrar = (caminhoCru, componente) => {
|
|
823
|
+
const caminho = normalizarRotaDeclaradaConsumer(caminhoCru);
|
|
824
|
+
const chave = `${caminho}:${normalizarCaminhoImportado(relacaoArquivo)}:${componente ?? "router"}`;
|
|
825
|
+
rotas.set(chave, {
|
|
826
|
+
caminho,
|
|
827
|
+
arquivoRotas: normalizarCaminhoImportado(relacaoArquivo),
|
|
828
|
+
arquivoComponente: componente ? imports.get(componente) : undefined,
|
|
829
|
+
});
|
|
830
|
+
};
|
|
831
|
+
for (const match of codigo.matchAll(/(?:path\s*:\s*["'`]([^"'`]*)["'`]|index\s*:\s*true)[\s\S]{0,260}?(?:element\s*:\s*<\s*([A-Za-z_]\w*)|Component\s*:\s*([A-Za-z_]\w*))/g)) {
|
|
832
|
+
const caminhoCru = match[1] ?? "";
|
|
833
|
+
const componente = match[2] ?? match[3];
|
|
834
|
+
registrar(caminhoCru, componente);
|
|
835
|
+
}
|
|
836
|
+
for (const match of codigo.matchAll(/<Route\b[^>]*?(?:path=["'`]([^"'`]*)["'`][^>]*?)?(index\b)?[^>]*?(?:element=\{\s*<\s*([A-Za-z_]\w*)|Component=\{\s*([A-Za-z_]\w*))/g)) {
|
|
837
|
+
const caminhoCru = match[2] ? "" : (match[1] ?? "");
|
|
838
|
+
const componente = match[3] ?? match[4];
|
|
839
|
+
registrar(caminhoCru, componente);
|
|
840
|
+
}
|
|
841
|
+
return [...rotas.values()];
|
|
842
|
+
}
|
|
843
|
+
function extrairRotasAngularConsumerDiretas(relacaoArquivo, codigo, prefixo = "/") {
|
|
844
|
+
const imports = extrairImportsTypeScriptConsumer(relacaoArquivo, codigo);
|
|
845
|
+
const rotas = [];
|
|
846
|
+
for (const match of codigo.matchAll(/path\s*:\s*["'`]([^"'`]*)["'`][\s\S]{0,320}?component\s*:\s*([A-Za-z_]\w*)/g)) {
|
|
847
|
+
const caminhoCru = (match[1] ?? "").trim();
|
|
848
|
+
const componente = match[2];
|
|
849
|
+
rotas.push({
|
|
850
|
+
caminho: normalizarRotaDeclaradaConsumer(caminhoCru, prefixo),
|
|
851
|
+
arquivoRotas: normalizarCaminhoImportado(relacaoArquivo),
|
|
852
|
+
arquivoComponente: imports.get(componente),
|
|
853
|
+
});
|
|
854
|
+
}
|
|
855
|
+
for (const match of codigo.matchAll(/path\s*:\s*["'`]([^"'`]*)["'`][\s\S]{0,320}?loadComponent\s*:\s*\(\s*\)\s*=>\s*import\s*\(\s*["'`]([^"'`]+)["'`]\s*\)/g)) {
|
|
856
|
+
const caminhoCru = (match[1] ?? "").trim();
|
|
857
|
+
const relacaoImportada = resolverImportRelativoTypeScript(relacaoArquivo, match[2] ?? "");
|
|
858
|
+
rotas.push({
|
|
859
|
+
caminho: normalizarRotaDeclaradaConsumer(caminhoCru, prefixo),
|
|
860
|
+
arquivoRotas: normalizarCaminhoImportado(relacaoArquivo),
|
|
861
|
+
arquivoComponente: relacaoImportada,
|
|
862
|
+
});
|
|
863
|
+
}
|
|
864
|
+
for (const match of codigo.matchAll(/path\s*:\s*["'`]([^"'`]*)["'`][\s\S]{0,360}?loadChildren\s*:\s*\(\s*\)\s*=>\s*import\s*\(\s*["'`]([^"'`]+)["'`]\s*\)/g)) {
|
|
865
|
+
const caminhoCru = (match[1] ?? "").trim();
|
|
866
|
+
const relacaoImportada = resolverImportRelativoTypeScript(relacaoArquivo, match[2] ?? "");
|
|
867
|
+
rotas.push({
|
|
868
|
+
caminho: normalizarRotaDeclaradaConsumer(caminhoCru, prefixo),
|
|
869
|
+
arquivoRotas: normalizarCaminhoImportado(relacaoArquivo),
|
|
870
|
+
arquivoRotasFilhas: relacaoImportada,
|
|
871
|
+
});
|
|
872
|
+
}
|
|
873
|
+
return rotas;
|
|
874
|
+
}
|
|
875
|
+
async function extrairRotasAngularConsumer(baseProjeto, relacaoArquivo, prefixo = "/", visitados = new Set()) {
|
|
876
|
+
const relacaoNormalizada = normalizarCaminhoImportado(relacaoArquivo);
|
|
877
|
+
if (visitados.has(relacaoNormalizada)) {
|
|
878
|
+
return [];
|
|
879
|
+
}
|
|
880
|
+
visitados.add(relacaoNormalizada);
|
|
881
|
+
const caminhoAbsoluto = path.join(baseProjeto, relacaoNormalizada);
|
|
882
|
+
let codigo = "";
|
|
883
|
+
try {
|
|
884
|
+
codigo = await readFile(caminhoAbsoluto, "utf8");
|
|
885
|
+
}
|
|
886
|
+
catch {
|
|
887
|
+
return [];
|
|
888
|
+
}
|
|
889
|
+
const diretas = extrairRotasAngularConsumerDiretas(relacaoNormalizada, codigo, prefixo);
|
|
890
|
+
const agregadas = [...diretas];
|
|
891
|
+
for (const rota of diretas) {
|
|
892
|
+
if (!rota.arquivoRotasFilhas) {
|
|
893
|
+
continue;
|
|
894
|
+
}
|
|
895
|
+
agregadas.push(...await extrairRotasAngularConsumer(baseProjeto, rota.arquivoRotasFilhas, rota.caminho, visitados));
|
|
896
|
+
}
|
|
897
|
+
return agregadas;
|
|
898
|
+
}
|
|
899
|
+
function normalizarRotaDeclaradaFlutter(caminhoCru) {
|
|
900
|
+
return montarCaminhoRotaConsumer((caminhoCru ?? "").trim().replace(/^\/+|\/+$/g, "").split("/").filter(Boolean));
|
|
901
|
+
}
|
|
902
|
+
function extrairRotasFlutterConsumer(relacaoArquivo, codigo) {
|
|
903
|
+
const rotas = new Map();
|
|
904
|
+
const registrar = (caminhoCru) => {
|
|
905
|
+
const caminho = normalizarRotaDeclaradaFlutter(caminhoCru);
|
|
906
|
+
rotas.set(`${caminho}:${normalizarCaminhoImportado(relacaoArquivo)}`, {
|
|
907
|
+
caminho,
|
|
908
|
+
arquivoRotas: normalizarCaminhoImportado(relacaoArquivo),
|
|
909
|
+
});
|
|
910
|
+
};
|
|
911
|
+
for (const match of codigo.matchAll(/GoRoute\s*\([\s\S]{0,220}?path\s*:\s*["'`]([^"'`]+)["'`]/g)) {
|
|
912
|
+
registrar(match[1] ?? "");
|
|
913
|
+
}
|
|
914
|
+
for (const match of codigo.matchAll(/["'`]([^"'`]+)["'`]\s*:\s*\([^)]*\)\s*=>/g)) {
|
|
915
|
+
registrar(match[1] ?? "");
|
|
916
|
+
}
|
|
917
|
+
if (/home\s*:\s*(?:const\s+)?[A-Za-z_]\w*\(/.test(codigo)) {
|
|
918
|
+
registrar("/");
|
|
919
|
+
}
|
|
920
|
+
return [...rotas.values()];
|
|
921
|
+
}
|
|
922
|
+
async function carregarContextosBridgeConsumer(baseProjeto, arquivosBridge) {
|
|
923
|
+
return Promise.all(arquivosBridge.map(async (arquivo) => {
|
|
924
|
+
const texto = await readFile(arquivo, "utf8");
|
|
925
|
+
const scriptKind = arquivo.endsWith(".tsx") ? ts.ScriptKind.TSX : ts.ScriptKind.TS;
|
|
926
|
+
return {
|
|
927
|
+
sourceFile: ts.createSourceFile(arquivo, texto, ts.ScriptTarget.Latest, true, scriptKind),
|
|
928
|
+
texto,
|
|
929
|
+
relacao: path.relative(baseProjeto, arquivo),
|
|
930
|
+
};
|
|
931
|
+
}));
|
|
932
|
+
}
|
|
933
|
+
function extrairTasksBridgeConsumer(baseProjeto, contextosBridge) {
|
|
934
|
+
const tiposGlobais = consolidarTiposTs(contextosBridge);
|
|
935
|
+
const entitiesRef = new Set();
|
|
936
|
+
const enumsRef = new Set();
|
|
937
|
+
const tasks = [];
|
|
938
|
+
for (const contexto of contextosBridge) {
|
|
939
|
+
contexto.sourceFile.forEachChild((node) => {
|
|
940
|
+
if (ts.isFunctionDeclaration(node) && node.name && node.modifiers?.some((modifier) => modifier.kind === ts.SyntaxKind.ExportKeyword)) {
|
|
941
|
+
const nome = node.name.text;
|
|
942
|
+
const input = node.parameters.flatMap((parametro) => expandirCamposTs(parametro.name.getText(contexto.sourceFile), parametro.type?.getText(contexto.sourceFile), tiposGlobais, entitiesRef, enumsRef, !parametro.questionToken));
|
|
943
|
+
const output = node.type?.getText(contexto.sourceFile) && mapearTipoPrimitivo(node.type.getText(contexto.sourceFile)) === "Vazio"
|
|
944
|
+
? []
|
|
945
|
+
: deduplicarCampos(expandirCamposTs("resultado", node.type?.getText(contexto.sourceFile), tiposGlobais, entitiesRef, enumsRef, false));
|
|
946
|
+
tasks.push({
|
|
947
|
+
nome: nomeTaskBridgeConsumer(nome),
|
|
948
|
+
resumo: `Task consumer importada automaticamente de ${contexto.relacao}#${nome}.`,
|
|
949
|
+
input: deduplicarCampos(input),
|
|
950
|
+
output,
|
|
951
|
+
errors: node.body ? extrairErrosTs(node.body, contexto.sourceFile) : [],
|
|
952
|
+
effects: node.body ? descreverEfeitosPorHeuristica(node.body.getText(contexto.sourceFile)) : [],
|
|
953
|
+
impl: { ts: caminhoImplTs(baseProjeto, path.join(baseProjeto, contexto.relacao), nome) },
|
|
954
|
+
vinculos: deduplicarVinculos([
|
|
955
|
+
{ tipo: "arquivo", valor: normalizarCaminhoImportado(contexto.relacao) },
|
|
956
|
+
{ tipo: "simbolo", valor: caminhoImplTs(baseProjeto, path.join(baseProjeto, contexto.relacao), nome) },
|
|
957
|
+
]),
|
|
958
|
+
origemArquivo: contexto.relacao,
|
|
959
|
+
origemSimbolo: nome,
|
|
960
|
+
});
|
|
961
|
+
}
|
|
962
|
+
if (ts.isClassDeclaration(node) && node.name && node.modifiers?.some((modifier) => modifier.kind === ts.SyntaxKind.ExportKeyword)) {
|
|
963
|
+
const nomeClasse = node.name.text;
|
|
964
|
+
for (const member of node.members) {
|
|
965
|
+
if (!ts.isMethodDeclaration(member) || !member.name || !member.body) {
|
|
966
|
+
continue;
|
|
967
|
+
}
|
|
968
|
+
if (member.modifiers?.some((modifier) => modifier.kind === ts.SyntaxKind.PrivateKeyword || modifier.kind === ts.SyntaxKind.ProtectedKeyword)) {
|
|
969
|
+
continue;
|
|
970
|
+
}
|
|
971
|
+
const nomeMetodo = member.name.getText(contexto.sourceFile);
|
|
972
|
+
if (nomeMetodo === "constructor") {
|
|
973
|
+
continue;
|
|
974
|
+
}
|
|
975
|
+
const input = member.parameters.flatMap((parametro) => expandirCamposTs(parametro.name.getText(contexto.sourceFile), parametro.type?.getText(contexto.sourceFile), tiposGlobais, entitiesRef, enumsRef, !parametro.questionToken));
|
|
976
|
+
const output = member.type?.getText(contexto.sourceFile) && mapearTipoPrimitivo(member.type.getText(contexto.sourceFile)) === "Vazio"
|
|
977
|
+
? []
|
|
978
|
+
: deduplicarCampos(expandirCamposTs("resultado", member.type?.getText(contexto.sourceFile), tiposGlobais, entitiesRef, enumsRef, false));
|
|
979
|
+
const caminhoSimbolo = caminhoImplTs(baseProjeto, path.join(baseProjeto, contexto.relacao), `${nomeClasse}.${nomeMetodo}`);
|
|
980
|
+
tasks.push({
|
|
981
|
+
nome: nomeTaskBridgeConsumer(nomeMetodo),
|
|
982
|
+
resumo: `Task consumer importada automaticamente de ${contexto.relacao}#${nomeClasse}.${nomeMetodo}.`,
|
|
983
|
+
input: deduplicarCampos(input),
|
|
984
|
+
output,
|
|
985
|
+
errors: extrairErrosTs(member.body, contexto.sourceFile),
|
|
986
|
+
effects: descreverEfeitosPorHeuristica(member.body.getText(contexto.sourceFile)),
|
|
987
|
+
impl: { ts: caminhoSimbolo },
|
|
988
|
+
vinculos: deduplicarVinculos([
|
|
989
|
+
{ tipo: "arquivo", valor: normalizarCaminhoImportado(contexto.relacao) },
|
|
990
|
+
{ tipo: "simbolo", valor: caminhoSimbolo },
|
|
991
|
+
]),
|
|
992
|
+
origemArquivo: contexto.relacao,
|
|
993
|
+
origemSimbolo: `${nomeClasse}.${nomeMetodo}`,
|
|
994
|
+
});
|
|
995
|
+
}
|
|
996
|
+
}
|
|
997
|
+
});
|
|
998
|
+
}
|
|
999
|
+
const { entities, enums } = criarEntidadesReferenciadas(tiposGlobais, entitiesRef, enumsRef);
|
|
1000
|
+
return {
|
|
1001
|
+
tasks,
|
|
1002
|
+
entities,
|
|
1003
|
+
enums,
|
|
1004
|
+
};
|
|
1005
|
+
}
|
|
1006
|
+
function montarVinculosSuperficiesConsumer(superficies) {
|
|
1007
|
+
return deduplicarVinculos(superficies.flatMap((superficie) => [
|
|
1008
|
+
{ tipo: "superficie", valor: superficie.caminho },
|
|
1009
|
+
{ tipo: "arquivo", valor: normalizarCaminhoImportado(superficie.arquivo) },
|
|
1010
|
+
]));
|
|
1011
|
+
}
|
|
1012
|
+
async function importarConsumerBase(diretorio, namespaceBase, descricaoFramework, ehBridge, coletarSuperficies) {
|
|
1013
|
+
const escopo = resolverEscopoImportacaoFrontendConsumer(diretorio);
|
|
1014
|
+
const arquivos = await listarArquivosRecursivos(escopo.baseProjeto, [".ts", ".tsx", ".js", ".jsx"]);
|
|
1015
|
+
const arquivosBridge = arquivos.filter((arquivo) => ehBridge(path.relative(escopo.baseProjeto, arquivo)));
|
|
1016
|
+
const contextosBridge = await carregarContextosBridgeConsumer(escopo.baseProjeto, arquivosBridge);
|
|
1017
|
+
const { tasks, entities, enums } = extrairTasksBridgeConsumer(escopo.baseProjeto, contextosBridge);
|
|
1018
|
+
const superficiesImportadas = await coletarSuperficies(escopo.baseProjeto, arquivos);
|
|
1019
|
+
const superficies = montarVinculosSuperficiesConsumer(superficiesImportadas);
|
|
1020
|
+
if (tasks.length === 0 && superficies.length === 0) {
|
|
1021
|
+
return [];
|
|
1022
|
+
}
|
|
1023
|
+
const nomeModulo = namespaceBase.endsWith(".consumer")
|
|
1024
|
+
? namespaceBase
|
|
1025
|
+
: `${namespaceBase}.consumer`;
|
|
1026
|
+
return [{
|
|
1027
|
+
nome: nomeModulo,
|
|
1028
|
+
resumo: `Rascunho Sema importado automaticamente do consumer ${descricaoFramework} em ${escopo.baseProjeto}.`,
|
|
1029
|
+
tasks: deduplicarTarefas(tasks),
|
|
1030
|
+
routes: [],
|
|
1031
|
+
entities,
|
|
1032
|
+
enums,
|
|
1033
|
+
vinculos: superficies,
|
|
1034
|
+
}];
|
|
1035
|
+
}
|
|
1036
|
+
async function coletarSuperficiesNextJsConsumer(baseProjeto, arquivos) {
|
|
1037
|
+
return arquivos
|
|
1038
|
+
.map((arquivo) => inferirCaminhoNextJsConsumer(path.relative(baseProjeto, arquivo)))
|
|
1039
|
+
.filter((item) => Boolean(item));
|
|
1040
|
+
}
|
|
1041
|
+
async function coletarSuperficiesReactViteConsumer(baseProjeto, arquivos) {
|
|
1042
|
+
const superficies = [];
|
|
1043
|
+
for (const arquivo of arquivos) {
|
|
1044
|
+
const relacao = path.relative(baseProjeto, arquivo);
|
|
1045
|
+
const codigo = await readFile(arquivo, "utf8");
|
|
1046
|
+
if (arquivoEhRotasReactViteConsumer(relacao, codigo)) {
|
|
1047
|
+
for (const rota of extrairRotasReactViteConsumer(relacao, codigo)) {
|
|
1048
|
+
superficies.push({
|
|
1049
|
+
caminho: rota.caminho,
|
|
1050
|
+
arquivo: rota.arquivoRotas,
|
|
1051
|
+
tipoArquivo: "router",
|
|
1052
|
+
});
|
|
1053
|
+
if (rota.arquivoComponente) {
|
|
1054
|
+
superficies.push({
|
|
1055
|
+
caminho: rota.caminho,
|
|
1056
|
+
arquivo: rota.arquivoComponente,
|
|
1057
|
+
tipoArquivo: "page",
|
|
1058
|
+
});
|
|
1059
|
+
}
|
|
1060
|
+
}
|
|
1061
|
+
}
|
|
1062
|
+
}
|
|
1063
|
+
for (const arquivo of arquivos) {
|
|
1064
|
+
const superficie = inferirCaminhoReactViteConsumer(path.relative(baseProjeto, arquivo));
|
|
1065
|
+
if (superficie) {
|
|
1066
|
+
superficies.push(superficie);
|
|
1067
|
+
}
|
|
1068
|
+
}
|
|
1069
|
+
return superficies;
|
|
1070
|
+
}
|
|
1071
|
+
async function coletarSuperficiesAngularConsumer(baseProjeto, arquivos) {
|
|
1072
|
+
const superficies = [];
|
|
1073
|
+
const arquivosRotas = arquivos.filter((arquivo) => arquivoEhRotasAngularConsumer(path.relative(baseProjeto, arquivo)));
|
|
1074
|
+
const arquivosRaiz = arquivosRotas.filter((arquivo) => arquivoEhRotasAngularConsumerRaiz(path.relative(baseProjeto, arquivo)));
|
|
1075
|
+
const pontosEntrada = arquivosRaiz.length > 0 ? arquivosRaiz : arquivosRotas;
|
|
1076
|
+
for (const arquivoRotas of pontosEntrada) {
|
|
1077
|
+
const relacao = path.relative(baseProjeto, arquivoRotas);
|
|
1078
|
+
for (const rota of await extrairRotasAngularConsumer(baseProjeto, relacao)) {
|
|
1079
|
+
superficies.push({
|
|
1080
|
+
caminho: rota.caminho,
|
|
1081
|
+
arquivo: rota.arquivoRotas,
|
|
1082
|
+
tipoArquivo: "routes",
|
|
1083
|
+
});
|
|
1084
|
+
if (rota.arquivoComponente) {
|
|
1085
|
+
superficies.push({
|
|
1086
|
+
caminho: rota.caminho,
|
|
1087
|
+
arquivo: rota.arquivoComponente,
|
|
1088
|
+
tipoArquivo: "component",
|
|
1089
|
+
});
|
|
1090
|
+
}
|
|
1091
|
+
}
|
|
1092
|
+
}
|
|
1093
|
+
return superficies;
|
|
1094
|
+
}
|
|
1095
|
+
async function importarNextJsConsumerBase(diretorio, namespaceBase) {
|
|
1096
|
+
return importarConsumerBase(diretorio, namespaceBase, "Next.js", arquivoEhBridgeNextJsConsumer, coletarSuperficiesNextJsConsumer);
|
|
1097
|
+
}
|
|
1098
|
+
async function importarReactViteConsumerBase(diretorio, namespaceBase) {
|
|
1099
|
+
return importarConsumerBase(diretorio, namespaceBase, "React/Vite", arquivoEhBridgeReactViteConsumer, coletarSuperficiesReactViteConsumer);
|
|
1100
|
+
}
|
|
1101
|
+
async function importarAngularConsumerBase(diretorio, namespaceBase) {
|
|
1102
|
+
return importarConsumerBase(diretorio, namespaceBase, "Angular", arquivoEhBridgeAngularConsumer, coletarSuperficiesAngularConsumer);
|
|
1103
|
+
}
|
|
1104
|
+
async function extrairTasksBridgeFlutterConsumer(baseProjeto, arquivosBridge) {
|
|
1105
|
+
const tasks = [];
|
|
1106
|
+
for (const arquivo of arquivosBridge) {
|
|
1107
|
+
const texto = await readFile(arquivo, "utf8");
|
|
1108
|
+
const relacao = path.relative(baseProjeto, arquivo);
|
|
1109
|
+
for (const match of texto.matchAll(/(?:Future<([^\n]+)>|([\w?<>.,\s]+))\s+(\w+)\(([^)]*)\)\s*(?:async\s*)?\{/g)) {
|
|
1110
|
+
const retorno = (match[1] ?? match[2] ?? "").trim();
|
|
1111
|
+
const nome = match[3];
|
|
1112
|
+
if (["build", "toString", "hashCode"].includes(nome)) {
|
|
1113
|
+
continue;
|
|
1114
|
+
}
|
|
1115
|
+
const parametros = match[4];
|
|
1116
|
+
const input = parametros
|
|
1117
|
+
.split(",")
|
|
1118
|
+
.map((item) => item.trim())
|
|
1119
|
+
.filter(Boolean)
|
|
1120
|
+
.map((item) => item.replace(/^(required|final)\s+/g, ""))
|
|
1121
|
+
.map((item) => {
|
|
1122
|
+
const partes = item.split(/\s+/).filter(Boolean);
|
|
1123
|
+
const nomeParametro = partes.at(-1) ?? "arg";
|
|
1124
|
+
const tipoParametro = partes.slice(0, -1).join(" ");
|
|
1125
|
+
return {
|
|
1126
|
+
nome: paraSnakeCase(nomeParametro),
|
|
1127
|
+
tipo: mapearTipoPrimitivo(tipoParametro || "Json"),
|
|
1128
|
+
obrigatorio: !/\?/.test(tipoParametro),
|
|
1129
|
+
};
|
|
1130
|
+
});
|
|
1131
|
+
const caminhoSimbolo = caminhoImplDart(baseProjeto, arquivo, nome);
|
|
1132
|
+
tasks.push({
|
|
1133
|
+
nome: nomeTaskBridgeConsumer(nome),
|
|
1134
|
+
resumo: `Task consumer importada automaticamente de ${relacao}#${nome}.`,
|
|
1135
|
+
input,
|
|
1136
|
+
output: retorno && mapearTipoPrimitivo(retorno) === "Vazio"
|
|
1137
|
+
? []
|
|
1138
|
+
: [{ nome: "resultado", tipo: mapearTipoPrimitivo(retorno || "Json"), obrigatorio: false }],
|
|
1139
|
+
errors: [],
|
|
1140
|
+
effects: descreverEfeitosPorHeuristica(texto),
|
|
1141
|
+
impl: { dart: caminhoSimbolo },
|
|
1142
|
+
vinculos: deduplicarVinculos([
|
|
1143
|
+
{ tipo: "arquivo", valor: normalizarCaminhoImportado(relacao) },
|
|
1144
|
+
{ tipo: "simbolo", valor: caminhoSimbolo },
|
|
1145
|
+
]),
|
|
1146
|
+
origemArquivo: relacao,
|
|
1147
|
+
origemSimbolo: nome,
|
|
1148
|
+
});
|
|
1149
|
+
}
|
|
1150
|
+
}
|
|
1151
|
+
return tasks;
|
|
1152
|
+
}
|
|
1153
|
+
async function coletarSuperficiesFlutterConsumer(baseProjeto, arquivos) {
|
|
1154
|
+
const superficies = [];
|
|
1155
|
+
for (const arquivo of arquivos) {
|
|
1156
|
+
const relacao = path.relative(baseProjeto, arquivo);
|
|
1157
|
+
const codigo = await readFile(arquivo, "utf8");
|
|
1158
|
+
if (arquivoEhRotasFlutterConsumer(relacao, codigo)) {
|
|
1159
|
+
for (const rota of extrairRotasFlutterConsumer(relacao, codigo)) {
|
|
1160
|
+
superficies.push({
|
|
1161
|
+
caminho: rota.caminho,
|
|
1162
|
+
arquivo: rota.arquivoRotas,
|
|
1163
|
+
tipoArquivo: "router",
|
|
1164
|
+
});
|
|
1165
|
+
}
|
|
1166
|
+
}
|
|
1167
|
+
}
|
|
1168
|
+
for (const arquivo of arquivos) {
|
|
1169
|
+
const superficie = inferirCaminhoFlutterConsumer(path.relative(baseProjeto, arquivo));
|
|
1170
|
+
if (superficie) {
|
|
1171
|
+
superficies.push(superficie);
|
|
1172
|
+
}
|
|
1173
|
+
}
|
|
1174
|
+
return superficies;
|
|
1175
|
+
}
|
|
1176
|
+
async function importarFlutterConsumerBase(diretorio, namespaceBase) {
|
|
1177
|
+
const escopo = resolverEscopoImportacaoFrontendConsumer(diretorio);
|
|
1178
|
+
const arquivos = (await listarArquivosRecursivos(escopo.baseProjeto, [".dart"]))
|
|
1179
|
+
.filter((arquivo) => !arquivo.endsWith(".g.dart") && !arquivo.endsWith(".freezed.dart"));
|
|
1180
|
+
const arquivosBridge = arquivos.filter((arquivo) => arquivoEhBridgeFlutterConsumer(path.relative(escopo.baseProjeto, arquivo)));
|
|
1181
|
+
const tasks = await extrairTasksBridgeFlutterConsumer(escopo.baseProjeto, arquivosBridge);
|
|
1182
|
+
const superficiesImportadas = await coletarSuperficiesFlutterConsumer(escopo.baseProjeto, arquivos);
|
|
1183
|
+
const superficies = montarVinculosSuperficiesConsumer(superficiesImportadas);
|
|
1184
|
+
if (tasks.length === 0 && superficies.length === 0) {
|
|
1185
|
+
return [];
|
|
1186
|
+
}
|
|
1187
|
+
const nomeModulo = namespaceBase.endsWith(".consumer")
|
|
1188
|
+
? namespaceBase
|
|
1189
|
+
: `${namespaceBase}.consumer`;
|
|
1190
|
+
return [{
|
|
1191
|
+
nome: nomeModulo,
|
|
1192
|
+
resumo: `Rascunho Sema importado automaticamente do consumer Flutter em ${escopo.baseProjeto}.`,
|
|
1193
|
+
tasks: deduplicarTarefas(tasks),
|
|
1194
|
+
routes: [],
|
|
1195
|
+
entities: [],
|
|
1196
|
+
enums: [],
|
|
1197
|
+
vinculos: superficies,
|
|
1198
|
+
}];
|
|
1199
|
+
}
|
|
1200
|
+
function nomeTaskBridgeConsumer(nome) {
|
|
1201
|
+
return paraSnakeCase(nome.replace(/^sema/, "")) || paraSnakeCase(nome) || "task_consumer";
|
|
1202
|
+
}
|
|
571
1203
|
function extrairChamadaServiceTs(node) {
|
|
572
1204
|
let encontrado;
|
|
573
1205
|
const visitar = (atual) => {
|
|
@@ -614,6 +1246,16 @@ function deduplicarEfeitos(effects) {
|
|
|
614
1246
|
}
|
|
615
1247
|
return [...mapa.values()];
|
|
616
1248
|
}
|
|
1249
|
+
function deduplicarVinculos(vinculos) {
|
|
1250
|
+
const mapa = new Map();
|
|
1251
|
+
for (const vinculo of vinculos) {
|
|
1252
|
+
const chave = `${vinculo.tipo}:${vinculo.valor}`;
|
|
1253
|
+
if (!mapa.has(chave)) {
|
|
1254
|
+
mapa.set(chave, vinculo);
|
|
1255
|
+
}
|
|
1256
|
+
}
|
|
1257
|
+
return [...mapa.values()];
|
|
1258
|
+
}
|
|
617
1259
|
function deduplicarEntidades(entities) {
|
|
618
1260
|
const mapa = new Map();
|
|
619
1261
|
for (const entity of entities) {
|
|
@@ -644,6 +1286,7 @@ function deduplicarTarefas(tasks) {
|
|
|
644
1286
|
existente.output = deduplicarCampos([...existente.output, ...task.output]);
|
|
645
1287
|
existente.errors = deduplicarErros([...existente.errors, ...task.errors]);
|
|
646
1288
|
existente.effects = deduplicarEfeitos([...existente.effects, ...task.effects]);
|
|
1289
|
+
existente.vinculos = deduplicarVinculos([...(existente.vinculos ?? []), ...(task.vinculos ?? [])]);
|
|
647
1290
|
}
|
|
648
1291
|
return [...mapa.values()];
|
|
649
1292
|
}
|
|
@@ -714,6 +1357,7 @@ function renderizarImpl(impl, indentacao = " ") {
|
|
|
714
1357
|
...(impl.ts ? [`${indentacao} ts: ${impl.ts}`] : []),
|
|
715
1358
|
...(impl.py ? [`${indentacao} py: ${impl.py}`] : []),
|
|
716
1359
|
...(impl.dart ? [`${indentacao} dart: ${impl.dart}`] : []),
|
|
1360
|
+
...(impl.lua ? [`${indentacao} lua: ${impl.lua}`] : []),
|
|
717
1361
|
...(impl.cs ? [`${indentacao} cs: ${impl.cs}`] : []),
|
|
718
1362
|
...(impl.java ? [`${indentacao} java: ${impl.java}`] : []),
|
|
719
1363
|
...(impl.go ? [`${indentacao} go: ${impl.go}`] : []),
|
|
@@ -723,6 +1367,26 @@ function renderizarImpl(impl, indentacao = " ") {
|
|
|
723
1367
|
"",
|
|
724
1368
|
];
|
|
725
1369
|
}
|
|
1370
|
+
function renderizarValorVinculo(vinculo) {
|
|
1371
|
+
if (vinculo.tipo === "simbolo") {
|
|
1372
|
+
return vinculo.valor;
|
|
1373
|
+
}
|
|
1374
|
+
if (vinculo.tipo === "arquivo" || vinculo.valor.includes("/") || vinculo.valor.includes("\\") || vinculo.valor.includes("{")) {
|
|
1375
|
+
return `"${escaparTexto(vinculo.valor)}"`;
|
|
1376
|
+
}
|
|
1377
|
+
return vinculo.valor;
|
|
1378
|
+
}
|
|
1379
|
+
function renderizarVinculos(vinculos, indentacao = " ") {
|
|
1380
|
+
if (!vinculos || vinculos.length === 0) {
|
|
1381
|
+
return [];
|
|
1382
|
+
}
|
|
1383
|
+
return [
|
|
1384
|
+
`${indentacao}vinculos {`,
|
|
1385
|
+
...vinculos.map((vinculo) => `${indentacao} ${vinculo.tipo}: ${renderizarValorVinculo(vinculo)}`),
|
|
1386
|
+
`${indentacao}}`,
|
|
1387
|
+
"",
|
|
1388
|
+
];
|
|
1389
|
+
}
|
|
726
1390
|
function renderizarTask(task) {
|
|
727
1391
|
const linhas = [
|
|
728
1392
|
` task ${task.nome} {`,
|
|
@@ -734,6 +1398,7 @@ function renderizarTask(task) {
|
|
|
734
1398
|
...renderizarCampos("output", task.output, " ", true),
|
|
735
1399
|
...renderizarEffects(task.effects, " "),
|
|
736
1400
|
...renderizarImpl(task.impl, " "),
|
|
1401
|
+
...renderizarVinculos(task.vinculos, " "),
|
|
737
1402
|
...renderizarErrors(task.errors, " "),
|
|
738
1403
|
];
|
|
739
1404
|
linhas.push(" guarantees {");
|
|
@@ -791,6 +1456,7 @@ function moduloParaCodigo(modulo) {
|
|
|
791
1456
|
` resumo: "${escaparTexto(modulo.resumo)}"`,
|
|
792
1457
|
" }",
|
|
793
1458
|
"",
|
|
1459
|
+
...renderizarVinculos(modulo.vinculos, " "),
|
|
794
1460
|
...modulo.enums.flatMap(renderizarEnum),
|
|
795
1461
|
...modulo.entities.flatMap(renderizarEntidade),
|
|
796
1462
|
...modulo.tasks.flatMap(renderizarTask),
|
|
@@ -1369,6 +2035,14 @@ function caminhoImplPython(diretorioBase, arquivo, simbolo) {
|
|
|
1369
2035
|
function caminhoImplDart(diretorioBase, arquivo, simbolo) {
|
|
1370
2036
|
return caminhoImplGenerico(diretorioBase, arquivo, simbolo);
|
|
1371
2037
|
}
|
|
2038
|
+
function caminhoImplLua(diretorioBase, arquivo, simbolo) {
|
|
2039
|
+
const caminhoBase = caminhoImplGenerico(diretorioBase, arquivo, "");
|
|
2040
|
+
const ultimoSegmentoArquivo = caminhoBase.split(".").filter(Boolean).at(-1);
|
|
2041
|
+
const simboloNormalizado = ultimoSegmentoArquivo && simbolo.startsWith(`${ultimoSegmentoArquivo}.`)
|
|
2042
|
+
? simbolo.slice(ultimoSegmentoArquivo.length + 1)
|
|
2043
|
+
: simbolo;
|
|
2044
|
+
return [...caminhoBase.split(".").filter(Boolean), simboloNormalizado].filter(Boolean).join(".");
|
|
2045
|
+
}
|
|
1372
2046
|
function dividirParametrosPython(parametros) {
|
|
1373
2047
|
const partes = [];
|
|
1374
2048
|
let atual = "";
|
|
@@ -1672,7 +2346,54 @@ async function importarDartBase(diretorio, namespaceBase) {
|
|
|
1672
2346
|
}
|
|
1673
2347
|
return modulos;
|
|
1674
2348
|
}
|
|
1675
|
-
function
|
|
2349
|
+
async function importarLuaBase(diretorio, namespaceBase) {
|
|
2350
|
+
const arquivos = (await listarArquivosRecursivos(diretorio, [".lua"]))
|
|
2351
|
+
.filter((arquivo) => !/(^|[\\/])(spec|specs|test|tests)([\\/]|$)/i.test(arquivo))
|
|
2352
|
+
.filter((arquivo) => !/[_-](spec|test)\.lua$/i.test(arquivo));
|
|
2353
|
+
const modulos = [];
|
|
2354
|
+
for (const arquivo of arquivos) {
|
|
2355
|
+
const texto = await readFile(arquivo, "utf8");
|
|
2356
|
+
const relacao = path.relative(diretorio, arquivo);
|
|
2357
|
+
const contextoSegmentos = inferirContextoPorArquivo(relacao, { preservarUltimo: true });
|
|
2358
|
+
const nomeModulo = [namespaceBase, ...contextoSegmentos].join(".");
|
|
2359
|
+
const tasks = [];
|
|
2360
|
+
for (const simbolo of extrairSimbolosLua(texto)) {
|
|
2361
|
+
const nomeBase = simbolo.simbolo.split(".").at(-1) ?? simbolo.simbolo;
|
|
2362
|
+
const input = simbolo.parametros.map((parametro) => ({
|
|
2363
|
+
nome: normalizarNomeCampoImportado(parametro.nome),
|
|
2364
|
+
tipo: "Json",
|
|
2365
|
+
obrigatorio: parametro.obrigatorio,
|
|
2366
|
+
}));
|
|
2367
|
+
const output = /\breturn\b/.test(texto)
|
|
2368
|
+
? [{ nome: "resultado", tipo: "Json", obrigatorio: false }]
|
|
2369
|
+
: [];
|
|
2370
|
+
tasks.push({
|
|
2371
|
+
nome: paraSnakeCase(nomeBase),
|
|
2372
|
+
resumo: `Task importada automaticamente de ${relacao}#${simbolo.simbolo}.`,
|
|
2373
|
+
input,
|
|
2374
|
+
output,
|
|
2375
|
+
errors: [],
|
|
2376
|
+
effects: descreverEfeitosPorHeuristica(texto),
|
|
2377
|
+
impl: { lua: caminhoImplLua(diretorio, arquivo, simbolo.simbolo) },
|
|
2378
|
+
origemArquivo: relacao,
|
|
2379
|
+
origemSimbolo: simbolo.simbolo,
|
|
2380
|
+
});
|
|
2381
|
+
}
|
|
2382
|
+
if (tasks.length === 0) {
|
|
2383
|
+
continue;
|
|
2384
|
+
}
|
|
2385
|
+
modulos.push({
|
|
2386
|
+
nome: nomeModulo,
|
|
2387
|
+
resumo: `Rascunho Sema importado automaticamente de ${relacao}.`,
|
|
2388
|
+
tasks: deduplicarTarefas(tasks),
|
|
2389
|
+
routes: [],
|
|
2390
|
+
entities: [],
|
|
2391
|
+
enums: [],
|
|
2392
|
+
});
|
|
2393
|
+
}
|
|
2394
|
+
return modulos;
|
|
2395
|
+
}
|
|
2396
|
+
function criarModuloImportadoSimples(nome, resumo, tasks, routes = [], vinculos = []) {
|
|
1676
2397
|
sincronizarRotasComTasks(routes, tasks);
|
|
1677
2398
|
return {
|
|
1678
2399
|
nome,
|
|
@@ -1681,6 +2402,7 @@ function criarModuloImportadoSimples(nome, resumo, tasks, routes = []) {
|
|
|
1681
2402
|
routes: deduplicarRotas(routes),
|
|
1682
2403
|
entities: [],
|
|
1683
2404
|
enums: [],
|
|
2405
|
+
vinculos: deduplicarVinculos(vinculos),
|
|
1684
2406
|
};
|
|
1685
2407
|
}
|
|
1686
2408
|
function acumularModuloImportado(modulos, modulo) {
|
|
@@ -1693,6 +2415,7 @@ function acumularModuloImportado(modulos, modulo) {
|
|
|
1693
2415
|
existente.routes = deduplicarRotas([...existente.routes, ...modulo.routes]);
|
|
1694
2416
|
existente.entities = deduplicarEntidades([...existente.entities, ...modulo.entities]);
|
|
1695
2417
|
existente.enums = deduplicarEnums([...existente.enums, ...modulo.enums]);
|
|
2418
|
+
existente.vinculos = deduplicarVinculos([...(existente.vinculos ?? []), ...(modulo.vinculos ?? [])]);
|
|
1696
2419
|
}
|
|
1697
2420
|
function selecionarSimbolosPreferidos(simbolos) {
|
|
1698
2421
|
const mapa = new Map();
|
|
@@ -2029,6 +2752,18 @@ export async function importarProjetoLegado(fonte, diretorio, namespaceBase) {
|
|
|
2029
2752
|
else if (fonte === "nextjs") {
|
|
2030
2753
|
modulos = await importarNextJsBase(base, namespace);
|
|
2031
2754
|
}
|
|
2755
|
+
else if (fonte === "nextjs-consumer") {
|
|
2756
|
+
modulos = await importarNextJsConsumerBase(base, namespace);
|
|
2757
|
+
}
|
|
2758
|
+
else if (fonte === "react-vite-consumer") {
|
|
2759
|
+
modulos = await importarReactViteConsumerBase(base, namespace);
|
|
2760
|
+
}
|
|
2761
|
+
else if (fonte === "angular-consumer") {
|
|
2762
|
+
modulos = await importarAngularConsumerBase(base, namespace);
|
|
2763
|
+
}
|
|
2764
|
+
else if (fonte === "flutter-consumer") {
|
|
2765
|
+
modulos = await importarFlutterConsumerBase(base, namespace);
|
|
2766
|
+
}
|
|
2032
2767
|
else if (fonte === "firebase") {
|
|
2033
2768
|
modulos = await importarFirebaseBase(base, namespace);
|
|
2034
2769
|
}
|
|
@@ -2047,6 +2782,9 @@ export async function importarProjetoLegado(fonte, diretorio, namespaceBase) {
|
|
|
2047
2782
|
else if (fonte === "dart") {
|
|
2048
2783
|
modulos = await importarDartBase(base, namespace);
|
|
2049
2784
|
}
|
|
2785
|
+
else if (fonte === "lua") {
|
|
2786
|
+
modulos = await importarLuaBase(base, namespace);
|
|
2787
|
+
}
|
|
2050
2788
|
else if (fonte === "dotnet") {
|
|
2051
2789
|
modulos = await importarDotnetBase(base, namespace);
|
|
2052
2790
|
}
|