Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MVC Scaffolding: Gerador de Visões e Rotas #32

Merged
merged 3 commits into from
Jan 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 17 additions & 12 deletions index-gerar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ import prompts from 'prompts';
import { Classe } from '@designliquido/delegua/fontes/declaracoes';
import { pluralizar } from '@designliquido/flexoes';

import { criarDiretorioSeNaoExiste, criarNovaVisao, criarNovoControlador, importarModelos, obterTodosModelos } from './interface-linha-comando/gerar';
import { criarDiretorioSeNaoExiste, importarModelos, obterTodosModelos } from './interface-linha-comando/gerar';
import { GeradorVisoes } from './interface-linha-comando/gerar/gerador-visoes';
import { GeradorRotas } from './interface-linha-comando/gerar/gerador-rotas';

const pontoDeEntradaGerar = async (argumentos: string[]) => {
// argumentos[0] normalmente é o nome do executável, seja Node, Bun, etc.
Expand All @@ -25,29 +27,32 @@ const pontoDeEntradaGerar = async (argumentos: string[]) => {
}

const declaracoes = importarModelos(nomeModelo);
criarDiretorioSeNaoExiste('controladores');
criarDiretorioSeNaoExiste('rotas');

const geradorVisoes = new GeradorVisoes();
const geradorRotas = new GeradorRotas();

// Aqui apenas aceitamos declarações de classes. Pode ser mais de uma.
for (const declaracao of declaracoes.filter((d) => d instanceof Classe)) {
const declaracaoClasse = <Classe>declaracao;
const nomeBaseModelo = declaracaoClasse.simbolo.lexema.toLocaleLowerCase('pt');
const nomeControladorPlural = pluralizar(nomeBaseModelo).toLocaleLowerCase('pt');
const declaracaoModelo = <Classe>declaracao;
const nomeBaseModelo = declaracaoModelo.simbolo.lexema.toLocaleLowerCase('pt');
const nomeModeloPlural = pluralizar(nomeBaseModelo).toLocaleLowerCase('pt');

const caminhoControlador = criarNovoControlador(nomeControladorPlural);
const caminhoControlador = geradorRotas.criarNovasRotas(declaracaoModelo);
console.info(`Controlador ${caminhoControlador}`);

// Visões
criarDiretorioSeNaoExiste('visoes', nomeControladorPlural);
criarDiretorioSeNaoExiste('visoes', nomeModeloPlural);

const visaoSelecionarTudo = criarNovaVisao(nomeControladorPlural, 'selecionarTudo');
const visaoSelecionarTudo = geradorVisoes.criarNovaVisao(nomeModeloPlural, declaracaoModelo, 'selecionarTudo');
console.info(`Visão ${visaoSelecionarTudo}`);
const visaoSelecionarUm = criarNovaVisao(nomeControladorPlural, 'selecionarUm');
const visaoSelecionarUm = geradorVisoes.criarNovaVisao(nomeModeloPlural, declaracaoModelo, 'selecionarUm');
console.info(`Visão ${visaoSelecionarUm}`);
const visaoAdicionar = criarNovaVisao(nomeControladorPlural, 'adicionar');
const visaoAdicionar = geradorVisoes.criarNovaVisao(nomeModeloPlural, declaracaoModelo, 'adicionar');
console.info(`Visão ${visaoAdicionar}`);
const visaoAtualizar = criarNovaVisao(nomeControladorPlural, 'atualizar');
const visaoAtualizar = geradorVisoes.criarNovaVisao(nomeModeloPlural, declaracaoModelo, 'atualizar');
console.info(`Visão ${visaoAtualizar}`);
const visaoExcluir = criarNovaVisao(nomeControladorPlural, 'excluir');
const visaoExcluir = geradorVisoes.criarNovaVisao(nomeModeloPlural, declaracaoModelo, 'excluir');
console.info(`Visão ${visaoExcluir}`);
}
};
Expand Down
66 changes: 66 additions & 0 deletions interface-linha-comando/gerar/gerador-rotas.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import * as sistemaArquivos from 'fs';
import * as caminho from 'path';

