Utilizando GitLab snippets para automação de criação de projetos React

vinniciusj

Vinicius Jimenez

Posted on August 19, 2024

Utilizando GitLab snippets para automação de criação de projetos React

Durante o processo de desenvolvimento de aplicações web com React, é gasto um bom tempo configurando ESLint, Prettier, lint-staged e husky, o que acaba sendo muito custoso e repetitivo. De forma a agilizar isso, nesse artigo busco apresentar como utilizando os snippeets do GitLab é possível automatizar isso.

De acordo com a documentação do GitLab:

Com os snippets do GitLab, você pode armazenar e compartilhar trechos de código e texto com outros usuários. É possível comentar, clonar e utilizar controle de versão em snippets. Eles podem conter vários arquivos. Além disso, suportam realce de sintaxe, incorporação, download, e você pode gerenciar seus snippets com a API de snippets

Como o snippets podem ser clonados, é possível clonar trechos de códigos e arquivos para dentro de seu projeto. Utilizando essa funcionalidade, podemos dividir partes das configurações iniciais do projeto em alguns snippets e depois clonar por partes dentro do nosso projeto. Além disso, vamos criar um script em bash para criar o projeto React com vite e clonar os snippets dentro do nosso projeto.

Criando os snippets

Neste artigo, abordaremos os seguintes snippets: start react project, react project configs, language config e react resets config. Cada snippet possui uma função específica, o que é necessário devido à limitação de 10 arquivos por snippet. Essa separação também facilitará a criação do script bash. A seguir, explico a função de cada um:

  • start react project: esse é o snippet principal, a ideia dele é que o usuário possa fazer clone nele e executar o script bash;
  • react project configs: esse arquivos contém os arquivos de configuração geral da aplicação React.
  • language config: esse snippet contém os arquivos de configuração de internacionalização utilizando a biblioteca react-intl;
  • react reset config: ao iniciar um projeto React, o vite gera alguns arquivos de exemplo que geralmente acabamos deletando. O objetivo deste snippet é fornecer um projeto “limpo” para evitar esse retrabalho.

Image description

React project configs

Os arquivos que estão presentes nesse snippet são os arquivos de configurações do projeto que ficam normalmente na raiz do projeto. São eles:

  • .env.development: arquivo de variáveis de ambiente para o modo de desenvolvimento;
  • .env.production: arquivo de variáveis de ambiente para o modo de produção;
  • .eslintrc.yml: arquivo de configuração do ESLint. A nova versão 9 do ESLint tem usado um arquivo .js para essas configurações, no entanto, nem todas as bibliotecas de linting que nós utilizamos tinha suporte a versão 9, portanto, estamos utilizando a versão 8 que permite usar YAML;
  • .prettierrc.yml: arquivo de configuração de formatação do Prettier;
  • gitlab-ci.yml: arquivo de configuração de CI/CD do GitLab;
  • Dockerfile: arquivo de instruções para gerar uma imagem Docker do projeto React;
  • nginx/default.conf.template: arquivo de configuração default para iniciar um servidor NGINX. Estamos utilizando a extensão .template, pois, com ela é possível utilizar variáveis de ambiente. Essa feature foi introduzida na versão 25 do NGINX;
  • nginx/nginx.conf: arquivo de configuração do NGINX que inclui as configurações default do nginx/default.conf.template;
  • tsconfig.app.json: arquivo de configuração do TypeScript, configurado para habilitar o uso de aliases dentro da aplicação. O alias que utilizamos é o @ que faz referência ao diretório src;
  • vite.config.ts: e por fim, o arquivo de configuração do vite, que além de habilitar e resolver os aliases na hora de empacotar, ele está configurado para utilizar o import de SVG como componentes utilizando o plugin vite-plugin-svgr.

É importante notar que os arquivos que iniciam com nginx/quando forem clonados via Git serão adicionados a mesma pasta nginx/.

React reset config

Como dito anteriormente, quando um projeto React é criado, por padrão, vem bastante código “poluído” de template. Normalmente, umas das primeiras fases do desenvolvimento do projeto é remover esse código template e organizar os arquivos App.tsx e o main.tsx. Para isso, utilizamos os seguintes arquivos:

  • app.tsx: componente principal da aplicação React;
