├── .env.example ├── src ├── vite-env.d.ts ├── main.tsx ├── App.css ├── index.css ├── App.tsx └── assets │ ├── react.svg │ └── elbrus.svg ├── tsconfig.json ├── .gitignore ├── index.html ├── vite.config.ts ├── .prettierrc ├── tsconfig.node.json ├── tsconfig.app.json ├── package.json ├── public └── vite.svg ├── README.md └── eslint.config.js /.env.example: -------------------------------------------------------------------------------- 1 | VITE_APP_TITLE= -------------------------------------------------------------------------------- /src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "files": [], 3 | "references": [{ "path": "./tsconfig.app.json" }, { "path": "./tsconfig.node.json" }] 4 | } 5 | -------------------------------------------------------------------------------- /src/main.tsx: -------------------------------------------------------------------------------- 1 | import { StrictMode } from 'react'; 2 | import { createRoot } from 'react-dom/client'; 3 | import './index.css'; 4 | import App from './App.tsx'; 5 | 6 | createRoot(document.getElementById('root')!).render( 7 | 8 | 9 | , 10 | ); 11 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | .env 16 | # Editor directories and files 17 | .vscode/* 18 | !.vscode/extensions.json 19 | .idea 20 | .DS_Store 21 | *.suo 22 | *.ntvs* 23 | *.njsproj 24 | *.sln 25 | *.sw? 26 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite + React + TS 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /vite.config.ts: -------------------------------------------------------------------------------- 1 | import type { AliasOptions } from 'vite'; 2 | import { defineConfig } from 'vite'; 3 | import react from '@vitejs/plugin-react'; 4 | import path from 'path'; 5 | 6 | const root = path.resolve(__dirname, 'src'); 7 | 8 | // https://vite.dev/config/ 9 | export default defineConfig({ 10 | plugins: [react()], 11 | resolve: { 12 | alias: { 13 | '@': root, 14 | } as AliasOptions, 15 | }, 16 | }); 17 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "experimentalTernaries": false, 3 | "printWidth": 100, 4 | "tabWidth": 2, 5 | "useTabs": false, 6 | "semi": true, 7 | "singleQuote": true, 8 | "quoteProps": "as-needed", 9 | "jsxSingleQuote": false, 10 | "trailingComma": "all", 11 | "bracketSpacing": true, 12 | "bracketSameLine": false, 13 | "arrowParens": "always", 14 | "proseWrap": "always", 15 | "endOfLine": "lf", 16 | "embeddedLanguageFormatting": "auto", 17 | "singleAttributePerLine": false 18 | } 19 | -------------------------------------------------------------------------------- /tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo", 4 | "target": "ES2022", 5 | "lib": ["ES2023"], 6 | "module": "ESNext", 7 | "skipLibCheck": true, 8 | 9 | /* Bundler mode */ 10 | "moduleResolution": "bundler", 11 | "allowImportingTsExtensions": true, 12 | "isolatedModules": true, 13 | "moduleDetection": "force", 14 | "noEmit": true, 15 | 16 | /* Linting */ 17 | "strict": true, 18 | "noUnusedLocals": true, 19 | "noUnusedParameters": true, 20 | "noFallthroughCasesInSwitch": true, 21 | "noUncheckedSideEffectImports": true 22 | }, 23 | "include": ["vite.config.ts"] 24 | } 25 | -------------------------------------------------------------------------------- /src/App.css: -------------------------------------------------------------------------------- 1 | #root { 2 | max-width: 1280px; 3 | margin: 0 auto; 4 | padding: 2rem; 5 | text-align: center; 6 | } 7 | 8 | .logo { 9 | height: 6em; 10 | padding: 1.5em; 11 | will-change: filter; 12 | transition: filter 300ms; 13 | } 14 | .logo:hover { 15 | filter: drop-shadow(0 0 2em #646cffaa); 16 | } 17 | .logo.react:hover { 18 | filter: drop-shadow(0 0 2em #61dafbaa); 19 | } 20 | 21 | @keyframes logo-spin { 22 | from { 23 | transform: rotate(0deg); 24 | } 25 | to { 26 | transform: rotate(360deg); 27 | } 28 | } 29 | 30 | @media (prefers-reduced-motion: no-preference) { 31 | a:nth-of-type(2) .logo { 32 | animation: logo-spin infinite 20s linear; 33 | } 34 | } 35 | 36 | .card { 37 | padding: 2em; 38 | } 39 | 40 | .read-the-docs { 41 | color: #888; 42 | } 43 | -------------------------------------------------------------------------------- /tsconfig.app.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo", 4 | "target": "ES2020", 5 | "useDefineForClassFields": true, 6 | "lib": ["ESNext", "DOM", "DOM.Iterable"], 7 | "module": "ESNext", 8 | "skipLibCheck": true, 9 | "paths": { 10 | "@/*": ["./src/*"] 11 | }, 12 | 13 | /* Bundler mode */ 14 | "moduleResolution": "bundler", 15 | "allowImportingTsExtensions": true, 16 | "isolatedModules": true, 17 | "moduleDetection": "force", 18 | "noEmit": true, 19 | "jsx": "react-jsx", 20 | 21 | /* Linting */ 22 | "strict": true, 23 | "noUnusedLocals": true, 24 | "noUnusedParameters": true, 25 | "noFallthroughCasesInSwitch": true, 26 | "noUncheckedSideEffectImports": true 27 | }, 28 | "include": ["src"] 29 | } 30 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "client", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "description": "Elbrus Bootcamp Vite React Typescript Template", 7 | "keywords": [ 8 | "Elbrus", 9 | "Bootcamp", 10 | "Vite", 11 | "React", 12 | "Typescript" 13 | ], 14 | "author": "Elbrus Bootcamp", 15 | "license": "ISC", 16 | "scripts": { 17 | "dev": "vite", 18 | "build": "tsc -b && vite build", 19 | "lint": "eslint .", 20 | "preview": "vite preview" 21 | }, 22 | "dependencies": { 23 | "react": "^19.1.0", 24 | "react-dom": "^19.1.0" 25 | }, 26 | "devDependencies": { 27 | "@elbrus/eslint-config": "^1.1.0", 28 | "@elbrus/eslint-plugin": "^1.0.1", 29 | "@types/node": "^22.15.19", 30 | "@eslint/js": "^9.27.0", 31 | "@types/react": "^19.1.4", 32 | "@types/react-dom": "^19.1.5", 33 | "@vitejs/plugin-react": "^4.4.1", 34 | "eslint": "^9.27.0", 35 | "eslint-plugin-fsd-layers": "^0.0.18", 36 | "eslint-plugin-react": "^7.37.5", 37 | "eslint-plugin-react-hooks": "^5.2.0", 38 | "eslint-plugin-react-refresh": "^0.4.20", 39 | "globals": "^16.1.0", 40 | "typescript": "~5.8.3", 41 | "typescript-eslint": "^8.32.1", 42 | "vite": "^6.3.5" 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/index.css: -------------------------------------------------------------------------------- 1 | :root { 2 | font-family: system-ui, Avenir, Helvetica, Arial, sans-serif; 3 | line-height: 1.5; 4 | font-weight: 400; 5 | 6 | color-scheme: light dark; 7 | color: rgba(255, 255, 255, 0.87); 8 | background-color: #242424; 9 | 10 | font-synthesis: none; 11 | text-rendering: optimizeLegibility; 12 | -webkit-font-smoothing: antialiased; 13 | -moz-osx-font-smoothing: grayscale; 14 | } 15 | 16 | a { 17 | font-weight: 500; 18 | color: #646cff; 19 | text-decoration: inherit; 20 | } 21 | a:hover { 22 | color: #535bf2; 23 | } 24 | 25 | body { 26 | margin: 0; 27 | display: flex; 28 | place-items: center; 29 | min-width: 320px; 30 | min-height: 100vh; 31 | } 32 | 33 | h1 { 34 | font-size: 3.2em; 35 | line-height: 1.1; 36 | } 37 | 38 | button { 39 | border-radius: 8px; 40 | border: 1px solid transparent; 41 | padding: 0.6em 1.2em; 42 | font-size: 1em; 43 | font-weight: 500; 44 | font-family: inherit; 45 | background-color: #1a1a1a; 46 | cursor: pointer; 47 | transition: border-color 0.25s; 48 | } 49 | button:hover { 50 | border-color: #646cff; 51 | } 52 | button:focus, 53 | button:focus-visible { 54 | outline: 4px auto -webkit-focus-ring-color; 55 | } 56 | 57 | @media (prefers-color-scheme: light) { 58 | :root { 59 | color: #213547; 60 | background-color: #ffffff; 61 | } 62 | a:hover { 63 | color: #747bff; 64 | } 65 | button { 66 | background-color: #f9f9f9; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /public/vite.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/App.tsx: -------------------------------------------------------------------------------- 1 | import type { JSX } from 'react'; 2 | import { useState } from 'react'; 3 | import reactLogo from '@/assets/react.svg'; 4 | import elbrusLogo from '@/assets/elbrus.svg'; 5 | import viteLogo from '/vite.svg'; 6 | import '@/App.css'; 7 | 8 | function App(): JSX.Element { 9 | const [count, setCount] = useState(0); 10 | 11 | return ( 12 | <> 13 | Elbrus Bootcamp 14 | 15 |
16 | 17 | Vite logo 18 | 19 | 20 | Elbrus logo 21 | 22 | 23 | React logo 24 | 25 |
26 |