import { Classe } from '@designliquido/delegua/fontes/declaracoes';
import { pluralizar } from '@designliquido/flexoes';
import { criarDiretorioSeNaoExiste } from '.';

export class GeradorRotas {
indentacao: number;

constructor() {
this.indentacao = 4;
}

/**
* Cria arquivos `.delegua` no diretório 'rotas/<modelo no plural>' com cinco rotas:
* - Arquivo `inicial.delegua`
* - rotaGet (para selecionar vários registros na base de dados)
* - rotaPost (para gravar 1 registro na base de dados)
* - Arquivo `[id].delegua`
* - rotaGet (para selecionar 1 registro por id na base de dados)
* - rotaPut (para alterar 1 registro na base de dados)
* - rotaDelete (para excluir 1 registro na base de dados)
* @param {Classe} declaracaoModelo O nome do diretório das rotas: o nome do modelo no plural.
* @returns {string} O caminho completo onde os arquivos de rotas foram criados.
*/
criarNovasRotas(declaracaoModelo: Classe): string {
const nomeBaseModelo = declaracaoModelo.simbolo.lexema.toLocaleLowerCase('pt');
const nomeModeloPlural = pluralizar(nomeBaseModelo).toLocaleLowerCase('pt');
const diretorioRotas = caminho.join(process.cwd(), 'rotas', nomeModeloPlural);

const conteudoSelecionarTudo = this.criarRotaSelecionarTudo(declaracaoModelo);
const conteudoAdicionar = `liquido.rotaPost(funcao(requisicao, resposta) {\n resposta.lmht({ "titulo": "Liquido" })\n})\n\n`;
const conteudoAtualizar = `liquido.rotaPut(funcao(requisicao, resposta) {\n resposta.lmht({ "titulo": "Liquido" })\n})\n\n`;
const conteudoExcluir = `liquido.rotaDelete(funcao(requisicao, resposta) {\n resposta.lmht({ "titulo": "Liquido" })\n})\n\n`;
const conteudoControlador = `${conteudoSelecionarTudo}${conteudoAdicionar}${conteudoAtualizar}${conteudoExcluir}`;

criarDiretorioSeNaoExiste('rotas', nomeModeloPlural);
const caminhoRotas = caminho.join(diretorioRotas, 'inicial.delegua');
sistemaArquivos.writeFileSync(
caminhoRotas,
conteudoControlador
);

return caminhoRotas;
}

private criarRotaSelecionarTudo(declaracaoModelo: Classe) {
// Isso aqui não vai ficar assim.
// É preciso montar as partes de dados antes.
const dadosTestes = [];
for (const propriedade of declaracaoModelo.propriedades) {
dadosTestes.push(`"${propriedade.nome.lexema}": "Teste"`);
}

return `liquido.rotaGet(funcao(requisicao, resposta) {\n` +
`${" ".repeat(this.indentacao)}resposta.lmht({\n` +
`${" ".repeat(this.indentacao * 2)}"linhas": [\n` +
`${" ".repeat(this.indentacao * 3)}{${dadosTestes.reduce(
(acumulador, elemento) => acumulador + ', ' + elemento
)}}\n` +
`${" ".repeat(this.indentacao * 2)}]\n` +
`${" ".repeat(this.indentacao)}})\n` +
`})\n\n`;
}
}
97 changes: 97 additions & 0 deletions interface-linha-comando/gerar/gerador-visoes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
import * as sistemaArquivos from 'fs';
import * as caminho from 'path';

import { Classe } from '@designliquido/delegua/fontes/declaracoes';

import { TipoVisao } from './tipo-visao';

