dotMind é um grupo formado para o desenvolvimento de sistemas. Este repositório tem por finalidade facilitar a criação de um ambiente de desenvolvimento baseado nas tecnologias NextJS, TypeScript, Strapi, Postgres dentre outras. Este boilerplate foi testado em máquinas com chips Apple M1
{ INSERIR REFERENCIA AO BOILERPLATE DO WILLIAN JUSTEN}
yarn create next-app -e https://github.com/<repo boilerplate> <nome projeto>
- Download do Docker para Mac with Apple Silicon
- Estrutura do diretório para utilização do container
- Arquivo docker composer para configuração do container
- Teste do container do postgres
cd ~/MeuProjeto
mkdir data
docker-compose.yml:
version: "3"
services:
postgres:
image: postgres
environment:
POSTGRES_USER: strapi
POSTGRES_PASSWORD: strapi123
volumes:
- ./data:/var/lib/postgresql/data
ports:
- "5432:5432”
docker-compose up -d
- Download NVM como versionador
- Habilitar base do XCode
- Instalar yarn
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.1/install.sh | bash
sudo Xcode-select --install
nvm install 16.14.0
npm install --global yarn
- Comandos NVM úteis:
nvm current
nvm ls
nvm use 16.14.0
nvm uninstall 13.17.6
- Instalar Utilizando:
- installation type Custom
- default database client postgres
- database name strapi
- host 127.0.0.1
- port 5432
- username strapi
- password strapi123
- SSL connection No
yarn create strapi-app api
- Diretório de dados do Postgres é data. Caso queira apaagar a base de Dados, basta remover esse diretório e reiniciar o container
- Após a instalação do strapi, entre no diretório api e execute o strapi para configurar o acesso do usuario administrador
- Utilize para First/LastName strapi / strapi e para senha Strapi123
- Dowload VSCode Download VSCode
- Instalação das extensions
- clone o repositório: https://github.com/React-Avancado/reactavancado-extension-pack
- instale o vsce
- crie o pacote da extensão
- instale a extensão a partir do arquivo .visix gerado anteriormente
- VSCode / Extensions Menu / More / Install from VISIX / Reload Now
wget https://code.visualstudio.com/sha/download?build=stable&os=darwin-arm64
git clone https://github.com/React-Avancado/reactavancado-extension-pack
cd reactavancado-extension-pack
npm install -g vsce
vsce package
- Adicionar VSCode ao path abrindo o Command Palette (Cmd+Shift+P)
- Procure por 'shell command'
- Opção Install 'code' command in PATH
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
brew install git
cd ~/MeuProjeto
git init
- Criar um projeto boilerplate
- Sinalizar utilização de TypeScript
- Verificar e instalar dependencias para TypeScript
- Teste, tipagem e configuração do typescript
cd ~/MeuProjeto
yarn create next-app frontpage
cd frontpage
touch tsconfig.json
yarn dev
yarn add --dev [email protected] @types/react @types/node
yarn dev
tsconfig.json:
{
"compilerOptions": {
"baseUrl": "src",
"target": "es5",
"lib": [
"dom",
"dom.iterable",
"esnext"
],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"noEmit": true,
"incremental": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve"
},
"include": [
"next-env.d.ts",
"**/*.ts",
"**/*.tsx"
],
"exclude": [
"node_modules"
]
}
- Criar diretório src e mover pasta pages para dentro de src
- renomear extensão da index de .ts para .tsx
cd ~/MeuProjeto/frontpage
mkdir src
mv pages/index.js pages/index.jsx
mv pages src
- Configurar a consistência dos arquivos no VSCode via editor config
.editorconfig:
# editorconfig.org
root = true
[*]
indent_style = spaces
indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
- Instalar o ESLint escolhendo:
- checar sintaxe e encontrar problemas
- javascript modules - import / export
- react
- typescript
- browser
- json
- yarn -> será apresentado uma relação de plugins para ser instalada manualmente.
npx eslint --init
yarn add --dev eslint-plugin-react@latest @typescript-eslint/eslint-plugin@latest @typescript-eslint/parser@latest eslint@latest
- Instalar ESLINT-PLUGIN-REACT-HOOKS
- Configurar Plugins
- ESLINT-PLUGIN-REACT-HOOKS: melhora o desenvolvimento com relação à utilização de hooks e dependências
- Desligar PROP-TYPES: para não sermos lembrados a todo momento de que não há PROP TYPES
- Desligar REACT-IN-JSX-SCOPE: o Nextjs já importa React globalmente
- Desligar EXPLICT-MODULE-BOUNDARY-TYPES: evita a necessidade de tipar todo retorno de função aumentando assim a verbosidade
- Observar configuração customizada separada para "plugins" e "rules"
- Configurar a versão do react em "settings" para o correto funcionamento do REACT-PLUGIN
- Instalar ESLINT-PLUGIN-NEXT para funcionamento adequado do modo PRODUÇÃO (NODE_ENV=production; yarn build)
Migrating Existing Config: eslint-plugin-next
Configuração dos plugins: typescript-eslint
yarn add eslint-plugin-react-hooks --dev
yarn add @next/eslint-plugin-next --dev
.eslintrc.json:
{
"root": true,
"env": {
"browser": true,
"es2021": true
},
"settings": {
"react": {
"version": "detect"
}
},
"extends": [
"eslint:recommended",
"plugin:react/recommended",
"plugin:@typescript-eslint/recommended",
"plugin:@next/next/recommended"
],
"parser": "@typescript-eslint/parser",
"parserOptions": {
"ecmaFeatures": {
"jsx": true
},
"ecmaVersion": "latest",
"sourceType": "module"
},
"plugins": [
"react",
"react-hooks",
"@typescript-eslint"
],
"rules": {
"react-hooks/rules-of-hooks": "error",
"react-hooks/exhaustive-deps": "warn",
"react/prop-types": "off",
"react/react-in-jsx-scope": "off",
"@typescript-eslint/explicit-module-boundary-types": "off",
"@typescript-eslint/no-non-null-assertion": "off"
}
}
- Habilitar o plugin do eslint dentro do VSCode Extensions
- Configurar "scripts" de package.json para chamar o ESLINT
~/MeuProjeto/frontpage/package.json:
{
"name": "frontpage",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "eslint 'src/**/*.{js,jsx}'"
},
"dependencies": {
"next": "12.1.0",
"react": "17.0.2",
"react-dom": "17.0.2"
},
"devDependencies": {
"@types/node": "^17.0.21",
"@types/react": "^17.0.39",
"@typescript-eslint/eslint-plugin": "^5.13.0",
"@typescript-eslint/parser": "^5.13.0",
"eslint": "^8.10.0",
"eslint-config-next": "12.1.0",
"eslint-plugin-react": "^7.29.2",
"eslint-plugin-react-hooks": "^4.3.0",
"typescript": "4.5.5"
}
}
- Testar comando eslint no terminal
yarn lint
- Instalar e configurar
yarn add --dev --exact prettier
~/MeuProjeto/frontpage/.prettierrc:
{
"trailingComma": "none",
"semi": false,
"singleQuote": true
}
- Instalar plugins de integração com eslint: prettier como uma regra do eslint
yarn add --dev eslint-plugin-prettier eslint-config-prettier
- Configurar regra para execução do prettier pelo eslint em .eslintrc.json
~/MeuProjeto/frontpage/.eslintrc.json:
{
"env": {
"browser": true,
"es2021": true
},
"settings": {
"react": {
"version": "detect"
}
},
"extends": [
"eslint:recommended",
"plugin:react/recommended",
"plugin:@typescript-eslint/recommended",
"plugin:prettier/recommended"
],
"parser": "@typescript-eslint/parser",
"parserOptions": {
"ecmaFeatures": {
"jsx": true
},
"ecmaVersion": "latest",
"sourceType": "module"
},
"plugins": [
"react",
"react-hooks",
"@typescript-eslint",
"prettier"
],
"rules": {
"react-hooks/rules-of-hooks": "error",
"react-hooks/exhaustive-deps": "warn",
"react/prop-types": "off",
"react/react-in-jsx-scope": "off",
"@typescript-eslint/explicit-module-boundary-types": "off",
"@typescript-eslint/no-non-null-assertion": "off"
}
}
- Configurar VSCode para executar eslint/prettier antes de salvar
cd ~/MeuProjeto/frontpage
mkdir .vscode
cd .vscode
touch settings.json
settings.json`
{
"window.zoomLevel": 2,
"editor.fontSize": 10,
"terminal.integrated.fontSize": 10,
"editor.formatOnSave": false,
"editor.codeActionsOnSave": {
"source.fixAll.eslint": true
}
}
- Reload do plugin ESLINT para ativar alterações no VSCode
- Instalar e configurar Husky e Lint-staged
yarn add husky lint-staged --dev
yarn husky install
yarn husky add .husky/pre-commit "yarn lint-staged"
package.json:
{
"name": "frontpage",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "eslint 'src/**/*.{js,jsx}' --max-warnings=0"
},
"lint-staged": {
"src/**/*": [ "yarn lint --fix" ]
},
"dependencies": {
"next": "12.1.0",
"react": "17.0.2",
"react-dom": "17.0.2"
},
"devDependencies": {
"@types/node": "^17.0.21",
"@types/react": "^17.0.39",
"@typescript-eslint/eslint-plugin": "^5.13.0",
"@typescript-eslint/parser": "^5.13.0",
"eslint": "^8.10.0",
"eslint-config-next": "12.1.0",
"eslint-config-prettier": "^8.4.0",
"eslint-plugin-prettier": "^4.0.0",
"eslint-plugin-react": "^7.29.2",
"eslint-plugin-react-hooks": "^4.3.0",
"husky": "^7.0.4",
"prettier": "2.5.1",
"typescript": "4.5.5"
}
}
- Caso o diretório .git seja removido, instalar novamente o husky e adicionar novamento o hook para o pre-commit
git init
yarn husky install
yarn husky add .husky/pre-commit "yarn lint-staged"
- Configurar
- "node": true adicionado ao "env" de .eslintrc.json pelo fato de que jest utiliza module.exports e a falta desta configuração faria o eslint gerar avisos
.eslintrc.json:
{
"env": {
"browser": true,
"es2021": true,
"jest": true,
"node": true
},
"settings": {
"react": {
"version": "detect"
}
},
"extends": [
"eslint:recommended",
"plugin:react/recommended",
"plugin:@typescript-eslint/recommended",
"plugin:prettier/recommended"
],
"parser": "@typescript-eslint/parser",
"parserOptions": {
"ecmaFeatures": {
"jsx": true
},
"ecmaVersion": "latest",
"sourceType": "module"
},
"plugins": [
"react",
"react-hooks",
"@typescript-eslint",
"prettier"
],
"rules": {
"react-hooks/rules-of-hooks": "error",
"react-hooks/exhaustive-deps": "warn",
"react/prop-types": "off",
"react/react-in-jsx-scope": "off",
"@typescript-eslint/explicit-module-boundary-types": "off",
"@typescript-eslint/no-non-null-assertion": "off"
}
}
- Instalar e configurar Jest
yarn add jest @babel/preset-typescript @types/jest --dev
~/MeuProjeto/frontpage/jest.config.js:
module.exports = {
testEnvironment: 'jsdom',
testPathIgnorePatterns: ['/node_modules/','/.next/'],
collectCoverage: true,
collectCoverageFrom: ['src/**/*.ts(x)?', '!src/**/*.stories.tsx'],
setupFilesAfterEnv: ['<rootDir>/.jest/setup.ts'],
moduleNameMapper: {
'^styled-components': '<rootDir>/node_modules/styled-components/dist/styled-components.browser.cjs.js'
}
}
- Configurar
- next/babel para escrever os códigos de teste no Jest utilizando novidades de JavaScript
- @babel/preset-typescript pelo fato de estarem escritos em typescript
~/MeuProjeto/frontpage/.babelrc`
{
"presets": ["next/babel", "@babel/preset-typescript"]
}
~/MeuProjeto/frontpage/package.json`
{
"name": "frontpage",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "eslint 'src/**/*.{js,jsx}' --max-warnings=0",
"test": "jest"
},
"lint-staged": {
"src/**/*": [
"yarn lint --fix"
]
},
"dependencies": {
"next": "12.1.0",
"react": "17.0.2",
"react-dom": "17.0.2"
},
"devDependencies": {
"@babel/preset-typescript": "^7.16.7",
"@types/jest": "^27.4.1",
"@types/node": "^17.0.21",
"@types/react": "^17.0.39",
"@typescript-eslint/eslint-plugin": "^5.13.0",
"@typescript-eslint/parser": "^5.13.0",
"eslint": "^8.10.0",
"eslint-config-next": "12.1.0",
"eslint-config-prettier": "^8.4.0",
"eslint-plugin-prettier": "^4.0.0",
"eslint-plugin-react": "^7.29.2",
"eslint-plugin-react-hooks": "^4.3.0",
"husky": "^7.0.4",
"jest": "^27.5.1",
"lint-staged": "^12.3.4",
"prettier": "2.5.1",
"typescript": "4.5.5"
}
}
- Jest Setup
- .jest/setup.ts contém as informações do Jest tais como imports de assets, expects e outros artefatos para trabalhar com jsdom
cd ~/MeuProjeto/frontpage
mkdir .jest
cd .jest
touch setup.ts
- Instalar React Testing Library e Matcher do Jest (jest-dom)
- Criar arquivos para teste e testar
cd ~/MeuProjeto/frontpage
yarn add --dev @testing-library/react @testing-library/jest-dom
echo "import '@testing-library/jest-dom' >> .jest/setup.ts"
cd src
mkdir components components/Main
cd components/Main
touch index.tsx test.tsx
index.tsx:
const Main = () => (
<main>
<h1>React</h1>
</main>
)
export default Main
- Baixar e imprimir o cheat sheet para funções de teste Cheat Sheet
test.tsx:
import { render, screen } from '@testing-library/react'
import Main from '.'
describe ('<Main />', () => {
it ('should render the heading', () => {
render(<Main />)
expect(
screen.getByRole('heading', { name: /react avançado/i })
).toBeInTheDocument()
})
})
- Rodar o teste e verificar ocorrência intencional da falha
yarn test
yarn run v1.22.17
$ jest
FAIL src/components/Main/test.tsx
<Main />
✕ should render the heading (40 ms)
● <Main /> › should render the heading
TestingLibraryElementError: Unable to find an accessible element with the role "heading" and name `/react avançado/i`
Here are the accessible roles:
main:
Name "":
<main />
--------------------------------------------------
heading:
Name "Avançado":
<h1 />
--------------------------------------------------
Ignored nodes: comments, <script />, <style />
<body>
<div>
<main>
<h1>
Avançado
</h1>
</main>
</div>
</body>
6 | it('should render the heading', () => {
7 | render(<Main />)
> 8 | expect(screen.getByRole('heading', { name: /react avançado/i }))
| ^
9 | .toBeInTheDocument
10 | })
11 | })
at Object.getElementError (node_modules/@testing-library/dom/dist/config.js:38:19)
at node_modules/@testing-library/dom/dist/query-helpers.js:90:38
at node_modules/@testing-library/dom/dist/query-helpers.js:62:17
at getByRole (node_modules/@testing-library/dom/dist/query-helpers.js:111:19)
at Object.<anonymous> (src/components/Main/test.tsx:8:19)
at TestScheduler.scheduleTests (node_modules/@jest/core/build/TestScheduler.js:333:13)
at runJest (node_modules/@jest/core/build/runJest.js:404:19)
at _run10000 (node_modules/@jest/core/build/cli/index.js:320:7)
at runCLI (node_modules/@jest/core/build/cli/index.js:173:3)
-----------|---------|----------|---------|---------|-------------------
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
-----------|---------|----------|---------|---------|-------------------
All files | 100 | 100 | 100 | 100 |
index.tsx | 100 | 100 | 100 | 100 |
-----------|---------|----------|---------|---------|-------------------
Test Suites: 1 failed, 1 total
Tests: 1 failed, 1 total
Snapshots: 0 total
Time: 0.855 s, estimated 1 s
Ran all test suites.
error Command failed with exit code 1.
info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.
- Alterar index.tsx para passar no teste e novamente verificar
~/MeuProjeto/frontpage/src/components/Main/index.tsx:
const Main = () => (
<main>
<h1>React Avançado</h1>
</main>
)
export default Main
yarn test
yarn run v1.22.17
$ jest
PASS src/components/Main/test.tsx
<Main />
✓ should render the heading (29 ms)
-----------|---------|----------|---------|---------|-------------------
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
-----------|---------|----------|---------|---------|-------------------
All files | 100 | 100 | 100 | 100 |
index.tsx | 100 | 100 | 100 | 100 |
-----------|---------|----------|---------|---------|-------------------
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 0.828 s, estimated 1 s
Ran all test suites.
✨ Done in 1.99s.
- Configurar script para teste assistido, "test:watch"
package.json:
{
"name": "frontpage",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "eslint 'src/**/*.{js,jsx}' --max-warnings=0",
"test": "jest",
"test:watch": "yarn test --watch"
},
"lint-staged": {
"src/**/*": [
"yarn lint --fix"
]
},
"dependencies": {
"next": "12.1.0",
"react": "17.0.2",
"react-dom": "17.0.2"
},
"devDependencies": {
"@babel/preset-typescript": "^7.16.7",
"@testing-library/jest-dom": "^5.16.2",
"@testing-library/react": "^12.1.3",
"@types/jest": "^27.4.1",
"@types/node": "^17.0.21",
"@types/react": "^17.0.39",
"@typescript-eslint/eslint-plugin": "^5.13.0",
"@typescript-eslint/parser": "^5.13.0",
"eslint": "^8.10.0",
"eslint-config-next": "12.1.0",
"eslint-config-prettier": "^8.4.0",
"eslint-plugin-prettier": "^4.0.0",
"eslint-plugin-react": "^7.29.2",
"eslint-plugin-react-hooks": "^4.3.0",
"husky": "^7.0.4",
"jest": "^27.5.1",
"lint-staged": "^12.3.4",
"prettier": "2.5.1",
"typescript": "4.5.5"
}
}
- Configurar snapshot no arquivo de teste
src/components/Main/test.tsx:
import { render, screen } from '@testing-library/react'
import Main from '.'
describe('<Main />', () => {
it('should render the heading', () => {
const { container } = render(<Main />)
expect(screen.getByRole('heading', { name: /react avançado/i }))
.toBeInTheDocument
expect(container.firstChild).toMatchSnapshot()
})
})
- Para testar o snapshot, rodar o teste uma vez e depois alter o heading de h1 para h2 em main.tsx, de forma que passe no primeiro test mas falhe no snapshot
- Notas:
- Se estiver utilizando "test:watch", ao digitar "u" teremos a atualização do snapshot
- No teste manual o snapshot é atulizado com yarn test -u
- O snapshot faz parte do repositório
O Lint Staged é executado no momento do commit e evita que bugs entrem no versionamento
- Adicionar testes ao LINT-STAGED
- "yarn test --findRelatedTests --bail":
- bail serve para que tudo pare ao primeiro teste que não passe
- findRelatedTests serve para que modificações em arquivos que não sejam relacionadas com os testes não quebrem o processo informando que não há testes para testar
package.json:
{
"name": "frontpage",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "eslint 'src/**/*.{js,jsx}' --max-warnings=0",
"test": "jest",
"test:watch": "yarn test --watch"
},
"lint-staged": {
"src/**/*": [
"yarn lint --fix",
"yarn test --findRelatedTests --bail"
]
},
"dependencies": {
"next": "12.1.0",
"react": "17.0.2",
"react-dom": "17.0.2"
},
"devDependencies": {
"@babel/preset-typescript": "^7.16.7",
"@testing-library/jest-dom": "^5.16.2",
"@testing-library/react": "^12.1.3",
"@types/jest": "^27.4.1",
"@types/node": "^17.0.21",
"@types/react": "^17.0.39",
"@typescript-eslint/eslint-plugin": "^5.13.0",
"@typescript-eslint/parser": "^5.13.0",
"eslint": "^8.10.0",
"eslint-config-next": "12.1.0",
"eslint-config-prettier": "^8.4.0",
"eslint-plugin-prettier": "^4.0.0",
"eslint-plugin-react": "^7.29.2",
"eslint-plugin-react-hooks": "^4.3.0",
"husky": "^7.0.4",
"jest": "^27.5.1",
"lint-staged": "^12.3.4",
"prettier": "2.5.1",
"typescript": "4.5.5"
}
}
- Instalar Styled-Components
- Instalar dependencias de desenvolvimento e configurar Babel
- Instalar integração de Styled Components com JEST
yarn add styled-components
yarn add --dev @types/styled-components babel-plugin-styled-components jest-styled-components
.jest/setup.ts:
import '@testing-library/jest-dom'
import 'jest-styled-components'
.babelrc:
{
"presets": ["next/babel", "@babel/preset-typescript"],
"plugins": [
[ "babel-plugin-styled-components",
{
"ssr": true
}
]
]
}
- Configurar _document.tsx, arquivo padrão do Nextjs, para passar informações ao Nextjs sobre a renderização das páginas
- Disponibilizar a função Render para permitir que se edite a linguagem HTML e outros detalhes sem que o nextjs faça uma renderização padrão das paginas, o que ocasionaria em conflitos com o Styled Components
pages/_document.tsx:
import Document, {
DocumentContext,
Html,
Head,
Main,
NextScript
} from 'next/document'
import { ServerStyleSheet } from 'styled-components'
export default class MyDocument extends Document {
static async getInitialProps(ctx: DocumentContext) {
const sheet = new ServerStyleSheet()
const originalRenderPage = ctx.renderPage
try {
ctx.renderPage = () =>
originalRenderPage({
enhanceApp: (App) => (props) => sheet.collectStyles(<App {...props} />),
})
const initialProps = await Document.getInitialProps(ctx)
return {
...initialProps,
styles: (
<>
{initialProps.styles}
{sheet.getStyleElement()}
</>
),
}
} finally {
sheet.seal()
}
}
render(): JSX.Element {
return (
<Html lang="pt-BR">
<Head />
<body>
<Main />
<NextScript />
</body>
</Html>
)
}
}
- Testar o carregamento dos originais gerados pelo NextJs acessando http://localhost:3000
yarn dev
yarn run v1.22.17
$ next dev
ready - started server on 0.0.0.0:3000, url: http://localhost:3000
info - Disabled SWC as replacement for Babel because of custom Babel configuration ".babelrc" https://nextjs.org/docs/messages/swc-disabled
event - compiled client and server successfully in 198 ms (144 modules)
wait - compiling /_error (client and server)...
event - compiled client and server successfully in 41 ms (145 modules)
wait - compiling / (client and server)...
event - compiled client and server successfully in 66 ms (168 modules)
- Criando estilos globais com o helper createGlobalStyle
- Estruturar diretório e arquivo global.ts
- Organizar bordas, marges, box-sizing, fonts padrão, temas e etc
- Organizar src/pages/index.tsx
- Criar src/pages/_app.tsx para importar o global style
- _app.tsx tem a função de permitir as seguintes funcionalidades:
- Persistência de layout durante mudança de páginas
- persistência de estado durante a navegação
- injeção de dados adicionais
- global css
- path alias e absolute imports (tsconfig.json)
cd ~/MeuProjeto/frontpage/src/
mkdir styles
touch styles/global.ts
~/MeuProjeto/frontpage/tsconfig.json:
{
"compilerOptions": {
"baseUrl": "src",
"target": "es5",
"lib": [
"dom",
"dom.iterable",
"esnext"
],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"noEmit": true,
"incremental": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve"
},
"include": [
"next-env.d.ts",
"**/*.ts",
"**/*.tsx"
],
"exclude": [
"node_modules"
]
}
styles/global.ts:
import { createGlobalStyle } from 'styled-components'
const GlobalStyles = createGlobalStyle`
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
html {
font-size: 62.5%;
}
html, body, #__next {
height: 100%;
}
body {
font-family; -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif
}
`
export default GlobalStyles
pages/_app.tsx:
import type { AppProps } from 'next/app'
import Head from 'next/head'
import GlobalStyles from 'styles/global'
function App({ Component, pageProps }: AppProps) {
return (
<>
<Head>
<title>React Avançado - Boilerplate</title>
<link rel="shortcut icon" href="/img/icon-512.png" />
<link rel="apple-touch-icon" href="/img/icon-512.png" />
<meta
name="description"
content="A simple project starter to work with TypeScript, React, NextJS and Styled Components"
/>
</Head>
<GlobalStyles />
<Component {...pageProps} />
</>
)
}
export default App
pages/index.tsx:
import Main from 'components/Main'
export default function Home() {
return <Main />
}
- Criar para cada componente um arquivo styles.ts e configurar os estilos
- No componente, index.tsx, importar todos os estilos como 'S' para facilitar a identificação entre estilos e componentes
- Editar testes para incluir a verificação de estilos
cd ~/MeuProjeto/frontpage/src/components/Main
styles.ts:
import styled from 'styled-components'
export const Wrapper = styled.main`
background-color: #06092b;
color: #fff;
width: 100%;
height: 100%;
padding: 3rem;
display: flex;
flex-direction: column;
text-align: center;
align-items: center;
justify-content: center;
`
export const Logo = styled.img`
width: 25rem;
margin-bottom: 2rem;
`
export const Title = styled.h1`
font-size: 2.5rem;
`
export const Description = styled.h2`
font-size: 2rem;
font-weight: 400;
`
export const Illustration = styled.img`
margin-top: 3rem;
width: min(30rem, 100%);
`
index.tsx:
import * as S from './styles'
const Main = () => (
<S.Wrapper>
<S.Logo
src="/img/logo.svg"
alt="Imagem de um átomo e React Avançado escrito ao lado."
/>
<S.Title>React Avançado</S.Title>
<S.Description>
TypeScript, ReactJS, NextJs e Styled Components
</S.Description>
<S.Illustration
src="img/hero-illustration.svg"
alt="Um desenvolvedor de frente para uma tela com código."
/>
</S.Wrapper>
)
export default Main
test.tsx:
import { render, screen } from '@testing-library/react'
import Main from '.'
describe('<Main />', () => {
it('should render the heading', () => {
const { container } = render(<Main />)
expect(screen.getByRole('heading', { name: /react avançado/i }))
.toBeInTheDocument()
expect(container.firstChild).toMatchSnapshot()
})
it('should render styles correctly', () => {
const { container } = render(<Main />)
expect(container.firstChild).toHaveStyle(`
background-color: #06092b;
display: flex;
flex-direction: column;
`)
})
})
- Instalar e configurar para utilizar global styles
- Instalar http-server para teste de staticos
- Instalar plugin de integração com o ESLint
- Instalar extension Storybook helper (Riccardo Forina) no VSCode
- Configurar stories para serem encontrados junto da pasta de cada componente
- Nomenclatura
- Iniciar named story exports com Letra Maiúcula
- Configurar diretórios e ajustar para que a pasta public seja utilizada pelos stories
- Alterar component Main para receber parâmetros
cd ~/MeuProjeto/frontpage
yarn add --dev eslint-plugin-storybook
npx sb init
rm -rf stories
touch src/components/Main/stories.txs
brew install http-server
package.json:
{
"name": "frontpage",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "eslint 'src/**/*.{js,jsx}' --max-warnings=0",
"test": "jest",
"test:watch": "yarn test --watch",
"storybook": "start-storybook -s ./public -p 6006",
"build-storybook": "build-storybook -s ./public"
},
"lint-staged": {
"src/**/*": [
"yarn lint --fix",
"yarn test --findRelatedTests --bail"
]
},
"dependencies": {
"next": "12.1.0",
"react": "17.0.2",
"react-dom": "17.0.2",
"styled-components": "^5.3.3"
},
"devDependencies": {
"@babel/core": "^7.17.5",
"@babel/preset-typescript": "^7.16.7",
"@storybook/addon-essentials": "^6.4.19",
"@storybook/react": "^6.4.19",
"@storybook/testing-library": "^0.0.9",
"@testing-library/jest-dom": "^5.16.2",
"@testing-library/react": "^12.1.3",
"@types/jest": "^27.4.1",
"@types/node": "^17.0.21",
"@types/react": "^17.0.39",
"@types/styled-components": "^5.1.24",
"@typescript-eslint/eslint-plugin": "^5.13.0",
"@typescript-eslint/parser": "^5.13.0",
"babel-loader": "^8.2.3",
"babel-plugin-styled-components": "^2.0.6",
"eslint": "^8.10.0",
"eslint-config-next": "12.1.0",
"eslint-config-prettier": "^8.4.0",
"eslint-plugin-prettier": "^4.0.0",
"eslint-plugin-react": "^7.29.2",
"eslint-plugin-react-hooks": "^4.3.0",
"eslint-plugin-storybook": "^0.5.7",
"husky": "^7.0.4",
"jest": "^27.5.1",
"jest-styled-components": "^7.0.8",
"lint-staged": "^12.3.4",
"prettier": "2.5.1",
"typescript": "4.5.5"
}
}
.storybook/preview.js:
import GlobalStyles from '../src/styles/global'
export const parameters = {
actions: { argTypesRegex: "^on[A-Z].*" },
controls: {
matchers: {
color: /(background|color)$/i,
date: /Date$/,
},
},
}
export const decorators = [
(Story) => (
<>
<GlobalStyles />
<Story />
</>
),
]
.storybook/main.js:
module.exports = {
"stories": [
"../src/components/**/*.stories.mdx",
"../src/components/**/stories.@(js|jsx|ts|tsx)"
],
"addons": [ "@storybook/addon-essentials" ],
"framework": "@storybook/react"
}
- Configurar Componente Main e Executar Stories
src/components/Main/index.tsx:
import * as S from './styles'
type mainProps = {
title: string
description: string
}
const Main = ({
title = 'React Avançado',
description = 'TypeScript, ReactJS, NextJs e Styled Components'
}: mainProps) => (
<S.Wrapper>
<S.Logo
src="/img/logo.svg"
alt="Imagem de um átomo e React Avançado escrito ao lado."
/>
<S.Title>{title}</S.Title>
<S.Description>{description}</S.Description>
<S.Illustration
src="img/hero-illustration.svg"
alt="Um desenvolvedor de frente para uma tela com código."
/>
</S.Wrapper>
)
export default Main
src/components/Main/stories.tsx:
import { ComponentStory, ComponentMeta } from '@storybook/react'
import Main from '.'
export default {
title: 'Main',
component: Main,
args: {
title: 'Valores Padrão',
description: 'Descrição Padrão'
}
} as ComponentMeta<typeof Main>
const Template: ComponentStory<typeof Main> = (args) => <Main {...args} />
export const Basic = Template.bind({})
export const Advanced = Template.bind({})
export const Complex = Template.bind({})
Advanced.args = {
title: 'React Advanced Args',
description: 'TypeScript, ReactJS, NextJs e Styled Components'
}
Complex.args = {
title: 'React Complex Args',
description: 'TypeScript, ReactJS, NextJs e Styled Components'
}
yarn storybook
webpack built preview d8347124f2dac1508f80 in 4091ms
╭───────────────────────────────────────────────────╮
│ │
│ Storybook 6.4.19 for React started │
│ 4.24 s for preview │
│ │
│ Local: http://localhost:6006/ │
│ On your network: http://192.168.1.43:6006/ │
│ │
╰───────────────────────────────────────────────────╯
yarn build-storybook
cd ~/MeuProjeto/frontpage/storybook-static
http-server
Starting up http-server, serving ./
http-server version: 14.1.0
http-server settings:
CORS: disabled
Cache: 3600 seconds
Connection Timeout: 120 seconds
Directory Listings: visible
AutoIndex: visible
Serve GZIP Files: false
Serve Brotli Files: false
Default File Extension: none
Available on:
http://127.0.0.1:8080
http://192.168.1.43:8080
Hit CTRL-C to stop the server
- Funcionamento off-line
- Instalar e Configurar puglins next-pwa
- Configurar PWA ativo apenas em Produção
- Configurar Manifest e Head Meta
cd ~/MeuProjeto/frontpage
yarn add next-pwa
next.config.js:
const withPWA = require('next-pwa')
const isProd = process.env.NODE_ENV === 'production'
/** @type {import('next').NextConfig} */
const nextConfig = {
reactStrictMode: !isProd,
pwa: {
dest: 'public',
disable: !isProd
}
}
module.exports = withPWA(nextConfig)
public/manifest.json:
{
"name": "React Avançado - Boilerplate",
"short_name": "React Avançado",
"icons": [
{
"src": "/img/icon-192.png",
"type": "image/png",
"sizes": "192x192"
},
{
"src": "/img/icon-512.png",
"type": "image/png",
"sizes": "512x512"
}
],
"background_color": "#06092B",
"description": "Boilerplate utilizando Typescript, React, NextJS e Styled Components!",
"display": "fullscreen",
"start_url": "/",
"theme_color": "#06092B"
}
src/pages/_app.tsx:
import type { AppProps } from 'next/app'
import Head from 'next/head'
import GlobalStyles from 'styles/global'
function App({ Component, pageProps }: AppProps) {
return (
<>
<Head>
<title>React Avançado - Boilerplate</title>
<link rel="shortcut icon" href="/img/icon-512.png" />
<link rel="apple-touch-icon" href="/img/icon-512.png" />
<link rel="manifest" href="/static/manifest.json" />
<meta
name="description"
content="A simple project starter to work with TypeScript, React, NextJS and Styled Components"
/>
</Head>
<GlobalStyles />
<Component {...pageProps} />
</>
)
}
export default App
- Testar PWA colocando no ambiente de Produção
NODE_ENV=production
yarn build
yarn start
- Criar repositório no github
- Fazer caching de github credentials
- Instalar Github CLI gh
- github.com
- https
- Autenticate to your GitHub credentias
- Login with web browser
- Instalar Github CLI gh
cd ~/MeuProjeto/frontpage
brew install gh
gh auth login
git remote add origin https://github.com/<seu usuario git>/<boilerplate name>.git
git branch -M main
git push -u origin main
- Habilitar dependabot no github
- abrir repositório no github e procurar por settings/security/code security and analysis
- habilitar dependabot alerts e dependabot security updates
- Configurar diretório github e arquivos
cd ~/MeuProjeto/frontpage
mkdir .github
touch .github/dependabot.yml
cd .github
dependabot.yml:
version: 2
updates:
- package-ecosystem: yarn
directory: "/"
schedule:
interval: daily
open-pull-requests-limit: 10
- Atualizar repositório
cd ~/MeuProjeto/frontpage
git status
git add .
git status
git commit -m "INTEGRAÇÃO DEPENDABOT"
git push origin main
- Entrar no github do projeto e ver os dependabot alerts
- Configurar diretórios e arquivos para integração
cd ~/MeuProjeto/frontpage/.github
mkdir workflows
touch workflows/ci.yml
workflows/ci.yml:
name: ci
on: [push]
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout Repository
uses: actions/checkout@v2
- name: Setup Node
uses: actions/setup-node@v1
with:
node-version: 14.19.x
- uses: actions/cache@v2
id: yarn-cache
with:
path: |
~/cache
!~/cache/exclude
**/node_modules
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
restore-keys: |
${{ runner.os }}-yarn-
- name: Install dependencies
run: yarn install
- name: Linting
run: yarn lint
- name: Test
run: yarn test:ci
- name: Build
run: yarn build
cd ~/MeuProjeto/frontpage
package.json:
{
"name": "frontpage",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "eslint 'src/**/*.{ts,tsx}' --max-warnings=0",
"test": "jest",
"test:ci":"jest",
"test:watch": "yarn test --watch",
"storybook": "start-storybook -s ./public -p 6006",
"build-storybook": "build-storybook -s ./public"
},
"lint-staged": {
"src/**/*": [
"yarn lint --fix",
"yarn test --findRelatedTests --bail"
]
},
"dependencies": {
"next": "12.1.0",
"next-pwa": "^5.4.5",
"react": "17.0.2",
"react-dom": "17.0.2",
"styled-components": "^5.3.3"
},
"resolutions": {
"**/trim": "^1.0.0",
"**/glob-parent": "^5.1.2"
},
"devDependencies": {
"@babel/core": "^7.17.5",
"@babel/preset-typescript": "^7.16.7",
"@next/eslint-plugin-next": "^12.1.0",
"@storybook/addon-essentials": "^6.4.19",
"@storybook/react": "^6.4.19",
"@storybook/testing-library": "^0.0.9",
"@testing-library/jest-dom": "^5.16.2",
"@testing-library/react": "^12.1.3",
"@types/jest": "^27.4.1",
"@types/node": "^17.0.21",
"@types/react": "^17.0.39",
"@types/styled-components": "^5.1.24",
"@typescript-eslint/eslint-plugin": "^5.13.0",
"@typescript-eslint/parser": "^5.13.0",
"babel-loader": "^8.2.3",
"babel-plugin-styled-components": "^2.0.6",
"eslint": "^8.10.0",
"eslint-config-next": "12.1.0",
"eslint-config-prettier": "^8.4.0",
"eslint-plugin-prettier": "^4.0.0",
"eslint-plugin-react": "^7.29.2",
"eslint-plugin-react-hooks": "^4.3.0",
"eslint-plugin-storybook": "^0.5.7",
"husky": "^7.0.4",
"jest": "^27.5.1",
"jest-styled-components": "^7.0.8",
"lint-staged": "^12.3.4",
"prettier": "2.5.1",
"typescript": "4.5.5"
}
}
- Verificar funcionamento no github pull requests após enviar um push
Plop facilita a criação de componentes pela criação dos arquivos necessários: index.tsx, stories.tsx, styles.ts e test.tsx
-
Plop case modifiers
- camelCase: changeFormatToThis
- snakeCase: change_format_to_this
- dashCase/kebabCase: change-format-to-this
- dotCase: change.format.to.this
- pathCase: change/format/to/this
- properCase/pascalCase: ChangeFormatToThis
- lowerCase: change format to this
- sentenceCase: Change format to this,
- constantCase: CHANGE_FORMAT_TO_THIS
- titleCase: Change Format To This
-
Instalar e Configurar
cd ~/MeuProjeto/frontpage
yarn add --dev plop
mkdir generators
mkdir generators/templates
touch generators/plopfile.js
touch generators/templates/index.tsx.hbs
generators/plopfile.js:
module.exports = function (plop) {
// create your generators here
plop.setGenerator('component', {
description: 'application component logic',
prompts: [
{
type: 'input',
name: 'name',
message: 'component name please'
}
],
actions: [
{
type: 'add',
path: '../src/components/{{pascalCase name}}/index.tsx',
templateFile: 'templates/index.tsx.hbs'
},
{
type: 'add',
path: '../src/components/{{pascalCase name}}/stories.tsx',
templateFile: 'templates/stories.tsx.hbs'
},
{
type: 'add',
path: '../src/components/{{pascalCase name}}/styles.ts',
templateFile: 'templates/styles.ts.hbs'
},
{
type: 'add',
path: '../src/components/{{pascalCase name}}/test.tsx',
templateFile: 'templates/test.tsx.hbs'
}
]
})
}
generators/templates/index.tsx.hbs:
generators/templates/stories.tsx.hbs`
generators/templates/styles.ts.hbs`
generators/templates/test.tsx.hbs`
package.json:
{
"name": "frontpage",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "eslint 'src/**/*.{ts,tsx}' --max-warnings=0",
"test": "jest",
"test:ci": "jest",
"test:watch": "yarn test --watch",
"storybook": "start-storybook -s ./public -p 6006",
"build-storybook": "build-storybook -s ./public",
"generate": "yarn plop --plopfile ./generators/plopfile.js"
},
"lint-staged": {
"src/**/*": [
"yarn lint --fix",
"yarn test --findRelatedTests --bail"
]
},
"dependencies": {
"next": "12.1.0",
"next-pwa": "^5.4.5",
"react": "17.0.2",
"react-dom": "17.0.2",
"styled-components": "^5.3.3"
},
"resolutions": {
"**/trim": "^1.0.0",
"**/glob-parent": "^5.1.2"
},
"devDependencies": {
"@babel/core": "^7.17.5",
"@babel/preset-typescript": "^7.16.7",
"@next/eslint-plugin-next": "^12.1.0",
"@storybook/addon-essentials": "^6.4.19",
"@storybook/react": "^6.4.19",
"@storybook/testing-library": "^0.0.9",
"@testing-library/jest-dom": "^5.16.2",
"@testing-library/react": "^12.1.3",
"@types/jest": "^27.4.1",
"@types/node": "^17.0.21",
"@types/react": "^17.0.39",
"@types/styled-components": "^5.1.24",
"@typescript-eslint/eslint-plugin": "^5.13.0",
"@typescript-eslint/parser": "^5.13.0",
"babel-loader": "^8.2.3",
"babel-plugin-styled-components": "^2.0.6",
"eslint": "^8.10.0",
"eslint-config-next": "12.1.0",
"eslint-config-prettier": "^8.4.0",
"eslint-plugin-prettier": "^4.0.0",
"eslint-plugin-react": "^7.29.2",
"eslint-plugin-react-hooks": "^4.3.0",
"eslint-plugin-storybook": "^0.5.7",
"husky": "^7.0.4",
"jest": "^27.5.1",
"jest-styled-components": "^7.0.8",
"lint-staged": "^12.3.4",
"plop": "^3.0.5",
"prettier": "2.5.1",
"typescript": "4.5.5"
}
}
- Testar Plop na criação de um novo componente
yarn generate NomeComponente