UNPKG

liquido

Version:

Conjunto de ferramentas para desenvolvimento de aplicações para a internet 100% em português

892 lines (882 loc) 45.9 kB
"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