@semacode/cli 1.5.14 → 1.5.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -1
- package/dist/index.js +76 -43
- package/package.json +26 -35
- package/semacode-cli-1.3.1.tgz +0 -0
- package/src/angular-consumer-standalone.ts +312 -0
- package/src/cpp-symbols.ts +82 -0
- package/src/docs.ts +535 -0
- package/src/dotnet-http.ts +355 -0
- package/src/drift.ts +4933 -0
- package/src/go-http.ts +118 -0
- package/src/importador.ts +3891 -0
- package/src/index.ts +5641 -0
- package/src/java-http.ts +247 -0
- package/src/lua-symbols.ts +114 -0
- package/src/php-symbols.ts +462 -0
- package/src/projeto.ts +862 -0
- package/src/python-http.ts +258 -0
- package/src/rust-http.ts +125 -0
- package/src/tipos.ts +24 -0
- package/src/typescript-http.ts +1076 -0
- package/tsconfig.json +20 -0
- package/AGENTS.md +0 -272
- package/LICENSE +0 -22
- package/SEMA_BRIEF.curto.txt +0 -9
- package/SEMA_BRIEF.md +0 -63
- package/SEMA_BRIEF.micro.txt +0 -7
- package/SEMA_INDEX.json +0 -799
- package/dist/angular-consumer-standalone.d.ts +0 -6
- package/dist/angular-consumer-standalone.js.map +0 -1
- package/dist/cpp-symbols.d.ts +0 -10
- package/dist/cpp-symbols.js.map +0 -1
- package/dist/docs.d.ts +0 -56
- package/dist/docs.js.map +0 -1
- package/dist/dotnet-http.d.ts +0 -23
- package/dist/dotnet-http.js.map +0 -1
- package/dist/drift.d.ts +0 -225
- package/dist/drift.js.map +0 -1
- package/dist/go-http.d.ts +0 -23
- package/dist/go-http.js.map +0 -1
- package/dist/importador.d.ts +0 -31
- package/dist/importador.js.map +0 -1
- package/dist/index.d.ts +0 -2
- package/dist/index.js.map +0 -1
- package/dist/java-http.d.ts +0 -23
- package/dist/java-http.js.map +0 -1
- package/dist/lua-symbols.d.ts +0 -10
- package/dist/lua-symbols.js.map +0 -1
- package/dist/php-symbols.d.ts +0 -24
- package/dist/php-symbols.js.map +0 -1
- package/dist/projeto.d.ts +0 -53
- package/dist/projeto.js.map +0 -1
- package/dist/python-http.d.ts +0 -23
- package/dist/python-http.js.map +0 -1
- package/dist/rust-http.d.ts +0 -23
- package/dist/rust-http.js.map +0 -1
- package/dist/tipos.d.ts +0 -3
- package/dist/tipos.js.map +0 -1
- package/dist/typescript-http.d.ts +0 -35
- package/dist/typescript-http.js.map +0 -1
- package/docs/AGENT_STARTER.md +0 -102
- package/docs/cli.md +0 -117
- package/docs/como-ensinar-a-sema-para-ia.md +0 -149
- package/docs/deploy.md +0 -70
- package/docs/documentacao.md +0 -63
- package/docs/env.md +0 -56
- package/docs/extensao-vscode.md +0 -45
- package/docs/fluxo-pratico-ia-sema.md +0 -177
- package/docs/instalacao-e-primeiro-uso.md +0 -112
- package/docs/integracao-com-ia.md +0 -101
- package/docs/mcp.md +0 -53
- package/docs/pagamento-ponta-a-ponta.md +0 -155
- package/docs/persistencia-vendor-first.md +0 -145
- package/docs/prompt-base-ia-sema.md +0 -104
- package/docs/rollback.md +0 -47
- package/docs/sintaxe.md +0 -410
- package/exemplos/agendamento.sema +0 -106
- package/exemplos/assinatura.sema +0 -136
- package/exemplos/auditoria.sema +0 -88
- package/exemplos/autenticacao.sema +0 -125
- package/exemplos/automacao.sema +0 -107
- package/exemplos/cadastro_usuario.sema +0 -54
- package/exemplos/calculadora.sema +0 -78
- package/exemplos/crud_simples.sema +0 -89
- package/exemplos/estoque.sema +0 -126
- package/exemplos/exportacao.sema +0 -94
- package/exemplos/fila.sema +0 -131
- package/exemplos/integracao_externa.sema +0 -94
- package/exemplos/multi_tenant.sema +0 -140
- package/exemplos/notificacao.sema +0 -98
- package/exemplos/operacao_estrategia.sema +0 -402
- package/exemplos/pagamento.sema +0 -222
- package/exemplos/pagamento_dominio.sema +0 -35
- package/exemplos/pedido.sema +0 -119
- package/exemplos/permissao.sema +0 -121
- package/exemplos/persistencia_vendor_first.sema +0 -86
- package/exemplos/relatorio.sema +0 -93
- package/exemplos/testes_embutidos.sema +0 -45
- package/exemplos/tratamento_erro.sema +0 -157
- package/exemplos/upload_arquivo.sema +0 -93
- package/exemplos/webhook.sema +0 -96
- package/llms-full.txt +0 -34
- package/llms.txt +0 -17
- package/node_modules/@sema/gerador-css/dist/index.d.ts +0 -3
- package/node_modules/@sema/gerador-css/dist/index.js +0 -592
- package/node_modules/@sema/gerador-css/dist/index.js.map +0 -1
- package/node_modules/@sema/gerador-css/package.json +0 -7
- package/node_modules/@sema/gerador-dart/dist/index.d.ts +0 -3
- package/node_modules/@sema/gerador-dart/dist/index.js +0 -44
- package/node_modules/@sema/gerador-dart/dist/index.js.map +0 -1
- package/node_modules/@sema/gerador-dart/package.json +0 -7
- package/node_modules/@sema/gerador-html/dist/index.d.ts +0 -3
- package/node_modules/@sema/gerador-html/dist/index.js +0 -163
- package/node_modules/@sema/gerador-html/dist/index.js.map +0 -1
- package/node_modules/@sema/gerador-html/package.json +0 -7
- package/node_modules/@sema/gerador-javascript/dist/index.d.ts +0 -3
- package/node_modules/@sema/gerador-javascript/dist/index.js +0 -421
- package/node_modules/@sema/gerador-javascript/dist/index.js.map +0 -1
- package/node_modules/@sema/gerador-javascript/package.json +0 -7
- package/node_modules/@sema/gerador-lua/dist/index.d.ts +0 -3
- package/node_modules/@sema/gerador-lua/dist/index.js +0 -328
- package/node_modules/@sema/gerador-lua/dist/index.js.map +0 -1
- package/node_modules/@sema/gerador-lua/package.json +0 -7
- package/node_modules/@sema/gerador-python/dist/index.d.ts +0 -6
- package/node_modules/@sema/gerador-python/dist/index.js +0 -729
- package/node_modules/@sema/gerador-python/dist/index.js.map +0 -1
- package/node_modules/@sema/gerador-python/package.json +0 -7
- package/node_modules/@sema/gerador-typescript/dist/index.d.ts +0 -6
- package/node_modules/@sema/gerador-typescript/dist/index.js +0 -793
- package/node_modules/@sema/gerador-typescript/dist/index.js.map +0 -1
- package/node_modules/@sema/gerador-typescript/package.json +0 -7
- package/node_modules/@sema/nucleo/dist/ast/tipos.d.ts +0 -125
- package/node_modules/@sema/nucleo/dist/ast/tipos.js +0 -2
- package/node_modules/@sema/nucleo/dist/ast/tipos.js.map +0 -1
- package/node_modules/@sema/nucleo/dist/diagnosticos/index.d.ts +0 -21
- package/node_modules/@sema/nucleo/dist/diagnosticos/index.js +0 -12
- package/node_modules/@sema/nucleo/dist/diagnosticos/index.js.map +0 -1
- package/node_modules/@sema/nucleo/dist/formatador/index.d.ts +0 -9
- package/node_modules/@sema/nucleo/dist/formatador/index.js +0 -487
- package/node_modules/@sema/nucleo/dist/formatador/index.js.map +0 -1
- package/node_modules/@sema/nucleo/dist/index.d.ts +0 -35
- package/node_modules/@sema/nucleo/dist/index.js +0 -96
- package/node_modules/@sema/nucleo/dist/index.js.map +0 -1
- package/node_modules/@sema/nucleo/dist/ir/conversor.d.ts +0 -5
- package/node_modules/@sema/nucleo/dist/ir/conversor.js +0 -1058
- package/node_modules/@sema/nucleo/dist/ir/conversor.js.map +0 -1
- package/node_modules/@sema/nucleo/dist/ir/modelos.d.ts +0 -377
- package/node_modules/@sema/nucleo/dist/ir/modelos.js +0 -2
- package/node_modules/@sema/nucleo/dist/ir/modelos.js.map +0 -1
- package/node_modules/@sema/nucleo/dist/lexer/lexer.d.ts +0 -7
- package/node_modules/@sema/nucleo/dist/lexer/lexer.js +0 -122
- package/node_modules/@sema/nucleo/dist/lexer/lexer.js.map +0 -1
- package/node_modules/@sema/nucleo/dist/lexer/tokens.d.ts +0 -8
- package/node_modules/@sema/nucleo/dist/lexer/tokens.js +0 -82
- package/node_modules/@sema/nucleo/dist/lexer/tokens.js.map +0 -1
- package/node_modules/@sema/nucleo/dist/parser/parser.d.ts +0 -9
- package/node_modules/@sema/nucleo/dist/parser/parser.js +0 -807
- package/node_modules/@sema/nucleo/dist/parser/parser.js.map +0 -1
- package/node_modules/@sema/nucleo/dist/persistencia/contratos.d.ts +0 -39
- package/node_modules/@sema/nucleo/dist/persistencia/contratos.js +0 -294
- package/node_modules/@sema/nucleo/dist/persistencia/contratos.js.map +0 -1
- package/node_modules/@sema/nucleo/dist/semantico/analisador.d.ts +0 -58
- package/node_modules/@sema/nucleo/dist/semantico/analisador.js +0 -1912
- package/node_modules/@sema/nucleo/dist/semantico/analisador.js.map +0 -1
- package/node_modules/@sema/nucleo/dist/semantico/estruturas.d.ts +0 -104
- package/node_modules/@sema/nucleo/dist/semantico/estruturas.js +0 -445
- package/node_modules/@sema/nucleo/dist/semantico/estruturas.js.map +0 -1
- package/node_modules/@sema/nucleo/dist/semantico/seguranca.d.ts +0 -91
- package/node_modules/@sema/nucleo/dist/semantico/seguranca.js +0 -258
- package/node_modules/@sema/nucleo/dist/semantico/seguranca.js.map +0 -1
- package/node_modules/@sema/nucleo/dist/util/arquivos.d.ts +0 -2
- package/node_modules/@sema/nucleo/dist/util/arquivos.js +0 -25
- package/node_modules/@sema/nucleo/dist/util/arquivos.js.map +0 -1
- package/node_modules/@sema/nucleo/package.json +0 -7
- package/node_modules/@sema/padroes/dist/index.d.ts +0 -25
- package/node_modules/@sema/padroes/dist/index.js +0 -316
- package/node_modules/@sema/padroes/dist/index.js.map +0 -1
- package/node_modules/@sema/padroes/package.json +0 -7
|
@@ -0,0 +1,462 @@
|
|
|
1
|
+
export interface ParametroPhpExtraido {
|
|
2
|
+
nome: string;
|
|
3
|
+
obrigatorio: boolean;
|
|
4
|
+
tipoTexto?: string;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export interface ParametroRotaPhp {
|
|
8
|
+
nome: string;
|
|
9
|
+
tipoSema: "Texto" | "Inteiro" | "Decimal" | "Id";
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface SimboloPhpExtraido {
|
|
13
|
+
simbolo: string;
|
|
14
|
+
retorno?: string;
|
|
15
|
+
parametros: ParametroPhpExtraido[];
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface RotaPhpExtraida {
|
|
19
|
+
origem: "php";
|
|
20
|
+
metodo: string;
|
|
21
|
+
caminho: string;
|
|
22
|
+
simbolo: string;
|
|
23
|
+
parametros: ParametroRotaPhp[];
|
|
24
|
+
retorno?: string;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const METODOS_HTTP = new Set(["GET", "POST", "PUT", "PATCH", "DELETE"]);
|
|
28
|
+
const METODOS_RECURSO_LARAVEL: Array<{ metodo: string; sufixo: string; acao: string }> = [
|
|
29
|
+
{ metodo: "GET", sufixo: "", acao: "index" },
|
|
30
|
+
{ metodo: "POST", sufixo: "", acao: "store" },
|
|
31
|
+
{ metodo: "GET", sufixo: "/{id}", acao: "show" },
|
|
32
|
+
{ metodo: "PUT", sufixo: "/{id}", acao: "update" },
|
|
33
|
+
{ metodo: "PATCH", sufixo: "/{id}", acao: "update" },
|
|
34
|
+
{ metodo: "DELETE", sufixo: "/{id}", acao: "destroy" },
|
|
35
|
+
];
|
|
36
|
+
|
|
37
|
+
function removerComentariosPhp(codigo: string): string {
|
|
38
|
+
return codigo
|
|
39
|
+
.replace(/<\?(?:php)?|(\?>)/gi, "")
|
|
40
|
+
.replace(/\/\*[\s\S]*?\*\//g, "")
|
|
41
|
+
.replace(/(^|[^\:])\/\/[^\r\n]*/g, "$1")
|
|
42
|
+
.replace(/#[^\[\r\n][^\r\n]*/g, "");
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function contarChar(texto: string, alvo: string): number {
|
|
46
|
+
return [...texto].filter((char) => char === alvo).length;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function normalizarNamespacePhp(valor?: string): string {
|
|
50
|
+
return (valor ?? "")
|
|
51
|
+
.replace(/^\\+|\\+$/g, "")
|
|
52
|
+
.replace(/\\/g, ".")
|
|
53
|
+
.replace(/::/g, ".")
|
|
54
|
+
.replace(/\.+/g, ".")
|
|
55
|
+
.replace(/^\.+|\.+$/g, "");
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function normalizarSimboloPhp(valor: string): string {
|
|
59
|
+
return normalizarNamespacePhp(valor)
|
|
60
|
+
.replace(/\$\b/g, "")
|
|
61
|
+
.replace(/\.+/g, ".")
|
|
62
|
+
.replace(/^\.+|\.+$/g, "");
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function dividirListaUsePhp(declaracao: string): string[] {
|
|
66
|
+
const partes: string[] = [];
|
|
67
|
+
let atual = "";
|
|
68
|
+
let profundidade = 0;
|
|
69
|
+
for (const caractere of declaracao) {
|
|
70
|
+
if (caractere === "," && profundidade === 0) {
|
|
71
|
+
if (atual.trim()) {
|
|
72
|
+
partes.push(atual.trim());
|
|
73
|
+
}
|
|
74
|
+
atual = "";
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
if (caractere === "{") {
|
|
78
|
+
profundidade += 1;
|
|
79
|
+
} else if (caractere === "}" && profundidade > 0) {
|
|
80
|
+
profundidade -= 1;
|
|
81
|
+
}
|
|
82
|
+
atual += caractere;
|
|
83
|
+
}
|
|
84
|
+
if (atual.trim()) {
|
|
85
|
+
partes.push(atual.trim());
|
|
86
|
+
}
|
|
87
|
+
return partes;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function registrarUsePhp(imports: Map<string, string>, item: string): void {
|
|
91
|
+
const alias = item.match(/\s+as\s+([A-Za-z_]\w*)$/i)?.[1];
|
|
92
|
+
const semAlias = item.replace(/\s+as\s+[A-Za-z_]\w*$/i, "").trim();
|
|
93
|
+
const completo = normalizarNamespacePhp(semAlias);
|
|
94
|
+
const nome = alias ?? completo.split(".").at(-1);
|
|
95
|
+
if (nome && completo) {
|
|
96
|
+
imports.set(nome, completo);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function extrairImportsPhp(codigo: string): Map<string, string> {
|
|
101
|
+
const imports = new Map<string, string>();
|
|
102
|
+
for (const match of codigo.matchAll(/\buse\s+(?!function\b|const\b)([^;]+);/gi)) {
|
|
103
|
+
const declaracao = match[1]!.trim();
|
|
104
|
+
const grupo = declaracao.match(/^(.+?)\\\{(.+)\}$/);
|
|
105
|
+
if (grupo) {
|
|
106
|
+
const base = grupo[1]!;
|
|
107
|
+
for (const item of dividirListaUsePhp(grupo[2]!)) {
|
|
108
|
+
registrarUsePhp(imports, `${base}\\${item}`);
|
|
109
|
+
}
|
|
110
|
+
continue;
|
|
111
|
+
}
|
|
112
|
+
for (const item of dividirListaUsePhp(declaracao)) {
|
|
113
|
+
registrarUsePhp(imports, item);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
return imports;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function resolverClassePhp(classe: string, imports: Map<string, string>): string {
|
|
120
|
+
const normalizada = normalizarNamespacePhp(classe);
|
|
121
|
+
const partes = normalizada.split(".").filter(Boolean);
|
|
122
|
+
const primeiro = partes[0];
|
|
123
|
+
if (primeiro && imports.has(primeiro)) {
|
|
124
|
+
return [imports.get(primeiro), ...partes.slice(1)].filter(Boolean).join(".");
|
|
125
|
+
}
|
|
126
|
+
return normalizada;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function juntarSimbolo(...partes: Array<string | undefined>): string {
|
|
130
|
+
return partes.map(normalizarNamespacePhp).filter(Boolean).join(".");
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function normalizarCaminhoPhp(caminho: string): string {
|
|
134
|
+
const semAspas = caminho.trim().replace(/^['"]|['"]$/g, "");
|
|
135
|
+
return `/${semAspas.replace(/^\/+|\/+$/g, "")}`
|
|
136
|
+
.replace(/:([A-Za-z_]\w*)/g, "{$1}")
|
|
137
|
+
.replace(/\{([A-Za-z_]\w*)\??\}/g, "{$1}")
|
|
138
|
+
.replace(/\/+/g, "/");
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function juntarCaminho(base: string | undefined, sufixo: string | undefined): string {
|
|
142
|
+
const partes = [base, sufixo]
|
|
143
|
+
.map((parte) => parte?.trim())
|
|
144
|
+
.filter((parte): parte is string => Boolean(parte))
|
|
145
|
+
.map((parte) => parte.replace(/^\/+|\/+$/g, ""));
|
|
146
|
+
return normalizarCaminhoPhp(partes.join("/"));
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function mapearTipoRotaPhp(tipo?: string): ParametroRotaPhp["tipoSema"] {
|
|
150
|
+
const normalizado = (tipo ?? "").replace(/^\?/, "").replace(/^\\+/, "").toLowerCase();
|
|
151
|
+
if (/^(int|integer)$/.test(normalizado)) {
|
|
152
|
+
return "Inteiro";
|
|
153
|
+
}
|
|
154
|
+
if (/^(float|double|decimal)$/.test(normalizado)) {
|
|
155
|
+
return "Decimal";
|
|
156
|
+
}
|
|
157
|
+
if (/uuid|id$/.test(normalizado)) {
|
|
158
|
+
return "Id";
|
|
159
|
+
}
|
|
160
|
+
return "Texto";
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function dividirParametrosPhp(parametros: string): ParametroPhpExtraido[] {
|
|
164
|
+
const encontrados: ParametroPhpExtraido[] = [];
|
|
165
|
+
|
|
166
|
+
for (const parametro of parametros.split(",")) {
|
|
167
|
+
const item = parametro.trim();
|
|
168
|
+
if (!item) {
|
|
169
|
+
continue;
|
|
170
|
+
}
|
|
171
|
+
const normalizado = item
|
|
172
|
+
.replace(/#\[[^\]]+\]\s*/g, "")
|
|
173
|
+
.replace(/=.*$/, "")
|
|
174
|
+
.replace(/\b(?:public|protected|private|readonly)\s+/g, "")
|
|
175
|
+
.replace(/&/g, "")
|
|
176
|
+
.replace(/\.\.\./g, "")
|
|
177
|
+
.trim();
|
|
178
|
+
const match = normalizado.match(/^(?:(?<tipo>[?\\A-Za-z_][\\A-Za-z0-9_|.[\]?]*(?:\s*\|\s*[?\\A-Za-z_][\\A-Za-z0-9_|.[\]?]*)*)\s+)?\$(?<nome>[A-Za-z_]\w*)$/);
|
|
179
|
+
if (!match?.groups?.["nome"]) {
|
|
180
|
+
continue;
|
|
181
|
+
}
|
|
182
|
+
const tipoTexto = match.groups["tipo"]?.replace(/\s+/g, "") || undefined;
|
|
183
|
+
encontrados.push({
|
|
184
|
+
nome: match.groups["nome"],
|
|
185
|
+
...(tipoTexto ? { tipoTexto } : {}),
|
|
186
|
+
obrigatorio: !tipoTexto?.startsWith("?"),
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
return encontrados;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
function extrairParametrosRotaPhp(caminho: string, parametros: ParametroPhpExtraido[] = []): ParametroRotaPhp[] {
|
|
194
|
+
const tipos = new Map(parametros.map((parametro) => [parametro.nome, parametro.tipoTexto] as const));
|
|
195
|
+
return [...normalizarCaminhoPhp(caminho).matchAll(/\{([^}]+)\}/g)].map((match) => {
|
|
196
|
+
const nome = match[1]!;
|
|
197
|
+
return {
|
|
198
|
+
nome,
|
|
199
|
+
tipoSema: mapearTipoRotaPhp(tipos.get(nome)),
|
|
200
|
+
};
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
function extrairAtributosPhp(linhas: string[], inicio: number): { atributos: string[]; proximoIndice: number } {
|
|
205
|
+
const atributos: string[] = [];
|
|
206
|
+
let indice = inicio;
|
|
207
|
+
while (indice < linhas.length) {
|
|
208
|
+
const linha = linhas[indice]!.trim();
|
|
209
|
+
if (!linha.startsWith("#[")) {
|
|
210
|
+
break;
|
|
211
|
+
}
|
|
212
|
+
let atual = linha;
|
|
213
|
+
let saldo = contarChar(linha, "[") - contarChar(linha, "]");
|
|
214
|
+
while (saldo > 0 && indice + 1 < linhas.length) {
|
|
215
|
+
indice += 1;
|
|
216
|
+
const complemento = linhas[indice]!.trim();
|
|
217
|
+
atual += ` ${complemento}`;
|
|
218
|
+
saldo += contarChar(complemento, "[") - contarChar(complemento, "]");
|
|
219
|
+
}
|
|
220
|
+
atributos.push(atual);
|
|
221
|
+
indice += 1;
|
|
222
|
+
}
|
|
223
|
+
return { atributos, proximoIndice: indice };
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
function extrairCaminhoAtributoPhp(atributo: string): string | undefined {
|
|
227
|
+
return atributo.match(/(?:path\s*:\s*)?["']([^"']+)["']/i)?.[1];
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
function extrairMetodosAtributoPhp(atributo: string): string[] {
|
|
231
|
+
const direto = atributo.match(/#\[\s*(?:[A-Za-z_\\][A-Za-z0-9_\\]*\\)?(Get|Post|Put|Patch|Delete)\b/i)?.[1]?.toUpperCase();
|
|
232
|
+
if (direto && METODOS_HTTP.has(direto)) {
|
|
233
|
+
return [direto];
|
|
234
|
+
}
|
|
235
|
+
const encontrados = [...atributo.matchAll(/["'](GET|POST|PUT|PATCH|DELETE)["']/gi)]
|
|
236
|
+
.map((match) => match[1]!.toUpperCase())
|
|
237
|
+
.filter((metodo) => METODOS_HTTP.has(metodo));
|
|
238
|
+
return encontrados.length > 0 ? [...new Set(encontrados)] : [];
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
function registrarSimbolo(
|
|
242
|
+
simbolos: Map<string, SimboloPhpExtraido>,
|
|
243
|
+
simbolo: string,
|
|
244
|
+
parametros: string,
|
|
245
|
+
retorno?: string,
|
|
246
|
+
): void {
|
|
247
|
+
const normalizado = normalizarSimboloPhp(simbolo);
|
|
248
|
+
if (!normalizado) {
|
|
249
|
+
return;
|
|
250
|
+
}
|
|
251
|
+
simbolos.set(normalizado, {
|
|
252
|
+
simbolo: normalizado,
|
|
253
|
+
retorno: retorno?.replace(/[;{].*$/, "").trim() || undefined,
|
|
254
|
+
parametros: dividirParametrosPhp(parametros),
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
function extrairSimboloHandlerPhp(handler: string, imports = new Map<string, string>()): string | undefined {
|
|
259
|
+
const arrayClass = handler.match(/\[\s*([A-Za-z_\\][A-Za-z0-9_\\]*)::class\s*,\s*["']([A-Za-z_]\w*)["']\s*\]/);
|
|
260
|
+
if (arrayClass) {
|
|
261
|
+
return normalizarSimboloPhp(`${resolverClassePhp(arrayClass[1]!, imports)}.${arrayClass[2]!}`);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
const stringController = handler.match(/["']([A-Za-z_\\][A-Za-z0-9_\\]*)@([A-Za-z_]\w*)["']/);
|
|
265
|
+
if (stringController) {
|
|
266
|
+
return normalizarSimboloPhp(`${resolverClassePhp(stringController[1]!, imports)}.${stringController[2]!}`);
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
const invokable = handler.match(/([A-Za-z_\\][A-Za-z0-9_\\]*)::class/);
|
|
270
|
+
if (invokable) {
|
|
271
|
+
return normalizarSimboloPhp(`${resolverClassePhp(invokable[1]!, imports)}.__invoke`);
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
const callable = handler.match(/([A-Za-z_\\][A-Za-z0-9_\\]*)::([A-Za-z_]\w*)/);
|
|
275
|
+
if (callable) {
|
|
276
|
+
return normalizarSimboloPhp(`${resolverClassePhp(callable[1]!, imports)}.${callable[2]!}`);
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
return undefined;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
function registrarRota(
|
|
283
|
+
rotas: Map<string, RotaPhpExtraida>,
|
|
284
|
+
metodo: string,
|
|
285
|
+
caminho: string,
|
|
286
|
+
simbolo: string,
|
|
287
|
+
parametros: ParametroPhpExtraido[] = [],
|
|
288
|
+
retorno?: string,
|
|
289
|
+
): void {
|
|
290
|
+
const metodoNormalizado = metodo.toUpperCase();
|
|
291
|
+
if (!METODOS_HTTP.has(metodoNormalizado)) {
|
|
292
|
+
return;
|
|
293
|
+
}
|
|
294
|
+
const caminhoNormalizado = normalizarCaminhoPhp(caminho);
|
|
295
|
+
const simboloNormalizado = normalizarSimboloPhp(simbolo);
|
|
296
|
+
if (!simboloNormalizado) {
|
|
297
|
+
return;
|
|
298
|
+
}
|
|
299
|
+
rotas.set(`${metodoNormalizado}:${caminhoNormalizado}:${simboloNormalizado}`, {
|
|
300
|
+
origem: "php",
|
|
301
|
+
metodo: metodoNormalizado,
|
|
302
|
+
caminho: caminhoNormalizado,
|
|
303
|
+
simbolo: simboloNormalizado,
|
|
304
|
+
parametros: extrairParametrosRotaPhp(caminhoNormalizado, parametros),
|
|
305
|
+
retorno,
|
|
306
|
+
});
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
export function extrairSimbolosPhp(codigo: string): SimboloPhpExtraido[] {
|
|
310
|
+
const simbolos = new Map<string, SimboloPhpExtraido>();
|
|
311
|
+
const linhas = removerComentariosPhp(codigo).split(/\r?\n/);
|
|
312
|
+
const pilhaClasses: Array<{ nome: string; profundidade: number }> = [];
|
|
313
|
+
let classePendente: { nome: string; profundidade: number } | undefined;
|
|
314
|
+
let namespaceAtual = "";
|
|
315
|
+
let profundidade = 0;
|
|
316
|
+
|
|
317
|
+
for (let indice = 0; indice < linhas.length; indice += 1) {
|
|
318
|
+
const linhaOriginal = linhas[indice]!;
|
|
319
|
+
const linhaTrim = linhaOriginal.trim();
|
|
320
|
+
const namespace = linhaTrim.match(/^namespace\s+([^;{]+)[;{]/);
|
|
321
|
+
if (namespace) {
|
|
322
|
+
namespaceAtual = normalizarNamespacePhp(namespace[1]);
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
if (classePendente && linhaTrim.startsWith("{")) {
|
|
326
|
+
pilhaClasses.push(classePendente);
|
|
327
|
+
classePendente = undefined;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
const { atributos, proximoIndice } = extrairAtributosPhp(linhas, indice);
|
|
331
|
+
if (atributos.length > 0) {
|
|
332
|
+
indice = proximoIndice;
|
|
333
|
+
}
|
|
334
|
+
const linha = (linhas[indice] ?? linhaOriginal).trim();
|
|
335
|
+
|
|
336
|
+
const classe = linha.match(/\b(?:abstract\s+|final\s+)?(?:class|trait|interface|enum)\s+([A-Za-z_]\w*)/);
|
|
337
|
+
if (classe) {
|
|
338
|
+
const entrada = { nome: classe[1]!, profundidade: profundidade + 1 };
|
|
339
|
+
if (linha.includes("{")) {
|
|
340
|
+
pilhaClasses.push(entrada);
|
|
341
|
+
} else {
|
|
342
|
+
classePendente = entrada;
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
const metodo = linha.match(/\b(?:public|protected|private)?\s*(?:static\s+)?function\s+([A-Za-z_]\w*)\s*\(([^)]*)\)\s*(?::\s*([^;{]+))?/);
|
|
347
|
+
if (metodo) {
|
|
348
|
+
const classeAtual = pilhaClasses[pilhaClasses.length - 1]?.nome;
|
|
349
|
+
registrarSimbolo(
|
|
350
|
+
simbolos,
|
|
351
|
+
juntarSimbolo(namespaceAtual, classeAtual, metodo[1]),
|
|
352
|
+
metodo[2] ?? "",
|
|
353
|
+
metodo[3],
|
|
354
|
+
);
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
for (const closure of linha.matchAll(/\$([A-Za-z_]\w*)\s*=\s*(?:static\s+)?function\s*\(([^)]*)\)\s*(?::\s*([^;{]+))?/g)) {
|
|
358
|
+
registrarSimbolo(simbolos, juntarSimbolo(namespaceAtual, closure[1]), closure[2] ?? "", closure[3]);
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
profundidade += contarChar(linha, "{") - contarChar(linha, "}");
|
|
362
|
+
while (pilhaClasses.length > 0 && profundidade < pilhaClasses[pilhaClasses.length - 1]!.profundidade) {
|
|
363
|
+
pilhaClasses.pop();
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
return [...simbolos.values()].sort((a, b) => a.simbolo.localeCompare(b.simbolo, "pt-BR"));
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
export function extrairRotasPhp(codigo: string): RotaPhpExtraida[] {
|
|
371
|
+
const rotas = new Map<string, RotaPhpExtraida>();
|
|
372
|
+
const texto = removerComentariosPhp(codigo);
|
|
373
|
+
const imports = extrairImportsPhp(texto);
|
|
374
|
+
const linhas = texto.split(/\r?\n/);
|
|
375
|
+
const pilhaClasses: Array<{ nome: string; profundidade: number; caminhoBase?: string }> = [];
|
|
376
|
+
let classePendente: { nome: string; profundidade: number; caminhoBase?: string } | undefined;
|
|
377
|
+
let namespaceAtual = "";
|
|
378
|
+
let profundidade = 0;
|
|
379
|
+
|
|
380
|
+
for (const match of texto.matchAll(/\bRoute::(get|post|put|patch|delete)\s*\(\s*["']([^"']+)["']\s*,\s*([\s\S]*?)\)\s*;/gi)) {
|
|
381
|
+
const simbolo = extrairSimboloHandlerPhp(match[3] ?? "", imports);
|
|
382
|
+
if (simbolo) {
|
|
383
|
+
registrarRota(rotas, match[1]!, match[2]!, simbolo);
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
for (const match of texto.matchAll(/\$[A-Za-z_]\w*->(get|post|put|patch|delete)\s*\(\s*["']([^"']+)["']\s*,\s*([\s\S]*?)\)\s*;/gi)) {
|
|
388
|
+
const simbolo = extrairSimboloHandlerPhp(match[3] ?? "", imports);
|
|
389
|
+
if (simbolo) {
|
|
390
|
+
registrarRota(rotas, match[1]!, match[2]!, simbolo);
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
for (const match of texto.matchAll(/\bRoute::(?:apiResource|resource)\s*\(\s*["']([^"']+)["']\s*,\s*([A-Za-z_\\][A-Za-z0-9_\\]*)::class/gi)) {
|
|
395
|
+
const recurso = match[1]!;
|
|
396
|
+
const classe = resolverClassePhp(match[2]!, imports);
|
|
397
|
+
for (const rota of METODOS_RECURSO_LARAVEL) {
|
|
398
|
+
registrarRota(rotas, rota.metodo, juntarCaminho(recurso, rota.sufixo), `${classe}.${rota.acao}`);
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
for (let indice = 0; indice < linhas.length; indice += 1) {
|
|
403
|
+
const linhaOriginal = linhas[indice]!;
|
|
404
|
+
const linhaTrim = linhaOriginal.trim();
|
|
405
|
+
const namespace = linhaTrim.match(/^namespace\s+([^;{]+)[;{]/);
|
|
406
|
+
if (namespace) {
|
|
407
|
+
namespaceAtual = normalizarNamespacePhp(namespace[1]);
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
if (classePendente && linhaTrim.startsWith("{")) {
|
|
411
|
+
pilhaClasses.push(classePendente);
|
|
412
|
+
classePendente = undefined;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
const { atributos, proximoIndice } = extrairAtributosPhp(linhas, indice);
|
|
416
|
+
if (atributos.length > 0) {
|
|
417
|
+
indice = proximoIndice;
|
|
418
|
+
}
|
|
419
|
+
const linha = (linhas[indice] ?? linhaOriginal).trim();
|
|
420
|
+
const caminhoClasse = atributos.find((atributo) => /#\[\s*(?:[A-Za-z_\\][A-Za-z0-9_\\]*\\)?Route\b/i.test(atributo))
|
|
421
|
+
? extrairCaminhoAtributoPhp(atributos.find((atributo) => /#\[\s*(?:[A-Za-z_\\][A-Za-z0-9_\\]*\\)?Route\b/i.test(atributo))!)
|
|
422
|
+
: undefined;
|
|
423
|
+
|
|
424
|
+
const classe = linha.match(/\b(?:abstract\s+|final\s+)?(?:class|trait|interface|enum)\s+([A-Za-z_]\w*)/);
|
|
425
|
+
if (classe) {
|
|
426
|
+
const entrada = { nome: classe[1]!, profundidade: profundidade + 1, caminhoBase: caminhoClasse };
|
|
427
|
+
if (linha.includes("{")) {
|
|
428
|
+
pilhaClasses.push(entrada);
|
|
429
|
+
} else {
|
|
430
|
+
classePendente = entrada;
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
const metodo = linha.match(/\b(?:public|protected|private)?\s*(?:static\s+)?function\s+([A-Za-z_]\w*)\s*\(([^)]*)\)\s*(?::\s*([^;{]+))?/);
|
|
435
|
+
if (metodo) {
|
|
436
|
+
const classeAtual = pilhaClasses[pilhaClasses.length - 1];
|
|
437
|
+
const atributosRota = atributos.filter((atributo) =>
|
|
438
|
+
/#\[\s*(?:[A-Za-z_\\][A-Za-z0-9_\\]*\\)?(?:Route|Get|Post|Put|Patch|Delete)\b/i.test(atributo));
|
|
439
|
+
for (const atributo of atributosRota) {
|
|
440
|
+
const caminho = extrairCaminhoAtributoPhp(atributo);
|
|
441
|
+
if (!caminho) {
|
|
442
|
+
continue;
|
|
443
|
+
}
|
|
444
|
+
const metodos = extrairMetodosAtributoPhp(atributo);
|
|
445
|
+
const simbolo = juntarSimbolo(namespaceAtual, classeAtual?.nome, metodo[1]);
|
|
446
|
+
for (const metodoHttp of metodos.length > 0 ? metodos : ["GET"]) {
|
|
447
|
+
registrarRota(rotas, metodoHttp, juntarCaminho(classeAtual?.caminhoBase, caminho), simbolo, dividirParametrosPhp(metodo[2] ?? ""), metodo[3]);
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
profundidade += contarChar(linha, "{") - contarChar(linha, "}");
|
|
453
|
+
while (pilhaClasses.length > 0 && profundidade < pilhaClasses[pilhaClasses.length - 1]!.profundidade) {
|
|
454
|
+
pilhaClasses.pop();
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
return [...rotas.values()].sort((a, b) =>
|
|
459
|
+
a.caminho.localeCompare(b.caminho, "pt-BR")
|
|
460
|
+
|| a.metodo.localeCompare(b.metodo, "pt-BR")
|
|
461
|
+
|| a.simbolo.localeCompare(b.simbolo, "pt-BR"));
|
|
462
|
+
}
|