From 4c8998cdfbf4f398fd66647ffda8a24f090a2176 Mon Sep 17 00:00:00 2001 From: moolcoov Date: Wed, 26 Feb 2025 21:32:31 +0300 Subject: [PATCH] init: add vite setup --- services/frontend/.gitignore | 5 -- services/frontend/README.md | 50 ++++++++++++++++++ services/frontend/bun.lockb | Bin 0 -> 74547 bytes services/frontend/eslint.config.js | 28 ++++++++++ services/frontend/index.html | 13 +++++ services/frontend/package.json | 29 +++++++++++ services/frontend/public/vite.svg | 1 + services/frontend/src/App.css | 42 +++++++++++++++ services/frontend/src/App.tsx | 35 +++++++++++++ services/frontend/src/assets/react.svg | 1 + services/frontend/src/index.css | 68 +++++++++++++++++++++++++ services/frontend/src/main.tsx | 10 ++++ services/frontend/src/vite-env.d.ts | 1 + services/frontend/tsconfig.app.json | 26 ++++++++++ services/frontend/tsconfig.json | 7 +++ services/frontend/tsconfig.node.json | 24 +++++++++ services/frontend/vite.config.ts | 7 +++ 17 files changed, 342 insertions(+), 5 deletions(-) create mode 100644 services/frontend/README.md create mode 100644 services/frontend/bun.lockb create mode 100644 services/frontend/eslint.config.js create mode 100644 services/frontend/index.html create mode 100644 services/frontend/package.json create mode 100644 services/frontend/public/vite.svg create mode 100644 services/frontend/src/App.css create mode 100644 services/frontend/src/App.tsx create mode 100644 services/frontend/src/assets/react.svg create mode 100644 services/frontend/src/index.css create mode 100644 services/frontend/src/main.tsx create mode 100644 services/frontend/src/vite-env.d.ts create mode 100644 services/frontend/tsconfig.app.json create mode 100644 services/frontend/tsconfig.json create mode 100644 services/frontend/tsconfig.node.json create mode 100644 services/frontend/vite.config.ts diff --git a/services/frontend/.gitignore b/services/frontend/.gitignore index 78add94..a547bf3 100644 --- a/services/frontend/.gitignore +++ b/services/frontend/.gitignore @@ -8,8 +8,6 @@ pnpm-debug.log* lerna-debug.log* node_modules -package-lock.json -pnpm-lock.yaml dist dist-ssr *.local @@ -24,6 +22,3 @@ dist-ssr *.njsproj *.sln *.sw? - -# Env files -.env diff --git a/services/frontend/README.md b/services/frontend/README.md new file mode 100644 index 0000000..74872fd --- /dev/null +++ b/services/frontend/README.md @@ -0,0 +1,50 @@ +# React + TypeScript + Vite + +This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules. + +Currently, two official plugins are available: + +- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh +- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh + +## Expanding the ESLint configuration + +If you are developing a production application, we recommend updating the configuration to enable type aware lint rules: + +- Configure the top-level `parserOptions` property like this: + +```js +export default tseslint.config({ + languageOptions: { + // other options... + parserOptions: { + project: ['./tsconfig.node.json', './tsconfig.app.json'], + tsconfigRootDir: import.meta.dirname, + }, + }, +}) +``` + +- Replace `tseslint.configs.recommended` to `tseslint.configs.recommendedTypeChecked` or `tseslint.configs.strictTypeChecked` +- Optionally add `...tseslint.configs.stylisticTypeChecked` +- Install [eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react) and update the config: + +```js +// eslint.config.js +import react from 'eslint-plugin-react' + +export default tseslint.config({ + // Set the react version + settings: { react: { version: '18.3' } }, + plugins: { + // Add the react plugin + react, + }, + rules: { + // other rules... + // Enable its recommended rules + ...react.configs.recommended.rules, + ...react.configs['jsx-runtime'].rules, + }, +}) +``` diff --git a/services/frontend/bun.lockb b/services/frontend/bun.lockb new file mode 100644 index 0000000000000000000000000000000000000000..3c24f3f4548b973920afc75bcce8348a67d379f6 GIT binary patch literal 74547 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 literal 0 HcmV?d00001 diff --git a/services/frontend/eslint.config.js b/services/frontend/eslint.config.js new file mode 100644 index 0000000..092408a --- /dev/null +++ b/services/frontend/eslint.config.js @@ -0,0 +1,28 @@ +import js from '@eslint/js' +import globals from 'globals' +import reactHooks from 'eslint-plugin-react-hooks' +import reactRefresh from 'eslint-plugin-react-refresh' +import tseslint from 'typescript-eslint' + +export default tseslint.config( + { ignores: ['dist'] }, + { + extends: [js.configs.recommended, ...tseslint.configs.recommended], + files: ['**/*.{ts,tsx}'], + languageOptions: { + ecmaVersion: 2020, + globals: globals.browser, + }, + plugins: { + 'react-hooks': reactHooks, + 'react-refresh': reactRefresh, + }, + rules: { + ...reactHooks.configs.recommended.rules, + 'react-refresh/only-export-components': [ + 'warn', + { allowConstantExport: true }, + ], + }, + }, +) diff --git a/services/frontend/index.html b/services/frontend/index.html new file mode 100644 index 0000000..e4b78ea --- /dev/null +++ b/services/frontend/index.html @@ -0,0 +1,13 @@ + + + + + + + Vite + React + TS + + +
+ + + diff --git a/services/frontend/package.json b/services/frontend/package.json new file mode 100644 index 0000000..e9a3d81 --- /dev/null +++ b/services/frontend/package.json @@ -0,0 +1,29 @@ +{ + "name": "frontend", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "tsc -b && vite build", + "lint": "eslint .", + "preview": "vite preview" + }, + "dependencies": { + "react": "^19.0.0", + "react-dom": "^19.0.0" + }, + "devDependencies": { + "@eslint/js": "^9.21.0", + "@types/react": "^19.0.10", + "@types/react-dom": "^19.0.4", + "@vitejs/plugin-react-swc": "^3.8.0", + "eslint": "^9.21.0", + "eslint-plugin-react-hooks": "^5.0.0", + "eslint-plugin-react-refresh": "^0.4.19", + "globals": "^15.15.0", + "typescript": "~5.7.2", + "typescript-eslint": "^8.24.1", + "vite": "^6.2.0" + } +} diff --git a/services/frontend/public/vite.svg b/services/frontend/public/vite.svg new file mode 100644 index 0000000..e7b8dfb --- /dev/null +++ b/services/frontend/public/vite.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/services/frontend/src/App.css b/services/frontend/src/App.css new file mode 100644 index 0000000..b9d355d --- /dev/null +++ b/services/frontend/src/App.css @@ -0,0 +1,42 @@ +#root { + max-width: 1280px; + margin: 0 auto; + padding: 2rem; + text-align: center; +} + +.logo { + height: 6em; + padding: 1.5em; + will-change: filter; + transition: filter 300ms; +} +.logo:hover { + filter: drop-shadow(0 0 2em #646cffaa); +} +.logo.react:hover { + filter: drop-shadow(0 0 2em #61dafbaa); +} + +@keyframes logo-spin { + from { + transform: rotate(0deg); + } + to { + transform: rotate(360deg); + } +} + +@media (prefers-reduced-motion: no-preference) { + a:nth-of-type(2) .logo { + animation: logo-spin infinite 20s linear; + } +} + +.card { + padding: 2em; +} + +.read-the-docs { + color: #888; +} diff --git a/services/frontend/src/App.tsx b/services/frontend/src/App.tsx new file mode 100644 index 0000000..3d7ded3 --- /dev/null +++ b/services/frontend/src/App.tsx @@ -0,0 +1,35 @@ +import { useState } from 'react' +import reactLogo from './assets/react.svg' +import viteLogo from '/vite.svg' +import './App.css' + +function App() { + const [count, setCount] = useState(0) + + return ( + <> + +

