my-app 开发文档 本文档面向 `my-app` 项目的开发者,介绍项目结构、前后端架构、构建流程、测试、代码规范等内容。 用户使用说明请参阅 [README.md](README.md)。 ## 目录 - [项目结构](#项目结构) - [前后端边界](#前后端边界) - [一、后端开发指引](#一后端开发指引) - [二、前端开发指引](#二前端开发指引) - [三、项目运行、集成与打包](#三项目运行集成与打包) - [代码质量](#代码质量) - [测试](#测试) - [已知限制](#已知限制) --- ## 项目结构 ```text src/ server/ bootstrap.ts 后端统一启动引导(loadServerConfig → startServer) config.ts CLI 参数解析与配置文件加载 facade(可选 YAML configPath,支持 --help/-h) config/ 配置解析模块(types、issues、variables、normalizer、schema) dev.ts 开发模式启动入口(mode: "development") main.ts 生产模式启动入口(mode: "production",安全头启用) server.ts HTTP server 启动工厂(Bun.serve routes 声明式路由 + fetch fallback 静态资源服务) static.ts 生产模式静态资源服务(SPA fallback、Content-Type 映射、immutable 缓存) helpers.ts 共享响应格式化工具(见下方函数清单) middleware.ts API 参数校验中间件(validateIdParam、validatePagination、validateTimeRange) routes/ API 路由 handler(按端点拆分) meta.ts GET /api/meta version.ts 运行时版本号读取(从 package.json 读取并验证) shared/ api.ts 前后端共享 TypeScript 类型 app.ts 应用全局常量(name、title、subtitle、description) web/ React 前端(通过 Vite 构建) index.html HTML 入口 app.tsx 根组件(Admin 布局:Header + Sidebar + Content + 版本号展示) main.tsx 入口(BrowserRouter + QueryClient 挂载 + ErrorBoundary + ReactQueryDevtools + TDesign CSS 导入) routes.tsx 路由配置(定义所有页面路由) styles.css 全局样式与自定义 CSS 变量 css.d.ts CSS 模块类型声明 pages/ 页面组件 dashboard/ index.tsx 仪表盘页(欢迎语 + /api/meta 联调示例) users/ index.tsx 用户管理页(占位) settings/ index.tsx 系统设置页(占位) 404/ index.tsx 404 页面 components/ UI 组件 ErrorBoundary.tsx React 错误边界,捕获渲染异常并展示降级 UI Sidebar/ index.tsx 侧边栏菜单组件(TDesign Menu + 底部折叠按钮) hooks/ React hooks use-theme-preference.ts 主题偏好 hook(system/light/dark,localStorage 记忆 + matchMedia 监听) use-sidebar-collapsed.ts 侧边栏折叠状态 hook(localStorage 记忆) utils/ 前端工具函数 time.ts 时间处理(formatCountdown、formatDurationUnit、formatRelativeTime、isOlderThan、subtractHours) menu.tsx 菜单配置(路由与菜单项统一数据源) routes.tsx 路由配置(定义所有页面路由) scripts/ dev.ts 双进程开发服务(Bun API server + Vite dev server) build.ts Vite → codegen → Bun compile 三步构建流水线(含版本号注入) generate-config-schema.ts 配置 JSON Schema 生成与同步校验 bump-version-logic.ts 纯版本管理逻辑(parse、validate、bump、format) bump-version.ts 版本升迁 CLI 脚本 clean.ts 清理构建产物与临时文件 tests/ Bun test 测试(结构镜像 src 目录) setup.ts 全局测试配置(jsdom、polyfill) helpers.ts 测试辅助工具(rmRetry) server/ 后端测试 bootstrap.test.ts config.test.ts config/ 配置模块测试 variables.test.ts schema.test.ts middleware.test.ts static.test.ts web/ 前端测试 App.test.tsx test-utils.tsx openspec/ OpenSpec 变更、规格文档与 fast-drive workflow schema config.example.yaml 配置文件示例(server.listen 布局 + 显式变量引用) config.schema.json 配置文件 JSON Schema(由 bun run schema 生成) ``` --- ## 前后端边界 前端只通过 HTTP 调用后端,API 路径为 `/api/*`。共享类型放在 `src/shared`,前端不得 `import src/server` 的运行时实现。 --- ## 一、后端开发指引 ### 1.1 架构概览 ``` 启动流程: dev.ts / main.ts → parseRuntimeArgs(cli args) → bootstrap({ configPath, mode }) → loadServerConfig(configPath):可选 YAML 解析 → ServerConfig{ host, port } → startServer({ config, mode }):Bun.serve routes 声明式路由 + fetch fallback → 注册 SIGINT/SIGTERM shutdown HTTP 请求: Request → Bun.serve routes 声明式匹配 → routes/*.ts(handler) → helpers.ts(响应格式化) → Response 前端: fetch fallback → serveStaticAsset (生产) / Vite proxy (开发) ``` ### 1.2 库使用优先级 后端代码开发遵循严格的库选择顺序: | 优先级 | 来源 | 典型用途 | | ------ | ------------ | --------------------------------------------------------------------------------------------------- | | 1 | Bun 内置 API | `Bun.serve`、`Bun.file`、`Bun.YAML`、`Bun.spawn`、`bun:sqlite`(如需数据存储) | | 2 | es-toolkit | 类型判断(`isPlainObject`/`isNil`/`isEmptyObject`)、深度比较(`isEqual`)、并发控制(`Semaphore`) | | 3 | 标准 Web API | `Object.fromEntries`、`Headers`、`fetch`、`AbortController`、`Response` | | 4 | 主流三方库 | 按需引入,优先社区活跃、类型完善的库 | | 5 | 自行实现 | 仅在以上都无法满足时(如 `parseDuration`、`parseSize` 等专项逻辑) | **原则**:新增依赖前先检查上述每一层级是否已有可用方案。禁止随意引入新依赖。 ### 1.3 API 路由开发 路由文件位于 `src/server/routes/`,每个端点一个文件。路由通过 `server.ts` 的 `Bun.serve({ routes })` 声明式注册,使用 per-method handler 对象: ```typescript // server.ts 中的路由注册 routes: { "/api/*": () => jsonResponse(createApiError("API route not found", 404), { mode, status: 404 }), "/api/meta": { GET: async () => handleMeta(mode, await resolveVersion()), }, } ``` Handler 函数签名: ```typescript // 带版本号参数的路由 export function handleMeta(mode: RuntimeMode, version: string): Response; ``` **请求处理流程**: 1. `Bun.serve` 的 `routes` 对象按路径 + HTTP 方法匹配请求 2. 未匹配方法的请求落入 `/api/*` 通配符(返回 404) 3. 各 handler 内部通过 `helpers.ts` 的 `jsonResponse`、`createApiError` 等格式化输出 4. 需要参数校验时使用 `middleware.ts` 提供的校验函数,返回 `Response` 实例表示校验失败(直接返回),返回数据对象表示通过 **新增路由步骤**: 1. 在 `src/server/routes/` 下创建 `.ts` 2. 实现 handler 函数并 export 3. 在 `server.ts` 的 `routes` 对象中注册路径和 method handler 4. 在 `tests/server/` 中添加对应测试 ### 1.4 共享工具 - **`helpers.ts`**:跨路由共用的响应工具函数 - `createApiError(error, status)` — 构造 API 错误体 - `createHeaders(mode, init)` — 创建响应 Headers(生产模式附加安全头:`X-Content-Type-Options`、`Referrer-Policy`) - `createMetaResponse(version)` — 构造应用元信息响应 `{ ok: true, service, timestamp, version }` - `formatDuration(ms)` — 毫秒转为可读时长字符串 - `jsonResponse(body, options)` — JSON 响应构造 - **`middleware.ts`**:API 参数校验函数 - `validateIdParam(idStr, mode)` — 校验 ID 参数格式(字母数字下划线连字符,字母开头),返回 `{ id }` 或 `Response` - `validatePagination(pageParam, pageSizeParam, mode)` — 校验分页参数(默认 page=1, pageSize=20,pageSize 上限 200),返回 `{ page, pageSize }` 或 `Response` - `validateTimeRange(from, to, mode)` — 校验时间范围参数(ISO 格式、from < to),返回 `{ from, to }` 或 `Response` - **`static.ts`**:生产模式静态资源服务 - `serveStaticAsset(pathname, assets)` — 静态资源分发(文件扩展名路由 → immutable 缓存,无扩展名 → SPA fallback 返回 index.html) - `hasFileExtension(path)` / `contentTypeFor(path)` / `htmlResponse(html)` — 辅助函数 ### 1.5 类型定义规范 - **共享类型**以 `src/shared/api.ts` 为唯一源头,前后端共同引用 - **应用常量**以 `src/shared/app.ts` 为唯一源头,定义 `APP` 对象(name、title、subtitle、description),前后端及构建脚本共同引用 - **版本号**以 `package.json.version` 为唯一源头,通过 `src/server/version.ts` 运行时读取或构建时注入字面量 - 前端不得 `import src/server/` 下的任何文件 - **严格联合类型**优先于宽类型:如 `RuntimeMode: "development" | "production" | "test"` 而非 `RuntimeMode: string` - API 响应类型(`ApiErrorResponse`、`MetaResponse`)定义在 shared 中 ### 1.6 配置文件规范 配置采用分层生命周期:`unknown → AuthoringConfig → NormalizedConfig → ValidatedConfig → ServerConfig`。 ``` CLI argv → parseRuntimeArgs → { configPath? } → loadServerConfig(configPath) → 无 configPath → 默认值 { host: "127.0.0.1", port: 3000 } → 有 configPath → YAML 解析 → normalize(变量替换) → strict validate → resolve → ServerConfig{ host, port } ``` 配置加载流程: 1. **解析 YAML**:`Bun.YAML.parse` 将配置文件解析为 `unknown` 2. **normalize**:提取 `variables`,替换 `${KEY}` / `${KEY|default}` 变量引用,移除 `variables` 段,产出 `NormalizedConfig` 3. **strict validate**:使用 Ajv + TypeBox 生成的 schema 严格校验(拒绝未知字段、校验类型和范围) 4. **resolve**:从校验后的配置中提取 `server.listen.host` 和 `server.listen.port`,填充默认值 `ServerConfig` 包含以下字段: | 字段 | 来源 | 默认值 | | ------ | ------------------------------------------ | ----------- | | `host` | `server.listen.host`(可含变量引用)→ 默认 | `127.0.0.1` | | `port` | `server.listen.port`(可含变量引用)→ 默认 | `3000` | 配置文件示例(`config.example.yaml`): ```yaml # yaml-language-server: $schema=./config.schema.json server: listen: host: "${HOST|127.0.0.1}" port: ${PORT|3000} ``` 变量语法: | 语法 | 说明 | | --------------- | ------------------------------ | | `${KEY}` | 引用变量,未定义时报错 | | `${KEY\|value}` | 引用变量,未定义时使用默认值 | | `${KEY\|}` | 引用变量,未定义时使用空字符串 | | `$${KEY}` | 转义,输出 `${KEY}` 原文 | 变量解析优先级:`variables 字段` → `process.env` → `默认值` → `unresolved 报错` 完整变量引用保留原始类型,部分拼接转为 string。环境变量不会隐式覆盖配置。 配置模块结构(`src/server/config/`): | 文件 | 职责 | | --------------------- | -------------------------------------------------------- | | `types.ts` | AuthoringConfig、NormalizedConfig、ValidatedConfig 类型 | | `issues.ts` | 结构化配置问题、路径渲染、去重、中文格式化 | | `variables.ts` | 变量提取、引用解析、默认值、环境变量查找、类型推断 | | `normalizer.ts` | Authoring → Normalized 转换(变量替换 + 移除 variables) | | `schema/fragments.ts` | TypeBox schema 片段 | | `schema/builder.ts` | Authoring/Normalized schema 构建 | | `schema/validate.ts` | Ajv strict 校验 + 错误映射 | | `schema/export.ts` | JSON Schema 导出(用于生成 config.schema.json) | JSON Schema 相关命令: ```bash bun run schema # 重新生成 config.schema.json bun run schema:check # 校验 config.schema.json 是否同步 ``` 运行时依赖 `@sinclair/typebox`(JSON Schema 类型构建)和 `ajv`(JSON Schema 校验),用于配置启动时严格契约校验。 ### 1.7 版本管理 项目使用 `package.json.version` 作为版本号唯一来源,严格 `MAJOR.MINOR.PATCH` 格式。 **版本获取方式**: - 开发模式:`src/server/version.ts` 运行时从 `package.json` 读取版本号 - 生产模式:`scripts/build.ts` 在构建时将版本号烘焙为 `APP_VERSION` 字面量注入 `server-entry.ts` **版本升迁命令**: ```bash bun run version:patch # 升迁 patch 版本(0.1.0 → 0.1.1) bun run version:minor # 升迁 minor 版本(0.1.0 → 0.2.0) bun run version:major # 升迁 major 版本(0.1.0 → 1.0.0) bun run version:set 2.0.0 # 显式设置版本号 ``` 版本升迁仅更新 `package.json.version`,不自动创建 git commit、tag 或 changelog。 **API 暴露**:`GET /api/meta` 返回 `{ ok, service, timestamp, version }`,前端通过此接口获取并展示版本号。 ### 1.8 错误模式 - **API 错误**:`{ error: "描述", status: }`,状态码 400/404 - **日志**:非致命异常用 `console.warn`,启动失败用 `console.error` + `process.exit(1)` --- ## 二、前端开发指引 ### 2.1 技术栈概览 | 层面 | 技术 | 用途 | | ------ | --------------------------------------------------- | ------------------------ | | 框架 | React 19 | UI 组件开发 | | 构建 | Vite(开发)+ Bun compile(生产) | 开发服务 HMR 与生产构建 | | 语言 | TypeScript | 类型安全 | | UI 库 | TDesign React + tdesign-icons-react | UI 组件与图标 | | 数据层 | TanStack Query (React Query) + React Query Devtools | 服务端状态管理与自动刷新 | | 路由 | React Router v7 (Declarative mode) | SPA 路由与页面导航 | **不引入的依赖**:状态管理库(TanStack Query 即服务端状态层,组件内用 `useState` 足够) ### 2.2 组件树与数据流 ``` main.tsx └── StrictMode └── ErrorBoundary(React 错误边界) └── QueryClientProvider(TanStack Query 全局挂载) └── BrowserRouter(React Router 路由) ├── App(根组件,Admin 布局) │ ├── useThemePreference() ── Header 主题模式 RadioGroup(系统/明亮/黑暗) │ ├── useSidebarCollapsed() ── 侧边栏折叠状态(localStorage 记忆) │ ├── Layout │ │ ├── Header(品牌名 + 版本号 + 页标题 + 主题切换) │ │ └── Layout(嵌套) │ │ ├── Aside │ │ │ └── Sidebar(TDesign Menu + 底部折叠按钮,菜单项点击导航) │ │ └── Content │ │ └── AppRoutes(路由配置) │ │ ├── / → DashboardPage(欢迎语 + /api/meta 联调) │ │ ├── /users → UsersPage(占位) │ │ ├── /settings → SettingsPage(占位) │ │ └── * → NotFoundPage(404) └── ReactQueryDevtools(开发工具,仅开发环境) ``` **Hook 架构**: ``` hooks/use-theme-preference.ts(浏览器 UI 偏好) ├── ThemePreference: system / light / dark(RadioGroup 受控值) ├── EffectiveTheme: light / dark(写入 document.documentElement theme-mode) ├── localStorage key: theme.preference(同一浏览器记忆) └── matchMedia("(prefers-color-scheme: dark)")(系统模式下跟随系统明暗变化) hooks/use-sidebar-collapsed.ts(侧边栏折叠状态) ├── collapsed: boolean(折叠状态) ├── localStorage key: sidebar.collapsed(同一浏览器记忆) └── toggleCollapsed()(切换折叠状态) ``` **菜单配置**: ``` utils/menu-config.ts(路由与菜单统一数据源) ├── MENU_ITEMS: MenuItemConfig[](菜单项配置数组) │ ├── { value: "dashboard", label: "仪表盘", path: "/", icon: } │ ├── { value: "users", label: "用户管理", path: "/users", icon: } │ └── { value: "settings", label: "系统设置", path: "/settings", icon: } └── Sidebar 和 Routes 共同引用,保证菜单项与路由同步 ``` ### 2.3 TanStack Query 数据层 #### Query Key 规范 ```typescript // 使用 structured array(非字符串),以便精确匹配和按 prefix 失效 const queryKeys = { meta: () => ["meta"] as const, }; ``` - Key 使用 **structured array**(非字符串),以便精确匹配和按 prefix 失效 - 使用 `as const` 保持字面量类型 #### 查询配置规范 ```typescript // 全局级查询(需要持续刷新) useQuery({ queryKey: queryKeys.meta(), queryFn: () => fetchJson("/api/meta"), refetchInterval: 30000, // 30s 轮询 refetchIntervalInBackground: false, // 切后台不轮询 staleTime: 5000, // 5s 内视为 fresh }); ``` #### fetch 封装 ```typescript async function fetchJson(url: string): Promise { const response = await fetch(url); if (!response.ok) throw new Error(`HTTP ${response.status}`); return response.json() as Promise; } ``` - 统一使用 `fetch`(不引入 axios),与后端共享 Web API 生态 - 错误抛异常,由 TanStack Query 的 `error` 状态承接 #### QueryClient 全局配置 ```typescript new QueryClient({ defaultOptions: { queries: { retry: 1, // 失败重试 1 次 refetchOnWindowFocus: true, // 窗口聚焦时刷新 staleTime: 5000, // 5s 内视为 fresh,避免重复请求 }, }, }); ``` ### 2.4 组件开发规范 #### 文件命名与导入 - 每个 React 组件一个 `.tsx` 文件,文件名使用 PascalCase(如 `ErrorBoundary.tsx`) - 组件 props 定义为 `interface XxxProps`,紧邻组件函数声明 - 类型从 `../shared/api` 导入,使用 `type` 导入(`import type { ... }`) ```typescript import type { MetaResponse } from "../shared/api"; interface AppProps { title?: string; } export function App({ title }: AppProps) { // ... } ``` #### 组件拆分原则 - **展示组件**(`components/`):纯渲染逻辑,通过 props 接收数据,通过回调返回事件 - **容器逻辑**放在 hooks 中,组件只做数据消费 - **工具函数**(时间处理等)放在 `utils/`,保持纯函数无副作用 ### 2.5 样式开发规范 前端基于 TDesign React 构建 UI,样式开发遵循以下优先级(从高到低): 1. **使用 TDesign 组件**:布局、间距、排版优先使用 TDesign 组件(如 Space、Divider、Typography) 2. **使用 TDesign 组件 props**:通过组件的 props 参数控制外观(如 `theme`、`variant`、`size`) 3. **使用 TDesign CSS tokens**:颜色、间距、字体等使用 `--td-*` CSS 变量(如 `--td-success-color`、`--td-comp-margin-xxl`) 4. **在 styles.css 中定义 CSS 类**:无法通过上述方式满足的样式需求,集中定义在 `styles.css` 中 5. **自行开发组件**:仅在 TDesign 无法满足需求时自行开发 **红线**: - **严禁在组件中使用 `style` 属性内联调整样式** - **严禁通过 CSS 覆盖 TDesign 组件内部类名**(如 `.t-tab-panel`),如需定制使用组件的 `className` prop - **严禁使用 `!important`** - 颜色统一使用 TDesign CSS tokens(`--td-success-color`、`--td-error-color`、`--td-warning-color` 等),不使用硬编码色值 **styles.css 组织**: - 自定义 CSS 变量定义在 `:root` 中 - 布局类(`.dashboard`、`.dashboard-header-controls`)定义全局页面结构 - 组件修饰类为自定义视觉组件提供样式变体 - 通用工具类(`.full-width`、`.text-disabled`、`.tabular-nums`)提供公用排版能力 ### 2.6 前端测试规范 - 测试目录:`tests/web/`,结构对应 `src/web/` - 重点测试 **纯函数**(时间处理、格式化等)和**组件渲染** - 使用 `bun:test` 框架 - 组件测试使用 `@testing-library/react` 的语义化查询(getByText、getByRole)而非 CSS 选择器 - 测试用户行为而非实现细节:模拟用户点击、输入等操作,而非直接调用组件方法 - 只 mock 系统边界:mock fetch 返回预设响应,使用真实的 QueryClientProvider 包裹组件 --- ## 三、项目运行、集成与打包 ### 3.1 开发期运行 ```bash bun run dev [config.yaml] ``` `scripts/dev.ts` 同时启动两个进程: - **Bun API server**(端口 3000):后端 API 服务,`--watch` 监听后端文件变更自动重启 - **Vite dev server**(端口 5173):前端 SPA + HMR 热更新 开发时访问 `http://127.0.0.1:5173`,Vite 自动将 `/api` 请求代理到后端。 也可以单独启动: ```bash bun run dev:server [config.yaml] # 仅启动后端 API server(--watch 模式) bun run dev:web # 仅启动 Vite dev server ``` ### 3.2 前后端集成方式 #### 双进程开发架构 开发模式下前后端分别由 Vite 和 Bun 服务: - Vite dev server 负责前端 SPA、HMR、模块热替换 - Bun API server 负责后端 API 路由 - Vite 通过 proxy 配置将 `/api/*` 转发到 Bun #### 生产模式架构 生产模式下前端通过 Vite 构建为静态资源,通过 `import with { type: "file" }` 嵌入 Bun 可执行文件: ```typescript // server.ts const server = Bun.serve({ fetch(req) { // staticAssets 存在时服务嵌入的前端资源 if (staticAssets) { return serveStaticAsset(new URL(req.url).pathname, staticAssets); } return new Response("Frontend is served by Vite dev server on :5173", { status: 404 }); }, routes: { "/api/*": () => ..., "/api/meta": { GET: async () => handleMeta(mode, await resolveVersion()) }, }, }); ``` #### 路由优先级 Bun routes 的匹配规则:具体路径 > 通配符。`/api/meta` 优先于 `/api/*`。 未匹配 method 的请求(如 POST /api/meta)会落入 `/api/*` 通配符返回 404;若无该通配符会落入 fetch fallback。 非 API 路径由 fetch fallback 处理:有文件扩展名的返回对应静态资源或 404,无扩展名的返回 SPA index.html。 ### 3.3 构建打包 #### 构建命令 ```bash bun run build ``` #### 构建流程 `scripts/build.ts` 执行三步流水线: ``` 1. Vite build → dist/web/ (前端静态资源,含 code splitting) 2. Code generation → .build/static-assets.ts + .build/server-entry.ts(含版本号字面量注入) 3. Bun compile → dist/my-app (单可执行文件) ``` - Vite 构建前端资源到 `dist/web/`,自动 code splitting(vendor-react、vendor-tdesign、vendor-chart) - Code generation 扫描 `dist/web/` 生成 `import with { type: "file" }` 声明,将资源嵌入 binary - Bun compile 以 `.build/server-entry.ts` 为入口编译最终可执行文件 - `.build/` 临时目录在构建完成后自动清理 #### 产物 | 产物 | 用途 | | ------------- | ---------------------------------------- | | `dist/my-app` | 生产可执行文件(含前端资源,单文件部署) | | `dist/web/` | Vite 构建的前端资源(构建中间产物) | #### 构建参数 | 环境变量 | 说明 | | --------------------------- | -------------------------------------- | | `BUN_TARGET`/`BUILD_TARGET` | 交叉编译目标平台(如 `bun-linux-x64`) | #### 运行可执行文件 ```bash ./dist/my-app [config.yaml] ``` 启动后: - 访问 `http://127.0.0.1:3000/` → 返回前端 SPA - 访问 `http://127.0.0.1:3000/api/meta` → 返回应用元信息 JSON(含版本号) #### 清理 ```bash bun run clean # 清理 dist/ 构建产物和 .build/ 临时文件 ``` ### 3.4 开发工作流 #### 日常开发循环 ```bash bun run dev [config.yaml] # 启动双进程开发环境(Vite :5173 + API :3000) # 访问 http://127.0.0.1:5173 # 修改前端代码 → Vite HMR 热更新 / 修改后端代码 → --watch 自动重启 bun run check # 提交前运行完整质量检查 ``` #### 完整验证流程 ```bash bun run verify # = bun run check + bun run build ``` `verify` 适合 CI 或正式提交前,会完整验证类型检查、lint、格式、单元测试和生产构建。 ### 3.5 脚本说明 | 脚本 | 文件 | 说明 | | ----------------------- | ------------------------- | ---------------------------------------- | | `bun run dev` | `scripts/dev.ts` | 双进程开发服务(Vite :5173 + API :3000) | | `bun run dev:server` | `src/server/dev.ts` | 仅启动后端 API server(--watch 模式) | | `bun run dev:web` | Vite CLI | 仅启动 Vite dev server | | `bun run build` | `scripts/build.ts` | Vite → codegen → Bun compile 三步构建 | | `bun run clean` | `scripts/clean.ts` | 清理构建缓存与临时文件 | | `bun run version:patch` | `scripts/bump-version.ts` | 升迁 patch 版本(x.y.Z) | | `bun run version:minor` | `scripts/bump-version.ts` | 升迁 minor 版本(x.Y.0) | | `bun run version:major` | `scripts/bump-version.ts` | 升迁 major 版本(X.0.0) | | `bun run version:set` | `scripts/bump-version.ts` | 显式设置版本号 | ### 3.6 环境变量 | 变量 | 用途 | 默认值 | | --------------------------- | ----------------------------------------------- | -------- | | `BUN_TARGET`/`BUILD_TARGET` | 交叉编译目标平台(仅在 `bun run build` 时有效) | 当前平台 | ### 3.7 项目配置文件 | 文件 | 用途 | | ---------------------- | ------------------------------------------------------------ | | `package.json` | 项目信息、脚本、依赖声明 | | `tsconfig.json` | TypeScript 配置(ESNext 模块、严格模式) | | `eslint.config.js` | ESLint 规则(含前端不得 import server 的检查) | | `commitlint.config.js` | commitlint 提交信息格式校验 | | `.prettierrc.json` | Prettier 格式化规则(`printWidth: 120`) | | `.prettierignore` | Prettier 排除路径 | | `.lintstagedrc.json` | lint-staged 配置(TS/TSX → ESLint,MD/JSON/YAML → Prettier) | | `config.example.yaml` | 配置文件示例(server.listen 布局 + 显式变量引用) | | `config.schema.json` | 配置文件 JSON Schema(由 bun run schema 生成) | | `vite.config.ts` | Vite 构建配置(React 插件、代码分割、API proxy) | | `bunfig.toml` | Bun 配置(测试 preload、排除规则) | | `opencode.json` | OpenCode 工具配置 | ### 3.8 依赖管理 - **包管理器**:仅使用 `bun`,禁止使用 npm、pnpm、yarn - **安装依赖**:`bun install` - **运行工具**:使用 `bunx`,禁止使用 `npx`、`pnpx` - **锁文件**:`bun.lock` ### 3.9 目录约定 | 目录 | 约定 | | ------------- | ---------------------------------------------------- | | `src/server/` | 后端代码,不能 import `src/web/`(HTML import 除外) | | `src/web/` | 前端代码,不能 import `src/server/` | | `src/shared/` | 前后端共享类型,双向可引用 | | `scripts/` | 独立运行脚本,可 import 项目源码 | | `tests/` | 测试目录,结构镜像 src 目录 | | `dist/` | 构建产物(gitignore) | | `openspec/` | OpenSpec 变更管理与规格文档 | --- ## 代码质量 项目使用多层代码质量保障体系:ESLint 类型感知规则 + Perfectionist 导入排序 + Prettier 格式化(通过 eslint-plugin-prettier 集成至 ESLint)+ TypeScript 严格模式 + Git hooks 自动化。 ```bash bun run lint # ESLint 检查(含类型感知规则、导入排序、导入验证、Prettier 格式) bun run format # Prettier 自动格式化 bun run typecheck # TypeScript 类型检查 bun run schema # 生成 config.schema.json bun run schema:check # 校验 config.schema.json 是否同步 bun test # 运行所有测试 bun run check # 一键运行 schema:check + typecheck + lint + test bun run verify # 完整验证(check + build) ``` `check` 是日常开发推荐的质量检查命令。 ### ESLint 规则 配置文件:`eslint.config.js` | 配置来源 | 用途 | | --------------------------------------------------------------- | -------------------------------------------------- | | `@eslint/js` recommended | JavaScript 基础规则 | | `typescript-eslint` recommended-type-checked | TypeScript 类型感知规则(no-floating-promises 等) | | `typescript-eslint` stylistic-type-checked | TypeScript 风格规则(命名规范、语法选择等) | | `eslint-plugin-perfectionist` recommended-natural | 导入语句和命名导出自动排序 | | `eslint-plugin-import` | 导入路径验证、循环依赖检测、重复导入合并 | | `eslint-plugin-react-hooks` recommended | React Hooks 规则(依赖数组完整性检查等) | | `eslint-plugin-react-refresh` | React Fast Refresh 兼容性检查 | | `eslint-plugin-prettier` recommended + `eslint-config-prettier` | 将 Prettier 格式集成为 ESLint 规则,禁用冲突规则 | **前端导入限制**:`src/web/` 下的文件禁止 `import src/server/` 下的运行时实现,通过 `no-restricted-imports` 规则强制执行。 ### Prettier 配置 配置文件:`.prettierrc.json`,通过 `eslint-plugin-prettier` 集成为 ESLint 规则(`lint` 命令同时检查格式),也可通过 `format` 命令独立运行。 显式声明所有格式化参数(`printWidth: 120`、`semi: true`、`singleQuote: false`、`trailingComma: "all"`、`endOfLine: "lf"` 等),确保不同开发环境产出完全一致的格式化结果。 ### TypeScript 严格标志 | 标志 | 值 | 说明 | | ------------------------------------ | ----- | ------------------------------------------------ | | `strict` | true | 全局严格模式 | | `noUnusedLocals` | true | 未使用局部变量视为错误 | | `noUnusedParameters` | false | 保留关闭(路由 handler 统一签名需要) | | `noPropertyAccessFromIndexSignature` | true | 禁止通过点号访问索引签名属性,强制使用括号语法 | | `noUncheckedIndexedAccess` | true | 数组/Map 访问必须运行时真值检查 | | `noImplicitOverride` | true | 子类覆盖父类方法时必须显式使用 `override` 关键字 | | `verbatimModuleSyntax` | true | 强制 `import type` 纯类型导入,与 Bun 构建兼容 | ### Git Hooks 通过 husky 在 commit 阶段自动执行检查: | Hook | 行为 | | ------------ | -------------------------------------------------------------------------------------------------------------- | | `pre-commit` | lint-staged 对变更文件运行 `eslint --fix`(TS/TSX,含 Prettier 格式修复)或 `prettier --write`(MD/JSON/YAML) | | `commit-msg` | commitlint 校验提交信息格式 `类型: 简短描述` | 提交类型限定:`feat`、`fix`、`refactor`、`docs`、`style`、`test`、`chore`。 `bun install` 时自动初始化 husky hooks,无需手动配置。 ### 质量检查完整清单 提交代码前建议运行: ```bash bun run verify ``` CI 或正式提交前执行完整验证(类型检查 + lint + 格式 + 测试 + 构建),确保代码可编译并通过所有检查。 --- ## 测试 项目采用两层测试体系:单元测试 + 组件测试。所有测试使用 `bun:test` 运行。 ### 测试分层 | 层级 | 覆盖范围 | 位置 | 命令 | | -------- | ---------------------- | ------------------------------------------------------------------------------------------------- | --------------------------------------------- | | 单元测试 | 后端函数、纯函数、常量 | `tests/server/**/*.test.ts`、`tests/scripts/**/*.test.ts`、`tests/web/{utils,hooks}/**/*.test.ts` | `bun test tests/server`、`bun test tests/web` | | 组件测试 | React 组件渲染和交互 | `tests/web/components/**/*.test.tsx` | `bun test tests/web` | ### 运行命令 ```bash bun test # 运行所有单元测试和组件测试 bun test tests/server # 只运行后端单元测试 bun test tests/web # 只运行前端测试(单元 + 组件) bun run check # 日常开发(类型检查 + lint + 测试) bun run verify # 完整验证(check + 构建) ``` ### 组件测试环境 组件测试使用 jsdom 模拟浏览器环境,配置位于 `tests/setup.ts`(通过 `bunfig.toml` preload 加载): - jsdom 提供完整的 DOM 环境 - TDesign 组件所需的 polyfill:ResizeObserver、IntersectionObserver、MutationObserver、matchMedia、attachEvent - 全局 `afterEach` 清理 document.body 内容,确保测试隔离 ### 编写规范 - **优先使用 `@testing-library/react`** 的语义化查询(getByText、getByRole)而非 CSS 选择器 - **测试用户行为而非实现细节**:模拟用户点击、输入等操作,而非直接调用组件方法 - **只 mock 系统边界**:mock fetch 返回预设响应,使用真实的 QueryClientProvider 包裹组件 - **组件测试文件命名**:`tests/web/ComponentName.test.tsx` - **测试目录镜像源码目录**:`tests/server/config.test.ts` 对应 `src/server/config.ts` --- ## 已知限制 - 当前仅为单页面应用,不涉及用户认证和权限控制 - 不支持集群部署,单进程运行 - 配置文件仅支持 YAML 格式,不支持热加载 - 变量替换范围仅限 `server` 子树 - 无国际化和多语言支持