@semacode/cli 0.8.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +22 -0
- package/README.md +52 -0
- package/dist/cpp-symbols.d.ts +10 -0
- package/dist/cpp-symbols.js +71 -0
- package/dist/cpp-symbols.js.map +1 -0
- package/dist/dotnet-http.d.ts +23 -0
- package/dist/dotnet-http.js +301 -0
- package/dist/dotnet-http.js.map +1 -0
- package/dist/drift.d.ts +74 -0
- package/dist/drift.js +878 -0
- package/dist/drift.js.map +1 -0
- package/dist/go-http.d.ts +23 -0
- package/dist/go-http.js +90 -0
- package/dist/go-http.js.map +1 -0
- package/dist/importador.d.ts +29 -0
- package/dist/importador.js +2094 -0
- package/dist/importador.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2150 -0
- package/dist/index.js.map +1 -0
- package/dist/java-http.d.ts +23 -0
- package/dist/java-http.js +204 -0
- package/dist/java-http.js.map +1 -0
- package/dist/projeto.d.ts +48 -0
- package/dist/projeto.js +560 -0
- package/dist/projeto.js.map +1 -0
- package/dist/python-http.d.ts +23 -0
- package/dist/python-http.js +200 -0
- package/dist/python-http.js.map +1 -0
- package/dist/rust-http.d.ts +23 -0
- package/dist/rust-http.js +95 -0
- package/dist/rust-http.js.map +1 -0
- package/dist/tipos.d.ts +3 -0
- package/dist/tipos.js +2 -0
- package/dist/tipos.js.map +1 -0
- package/dist/typescript-http.d.ts +35 -0
- package/dist/typescript-http.js +854 -0
- package/dist/typescript-http.js.map +1 -0
- package/logo.png +0 -0
- package/node_modules/@sema/gerador-dart/dist/index.d.ts +3 -0
- package/node_modules/@sema/gerador-dart/dist/index.js +44 -0
- package/node_modules/@sema/gerador-dart/dist/index.js.map +1 -0
- package/node_modules/@sema/gerador-dart/package.json +7 -0
- package/node_modules/@sema/gerador-python/dist/index.d.ts +6 -0
- package/node_modules/@sema/gerador-python/dist/index.js +510 -0
- package/node_modules/@sema/gerador-python/dist/index.js.map +1 -0
- package/node_modules/@sema/gerador-python/package.json +7 -0
- package/node_modules/@sema/gerador-typescript/dist/index.d.ts +6 -0
- package/node_modules/@sema/gerador-typescript/dist/index.js +646 -0
- package/node_modules/@sema/gerador-typescript/dist/index.js.map +1 -0
- package/node_modules/@sema/gerador-typescript/package.json +7 -0
- package/node_modules/@sema/nucleo/dist/ast/tipos.d.ts +103 -0
- package/node_modules/@sema/nucleo/dist/ast/tipos.js +2 -0
- package/node_modules/@sema/nucleo/dist/ast/tipos.js.map +1 -0
- package/node_modules/@sema/nucleo/dist/diagnosticos/index.d.ts +21 -0
- package/node_modules/@sema/nucleo/dist/diagnosticos/index.js +12 -0
- package/node_modules/@sema/nucleo/dist/diagnosticos/index.js.map +1 -0
- package/node_modules/@sema/nucleo/dist/formatador/index.d.ts +9 -0
- package/node_modules/@sema/nucleo/dist/formatador/index.js +289 -0
- package/node_modules/@sema/nucleo/dist/formatador/index.js.map +1 -0
- package/node_modules/@sema/nucleo/dist/index.d.ts +34 -0
- package/node_modules/@sema/nucleo/dist/index.js +95 -0
- package/node_modules/@sema/nucleo/dist/index.js.map +1 -0
- package/node_modules/@sema/nucleo/dist/ir/conversor.d.ts +5 -0
- package/node_modules/@sema/nucleo/dist/ir/conversor.js +241 -0
- package/node_modules/@sema/nucleo/dist/ir/conversor.js.map +1 -0
- package/node_modules/@sema/nucleo/dist/ir/modelos.d.ts +131 -0
- package/node_modules/@sema/nucleo/dist/ir/modelos.js +2 -0
- package/node_modules/@sema/nucleo/dist/ir/modelos.js.map +1 -0
- package/node_modules/@sema/nucleo/dist/lexer/lexer.d.ts +7 -0
- package/node_modules/@sema/nucleo/dist/lexer/lexer.js +122 -0
- package/node_modules/@sema/nucleo/dist/lexer/lexer.js.map +1 -0
- package/node_modules/@sema/nucleo/dist/lexer/tokens.d.ts +8 -0
- package/node_modules/@sema/nucleo/dist/lexer/tokens.js +30 -0
- package/node_modules/@sema/nucleo/dist/lexer/tokens.js.map +1 -0
- package/node_modules/@sema/nucleo/dist/parser/parser.d.ts +9 -0
- package/node_modules/@sema/nucleo/dist/parser/parser.js +423 -0
- package/node_modules/@sema/nucleo/dist/parser/parser.js.map +1 -0
- package/node_modules/@sema/nucleo/dist/semantico/analisador.d.ts +52 -0
- package/node_modules/@sema/nucleo/dist/semantico/analisador.js +837 -0
- package/node_modules/@sema/nucleo/dist/semantico/analisador.js.map +1 -0
- package/node_modules/@sema/nucleo/dist/semantico/estruturas.d.ts +99 -0
- package/node_modules/@sema/nucleo/dist/semantico/estruturas.js +395 -0
- package/node_modules/@sema/nucleo/dist/semantico/estruturas.js.map +1 -0
- package/node_modules/@sema/nucleo/dist/util/arquivos.d.ts +2 -0
- package/node_modules/@sema/nucleo/dist/util/arquivos.js +25 -0
- package/node_modules/@sema/nucleo/dist/util/arquivos.js.map +1 -0
- package/node_modules/@sema/nucleo/package.json +7 -0
- package/node_modules/@sema/padroes/dist/index.d.ts +20 -0
- package/node_modules/@sema/padroes/dist/index.js +79 -0
- package/node_modules/@sema/padroes/dist/index.js.map +1 -0
- package/node_modules/@sema/padroes/package.json +7 -0
- package/package.json +57 -0
package/dist/drift.js
ADDED
|
@@ -0,0 +1,878 @@
|
|
|
1
|
+
import { readdir, readFile } from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import ts from "typescript";
|
|
4
|
+
import { extrairSimbolosCpp } from "./cpp-symbols.js";
|
|
5
|
+
import { extrairRotasDotnet, extrairSimbolosDotnet } from "./dotnet-http.js";
|
|
6
|
+
import { extrairRotasGo, extrairSimbolosGo } from "./go-http.js";
|
|
7
|
+
import { extrairRotasJava, extrairSimbolosJava } from "./java-http.js";
|
|
8
|
+
import { contarIndentacaoPython, extrairRotasFlaskDecoradas, normalizarCaminhoFlask } from "./python-http.js";
|
|
9
|
+
import { extrairRotasRust, extrairSimbolosRust } from "./rust-http.js";
|
|
10
|
+
import { extrairRotasTypeScriptHttp } from "./typescript-http.js";
|
|
11
|
+
const DIRETORIOS_IGNORADOS = new Set([
|
|
12
|
+
".git",
|
|
13
|
+
".hg",
|
|
14
|
+
".svn",
|
|
15
|
+
"node_modules",
|
|
16
|
+
"dist",
|
|
17
|
+
"build",
|
|
18
|
+
".next",
|
|
19
|
+
".nuxt",
|
|
20
|
+
".dart_tool",
|
|
21
|
+
"__pycache__",
|
|
22
|
+
".venv",
|
|
23
|
+
"venv",
|
|
24
|
+
"coverage",
|
|
25
|
+
".tmp",
|
|
26
|
+
"generated",
|
|
27
|
+
]);
|
|
28
|
+
function paraIdentificadorModulo(valor) {
|
|
29
|
+
return valor
|
|
30
|
+
.replace(/([a-z0-9])([A-Z])/g, "$1_$2")
|
|
31
|
+
.replace(/[^A-Za-z0-9_]+/g, "_")
|
|
32
|
+
.replace(/_{2,}/g, "_")
|
|
33
|
+
.replace(/^_+|_+$/g, "")
|
|
34
|
+
.toLowerCase();
|
|
35
|
+
}
|
|
36
|
+
function extrairTextoLiteral(expr) {
|
|
37
|
+
if (!expr) {
|
|
38
|
+
return undefined;
|
|
39
|
+
}
|
|
40
|
+
if (ts.isStringLiteralLike(expr) || ts.isNoSubstitutionTemplateLiteral(expr)) {
|
|
41
|
+
return expr.text;
|
|
42
|
+
}
|
|
43
|
+
if (ts.isNumericLiteral(expr)) {
|
|
44
|
+
return expr.text;
|
|
45
|
+
}
|
|
46
|
+
return undefined;
|
|
47
|
+
}
|
|
48
|
+
function listarDecoradores(node) {
|
|
49
|
+
return ts.canHaveDecorators(node) ? ts.getDecorators(node) ?? [] : [];
|
|
50
|
+
}
|
|
51
|
+
function lerDecorator(node, nomes) {
|
|
52
|
+
for (const decorator of listarDecoradores(node)) {
|
|
53
|
+
const expressao = decorator.expression;
|
|
54
|
+
if (ts.isCallExpression(expressao)) {
|
|
55
|
+
const alvo = expressao.expression;
|
|
56
|
+
if (ts.isIdentifier(alvo) && nomes.includes(alvo.text)) {
|
|
57
|
+
return { nome: alvo.text, argumentos: expressao.arguments };
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
else if (ts.isIdentifier(expressao) && nomes.includes(expressao.text)) {
|
|
61
|
+
return { nome: expressao.text, argumentos: ts.factory.createNodeArray() };
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return undefined;
|
|
65
|
+
}
|
|
66
|
+
function juntarCaminhoHttp(base, sufixo) {
|
|
67
|
+
const partes = [base ?? "", sufixo ?? ""]
|
|
68
|
+
.map((item) => item.trim())
|
|
69
|
+
.filter(Boolean)
|
|
70
|
+
.map((item) => item.replace(/^\/+|\/+$/g, ""));
|
|
71
|
+
const caminho = `/${partes.join("/")}`.replace(/\/+/g, "/");
|
|
72
|
+
return caminho === "//" ? "/" : caminho;
|
|
73
|
+
}
|
|
74
|
+
async function listarArquivosRecursivos(diretorio, extensoes) {
|
|
75
|
+
let entradas;
|
|
76
|
+
try {
|
|
77
|
+
entradas = await readdir(diretorio, { withFileTypes: true, encoding: "utf8" });
|
|
78
|
+
}
|
|
79
|
+
catch {
|
|
80
|
+
return [];
|
|
81
|
+
}
|
|
82
|
+
const encontrados = [];
|
|
83
|
+
for (const entrada of entradas) {
|
|
84
|
+
if (DIRETORIOS_IGNORADOS.has(entrada.name)) {
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
87
|
+
const caminhoAtual = path.join(diretorio, entrada.name);
|
|
88
|
+
if (entrada.isDirectory()) {
|
|
89
|
+
encontrados.push(...await listarArquivosRecursivos(caminhoAtual, extensoes));
|
|
90
|
+
continue;
|
|
91
|
+
}
|
|
92
|
+
if (extensoes.some((extensao) => entrada.name.endsWith(extensao))) {
|
|
93
|
+
encontrados.push(caminhoAtual);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return encontrados.sort((a, b) => a.localeCompare(b, "pt-BR"));
|
|
97
|
+
}
|
|
98
|
+
function caminhosSimbolicos(baseDiretorio, arquivo) {
|
|
99
|
+
const relativo = path.relative(baseDiretorio, arquivo).replace(/\.[^.]+$/, "");
|
|
100
|
+
const semPrefixo = relativo
|
|
101
|
+
.split(path.sep)
|
|
102
|
+
.map((segmento) => paraIdentificadorModulo(segmento))
|
|
103
|
+
.filter(Boolean)
|
|
104
|
+
.join(".");
|
|
105
|
+
const prefixo = paraIdentificadorModulo(path.basename(baseDiretorio));
|
|
106
|
+
const comPrefixo = prefixo ? [prefixo, semPrefixo].filter(Boolean).join(".") : semPrefixo;
|
|
107
|
+
return [...new Set([semPrefixo, comPrefixo].filter(Boolean))];
|
|
108
|
+
}
|
|
109
|
+
function registrarSimboloTypeScript(simbolos, basesSimbolicas, arquivo, nome, nomeClasse) {
|
|
110
|
+
for (const baseSimbolica of basesSimbolicas) {
|
|
111
|
+
const caminho = nomeClasse
|
|
112
|
+
? `${baseSimbolica}.${nomeClasse}.${nome}`
|
|
113
|
+
: `${baseSimbolica}.${nome}`;
|
|
114
|
+
simbolos.set(caminho, {
|
|
115
|
+
origem: "ts",
|
|
116
|
+
caminho,
|
|
117
|
+
arquivo,
|
|
118
|
+
simbolo: nomeClasse ? `${nomeClasse}.${nome}` : nome,
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
function extrairColecoesFirebase(arquivo, codigo) {
|
|
123
|
+
const recursos = new Map();
|
|
124
|
+
const registrar = (nome) => {
|
|
125
|
+
if (!nome) {
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
recursos.set(`${nome}:${arquivo}`, {
|
|
129
|
+
origem: "firebase",
|
|
130
|
+
nome,
|
|
131
|
+
arquivo,
|
|
132
|
+
tipo: "colecao",
|
|
133
|
+
});
|
|
134
|
+
};
|
|
135
|
+
for (const match of codigo.matchAll(/\b(?:export\s+)?const\s+\w*COLLECTIONS?\w*\s*=\s*\{([\s\S]*?)\n\}/g)) {
|
|
136
|
+
const corpo = match[1] ?? "";
|
|
137
|
+
for (const valor of corpo.matchAll(/:\s*["'`]([^"'`]+)["'`]/g)) {
|
|
138
|
+
registrar(valor[1]);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
for (const match of codigo.matchAll(/\b(?:db\.)?collection\s*\(\s*["'`]([^"'`]+)["'`]\s*\)/g)) {
|
|
142
|
+
registrar(match[1]);
|
|
143
|
+
}
|
|
144
|
+
for (const match of codigo.matchAll(/\bdoc\s*\(\s*[^,]+,\s*["'`]([^"'`]+)["'`]/g)) {
|
|
145
|
+
registrar(match[1]);
|
|
146
|
+
}
|
|
147
|
+
return [...recursos.values()];
|
|
148
|
+
}
|
|
149
|
+
async function indexarTypeScript(diretorios) {
|
|
150
|
+
const simbolos = new Map();
|
|
151
|
+
const rotas = [];
|
|
152
|
+
const recursos = new Map();
|
|
153
|
+
for (const diretorio of diretorios) {
|
|
154
|
+
const arquivos = (await listarArquivosRecursivos(diretorio, [".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"]))
|
|
155
|
+
.filter((arquivo) => !arquivo.endsWith(".d.ts")
|
|
156
|
+
&& !arquivo.endsWith(".spec.ts")
|
|
157
|
+
&& !arquivo.endsWith(".test.ts"));
|
|
158
|
+
for (const arquivo of arquivos) {
|
|
159
|
+
const codigo = await readFile(arquivo, "utf8");
|
|
160
|
+
const scriptKind = arquivo.endsWith(".tsx") ? ts.ScriptKind.TSX : ts.ScriptKind.TS;
|
|
161
|
+
const sourceFile = ts.createSourceFile(arquivo, codigo, ts.ScriptTarget.Latest, true, scriptKind);
|
|
162
|
+
const basesSimbolicas = caminhosSimbolicos(diretorio, arquivo);
|
|
163
|
+
const relacao = path.relative(diretorio, arquivo);
|
|
164
|
+
for (const recurso of extrairColecoesFirebase(arquivo, codigo)) {
|
|
165
|
+
recursos.set(`${recurso.nome}:${recurso.arquivo}:${recurso.tipo}`, recurso);
|
|
166
|
+
}
|
|
167
|
+
for (const rota of extrairRotasTypeScriptHttp(sourceFile, relacao)) {
|
|
168
|
+
rotas.push({
|
|
169
|
+
origem: rota.origem,
|
|
170
|
+
metodo: rota.metodo,
|
|
171
|
+
caminho: rota.caminho,
|
|
172
|
+
arquivo,
|
|
173
|
+
simbolo: rota.simbolo,
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
for (const node of sourceFile.statements) {
|
|
177
|
+
if (ts.isFunctionDeclaration(node) && node.name && node.modifiers?.some((m) => m.kind === ts.SyntaxKind.ExportKeyword)) {
|
|
178
|
+
registrarSimboloTypeScript(simbolos, basesSimbolicas, arquivo, node.name.text);
|
|
179
|
+
}
|
|
180
|
+
if (ts.isVariableStatement(node) && node.modifiers?.some((m) => m.kind === ts.SyntaxKind.ExportKeyword)) {
|
|
181
|
+
for (const declaracao of node.declarationList.declarations) {
|
|
182
|
+
if (!ts.isIdentifier(declaracao.name) || !declaracao.initializer) {
|
|
183
|
+
continue;
|
|
184
|
+
}
|
|
185
|
+
if (ts.isArrowFunction(declaracao.initializer) || ts.isFunctionExpression(declaracao.initializer)) {
|
|
186
|
+
registrarSimboloTypeScript(simbolos, basesSimbolicas, arquivo, declaracao.name.text);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
if (!ts.isClassDeclaration(node) || !node.name) {
|
|
191
|
+
continue;
|
|
192
|
+
}
|
|
193
|
+
const controllerDecorator = lerDecorator(node, ["Controller"]);
|
|
194
|
+
const basePath = extrairTextoLiteral(controllerDecorator?.argumentos[0]);
|
|
195
|
+
for (const member of node.members) {
|
|
196
|
+
if (!ts.isMethodDeclaration(member) || !member.name) {
|
|
197
|
+
continue;
|
|
198
|
+
}
|
|
199
|
+
if (member.modifiers?.some((m) => m.kind === ts.SyntaxKind.PrivateKeyword || m.kind === ts.SyntaxKind.ProtectedKeyword)) {
|
|
200
|
+
continue;
|
|
201
|
+
}
|
|
202
|
+
const nomeMetodo = member.name.getText(sourceFile);
|
|
203
|
+
if (nomeMetodo === "constructor") {
|
|
204
|
+
continue;
|
|
205
|
+
}
|
|
206
|
+
registrarSimboloTypeScript(simbolos, basesSimbolicas, arquivo, nomeMetodo, node.name.text);
|
|
207
|
+
for (const baseSimbolica of basesSimbolicas) {
|
|
208
|
+
const caminhoMetodoDireto = `${baseSimbolica}.${nomeMetodo}`;
|
|
209
|
+
if (!simbolos.has(caminhoMetodoDireto)) {
|
|
210
|
+
simbolos.set(caminhoMetodoDireto, { origem: "ts", caminho: caminhoMetodoDireto, arquivo, simbolo: nomeMetodo });
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
if (controllerDecorator) {
|
|
214
|
+
const httpDecorator = lerDecorator(member, ["Get", "Post", "Put", "Patch", "Delete"]);
|
|
215
|
+
if (httpDecorator) {
|
|
216
|
+
rotas.push({
|
|
217
|
+
origem: "nestjs",
|
|
218
|
+
metodo: httpDecorator.nome.toUpperCase(),
|
|
219
|
+
caminho: juntarCaminhoHttp(basePath, extrairTextoLiteral(httpDecorator.argumentos[0])),
|
|
220
|
+
arquivo,
|
|
221
|
+
simbolo: `${node.name.text}.${nomeMetodo}`,
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
return { simbolos: [...simbolos.values()], rotas, recursos: [...recursos.values()] };
|
|
230
|
+
}
|
|
231
|
+
function registrarSimboloPython(simbolos, basesSimbolicas, arquivo, nome, nomeClasse) {
|
|
232
|
+
for (const baseSimbolica of basesSimbolicas) {
|
|
233
|
+
const caminho = nomeClasse
|
|
234
|
+
? `${baseSimbolica}.${nomeClasse}.${nome}`
|
|
235
|
+
: `${baseSimbolica}.${nome}`;
|
|
236
|
+
simbolos.set(caminho, {
|
|
237
|
+
origem: "py",
|
|
238
|
+
caminho,
|
|
239
|
+
arquivo,
|
|
240
|
+
simbolo: nomeClasse ? `${nomeClasse}.${nome}` : nome,
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
function registrarRotasPython(rotas, decoratorsPendentes, prefixo, arquivo, nomeFuncao) {
|
|
245
|
+
for (const decorator of decoratorsPendentes) {
|
|
246
|
+
const match = decorator.match(/^@(router|app)\.(get|post|put|patch|delete)\((.*)\)\s*$/);
|
|
247
|
+
if (!match) {
|
|
248
|
+
continue;
|
|
249
|
+
}
|
|
250
|
+
const metodo = match[2].toUpperCase();
|
|
251
|
+
const sufixo = match[3]?.match(/["']([^"']+)["']/)?.[1];
|
|
252
|
+
rotas.push({
|
|
253
|
+
origem: "fastapi",
|
|
254
|
+
metodo,
|
|
255
|
+
caminho: juntarCaminhoHttp(prefixo, sufixo),
|
|
256
|
+
arquivo,
|
|
257
|
+
simbolo: nomeFuncao,
|
|
258
|
+
});
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
async function indexarPython(diretorios) {
|
|
262
|
+
const simbolos = new Map();
|
|
263
|
+
const rotas = [];
|
|
264
|
+
for (const diretorio of diretorios) {
|
|
265
|
+
const arquivos = (await listarArquivosRecursivos(diretorio, [".py"]))
|
|
266
|
+
.filter((arquivo) => !arquivo.endsWith("__init__.py") && !/tests?[\\/]/i.test(arquivo));
|
|
267
|
+
for (const arquivo of arquivos) {
|
|
268
|
+
const texto = await readFile(arquivo, "utf8");
|
|
269
|
+
const basesSimbolicas = caminhosSimbolicos(diretorio, arquivo);
|
|
270
|
+
const prefixo = texto.match(/APIRouter\s*\(\s*prefix\s*=\s*["']([^"']+)["']/)?.[1];
|
|
271
|
+
for (const rota of extrairRotasFlaskDecoradas(texto)) {
|
|
272
|
+
rotas.push({
|
|
273
|
+
origem: "flask",
|
|
274
|
+
metodo: rota.metodo,
|
|
275
|
+
caminho: rota.caminho,
|
|
276
|
+
arquivo,
|
|
277
|
+
simbolo: rota.nomeFuncao,
|
|
278
|
+
});
|
|
279
|
+
}
|
|
280
|
+
const blocos = [];
|
|
281
|
+
let decoratorsPendentes = [];
|
|
282
|
+
for (const linha of texto.split(/\r?\n/)) {
|
|
283
|
+
const trim = linha.trim();
|
|
284
|
+
if (trim === "" || trim.startsWith("#")) {
|
|
285
|
+
decoratorsPendentes = [];
|
|
286
|
+
continue;
|
|
287
|
+
}
|
|
288
|
+
const indentacao = contarIndentacaoPython(linha);
|
|
289
|
+
while (blocos.length > 0 && indentacao <= blocos[blocos.length - 1].indentacao) {
|
|
290
|
+
blocos.pop();
|
|
291
|
+
}
|
|
292
|
+
if (trim.startsWith("@")) {
|
|
293
|
+
decoratorsPendentes.push(trim);
|
|
294
|
+
continue;
|
|
295
|
+
}
|
|
296
|
+
const classe = trim.match(/^class\s+([A-Za-z_]\w*)(?:\([^)]*\))?:\s*(?:#.*)?$/);
|
|
297
|
+
if (classe) {
|
|
298
|
+
blocos.push({ tipo: "class", nome: classe[1], indentacao });
|
|
299
|
+
decoratorsPendentes = [];
|
|
300
|
+
continue;
|
|
301
|
+
}
|
|
302
|
+
const definicao = trim.match(/^(?:async\s+def|def)\s+([A-Za-z_]\w*)\s*\(/);
|
|
303
|
+
if (definicao) {
|
|
304
|
+
const nomeFuncao = definicao[1];
|
|
305
|
+
const existeDefPai = blocos.some((bloco) => bloco.tipo === "def");
|
|
306
|
+
const classeAtual = [...blocos].reverse().find((bloco) => bloco.tipo === "class");
|
|
307
|
+
if (!existeDefPai && classeAtual) {
|
|
308
|
+
registrarSimboloPython(simbolos, basesSimbolicas, arquivo, nomeFuncao, classeAtual.nome);
|
|
309
|
+
}
|
|
310
|
+
else if (!existeDefPai) {
|
|
311
|
+
registrarSimboloPython(simbolos, basesSimbolicas, arquivo, nomeFuncao);
|
|
312
|
+
registrarRotasPython(rotas, decoratorsPendentes, prefixo, arquivo, nomeFuncao);
|
|
313
|
+
}
|
|
314
|
+
blocos.push({ tipo: "def", nome: nomeFuncao, indentacao });
|
|
315
|
+
decoratorsPendentes = [];
|
|
316
|
+
continue;
|
|
317
|
+
}
|
|
318
|
+
decoratorsPendentes = [];
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
return { simbolos: [...simbolos.values()], rotas };
|
|
323
|
+
}
|
|
324
|
+
async function indexarDart(diretorios) {
|
|
325
|
+
const simbolos = new Map();
|
|
326
|
+
for (const diretorio of diretorios) {
|
|
327
|
+
const arquivos = (await listarArquivosRecursivos(diretorio, [".dart"]))
|
|
328
|
+
.filter((arquivo) => !arquivo.endsWith(".g.dart") && !arquivo.endsWith(".freezed.dart"));
|
|
329
|
+
for (const arquivo of arquivos) {
|
|
330
|
+
const texto = await readFile(arquivo, "utf8");
|
|
331
|
+
const basesSimbolicas = caminhosSimbolicos(diretorio, arquivo);
|
|
332
|
+
for (const match of texto.matchAll(/(?:Future<[^\n]+>|[\w?<>.,\s]+)\s+(\w+)\(([^)]*)\)\s*(?:async\s*)?\{/g)) {
|
|
333
|
+
const nome = match[1];
|
|
334
|
+
if (["build", "toString"].includes(nome)) {
|
|
335
|
+
continue;
|
|
336
|
+
}
|
|
337
|
+
for (const baseSimbolica of basesSimbolicas) {
|
|
338
|
+
const caminho = `${baseSimbolica}.${nome}`;
|
|
339
|
+
simbolos.set(caminho, { origem: "dart", caminho, arquivo, simbolo: nome });
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
return [...simbolos.values()];
|
|
345
|
+
}
|
|
346
|
+
function registrarSimboloGenerico(simbolos, origem, basesSimbolicas, arquivo, simbolo) {
|
|
347
|
+
for (const baseSimbolica of basesSimbolicas) {
|
|
348
|
+
const caminho = `${baseSimbolica}.${simbolo}`;
|
|
349
|
+
simbolos.set(caminho, {
|
|
350
|
+
origem,
|
|
351
|
+
caminho,
|
|
352
|
+
arquivo,
|
|
353
|
+
simbolo,
|
|
354
|
+
});
|
|
355
|
+
const ultimo = simbolo.split(".").at(-1);
|
|
356
|
+
if (ultimo) {
|
|
357
|
+
const caminhoDireto = `${baseSimbolica}.${ultimo}`;
|
|
358
|
+
if (!simbolos.has(caminhoDireto)) {
|
|
359
|
+
simbolos.set(caminhoDireto, {
|
|
360
|
+
origem,
|
|
361
|
+
caminho: caminhoDireto,
|
|
362
|
+
arquivo,
|
|
363
|
+
simbolo: ultimo,
|
|
364
|
+
});
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
async function indexarDotnet(diretorios) {
|
|
370
|
+
const simbolos = new Map();
|
|
371
|
+
const rotas = [];
|
|
372
|
+
for (const diretorio of diretorios) {
|
|
373
|
+
const arquivos = (await listarArquivosRecursivos(diretorio, [".cs"]))
|
|
374
|
+
.filter((arquivo) => !/(^|[\\/])(bin|obj|Test[s]?)([\\/]|$)/i.test(arquivo));
|
|
375
|
+
for (const arquivo of arquivos) {
|
|
376
|
+
const codigo = await readFile(arquivo, "utf8");
|
|
377
|
+
const basesSimbolicas = caminhosSimbolicos(diretorio, arquivo);
|
|
378
|
+
for (const simbolo of extrairSimbolosDotnet(codigo)) {
|
|
379
|
+
registrarSimboloGenerico(simbolos, "cs", basesSimbolicas, arquivo, simbolo.simbolo);
|
|
380
|
+
}
|
|
381
|
+
for (const rota of extrairRotasDotnet(codigo)) {
|
|
382
|
+
rotas.push({
|
|
383
|
+
origem: "dotnet",
|
|
384
|
+
metodo: rota.metodo,
|
|
385
|
+
caminho: rota.caminho,
|
|
386
|
+
arquivo,
|
|
387
|
+
simbolo: rota.simbolo,
|
|
388
|
+
});
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
return { simbolos: [...simbolos.values()], rotas };
|
|
393
|
+
}
|
|
394
|
+
async function indexarJava(diretorios) {
|
|
395
|
+
const simbolos = new Map();
|
|
396
|
+
const rotas = [];
|
|
397
|
+
for (const diretorio of diretorios) {
|
|
398
|
+
const arquivos = (await listarArquivosRecursivos(diretorio, [".java"]))
|
|
399
|
+
.filter((arquivo) => !/(^|[\\/])(target|build|out|Test[s]?)([\\/]|$)/i.test(arquivo));
|
|
400
|
+
for (const arquivo of arquivos) {
|
|
401
|
+
const codigo = await readFile(arquivo, "utf8");
|
|
402
|
+
const basesSimbolicas = caminhosSimbolicos(diretorio, arquivo);
|
|
403
|
+
for (const simbolo of extrairSimbolosJava(codigo)) {
|
|
404
|
+
registrarSimboloGenerico(simbolos, "java", basesSimbolicas, arquivo, simbolo.simbolo);
|
|
405
|
+
}
|
|
406
|
+
for (const rota of extrairRotasJava(codigo)) {
|
|
407
|
+
rotas.push({
|
|
408
|
+
origem: "java",
|
|
409
|
+
metodo: rota.metodo,
|
|
410
|
+
caminho: rota.caminho,
|
|
411
|
+
arquivo,
|
|
412
|
+
simbolo: rota.simbolo,
|
|
413
|
+
});
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
return { simbolos: [...simbolos.values()], rotas };
|
|
418
|
+
}
|
|
419
|
+
async function indexarGo(diretorios) {
|
|
420
|
+
const simbolos = new Map();
|
|
421
|
+
const rotas = [];
|
|
422
|
+
for (const diretorio of diretorios) {
|
|
423
|
+
const arquivos = await listarArquivosRecursivos(diretorio, [".go"]);
|
|
424
|
+
for (const arquivo of arquivos) {
|
|
425
|
+
const codigo = await readFile(arquivo, "utf8");
|
|
426
|
+
const basesSimbolicas = caminhosSimbolicos(diretorio, arquivo);
|
|
427
|
+
for (const simbolo of extrairSimbolosGo(codigo)) {
|
|
428
|
+
registrarSimboloGenerico(simbolos, "go", basesSimbolicas, arquivo, simbolo.simbolo);
|
|
429
|
+
}
|
|
430
|
+
for (const rota of extrairRotasGo(codigo)) {
|
|
431
|
+
rotas.push({
|
|
432
|
+
origem: "go",
|
|
433
|
+
metodo: rota.metodo,
|
|
434
|
+
caminho: rota.caminho,
|
|
435
|
+
arquivo,
|
|
436
|
+
simbolo: rota.simbolo,
|
|
437
|
+
});
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
return { simbolos: [...simbolos.values()], rotas };
|
|
442
|
+
}
|
|
443
|
+
async function indexarRust(diretorios) {
|
|
444
|
+
const simbolos = new Map();
|
|
445
|
+
const rotas = [];
|
|
446
|
+
for (const diretorio of diretorios) {
|
|
447
|
+
const arquivos = await listarArquivosRecursivos(diretorio, [".rs"]);
|
|
448
|
+
for (const arquivo of arquivos) {
|
|
449
|
+
const codigo = await readFile(arquivo, "utf8");
|
|
450
|
+
const basesSimbolicas = caminhosSimbolicos(diretorio, arquivo);
|
|
451
|
+
for (const simbolo of extrairSimbolosRust(codigo)) {
|
|
452
|
+
registrarSimboloGenerico(simbolos, "rust", basesSimbolicas, arquivo, simbolo.simbolo);
|
|
453
|
+
}
|
|
454
|
+
for (const rota of extrairRotasRust(codigo)) {
|
|
455
|
+
rotas.push({
|
|
456
|
+
origem: "rust",
|
|
457
|
+
metodo: rota.metodo,
|
|
458
|
+
caminho: rota.caminho,
|
|
459
|
+
arquivo,
|
|
460
|
+
simbolo: rota.simbolo,
|
|
461
|
+
});
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
return { simbolos: [...simbolos.values()], rotas };
|
|
466
|
+
}
|
|
467
|
+
async function indexarCpp(diretorios) {
|
|
468
|
+
const simbolos = new Map();
|
|
469
|
+
for (const diretorio of diretorios) {
|
|
470
|
+
const arquivos = (await listarArquivosRecursivos(diretorio, [".cpp", ".cc", ".cxx", ".hpp", ".h"]))
|
|
471
|
+
.filter((arquivo) => !/(^|[\\/])(windows|linux|macos|runner|flutter|ephemeral|build|vendor)([\\/]|$)/i.test(arquivo));
|
|
472
|
+
for (const arquivo of arquivos) {
|
|
473
|
+
const codigo = await readFile(arquivo, "utf8");
|
|
474
|
+
const basesSimbolicas = caminhosSimbolicos(diretorio, arquivo);
|
|
475
|
+
for (const simbolo of extrairSimbolosCpp(codigo)) {
|
|
476
|
+
registrarSimboloGenerico(simbolos, "cpp", basesSimbolicas, arquivo, simbolo.simbolo);
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
return [...simbolos.values()];
|
|
481
|
+
}
|
|
482
|
+
function normalizarCaminhoRota(caminho) {
|
|
483
|
+
if (!caminho) {
|
|
484
|
+
return "/";
|
|
485
|
+
}
|
|
486
|
+
const limpo = normalizarCaminhoFlask(caminho.trim());
|
|
487
|
+
const comBarra = limpo.startsWith("/") ? limpo : `/${limpo}`;
|
|
488
|
+
const normalizado = comBarra.replace(/\/+/g, "/");
|
|
489
|
+
return normalizado.endsWith("/") && normalizado !== "/" ? normalizado.slice(0, -1) : normalizado;
|
|
490
|
+
}
|
|
491
|
+
function extrairFontesHttpTypeScript(fontesLegado) {
|
|
492
|
+
return fontesLegado.filter((fonte) => fonte === "nestjs" || fonte === "nextjs" || fonte === "firebase");
|
|
493
|
+
}
|
|
494
|
+
function extrairFontesHttpBackend(fontesLegado) {
|
|
495
|
+
return fontesLegado.filter((fonte) => fonte === "dotnet" || fonte === "java" || fonte === "go" || fonte === "rust");
|
|
496
|
+
}
|
|
497
|
+
function ultimoSegmentoSimbolico(caminho) {
|
|
498
|
+
const partes = caminho.split(".").filter(Boolean);
|
|
499
|
+
return paraIdentificadorModulo(partes[partes.length - 1] ?? caminho);
|
|
500
|
+
}
|
|
501
|
+
function pontuarCandidatoDeclarado(candidato, origem, caminhoDeclarado) {
|
|
502
|
+
if (candidato.origem !== origem) {
|
|
503
|
+
return undefined;
|
|
504
|
+
}
|
|
505
|
+
const caminhoNormalizado = paraIdentificadorModulo(caminhoDeclarado.replace(/\./g, "_"));
|
|
506
|
+
const candidatoNormalizado = paraIdentificadorModulo(candidato.caminho.replace(/\./g, "_"));
|
|
507
|
+
const ultimoDeclarado = ultimoSegmentoSimbolico(caminhoDeclarado);
|
|
508
|
+
const ultimoCandidato = ultimoSegmentoSimbolico(candidato.caminho);
|
|
509
|
+
const prefixoDeclarado = caminhoDeclarado.split(".").slice(0, -1).join(".");
|
|
510
|
+
const prefixoCandidato = candidato.caminho.split(".").slice(0, -1).join(".");
|
|
511
|
+
if (candidato.caminho === caminhoDeclarado) {
|
|
512
|
+
return {
|
|
513
|
+
origem: candidato.origem,
|
|
514
|
+
caminho: candidato.caminho,
|
|
515
|
+
arquivo: candidato.arquivo,
|
|
516
|
+
simbolo: candidato.simbolo,
|
|
517
|
+
confianca: "alta",
|
|
518
|
+
motivo: "Caminho simbolico bate exatamente com o declarado.",
|
|
519
|
+
};
|
|
520
|
+
}
|
|
521
|
+
if (ultimoDeclarado && ultimoDeclarado === ultimoCandidato) {
|
|
522
|
+
return {
|
|
523
|
+
origem: candidato.origem,
|
|
524
|
+
caminho: candidato.caminho,
|
|
525
|
+
arquivo: candidato.arquivo,
|
|
526
|
+
simbolo: candidato.simbolo,
|
|
527
|
+
confianca: "alta",
|
|
528
|
+
motivo: "Ultimo simbolo bate com a implementacao declarada.",
|
|
529
|
+
};
|
|
530
|
+
}
|
|
531
|
+
if (ultimoDeclarado && (candidatoNormalizado.includes(ultimoDeclarado) || caminhoNormalizado.includes(ultimoCandidato))) {
|
|
532
|
+
return {
|
|
533
|
+
origem: candidato.origem,
|
|
534
|
+
caminho: candidato.caminho,
|
|
535
|
+
arquivo: candidato.arquivo,
|
|
536
|
+
simbolo: candidato.simbolo,
|
|
537
|
+
confianca: "media",
|
|
538
|
+
motivo: "Trecho relevante do caminho simbolico parece compativel com o declarado.",
|
|
539
|
+
};
|
|
540
|
+
}
|
|
541
|
+
if (prefixoDeclarado && prefixoDeclarado === prefixoCandidato) {
|
|
542
|
+
return {
|
|
543
|
+
origem: candidato.origem,
|
|
544
|
+
caminho: candidato.caminho,
|
|
545
|
+
arquivo: candidato.arquivo,
|
|
546
|
+
simbolo: candidato.simbolo,
|
|
547
|
+
confianca: "media",
|
|
548
|
+
motivo: "Prefixo do caminho simbolico bate com a implementacao declarada; o simbolo final pode ter mudado.",
|
|
549
|
+
};
|
|
550
|
+
}
|
|
551
|
+
return undefined;
|
|
552
|
+
}
|
|
553
|
+
function pontuarCandidatoPorTask(candidato, task) {
|
|
554
|
+
const taskNormalizada = paraIdentificadorModulo(task);
|
|
555
|
+
const simboloNormalizado = paraIdentificadorModulo(candidato.simbolo.replace(/\./g, "_"));
|
|
556
|
+
const caminhoNormalizado = paraIdentificadorModulo(candidato.caminho.replace(/\./g, "_"));
|
|
557
|
+
if (!taskNormalizada) {
|
|
558
|
+
return undefined;
|
|
559
|
+
}
|
|
560
|
+
if (simboloNormalizado === taskNormalizada || ultimoSegmentoSimbolico(candidato.caminho) === taskNormalizada) {
|
|
561
|
+
return {
|
|
562
|
+
origem: candidato.origem,
|
|
563
|
+
caminho: candidato.caminho,
|
|
564
|
+
arquivo: candidato.arquivo,
|
|
565
|
+
simbolo: candidato.simbolo,
|
|
566
|
+
confianca: "alta",
|
|
567
|
+
motivo: "Nome da task bate com o simbolo encontrado no codigo vivo.",
|
|
568
|
+
};
|
|
569
|
+
}
|
|
570
|
+
if (simboloNormalizado.includes(taskNormalizada) || taskNormalizada.includes(simboloNormalizado) || caminhoNormalizado.includes(taskNormalizada)) {
|
|
571
|
+
return {
|
|
572
|
+
origem: candidato.origem,
|
|
573
|
+
caminho: candidato.caminho,
|
|
574
|
+
arquivo: candidato.arquivo,
|
|
575
|
+
simbolo: candidato.simbolo,
|
|
576
|
+
confianca: "media",
|
|
577
|
+
motivo: "Nome da task parece compativel com o simbolo encontrado no codigo vivo.",
|
|
578
|
+
};
|
|
579
|
+
}
|
|
580
|
+
return undefined;
|
|
581
|
+
}
|
|
582
|
+
function deduplicarCandidatos(candidatos) {
|
|
583
|
+
const mapa = new Map();
|
|
584
|
+
for (const candidato of candidatos) {
|
|
585
|
+
const chave = `${candidato.origem}:${candidato.caminho}:${candidato.arquivo}:${candidato.simbolo}`;
|
|
586
|
+
const anterior = mapa.get(chave);
|
|
587
|
+
if (!anterior || (anterior.confianca === "media" && candidato.confianca === "alta")) {
|
|
588
|
+
mapa.set(chave, candidato);
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
return [...mapa.values()];
|
|
592
|
+
}
|
|
593
|
+
function ordenarCandidatos(candidatos) {
|
|
594
|
+
return [...candidatos].sort((a, b) => {
|
|
595
|
+
if (a.confianca !== b.confianca) {
|
|
596
|
+
return a.confianca === "alta" ? -1 : 1;
|
|
597
|
+
}
|
|
598
|
+
return a.caminho.localeCompare(b.caminho, "pt-BR");
|
|
599
|
+
});
|
|
600
|
+
}
|
|
601
|
+
function sugerirCandidatosParaImpl(simbolos, origem, caminhoDeclarado) {
|
|
602
|
+
return ordenarCandidatos(deduplicarCandidatos(simbolos
|
|
603
|
+
.map((candidato) => pontuarCandidatoDeclarado(candidato, origem, caminhoDeclarado))
|
|
604
|
+
.filter((item) => Boolean(item)))).slice(0, 5);
|
|
605
|
+
}
|
|
606
|
+
function sugerirCandidatosParaTaskSemImpl(simbolos, nomeTask) {
|
|
607
|
+
return ordenarCandidatos(deduplicarCandidatos(simbolos
|
|
608
|
+
.map((candidato) => pontuarCandidatoPorTask(candidato, nomeTask))
|
|
609
|
+
.filter((item) => Boolean(item)))).slice(0, 5);
|
|
610
|
+
}
|
|
611
|
+
function escolherRotasEsperadas(task, fontesLegado) {
|
|
612
|
+
const fontesTs = extrairFontesHttpTypeScript(fontesLegado);
|
|
613
|
+
const fontesBackend = extrairFontesHttpBackend(fontesLegado);
|
|
614
|
+
const implTs = task.implementacoesExternas.find((impl) => impl.origem === "ts");
|
|
615
|
+
if (implTs) {
|
|
616
|
+
const esperadas = new Set();
|
|
617
|
+
if (/\.route\.(GET|POST|PUT|PATCH|DELETE|HEAD|OPTIONS)$/i.test(implTs.caminho) || /\.route\./i.test(implTs.caminho)) {
|
|
618
|
+
esperadas.add("nextjs");
|
|
619
|
+
}
|
|
620
|
+
if (/\bcontroller\b/i.test(implTs.caminho) && fontesTs.includes("nestjs")) {
|
|
621
|
+
esperadas.add("nestjs");
|
|
622
|
+
}
|
|
623
|
+
if (fontesTs.includes("firebase") && /(apps\.worker|worker|sema_contract_bridge|health)/i.test(implTs.caminho)) {
|
|
624
|
+
esperadas.add("firebase");
|
|
625
|
+
}
|
|
626
|
+
if (esperadas.size > 0) {
|
|
627
|
+
return [...esperadas];
|
|
628
|
+
}
|
|
629
|
+
if (fontesTs.length > 0) {
|
|
630
|
+
return fontesTs;
|
|
631
|
+
}
|
|
632
|
+
return ["nestjs", "nextjs", "firebase"];
|
|
633
|
+
}
|
|
634
|
+
if (task.implementacoesExternas.some((impl) => impl.origem === "py")) {
|
|
635
|
+
const fontesPython = fontesLegado.filter((fonte) => fonte === "fastapi" || fonte === "flask");
|
|
636
|
+
if (fontesPython.length > 0) {
|
|
637
|
+
return fontesPython;
|
|
638
|
+
}
|
|
639
|
+
return ["fastapi", "flask"];
|
|
640
|
+
}
|
|
641
|
+
const implCs = task.implementacoesExternas.find((impl) => impl.origem === "cs");
|
|
642
|
+
if (implCs) {
|
|
643
|
+
return fontesBackend.includes("dotnet") ? ["dotnet"] : ["dotnet"];
|
|
644
|
+
}
|
|
645
|
+
const implJava = task.implementacoesExternas.find((impl) => impl.origem === "java");
|
|
646
|
+
if (implJava) {
|
|
647
|
+
return fontesBackend.includes("java") ? ["java"] : ["java"];
|
|
648
|
+
}
|
|
649
|
+
const implGo = task.implementacoesExternas.find((impl) => impl.origem === "go");
|
|
650
|
+
if (implGo) {
|
|
651
|
+
return fontesBackend.includes("go") ? ["go"] : ["go"];
|
|
652
|
+
}
|
|
653
|
+
const implRust = task.implementacoesExternas.find((impl) => impl.origem === "rust");
|
|
654
|
+
if (implRust) {
|
|
655
|
+
return fontesBackend.includes("rust") ? ["rust"] : ["rust"];
|
|
656
|
+
}
|
|
657
|
+
if (fontesTs.length > 0) {
|
|
658
|
+
return fontesTs;
|
|
659
|
+
}
|
|
660
|
+
const fontesPython = fontesLegado.filter((fonte) => fonte === "fastapi" || fonte === "flask");
|
|
661
|
+
if (fontesPython.length > 0) {
|
|
662
|
+
return fontesPython;
|
|
663
|
+
}
|
|
664
|
+
if (fontesBackend.length > 0) {
|
|
665
|
+
return fontesBackend;
|
|
666
|
+
}
|
|
667
|
+
return [];
|
|
668
|
+
}
|
|
669
|
+
function taskEhBridgeFirebase(task) {
|
|
670
|
+
return task.implementacoesExternas.some((impl) => impl.origem === "ts" && /sema_contract_bridge|collections?|apps\.worker/i.test(impl.caminho));
|
|
671
|
+
}
|
|
672
|
+
function extrairRecursosEsperados(task) {
|
|
673
|
+
if (!taskEhBridgeFirebase(task)) {
|
|
674
|
+
return [];
|
|
675
|
+
}
|
|
676
|
+
return task.efeitosEstruturados
|
|
677
|
+
.filter((efeito) => efeito.categoria === "persistencia" && Boolean(efeito.alvo))
|
|
678
|
+
.map((efeito) => ({
|
|
679
|
+
categoria: "persistencia",
|
|
680
|
+
alvo: efeito.alvo,
|
|
681
|
+
}));
|
|
682
|
+
}
|
|
683
|
+
export async function analisarDriftLegado(contexto) {
|
|
684
|
+
const indexTs = await indexarTypeScript(contexto.diretoriosCodigo);
|
|
685
|
+
const indexPy = await indexarPython(contexto.diretoriosCodigo);
|
|
686
|
+
const indexDart = await indexarDart(contexto.diretoriosCodigo);
|
|
687
|
+
const indexDotnet = await indexarDotnet(contexto.diretoriosCodigo);
|
|
688
|
+
const indexJava = await indexarJava(contexto.diretoriosCodigo);
|
|
689
|
+
const indexGo = await indexarGo(contexto.diretoriosCodigo);
|
|
690
|
+
const indexRust = await indexarRust(contexto.diretoriosCodigo);
|
|
691
|
+
const indexCpp = await indexarCpp(contexto.diretoriosCodigo);
|
|
692
|
+
const todosSimbolos = [
|
|
693
|
+
...indexTs.simbolos,
|
|
694
|
+
...indexPy.simbolos,
|
|
695
|
+
...indexDart,
|
|
696
|
+
...indexDotnet.simbolos,
|
|
697
|
+
...indexJava.simbolos,
|
|
698
|
+
...indexGo.simbolos,
|
|
699
|
+
...indexRust.simbolos,
|
|
700
|
+
...indexCpp,
|
|
701
|
+
];
|
|
702
|
+
const mapaImpl = new Map([
|
|
703
|
+
...indexTs.simbolos.map((item) => [item.caminho, item]),
|
|
704
|
+
...indexPy.simbolos.map((item) => [item.caminho, item]),
|
|
705
|
+
...indexDart.map((item) => [item.caminho, item]),
|
|
706
|
+
...indexDotnet.simbolos.map((item) => [item.caminho, item]),
|
|
707
|
+
...indexJava.simbolos.map((item) => [item.caminho, item]),
|
|
708
|
+
...indexGo.simbolos.map((item) => [item.caminho, item]),
|
|
709
|
+
...indexRust.simbolos.map((item) => [item.caminho, item]),
|
|
710
|
+
...indexCpp.map((item) => [item.caminho, item]),
|
|
711
|
+
]);
|
|
712
|
+
const mapaRecursos = new Map(indexTs.recursos.map((item) => [item.nome, item]));
|
|
713
|
+
const implsValidos = [];
|
|
714
|
+
const implsQuebrados = [];
|
|
715
|
+
const rotasDivergentes = [];
|
|
716
|
+
const recursosValidos = [];
|
|
717
|
+
const recursosDivergentes = [];
|
|
718
|
+
const diagnosticos = [];
|
|
719
|
+
const tasksResumo = [];
|
|
720
|
+
for (const item of contexto.modulosSelecionados) {
|
|
721
|
+
const ir = item.resultado.ir;
|
|
722
|
+
if (!ir) {
|
|
723
|
+
continue;
|
|
724
|
+
}
|
|
725
|
+
for (const task of ir.tasks) {
|
|
726
|
+
let validos = 0;
|
|
727
|
+
let quebrados = 0;
|
|
728
|
+
const arquivosReferenciados = new Set();
|
|
729
|
+
const simbolosReferenciados = new Set();
|
|
730
|
+
const candidatosTask = new Map();
|
|
731
|
+
if (task.implementacoesExternas.length === 0) {
|
|
732
|
+
for (const candidato of sugerirCandidatosParaTaskSemImpl(todosSimbolos, task.nome)) {
|
|
733
|
+
candidatosTask.set(`${candidato.origem}:${candidato.caminho}:${candidato.arquivo}:${candidato.simbolo}`, candidato);
|
|
734
|
+
}
|
|
735
|
+
diagnosticos.push({
|
|
736
|
+
tipo: "task_sem_impl",
|
|
737
|
+
modulo: ir.nome,
|
|
738
|
+
task: task.nome,
|
|
739
|
+
mensagem: `Task "${task.nome}" ainda nao foi ligada a nenhuma implementacao externa.`,
|
|
740
|
+
});
|
|
741
|
+
}
|
|
742
|
+
for (const impl of task.implementacoesExternas) {
|
|
743
|
+
const resolvido = mapaImpl.get(impl.caminho);
|
|
744
|
+
const registro = {
|
|
745
|
+
modulo: ir.nome,
|
|
746
|
+
task: task.nome,
|
|
747
|
+
origem: impl.origem,
|
|
748
|
+
caminho: impl.caminho,
|
|
749
|
+
arquivo: resolvido?.arquivo,
|
|
750
|
+
simbolo: resolvido?.simbolo,
|
|
751
|
+
caminhoResolvido: resolvido?.caminho,
|
|
752
|
+
status: resolvido ? "resolvido" : "quebrado",
|
|
753
|
+
};
|
|
754
|
+
if (resolvido) {
|
|
755
|
+
arquivosReferenciados.add(resolvido.arquivo);
|
|
756
|
+
simbolosReferenciados.add(resolvido.simbolo);
|
|
757
|
+
implsValidos.push(registro);
|
|
758
|
+
validos += 1;
|
|
759
|
+
}
|
|
760
|
+
else {
|
|
761
|
+
registro.candidatos = sugerirCandidatosParaImpl(todosSimbolos, impl.origem, impl.caminho);
|
|
762
|
+
for (const candidato of registro.candidatos) {
|
|
763
|
+
candidatosTask.set(`${candidato.origem}:${candidato.caminho}:${candidato.arquivo}:${candidato.simbolo}`, candidato);
|
|
764
|
+
}
|
|
765
|
+
implsQuebrados.push(registro);
|
|
766
|
+
quebrados += 1;
|
|
767
|
+
diagnosticos.push({
|
|
768
|
+
tipo: "impl_quebrado",
|
|
769
|
+
modulo: ir.nome,
|
|
770
|
+
task: task.nome,
|
|
771
|
+
mensagem: `Implementacao externa "${impl.origem}:${impl.caminho}" nao foi encontrada nos diretorios de codigo vivos.`,
|
|
772
|
+
});
|
|
773
|
+
}
|
|
774
|
+
}
|
|
775
|
+
tasksResumo.push({
|
|
776
|
+
modulo: ir.nome,
|
|
777
|
+
task: task.nome,
|
|
778
|
+
impls: task.implementacoesExternas.length,
|
|
779
|
+
implsValidos: validos,
|
|
780
|
+
implsQuebrados: quebrados,
|
|
781
|
+
semImplementacao: task.implementacoesExternas.length === 0,
|
|
782
|
+
arquivosReferenciados: [...arquivosReferenciados].sort((a, b) => a.localeCompare(b, "pt-BR")),
|
|
783
|
+
simbolosReferenciados: [...simbolosReferenciados].sort((a, b) => a.localeCompare(b, "pt-BR")),
|
|
784
|
+
candidatosImpl: ordenarCandidatos([...candidatosTask.values()]).slice(0, 5),
|
|
785
|
+
});
|
|
786
|
+
for (const recursoEsperado of extrairRecursosEsperados(task)) {
|
|
787
|
+
const resolvido = mapaRecursos.get(recursoEsperado.alvo);
|
|
788
|
+
const registro = {
|
|
789
|
+
modulo: ir.nome,
|
|
790
|
+
task: task.nome,
|
|
791
|
+
categoria: recursoEsperado.categoria,
|
|
792
|
+
alvo: recursoEsperado.alvo,
|
|
793
|
+
arquivo: resolvido?.arquivo ?? "",
|
|
794
|
+
origem: "firebase",
|
|
795
|
+
tipo: "colecao",
|
|
796
|
+
status: resolvido ? "resolvido" : "divergente",
|
|
797
|
+
};
|
|
798
|
+
if (resolvido) {
|
|
799
|
+
registro.arquivo = resolvido.arquivo;
|
|
800
|
+
recursosValidos.push(registro);
|
|
801
|
+
}
|
|
802
|
+
else {
|
|
803
|
+
recursosDivergentes.push(registro);
|
|
804
|
+
diagnosticos.push({
|
|
805
|
+
tipo: "recurso_divergente",
|
|
806
|
+
modulo: ir.nome,
|
|
807
|
+
task: task.nome,
|
|
808
|
+
mensagem: `Recurso vivo "${recursoEsperado.alvo}" nao foi encontrado nos bridges/configuracoes Firebase do codigo legado.`,
|
|
809
|
+
});
|
|
810
|
+
}
|
|
811
|
+
}
|
|
812
|
+
}
|
|
813
|
+
for (const route of ir.routes) {
|
|
814
|
+
const taskAssociada = ir.tasks.find((task) => task.nome === route.task);
|
|
815
|
+
const esperadas = escolherRotasEsperadas(taskAssociada ?? {
|
|
816
|
+
nome: "",
|
|
817
|
+
input: [],
|
|
818
|
+
output: [],
|
|
819
|
+
rules: [],
|
|
820
|
+
regrasEstruturadas: [],
|
|
821
|
+
effects: [],
|
|
822
|
+
efeitosEstruturados: [],
|
|
823
|
+
implementacoesExternas: [],
|
|
824
|
+
guarantees: [],
|
|
825
|
+
garantiasEstruturadas: [],
|
|
826
|
+
errors: {},
|
|
827
|
+
tests: [],
|
|
828
|
+
}, contexto.fontesLegado);
|
|
829
|
+
if (!esperadas.length || !route.metodo || !route.caminho) {
|
|
830
|
+
continue;
|
|
831
|
+
}
|
|
832
|
+
const encontradas = [
|
|
833
|
+
...indexTs.rotas,
|
|
834
|
+
...indexPy.rotas,
|
|
835
|
+
...indexDotnet.rotas,
|
|
836
|
+
...indexJava.rotas,
|
|
837
|
+
...indexGo.rotas,
|
|
838
|
+
...indexRust.rotas,
|
|
839
|
+
].filter((rotaResolvida) => esperadas.includes(rotaResolvida.origem));
|
|
840
|
+
const combina = encontradas.some((rotaResolvida) => rotaResolvida.metodo === route.metodo
|
|
841
|
+
&& normalizarCaminhoRota(rotaResolvida.caminho) === normalizarCaminhoRota(route.caminho));
|
|
842
|
+
if (!combina) {
|
|
843
|
+
const registro = {
|
|
844
|
+
modulo: ir.nome,
|
|
845
|
+
route: route.nome,
|
|
846
|
+
metodo: route.metodo,
|
|
847
|
+
caminho: route.caminho,
|
|
848
|
+
motivo: `Nenhuma rota publica ${route.metodo} ${route.caminho} foi encontrada no codigo legado para o framework esperado.`,
|
|
849
|
+
};
|
|
850
|
+
rotasDivergentes.push(registro);
|
|
851
|
+
diagnosticos.push({
|
|
852
|
+
tipo: "rota_divergente",
|
|
853
|
+
modulo: ir.nome,
|
|
854
|
+
route: route.nome,
|
|
855
|
+
mensagem: registro.motivo,
|
|
856
|
+
});
|
|
857
|
+
}
|
|
858
|
+
}
|
|
859
|
+
}
|
|
860
|
+
return {
|
|
861
|
+
comando: "drift",
|
|
862
|
+
sucesso: implsQuebrados.length === 0 && rotasDivergentes.length === 0 && recursosDivergentes.length === 0,
|
|
863
|
+
modulos: contexto.modulosSelecionados.map((item) => ({
|
|
864
|
+
caminho: item.caminho,
|
|
865
|
+
modulo: item.resultado.ir?.nome ?? item.resultado.modulo?.nome ?? null,
|
|
866
|
+
tasks: item.resultado.ir?.tasks.length ?? 0,
|
|
867
|
+
routes: item.resultado.ir?.routes.length ?? 0,
|
|
868
|
+
})),
|
|
869
|
+
tasks: tasksResumo,
|
|
870
|
+
impls_validos: implsValidos,
|
|
871
|
+
impls_quebrados: implsQuebrados,
|
|
872
|
+
rotas_divergentes: rotasDivergentes,
|
|
873
|
+
recursos_validos: recursosValidos,
|
|
874
|
+
recursos_divergentes: recursosDivergentes,
|
|
875
|
+
diagnosticos,
|
|
876
|
+
};
|
|
877
|
+
}
|
|
878
|
+
//# sourceMappingURL=drift.js.map
|