Vite + React

+
+ +

+ Edit src/App.tsx and save to test HMR +

+
+

+ Click on the Vite and React logos to learn more +

+ + ) +} + +export default App diff --git a/services/frontend/src/assets/react.svg b/services/frontend/src/assets/react.svg new file mode 100644 index 0000000..6c87de9 --- /dev/null +++ b/services/frontend/src/assets/react.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/services/frontend/src/index.css b/services/frontend/src/index.css new file mode 100644 index 0000000..08a3ac9 --- /dev/null +++ b/services/frontend/src/index.css @@ -0,0 +1,68 @@ +:root { + font-family: system-ui, Avenir, Helvetica, Arial, sans-serif; + line-height: 1.5; + font-weight: 400; + + color-scheme: light dark; + color: rgba(255, 255, 255, 0.87); + background-color: #242424; + + font-synthesis: none; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +a { + font-weight: 500; + color: #646cff; + text-decoration: inherit; +} +a:hover { + color: #535bf2; +} + +body { + margin: 0; + display: flex; + place-items: center; + min-width: 320px; + min-height: 100vh; +} + +h1 { + font-size: 3.2em; + line-height: 1.1; +} + +button { + border-radius: 8px; + border: 1px solid transparent; + padding: 0.6em 1.2em; + font-size: 1em; + font-weight: 500; + font-family: inherit; + background-color: #1a1a1a; + cursor: pointer; + transition: border-color 0.25s; +} +button:hover { + border-color: #646cff; +} +button:focus, +button:focus-visible { + outline: 4px auto -webkit-focus-ring-color; +} + +@media (prefers-color-scheme: light) { + :root { + color: #213547; + background-color: #ffffff; + } + a:hover { + color: #747bff; + } + button { + background-color: #f9f9f9; + } +} diff --git a/services/frontend/src/main.tsx b/services/frontend/src/main.tsx new file mode 100644 index 0000000..bef5202 --- /dev/null +++ b/services/frontend/src/main.tsx @@ -0,0 +1,10 @@ +import { StrictMode } from 'react' +import { createRoot } from 'react-dom/client' +import './index.css' +import App from './App.tsx' + +createRoot(document.getElementById('root')!).render( + + + , +) diff --git a/services/frontend/src/vite-env.d.ts b/services/frontend/src/vite-env.d.ts new file mode 100644 index 0000000..11f02fe --- /dev/null +++ b/services/frontend/src/vite-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/services/frontend/tsconfig.app.json b/services/frontend/tsconfig.app.json new file mode 100644 index 0000000..358ca9b --- /dev/null +++ b/services/frontend/tsconfig.app.json @@ -0,0 +1,26 @@ +{ + "compilerOptions": { + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo", + "target": "ES2020", + "useDefineForClassFields": true, + "lib": ["ES2020", "DOM", "DOM.Iterable"], + "module": "ESNext", + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "isolatedModules": true, + "moduleDetection": "force", + "noEmit": true, + "jsx": "react-jsx", + + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true, + "noUncheckedSideEffectImports": true + }, + "include": ["src"] +} diff --git a/services/frontend/tsconfig.json b/services/frontend/tsconfig.json new file mode 100644 index 0000000..1ffef60 --- /dev/null +++ b/services/frontend/tsconfig.json @@ -0,0 +1,7 @@ +{ + "files": [], + "references": [ + { "path": "./tsconfig.app.json" }, + { "path": "./tsconfig.node.json" } + ] +} diff --git a/services/frontend/tsconfig.node.json b/services/frontend/tsconfig.node.json new file mode 100644 index 0000000..db0becc --- /dev/null +++ b/services/frontend/tsconfig.node.json @@ -0,0 +1,24 @@ +{ + "compilerOptions": { + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo", + "target": "ES2022", + "lib": ["ES2023"], + "module": "ESNext", + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "isolatedModules": true, + "moduleDetection": "force", + "noEmit": true, + + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true, + "noUncheckedSideEffectImports": true + }, + "include": ["vite.config.ts"] +} diff --git a/services/frontend/vite.config.ts b/services/frontend/vite.config.ts new file mode 100644 index 0000000..2328e17 --- /dev/null +++ b/services/frontend/vite.config.ts @@ -0,0 +1,7 @@ +import { defineConfig } from 'vite' +import react from '@vitejs/plugin-react-swc' + +// https://vite.dev/config/ +export default defineConfig({ + plugins: [react()], +})