export class GeradorVisoes {
indentacao: number;

constructor() {
this.indentacao = 4;
}

/**
* Cria uma nova visão, de acordo com o nome do controlador e o tipo de visão desejado.
* @param {string} nomeControlador O nome do controlador.
* @param {TipoVisao} tipoVisao O tipo da visão.
* @returns O caminho completo onde a visão foi criada.
*/
criarNovaVisao(nomeControlador: string, declaracaoModelo: Classe, tipoVisao: TipoVisao) {
let caminhoVisao: string;
let corpo: string;
const diretorioVisoes = caminho.join(process.cwd(), 'visoes', nomeControlador);
const cabecalhoComum = " ".repeat(this.indentacao) + '<cabeça><título>Teste</título></cabeça>\n';

switch (tipoVisao) {
case 'selecionarTudo':
caminhoVisao = caminho.join(diretorioVisoes, 'inicial.lmht');
corpo = `${" ".repeat(this.indentacao)}<corpo>\n${this.corpoInicial(declaracaoModelo)}\n${" ".repeat(this.indentacao)}</corpo>\n`;
break;
case 'selecionarUm':
caminhoVisao = caminho.join(diretorioVisoes, 'detalhes.lmht');
corpo = ' <corpo>Teste</corpo>\n';
break;
case 'adicionar':
caminhoVisao = caminho.join(diretorioVisoes, 'adicionar.lmht');
corpo = ' <corpo>Teste</corpo>\n';
break;
case 'atualizar':
caminhoVisao = caminho.join(diretorioVisoes, 'atualizar.lmht');
corpo = ' <corpo>Teste</corpo>\n';
break;
case 'excluir':
caminhoVisao = caminho.join(diretorioVisoes, 'excluir.lmht');
corpo = ' <corpo>Teste</corpo>\n';
break;
}

const conteudoVisao: string = `<lmht>\n${cabecalhoComum}${corpo}</lmht>`;

sistemaArquivos.writeFileSync(caminhoVisao, conteudoVisao);

return caminhoVisao;
}

/**
* Função que gera o corpo de `inicial.lmht` de cada visão gerada por linha de comando.
* @param {Classe} declaracaoModelo A declaração do modelo de dados, com suas propriedades e definições.
* @returns {string} Um trecho em LMHT com a estrutura do corpo da página.
*/
private corpoInicial(declaracaoModelo: Classe): string {
// Colunas de cabeçalho
const colunasCabecaTabela: string[] = [];
for (const propriedade of declaracaoModelo.propriedades) {
colunasCabecaTabela.push(" ".repeat(this.indentacao * 5) + `<célula>${propriedade.nome.lexema}</célula>`);
}

const linhaCabecaTabela = " ".repeat(this.indentacao * 4) + '<linha>\n' +
colunasCabecaTabela.reduce(
(acumulador, elemento) => acumulador + '\n' + elemento
) +
'\n' + " ".repeat(this.indentacao * 4) + '</linha>';

const cabecaTabela = `${" ".repeat(this.indentacao * 3)}<cabeça-tabela>\n${linhaCabecaTabela}\n${" ".repeat(this.indentacao * 3)}</cabeça-tabela>`;

// Colunas do corpo da tabela
const colunasCorpoTabela: string[] = [];
for (const propriedade of declaracaoModelo.propriedades) {
colunasCorpoTabela.push(" ".repeat(this.indentacao * 5) + `<célula>{{${propriedade.nome.lexema}}}</célula>`);
}

const linhaCorpoTabela = " ".repeat(this.indentacao * 4) + '<linha>\n' +
colunasCorpoTabela.reduce(
(acumulador, elemento) => acumulador + '\n' + elemento
) +
'\n' + " ".repeat(this.indentacao * 4) + '</linha>';

const corpoTabela = `${" ".repeat(this.indentacao * 3)}<corpo-tabela>\n` +
`${" ".repeat(this.indentacao * 3)}{{#cada linhas}}\n` +
`${linhaCorpoTabela}\n` +
`${" ".repeat(this.indentacao * 3)}{{/cada}}\n` +
`${" ".repeat(this.indentacao * 3)}</corpo-tabela>`;

return `${" ".repeat(this.indentacao * 2)}<tabela>\n${cabecaTabela}\n${corpoTabela}\n${" ".repeat(this.indentacao * 2)}</tabela>`;
}
}
66 changes: 0 additions & 66 deletions interface-linha-comando/gerar/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@ import { AvaliadorSintatico } from '@designliquido/delegua/fontes/avaliador-sint
import { Importador } from '@designliquido/delegua-node/fontes/importador';
import { Declaracao } from '@designliquido/delegua/fontes/declaracoes';

import { TipoVisao } from './tipo-visao';