export const App = () => {
 return <div></div>
}
Enter fullscreen mode Exit fullscreen mode
  • main.tsx: componente que renderiza todo o projeto;
import { StrictMode } from 'react'

import { createRoot } from 'react-dom/client'

import { App } from './app'

const container = document.querySelector('#root')!
const root = createRoot(container)

root.render(
 <StrictMode>
  <App />
 </StrictMode>
)
Enter fullscreen mode Exit fullscreen mode
  • env.d.ts: arquivo de configuração de definição das variáveis de ambiente para serem expostas ao import.meta.
/// <reference types="vite/client" />

interface ImportMetaEnv {
 readonly VITE_API_URL: string
 readonly VITE_APP_VERSION: string
}

interface ImportMeta {
 readonly env: ImportMetaEnv
}
Enter fullscreen mode Exit fullscreen mode

Language config

Alguns projetos que desenvolvemos possuem como requisito ter dois idiomas: espanhol e português. Dessa forma, para manter um padrão, foi criado um snippet só com os arquivos necessário para agregar esse requisito nas nossas aplicações. Os arquivos desse snippet são:

  • translations/es-PY.json: arquivo JSON com todas as labels em espanhol;
  • translations/pt-BR.json: arquivo JSON com todas as labels em português;
  • utils/lang.ts: arquivo com a função para pegar o idioma preferido do usuário;
  • types/lang.ts: arquivo contendo os tipos e interfaces necessárias para a aplicação;
  • translations/index.tsx: arquivo que exporta o objeto de messages utilizados pelo provider do react-intl, contendo as labels em português e espanhol.
  • hooks/lang.ts: arquivo que exporta o hook userUserLanguage;
  • app.tsx: neste caso, o arquivo app.tsx contém o provider do react-intl utilizando as messages do arquivo translations/index.tsx;

No futuro, pretendo criar uma artigo explicando desde o começo como
aplicamos internacionalização nas aplicações React que desenvolvemos.

Start react project

E por fim, esse é o principal snippet desse artigo. Ele contém somente dois arquivos, um README.md, explicando como utiliza-lo, e o arquivo bash para criar o projeto React. O script é divido em várias funções, irie apresentar uma por vez neste artigo.

#!/bin/bash
initial_branch="main"
remote_address=""
project_name=""
template=""

parse_params(){
    while getopts ":b:r:n:t:" opt; do
        case ${opt} in
            b )
                initial_branch=$OPTARG
                ;;
            r )
                remote_address=$OPTARG
                ;;
            n )
                project_name=$OPTARG
                ;;
            t )
                template=$OPTARG
                ;;
            \? )
                echo "Invalid option: -$OPTARG" >&2
                exit 1
                ;;
            : )
                echo "Invalid option: -$OPTARG requires an argument." >&2
                exit 1
                ;;
        esac
    done
    shift $((OPTIND -1))
    if [ -z "$project_name" ]; then
        echo "Error: Project name is required." >&2
        exit 1
    fi
}
Enter fullscreen mode Exit fullscreen mode

O script aceita 4 parâmetros para ser executado:

n: nome do projeto React a ser criado (Obrigatório);
b: nome da branch inicial (Opcional);
r: endereço do repositório remoto (Opcional);
t: template que utilizamos, neste caso, um deles é o i18n, para sistemas internacionalizados (Opcional);

start_vite_project(){
    echo "Starting Vite project with React TS template..."

    npm create vite@latest "$project_name" -- --template react-ts --silent

    cd "$project_name" || { echo "Failed to change directory to $project_name"; exit 1; }
}
Enter fullscreen mode Exit fullscreen mode

A função acima é responsável por inicializar um projeto utilizando a ferramenta vite, passando como nome do projeto, o nome informado como parâmetro. Após isso, é realizado um comando cd para entrar dentro do diretório do projeto.

