Files
bun-app-template/DEVELOPMENT.md
lanyuanxiaoyao c592f2b97c feat: 引入分层配置生命周期,支持变量引用和 JSON Schema 校验
- 新增 src/server/config/ 模块(types、issues、variables、normalizer、schema)
- 配置布局从 server.host/server.port 切换为 server.listen.host/server.listen.port
- 移除 HOST/PORT 隐式环境变量覆盖,改为 YAML 显式 ${KEY} 变量引用
- 支持 ${KEY}、${KEY|default}、${KEY|}、$${KEY} 变量语法
- 使用 @sinclair/typebox + ajv 实现运行时严格契约校验和 JSON Schema 导出
- 新增 scripts/generate-config-schema.ts 和 config.schema.json
- 新增 bun run schema / schema:check 命令,check 先执行 schema:check
- 更新 README.md 和 DEVELOPMENT.md 匹配新配置体系
- 新增变量解析、schema 校验和 schema 同步测试
2026-05-25 12:17:40 +08:00

36 KiB
Raw Blame History

my-app 开发文档

本文档面向 my-app 项目的开发者,介绍项目结构、前后端架构、构建流程、测试、代码规范等内容。

用户使用说明请参阅 README.md

目录


项目结构

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  主题偏好 hooksystem/light/darklocalStorage 记忆 + matchMedia 监听)
      use-sidebar-collapsed.ts 侧边栏折叠状态 hooklocalStorage 记忆)
    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.serveBun.fileBun.YAMLBun.spawnbun:sqlite(如需数据存储)
