提交 01d63698 authored 作者: 王鹏飞's avatar 王鹏飞

feat: scaffold learning application foundation

上级 a496cc2f
...@@ -8,9 +8,10 @@ pnpm-debug.log* ...@@ -8,9 +8,10 @@ pnpm-debug.log*
lerna-debug.log* lerna-debug.log*
node_modules node_modules
dist dist
dist-ssr dist-ssr
*.local *.tsbuildinfo
*.local
# Editor directories and files # Editor directories and files
.vscode/* .vscode/*
......
...@@ -14,12 +14,17 @@ This repository is a pnpm workspace. Use Node >=22.13 and pnpm 11.13.1 from the ...@@ -14,12 +14,17 @@ This repository is a pnpm workspace. Use Node >=22.13 and pnpm 11.13.1 from the
Currently available root commands: Currently available root commands:
- `pnpm install` - `pnpm install`
- `pnpm dev` — runs both apps in parallel.
- `pnpm dev:admin` - `pnpm dev:admin`
- `pnpm dev:learning`
- `pnpm build` — builds learning first, then admin.
- `pnpm build:admin` - `pnpm build:admin`
- `pnpm build:learning`
- `pnpm test` — runs each workspace package's test command.
- `pnpm test:admin` - `pnpm test:admin`
- `pnpm test:learning`
- `pnpm lint:admin` — the inherited admin script uses `--fix`; inspect its diff and do not run it for read-only validation. - `pnpm lint:admin` — the inherited admin script uses `--fix`; inspect its diff and do not run it for read-only validation.
- `pnpm lint:learning`
Learning commands (`dev:learning`, `build:learning`, `test:learning`, and `lint:learning`) and root verification commands are planned, but do not exist until their corresponding Stage 0 tasks create them.
## Code and documentation ## Code and documentation
......
module.exports = {
root: true,
env: { browser: true, es2022: true },
parser: '@typescript-eslint/parser',
plugins: ['@typescript-eslint', 'react-hooks', 'react-refresh'],
extends: ['eslint:recommended', 'plugin:@typescript-eslint/recommended', 'plugin:react-hooks/recommended'],
parserOptions: { ecmaVersion: 'latest', sourceType: 'module' },
ignorePatterns: ['dist'],
}
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>清控紫荆数智学堂</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
{
"name": "@ebook/learning",
"private": true,
"version": "1.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "tsc -b && vite build",
"lint": "eslint . --ext ts,tsx --max-warnings 0",
"test": "vitest run --passWithNoTests",
"test:watch": "vitest"
},
"dependencies": {
"@tanstack/react-query": "^5.76.2",
"axios": "^1.8.4",
"nuqs": "^2.9.0",
"react": "^19.2.7",
"react-dom": "^19.2.7",
"react-router": "^7.4.0",
"zustand": "^5.0.3"
},
"devDependencies": {
"@testing-library/jest-dom": "^6.6.3",
"@testing-library/react": "^16.3.0",
"@testing-library/user-event": "^14.6.1",
"@types/node": "^22.13.9",
"@types/react": "^19.2.17",
"@types/react-dom": "^19.2.3",
"@typescript-eslint/eslint-plugin": "^7.18.0",
"@typescript-eslint/parser": "^7.18.0",
"@vitejs/plugin-react": "^6.0.3",
"eslint": "^8.57.0",
"eslint-plugin-react-hooks": "^5.2.0",
"eslint-plugin-react-refresh": "^0.4.19",
"jsdom": "^26.1.0",
"typescript": "^5.7.3",
"vite": "^8.1.5",
"vite-plugin-mkcert": "^1.17.10",
"vitest": "^4.1.10"
}
}
import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import { RouterProvider } from 'react-router'
import { router } from '@/router/routes'
export const queryClient = new QueryClient({
defaultOptions: {
queries: { staleTime: 10 * 60 * 1000, retry: 1 },
},
})
createRoot(document.getElementById('root')!).render(
<StrictMode>
<QueryClientProvider client={queryClient}>
<RouterProvider router={router} />
</QueryClientProvider>
</StrictMode>
)
import type { RouteObject } from 'react-router'
import { LibraryView } from './views/LibraryView'
export const routes: RouteObject[] = [
{ path: 'library', element: <LibraryView /> },
]
export function LibraryView() {
return (
<main>
<h1>图书馆</h1>
<p>学习端架构初始化完成。</p>
</main>
)
}
import { NuqsAdapter } from 'nuqs/adapters/react-router/v7'
import { Outlet } from 'react-router'
export function RootLayout() {
return (
<NuqsAdapter>
<Outlet />
</NuqsAdapter>
)
}
import { createBrowserRouter, Navigate, type RouteObject } from 'react-router'
import { RootLayout } from './RootLayout'
type RouteModule = { routes: RouteObject[] }
const modules = import.meta.glob<RouteModule>('../modules/**/routes.tsx', { eager: true })
const moduleRoutes = Object.values(modules).flatMap(module => module.routes)
export const router = createBrowserRouter([
{
path: '/',
element: <RootLayout />,
children: [
{ index: true, element: <Navigate to="/library" replace /> },
...moduleRoutes,
],
},
{ path: '*', element: <Navigate to="/library" replace /> },
])
import '@testing-library/jest-dom/vitest'
import axios from 'axios'
export const http = axios.create({
baseURL: '/api/web',
withCredentials: true,
headers: { 'Content-Type': 'application/json' },
})
/// <reference types="vite/client" />
{
"compilerOptions": {
"target": "ES2022",
"useDefineForClassFields": true,
"lib": ["ES2022", "DOM", "DOM.Iterable"],
"allowJs": false,
"skipLibCheck": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"strict": true,
"composite": true,
"forceConsistentCasingInFileNames": true,
"module": "ESNext",
"moduleResolution": "Bundler",
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "react-jsx",
"baseUrl": ".",
"paths": { "@/*": ["src/*"] }
},
"include": ["src"]
}
{
"files": [],
"references": [
{ "path": "./tsconfig.app.json" },
{ "path": "./tsconfig.node.json" }
]
}
{
"compilerOptions": {
"composite": true,
"skipLibCheck": true,
"module": "ESNext",
"moduleResolution": "Bundler",
"allowImportingTsExtensions": true,
"strict": true,
"noEmit": true
},
"include": ["vite.config.ts"]
}
import { fileURLToPath, URL } from 'node:url'
import { loadEnv } from 'vite'
import { defineConfig } from 'vitest/config'
import react from '@vitejs/plugin-react'
import mkcert from 'vite-plugin-mkcert'
export default defineConfig(({ mode }) => {
const env = loadEnv(mode, process.cwd(), '')
const learningApiTarget = env.VITE_LEARNING_API_TARGET || 'http://127.0.0.1:7421'
const adminApiTarget = env.VITE_ADMIN_API_TARGET || 'http://127.0.0.1:7419'
return {
base: '/',
plugins: [react(), mkcert()],
server: {
open: '/library',
host: 'dev.ezijing.com',
port: 5173,
strictPort: true,
proxy: {
'/admin': {
target: 'https://dev.ezijing.com:5174',
changeOrigin: true,
secure: false,
ws: true,
},
'/api/web': {
target: learningApiTarget,
changeOrigin: true,
rewrite: path => path.replace(/^\/api\/web/, '/web'),
},
'/api/admin': {
target: adminApiTarget,
changeOrigin: true,
rewrite: path => path.replace(/^\/api\/admin/, ''),
},
},
},
build: {
outDir: '../../dist/learning',
emptyOutDir: true,
},
resolve: {
alias: { '@': fileURLToPath(new URL('./src', import.meta.url)) },
},
test: {
environment: 'jsdom',
setupFiles: './src/test/setup.ts',
},
}
})
...@@ -4,9 +4,16 @@ ...@@ -4,9 +4,16 @@
"version": "1.0.0", "version": "1.0.0",
"packageManager": "pnpm@11.13.1", "packageManager": "pnpm@11.13.1",
"scripts": { "scripts": {
"dev": "pnpm --parallel --filter @ebook/admin --filter @ebook/learning dev",
"dev:admin": "pnpm --filter @ebook/admin dev", "dev:admin": "pnpm --filter @ebook/admin dev",
"dev:learning": "pnpm --filter @ebook/learning dev",
"build": "pnpm run build:learning && pnpm run build:admin",
"build:admin": "pnpm --filter @ebook/admin build", "build:admin": "pnpm --filter @ebook/admin build",
"build:learning": "pnpm --filter @ebook/learning build",
"test": "pnpm --recursive test",
"lint:admin": "pnpm --filter @ebook/admin lint", "lint:admin": "pnpm --filter @ebook/admin lint",
"test:admin": "pnpm --filter @ebook/admin test" "lint:learning": "pnpm --filter @ebook/learning lint",
"test:admin": "pnpm --filter @ebook/admin test",
"test:learning": "pnpm --filter @ebook/learning test"
} }
} }
差异被折叠。
Markdown 格式
0% 或
您添加了 0 人 到此讨论。请谨慎行事。
请先完成此评论的编辑!
请 注册 或者 后发表评论