install_dependencies(){
    echo "Installing dependencies..."

    npm install --silent
    npm i -D eslint@8.53.0 --silent
    npm i -D prettier --silent
    npm i -D husky --silent
    npm i -D lint-staged --silent
    npm i -D vite-plugin-svgr
    npm i -D @types/node

    npx husky init --silent

    npm i -D @typescript-eslint/eslint-plugin --silent
    npm i -D @typescript-eslint/parser --silent
    npm i -D eslint-config-prettier --silent
    npm i -D eslint-import-resolver-typescript --silent
    npm i -D eslint-plugin-import --silent
    npm i -D eslint-plugin-react --silent
    npm i -D eslint-plugin-react-hooks --silent
    npm i -D eslint-plugin-react-refresh --silent
    npm i -D eslint-plugin-sonarjs --silent
    npm i -D eslint-plugin-unicorn --silent
    npm i -D eslint-plugin-unused-imports --silent
    npm i -D eslint-plugin-prettier@latest --silent
}
Enter fullscreen mode Exit fullscreen mode

A função install_dependencies instala todas as dependências adicionadas pelo vite as dependências esperadas pelo arquivo de configuração do ESLint e Prettier. Além disso, também é instalado e iniciado o Husky para adicionar rotinas de pré commit para o projeto.

set_scripts(){
    echo "Configuring formatting, type checking, and linting scripts..."

    npm pkg set scripts.lint="eslint \"src/**/*.{ts,tsx}\" --quiet"
    npm pkg set scripts.lint:fix="eslint \"src/**/*.{ts,tsx}\" --quiet --fix"
    npm pkg set scripts.type:check="tsc --noEmit"
    npm pkg set scripts.format="prettier --write \"src/**/*.{ts,tsx}\""

    npm pkg set scripts.prepare="husky install"
}
Enter fullscreen mode Exit fullscreen mode

De modo a facilitar o uso do ESLint e do Prettier, criamos alguns scripts para realizar linting e formatação do código. Além disso, ao refatorar código, nem sempre a IDE mostra todo os erros de tipo no código, portanto, criamos um script para verificar os tipos.

  • lint: realiza as verificações de linting baseado na configuração do .eslintrc.yml;
  • lint:fix: realiza as verificações e corrige os erros de linting;
  • type:check: executa a verificação de tipos sem gerar o código compilado; format: formata todo o código baseado nas configurações definidas em .prettierrc.yml.
set_lint_stage(){
    npm pkg set lint-staged='{"./src/**/*.{ts,tsx}": ["npm run format", "npm run lint:fix"]}' --json
    npm pkg set husky='{"hooks": {"pre-commit": "lint-staged"}}' --json

    echo -n "" > ./.husky/pre-commit
    echo "npx lint-staged && npm run type:check" >> ./.husky/pre-commit
}
Enter fullscreen mode Exit fullscreen mode

Essa função configura os Git hooks criados pelo Husky e utilizando o lint-staged. Neste caso, estamos utilizando somente um hook que é para ser executado antes de realizar o commit.