2 es-toolkit 类型判断(isPlainObject/isNil/isEmptyObject)、深度比较(isEqual)、并发控制(Semaphore
3 标准 Web API Object.fromEntriesHeadersfetchAbortControllerResponse
4 主流三方库 按需引入,优先社区活跃、类型完善的库
5 自行实现 仅在以上都无法满足时(如 parseDurationparseSize 等专项逻辑)

原则:新增依赖前先检查上述每一层级是否已有可用方案。禁止随意引入新依赖。

1.3 API 路由开发

路由文件位于 src/server/routes/,每个端点一个文件。路由通过 server.tsBun.serve({ routes }) 声明式注册,使用 per-method handler 对象:

// server.ts 中的路由注册
routes: {
  "/api/*": () => jsonResponse(createApiError("API route not found", 404), { mode, status: 404 }),
  "/api/meta": {
    GET: async () => handleMeta(mode, await resolveVersion()),
  },
}

Handler 函数签名:

// 带版本号参数的路由
export function handleMeta(mode: RuntimeMode, version: string): Response;

请求处理流程

  1. Bun.serveroutes 对象按路径 + HTTP 方法匹配请求
  2. 未匹配方法的请求落入 /api/* 通配符(返回 404
  3. 各 handler 内部通过 helpers.tsjsonResponsecreateApiError 等格式化输出
  4. 需要参数校验时使用 middleware.ts 提供的校验函数,返回 Response 实例表示校验失败(直接返回),返回数据对象表示通过

新增路由步骤

  1. src/server/routes/ 下创建 <name>.ts
  2. 实现 handler 函数并 export
  3. server.tsroutes 对象中注册路径和 method handler
  4. tests/server/ 中添加对应测试

1.4 共享工具

  • helpers.ts:跨路由共用的响应工具函数

    • createApiError(error, status) — 构造 API 错误体
    • createHeaders(mode, init) — 创建响应 Headers生产模式附加安全头X-Content-Type-OptionsReferrer-Policy
    • createMetaResponse(version) — 构造应用元信息响应 { ok: true, service, timestamp, version }
    • formatDuration(ms) — 毫秒转为可读时长字符串
    • jsonResponse(body, options) — JSON 响应构造
  • middleware.tsAPI 参数校验函数

    • validateIdParam(idStr, mode) — 校验 ID 参数格式(字母数字下划线连字符,字母开头),返回 { id }Response
    • validatePagination(pageParam, pageSizeParam, mode) — 校验分页参数(默认 page=1, pageSize=20pageSize 上限 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 响应类型(ApiErrorResponseMetaResponse)定义在 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. 解析 YAMLBun.YAML.parse 将配置文件解析为 unknown
  2. normalize:提取 variables,替换 ${KEY} / ${KEY|default} 变量引用,移除 variables 段,产出 NormalizedConfig
  3. strict validate:使用 Ajv + TypeBox 生成的 schema 严格校验(拒绝未知字段、校验类型和范围)
  4. resolve:从校验后的配置中提取 server.listen.hostserver.listen.port,填充默认值

ServerConfig 包含以下字段:

字段 来源 默认值
host server.listen.host(可含变量引用)→ 默认 127.0.0.1
port server.listen.port(可含变量引用)→ 默认 3000

配置文件示例(config.example.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 相关命令:

bun run schema        # 重新生成 config.schema.json
bun run schema:check  # 校验 config.schema.json 是否同步

运行时依赖 @sinclair/typeboxJSON Schema 类型构建)和 ajvJSON Schema 校验),用于配置启动时严格契约校验。

1.7 版本管理

项目使用 package.json.version 作为版本号唯一来源,严格 MAJOR.MINOR.PATCH 格式。

版本获取方式

  • 开发模式:src/server/version.ts 运行时从 package.json 读取版本号
  • 生产模式:scripts/build.ts 在构建时将版本号烘焙为 APP_VERSION 字面量注入 server-entry.ts

版本升迁命令

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: <code> },状态码 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
    └── ErrorBoundaryReact 错误边界)
        └── QueryClientProviderTanStack Query 全局挂载)
            └── BrowserRouterReact Router 路由)
                ├── App根组件Admin 布局)
                │   ├── useThemePreference() ── Header 主题模式 RadioGroup系统/明亮/黑暗)
                │   ├── useSidebarCollapsed() ── 侧边栏折叠状态localStorage 记忆)
                │   ├── Layout
                 │   │   ├── Header品牌名 + 版本号 + 页标题 + 主题切换)
                │   │   └── Layout嵌套
                │   │       ├── Aside
                 │   │       │   └── SidebarTDesign Menu + 底部折叠按钮,菜单项点击导航)
                │   │       └── Content
                │   │           └── AppRoutes路由配置
                │   │               ├── / → DashboardPage欢迎语 + /api/meta 联调)
                │   │               ├── /users → UsersPage占位
                │   │               ├── /settings → SettingsPage占位
                │   │               └── * → NotFoundPage404
                └── ReactQueryDevtools开发工具仅开发环境

Hook 架构

hooks/use-theme-preference.ts浏览器 UI 偏好)
├── ThemePreference: system / light / darkRadioGroup 受控值)
├── 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: <DashboardIcon /> }
│   ├── { value: "users", label: "用户管理", path: "/users", icon: <UserIcon /> }
│   └── { value: "settings", label: "系统设置", path: "/settings", icon: <SettingIcon /> }
└── Sidebar 和 Routes 共同引用,保证菜单项与路由同步

2.3 TanStack Query 数据层

Query Key 规范

// 使用 structured array非字符串以便精确匹配和按 prefix 失效
const queryKeys = {
  meta: () => ["meta"] as const,
};
  • Key 使用 structured array(非字符串),以便精确匹配和按 prefix 失效
  • 使用 as const 保持字面量类型

查询配置规范

// 全局级查询(需要持续刷新)
useQuery({
  queryKey: queryKeys.meta(),
  queryFn: () => fetchJson<MetaResponse>("/api/meta"),
  refetchInterval: 30000, // 30s 轮询
  refetchIntervalInBackground: false, // 切后台不轮询
  staleTime: 5000, // 5s 内视为 fresh
});

fetch 封装

async function fetchJson<T>(url: string): Promise<T> {
  const response = await fetch(url);
  if (!response.ok) throw new Error(`HTTP ${response.status}`);
  return response.json() as Promise<T>;
}
  • 统一使用 fetch(不引入 axios与后端共享 Web API 生态
  • 错误抛异常,由 TanStack Query 的 error 状态承接

QueryClient 全局配置

new QueryClient({
  defaultOptions: {
    queries: {
      retry: 1, // 失败重试 1 次
      refetchOnWindowFocus: true, // 窗口聚焦时刷新
      staleTime: 5000, // 5s 内视为 fresh避免重复请求
    },
  },
});

2.4 组件开发规范

文件命名与导入

  • 每个 React 组件一个 .tsx 文件,文件名使用 PascalCaseErrorBoundary.tsx
  • 组件 props 定义为 interface XxxProps,紧邻组件函数声明
  • 类型从 ../shared/api 导入,使用 type 导入(import type { ... }
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 参数控制外观(如 themevariantsize
  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 开发期运行

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:5173Vite 自动将 /api 请求代理到后端。

也可以单独启动:

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 可执行文件:

// 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 构建打包

构建命令

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 splittingvendor-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

运行可执行文件

./dist/my-app [config.yaml]

启动后:

  • 访问 http://127.0.0.1:3000/ → 返回前端 SPA
  • 访问 http://127.0.0.1:3000/api/meta → 返回应用元信息 JSON含版本号

清理

bun run clean
# 清理 dist/ 构建产物和 .build/ 临时文件

3.4 开发工作流

日常开发循环

bun run dev [config.yaml]  # 启动双进程开发环境Vite :5173 + API :3000
# 访问 http://127.0.0.1:5173
# 修改前端代码 → Vite HMR 热更新 / 修改后端代码 → --watch 自动重启
bun run check              # 提交前运行完整质量检查

完整验证流程

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 → ESLintMD/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,禁止使用 npxpnpx
  • 锁文件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 自动化。

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: 120semi: truesingleQuote: falsetrailingComma: "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 --fixTS/TSX含 Prettier 格式修复)或 prettier --writeMD/JSON/YAML
commit-msg commitlint 校验提交信息格式 类型: 简短描述

提交类型限定:featfixrefactordocsstyletestchore

bun install 时自动初始化 husky hooks无需手动配置。

质量检查完整清单

提交代码前建议运行:

bun run verify

CI 或正式提交前执行完整验证(类型检查 + lint + 格式 + 测试 + 构建),确保代码可编译并通过所有检查。


测试

项目采用两层测试体系:单元测试 + 组件测试。所有测试使用 bun:test 运行。

测试分层

层级 覆盖范围 位置 命令
单元测试 后端函数、纯函数、常量 tests/server/**/*.test.tstests/scripts/**/*.test.tstests/web/{utils,hooks}/**/*.test.ts bun test tests/serverbun test tests/web
组件测试 React 组件渲染和交互 tests/web/components/**/*.test.tsx bun test tests/web

运行命令

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 组件所需的 polyfillResizeObserver、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 子树
  • 无国际化和多语言支持