@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
|
@@ -0,0 +1,854 @@
|
|
|
1
|
+
import ts from "typescript";
|
|
2
|
+
const METODOS_HTTP = new Set(["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS"]);
|
|
3
|
+
function normalizarSeparadores(relacao) {
|
|
4
|
+
return relacao.replace(/\\/g, "/");
|
|
5
|
+
}
|
|
6
|
+
function deduplicarRotas(rotas) {
|
|
7
|
+
const mapa = new Map();
|
|
8
|
+
for (const rota of rotas) {
|
|
9
|
+
const chave = `${rota.origem}:${rota.metodo}:${rota.caminho}:${rota.simbolo}`;
|
|
10
|
+
if (!mapa.has(chave)) {
|
|
11
|
+
mapa.set(chave, rota);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
return [...mapa.values()];
|
|
15
|
+
}
|
|
16
|
+
function deduplicarCampos(campos) {
|
|
17
|
+
const mapa = new Map();
|
|
18
|
+
for (const campo of campos) {
|
|
19
|
+
const chave = campo.nome;
|
|
20
|
+
const existente = mapa.get(chave);
|
|
21
|
+
if (!existente) {
|
|
22
|
+
mapa.set(chave, campo);
|
|
23
|
+
continue;
|
|
24
|
+
}
|
|
25
|
+
mapa.set(chave, {
|
|
26
|
+
nome: campo.nome,
|
|
27
|
+
tipoTexto: existente.tipoTexto ?? campo.tipoTexto,
|
|
28
|
+
obrigatorio: existente.obrigatorio || campo.obrigatorio,
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
return [...mapa.values()];
|
|
32
|
+
}
|
|
33
|
+
function deduplicarNumeros(valores) {
|
|
34
|
+
return [...new Set(valores)].sort((a, b) => a - b);
|
|
35
|
+
}
|
|
36
|
+
function desembrulharExpressao(expr) {
|
|
37
|
+
let atual = expr;
|
|
38
|
+
while (true) {
|
|
39
|
+
if (ts.isParenthesizedExpression(atual) || ts.isAsExpression(atual) || ts.isSatisfiesExpression(atual) || ts.isTypeAssertionExpression(atual)) {
|
|
40
|
+
atual = atual.expression;
|
|
41
|
+
continue;
|
|
42
|
+
}
|
|
43
|
+
if (ts.isAwaitExpression(atual)) {
|
|
44
|
+
atual = atual.expression;
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
return atual;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
function extrairNomeExportado(statement, sourceFile) {
|
|
51
|
+
if (ts.isFunctionDeclaration(statement) && statement.name && statement.modifiers?.some((item) => item.kind === ts.SyntaxKind.ExportKeyword)) {
|
|
52
|
+
return [statement.name.text];
|
|
53
|
+
}
|
|
54
|
+
if (!ts.isVariableStatement(statement) || !statement.modifiers?.some((item) => item.kind === ts.SyntaxKind.ExportKeyword)) {
|
|
55
|
+
return [];
|
|
56
|
+
}
|
|
57
|
+
const nomes = [];
|
|
58
|
+
for (const declaracao of statement.declarationList.declarations) {
|
|
59
|
+
if (!ts.isIdentifier(declaracao.name) || !declaracao.initializer) {
|
|
60
|
+
continue;
|
|
61
|
+
}
|
|
62
|
+
if (ts.isArrowFunction(declaracao.initializer) || ts.isFunctionExpression(declaracao.initializer)) {
|
|
63
|
+
nomes.push(declaracao.name.getText(sourceFile));
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return nomes;
|
|
67
|
+
}
|
|
68
|
+
function extrairMetodosHttpNext(sourceFile) {
|
|
69
|
+
const encontrados = new Set();
|
|
70
|
+
for (const statement of sourceFile.statements) {
|
|
71
|
+
for (const nome of extrairNomeExportado(statement, sourceFile)) {
|
|
72
|
+
const metodo = nome.toUpperCase();
|
|
73
|
+
if (METODOS_HTTP.has(metodo)) {
|
|
74
|
+
encontrados.add(metodo);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return [...encontrados];
|
|
79
|
+
}
|
|
80
|
+
function inferirCaminhoNext(relacaoArquivo) {
|
|
81
|
+
const relacao = normalizarSeparadores(relacaoArquivo);
|
|
82
|
+
const segmentos = relacao.split("/");
|
|
83
|
+
const indiceSrcAppApi = segmentos.findIndex((segmento, indice) => segmento === "src" && segmentos[indice + 1] === "app" && segmentos[indice + 2] === "api");
|
|
84
|
+
const indiceAppApi = segmentos.findIndex((segmento, indice) => segmento === "app" && segmentos[indice + 1] === "api");
|
|
85
|
+
const inicioApi = indiceSrcAppApi >= 0
|
|
86
|
+
? indiceSrcAppApi + 3
|
|
87
|
+
: indiceAppApi >= 0
|
|
88
|
+
? indiceAppApi + 2
|
|
89
|
+
: -1;
|
|
90
|
+
if (inicioApi < 0) {
|
|
91
|
+
return undefined;
|
|
92
|
+
}
|
|
93
|
+
const caminhoAteRoute = segmentos.slice(inicioApi, -1);
|
|
94
|
+
if (segmentos.at(-1) !== "route.ts" && segmentos.at(-1) !== "route.js") {
|
|
95
|
+
return undefined;
|
|
96
|
+
}
|
|
97
|
+
const parametros = [];
|
|
98
|
+
const partes = caminhoAteRoute
|
|
99
|
+
.filter((segmento) => segmento && !/^\(.*\)$/.test(segmento) && !segmento.startsWith("@"))
|
|
100
|
+
.map((segmento) => {
|
|
101
|
+
const opcionalCatchAll = segmento.match(/^\[\[\.\.\.([A-Za-z_]\w*)\]\]$/);
|
|
102
|
+
if (opcionalCatchAll) {
|
|
103
|
+
parametros.push({ nome: opcionalCatchAll[1], tipoSema: "Texto" });
|
|
104
|
+
return `{${opcionalCatchAll[1]}}`;
|
|
105
|
+
}
|
|
106
|
+
const catchAll = segmento.match(/^\[\.\.\.([A-Za-z_]\w*)\]$/);
|
|
107
|
+
if (catchAll) {
|
|
108
|
+
parametros.push({ nome: catchAll[1], tipoSema: "Texto" });
|
|
109
|
+
return `{${catchAll[1]}}`;
|
|
110
|
+
}
|
|
111
|
+
const dinamico = segmento.match(/^\[([A-Za-z_]\w*)\]$/);
|
|
112
|
+
if (dinamico) {
|
|
113
|
+
const nome = dinamico[1];
|
|
114
|
+
parametros.push({
|
|
115
|
+
nome,
|
|
116
|
+
tipoSema: /(^|_)id$/i.test(nome) ? "Id" : "Texto",
|
|
117
|
+
});
|
|
118
|
+
return `{${nome}}`;
|
|
119
|
+
}
|
|
120
|
+
return segmento;
|
|
121
|
+
});
|
|
122
|
+
return {
|
|
123
|
+
caminho: (`/api/${partes.join("/")}`).replace(/\/+/g, "/"),
|
|
124
|
+
parametros,
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
function identificarRotasNodeHttp(sourceFile, nomeSimbolo, corpo) {
|
|
128
|
+
const rotas = [];
|
|
129
|
+
const visitar = (node) => {
|
|
130
|
+
if (ts.isIfStatement(node)) {
|
|
131
|
+
const expressao = node.expression.getText(sourceFile);
|
|
132
|
+
const caminho = expressao.match(/(?:req|request)\.url\s*===\s*["'`]([^"'`]+)["'`]/)?.[1];
|
|
133
|
+
const metodo = expressao.match(/(?:req|request)\.method\s*===\s*["'`]([A-Z]+)["'`]/)?.[1]?.toUpperCase();
|
|
134
|
+
if (caminho && metodo && METODOS_HTTP.has(metodo)) {
|
|
135
|
+
rotas.push({ caminho, metodo });
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
node.forEachChild(visitar);
|
|
139
|
+
};
|
|
140
|
+
visitar(corpo);
|
|
141
|
+
return deduplicarRotas(rotas.map((rota) => ({
|
|
142
|
+
origem: "firebase",
|
|
143
|
+
metodo: rota.metodo,
|
|
144
|
+
caminho: rota.caminho,
|
|
145
|
+
simbolo: nomeSimbolo,
|
|
146
|
+
parametros: [],
|
|
147
|
+
})));
|
|
148
|
+
}
|
|
149
|
+
function extrairRotasNodeWorker(sourceFile, relacaoArquivo) {
|
|
150
|
+
const relacao = normalizarSeparadores(relacaoArquivo);
|
|
151
|
+
const pareceWorkerHttp = /(?:^|\/)(?:apps\/worker\/|src\/services\/health-check|health-check\.ts$|sema_contract_bridge\.ts$)/.test(relacao);
|
|
152
|
+
if (!pareceWorkerHttp) {
|
|
153
|
+
return [];
|
|
154
|
+
}
|
|
155
|
+
const rotas = [];
|
|
156
|
+
for (const statement of sourceFile.statements) {
|
|
157
|
+
if (ts.isFunctionDeclaration(statement) && statement.name && statement.body && statement.modifiers?.some((item) => item.kind === ts.SyntaxKind.ExportKeyword)) {
|
|
158
|
+
rotas.push(...identificarRotasNodeHttp(sourceFile, statement.name.text, statement.body));
|
|
159
|
+
continue;
|
|
160
|
+
}
|
|
161
|
+
if (ts.isVariableStatement(statement) && statement.modifiers?.some((item) => item.kind === ts.SyntaxKind.ExportKeyword)) {
|
|
162
|
+
for (const declaracao of statement.declarationList.declarations) {
|
|
163
|
+
if (!ts.isIdentifier(declaracao.name) || !declaracao.initializer) {
|
|
164
|
+
continue;
|
|
165
|
+
}
|
|
166
|
+
if (ts.isArrowFunction(declaracao.initializer) || ts.isFunctionExpression(declaracao.initializer)) {
|
|
167
|
+
const corpo = declaracao.initializer.body;
|
|
168
|
+
if (ts.isBlock(corpo)) {
|
|
169
|
+
rotas.push(...identificarRotasNodeHttp(sourceFile, declaracao.name.text, corpo));
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
continue;
|
|
174
|
+
}
|
|
175
|
+
if (!ts.isClassDeclaration(statement) || !statement.name) {
|
|
176
|
+
continue;
|
|
177
|
+
}
|
|
178
|
+
for (const member of statement.members) {
|
|
179
|
+
if (!ts.isMethodDeclaration(member) || !member.body || !member.name) {
|
|
180
|
+
continue;
|
|
181
|
+
}
|
|
182
|
+
rotas.push(...identificarRotasNodeHttp(sourceFile, `${statement.name.text}.${member.name.getText(sourceFile)}`, member.body));
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
return deduplicarRotas(rotas);
|
|
186
|
+
}
|
|
187
|
+
function tipoInferidoPorNomeCampo(nome) {
|
|
188
|
+
return /(^|_)?id$/i.test(nome) || /Id$/.test(nome)
|
|
189
|
+
? "Id"
|
|
190
|
+
: undefined;
|
|
191
|
+
}
|
|
192
|
+
function normalizarTypeNode(typeNode) {
|
|
193
|
+
if (!typeNode) {
|
|
194
|
+
return undefined;
|
|
195
|
+
}
|
|
196
|
+
if (ts.isParenthesizedTypeNode(typeNode)) {
|
|
197
|
+
return normalizarTypeNode(typeNode.type);
|
|
198
|
+
}
|
|
199
|
+
if (ts.isUnionTypeNode(typeNode)) {
|
|
200
|
+
const uteis = typeNode.types
|
|
201
|
+
.filter((item) => item.kind !== ts.SyntaxKind.NullKeyword && item.kind !== ts.SyntaxKind.UndefinedKeyword)
|
|
202
|
+
.map((item) => normalizarTypeNode(item))
|
|
203
|
+
.filter((item) => Boolean(item));
|
|
204
|
+
if (uteis.length === 1) {
|
|
205
|
+
return uteis[0];
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
return typeNode;
|
|
209
|
+
}
|
|
210
|
+
function extrairCamposTypeNode(typeNode, sourceFile) {
|
|
211
|
+
const normalizado = normalizarTypeNode(typeNode);
|
|
212
|
+
if (!normalizado || !ts.isTypeLiteralNode(normalizado)) {
|
|
213
|
+
return [];
|
|
214
|
+
}
|
|
215
|
+
const campos = [];
|
|
216
|
+
for (const membro of normalizado.members) {
|
|
217
|
+
if (!ts.isPropertySignature(membro) || !membro.name) {
|
|
218
|
+
continue;
|
|
219
|
+
}
|
|
220
|
+
let nome;
|
|
221
|
+
if (ts.isIdentifier(membro.name) || ts.isStringLiteralLike(membro.name) || ts.isNumericLiteral(membro.name)) {
|
|
222
|
+
nome = membro.name.text;
|
|
223
|
+
}
|
|
224
|
+
if (!nome) {
|
|
225
|
+
continue;
|
|
226
|
+
}
|
|
227
|
+
campos.push({
|
|
228
|
+
nome,
|
|
229
|
+
tipoTexto: membro.type?.getText(sourceFile) ?? tipoInferidoPorNomeCampo(nome),
|
|
230
|
+
obrigatorio: !membro.questionToken,
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
return deduplicarCampos(campos);
|
|
234
|
+
}
|
|
235
|
+
function extrairMetadadosTipoExplicito(expr, sourceFile, typeNodeDeclarado) {
|
|
236
|
+
const typeNode = normalizarTypeNode(typeNodeDeclarado)
|
|
237
|
+
?? (() => {
|
|
238
|
+
let atual = expr;
|
|
239
|
+
while (true) {
|
|
240
|
+
if (ts.isParenthesizedExpression(atual)) {
|
|
241
|
+
atual = atual.expression;
|
|
242
|
+
continue;
|
|
243
|
+
}
|
|
244
|
+
if (ts.isAsExpression(atual) || ts.isSatisfiesExpression(atual) || ts.isTypeAssertionExpression(atual)) {
|
|
245
|
+
return normalizarTypeNode(atual.type);
|
|
246
|
+
}
|
|
247
|
+
if (ts.isAwaitExpression(atual)) {
|
|
248
|
+
atual = atual.expression;
|
|
249
|
+
continue;
|
|
250
|
+
}
|
|
251
|
+
return undefined;
|
|
252
|
+
}
|
|
253
|
+
})();
|
|
254
|
+
return {
|
|
255
|
+
tipoTexto: typeNode?.getText(sourceFile),
|
|
256
|
+
campos: extrairCamposTypeNode(typeNode, sourceFile),
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
function inferirTipoBasicoDeExpressao(expr, nomeCampo) {
|
|
260
|
+
if (!expr) {
|
|
261
|
+
return nomeCampo ? tipoInferidoPorNomeCampo(nomeCampo) : undefined;
|
|
262
|
+
}
|
|
263
|
+
if (ts.isParenthesizedExpression(expr) || ts.isAsExpression(expr) || ts.isSatisfiesExpression(expr) || ts.isTypeAssertionExpression(expr)) {
|
|
264
|
+
return inferirTipoBasicoDeExpressao(expr.expression, nomeCampo);
|
|
265
|
+
}
|
|
266
|
+
if (ts.isAwaitExpression(expr)) {
|
|
267
|
+
return inferirTipoBasicoDeExpressao(expr.expression, nomeCampo);
|
|
268
|
+
}
|
|
269
|
+
if (ts.isStringLiteralLike(expr) || ts.isNoSubstitutionTemplateLiteral(expr) || ts.isTemplateExpression(expr)) {
|
|
270
|
+
return "string";
|
|
271
|
+
}
|
|
272
|
+
if (ts.isNumericLiteral(expr)) {
|
|
273
|
+
return "number";
|
|
274
|
+
}
|
|
275
|
+
if (expr.kind === ts.SyntaxKind.TrueKeyword || expr.kind === ts.SyntaxKind.FalseKeyword) {
|
|
276
|
+
return "boolean";
|
|
277
|
+
}
|
|
278
|
+
if (ts.isArrayLiteralExpression(expr) || ts.isObjectLiteralExpression(expr)) {
|
|
279
|
+
return "Json";
|
|
280
|
+
}
|
|
281
|
+
if (expr.kind === ts.SyntaxKind.NullKeyword) {
|
|
282
|
+
return "Json";
|
|
283
|
+
}
|
|
284
|
+
if (ts.isCallExpression(expr)) {
|
|
285
|
+
const nome = expr.expression.getText().replace(/\s+/g, "");
|
|
286
|
+
if (/^(Number|parseFloat)$/.test(nome)) {
|
|
287
|
+
return "number";
|
|
288
|
+
}
|
|
289
|
+
if (/^parseInt$/.test(nome)) {
|
|
290
|
+
return "int";
|
|
291
|
+
}
|
|
292
|
+
if (/^(Boolean)$/.test(nome)) {
|
|
293
|
+
return "boolean";
|
|
294
|
+
}
|
|
295
|
+
if (/^(String)$/.test(nome)) {
|
|
296
|
+
return "string";
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
return nomeCampo ? tipoInferidoPorNomeCampo(nomeCampo) : undefined;
|
|
300
|
+
}
|
|
301
|
+
function extrairCamposObjetoLiteral(objeto, sourceFile) {
|
|
302
|
+
const campos = [];
|
|
303
|
+
for (const propriedade of objeto.properties) {
|
|
304
|
+
if (!ts.isPropertyAssignment(propriedade) && !ts.isShorthandPropertyAssignment(propriedade)) {
|
|
305
|
+
continue;
|
|
306
|
+
}
|
|
307
|
+
const nome = propriedade.name.getText(sourceFile).replace(/^["']|["']$/g, "");
|
|
308
|
+
campos.push({
|
|
309
|
+
nome,
|
|
310
|
+
tipoTexto: inferirTipoBasicoDeExpressao(ts.isPropertyAssignment(propriedade) ? propriedade.initializer : propriedade.name, nome),
|
|
311
|
+
obrigatorio: true,
|
|
312
|
+
});
|
|
313
|
+
}
|
|
314
|
+
return deduplicarCampos(campos);
|
|
315
|
+
}
|
|
316
|
+
function extrairStatusHttp(expr) {
|
|
317
|
+
if (!expr || !ts.isObjectLiteralExpression(expr)) {
|
|
318
|
+
return undefined;
|
|
319
|
+
}
|
|
320
|
+
for (const propriedade of expr.properties) {
|
|
321
|
+
if (!ts.isPropertyAssignment(propriedade) || propriedade.name.getText().replace(/^["']|["']$/g, "") !== "status") {
|
|
322
|
+
continue;
|
|
323
|
+
}
|
|
324
|
+
if (ts.isNumericLiteral(propriedade.initializer)) {
|
|
325
|
+
return Number(propriedade.initializer.text);
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
return undefined;
|
|
329
|
+
}
|
|
330
|
+
function ehRequestUrl(expr, requestNames) {
|
|
331
|
+
return ts.isPropertyAccessExpression(expr)
|
|
332
|
+
&& expr.name.text === "url"
|
|
333
|
+
&& ts.isIdentifier(expr.expression)
|
|
334
|
+
&& requestNames.has(expr.expression.text);
|
|
335
|
+
}
|
|
336
|
+
function ehNewUrl(expr, requestNames) {
|
|
337
|
+
return ts.isNewExpression(expr)
|
|
338
|
+
&& ts.isIdentifier(expr.expression)
|
|
339
|
+
&& expr.expression.text === "URL"
|
|
340
|
+
&& Boolean(expr.arguments?.some((argumento) => ehRequestUrl(argumento, requestNames)));
|
|
341
|
+
}
|
|
342
|
+
function ehSearchParamsSource(expr, requestNames, urlAliases, searchParamsAliases) {
|
|
343
|
+
const normalizado = desembrulharExpressao(expr);
|
|
344
|
+
if (ts.isIdentifier(normalizado)) {
|
|
345
|
+
return searchParamsAliases.has(normalizado.text);
|
|
346
|
+
}
|
|
347
|
+
if (ts.isPropertyAccessExpression(normalizado) && normalizado.name.text === "searchParams") {
|
|
348
|
+
const alvo = normalizado.expression;
|
|
349
|
+
if (ts.isPropertyAccessExpression(alvo) && alvo.name.text === "nextUrl" && ts.isIdentifier(alvo.expression) && requestNames.has(alvo.expression.text)) {
|
|
350
|
+
return true;
|
|
351
|
+
}
|
|
352
|
+
if (ts.isIdentifier(alvo) && urlAliases.has(alvo.text)) {
|
|
353
|
+
return true;
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
return false;
|
|
357
|
+
}
|
|
358
|
+
function ehCallRequest(expr, requestNames, metodo) {
|
|
359
|
+
const alvo = desembrulharExpressao(expr);
|
|
360
|
+
return ts.isCallExpression(alvo)
|
|
361
|
+
&& ts.isPropertyAccessExpression(alvo.expression)
|
|
362
|
+
&& alvo.expression.name.text === metodo
|
|
363
|
+
&& ts.isIdentifier(alvo.expression.expression)
|
|
364
|
+
&& requestNames.has(alvo.expression.expression.text);
|
|
365
|
+
}
|
|
366
|
+
function obterNomeSchema(call) {
|
|
367
|
+
if (!ts.isPropertyAccessExpression(call.expression)) {
|
|
368
|
+
return undefined;
|
|
369
|
+
}
|
|
370
|
+
const alvo = call.expression.expression;
|
|
371
|
+
if (!ts.isIdentifier(alvo)) {
|
|
372
|
+
return undefined;
|
|
373
|
+
}
|
|
374
|
+
return alvo.text;
|
|
375
|
+
}
|
|
376
|
+
function resolverMetadadosOrigemBody(expr, sourceFile, requestNames, bodyAliases, jsonAliases, safeParseAliases, schemasZod, typeNodeDeclarado) {
|
|
377
|
+
const normalizado = desembrulharExpressao(expr);
|
|
378
|
+
if (ts.isIdentifier(normalizado) && bodyAliases.has(normalizado.text)) {
|
|
379
|
+
return bodyAliases.get(normalizado.text);
|
|
380
|
+
}
|
|
381
|
+
if (ehCallRequest(expr, requestNames, "json")) {
|
|
382
|
+
return extrairMetadadosTipoExplicito(expr, sourceFile, typeNodeDeclarado);
|
|
383
|
+
}
|
|
384
|
+
if (ts.isCallExpression(normalizado)
|
|
385
|
+
&& ts.isPropertyAccessExpression(normalizado.expression)
|
|
386
|
+
&& ["parse", "safeParse"].includes(normalizado.expression.name.text)) {
|
|
387
|
+
const schemaNome = obterNomeSchema(normalizado);
|
|
388
|
+
const schemaCampos = schemaNome ? schemasZod.get(schemaNome) : undefined;
|
|
389
|
+
const argumento = normalizado.arguments[0];
|
|
390
|
+
const argumentoNormalizado = argumento ? desembrulharExpressao(argumento) : undefined;
|
|
391
|
+
if (schemaCampos
|
|
392
|
+
&& argumento
|
|
393
|
+
&& (ehCallRequest(argumento, requestNames, "json")
|
|
394
|
+
|| (argumentoNormalizado && ts.isIdentifier(argumentoNormalizado) && jsonAliases.has(argumentoNormalizado.text)))) {
|
|
395
|
+
return {
|
|
396
|
+
tipoTexto: undefined,
|
|
397
|
+
campos: schemaCampos,
|
|
398
|
+
};
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
if (ts.isPropertyAccessExpression(normalizado)
|
|
402
|
+
&& normalizado.name.text === "data"
|
|
403
|
+
&& ts.isIdentifier(normalizado.expression)
|
|
404
|
+
&& safeParseAliases.has(normalizado.expression.text)) {
|
|
405
|
+
return {
|
|
406
|
+
tipoTexto: undefined,
|
|
407
|
+
campos: safeParseAliases.get(normalizado.expression.text) ?? [],
|
|
408
|
+
};
|
|
409
|
+
}
|
|
410
|
+
return undefined;
|
|
411
|
+
}
|
|
412
|
+
function extrairCamposBindingPattern(pattern, sourceFile, camposConhecidos) {
|
|
413
|
+
const conhecidos = new Map(camposConhecidos.map((campo) => [campo.nome, campo]));
|
|
414
|
+
const campos = [];
|
|
415
|
+
for (const elemento of pattern.elements) {
|
|
416
|
+
if (!ts.isIdentifier(elemento.name)) {
|
|
417
|
+
continue;
|
|
418
|
+
}
|
|
419
|
+
const nomeCampo = elemento.propertyName?.getText(sourceFile) ?? elemento.name.text;
|
|
420
|
+
const conhecido = conhecidos.get(nomeCampo);
|
|
421
|
+
campos.push({
|
|
422
|
+
nome: nomeCampo,
|
|
423
|
+
tipoTexto: conhecido?.tipoTexto ?? inferirTipoBasicoDeExpressao(elemento.initializer, nomeCampo) ?? tipoInferidoPorNomeCampo(nomeCampo),
|
|
424
|
+
obrigatorio: conhecido?.obrigatorio ?? false,
|
|
425
|
+
});
|
|
426
|
+
}
|
|
427
|
+
return deduplicarCampos(campos);
|
|
428
|
+
}
|
|
429
|
+
function extrairCampoZod(expr, nome) {
|
|
430
|
+
const texto = expr.getText().replace(/\s+/g, "");
|
|
431
|
+
let tipoTexto = tipoInferidoPorNomeCampo(nome);
|
|
432
|
+
if (/\.uuid\(/i.test(texto)) {
|
|
433
|
+
tipoTexto = "Id";
|
|
434
|
+
}
|
|
435
|
+
else if (/\.string\(/i.test(texto)) {
|
|
436
|
+
tipoTexto = "string";
|
|
437
|
+
}
|
|
438
|
+
else if (/\.number\(/i.test(texto) || /\.int\(/i.test(texto)) {
|
|
439
|
+
tipoTexto = /\.int\(/i.test(texto) ? "int" : "number";
|
|
440
|
+
}
|
|
441
|
+
else if (/\.boolean\(/i.test(texto)) {
|
|
442
|
+
tipoTexto = "boolean";
|
|
443
|
+
}
|
|
444
|
+
else if (/\.date\(/i.test(texto)) {
|
|
445
|
+
tipoTexto = "date";
|
|
446
|
+
}
|
|
447
|
+
else if (/\.array\(/i.test(texto) || /\.object\(/i.test(texto) || /\.record\(/i.test(texto)) {
|
|
448
|
+
tipoTexto = "Json";
|
|
449
|
+
}
|
|
450
|
+
return {
|
|
451
|
+
nome,
|
|
452
|
+
tipoTexto,
|
|
453
|
+
obrigatorio: !/\.optional\(/i.test(texto),
|
|
454
|
+
};
|
|
455
|
+
}
|
|
456
|
+
function extrairSchemasZodLocais(sourceFile) {
|
|
457
|
+
const schemas = new Map();
|
|
458
|
+
sourceFile.forEachChild((node) => {
|
|
459
|
+
if (!ts.isVariableStatement(node)) {
|
|
460
|
+
return;
|
|
461
|
+
}
|
|
462
|
+
for (const declaracao of node.declarationList.declarations) {
|
|
463
|
+
if (!ts.isIdentifier(declaracao.name) || !declaracao.initializer) {
|
|
464
|
+
continue;
|
|
465
|
+
}
|
|
466
|
+
let chamada;
|
|
467
|
+
if (ts.isCallExpression(declaracao.initializer)) {
|
|
468
|
+
chamada = declaracao.initializer;
|
|
469
|
+
}
|
|
470
|
+
else if (ts.isCallExpression(declaracao.initializer)
|
|
471
|
+
&& ts.isPropertyAccessExpression(declaracao.initializer.expression)) {
|
|
472
|
+
chamada = declaracao.initializer;
|
|
473
|
+
}
|
|
474
|
+
else if (ts.isCallExpression(declaracao.initializer)
|
|
475
|
+
&& ts.isPropertyAccessExpression(declaracao.initializer.expression)) {
|
|
476
|
+
chamada = declaracao.initializer;
|
|
477
|
+
}
|
|
478
|
+
const chamadaInicial = chamada ?? (ts.isCallExpression(declaracao.initializer) ? declaracao.initializer : undefined);
|
|
479
|
+
if (!chamadaInicial) {
|
|
480
|
+
continue;
|
|
481
|
+
}
|
|
482
|
+
let chamadaObjeto;
|
|
483
|
+
if (ts.isPropertyAccessExpression(chamadaInicial.expression)
|
|
484
|
+
&& chamadaInicial.expression.name.text === "object") {
|
|
485
|
+
chamadaObjeto = chamadaInicial;
|
|
486
|
+
}
|
|
487
|
+
else if (ts.isPropertyAccessExpression(chamadaInicial.expression)
|
|
488
|
+
&& ts.isCallExpression(chamadaInicial.expression.expression)
|
|
489
|
+
&& ts.isPropertyAccessExpression(chamadaInicial.expression.expression.expression)
|
|
490
|
+
&& chamadaInicial.expression.expression.expression.name.text === "object") {
|
|
491
|
+
chamadaObjeto = chamadaInicial.expression.expression;
|
|
492
|
+
}
|
|
493
|
+
if (!chamadaObjeto) {
|
|
494
|
+
continue;
|
|
495
|
+
}
|
|
496
|
+
const objeto = chamadaObjeto.arguments[0];
|
|
497
|
+
if (!objeto || !ts.isObjectLiteralExpression(objeto)) {
|
|
498
|
+
continue;
|
|
499
|
+
}
|
|
500
|
+
const campos = [];
|
|
501
|
+
for (const propriedade of objeto.properties) {
|
|
502
|
+
if (!ts.isPropertyAssignment(propriedade)) {
|
|
503
|
+
continue;
|
|
504
|
+
}
|
|
505
|
+
const nomeCampo = propriedade.name.getText(sourceFile).replace(/^["']|["']$/g, "");
|
|
506
|
+
campos.push(extrairCampoZod(propriedade.initializer, nomeCampo));
|
|
507
|
+
}
|
|
508
|
+
if (campos.length > 0) {
|
|
509
|
+
schemas.set(declaracao.name.text, deduplicarCampos(campos));
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
});
|
|
513
|
+
return schemas;
|
|
514
|
+
}
|
|
515
|
+
function localizarExportacaoEmStatement(statement, nomeExportado, sourceFile) {
|
|
516
|
+
if (ts.isFunctionDeclaration(statement) && statement.name?.text === nomeExportado && statement.modifiers?.some((modifier) => modifier.kind === ts.SyntaxKind.ExportKeyword)) {
|
|
517
|
+
return {
|
|
518
|
+
corpo: statement.body,
|
|
519
|
+
retorno: statement.type?.getText(sourceFile),
|
|
520
|
+
parametros: statement.parameters,
|
|
521
|
+
declaracao: statement,
|
|
522
|
+
};
|
|
523
|
+
}
|
|
524
|
+
if (!ts.isVariableStatement(statement) || !statement.modifiers?.some((modifier) => modifier.kind === ts.SyntaxKind.ExportKeyword)) {
|
|
525
|
+
return undefined;
|
|
526
|
+
}
|
|
527
|
+
for (const declaracao of statement.declarationList.declarations) {
|
|
528
|
+
if (!ts.isIdentifier(declaracao.name) || declaracao.name.text !== nomeExportado || !declaracao.initializer) {
|
|
529
|
+
continue;
|
|
530
|
+
}
|
|
531
|
+
if (ts.isArrowFunction(declaracao.initializer) || ts.isFunctionExpression(declaracao.initializer)) {
|
|
532
|
+
return {
|
|
533
|
+
corpo: ts.isBlock(declaracao.initializer.body) ? declaracao.initializer.body : undefined,
|
|
534
|
+
retorno: declaracao.initializer.type?.getText(sourceFile),
|
|
535
|
+
parametros: declaracao.initializer.parameters,
|
|
536
|
+
declaracao: declaracao.initializer,
|
|
537
|
+
};
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
return undefined;
|
|
541
|
+
}
|
|
542
|
+
function inferirTipoPorContexto(node, nomeCampo) {
|
|
543
|
+
let atual = node;
|
|
544
|
+
let profundidade = 0;
|
|
545
|
+
while (atual && profundidade < 4) {
|
|
546
|
+
atual = atual.parent;
|
|
547
|
+
profundidade += 1;
|
|
548
|
+
if (!atual) {
|
|
549
|
+
break;
|
|
550
|
+
}
|
|
551
|
+
if (ts.isCallExpression(atual)) {
|
|
552
|
+
const nome = atual.expression.getText().replace(/\s+/g, "");
|
|
553
|
+
if (/^(Number|parseFloat)$/.test(nome)) {
|
|
554
|
+
return "number";
|
|
555
|
+
}
|
|
556
|
+
if (/^parseInt$/.test(nome)) {
|
|
557
|
+
return "int";
|
|
558
|
+
}
|
|
559
|
+
if (/^(Boolean)$/.test(nome)) {
|
|
560
|
+
return "boolean";
|
|
561
|
+
}
|
|
562
|
+
if (/^(String)$/.test(nome)) {
|
|
563
|
+
return "string";
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
return tipoInferidoPorNomeCampo(nomeCampo);
|
|
568
|
+
}
|
|
569
|
+
function extrairRetornoHttp(expr, sourceFile, valoresLocais) {
|
|
570
|
+
const normalizado = desembrulharExpressao(expr);
|
|
571
|
+
if (ts.isCallExpression(normalizado) && ts.isPropertyAccessExpression(normalizado.expression)) {
|
|
572
|
+
const alvo = normalizado.expression.expression.getText(sourceFile);
|
|
573
|
+
const metodo = normalizado.expression.name.text;
|
|
574
|
+
if ((alvo === "NextResponse" || alvo === "Response") && metodo === "json") {
|
|
575
|
+
const primeiro = normalizado.arguments[0];
|
|
576
|
+
const segundo = normalizado.arguments[1];
|
|
577
|
+
let campos = [];
|
|
578
|
+
let tipoTexto = normalizado.typeArguments?.[0]?.getText(sourceFile);
|
|
579
|
+
if (primeiro && ts.isObjectLiteralExpression(primeiro)) {
|
|
580
|
+
campos = extrairCamposObjetoLiteral(primeiro, sourceFile);
|
|
581
|
+
}
|
|
582
|
+
else if (primeiro && ts.isIdentifier(primeiro)) {
|
|
583
|
+
const valorLocal = valoresLocais.get(primeiro.text);
|
|
584
|
+
if (valorLocal) {
|
|
585
|
+
campos = valorLocal.campos;
|
|
586
|
+
tipoTexto ??= valorLocal.tipoTexto;
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
return {
|
|
590
|
+
campos,
|
|
591
|
+
tipoTexto,
|
|
592
|
+
status: extrairStatusHttp(segundo),
|
|
593
|
+
};
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
if (ts.isNewExpression(normalizado) && ts.isIdentifier(normalizado.expression) && normalizado.expression.text === "Response") {
|
|
597
|
+
const tipoTexto = normalizado.typeArguments?.[0]?.getText(sourceFile);
|
|
598
|
+
return {
|
|
599
|
+
campos: [],
|
|
600
|
+
tipoTexto,
|
|
601
|
+
status: extrairStatusHttp(normalizado.arguments?.[1]),
|
|
602
|
+
};
|
|
603
|
+
}
|
|
604
|
+
if (ts.isObjectLiteralExpression(normalizado)) {
|
|
605
|
+
return {
|
|
606
|
+
campos: extrairCamposObjetoLiteral(normalizado, sourceFile),
|
|
607
|
+
};
|
|
608
|
+
}
|
|
609
|
+
if (ts.isIdentifier(normalizado)) {
|
|
610
|
+
const valorLocal = valoresLocais.get(normalizado.text);
|
|
611
|
+
if (valorLocal) {
|
|
612
|
+
return {
|
|
613
|
+
campos: valorLocal.campos,
|
|
614
|
+
tipoTexto: valorLocal.tipoTexto,
|
|
615
|
+
status: valorLocal.status,
|
|
616
|
+
};
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
return undefined;
|
|
620
|
+
}
|
|
621
|
+
export function localizarExportacaoTypeScriptHttp(sourceFile, nomeExportado) {
|
|
622
|
+
for (const node of sourceFile.statements) {
|
|
623
|
+
const localizada = localizarExportacaoEmStatement(node, nomeExportado, sourceFile);
|
|
624
|
+
if (localizada) {
|
|
625
|
+
return localizada;
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
return undefined;
|
|
629
|
+
}
|
|
630
|
+
export function inferirSemanticaHandlerTypeScriptHttp(sourceFile, nomeExportado) {
|
|
631
|
+
const exportacao = localizarExportacaoTypeScriptHttp(sourceFile, nomeExportado);
|
|
632
|
+
if (!exportacao?.corpo) {
|
|
633
|
+
return undefined;
|
|
634
|
+
}
|
|
635
|
+
const requestNames = new Set();
|
|
636
|
+
const urlAliases = new Set();
|
|
637
|
+
const searchParamsAliases = new Set();
|
|
638
|
+
const jsonAliases = new Set();
|
|
639
|
+
const formAliases = new Set();
|
|
640
|
+
const bodyAliases = new Map();
|
|
641
|
+
const bodyDeclaracoesDiretas = [];
|
|
642
|
+
const safeParseAliases = new Map();
|
|
643
|
+
const valoresLocais = new Map();
|
|
644
|
+
const schemasZod = extrairSchemasZodLocais(sourceFile);
|
|
645
|
+
for (const parametro of exportacao.parametros) {
|
|
646
|
+
if (ts.isIdentifier(parametro.name)) {
|
|
647
|
+
requestNames.add(parametro.name.text);
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
const visitarDeclaracoes = (node) => {
|
|
651
|
+
if (ts.isVariableDeclaration(node) && node.initializer) {
|
|
652
|
+
const inicializadorNormalizado = desembrulharExpressao(node.initializer);
|
|
653
|
+
if (ts.isIdentifier(node.name)) {
|
|
654
|
+
const nome = node.name.text;
|
|
655
|
+
if (ehNewUrl(node.initializer, requestNames)) {
|
|
656
|
+
urlAliases.add(nome);
|
|
657
|
+
}
|
|
658
|
+
if (ehSearchParamsSource(node.initializer, requestNames, urlAliases, searchParamsAliases)) {
|
|
659
|
+
searchParamsAliases.add(nome);
|
|
660
|
+
}
|
|
661
|
+
if (ehCallRequest(node.initializer, requestNames, "json")) {
|
|
662
|
+
const metadadosBody = extrairMetadadosTipoExplicito(node.initializer, sourceFile, node.type);
|
|
663
|
+
jsonAliases.add(nome);
|
|
664
|
+
bodyAliases.set(nome, {
|
|
665
|
+
tipoTexto: metadadosBody.tipoTexto,
|
|
666
|
+
campos: metadadosBody.campos,
|
|
667
|
+
});
|
|
668
|
+
}
|
|
669
|
+
if (ehCallRequest(node.initializer, requestNames, "formData")) {
|
|
670
|
+
formAliases.add(nome);
|
|
671
|
+
}
|
|
672
|
+
if (ts.isCallExpression(inicializadorNormalizado)
|
|
673
|
+
&& ts.isPropertyAccessExpression(inicializadorNormalizado.expression)
|
|
674
|
+
&& ["parse", "safeParse"].includes(inicializadorNormalizado.expression.name.text)) {
|
|
675
|
+
const chamada = inicializadorNormalizado;
|
|
676
|
+
const acessoChamada = chamada.expression;
|
|
677
|
+
const schemaNome = obterNomeSchema(chamada);
|
|
678
|
+
const arg = chamada.arguments[0];
|
|
679
|
+
const schemaCampos = schemaNome ? schemasZod.get(schemaNome) : undefined;
|
|
680
|
+
const argNormalizado = arg ? desembrulharExpressao(arg) : undefined;
|
|
681
|
+
if (schemaCampos && arg && (ehCallRequest(arg, requestNames, "json") || (argNormalizado && ts.isIdentifier(argNormalizado) && jsonAliases.has(argNormalizado.text)))) {
|
|
682
|
+
if (acessoChamada.name.text === "parse") {
|
|
683
|
+
bodyAliases.set(nome, { campos: schemaCampos, tipoTexto: undefined });
|
|
684
|
+
}
|
|
685
|
+
else {
|
|
686
|
+
safeParseAliases.set(nome, schemaCampos);
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
if (ts.isPropertyAccessExpression(inicializadorNormalizado)
|
|
691
|
+
&& inicializadorNormalizado.name.text === "data"
|
|
692
|
+
&& ts.isIdentifier(inicializadorNormalizado.expression)
|
|
693
|
+
&& safeParseAliases.has(inicializadorNormalizado.expression.text)) {
|
|
694
|
+
const acesso = inicializadorNormalizado;
|
|
695
|
+
const alvoAcesso = acesso.expression;
|
|
696
|
+
bodyAliases.set(nome, {
|
|
697
|
+
campos: safeParseAliases.get(alvoAcesso.text) ?? [],
|
|
698
|
+
tipoTexto: undefined,
|
|
699
|
+
});
|
|
700
|
+
}
|
|
701
|
+
if (ts.isObjectLiteralExpression(node.initializer)) {
|
|
702
|
+
valoresLocais.set(nome, {
|
|
703
|
+
tipoTexto: node.type?.getText(sourceFile),
|
|
704
|
+
campos: extrairCamposObjetoLiteral(node.initializer, sourceFile),
|
|
705
|
+
});
|
|
706
|
+
}
|
|
707
|
+
else if (ts.isIdentifier(node.initializer) && valoresLocais.has(node.initializer.text)) {
|
|
708
|
+
valoresLocais.set(nome, valoresLocais.get(node.initializer.text));
|
|
709
|
+
}
|
|
710
|
+
else {
|
|
711
|
+
const retornoHttp = extrairRetornoHttp(node.initializer, sourceFile, valoresLocais);
|
|
712
|
+
if (retornoHttp) {
|
|
713
|
+
valoresLocais.set(nome, {
|
|
714
|
+
tipoTexto: retornoHttp.tipoTexto,
|
|
715
|
+
campos: retornoHttp.campos,
|
|
716
|
+
status: retornoHttp.status,
|
|
717
|
+
});
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
}
|
|
721
|
+
if (ts.isObjectBindingPattern(node.name)) {
|
|
722
|
+
const metadadosBody = resolverMetadadosOrigemBody(node.initializer, sourceFile, requestNames, bodyAliases, jsonAliases, safeParseAliases, schemasZod, node.type);
|
|
723
|
+
if (metadadosBody) {
|
|
724
|
+
const camposBinding = extrairCamposBindingPattern(node.name, sourceFile, metadadosBody.campos);
|
|
725
|
+
bodyDeclaracoesDiretas.push(...camposBinding);
|
|
726
|
+
const inicializadorNormalizado = desembrulharExpressao(node.initializer);
|
|
727
|
+
if (ts.isIdentifier(inicializadorNormalizado) && bodyAliases.has(inicializadorNormalizado.text)) {
|
|
728
|
+
bodyAliases.set(inicializadorNormalizado.text, {
|
|
729
|
+
tipoTexto: bodyAliases.get(inicializadorNormalizado.text)?.tipoTexto ?? metadadosBody.tipoTexto,
|
|
730
|
+
campos: deduplicarCampos([
|
|
731
|
+
...(bodyAliases.get(inicializadorNormalizado.text)?.campos ?? []),
|
|
732
|
+
...camposBinding,
|
|
733
|
+
]),
|
|
734
|
+
});
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
}
|
|
738
|
+
if (ts.isObjectBindingPattern(node.name)
|
|
739
|
+
&& ehNewUrl(node.initializer, requestNames)) {
|
|
740
|
+
for (const elemento of node.name.elements) {
|
|
741
|
+
if (!ts.isIdentifier(elemento.name)) {
|
|
742
|
+
continue;
|
|
743
|
+
}
|
|
744
|
+
const nomeCampo = elemento.propertyName?.getText(sourceFile) ?? elemento.name.text;
|
|
745
|
+
if (nomeCampo === "searchParams") {
|
|
746
|
+
searchParamsAliases.add(elemento.name.text);
|
|
747
|
+
}
|
|
748
|
+
}
|
|
749
|
+
}
|
|
750
|
+
}
|
|
751
|
+
node.forEachChild(visitarDeclaracoes);
|
|
752
|
+
};
|
|
753
|
+
visitarDeclaracoes(exportacao.corpo);
|
|
754
|
+
const query = [];
|
|
755
|
+
const body = [];
|
|
756
|
+
const response = [];
|
|
757
|
+
const statuses = [];
|
|
758
|
+
let bodyTipoTexto;
|
|
759
|
+
let responseTipoTexto;
|
|
760
|
+
const adicionarBodyCampos = (campos, tipoTexto) => {
|
|
761
|
+
if (campos.length > 0) {
|
|
762
|
+
body.push(...campos);
|
|
763
|
+
}
|
|
764
|
+
bodyTipoTexto ??= tipoTexto;
|
|
765
|
+
};
|
|
766
|
+
const visitarSemantica = (node) => {
|
|
767
|
+
if (ts.isCallExpression(node) && ts.isPropertyAccessExpression(node.expression)) {
|
|
768
|
+
const alvo = node.expression.expression;
|
|
769
|
+
const metodo = node.expression.name.text;
|
|
770
|
+
if (["get", "getAll", "has"].includes(metodo)) {
|
|
771
|
+
const argumento = node.arguments[0];
|
|
772
|
+
if (argumento && ts.isStringLiteralLike(argumento) && ehSearchParamsSource(alvo, requestNames, urlAliases, searchParamsAliases)) {
|
|
773
|
+
query.push({
|
|
774
|
+
nome: argumento.text,
|
|
775
|
+
tipoTexto: inferirTipoPorContexto(node, argumento.text) ?? "string",
|
|
776
|
+
obrigatorio: false,
|
|
777
|
+
});
|
|
778
|
+
}
|
|
779
|
+
if (argumento && ts.isStringLiteralLike(argumento) && ts.isIdentifier(alvo) && formAliases.has(alvo.text)) {
|
|
780
|
+
body.push({
|
|
781
|
+
nome: argumento.text,
|
|
782
|
+
tipoTexto: inferirTipoPorContexto(node, argumento.text) ?? "string",
|
|
783
|
+
obrigatorio: false,
|
|
784
|
+
});
|
|
785
|
+
}
|
|
786
|
+
}
|
|
787
|
+
if (["parse", "safeParse"].includes(metodo)) {
|
|
788
|
+
const schemaNome = obterNomeSchema(node);
|
|
789
|
+
const schemaCampos = schemaNome ? schemasZod.get(schemaNome) : undefined;
|
|
790
|
+
const argumento = node.arguments[0];
|
|
791
|
+
if (schemaCampos && argumento && (ehCallRequest(argumento, requestNames, "json") || (ts.isIdentifier(argumento) && jsonAliases.has(argumento.text)))) {
|
|
792
|
+
adicionarBodyCampos(schemaCampos);
|
|
793
|
+
}
|
|
794
|
+
}
|
|
795
|
+
}
|
|
796
|
+
if (ts.isPropertyAccessExpression(node) && ts.isIdentifier(node.expression) && bodyAliases.has(node.expression.text)) {
|
|
797
|
+
const bodyAlias = bodyAliases.get(node.expression.text);
|
|
798
|
+
if (!bodyAlias.campos.some((campo) => campo.nome === node.name.text)) {
|
|
799
|
+
body.push({
|
|
800
|
+
nome: node.name.text,
|
|
801
|
+
tipoTexto: inferirTipoPorContexto(node, node.name.text) ?? "string",
|
|
802
|
+
obrigatorio: false,
|
|
803
|
+
});
|
|
804
|
+
}
|
|
805
|
+
bodyTipoTexto ??= bodyAlias.tipoTexto;
|
|
806
|
+
}
|
|
807
|
+
if (ts.isReturnStatement(node) && node.expression) {
|
|
808
|
+
const retorno = extrairRetornoHttp(node.expression, sourceFile, valoresLocais);
|
|
809
|
+
if (retorno) {
|
|
810
|
+
if (typeof retorno.status === "number") {
|
|
811
|
+
statuses.push(retorno.status);
|
|
812
|
+
}
|
|
813
|
+
if (retorno.status === undefined || retorno.status < 400) {
|
|
814
|
+
response.push(...retorno.campos);
|
|
815
|
+
responseTipoTexto ??= retorno.tipoTexto;
|
|
816
|
+
}
|
|
817
|
+
}
|
|
818
|
+
}
|
|
819
|
+
node.forEachChild(visitarSemantica);
|
|
820
|
+
};
|
|
821
|
+
visitarSemantica(exportacao.corpo);
|
|
822
|
+
adicionarBodyCampos(bodyDeclaracoesDiretas);
|
|
823
|
+
for (const bodyAlias of bodyAliases.values()) {
|
|
824
|
+
adicionarBodyCampos(bodyAlias.campos, bodyAlias.tipoTexto);
|
|
825
|
+
}
|
|
826
|
+
const statusesDeduplicados = deduplicarNumeros(statuses);
|
|
827
|
+
return {
|
|
828
|
+
query: deduplicarCampos(query),
|
|
829
|
+
body: deduplicarCampos(body),
|
|
830
|
+
bodyTipoTexto,
|
|
831
|
+
response: deduplicarCampos(response),
|
|
832
|
+
responseTipoTexto,
|
|
833
|
+
statuses: statusesDeduplicados,
|
|
834
|
+
errorStatuses: statusesDeduplicados.filter((status) => status >= 400),
|
|
835
|
+
};
|
|
836
|
+
}
|
|
837
|
+
export function extrairRotasTypeScriptHttp(sourceFile, relacaoArquivo) {
|
|
838
|
+
const rotas = [];
|
|
839
|
+
const caminhoNext = inferirCaminhoNext(relacaoArquivo);
|
|
840
|
+
if (caminhoNext) {
|
|
841
|
+
for (const metodo of extrairMetodosHttpNext(sourceFile)) {
|
|
842
|
+
rotas.push({
|
|
843
|
+
origem: "nextjs",
|
|
844
|
+
metodo,
|
|
845
|
+
caminho: caminhoNext.caminho,
|
|
846
|
+
simbolo: metodo,
|
|
847
|
+
parametros: caminhoNext.parametros,
|
|
848
|
+
});
|
|
849
|
+
}
|
|
850
|
+
}
|
|
851
|
+
rotas.push(...extrairRotasNodeWorker(sourceFile, relacaoArquivo));
|
|
852
|
+
return deduplicarRotas(rotas);
|
|
853
|
+
}
|
|
854
|
+
//# sourceMappingURL=typescript-http.js.map
|