import_default_templates(){
    echo "Importing default templates..."  

    rm -rf src/*
    rm -rf vite.config.ts
    rm -rf tsconfig.app.json

    git clone https://gitlab.com/snippets/156.git ./src/ --quiet
    git clone https://gitlab.com/snippets/154.git ./temp --quiet

    mv ./temp/nginx . || exit
    mv ./temp/.eslintrc.yml . || exit
    mv ./temp/.prettierrc.yml . || exit
    mv ./temp/.gitlab-ci.yml . || exit
    mv ./temp/.env.development . || exit
    mv ./temp/.env.production . || exit
    mv ./temp/Dockerfile . || exit
    mv ./temp/tsconfig.app.json . || exit
    mv ./temp/vite.config.ts . || exit

    rm -rf eslint.config.js
    rm -rf temp
    rm -rf src/.git
}
Enter fullscreen mode Exit fullscreen mode

Nesta função são importados os templates padrão das aplicações React. Após criar um snippet, no lado superior direito, há um botão clone que mostra a URL para fazer o clone do snippet. Cada snippet possui um ID sequência e é clonado com base nisso. Os snippets react reset config e react project config representam os IDs 154 e 156, respectivamente.

O primeiro passo é remover todo o contéudo do diretório src, o arquivo de configuração do vite e o do TypeScript. Após isso, são clonados os snippets, um deles dentro do src(react reset config) e outro dentro de uma pasta temporária temp. Como não é possível fazer clone em um diretório com conteúdo, fazemos o clone em um pasta vazia e depois movemos para seus respectivos lugares. Todos os arquivos do diretório temp são movidos para a raiz do projeto. Por fim, é excluído a pasta temp e a pasta .git criado dentro de src, criada ao executar o clone do snippet.

import_i18n_template(){
    echo "Importing i18n template..."

    npm i react-intl --quiet
    npm i react-hook-form --quiet
    npm i jotai --quiet
    npm i flat --quiet

    git clone https://gitlab.com/snippets/155.git ./temp --quiet

    rm -rf ./src/app.tsx
    mv ./temp/* ./src/ || exit
    rm -rf ./temp
}

import_templates(){
    if [ "$template" = "i18n" ]; then
        import_i18n_template
    fi
}
Enter fullscreen mode Exit fullscreen mode

Caso o usuário tenha informado o parâmetro de template, esta função é responsável por importar os templates dentro do projeto. Neste caso, existe somente um template i18n. Novamente, como não podemos fazer o clone em um repositório não vazio, é necessário utilizar uma pasta temp.

start_git_repository(){
    echo "Initializing Git repository..."
    git init --initial-branch="$initial_branch"
    npm run prepare --silent
    git add .

    echo "Making initial commit..."
    git commit -m "feat: initial commit"

    if [ -n "$remote_address" ]; then
        echo "Adding remote repository..."
        git remote add origin "$remote_address"
        echo "Pushing initial commit..."
        git push origin "$initial_branch"
    fi 
}
Enter fullscreen mode Exit fullscreen mode

O script bash também inicializa um repositório Git e inicializando as rotinas de pré-commit definidas pelo Husky. Além disso, o script também realiza o commit inicial e, se for informado um repositório remoto, a função irá adiciona-lo como origin na lista de repositórios remotos e realizar push para aquele repositório remoto na branch inicial.

run(){
    parse_params "$@"
    start_vite_project
    install_dependencies
    set_scripts
    set_lint_stage
    import_default_templates

    if [ -n "$template" ]; then
        import_templates
    fi

    start_git_repository
    echo "Project setup completed successfully!"
}

run "$@"
Enter fullscreen mode Exit fullscreen mode

Por último, mas não menos importante, a função run é responsável por chamar todas as funções apresentadas acima na ordem correta. Caso tenha sido informado um template, ela irá importá-lo também. Em Bash, $@ é uma variável especial que representa todos os argumentos passados para um script ou função, como uma lista separada por espaços. Cada argumento é tratado como uma string individual.

Executando o script

Antes de executar o script, é necessário dar permissão para executa-lo. Para usuários de Linux, basta executar o seguinte comando.

$ chmod +x start-react-project.sh
Enter fullscreen mode Exit fullscreen mode

Após torna-lo executável, rode o seguinte comando:

$ ./start-react-project.sh -n <NOME_DO_PROJETO> -b <BRANCH_INICIAL> -r <REPO_REMOTO> -t <TEMPLATE>
Enter fullscreen mode Exit fullscreen mode

A execução do script pode demorar alguns segundos até instalar todas as bibliotecas e fazer clones de todos os snippets. Neste artigo, irie considerar o template i18n. A estrutura do projeto será a seguinte:

./
├── nginx/
│   ├── default.conf.template
│   └── nginx.conf
├── node_modules/  
├── public/
│   └── vite.svg
├── src/
│   ├── hooks/
│   │   └── lang.ts
│   ├── translations/
│   │   ├── es-PY.json
│   │   ├── index.ts
│   │   └── pt-BR.json
│   ├── types/
│   │   └── lang.ts
│   ├── utils/
│   │   └── lang.ts
│   ├── app.tsx
│   ├── env.d.ts
│   ├── main.tsx
│   └── vite-env.d.ts
├── Dockerfile
├── README.md
├── index.html
├── package-lock.json
├── package.json
├── tsconfig.app.json
├── tsconfig.json
├── tsconfig.node.json
└── vite.config.ts
Enter fullscreen mode Exit fullscreen mode

Conclusão

Neste artigo busquei mostrar um pouco de como, utilizando uma ferramenta simples, é possível criar automações para facilitar no desenvolvimento de aplicações. Espero que tenham gostado desse artigo, este é o meu primeiro artigo aqui no dev.to, espero que seja o primeiro de muitos. Muito obrigado pela atenção!

💖 💪 🙅 🚩
vinniciusj
Vinicius Jimenez

Posted on August 19, 2024

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related