/**
* Obtém todos os modelos do diretório 'modelos' do projeto.
* @returns {{ title: string, value: string }[]} Um vetor com todos os arquivos de modelos encontrados.
Expand Down Expand Up @@ -53,67 +51,3 @@ export function criarDiretorioSeNaoExiste(...partesDiretorio: string[]) {
sistemaArquivos.mkdirSync(caminhoDiretorio);
}
}

/**
* Cria um arquivo `.delegua` no diretório 'controladores' com quatro rotas:
* - rotaGet (para selecionar 1 ou vários registros na base de dados)
* - rotaPost (para gravar 1 registro na base de dados)
* - rotaPut (para alterar 1 registro na base de dados)
* - rotaDelete (para excluir 1 registro na base de dados)
* @param {string} nome O nome do controlador.
* @returns {string} O caminho completo onde o controlador foi criado.
*/
export function criarNovoControlador(nome: string): string {
const diretorioControladores = caminho.join(process.cwd(), 'controladores');

const conteudoControlador = `liquido.rotaGet(funcao(requisicao, resposta) {\n resposta.lmht({ "titulo": "Liquido" })\n})`;
const caminhoControlador = caminho.join(diretorioControladores, nome + '.delegua');
sistemaArquivos.writeFileSync(
caminhoControlador,
conteudoControlador
);

return caminhoControlador;
}

/**
* Cria uma nova visão, de acordo com o nome do controlador e o tipo de visão desejado.
* @param {string} nomeControlador O nome do controlador.
* @param {TipoVisao} tipoVisao O tipo da visão.
* @returns O caminho completo onde a visão foi criada.
*/
export function criarNovaVisao(nomeControlador: string, tipoVisao: TipoVisao) {
let caminhoVisao: string;
let conteudoVisao: string;
const diretorioVisoes = caminho.join(process.cwd(), 'visoes', nomeControlador);

switch (tipoVisao) {
case 'selecionarTudo':
caminhoVisao = caminho.join(diretorioVisoes, 'inicial.lmht');
conteudoVisao = `<lmht>\n <cabeça><título>Teste</título></cabeça>\n<corpo>Teste</corpo>\n</lmht>`;
break;
case 'selecionarUm':
caminhoVisao = caminho.join(diretorioVisoes, 'detalhes.lmht');
conteudoVisao = `<lmht>\n <cabeça><título>Teste</título></cabeça>\n<corpo>Teste</corpo>\n</lmht>`;
break;
case 'adicionar':
caminhoVisao = caminho.join(diretorioVisoes, 'adicionar.lmht');
conteudoVisao = `<lmht>\n <cabeça><título>Teste</título></cabeça>\n<corpo>Teste</corpo>\n</lmht>`;
break;
case 'atualizar':
caminhoVisao = caminho.join(diretorioVisoes, 'atualizar.lmht');
conteudoVisao = `<lmht>\n <cabeça><título>Teste</título></cabeça>\n<corpo>Teste</corpo>\n</lmht>`;
break;
case 'excluir':
caminhoVisao = caminho.join(diretorioVisoes, 'excluir.lmht');
conteudoVisao = `<lmht>\n <cabeça><título>Teste</título></cabeça>\n<corpo>Teste</corpo>\n</lmht>`;
break;
}

sistemaArquivos.writeFileSync(
caminhoVisao,
conteudoVisao
);

return caminhoVisao;
}
1 change: 1 addition & 0 deletions interface-linha-comando/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export * from './gerar';
export * from './novo';
12 changes: 6 additions & 6 deletions liquido.ts
Original file line number Diff line number Diff line change
Expand Up @@ -356,22 +356,22 @@
const { valor } = JSON.parse(retornoInterpretador.resultado.pop());