Elbrus Bootcamp

27 |

Vite + React

28 |
29 | 30 |

31 | Edit src/App.tsx and save to test HMR 32 |

33 |
34 |

Click on the Vite and React logos to learn more

35 | 36 | ); 37 | } 38 | 39 | export default App; 40 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Elbrus Bootcamp Vite bundle 2 | 3 | ## React + Typescript + ESLint + Prettier 4 | 5 | Современный быстрый сборщик React проекта на Typescript с предустановленными настройками ESLint и Prettier от Elbrus Bootcamp. 6 | 7 | ## Использование 8 | 9 | ``` 10 | npx degit Elbrus-Bootcamp/vite-react-ts my-app 11 | 12 | cd my-app 13 | 14 | npm i 15 | ``` 16 | 17 | ## Скрипты 18 | 19 | - Запустить в режиме разработки 20 | 21 | ``` 22 | npm run dev 23 | ``` 24 | 25 | - Собрать проект 26 | 27 | ``` 28 | npm run build 29 | ``` 30 | 31 | - Превью 32 | 33 | ``` 34 | npm run preview 35 | ``` 36 | 37 | ## `ENV` 38 | 39 | Для подключения переменных окружения нужно описать их в файле `.env` в корне. Переменные окружения можно получить из объекта `import.meta.env`. Чтобы Typescript подсказывал, какими переменными окружения можно пользоваться, их нужно прописать в файле `src/env.d.ts`. Только переменные с префиксом `VITE_` передаются на клиент. 40 | 41 | ## `settings.json` 42 | 43 | Чтобы настроить форматирование и линтование по сохранению, необходимо прописать в `settings.json` (ctrl + shift + P) следующие настройки: 44 | 45 | ```json 46 | { 47 | "editor.codeActionsOnSave": { 48 | "source.fixAll.eslint": true 49 | }, 50 | "editor.formatOnSave": true, 51 | "editor.defaultFormatter": "esbenp.prettier-vscode" 52 | } 53 | ``` 54 | 55 | Можно добавить форматирование Prettier для JS и React: 56 | 57 | ```json 58 | { 59 | "[javascript]": { 60 | "editor.defaultFormatter": "esbenp.prettier-vscode" 61 | }, 62 | "[javascriptreact]": { 63 | "editor.defaultFormatter": "esbenp.prettier-vscode" 64 | } 65 | } 66 | ``` 67 | 68 | При работе не из корневой директории необходимо настроить CWD для ESLint: 69 | 70 | ```json 71 | { 72 | "eslint.workingDirectories": [ 73 | { "directory": "./client", "changeProcessCWD": true }, 74 | { "directory": "./server", "changeProcessCWD": true } 75 | ] 76 | } 77 | ``` 78 | 79 | Если директория не `client` или `server`, то можно добавить соответствующую строчку в данный массив. 80 | 81 | ## FAQ 82 | 83 | 1. На windows из-под wsl Vite не следит за изменениями файлов 84 | Добавить в `vite.config` 85 | 86 | ```js 87 | server: { watch: { usePolling: true } } 88 | ``` 89 | Подробнее: https://vitejs.dev/config/server-options.html#server-watch. 90 | -------------------------------------------------------------------------------- /eslint.config.js: -------------------------------------------------------------------------------- 1 | import js from '@eslint/js'; 2 | import globals from 'globals'; 3 | import reactHooks from 'eslint-plugin-react-hooks'; 4 | import react from 'eslint-plugin-react'; 5 | import tseslint from 'typescript-eslint'; 6 | import elbrusConfig from '@elbrus/eslint-config'; 7 | import elbrusPlugin from '@elbrus/eslint-plugin'; 8 | import fsdLayers from 'eslint-plugin-fsd-layers'; 9 | import reactRefresh from 'eslint-plugin-react-refresh'; 10 | 11 | export default tseslint.config( 12 | { ignores: ['dist'] }, 13 | { 14 | extends: [ 15 | js.configs.recommended, 16 | ...elbrusConfig, 17 | tseslint.configs.stylisticTypeChecked, 18 | tseslint.configs.strictTypeChecked, 19 | ], 20 | files: ['**/*.{ts,tsx}'], 21 | languageOptions: { 22 | ecmaVersion: 2020, 23 | globals: globals.browser, 24 | parserOptions: { 25 | projectService: true, 26 | tsconfigRootDir: import.meta.dirname, 27 | }, 28 | }, 29 | plugins: { 30 | 'react-hooks': reactHooks, 31 | 'react-refresh': reactRefresh, 32 | 'fsd-layers': fsdLayers, 33 | '@elbrus': elbrusPlugin, 34 | }, 35 | rules: { 36 | ...reactHooks.configs.recommended.rules, 37 | 38 | // Общие правила 39 | '@elbrus/prefer-for-of': 'error', 40 | 'fsd-layers/no-import-from-top': 'error', 41 | 'class-methods-use-this': 'warn', 42 | 'no-console': 'off', 43 | 'default-param-last': 'off', 44 | 'consistent-return': 'warn', 45 | 'no-void': 'off', 46 | 'no-param-reassign': 'off', 47 | '@typescript-eslint/switch-exhaustiveness-check': 'error', 48 | '@typescript-eslint/no-non-null-assertion': 'warn', 49 | '@typescript-eslint/no-misused-promises': [ 50 | 'error', 51 | { 52 | checksVoidReturn: { 53 | attributes: false, 54 | }, 55 | }, 56 | ], 57 | '@typescript-eslint/no-confusing-void-expression': [ 58 | 'error', 59 | { 60 | ignoreArrowShorthand: true, 61 | ignoreVoidOperator: true, 62 | }, 63 | ], 64 | 65 | // Возвращаемые значения функций 66 | '@typescript-eslint/explicit-function-return-type': [ 67 | 'error', 68 | { 69 | allowExpressions: true, 70 | allowTypedFunctionExpressions: true, 71 | allowHigherOrderFunctions: true, 72 | allowDirectConstAssertionInArrowFunctions: true, 73 | allowConciseArrowFunctionExpressionsStartingWithVoid: true, 74 | allowFunctionsWithoutTypeParameters: false, 75 | }, 76 | ], 77 | 78 | // Правила работы с типами 79 | '@typescript-eslint/consistent-type-definitions': ['warn', 'type'], 80 | '@typescript-eslint/consistent-type-imports': ['error', { prefer: 'type-imports' }], 81 | '@typescript-eslint/consistent-type-exports': [ 82 | 'error', 83 | { 84 | fixMixedExportsWithInlineTypeSpecifier: true, 85 | }, 86 | ], 87 | }, 88 | }, 89 | { 90 | plugins: { 91 | react, 92 | }, 93 | rules: { 94 | ...react.configs.recommended.rules, 95 | ...react.configs['jsx-runtime'].rules, 96 | 'react/no-array-index-key': 'error', 97 | }, 98 | }, 99 | ); 100 | -------------------------------------------------------------------------------- /src/assets/react.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/elbrus.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 18 | 20 | 22 | 27 | 28 | 31 | 35 | 39 | 40 | 43 | 44 | 45 | 47 | 48 | 49 | 50 | 51 | --------------------------------------------------------------------------------