From f8e3605ba9eaf5c492c2a8032bc38ff59bed2bbe Mon Sep 17 00:00:00 2001 From: Tianyang Date: Sat, 1 Aug 2026 02:40:18 +0800 Subject: [PATCH] feat: scaffold frontend project with Vite, Vitest and Docker build --- frontend/Dockerfile | 16 ++++++++++++++++ frontend/index.html | 11 +++++++++++ frontend/nginx.conf | 18 ++++++++++++++++++ frontend/package.json | 29 +++++++++++++++++++++++++++++ frontend/src/App.tsx | 7 +++++++ frontend/src/main.tsx | 9 +++++++++ frontend/tests/App.test.tsx | 8 ++++++++ frontend/tests/setup.ts | 1 + frontend/tsconfig.json | 21 +++++++++++++++++++++ frontend/vite.config.ts | 11 +++++++++++ 10 files changed, 131 insertions(+) create mode 100644 frontend/Dockerfile create mode 100644 frontend/index.html create mode 100644 frontend/nginx.conf create mode 100644 frontend/package.json create mode 100644 frontend/src/App.tsx create mode 100644 frontend/src/main.tsx create mode 100644 frontend/tests/App.test.tsx create mode 100644 frontend/tests/setup.ts create mode 100644 frontend/tsconfig.json create mode 100644 frontend/vite.config.ts diff --git a/frontend/Dockerfile b/frontend/Dockerfile new file mode 100644 index 0000000..085bdd9 --- /dev/null +++ b/frontend/Dockerfile @@ -0,0 +1,16 @@ +# ---- build stage ---- +FROM node:20.16-alpine AS build +WORKDIR /app +COPY package.json package-lock.json* ./ +RUN npm install +COPY . . +ARG VITE_API_BASE_URL +ENV VITE_API_BASE_URL=$VITE_API_BASE_URL +RUN npm run build + +# ---- serve stage ---- +FROM nginx:1.27-alpine AS serve +COPY --from=build /app/dist /usr/share/nginx/html +COPY nginx.conf /etc/nginx/conf.d/default.conf +EXPOSE 80 +CMD ["nginx", "-g", "daemon off;"] diff --git a/frontend/index.html b/frontend/index.html new file mode 100644 index 0000000..0694783 --- /dev/null +++ b/frontend/index.html @@ -0,0 +1,11 @@ + + + + + DocHub + + +
+ + + diff --git a/frontend/nginx.conf b/frontend/nginx.conf new file mode 100644 index 0000000..6ee6ec0 --- /dev/null +++ b/frontend/nginx.conf @@ -0,0 +1,18 @@ +server { + listen 80; + server_name _; + root /usr/share/nginx/html; + index index.html; + + location / { + try_files $uri $uri/ /index.html; + } + + location /api/ { + proxy_pass http://backend:8000/api/; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } +} diff --git a/frontend/package.json b/frontend/package.json new file mode 100644 index 0000000..ecba67e --- /dev/null +++ b/frontend/package.json @@ -0,0 +1,29 @@ +{ + "name": "dochub-frontend", + "private": true, + "version": "0.1.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "tsc -b && vite build", + "preview": "vite preview", + "test": "vitest run" + }, + "dependencies": { + "@monaco-editor/react": "4.6.0", + "react": "18.3.1", + "react-dom": "18.3.1" + }, + "devDependencies": { + "@testing-library/jest-dom": "6.4.8", + "@testing-library/react": "16.0.0", + "@testing-library/user-event": "14.5.2", + "@types/react": "18.3.3", + "@types/react-dom": "18.3.0", + "@vitejs/plugin-react": "4.3.1", + "jsdom": "24.1.1", + "typescript": "5.5.4", + "vite": "5.4.1", + "vitest": "2.0.5" + } +} diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx new file mode 100644 index 0000000..7dd74e3 --- /dev/null +++ b/frontend/src/App.tsx @@ -0,0 +1,7 @@ +export default function App() { + return ( +
+

DocHub

+
+ ); +} diff --git a/frontend/src/main.tsx b/frontend/src/main.tsx new file mode 100644 index 0000000..abd10b8 --- /dev/null +++ b/frontend/src/main.tsx @@ -0,0 +1,9 @@ +import React from 'react'; +import ReactDOM from 'react-dom/client'; +import App from './App'; + +ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render( + + + +); diff --git a/frontend/tests/App.test.tsx b/frontend/tests/App.test.tsx new file mode 100644 index 0000000..8312a27 --- /dev/null +++ b/frontend/tests/App.test.tsx @@ -0,0 +1,8 @@ +import { render, screen } from '@testing-library/react'; +import { test, expect } from 'vitest'; +import App from '../src/App'; + +test('renders the DocHub heading', () => { + render(); + expect(screen.getByRole('heading', { name: 'DocHub' })).toBeInTheDocument(); +}); diff --git a/frontend/tests/setup.ts b/frontend/tests/setup.ts new file mode 100644 index 0000000..bb02c60 --- /dev/null +++ b/frontend/tests/setup.ts @@ -0,0 +1 @@ +import '@testing-library/jest-dom/vitest'; diff --git a/frontend/tsconfig.json b/frontend/tsconfig.json new file mode 100644 index 0000000..1fcd70f --- /dev/null +++ b/frontend/tsconfig.json @@ -0,0 +1,21 @@ +{ + "compilerOptions": { + "target": "ES2020", + "useDefineForClassFields": true, + "lib": ["ES2020", "DOM", "DOM.Iterable"], + "module": "ESNext", + "skipLibCheck": true, + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + "jsx": "react-jsx", + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true, + "types": ["vite/client", "vitest/globals", "@testing-library/jest-dom"] + }, + "include": ["src", "tests", "vite.config.ts"] +} diff --git a/frontend/vite.config.ts b/frontend/vite.config.ts new file mode 100644 index 0000000..0037f8a --- /dev/null +++ b/frontend/vite.config.ts @@ -0,0 +1,11 @@ +import { defineConfig } from 'vite'; +import react from '@vitejs/plugin-react'; + +export default defineConfig({ + plugins: [react()], + test: { + globals: true, + environment: 'jsdom', + setupFiles: ['./tests/setup.ts'], + }, +});