@semacode/cli 1.5.11 → 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 +6 -1
- package/dist/index.js +446 -32
- package/package.json +23 -32
- 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 -110
- 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 -244
- 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 -123
- 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 -460
- 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 -883
- package/node_modules/@sema/nucleo/dist/ir/conversor.js.map +0 -1
- package/node_modules/@sema/nucleo/dist/ir/modelos.d.ts +0 -345
- 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 -61
- 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 -771
- 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 -57
- package/node_modules/@sema/nucleo/dist/semantico/analisador.js +0 -1617
- 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
package/src/projeto.ts
ADDED
|
@@ -0,0 +1,862 @@
|
|
|
1
|
+
import { stat, readFile, readdir } from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import {
|
|
4
|
+
compilarProjeto,
|
|
5
|
+
lerArquivoTexto,
|
|
6
|
+
listarArquivosSema,
|
|
7
|
+
type EngineBanco,
|
|
8
|
+
type ResultadoCompilacaoProjetoModulo,
|
|
9
|
+
} from "@sema/nucleo";
|
|
10
|
+
import type { EstruturaSaida, FonteLegado, ModoAdocao } from "./tipos.js";
|
|
11
|
+
import type { AlvoGeracao, FrameworkGeracao } from "@sema/padroes";
|
|
12
|
+
|
|
13
|
+
export interface ConfiguracaoPersistenciaProjeto {
|
|
14
|
+
enginesHabilitados?: EngineBanco[];
|
|
15
|
+
adaptersPorEngine?: Partial<Record<EngineBanco, string>>;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface SemaConfigProjeto {
|
|
19
|
+
origem?: string;
|
|
20
|
+
origens?: string[];
|
|
21
|
+
saida?: string;
|
|
22
|
+
alvos?: AlvoGeracao[];
|
|
23
|
+
alvoPadrao?: AlvoGeracao;
|
|
24
|
+
modoEstrito?: boolean;
|
|
25
|
+
estruturaSaida?: EstruturaSaida;
|
|
26
|
+
framework?: FrameworkGeracao;
|
|
27
|
+
diretoriosSaidaPorAlvo?: Partial<Record<AlvoGeracao, string>>;
|
|
28
|
+
convencoesGeracaoPorProjeto?: "base" | "backend";
|
|
29
|
+
diretoriosCodigo?: string[];
|
|
30
|
+
fontesLegado?: FonteLegado[];
|
|
31
|
+
modoAdocao?: ModoAdocao;
|
|
32
|
+
persistencia?: ConfiguracaoPersistenciaProjeto;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export interface ConfiguracaoProjetoCarregada {
|
|
36
|
+
caminho: string;
|
|
37
|
+
baseDiretorio: string;
|
|
38
|
+
config: SemaConfigProjeto;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export interface ContextoProjetoCarregado {
|
|
42
|
+
entradaResolvida: string;
|
|
43
|
+
baseProjeto: string;
|
|
44
|
+
configCarregada?: ConfiguracaoProjetoCarregada;
|
|
45
|
+
arquivosProjeto: string[];
|
|
46
|
+
origensProjeto: string[];
|
|
47
|
+
diretoriosCodigo: string[];
|
|
48
|
+
fontesLegado: FonteLegado[];
|
|
49
|
+
modoAdocao: ModoAdocao;
|
|
50
|
+
modulosSelecionados: Array<{
|
|
51
|
+
caminho: string;
|
|
52
|
+
codigo: string;
|
|
53
|
+
resultado: ResultadoCompilacaoProjetoModulo;
|
|
54
|
+
}>;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
async function caminhoExiste(caminhoAlvo: string): Promise<boolean> {
|
|
58
|
+
try {
|
|
59
|
+
await stat(caminhoAlvo);
|
|
60
|
+
return true;
|
|
61
|
+
} catch {
|
|
62
|
+
return false;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export async function localizarConfiguracaoProjeto(entradaInicial: string): Promise<string | undefined> {
|
|
67
|
+
let atual = path.resolve(entradaInicial);
|
|
68
|
+
try {
|
|
69
|
+
const info = await stat(atual);
|
|
70
|
+
if (info.isFile()) {
|
|
71
|
+
atual = path.dirname(atual);
|
|
72
|
+
}
|
|
73
|
+
} catch {
|
|
74
|
+
atual = path.dirname(atual);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
for (;;) {
|
|
78
|
+
const candidato = path.join(atual, "sema.config.json");
|
|
79
|
+
if (await caminhoExiste(candidato)) {
|
|
80
|
+
return candidato;
|
|
81
|
+
}
|
|
82
|
+
const pai = path.dirname(atual);
|
|
83
|
+
if (pai === atual) {
|
|
84
|
+
return undefined;
|
|
85
|
+
}
|
|
86
|
+
atual = pai;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export async function carregarConfiguracaoProjeto(entradaInicial: string): Promise<ConfiguracaoProjetoCarregada | undefined> {
|
|
91
|
+
const caminhoConfig = await localizarConfiguracaoProjeto(entradaInicial);
|
|
92
|
+
if (!caminhoConfig) {
|
|
93
|
+
return undefined;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const conteudo = await readFile(caminhoConfig, "utf8");
|
|
97
|
+
const config = JSON.parse(conteudo) as SemaConfigProjeto;
|
|
98
|
+
return {
|
|
99
|
+
caminho: caminhoConfig,
|
|
100
|
+
baseDiretorio: path.dirname(caminhoConfig),
|
|
101
|
+
config,
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export function normalizarEstruturaSaida(valor?: string): EstruturaSaida {
|
|
106
|
+
if (valor === "modulos" || valor === "backend") {
|
|
107
|
+
return valor;
|
|
108
|
+
}
|
|
109
|
+
return "flat";
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export function normalizarFrameworkGeracao(valor?: string): FrameworkGeracao {
|
|
113
|
+
if (valor === "nestjs" || valor === "fastapi") {
|
|
114
|
+
return valor;
|
|
115
|
+
}
|
|
116
|
+
return "base";
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function normalizarAlvo(valor?: string): AlvoGeracao | undefined {
|
|
120
|
+
if (valor === "typescript" || valor === "python" || valor === "dart" || valor === "lua" || valor === "javascript" || valor === "html" || valor === "css") {
|
|
121
|
+
return valor;
|
|
122
|
+
}
|
|
123
|
+
if (valor === "js") {
|
|
124
|
+
return "javascript";
|
|
125
|
+
}
|
|
126
|
+
return undefined;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const NOMES_ORIGEM_CONTRATO = new Set(["sema", "contratos", "contracts"]);
|
|
130
|
+
|
|
131
|
+
async function resolverEntradaPadrao(
|
|
132
|
+
cwd: string,
|
|
133
|
+
configCarregada?: ConfiguracaoProjetoCarregada,
|
|
134
|
+
): Promise<string> {
|
|
135
|
+
if (configCarregada) {
|
|
136
|
+
return configCarregada.baseDiretorio;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
for (const nomeOrigem of NOMES_ORIGEM_CONTRATO) {
|
|
140
|
+
const origem = path.join(cwd, nomeOrigem);
|
|
141
|
+
if (await caminhoExiste(origem)) {
|
|
142
|
+
return path.resolve(origem);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
try {
|
|
147
|
+
const entradas = await readdir(cwd, { withFileTypes: true });
|
|
148
|
+
if (entradas.some((entradaAtual) => entradaAtual.isFile() && entradaAtual.name.endsWith(".sema"))) {
|
|
149
|
+
return path.resolve(cwd);
|
|
150
|
+
}
|
|
151
|
+
} catch {
|
|
152
|
+
// Se o cwd nao puder ser lido, seguimos para os fallbacks.
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const exemplos = path.resolve(cwd, "exemplos");
|
|
156
|
+
if (await caminhoExiste(exemplos)) {
|
|
157
|
+
return exemplos;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
return path.resolve(cwd);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
async function listarArquivosDeOrigens(origens: string[]): Promise<string[]> {
|
|
164
|
+
const encontrados = new Set<string>();
|
|
165
|
+
for (const origem of origens) {
|
|
166
|
+
if (!(await caminhoExiste(origem))) {
|
|
167
|
+
continue;
|
|
168
|
+
}
|
|
169
|
+
const arquivos = await listarArquivosSema(origem);
|
|
170
|
+
for (const arquivo of arquivos) {
|
|
171
|
+
encontrados.add(path.resolve(arquivo));
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
return [...encontrados].sort((a, b) => a.localeCompare(b, "pt-BR"));
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
function normalizarFonteLegado(valor: string): FonteLegado | undefined {
|
|
178
|
+
if (
|
|
179
|
+
valor === "nestjs"
|
|
180
|
+
|| valor === "fastapi"
|
|
181
|
+
|| valor === "flask"
|
|
182
|
+
|| valor === "nextjs"
|
|
183
|
+
|| valor === "nextjs-consumer"
|
|
184
|
+
|| valor === "react-vite-consumer"
|
|
185
|
+
|| valor === "angular-consumer"
|
|
186
|
+
|| valor === "flutter-consumer"
|
|
187
|
+
|| valor === "firebase"
|
|
188
|
+
|| valor === "typescript"
|
|
189
|
+
|| valor === "python"
|
|
190
|
+
|| valor === "dart"
|
|
191
|
+
|| valor === "lua"
|
|
192
|
+
|| valor === "php"
|
|
193
|
+
|| valor === "dotnet"
|
|
194
|
+
|| valor === "java"
|
|
195
|
+
|| valor === "go"
|
|
196
|
+
|| valor === "rust"
|
|
197
|
+
|| valor === "cpp"
|
|
198
|
+
) {
|
|
199
|
+
return valor;
|
|
200
|
+
}
|
|
201
|
+
return undefined;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
function normalizarModoAdocao(valor?: string): ModoAdocao {
|
|
205
|
+
return valor === "incremental" ? valor : "incremental";
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
async function lerConteudoSeExistir(caminhoAlvo: string): Promise<string | undefined> {
|
|
209
|
+
try {
|
|
210
|
+
return await readFile(caminhoAlvo, "utf8");
|
|
211
|
+
} catch {
|
|
212
|
+
return undefined;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
async function listarDiretoriosFilhos(diretorioBase: string): Promise<string[]> {
|
|
217
|
+
try {
|
|
218
|
+
const entradas = await readdir(diretorioBase, { withFileTypes: true });
|
|
219
|
+
return entradas
|
|
220
|
+
.filter((entrada) => entrada.isDirectory())
|
|
221
|
+
.map((entrada) => path.join(diretorioBase, entrada.name));
|
|
222
|
+
} catch {
|
|
223
|
+
return [];
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
async function listarArquivosRecursivosLimitado(
|
|
228
|
+
diretorioBase: string,
|
|
229
|
+
extensoes: string[],
|
|
230
|
+
profundidadeMaxima = 4,
|
|
231
|
+
limite = 40,
|
|
232
|
+
): Promise<string[]> {
|
|
233
|
+
const encontrados: string[] = [];
|
|
234
|
+
|
|
235
|
+
const visitar = async (diretorioAtual: string, profundidadeAtual: number): Promise<void> => {
|
|
236
|
+
if (encontrados.length >= limite) {
|
|
237
|
+
return;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
let entradas;
|
|
241
|
+
try {
|
|
242
|
+
entradas = await readdir(diretorioAtual, { withFileTypes: true });
|
|
243
|
+
} catch {
|
|
244
|
+
return;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
for (const entrada of entradas) {
|
|
248
|
+
if (encontrados.length >= limite) {
|
|
249
|
+
return;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
const caminhoAtual = path.join(diretorioAtual, entrada.name);
|
|
253
|
+
if (entrada.isDirectory()) {
|
|
254
|
+
if (profundidadeAtual <= 0 || DIRETORIOS_CODIGO_IGNORADOS.has(entrada.name.toLowerCase())) {
|
|
255
|
+
continue;
|
|
256
|
+
}
|
|
257
|
+
await visitar(caminhoAtual, profundidadeAtual - 1);
|
|
258
|
+
continue;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
if (extensoes.some((extensao) => entrada.name.toLowerCase().endsWith(extensao))) {
|
|
262
|
+
encontrados.push(caminhoAtual);
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
};
|
|
266
|
+
|
|
267
|
+
await visitar(diretorioBase, profundidadeMaxima);
|
|
268
|
+
return encontrados;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
async function procurarArquivosPorNome(
|
|
272
|
+
diretorioBase: string,
|
|
273
|
+
nomes: string[],
|
|
274
|
+
profundidadeMaxima = 4,
|
|
275
|
+
): Promise<string[]> {
|
|
276
|
+
const nomesNormalizados = new Set(nomes.map((nome) => nome.toLowerCase()));
|
|
277
|
+
const encontrados: string[] = [];
|
|
278
|
+
|
|
279
|
+
const visitar = async (diretorioAtual: string, profundidadeAtual: number): Promise<void> => {
|
|
280
|
+
let entradas;
|
|
281
|
+
try {
|
|
282
|
+
entradas = await readdir(diretorioAtual, { withFileTypes: true });
|
|
283
|
+
} catch {
|
|
284
|
+
return;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
for (const entrada of entradas) {
|
|
288
|
+
const caminhoAtual = path.join(diretorioAtual, entrada.name);
|
|
289
|
+
if (entrada.isDirectory()) {
|
|
290
|
+
if (profundidadeAtual <= 0 || DIRETORIOS_CODIGO_IGNORADOS.has(entrada.name.toLowerCase())) {
|
|
291
|
+
continue;
|
|
292
|
+
}
|
|
293
|
+
await visitar(caminhoAtual, profundidadeAtual - 1);
|
|
294
|
+
continue;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
if (nomesNormalizados.has(entrada.name.toLowerCase())) {
|
|
298
|
+
encontrados.push(caminhoAtual);
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
};
|
|
302
|
+
|
|
303
|
+
await visitar(diretorioBase, profundidadeMaxima);
|
|
304
|
+
return encontrados;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
const DIRETORIOS_CODIGO_FIXOS = [
|
|
308
|
+
"src",
|
|
309
|
+
"app",
|
|
310
|
+
"apps",
|
|
311
|
+
"backend",
|
|
312
|
+
"lib",
|
|
313
|
+
"api",
|
|
314
|
+
"server",
|
|
315
|
+
"services",
|
|
316
|
+
"models",
|
|
317
|
+
"data",
|
|
318
|
+
"pipeline",
|
|
319
|
+
"workers",
|
|
320
|
+
"functions",
|
|
321
|
+
"scripts",
|
|
322
|
+
];
|
|
323
|
+
|
|
324
|
+
const DIRETORIOS_CODIGO_IGNORADOS = new Set([
|
|
325
|
+
".git",
|
|
326
|
+
".github",
|
|
327
|
+
".pytest_cache",
|
|
328
|
+
".tmp",
|
|
329
|
+
".turbo",
|
|
330
|
+
".venv",
|
|
331
|
+
".next",
|
|
332
|
+
".nuxt",
|
|
333
|
+
".dart_tool",
|
|
334
|
+
"__pycache__",
|
|
335
|
+
"build",
|
|
336
|
+
"coverage",
|
|
337
|
+
"deploy",
|
|
338
|
+
"dist",
|
|
339
|
+
"doc",
|
|
340
|
+
"docs",
|
|
341
|
+
"generated",
|
|
342
|
+
"node_modules",
|
|
343
|
+
"ephemeral",
|
|
344
|
+
"sema",
|
|
345
|
+
"test",
|
|
346
|
+
"tests",
|
|
347
|
+
"vendor",
|
|
348
|
+
"venv",
|
|
349
|
+
]);
|
|
350
|
+
|
|
351
|
+
const EXTENSOES_CODIGO = [
|
|
352
|
+
".ts",
|
|
353
|
+
".tsx",
|
|
354
|
+
".js",
|
|
355
|
+
".jsx",
|
|
356
|
+
".mjs",
|
|
357
|
+
".cjs",
|
|
358
|
+
".py",
|
|
359
|
+
".dart",
|
|
360
|
+
".lua",
|
|
361
|
+
".php",
|
|
362
|
+
".cs",
|
|
363
|
+
".java",
|
|
364
|
+
".go",
|
|
365
|
+
".rs",
|
|
366
|
+
".cpp",
|
|
367
|
+
".cc",
|
|
368
|
+
".cxx",
|
|
369
|
+
".hpp",
|
|
370
|
+
".h",
|
|
371
|
+
];
|
|
372
|
+
async function resolverBaseProjeto(
|
|
373
|
+
entradaResolvida: string,
|
|
374
|
+
configCarregada?: ConfiguracaoProjetoCarregada,
|
|
375
|
+
): Promise<string> {
|
|
376
|
+
if (configCarregada) {
|
|
377
|
+
return configCarregada.baseDiretorio;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
const infoEntrada = await stat(entradaResolvida);
|
|
381
|
+
const pontoPartida = path.resolve(infoEntrada.isDirectory() ? entradaResolvida : path.dirname(entradaResolvida));
|
|
382
|
+
|
|
383
|
+
let atual = pontoPartida;
|
|
384
|
+
for (;;) {
|
|
385
|
+
if (NOMES_ORIGEM_CONTRATO.has(path.basename(atual).toLowerCase())) {
|
|
386
|
+
const contemMarcadorRaiz = await caminhoExiste(path.join(atual, "package.json"))
|
|
387
|
+
|| await caminhoExiste(path.join(atual, "sema.config.json"));
|
|
388
|
+
if (!contemMarcadorRaiz) {
|
|
389
|
+
const pai = path.dirname(atual);
|
|
390
|
+
if (pai !== atual) {
|
|
391
|
+
return pai;
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
const pai = path.dirname(atual);
|
|
397
|
+
if (pai === atual) {
|
|
398
|
+
break;
|
|
399
|
+
}
|
|
400
|
+
atual = pai;
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
return pontoPartida;
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
async function descobrirOrigemPadrao(baseProjeto: string, entradaResolvida: string): Promise<string> {
|
|
407
|
+
for (const nomeOrigem of NOMES_ORIGEM_CONTRATO) {
|
|
408
|
+
const origemContratos = path.join(baseProjeto, nomeOrigem);
|
|
409
|
+
if (await caminhoExiste(origemContratos)) {
|
|
410
|
+
return path.resolve(origemContratos);
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
const infoEntrada = await stat(entradaResolvida);
|
|
415
|
+
if (infoEntrada.isFile()) {
|
|
416
|
+
return path.resolve(path.dirname(entradaResolvida));
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
return path.resolve(baseProjeto);
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
async function resolverOrigensProjeto(
|
|
423
|
+
baseProjeto: string,
|
|
424
|
+
entradaResolvida: string,
|
|
425
|
+
configCarregada?: ConfiguracaoProjetoCarregada,
|
|
426
|
+
): Promise<string[]> {
|
|
427
|
+
if (configCarregada) {
|
|
428
|
+
const infoEntrada = await stat(entradaResolvida);
|
|
429
|
+
if (infoEntrada.isFile()) {
|
|
430
|
+
return [path.resolve(path.dirname(entradaResolvida))];
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
if (path.resolve(entradaResolvida) !== path.resolve(configCarregada.baseDiretorio)) {
|
|
434
|
+
return [path.resolve(entradaResolvida)];
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
const declaradas = configCarregada.config.origens ?? (configCarregada.config.origem ? [configCarregada.config.origem] : []);
|
|
438
|
+
if (declaradas.length > 0) {
|
|
439
|
+
return declaradas.map((origem) => path.resolve(configCarregada.baseDiretorio, origem));
|
|
440
|
+
}
|
|
441
|
+
return [configCarregada.baseDiretorio];
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
const infoEntrada = await stat(entradaResolvida);
|
|
445
|
+
if (infoEntrada.isFile()) {
|
|
446
|
+
return [path.resolve(path.dirname(entradaResolvida))];
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
if (path.resolve(entradaResolvida) !== path.resolve(baseProjeto)) {
|
|
450
|
+
return [path.resolve(entradaResolvida)];
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
return [await descobrirOrigemPadrao(baseProjeto, entradaResolvida)];
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
async function diretorioTemArquivosCodigo(diretorioBase: string, profundidadeMaxima = 4): Promise<boolean> {
|
|
457
|
+
let entradas;
|
|
458
|
+
try {
|
|
459
|
+
entradas = await readdir(diretorioBase, { withFileTypes: true });
|
|
460
|
+
} catch {
|
|
461
|
+
return false;
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
for (const entrada of entradas) {
|
|
465
|
+
const caminhoEntrada = path.join(diretorioBase, entrada.name);
|
|
466
|
+
if (entrada.isDirectory()) {
|
|
467
|
+
if (profundidadeMaxima <= 0 || DIRETORIOS_CODIGO_IGNORADOS.has(entrada.name.toLowerCase())) {
|
|
468
|
+
continue;
|
|
469
|
+
}
|
|
470
|
+
if (await diretorioTemArquivosCodigo(caminhoEntrada, profundidadeMaxima - 1)) {
|
|
471
|
+
return true;
|
|
472
|
+
}
|
|
473
|
+
continue;
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
if (EXTENSOES_CODIGO.some((extensao) => entrada.name.toLowerCase().endsWith(extensao))) {
|
|
477
|
+
return true;
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
return false;
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
async function inferirDiretoriosCodigo(
|
|
485
|
+
baseProjeto: string,
|
|
486
|
+
configCarregada?: ConfiguracaoProjetoCarregada,
|
|
487
|
+
): Promise<string[]> {
|
|
488
|
+
|
|
489
|
+
if (configCarregada?.config.diretoriosCodigo?.length) {
|
|
490
|
+
return [...new Set(configCarregada.config.diretoriosCodigo
|
|
491
|
+
.map((diretorio) => path.resolve(configCarregada.baseDiretorio, diretorio))
|
|
492
|
+
.filter(Boolean))]
|
|
493
|
+
.sort((a, b) => a.localeCompare(b, "pt-BR"));
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
const candidatosFixos = DIRETORIOS_CODIGO_FIXOS
|
|
497
|
+
.map((segmento) => path.join(baseProjeto, segmento));
|
|
498
|
+
const existentes: string[] = [];
|
|
499
|
+
for (const candidato of candidatosFixos) {
|
|
500
|
+
if (await caminhoExiste(candidato) && await diretorioTemArquivosCodigo(candidato)) {
|
|
501
|
+
existentes.push(path.resolve(candidato));
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
const filhos = await listarDiretoriosFilhos(baseProjeto);
|
|
506
|
+
const uteis: string[] = [];
|
|
507
|
+
for (const diretorio of filhos) {
|
|
508
|
+
const nome = path.basename(diretorio).toLowerCase();
|
|
509
|
+
if (DIRETORIOS_CODIGO_IGNORADOS.has(nome)) {
|
|
510
|
+
continue;
|
|
511
|
+
}
|
|
512
|
+
if (await diretorioTemArquivosCodigo(diretorio)) {
|
|
513
|
+
uteis.push(path.resolve(diretorio));
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
const combinados = [...new Set([...existentes, ...uteis])].sort((a, b) => a.localeCompare(b, "pt-BR"));
|
|
518
|
+
if (combinados.length > 0) {
|
|
519
|
+
return combinados;
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
if (await diretorioTemArquivosCodigo(baseProjeto, 0)) {
|
|
523
|
+
return [path.resolve(baseProjeto)];
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
return [];
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
async function inferirFontesLegado(
|
|
530
|
+
diretoriosCodigo: string[],
|
|
531
|
+
baseProjeto: string,
|
|
532
|
+
configCarregada?: ConfiguracaoProjetoCarregada,
|
|
533
|
+
): Promise<FonteLegado[]> {
|
|
534
|
+
if (configCarregada?.config.fontesLegado?.length) {
|
|
535
|
+
return [...new Set(configCarregada.config.fontesLegado
|
|
536
|
+
.map((fonte) => normalizarFonteLegado(fonte))
|
|
537
|
+
.filter((fonte): fonte is FonteLegado => Boolean(fonte)))]
|
|
538
|
+
.sort((a, b) => a.localeCompare(b, "pt-BR"));
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
const encontrados = new Set<FonteLegado>();
|
|
542
|
+
const packageJsonRaiz = await lerConteudoSeExistir(path.join(baseProjeto, "package.json"));
|
|
543
|
+
const marcadoresFirebaseProjeto = await procurarArquivosPorNome(baseProjeto, ["firebase.json", "firestore.rules"], 3);
|
|
544
|
+
|
|
545
|
+
if (packageJsonRaiz) {
|
|
546
|
+
if (/@nestjs\/common|@nestjs\/core/.test(packageJsonRaiz)) {
|
|
547
|
+
encontrados.add("nestjs");
|
|
548
|
+
}
|
|
549
|
+
if (/typescript/.test(packageJsonRaiz)) {
|
|
550
|
+
encontrados.add("typescript");
|
|
551
|
+
}
|
|
552
|
+
if (/"next"\s*:/.test(packageJsonRaiz)) {
|
|
553
|
+
encontrados.add("nextjs");
|
|
554
|
+
encontrados.add("typescript");
|
|
555
|
+
}
|
|
556
|
+
if (/firebase-admin|firebase-functions|firebase\b/.test(packageJsonRaiz)) {
|
|
557
|
+
encontrados.add("firebase");
|
|
558
|
+
encontrados.add("typescript");
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
if (marcadoresFirebaseProjeto.length > 0) {
|
|
563
|
+
encontrados.add("firebase");
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
for (const diretorio of diretoriosCodigo) {
|
|
567
|
+
const arquivosTs = await listarArquivosRecursivosLimitado(diretorio, [".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"], 5, 40);
|
|
568
|
+
const arquivosPy = await listarArquivosRecursivosLimitado(diretorio, [".py"], 5, 20);
|
|
569
|
+
const arquivosDart = await listarArquivosRecursivosLimitado(diretorio, [".dart"], 5, 20);
|
|
570
|
+
const arquivosLua = await listarArquivosRecursivosLimitado(diretorio, [".lua"], 5, 20);
|
|
571
|
+
const arquivosPhp = await listarArquivosRecursivosLimitado(diretorio, [".php"], 5, 30);
|
|
572
|
+
const arquivosCs = await listarArquivosRecursivosLimitado(diretorio, [".cs"], 5, 20);
|
|
573
|
+
const arquivosJava = await listarArquivosRecursivosLimitado(diretorio, [".java"], 5, 20);
|
|
574
|
+
const arquivosGo = await listarArquivosRecursivosLimitado(diretorio, [".go"], 5, 20);
|
|
575
|
+
const arquivosRust = await listarArquivosRecursivosLimitado(diretorio, [".rs"], 5, 20);
|
|
576
|
+
const arquivosCppBrutos = await listarArquivosRecursivosLimitado(diretorio, [".cpp", ".cc", ".cxx", ".hpp", ".h"], 5, 30);
|
|
577
|
+
const arquivosCpp = arquivosCppBrutos.filter((arquivo) => !/(^|[\\/])(windows|linux|macos|runner|flutter|ephemeral|build|vendor)([\\/]|$)/i.test(arquivo));
|
|
578
|
+
|
|
579
|
+
if (arquivosTs.length > 0) {
|
|
580
|
+
encontrados.add("typescript");
|
|
581
|
+
|
|
582
|
+
const packageJsons = await procurarArquivosPorNome(diretorio, ["package.json"], 3);
|
|
583
|
+
const nextConfigs = await procurarArquivosPorNome(diretorio, ["next.config.js", "next.config.ts", "next.config.mjs"], 3);
|
|
584
|
+
const viteConfigs = await procurarArquivosPorNome(diretorio, ["vite.config.ts", "vite.config.js", "vite.config.mjs"], 3);
|
|
585
|
+
const angularConfigs = await procurarArquivosPorNome(diretorio, ["angular.json"], 3);
|
|
586
|
+
const firebaseLocais = await procurarArquivosPorNome(diretorio, ["firebase.json", "firestore.rules"], 3);
|
|
587
|
+
const textosPackage = await Promise.all(packageJsons.slice(0, 8).map((arquivo) => lerConteudoSeExistir(arquivo)));
|
|
588
|
+
const amostrasTs = await Promise.all(arquivosTs.slice(0, 10).map((arquivo) => lerConteudoSeExistir(arquivo)));
|
|
589
|
+
const relacoesTs = arquivosTs.map((arquivo) => path.relative(diretorio, arquivo).replace(/\\/g, "/"));
|
|
590
|
+
|
|
591
|
+
const temNest = textosPackage.some((texto) => /@nestjs\/common|@nestjs\/core/.test(texto ?? ""))
|
|
592
|
+
|| amostrasTs.some((texto) => /@nestjs\/common|@nestjs\/core|@Controller\(|@Get\(|@Post\(|@Put\(|@Patch\(|@Delete\(/.test(texto ?? ""));
|
|
593
|
+
const temNext = textosPackage.some((texto) => /"next"\s*:/.test(texto ?? ""))
|
|
594
|
+
|| nextConfigs.length > 0
|
|
595
|
+
|| relacoesTs.some((relacao) => /(?:^|\/)(?:src\/)?app\/api\/.+\/route\.(?:ts|tsx|js|jsx)$/.test(relacao));
|
|
596
|
+
const temNextConsumer = relacoesTs.some((relacao) => /(?:^|\/)(?:src\/)?app\/(?:(?!api\/).)*?(?:page|layout|loading|error)\.(?:ts|tsx|js|jsx)$/.test(relacao));
|
|
597
|
+
const temSuperficieReactViteConsumer = relacoesTs.some((relacao) => /^(?:src\/)?pages\/.+\.(?:ts|tsx|js|jsx)$/.test(relacao))
|
|
598
|
+
|| relacoesTs.some((relacao) => /^(?:src\/)?App\.(?:ts|tsx|js|jsx)$/.test(relacao))
|
|
599
|
+
|| relacoesTs.some((relacao) => /(?:^|\/)(?:src\/)?(?:app\/)?(?:router|routes)\.(?:ts|tsx|js|jsx)$/.test(relacao));
|
|
600
|
+
const temBridgeReactViteConsumer = relacoesTs.some((relacao) => /(?:^|\/)(?:src\/)?lib\/(?:sema_consumer_bridge|sema\/.+)\.(?:ts|tsx|js|jsx)$/.test(relacao));
|
|
601
|
+
const temReactViteConsumer = temSuperficieReactViteConsumer
|
|
602
|
+
|| ((textosPackage.some((texto) => /"react"\s*:|"vite"\s*:|react-router-dom/.test(texto ?? "")) || viteConfigs.length > 0) && temBridgeReactViteConsumer);
|
|
603
|
+
const temSuperficieAngularConsumer = relacoesTs.some((relacao) => /(?:^|\/)(?:src\/)?app\/.+\.component\.(?:ts|js)$/.test(relacao))
|
|
604
|
+
|| relacoesTs.some((relacao) => /(?:^|\/)(?:src\/)?app(?:\/.+)?\/[^/]+\.routes\.(?:ts|js)$/.test(relacao));
|
|
605
|
+
const temBridgeAngularConsumer = relacoesTs.some((relacao) => /(?:^|\/)(?:src\/)?app\/(?:sema_consumer_bridge|sema\/.+)\.(?:ts|js)$/.test(relacao));
|
|
606
|
+
const temAngularConsumer = temSuperficieAngularConsumer
|
|
607
|
+
|| ((textosPackage.some((texto) => /@angular\/core|@angular\/router/.test(texto ?? "")) || angularConfigs.length > 0) && temBridgeAngularConsumer);
|
|
608
|
+
const temFirebase = marcadoresFirebaseProjeto.length > 0
|
|
609
|
+
|| firebaseLocais.length > 0
|
|
610
|
+
|| textosPackage.some((texto) => /firebase-admin|firebase-functions/.test(texto ?? ""))
|
|
611
|
+
|| amostrasTs.some((texto) => /firebase-admin|getFirestore|initializeApp|from\s+["']firebase-admin["']/.test(texto ?? ""));
|
|
612
|
+
|
|
613
|
+
if (temNest) {
|
|
614
|
+
encontrados.add("nestjs");
|
|
615
|
+
}
|
|
616
|
+
if (temNext) {
|
|
617
|
+
encontrados.add("nextjs");
|
|
618
|
+
}
|
|
619
|
+
if (temNextConsumer) {
|
|
620
|
+
encontrados.add("nextjs-consumer");
|
|
621
|
+
}
|
|
622
|
+
if (temReactViteConsumer) {
|
|
623
|
+
encontrados.add("react-vite-consumer");
|
|
624
|
+
}
|
|
625
|
+
if (temAngularConsumer) {
|
|
626
|
+
encontrados.add("angular-consumer");
|
|
627
|
+
}
|
|
628
|
+
if (temFirebase) {
|
|
629
|
+
encontrados.add("firebase");
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
if (arquivosPy.length > 0) {
|
|
634
|
+
encontrados.add("python");
|
|
635
|
+
const amostrasPython = await Promise.all(
|
|
636
|
+
arquivosPy
|
|
637
|
+
.slice(0, 8)
|
|
638
|
+
.map((arquivo) => lerConteudoSeExistir(arquivo)),
|
|
639
|
+
);
|
|
640
|
+
|
|
641
|
+
const temFastapi = amostrasPython.some((texto) => /from\s+fastapi\s+import|APIRouter|FastAPI/.test(texto ?? ""));
|
|
642
|
+
const temFlask = amostrasPython.some((texto) => /from\s+flask\s+import|import\s+flask\b|Blueprint\s*\(|Flask\s*\(|@\w+\.route\s*\(/.test(texto ?? ""));
|
|
643
|
+
|
|
644
|
+
if (temFastapi) {
|
|
645
|
+
encontrados.add("fastapi");
|
|
646
|
+
}
|
|
647
|
+
if (temFlask) {
|
|
648
|
+
encontrados.add("flask");
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
if (arquivosDart.length > 0) {
|
|
653
|
+
encontrados.add("dart");
|
|
654
|
+
const pubspecs = await procurarArquivosPorNome(diretorio, ["pubspec.yaml"], 3);
|
|
655
|
+
const textosPubspec = await Promise.all(pubspecs.slice(0, 4).map((arquivo) => lerConteudoSeExistir(arquivo)));
|
|
656
|
+
const amostrasDart = await Promise.all(arquivosDart.slice(0, 10).map((arquivo) => lerConteudoSeExistir(arquivo)));
|
|
657
|
+
const relacoesDart = arquivosDart.map((arquivo) => path.relative(diretorio, arquivo).replace(/\\/g, "/"));
|
|
658
|
+
const temBridgeFlutterConsumer = relacoesDart.some((relacao) =>
|
|
659
|
+
/(?:^|\/)(?:lib\/)?(?:sema_consumer_bridge|api\/sema_contract_bridge|sema\/.+)\.dart$/i.test(relacao));
|
|
660
|
+
const temSuperficieFlutterConsumer = relacoesDart.some((relacao) =>
|
|
661
|
+
/(?:^|\/)(?:lib\/)?(?:screens|pages)\/.+\.dart$/i.test(relacao)
|
|
662
|
+
|| /(?:^|\/)(?:lib\/)?(?:router|app_router|routes|main)\.dart$/i.test(relacao));
|
|
663
|
+
const temFlutterRuntime = textosPubspec.some((texto) => /\nflutter:\s*$|sdk:\s*flutter|dependencies:\s*[\s\S]*\bflutter:\s*$/m.test(texto ?? ""))
|
|
664
|
+
|| amostrasDart.some((texto) => /MaterialApp(?:\.router)?\s*\(|CupertinoApp(?:\.router)?\s*\(|GoRouter\s*\(/.test(texto ?? ""));
|
|
665
|
+
const temFlutterConsumer = temSuperficieFlutterConsumer
|
|
666
|
+
|| (temFlutterRuntime && temBridgeFlutterConsumer);
|
|
667
|
+
|
|
668
|
+
if (temFlutterConsumer) {
|
|
669
|
+
encontrados.add("flutter-consumer");
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
if (arquivosDart.length > 0) {
|
|
673
|
+
encontrados.add("dart");
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
if (arquivosLua.length > 0) {
|
|
677
|
+
encontrados.add("lua");
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
if (arquivosPhp.length > 0) {
|
|
681
|
+
encontrados.add("php");
|
|
682
|
+
const composerJsons = await procurarArquivosPorNome(diretorio, ["composer.json"], 4);
|
|
683
|
+
const artisan = await procurarArquivosPorNome(diretorio, ["artisan"], 3);
|
|
684
|
+
const amostrasPhp = await Promise.all(arquivosPhp.slice(0, 10).map((arquivo) => lerConteudoSeExistir(arquivo)));
|
|
685
|
+
if (
|
|
686
|
+
composerJsons.length > 0
|
|
687
|
+
|| artisan.length > 0
|
|
688
|
+
|| amostrasPhp.some((texto) =>
|
|
689
|
+
/Illuminate\\|Symfony\\|Route::(?:get|post|put|patch|delete|resource|apiResource)|#\[\s*(?:Route|Get|Post|Put|Patch|Delete)\b/i.test(texto ?? ""))
|
|
690
|
+
) {
|
|
691
|
+
encontrados.add("php");
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
if (arquivosCs.length > 0) {
|
|
696
|
+
encontrados.add("dotnet");
|
|
697
|
+
const marcadores = await procurarArquivosPorNome(diretorio, ["appsettings.json", "Program.cs"], 4);
|
|
698
|
+
const amostrasCs = await Promise.all(arquivosCs.slice(0, 8).map((arquivo) => lerConteudoSeExistir(arquivo)));
|
|
699
|
+
if (
|
|
700
|
+
marcadores.length > 0
|
|
701
|
+
|| amostrasCs.some((texto) => /\bWebApplication\.CreateBuilder\b|\[ApiController\]|\[Http(Get|Post|Put|Patch|Delete)\]|\bMap(Get|Post|Put|Patch|Delete)\(/.test(texto ?? ""))
|
|
702
|
+
) {
|
|
703
|
+
encontrados.add("dotnet");
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
if (arquivosJava.length > 0) {
|
|
708
|
+
encontrados.add("java");
|
|
709
|
+
const marcadoresJava = await procurarArquivosPorNome(diretorio, ["pom.xml", "build.gradle", "build.gradle.kts"], 4);
|
|
710
|
+
const amostrasJava = await Promise.all(arquivosJava.slice(0, 8).map((arquivo) => lerConteudoSeExistir(arquivo)));
|
|
711
|
+
if (
|
|
712
|
+
marcadoresJava.length > 0
|
|
713
|
+
|| amostrasJava.some((texto) => /@RestController|@GetMapping|@PostMapping|@RequestMapping/.test(texto ?? ""))
|
|
714
|
+
) {
|
|
715
|
+
encontrados.add("java");
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
if (arquivosGo.length > 0) {
|
|
720
|
+
encontrados.add("go");
|
|
721
|
+
const goMod = await procurarArquivosPorNome(diretorio, ["go.mod"], 3);
|
|
722
|
+
const amostrasGo = await Promise.all(arquivosGo.slice(0, 8).map((arquivo) => lerConteudoSeExistir(arquivo)));
|
|
723
|
+
if (
|
|
724
|
+
goMod.length > 0
|
|
725
|
+
|| amostrasGo.some((texto) => /\bhttp\.HandleFunc\b|\bNewServeMux\b|\.GET\(|\.POST\(|gin\.Default\(/.test(texto ?? ""))
|
|
726
|
+
) {
|
|
727
|
+
encontrados.add("go");
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
if (arquivosRust.length > 0) {
|
|
732
|
+
encontrados.add("rust");
|
|
733
|
+
const cargo = await procurarArquivosPorNome(diretorio, ["Cargo.toml"], 3);
|
|
734
|
+
const amostrasRust = await Promise.all(arquivosRust.slice(0, 8).map((arquivo) => lerConteudoSeExistir(arquivo)));
|
|
735
|
+
if (
|
|
736
|
+
cargo.length > 0
|
|
737
|
+
|| amostrasRust.some((texto) => /\bRouter::new\b|\.route\(|\bnest\(/.test(texto ?? ""))
|
|
738
|
+
) {
|
|
739
|
+
encontrados.add("rust");
|
|
740
|
+
}
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
if (arquivosCpp.length > 0) {
|
|
744
|
+
encontrados.add("cpp");
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
return [...encontrados].sort((a, b) => a.localeCompare(b, "pt-BR"));
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
export async function carregarProjeto(
|
|
752
|
+
entrada: string | undefined,
|
|
753
|
+
cwd: string,
|
|
754
|
+
): Promise<ContextoProjetoCarregado> {
|
|
755
|
+
const entradaBase = entrada ? path.resolve(cwd, entrada) : cwd;
|
|
756
|
+
const configCarregada = await carregarConfiguracaoProjeto(entradaBase);
|
|
757
|
+
const entradaResolvida = entrada ? path.resolve(cwd, entrada) : await resolverEntradaPadrao(cwd, configCarregada);
|
|
758
|
+
const baseProjeto = await resolverBaseProjeto(entradaResolvida, configCarregada);
|
|
759
|
+
const infoEntrada = await stat(entradaResolvida);
|
|
760
|
+
const origensProjeto = await resolverOrigensProjeto(baseProjeto, entradaResolvida, configCarregada);
|
|
761
|
+
const arquivosProjeto = await listarArquivosDeOrigens(origensProjeto);
|
|
762
|
+
const diretoriosCodigo = await inferirDiretoriosCodigo(baseProjeto, configCarregada);
|
|
763
|
+
const fontesLegado = await inferirFontesLegado(diretoriosCodigo, baseProjeto, configCarregada);
|
|
764
|
+
const modoAdocao = normalizarModoAdocao(configCarregada?.config.modoAdocao);
|
|
765
|
+
|
|
766
|
+
const arquivosSelecionados = infoEntrada.isFile()
|
|
767
|
+
? new Set([path.resolve(entradaResolvida)])
|
|
768
|
+
: new Set((
|
|
769
|
+
configCarregada && path.resolve(entradaResolvida) === path.resolve(configCarregada.baseDiretorio)
|
|
770
|
+
? arquivosProjeto
|
|
771
|
+
: await listarArquivosSema(entradaResolvida)
|
|
772
|
+
).map((arquivo) => path.resolve(arquivo)));
|
|
773
|
+
|
|
774
|
+
const fontes = [];
|
|
775
|
+
for (const arquivo of arquivosProjeto) {
|
|
776
|
+
const codigo = await lerArquivoTexto(arquivo);
|
|
777
|
+
fontes.push({ caminho: arquivo, codigo });
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
const resultadoProjeto = compilarProjeto(fontes);
|
|
781
|
+
const resultados = new Map<string, ResultadoCompilacaoProjetoModulo>(
|
|
782
|
+
resultadoProjeto.modulos.map((modulo) => [path.resolve(modulo.caminho), modulo]),
|
|
783
|
+
);
|
|
784
|
+
|
|
785
|
+
return {
|
|
786
|
+
entradaResolvida,
|
|
787
|
+
baseProjeto,
|
|
788
|
+
configCarregada,
|
|
789
|
+
arquivosProjeto,
|
|
790
|
+
origensProjeto,
|
|
791
|
+
diretoriosCodigo,
|
|
792
|
+
fontesLegado,
|
|
793
|
+
modoAdocao,
|
|
794
|
+
modulosSelecionados: fontes
|
|
795
|
+
.filter((fonte) => arquivosSelecionados.has(path.resolve(fonte.caminho)))
|
|
796
|
+
.map((fonte) => ({
|
|
797
|
+
caminho: fonte.caminho,
|
|
798
|
+
codigo: fonte.codigo,
|
|
799
|
+
resultado: resultados.get(path.resolve(fonte.caminho))!,
|
|
800
|
+
})),
|
|
801
|
+
};
|
|
802
|
+
}
|
|
803
|
+
|
|
804
|
+
export function resolverAlvoPadrao(
|
|
805
|
+
alvoExplicito: string | undefined,
|
|
806
|
+
configCarregada?: ConfiguracaoProjetoCarregada,
|
|
807
|
+
): AlvoGeracao {
|
|
808
|
+
return normalizarAlvo(alvoExplicito)
|
|
809
|
+
?? configCarregada?.config.alvoPadrao
|
|
810
|
+
?? configCarregada?.config.alvos?.[0]
|
|
811
|
+
?? "typescript";
|
|
812
|
+
}
|
|
813
|
+
|
|
814
|
+
export function resolverFrameworkPadrao(
|
|
815
|
+
frameworkExplicito: string | undefined,
|
|
816
|
+
configCarregada?: ConfiguracaoProjetoCarregada,
|
|
817
|
+
): FrameworkGeracao {
|
|
818
|
+
return normalizarFrameworkGeracao(frameworkExplicito ?? configCarregada?.config.framework);
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
export function resolverEstruturaSaidaPadrao(
|
|
822
|
+
estruturaExplicita: string | undefined,
|
|
823
|
+
framework: FrameworkGeracao,
|
|
824
|
+
configCarregada?: ConfiguracaoProjetoCarregada,
|
|
825
|
+
): EstruturaSaida {
|
|
826
|
+
const estrutura = normalizarEstruturaSaida(estruturaExplicita ?? configCarregada?.config.estruturaSaida);
|
|
827
|
+
if (!estruturaExplicita && !configCarregada?.config.estruturaSaida && framework !== "base") {
|
|
828
|
+
return "backend";
|
|
829
|
+
}
|
|
830
|
+
if (estrutura === "backend" && framework === "base") {
|
|
831
|
+
return "modulos";
|
|
832
|
+
}
|
|
833
|
+
return estrutura;
|
|
834
|
+
}
|
|
835
|
+
|
|
836
|
+
export function resolverSaidaPadrao(
|
|
837
|
+
saidaExplicita: string | undefined,
|
|
838
|
+
alvo: AlvoGeracao,
|
|
839
|
+
configCarregada?: ConfiguracaoProjetoCarregada,
|
|
840
|
+
): string {
|
|
841
|
+
if (saidaExplicita) {
|
|
842
|
+
return path.resolve(saidaExplicita);
|
|
843
|
+
}
|
|
844
|
+
|
|
845
|
+
const diretorioPorAlvo = configCarregada?.config.diretoriosSaidaPorAlvo?.[alvo];
|
|
846
|
+
if (diretorioPorAlvo && configCarregada) {
|
|
847
|
+
return path.resolve(configCarregada.baseDiretorio, diretorioPorAlvo);
|
|
848
|
+
}
|
|
849
|
+
|
|
850
|
+
if (configCarregada?.config.saida) {
|
|
851
|
+
return path.resolve(configCarregada.baseDiretorio, configCarregada.config.saida);
|
|
852
|
+
}
|
|
853
|
+
|
|
854
|
+
return path.resolve("./saida");
|
|
855
|
+
}
|
|
856
|
+
|
|
857
|
+
export function resolverAlvosVerificacao(configCarregada?: ConfiguracaoProjetoCarregada): AlvoGeracao[] {
|
|
858
|
+
const alvos: AlvoGeracao[] = configCarregada?.config.alvos?.length
|
|
859
|
+
? configCarregada.config.alvos
|
|
860
|
+
: ["typescript", "python"];
|
|
861
|
+
return [...new Set(alvos)];
|
|
862
|
+
}
|