liquido
Version:
Conjunto de ferramentas para desenvolvimento de aplicações para a internet 100% em português
892 lines (882 loc) • 45.9 kB
JavaScript
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.Liquido = void 0;
const sistemaDeArquivos = __importStar(require("fs"));
const caminho = __importStar(require("path"));
const avaliadores_sintaticos_1 = require("./infraestrutura/avaliadores-sintaticos");
const construtos_1 = require("@designliquido/delegua/construtos");
const declaracoes_1 = require("@designliquido/delegua/declaracoes");
const estruturas_1 = require("@designliquido/delegua/interpretador/estruturas");
const referencia_montao_1 = require("@designliquido/delegua/interpretador/estruturas/referencia-montao");
const informacao_elemento_sintatico_1 = require("@designliquido/delegua/informacao-elemento-sintatico");
const lexador_1 = require("@designliquido/delegua/lexador");
const importador_1 = require("@designliquido/delegua-node/importador");
const foles_1 = require("@designliquido/foles");
const infraestrutura_1 = require("./infraestrutura");
const formatadores_1 = require("./infraestrutura/formatadores");
const provedores_1 = require("./infraestrutura/provedores");
const interface_linha_comando_1 = require("./interface-linha-comando");
const roteador_1 = require("./infraestrutura/roteador");
const centro_configuracoes_1 = require("./infraestrutura/centro-configuracoes");
const auto_documentador_1 = require("./infraestrutura/auto-documentacao/auto-documentador");
const requisicao_1 = require("./infraestrutura/requisicao");
const interpretador_liquido_1 = require("./infraestrutura/interpretador-liquido");
const erros_1 = require("./erros");
/**
* O núcleo do framework.
*/
class Liquido {
constructor(diretorioBase) {
this.importador = undefined;
this.avaliadorSintatico = undefined;
this.interpretador = undefined;
this.classeContextoEntidades = null;
this.arquivosAbertos = {};
this.conteudoArquivosAbertos = {};
this.arquivosDelegua = [];
this.arquivosPitugues = [];
this.rotasDelegua = [];
this.rotasPitugues = [];
this.diretorioDescobertos = [];
this.diretorioBase = diretorioBase;
this.diretorioEstatico = 'publico';
this.configurarPipelineLinguagem('delegua');
this.formatadorLmht = new formatadores_1.FormatadorLmht(this.diretorioBase);
this.autoDocumentador = new auto_documentador_1.AutoDocumentador();
this.roteador = new roteador_1.Roteador(this.autoDocumentador);
this.provedorLincones = new provedores_1.ProvedorLincones();
this.foles = new foles_1.FolEs(false);
}
async iniciar() {
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q;
await this.importarArquivoConfiguracao();
const linguagemSelecionada = ((_b = (_a = this
.centroConfiguracoes) === null || _a === void 0 ? void 0 : _a.liquido) === null || _b === void 0 ? void 0 : _b.linguagem) || 'delegua';
this.configurarPipelineLinguagem(linguagemSelecionada);
if (this.provedorLincones.configurado && this.interpretador) {
const moduloLincones = await this.provedorLincones.resolver();
const dados = (_d = (_c = this.centroConfiguracoes) === null || _c === void 0 ? void 0 : _c.liquido) === null || _d === void 0 ? void 0 : _d.dados;
if ((_e = dados === null || dados === void 0 ? void 0 : dados.lincones) === null || _e === void 0 ? void 0 : _e.autoInicializar) {
try {
if (dados.motor === 'delegua-entidades') {
await (0, interface_linha_comando_1.inicializarBancoDeleguaEntidades)((_f = dados.lincones.tecnologia) !== null && _f !== void 0 ? _f : '', (_g = dados.lincones.caminho) !== null && _g !== void 0 ? _g : '', false, false, this.provedorLincones.instancia);
}
else {
await (0, interface_linha_comando_1.inicializarBancoLincones)((_h = dados.lincones.tecnologia) !== null && _h !== void 0 ? _h : '', (_j = dados.lincones.caminho) !== null && _j !== void 0 ? _j : '', dados.lincones.arquivoInicializacao, false, false, this.provedorLincones.instancia);
}
}
catch (erro) {
console.warn(`[Liquido] Inicialização automática do banco de dados falhou: ${(_k = erro === null || erro === void 0 ? void 0 : erro.message) !== null && _k !== void 0 ? _k : erro}`);
}
}
this.interpretador.pilhaEscoposExecucao.definirVariavel('lincones', moduloLincones);
if ((dados === null || dados === void 0 ? void 0 : dados.motor) === 'delegua-entidades') {
try {
const pacote = '@designliquido/delegua-entidades';
const modulo = await Promise.resolve(`${pacote}`).then(s => __importStar(require(s)));
this.classeContextoEntidades = modulo.ContextoEntidades;
}
catch (_r) {
console.warn('[Liquido] @designliquido/delegua-entidades não encontrado. Instale com: npm install @designliquido/delegua-entidades');
}
}
}
const diretorioEstatico = ((_o = (_m = (_l = this.centroConfiguracoes) === null || _l === void 0 ? void 0 : _l.liquido) === null || _m === void 0 ? void 0 : _m.roteador) === null || _o === void 0 ? void 0 : _o.diretorioEstatico) || this.diretorioEstatico;
const caminhoAbsolutoEstatico = caminho.join(this.diretorioBase, diretorioEstatico);
this.roteador.configurarArquivosEstaticos(caminhoAbsolutoEstatico);
const caminhoAbsolutoEstilos = caminho.join(this.diretorioBase, this.obterDiretorioBaseEstilos());
if (caminhoAbsolutoEstilos !== caminhoAbsolutoEstatico) {
this.roteador.configurarArquivosEstaticos(caminhoAbsolutoEstilos);
}
this.roteador.iniciarMiddlewares();
await this.importarArquivosRotas();
this.roteador.iniciar();
if (((_q = (_p = this.centroConfiguracoes) === null || _p === void 0 ? void 0 : _p.liquido) === null || _q === void 0 ? void 0 : _q.arquetipo) !== 'rest') {
this.escreverEstilos();
}
}
configurarPipelineLinguagem(linguagem = 'delegua') {
const linguagemNorm = this.normalizarLinguagem(linguagem);
const lexador = linguagemNorm === 'delegua'
? new lexador_1.Lexador()
: new lexador_1.LexadorPitugues();
this.importador = new importador_1.Importador(lexador, this.arquivosAbertos, this.conteudoArquivosAbertos, false);
if (linguagemNorm === 'delegua') {
this.interpretador = new interpretador_liquido_1.InterpretadorLiquido(this.importador, process.cwd(), false, console.log);
this.avaliadorSintatico = new avaliadores_sintaticos_1.AvaliadorSintaticoLiquido(this.importador);
}
else {
this.interpretador = new interpretador_liquido_1.InterpretadorLiquidoPitugues(this.importador, process.cwd(), false, console.log);
this.avaliadorSintatico = new avaliadores_sintaticos_1.AvaliadorSintaticoLiquidoPitugues(this.importador);
}
this.avaliadorSintatico.tiposDeFerramentasExternas = {
liquido: {
lincones: 'módulo',
liquido: 'módulo',
requisicao: 'módulo',
resposta: 'módulo',
contexto: 'módulo'
}
};
}
/**
* Método de importação do arquivo `configuracao.delprops`.
* @returns void.
*/
async importarArquivoConfiguracao() {
var _a, _b;
const caminhoConfigArquivo = this.resolverArquivoConfiguracao();
if (caminhoConfigArquivo.valor === false) {
console.info("Arquivo 'configuracao.delprops' não encontrado.");
return;
}
try {
if (!caminhoConfigArquivo.caminho) {
return;
}
const retornoImportador = (_a = this.importador) === null || _a === void 0 ? void 0 : _a.importar(caminhoConfigArquivo.caminho, -1);
const retornoAvaliadorSintatico = await ((_b = this.avaliadorSintatico) === null || _b === void 0 ? void 0 : _b.analisar(retornoImportador === null || retornoImportador === void 0 ? void 0 : retornoImportador.retornoLexador, retornoImportador === null || retornoImportador === void 0 ? void 0 : retornoImportador.hashArquivo));
if (((retornoAvaliadorSintatico === null || retornoAvaliadorSintatico === void 0 ? void 0 : retornoAvaliadorSintatico.erros) || []).length > 0) {
let mensagemCompleta = "";
for (const erro of (retornoAvaliadorSintatico === null || retornoAvaliadorSintatico === void 0 ? void 0 : retornoAvaliadorSintatico.erros) || []) {
mensagemCompleta += `[Linha ${erro.linha}] Erro no arquivo de configuração: ${erro.message}\n`;
}
throw new Error(mensagemCompleta);
}
this.centroConfiguracoes = new centro_configuracoes_1.CentroConfiguracoes((retornoAvaliadorSintatico === null || retornoAvaliadorSintatico === void 0 ? void 0 : retornoAvaliadorSintatico.declaracoes) || []);
for (const [chave, configuracao] of Object.entries(this.centroConfiguracoes)) {
switch (chave) {
case 'liquido':
const configuracaoTipada = configuracao;
configuracaoTipada.configurar({
autoDocumentador: this.autoDocumentador,
roteador: this.roteador,
provedorLincones: this.provedorLincones
});
break;
default:
throw new Error(`Chave de configuração desconhecida: ${chave}`);
}
}
}
catch (error) {
console.error(error);
}
}
/**
* Retorna o caminho do arquivo de configuração se existir.
* @param {string} caminhoTotal O caminho para o diretório a ser pesquisado.
* @returns Um objeto com duas propriedades: `caminho` e `valor`. Se o caminho foi
* encontrado, `valor` será `true`, e `caminho` terá o caminho completo
* do arquivo de configuração. Caso contrário, `valor` será `false`, e
* `caminho` será nulo.
*/
resolverArquivoConfiguracao(caminhoTotal = '') {
const diretorioBase = caminhoTotal === '' ? this.diretorioBase : caminhoTotal;
let listaDeArquivos;
try {
listaDeArquivos = sistemaDeArquivos.readdirSync(diretorioBase);
}
catch (_a) {
return { caminho: null, valor: false };
}
for (const arquivo of listaDeArquivos) {
if (arquivo === 'configuracao.delprops') {
return { caminho: caminho.join(diretorioBase, arquivo), valor: true };
}
}
return { caminho: null, valor: false };
}
normalizarLinguagem(linguagem) {
return linguagem.normalize('NFD').replace(/[̀-ͯ]/g, '');
}
/**
* Método de descoberta de rotas. Recursivo.
* @param diretorio O diretório a ser pesquisado.
*/
descobrirRotas(diretorio, linguagem) {
const linguagemNorm = this.normalizarLinguagem(linguagem);
const listaDeRotas = sistemaDeArquivos.readdirSync(diretorio);
const diretorioDescobertos = [];
for (const diretorioOuArquivo of listaDeRotas) {
const caminhoAbsoluto = caminho.join(diretorio, diretorioOuArquivo);
if (linguagemNorm === 'delegua') {
if (caminhoAbsoluto.endsWith('.delegua')) {
this.arquivosDelegua.push(caminhoAbsoluto);
continue;
}
}
if (linguagemNorm === 'pitugues') {
if (caminhoAbsoluto.endsWith('.pitu')) {
this.arquivosPitugues.push(caminhoAbsoluto);
continue;
}
}
if (sistemaDeArquivos.lstatSync(caminhoAbsoluto).isDirectory()) {
diretorioDescobertos.push(caminhoAbsoluto);
}
}
for (const diretorioDescoberto of diretorioDescobertos) {
this.descobrirRotas(diretorioDescoberto, linguagemNorm);
}
}
descobrirEstilos() {
try {
const listaDeEstilos = sistemaDeArquivos.readdirSync('./estilos');
const arquivosDescobertos = [];
listaDeEstilos.forEach((diretorioOuArquivo) => {
const caminhoAbsoluto = caminho.join('./estilos', diretorioOuArquivo);
if (caminhoAbsoluto.endsWith('.foles')) {
arquivosDescobertos.push(caminhoAbsoluto);
}
});
return arquivosDescobertos;
}
catch (erro) {
console.error(`Pulando descoberta de estilos. Causa: ${erro}.`);
return [];
}
}
/**
* Diretório onde o CSS gerado a partir de FolEs é escrito e a partir
* do qual é servido pelo Express (`liquido.estilos.diretorioBase`, padrão: 'publico/css').
*/
obterDiretorioBaseEstilos() {
var _a, _b, _c;
return ((_c = (_b = (_a = this.centroConfiguracoes) === null || _a === void 0 ? void 0 : _a.liquido) === null || _b === void 0 ? void 0 : _b.estilos) === null || _c === void 0 ? void 0 : _c.diretorioBase) || 'publico/css';
}
escreverEstilos() {
const arquivosEstilos = this.descobrirEstilos();
const diretorioBaseEstilos = this.obterDiretorioBaseEstilos();
if (!sistemaDeArquivos.existsSync(`./${diretorioBaseEstilos}`)) {
sistemaDeArquivos.mkdirSync(`./${diretorioBaseEstilos}`, { recursive: true });
}
for (const arquivo of arquivosEstilos) {
const teste = this.foles.converterParaCss(arquivo);
const arquivoDestino = caminho.join(process.cwd(), `./${diretorioBaseEstilos}`, arquivo.replace('estilos', '').replace('.foles', '.css'));
sistemaDeArquivos.writeFile(arquivoDestino, teste, (erro) => {
if (erro) {
return console.log(erro);
}
console.log(`Salvo: ${arquivoDestino}`);
});
}
}
/**
* Ele pega um caminho de arquivo e retorna uma rota
* @param {string} caminhoArquivo O caminho do arquivo que está sendo lido
* @returns A rota resolvida.
*/
resolverCaminhoRota(caminhoArquivo, linguagem) {
const extensaoLinguagem = this.normalizarLinguagem(linguagem) === 'delegua'
? 'delegua'
: 'pitu';
const partesArquivo = caminhoArquivo.split('rotas');
const rotaResolvida = partesArquivo[1]
.replace(`inicial.${extensaoLinguagem}`, '')
.replace(`.${extensaoLinguagem}`, '')
.replace(new RegExp(`\\${caminho.sep}`, 'g'), '/')
.replace(new RegExp(`/$`, 'g'), '')
.replace(new RegExp(`\\[(.+)\\]`, 'g'), ':$1');
return rotaResolvida === '' ? '/' : rotaResolvida;
}
async analisarArquivo(arquivo) {
var _a, _b;
const retornoImportador = (_a = this.importador) === null || _a === void 0 ? void 0 : _a.importar(arquivo, -1);
const retornoAvaliadorSintatico = await ((_b = this
.avaliadorSintatico) === null || _b === void 0 ? void 0 : _b.analisar(retornoImportador === null || retornoImportador === void 0 ? void 0 : retornoImportador.retornoLexador, retornoImportador === null || retornoImportador === void 0 ? void 0 : retornoImportador.hashArquivo));
if ((retornoAvaliadorSintatico === null || retornoAvaliadorSintatico === void 0 ? void 0 : retornoAvaliadorSintatico.erros.length) > 0) {
for (const erro of (retornoAvaliadorSintatico === null || retornoAvaliadorSintatico === void 0 ? void 0 : retornoAvaliadorSintatico.erros) || []) {
console.error(`[Linha ${erro.linha}] Erro na rota ${arquivo}: ${erro.message}`);
}
return null;
}
return (retornoAvaliadorSintatico === null || retornoAvaliadorSintatico === void 0 ? void 0 : retornoAvaliadorSintatico.declaracoes) || [];
}
coletarFuncoesDaRota(declaracoes) {
const funcaoDeclaracoes = new Map();
for (const declaracao of declaracoes) {
if (declaracao instanceof declaracoes_1.FuncaoDeclaracao) {
funcaoDeclaracoes.set(declaracao.simbolo.lexema, declaracao.funcao);
}
}
return funcaoDeclaracoes;
}
resolverArgumentosDaRota(argumentos, funcoesDeclaradas, arquivo) {
const argumentosResolvidos = [];
for (const argumento of argumentos) {
if (argumento instanceof construtos_1.Variavel) {
// Referência a função declarada
const nomeFuncao = argumento.simbolo.lexema;
if (funcoesDeclaradas.has(nomeFuncao)) {
const funcaoResolvida = funcoesDeclaradas.get(nomeFuncao);
if (funcaoResolvida) {
argumentosResolvidos.push(funcaoResolvida);
}
}
else {
console.error(`Função '${nomeFuncao}' referenciada mas não encontrada em ${arquivo}`);
}
}
else if (argumento instanceof construtos_1.FuncaoConstruto) {
// Função em linha/anônima
argumentosResolvidos.push(argumento);
}
else {
console.error(`Argumento de rota inválido em ${arquivo}: esperado função ou referência a função`);
}
}
return argumentosResolvidos;
}
extrairChamadaLiquido(declaracao) {
const expressao = declaracao instanceof declaracoes_1.Expressao
? declaracao.expressao
: null;
if (!expressao)
return null;
if (expressao instanceof construtos_1.Chamada) {
const expressaoChamada = expressao.entidadeChamada;
if (expressaoChamada instanceof construtos_1.AcessoMetodo) {
// Normalmente é o caso de uma chamada como `liquido.rotaGet(...)`
const objeto = expressaoChamada.objeto;
if (!objeto.simbolo || objeto.simbolo.lexema.toLowerCase() !== 'liquido') {
return null;
}
return {
nomeMetodo: expressaoChamada.nomeMetodo,
argumentos: expressao.argumentos
};
}
// Pituguês não converte para `AcessoMetodo`, então precisamos lidar com `AcessoMetodoOuPropriedade`.
if (expressaoChamada instanceof construtos_1.AcessoMetodoOuPropriedade) {
const objeto = expressaoChamada.objeto;
if (!objeto.simbolo || objeto.simbolo.lexema.toLowerCase() !== 'liquido') {
return null;
}
return {
nomeMetodo: expressaoChamada.simbolo.lexema,
argumentos: expressao.argumentos
};
}
}
return null;
}
async importarArquivosRotas() {
var _a, _b;
const metodosRotaPermitidos = new Set([
'rotaGet',
'rotaPost',
'rotaPut',
'rotaDelete',
'rotaPatch',
'rotaOptions',
'rotaCopy',
'rotaHead',
'rotaLock',
'rotaUnlock',
'rotaPurge',
'rotaPropfind'
]);
const linguagemSelecionada = this.normalizarLinguagem(((_b = (_a = this.centroConfiguracoes) === null || _a === void 0 ? void 0 : _a.liquido) === null || _b === void 0 ? void 0 : _b.linguagem) || 'delegua');
this.descobrirRotas(caminho.join(this.diretorioBase, 'rotas'), linguagemSelecionada);
const arquivosParaLer = linguagemSelecionada === 'delegua'
? this.arquivosDelegua
: this.arquivosPitugues;
for (const arquivo of arquivosParaLer) {
const declaracoes = await this.analisarArquivo(arquivo);
if (!declaracoes) {
console.error(`[Liquido] Arquivo de rota ignorado por erros de análise: ${arquivo}`);
continue;
}
const funcaoDeclaracoes = this.coletarFuncoesDaRota(declaracoes);
for (const declaracao of declaracoes) {
const chamadaLiquido = this.extrairChamadaLiquido(declaracao);
if (chamadaLiquido) {
const { nomeMetodo, argumentos } = chamadaLiquido;
if (metodosRotaPermitidos.has(nomeMetodo)) {
const argumentosResolvidos = this
.resolverArgumentosDaRota(argumentos, funcaoDeclaracoes, arquivo);
await this.adicionarRota(nomeMetodo, this.resolverCaminhoRota(arquivo, linguagemSelecionada), argumentosResolvidos);
}
}
}
}
}
/**
* O Interpretador Delégua exige alguns parâmetros definidos antes de executar.
* Esse método define esses parâmetros na posição inicial da pilha de execução
* do Interpretador.
* @param requisicao O objeto de requisição do Express.
* @param nomeFuncao O nome da função a ser chamada pelo Interpretador.
* @param funcaoConstruto O conteúdo da função, declarada no arquivo `.delegua` correspondente.
*/
async prepararRequisicao(requisicao, nomeFuncao, funcaoConstruto) {
var _a, _b, _c, _d, _e, _f, _g;
(_a = this.avaliadorSintatico) === null || _a === void 0 ? void 0 : _a.pilhaEscopos.definirInformacoesVariavel('liquido', new informacao_elemento_sintatico_1.InformacaoElementoSintatico('liquido', 'módulo'));
(_b = this.avaliadorSintatico) === null || _b === void 0 ? void 0 : _b.pilhaEscopos.definirInformacoesVariavel('requisicao', new informacao_elemento_sintatico_1.InformacaoElementoSintatico('requisicao', 'módulo'));
(_c = this.avaliadorSintatico) === null || _c === void 0 ? void 0 : _c.pilhaEscopos.definirInformacoesVariavel('resposta', new informacao_elemento_sintatico_1.InformacaoElementoSintatico('resposta', 'módulo'));
const descritorClasseRequisicao = new requisicao_1.Requisicao(requisicao);
await descritorClasseRequisicao.chamar(this.interpretador, []);
const instanciaRequisicao = new estruturas_1.ObjetoDeleguaClasse(descritorClasseRequisicao);
instanciaRequisicao.definir({ lexema: 'corpo' }, requisicao.body);
instanciaRequisicao.definir({ lexema: 'parametros' }, requisicao.params);
instanciaRequisicao.definir({ lexema: 'parametrosPesquisa' }, requisicao.query || {});
instanciaRequisicao.definir({ lexema: 'parametrosCaminho' }, requisicao.path);
(_d = this.interpretador) === null || _d === void 0 ? void 0 : _d.pilhaEscoposExecucao.definirVariavel('requisicao', instanciaRequisicao);
const descritorClasseResposta = new infraestrutura_1.Resposta();
await descritorClasseResposta.chamar(this.interpretador, []);
(_e = this.interpretador) === null || _e === void 0 ? void 0 : _e.pilhaEscoposExecucao.definirVariavel('resposta', new estruturas_1.ObjetoDeleguaClasse(descritorClasseResposta));
if (this.classeContextoEntidades && this.provedorLincones.instancia) {
const contexto = new this.classeContextoEntidades(this.provedorLincones.instancia);
(_f = this.interpretador) === null || _f === void 0 ? void 0 : _f.pilhaEscoposExecucao.definirVariavel('contexto', contexto);
}
const funcaoRetorno = new estruturas_1.DeleguaFuncao(nomeFuncao, funcaoConstruto);
(_g = this.interpretador) === null || _g === void 0 ? void 0 : _g.pilhaEscoposExecucao.definirVariavel(nomeFuncao, funcaoRetorno);
}
/**
* Chamada ao Interpretador Delégua com a estrutura declarativa para a
* execução da função nomeada na rota.
* @param nomeFuncao O nome da função da rota.
* @returns O resultado da interpretação.
*/
async chamarInterpretador(nomeFuncao) {
var _a;
try {
return await ((_a = this.interpretador) === null || _a === void 0 ? void 0 : _a.interpretar([
new declaracoes_1.Expressao(new construtos_1.Chamada(-1, new construtos_1.Variavel(-1, new lexador_1.Simbolo('IDENTIFICADOR', nomeFuncao, null, -1, -1)), [
new construtos_1.Variavel(-1, new lexador_1.Simbolo('IDENTIFICADOR', 'requisicao', null, -1, -1)),
new construtos_1.Variavel(-1, new lexador_1.Simbolo('IDENTIFICADOR', 'resposta', null, -1, -1))
]))
], true)) || {
erros: [],
resultado: []
};
}
catch (erro) {
console.error(erro);
throw erro;
}
}
classificarErro(erro) {
var _a;
const textoErro = (erro.mensagem || ((_a = erro.erroInterno) === null || _a === void 0 ? void 0 : _a.message) || '').toLowerCase();
for (const item of erros_1.listaDeErros) {
if (textoErro.includes(item.palavraChave)) {
return item.codigo;
}
}
return 'LIQ99999';
}
logicaComumErrosInterpretacao(retornoInterpretador) {
const listaErros = [];
for (const erro of retornoInterpretador.erros) {
const tipoErro = this.classificarErro(erro);
if (erro.erroInterno) {
const erroInternoTipado = erro.erroInterno;
console.error(`[Liquido] ${tipoErro}: ${erroInternoTipado.message}`);
listaErros.push(`
Código: ${tipoErro}\n
Mensagem: ${erroInternoTipado.message}\n
Pilha: ${erroInternoTipado.pilha}
`);
}
else {
console.error(`[Liquido] ${tipoErro} - [Linha ${erro.linha}]: ${erro.mensagem}`);
listaErros.push(`${tipoErro} - [Linha ${erro.linha}]: ${erro.mensagem}`);
}
}
let corpoFinal;
if (this.centroConfiguracoes.liquido.arquetipo === 'mvc') {
const itensLista = listaErros
.map(erro => `<li><pre>${erro}</pre></li>`)
.join('');
corpoFinal = `
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Erro de Execução - Líquido</title>
<style>
:root {
--fundo: #f4f6f8;
--texto: #333;
--vermelho-topo: #dc3545;
--branco: #ffffff;
--borda: #e1e4e8;
--fundo-codigo: #2d2d2d;
--texto-codigo: #f8f8f2;
}
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
background-color: var(--fundo);
color: var(--texto);
margin: 0;
padding: 0;
line-height: 1.6;
}
.cabecalho-erro {
background-color: var(--vermelho-topo);
color: var(--branco);
padding: 40px 20px;
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
}
.container {
max-width: 1000px;
margin: 0 auto;
}
h1 {
margin: 0;
font-size: 2.2rem;
font-weight: 600;
}
h2 {
color: var(--vermelho-topo);
border-bottom: 2px solid #ffcccc;
padding-bottom: 10px;
margin-top: 40px;
font-size: 1.5rem;
}
.conteudo {
padding: 20px;
}
ul.stack-trace {
list-style: none;
padding: 0;
margin: 0;
display: flex;
flex-direction: column;
gap: 15px;
}
ul.stack-trace li {
background: var(--branco);
border: 1px solid var(--borda);
border-radius: 8px;
padding: 20px;
box-shadow: 0 2px 4px rgba(0,0,0,0.05);
overflow-x: auto;
}
pre {
background-color: var(--fundo-codigo);
color: var(--texto-codigo);
padding: 15px;
border-radius: 6px;
font-family: 'Consolas', 'Monaco', 'Courier New', monospace;
font-size: 14px;
margin: 0;
white-space: pre-wrap;
word-wrap: break-word;
}
</style>
</head>
<body>
<div class="cabecalho-erro">
<div class="container">
<h1>Ocorreu um erro</h1>
</div>
</div>
<div class="container conteudo">
<h2>Pilha de Execução</h2>
<ul class="stack-trace">${itensLista}</ul>
</div>
</body>
</html>
`;
}
if (this.centroConfiguracoes.liquido.arquetipo === 'rest') {
corpoFinal = {
mensagem: 'Ocorreu um erro interno na aplicação.',
detalhes: listaErros
};
}
return { corpoRetorno: corpoFinal, statusHttp: 500 };
}
/**
* Lógica para processamento da resposta como uma visão LMHT.
* @param caminhoRota O caminho da rota da requisição.
* @param statusHttp O status HTTP pré-calculado.
* @param propriedades Propriedades da resposta, usadas para escolher a visão e parametrizá-la.
* @returns Um objeto com o corpo do retorno e o status HTTP correspondente.
*/
async logicaComumRespostaVisaoLmht(caminhoRota, statusHttp, propriedades) {
try {
let visao = caminhoRota;
// Verifica se foi definida uma preferência de visão.
// Se não foi, usa o sufixo da rota como visão correspondente.
// Por exemplo, `/rotas/inicial.delegua` tem como visão correspondente `/visoes/inicial.lmht`.
if (propriedades.visao) {
const partesRota = caminhoRota.split('/');
partesRota.pop();
visao = partesRota.join('/') + '/' + propriedades.visao;
}
const resultadoFormatacaoLmht = await this.formatadorLmht.formatar(visao, propriedades.valores);
return { corpoRetorno: resultadoFormatacaoLmht, statusHttp: statusHttp };
}
catch (erro) {
console.error(`Erro ao processar LMHT: ${erro}.`);
return { corpoRetorno: 'Erro ao processar visualizacao LMHT.', statusHttp: 500 };
}
}
obterObjetoRespostaDoEscopo() {
var _a;
try {
const variavel = (_a = this.interpretador) === null || _a === void 0 ? void 0 : _a.pilhaEscoposExecucao.obterVariavelPorNome('resposta');
if (!(variavel === null || variavel === void 0 ? void 0 : variavel.valor))
return null;
const valor = variavel.valor;
if (valor instanceof estruturas_1.ObjetoDeleguaClasse)
return valor;
if ((valor === null || valor === void 0 ? void 0 : valor.valor) instanceof estruturas_1.ObjetoDeleguaClasse)
return valor.valor;
}
catch (_b) {
// resposta não encontrada no escopo
}
return null;
}
async processarPropriedadesResposta(caminhoRota, objetoResposta) {
if (!(objetoResposta === null || objetoResposta === void 0 ? void 0 : objetoResposta.propriedades)) {
return {};
}
let statusHttp = 200;
if (objetoResposta.propriedades.statusHttp) {
statusHttp = objetoResposta.propriedades.statusHttp;
}
if (objetoResposta.propriedades.destino) {
return { redirecionamento: objetoResposta.propriedades.destino };
}
if (objetoResposta.propriedades.lmht) {
const resultadoVisao = await this.logicaComumRespostaVisaoLmht(caminhoRota, statusHttp, objetoResposta.propriedades);
resultadoVisao.tipoConteudo = 'HTML';
return resultadoVisao;
}
if (objetoResposta.propriedades.respostaJson) {
const jsonBruto = objetoResposta.propriedades.respostaJson;
const dadoParaLimpar = (jsonBruto &&
typeof jsonBruto === 'object' &&
'valor' in jsonBruto)
? jsonBruto.valor
: jsonBruto;
return {
corpoRetorno: this.limparObjeto(dadoParaLimpar),
tipoConteudo: 'JSON',
statusHttp: statusHttp
};
}
if (objetoResposta.propriedades.mensagem) {
return { corpoRetorno: objetoResposta.propriedades.mensagem, tipoConteudo: 'TEXTO', statusHttp: statusHttp };
}
return statusHttp !== 200 ? { statusHttp: statusHttp } : {};
}
async logicaComumResultadoInterpretador(caminhoRota, retornoInterpretador) {
if (retornoInterpretador.erros.length > 0) {
return this.logicaComumErrosInterpretacao(retornoInterpretador);
}
let objetoResposta = null;
// Tenta obter resposta a partir do valor retornado pela função
if (retornoInterpretador.resultado && retornoInterpretador.resultado.length > 0) {
const representacaoObjeto = retornoInterpretador.resultado.pop();
const valorRetornado = representacaoObjeto === null || representacaoObjeto === void 0 ? void 0 : representacaoObjeto.valorRetornado;
if (valorRetornado === null || valorRetornado === void 0 ? void 0 : valorRetornado.valor) {
const informacoesObjeto = valorRetornado.valor;
const candidato = (informacoesObjeto === null || informacoesObjeto === void 0 ? void 0 : informacoesObjeto.hasOwnProperty('valor'))
? informacoesObjeto.valor
: informacoesObjeto;
if (candidato === null || candidato === void 0 ? void 0 : candidato.propriedades) {
objetoResposta = candidato;
}
}
}
// Fallback: lê o objeto `resposta` do escopo do interpretador.
// Necessário quando a função de rota não usa `retorna` explicitamente
// mas chama métodos como `resposta.json(...)` como efeito colateral.
if (!objetoResposta) {
objetoResposta = this.obterObjetoRespostaDoEscopo();
}
if (!objetoResposta) {
return {};
}
return this.processarPropriedadesResposta(caminhoRota, objetoResposta);
}
/**
* Verifica se uma resposta foi definida pelo middleware ou handler.
* @param corpoEStatus O objeto de resposta retornado pelo interpretador.
* @returns Verdadeiro se alguma resposta foi definida, falso caso contrário.
*/
respostaFoiDefinida(corpoEStatus) {
if (!corpoEStatus)
return false;
return !!(corpoEStatus.corpoRetorno ||
corpoEStatus.redirecionamento ||
corpoEStatus.statusHttp);
}
/**
* Executa uma função (middleware ou handler) no contexto do interpretador.
* @param req O objeto de requisição do Express.
* @param caminhoRota O caminho da rota.
* @param funcao A função a ser executada.
* @param nomeFuncao O nome único para identificar a função no interpretador.
* @returns O corpo e status da resposta, se houver.
*/
async executarFuncaoRota(req, caminhoRota, funcao, nomeFuncao) {
await this.prepararRequisicao(req, nomeFuncao, funcao);
const retornoInterpretador = await this.chamarInterpretador(nomeFuncao);
return await this.logicaComumResultadoInterpretador(caminhoRota, retornoInterpretador);
}
/**
* Configuração de uma rota no roteador Express.
* @param metodoRoteador O método da rota.
* @param caminhoRota O caminho completo do arquivo que define a rota.
* @param argumentos Todas as funções em Delégua que devem ser executadas
* para a resolução da rota. O último argumento é o handler final,
* todos os anteriores são middlewares executados em sequência.
*/
async adicionarRota(metodoRoteador, caminhoRota, argumentos) {
var _a, _b;
if (argumentos.length === 0) {
console.error(`Rota ${caminhoRota} não possui nenhuma função definida.`);
return;
}
// Separa middlewares (todos menos o último) e handler (último argumento)
const middlewares = argumentos.slice(0, -1);
const handler = argumentos[argumentos.length - 1];
const chaveMetodo = metodoRoteador.replace('rota', '');
const metodoResolvido = roteador_1.MetodoRoteador[chaveMetodo];
const registradorRota = this.roteador.mapaRotas[metodoResolvido];
if (!metodoResolvido || !registradorRota) {
console.error(`Metodo de rota '${metodoRoteador}' nao suportado.`);
return;
}
const linguagemSelecionada = this.normalizarLinguagem(this.centroConfiguracoes.liquido.linguagem || 'delegua');
if (linguagemSelecionada === 'delegua') {
this.rotasDelegua.push(caminhoRota);
}
else {
this.rotasPitugues.push(caminhoRota);
}
registradorRota(caminhoRota, async (req, res) => {
var _a;
const inicioRequisicao = process.hrtime.bigint();
const duracaoMs = () => (Number(process.hrtime.bigint() - inicioRequisicao) / 1e6).toFixed(3);
let corpoEStatus = {};
// Executa middlewares em sequência
for (let i = 0; i < middlewares.length; i++) {
const middleware = middlewares[i];
const nomeMiddleware = `middleware${i}_${metodoRoteador}`;
corpoEStatus = await this.executarFuncaoRota(req, caminhoRota, middleware, nomeMiddleware);
// Se o middleware enviou uma resposta, para a execução
if (this.respostaFoiDefinida(corpoEStatus)) {
break;
}
}
// Se nenhum middleware enviou resposta, executa o handler final
if (!this.respostaFoiDefinida(corpoEStatus)) {
corpoEStatus = await this.executarFuncaoRota(req, caminhoRota, handler, `handler_${metodoRoteador}`);
}
// Envia a resposta
if (corpoEStatus.redirecionamento) {
res.redirect(corpoEStatus.redirecionamento);
console.log(`[Liquido] ${req.method} ${req.path} → redirecionado para ${corpoEStatus.redirecionamento} (${duracaoMs()} ms)`);
}
else if (corpoEStatus.statusHttp === 204) {
res.status(204).end();
console.log(`[Liquido] ${req.method} ${req.path} → aceito (204) (${duracaoMs()} ms)`);
}
else {
const statusResposta = (_a = corpoEStatus.statusHttp) !== null && _a !== void 0 ? _a : 200;
if (corpoEStatus.tipoConteudo === 'JSON') {
res.status(statusResposta).json(corpoEStatus.corpoRetorno);
}
else if (corpoEStatus.tipoConteudo === 'HTML') {
res.status(statusResposta).type('text/html').send(corpoEStatus.corpoRetorno);
}
else {
const corpo = corpoEStatus.corpoRetorno;
const corpoString = typeof corpo === 'string' ? corpo : String(corpo !== null && corpo !== void 0 ? corpo : '');
if (/^\s*<\?xml/.test(corpoString)) {
res.status(statusResposta).type('application/xml').send(corpoString);
}
else {
res.status(statusResposta).type('text/plain').send(corpoString);
}
}
if (statusResposta >= 500) {
console.error(`[Liquido] ${req.method} ${req.path} → rejeitado (${statusResposta}) (${duracaoMs()} ms)`);
}
else {
console.log(`[Liquido] ${req.method} ${req.path} → aceito (${statusResposta}) (${duracaoMs()} ms)`);
}
}
});
if ((_b = (_a = this.centroConfiguracoes) === null || _a === void 0 ? void 0 : _a.liquido) === null || _b === void 0 ? void 0 : _b.verboso) {
console.log(`[Liquido] Rota registrada: ${metodoResolvido.toUpperCase()} ${caminhoRota}`);
}
}
limparObjeto(item) {
var _a, _b, _c, _d;
if (item === null || item === undefined || typeof item !== 'object') {
return item;
}
if (item instanceof referencia_montao_1.ReferenciaMontao) {
try {
return this.limparObjeto((_a = this.interpretador) === null || _a === void 0 ? void 0 : _a.resolverValor(item));
}
catch (_e) {
// Bug em Delégua: classes estrangeiras podem criar ReferenciaMontao
// sem armazenar o valor no montão. Tenta acesso direto como fallback.
const valorDireto = (_d = (_c = (_b = this.interpretador) === null || _b === void 0 ? void 0 : _b.montao) === null || _c === void 0 ? void 0 : _c.referencias) === null || _d === void 0 ? void 0 : _d[item.endereco];
return valorDireto !== undefined ? this.limparObjeto(valorDireto) : null;
}
}
if (Array.isArray(item)) {
return item.map((i) => this.limparObjeto(i));
}
if ('valor' in item && item.valor !== undefined) {
return this.limparObjeto(item.valor);
}
if ('propriedades' in item && item.propriedades) {
const novoObjeto = {};
const propriedades = item.propriedades;
for (const [chave, valorProp] of Object.entries(propriedades)) {
novoObjeto[chave] = this.limparObjeto(valorProp);
}
return novoObjeto;
}
return item;
}
}
exports.Liquido = Liquido;
//# sourceMappingURL=liquido.js.map