Configuração Avançada
Este guia cobre opções de configuração avançadas para o OitoPorOito.
Variáveis de Ambiente
Arquivo .env
Todas as variáveis de ambiente devem ser prefixadas com VITE_ para serem acessíveis no frontend:
# ====================
# Supabase Configuration
# ====================
VITE_SUPABASE_URL=https://your-project.supabase.co
VITE_SUPABASE_ANON_KEY=your-anon-key
# ====================
# Stockfish Configuration
# ====================
VITE_STOCKFISH_PATH=/stockfish/
VITE_STOCKFISH_THREADS=2
VITE_STOCKFISH_HASH=128 # MB de memória
# ====================
# App Configuration
# ====================
VITE_APP_NAME=OitoPorOito
VITE_APP_URL=http://localhost:5173
VITE_APP_VERSION=0.1.0
# ====================
# Feature Flags
# ====================
VITE_ENABLE_SOCIAL=true
VITE_ENABLE_TOURNAMENTS=false
VITE_ENABLE_ANALYSIS=true
# ====================
# Analytics (Opcional)
# ====================
VITE_GA_TRACKING_ID=
VITE_SENTRY_DSN=
# ====================
# API Limits
# ====================
VITE_MAX_GAME_TIME=3600 # segundos
VITE_PUZZLE_DAILY_LIMIT=100
VITE_API_RATE_LIMIT=60 # requests/minuto
Ambientes Separados
Desenvolvimento (.env.development)
VITE_SUPABASE_URL=https://dev-project.supabase.co
VITE_SUPABASE_ANON_KEY=dev-key
VITE_APP_URL=http://localhost:5173
VITE_DEBUG=true
Produção (.env.production)
VITE_SUPABASE_URL=https://prod-project.supabase.co
VITE_SUPABASE_ANON_KEY=prod-key
VITE_APP_URL=https://oitoporoito.com
VITE_DEBUG=false
Acessando Variáveis no Código
// Importar variáveis
const supabaseUrl = import.meta.env.VITE_SUPABASE_URL;
const supabaseKey = import.meta.env.VITE_SUPABASE_ANON_KEY;
// Validar se estão definidas
if (!supabaseUrl || !supabaseKey) {
throw new Error('Missing Supabase credentials');
}
// Usar no código
export const supabase = createClient(supabaseUrl, supabaseKey);
Configuração do Vite
vite.config.js
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import path from 'path';
export default defineConfig({
plugins: [react()],
// Aliases de caminho
resolve: {
alias: {
'@': path.resolve(__dirname, './src'),
'@components': path.resolve(__dirname, './src/components'),
'@pages': path.resolve(__dirname, './src/pages'),
'@hooks': path.resolve(__dirname, './src/hooks'),
'@lib': path.resolve(__dirname, './src/lib'),
'@utils': path.resolve(__dirname, './src/utils'),
'@data': path.resolve(__dirname, './src/data'),
},
},
// Configuração do servidor
server: {
port: 5173,
host: true, // Expor na rede local
open: true, // Abrir navegador automaticamente
cors: true,
// Proxy para APIs externas
proxy: {
'/api': {
target: 'http://localhost:3000',
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, ''),
},
},
},
// Otimizações de build
build: {
outDir: 'dist',
sourcemap: true,
minify: 'terser',
// Code splitting
rollupOptions: {
output: {
manualChunks: {
'react-vendor': ['react', 'react-dom', 'react-router-dom'],
'ui-vendor': ['@radix-ui/react-dialog', '@radix-ui/react-dropdown-menu'],
'chess-vendor': ['chess.js', 'stockfish.wasm'],
},
},
},
// Chunk size warnings
chunkSizeWarningLimit: 1000,
},
// Otimizações de dependências
optimizeDeps: {
include: ['react', 'react-dom', 'react-router-dom', 'chess.js'],
exclude: ['stockfish.wasm'],
},
});
Plugins Customizados
O projeto inclui plugins customizados em plugins/visual-editor/:
// vite.config.js
import visualEditor from './plugins/visual-editor/vite-plugin-edit-mode';
export default defineConfig({
plugins: [
react(),
visualEditor(), // Plugin customizado
],
});
Configuração do TailwindCSS
tailwind.config.js
/** @type {import('tailwindcss').Config} */
export default {
content: [
"./index.html",
"./src/**/*.{js,ts,jsx,tsx}",
],
theme: {
extend: {
// Cores customizadas
colors: {
'chess-dark': '#1e1e1e',
'chess-medium': '#2c2c2c',
'chess-light': '#3a3a3a',
'chess-accent': '#f59e0b',
'chess-board-light': '#f0d9b5',
'chess-board-dark': '#b58863',
},
// Fontes
fontFamily: {
sans: ['Inter', 'system-ui', 'sans-serif'],
mono: ['JetBrains Mono', 'monospace'],
},
// Animações
animation: {
'fade-in': 'fadeIn 0.3s ease-in-out',
'slide-up': 'slideUp 0.3s ease-out',
'piece-move': 'pieceMove 0.2s ease-in-out',
},
keyframes: {
fadeIn: {
'0%': { opacity: '0' },
'100%': { opacity: '1' },
},
slideUp: {
'0%': { transform: 'translateY(10px)', opacity: '0' },
'100%': { transform: 'translateY(0)', opacity: '1' },
},
pieceMove: {
'0%': { transform: 'scale(1)' },
'50%': { transform: 'scale(1.1)' },
'100%': { transform: 'scale(1)' },
},
},
// Spacing customizado para tabuleiro
spacing: {
'board': '80vh',
'square': '12.5%',
},
},
},
plugins: [
require('tailwindcss-animate'),
],
}
Configuração do ESLint
.eslintrc.cjs
module.exports = {
root: true,
env: {
browser: true,
es2021: true,
node: true,
},
extends: [
'eslint:recommended',
'plugin:react/recommended',
'plugin:react/jsx-runtime',
'plugin:react-hooks/recommended',
],
parserOptions: {
ecmaVersion: 'latest',
sourceType: 'module',
ecmaFeatures: {
jsx: true,
},
},
settings: {
react: {
version: 'detect',
},
},
plugins: ['react', 'react-hooks', 'react-refresh'],
rules: {
'react/prop-types': 'warn',
'react-hooks/rules-of-hooks': 'error',
'react-hooks/exhaustive-deps': 'warn',
'react-refresh/only-export-components': 'warn',
'no-unused-vars': ['warn', { argsIgnorePattern: '^_' }],
'no-console': ['warn', { allow: ['warn', 'error'] }],
},
};
Configuração do Git
.gitignore
# Dependências
node_modules/
package-lock.json
yarn.lock
pnpm-lock.yaml
# Build outputs
dist/
build/
.vite/
# Ambientes
.env
.env.local
.env.development.local
.env.production.local
# IDEs
.vscode/
.idea/
*.swp
*.swo
*~
# OS
.DS_Store
Thumbs.db
# Logs
logs/
*.log
npm-debug.log*
# Testes
coverage/
.nyc_output/
# Temporários
tmp/
temp/
.gitattributes
# Auto detect text files
* text=auto
# Source code
*.js text eol=lf
*.jsx text eol=lf
*.json text eol=lf
*.css text eol=lf
*.html text eol=lf
*.md text eol=lf
# Binary files
*.png binary
*.jpg binary
*.wasm binary
VS Code Settings
.vscode/settings.json
{
"editor.formatOnSave": true,
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.codeActionsOnSave": {
"source.fixAll.eslint": true
},
"files.associations": {
"*.css": "tailwindcss"
},
"tailwindCSS.experimental.classRegex": [
["clsx\\(([^)]*)\\)", "(?:'|\"|`)([^']*)(?:'|\"|`)"]
],
"eslint.validate": [
"javascript",
"javascriptreact"
]
}
.vscode/extensions.json
{
"recommendations": [
"dbaeumer.vscode-eslint",
"esbenp.prettier-vscode",
"bradlc.vscode-tailwindcss",
"dsznajder.es7-react-js-snippets",
"formulahendry.auto-rename-tag"
]
}
Performance
Lazy Loading de Componentes
import { lazy, Suspense } from 'react';
// Lazy load páginas
const Login = lazy(() => import('./pages/Login'));
const Signup = lazy(() => import('./pages/Signup'));
const Play = lazy(() => import('./pages/Play'));
// Usar com Suspense
<Suspense fallback={<Loading />}>
<Route path="/login" element={<Login />} />
</Suspense>
Memoização
import { memo, useMemo, useCallback } from 'react';
// Memoizar componente
const ChessBoard = memo(({ position, onMove }) => {
// ...
}, (prev, next) => prev.position === next.position);
// Memoizar valor
const moves = useMemo(() => {
return chess.moves({ verbose: true });
}, [position]);
// Memoizar callback
const handleMove = useCallback((move) => {
// ...
}, [position]);