let statusHttp: number = 200;
if (valor.campos.statusHttp) {
statusHttp = valor.campos.statusHttp;
if (valor.propriedades.statusHttp) {
statusHttp = valor.propriedades.statusHttp;

Check warning on line 360 in liquido.ts

View workflow job for this annotation

GitHub Actions / Coverage annotations (🧪 jest-coverage-report-action)

🧾 Statement is not covered

Warning! Not covered statement
}

Check warning on line 361 in liquido.ts

View workflow job for this annotation

GitHub Actions / Coverage annotations (🧪 jest-coverage-report-action)

🧾 Statement is not covered

Warning! Not covered statement

Check warning on line 361 in liquido.ts

View workflow job for this annotation

GitHub Actions / Coverage annotations (🧪 jest-coverage-report-action)

🌿 Branch is not covered

Warning! Not covered branch

try {
if (valor.campos.lmht) {
const resultadoFormatacaoLmht = await this.formatadorLmht.formatar(caminhoRota, valor.campos.valores);
if (valor.propriedades.lmht) {
const resultadoFormatacaoLmht = await this.formatadorLmht.formatar(caminhoRota, valor.propriedades.valores);

Check warning on line 365 in liquido.ts

View workflow job for this annotation

GitHub Actions / Coverage annotations (🧪 jest-coverage-report-action)

🧾 Statement is not covered

Warning! Not covered statement
return {
corpoRetorno: resultadoFormatacaoLmht,
statusHttp: statusHttp
};
} else if (valor.campos.mensagem) {
} else if (valor.propriedades.mensagem) {
return {
corpoRetorno: valor.campos.mensagem,
corpoRetorno: valor.propriedades.mensagem,
statusHttp: statusHttp
};

Check warning on line 374 in liquido.ts

View workflow job for this annotation

GitHub Actions / Coverage annotations (🧪 jest-coverage-report-action)

🧾 Statement is not covered

Warning! Not covered statement
}

Check warning on line 375 in liquido.ts

View workflow job for this annotation

GitHub Actions / Coverage annotations (🧪 jest-coverage-report-action)

🧾 Statement is not covered

Warning! Not covered statement

Check warning on line 375 in liquido.ts

View workflow job for this annotation

GitHub Actions / Coverage annotations (🧪 jest-coverage-report-action)

🧾 Statement is not covered

Warning! Not covered statement

Check warning on line 375 in liquido.ts

View workflow job for this annotation

GitHub Actions / Coverage annotations (🧪 jest-coverage-report-action)

🌿 Branch is not covered

Warning! Not covered branch

Check warning on line 375 in liquido.ts

View workflow job for this annotation

GitHub Actions / Coverage annotations (🧪 jest-coverage-report-action)

🌿 Branch is not covered

Warning! Not covered branch

Check warning on line 375 in liquido.ts

View workflow job for this annotation

GitHub Actions / Coverage annotations (🧪 jest-coverage-report-action)

🌿 Branch is not covered

Warning! Not covered branch
} catch (erro: any) {
console.log(`Erro ao processar LMHT: ${erro}`);
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
},
"dependencies": {
"@designliquido/delegua-node": "^0.29.1",
"@designliquido/flexoes": "^0.0.1",
"@designliquido/flexoes": "^0.1.0",
"@designliquido/foles": "^0.6.1",
"@designliquido/lincones-sqlite": "^0.0.2",
"@designliquido/lmht-js": "^0.4.4",
Expand Down
8 changes: 4 additions & 4 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -325,10 +325,10 @@
esprima "^4.0.1"
lodash.clonedeep "^4.5.0"

"@designliquido/flexoes@^0.0.1":
version "0.0.1"
resolved "https://registry.yarnpkg.com/@designliquido/flexoes/-/flexoes-0.0.1.tgz#7e98904dbb4aec2cabc1c0879fe57b2015ef4604"
integrity sha512-zbl8Vofb4Dh97SiMyvjwLMfFaBRnUPuL9mJkj7REHWwcWxAxKMrnjLfpBtBfGg46Rq6R1XRFcV3IWWp9y4w4IA==
"@designliquido/flexoes@^0.1.0":
version "0.1.0"
resolved "https://registry.yarnpkg.com/@designliquido/flexoes/-/flexoes-0.1.0.tgz#59bc4c872f49f51ceca9fa4b8554697b82065000"
integrity sha512-5CpBpoeU4UgfwAV08pxJrJt5qljPqNQQdEU0DXkOsYcmDi0JcIBRjS/308stDuhsxuYH0AyeKn2wge+McynnoA==

"@designliquido/foles@^0.6.1":
version "0.6.1"
Expand Down
Loading