项目:vitest-playground | 版本:v1.1 | 生成日期:2026-10-06 | 依据:源码分析
本篇是配置参考手册:逐文件、逐段解释项目的全部配置面——两个 Vite/Vitest 配置、package.json 脚本与依赖策略、TypeScript 编译选项、周边工具配置、环境变量清单,以及从 NAS Docker 到 VPS nginx 的完整部署链路配置。运行行为如何由这些配置决定,交叉引用《01-系统架构》与《06-部署运行》;本篇保证"改任何配置前,在这篇里能查到它的全部联动点"。
| 文件 | 角色 | 关键内容 |
|---|---|---|
vite.config.ts | 构建 + dev/preview 服务器 + 运行器挂载 | react 插件、中间件插件、manualChunks、alias、COEP/COOP、allowedHosts |
vitest.config.ts | 本项目自测 | happy-dom、globals、setupFiles、include 收敛、coverage、testTimeout |
package.json | 脚本、依赖、工程约束 | 9 个 scripts、精确锁定 vitest 三件套、packageManager、engines |
tsconfig.json | 全仓 TS(strict) | bundler 解析、paths 别名、types 三件套、include 清单 |
.npmrc | pnpm 行为 | legacy-peer-deps=true |
pnpm-workspace.yaml | pnpm 构建脚本放行 | allowBuilds: { esbuild: true }(非多包工作区) |
.editorconfig | 编辑器风格 | 2 空格缩进、LF、UTF-8(md 不去行尾空白、Makefile 用 tab) |
.gitignore | 仓库卫生 | node_modules、dist、.vitest-runs、.vitest-sandbox、.playwright-mcp/ 等 |
.github/workflows/ci.yml | CI 门禁 | quality + lessons 双 job(详解见《09-测试与质量保障》§4) |
注意:仓库没有 ESLint/Prettier/Babel 配置文件(详见 §6)。以下按文件展开。
插件数组(第 24 行):react()(@vitejs/plugin-react,JSX 转换与 Fast Refresh)+ vitestRunnerMiddleware()。后者是本文件的核心自定义(第 7-21 行):一个返回 { name: 'vitest-runner-middleware', configureServer, configurePreviewServer } 的插件工厂,两个钩子里都是同一句 server.middlewares.use('/api/run-vitest', (req, res, next) => { handleRunVitest(req, res).catch(next); })——dev 与 preview 双挂载保证两种模式测试能力同构;对运行器的依赖只有 handleRunVitest 一个符号(最小接缝,见《07-模块详解》§2)。文件顶部的类型标注把 server 参数收窄为只声明 middlewares.use 接口,避免依赖 Vite 内部类型。
build 段(第 25-35 行):chunkSizeWarningLimit: 2500(放宽告警阈值以容纳 monaco 大块)与 rollupOptions.output.manualChunks 三分——monaco: ['monaco-editor', '@monaco-editor/react']、antd: ['antd', '@ant-design/icons']、'react-vendor': ['react', 'react-dom', 'react-router-dom']。AGENTS.md 铁律 4 明文要求不得改坏该拆分(体积与缓存粒度的设计依据见《10-性能设计与优化》§5)。
resolve.alias(第 37-42 行):@ → ./src、@root → 项目根,用 fileURLToPath(new URL(..., import.meta.url)) 实现 ESM 安全的绝对路径。别名有三处声明必须保持一致:这里是运行/构建时,vitest.config.ts 第 6-9 行(join(__dirname, 'src'))是自测时,tsconfig.json 的 paths 是类型检查时——新增别名要三处同步,漏一处会出现"类型通过但运行解析失败"的暗裂痕。
server 段(第 43-49 行):port: 8000;headers 设置 Cross-Origin-Embedder-Policy: require-corp 与 Cross-Origin-Opener-Policy: same-origin。注释原话说明其历史:"WebContainer 需要页面处于跨域隔离状态(SharedArrayBuffer)"——WebContainer 移除后头被保留,现行用途是 Monaco Worker 同源加载一致性(README.md 运行注意),铁律 5 要求不删、生产宿主配同样的头。缺失后果见《12-FAQ与故障排查》§3.7。
preview 段(第 51-58 行):allowedHosts: ['vitest-playground.zhangzhengyang.com'](Vite 的 Host 头校验白名单,防 DNS rebinding,生产域名变更时记得同步)+ 同样的 COEP/COOP headers。
自测配置与产品无运行时关系,但决定 pnpm test 的行为:resolve.alias 同步两别名;test.environment: 'happy-dom'(全自测在 DOM 模拟环境跑,tests/setupTests.ts 补齐最小浏览器面);globals: true(测试文件可不 import describe/it,但现有测试文件实际都显式 import,两种风格并存);setupFiles: ['./tests/setupTests.ts'];include 收敛到 src/pages/vitest-learn/**/*.{test,spec}.{ts,tsx} 与 tests/**/*.{test,spec}.{ts,tsx},注释原文说明目的是"leftover Ant Design Pro template files do not interfere";exclude: ['node_modules', 'dist'];coverage:v8 provider、reporter text/json/html、include src/**/*.{ts,tsx}(pnpm test:coverage 用);passWithNoTests: true(防止 include 收敛后空匹配直接失败);testTimeout: 15000(比默认 5s 宽,容纳真 spawn vitest 的集成用例)。新增测试文件必须落在 include 的两个目录里,否则静默不被拾取。
scripts 全表:
| script | 实际命令 | 用途 | 备注 |
|---|---|---|---|
dev | vite | 开发服务器 :8000 | 已挂运行器与 COEP/COOP |
build | vite build | 产出 dist/ | manualChunks 生效,chunk 告警即门禁 |
preview | vite preview --port 8000 | 预览产物 + 运行器 | 生产等价链路,发布清单用 |
tsc | tsc --noEmit | 仅类型检查 | CI quality 第一步 |
test | vitest run | 全量自测 | 含集成用例 |
test:watch | vitest | watch 模式 | 日常开发 |
test:ui | vitest --ui | Vitest UI 面板 | 依赖 @vitest/ui |
test:coverage | vitest run --coverage | 覆盖率报告 | v8 provider |
check:lessons | node --experimental-strip-types scripts/check-lessons.ts | 课时质检 | Node ≥ 22 的 strip-types 能力;依赖 node_modules/.pnpm 下的 esbuild 二进制 |
依赖策略:dependencies 全是前端运行时(antd 系、monaco 系、react 系);devDependencies 含测试与工具链。三个精确锁定值得强调:vitest、@vitest/coverage-v8、@vitest/ui 全部钉在 4.1.8(无 ^)——运行器行为(JSON reporter 结构、outputFile、bench 表格格式、pragma 语义)与版本强耦合,升级必须三包联动并全量回归(《06-部署运行》§9)。工程约束字段:"type": "module"(全仓 ESM,运行器能用顶层 import);"packageManager": "pnpm@10.34.5"(CI 的 pnpm/action-setup 自动读取);"engines": { "node": ">=22.0.0" }(运行器依赖 detached 进程组与 process.kill(-pid),且 check:lessons 依赖 strip-types)。
target/module: esnext + moduleResolution: "bundler"(Vite 生态标准组合,允许 import 裸模块与 ESM 语法);jsx: "react-jsx"(自动 JSX runtime,与沙箱配置 esbuild: { jsx: 'automatic' }、Monaco compilerOptions 的 ReactJSX 三方一致);strict: true + forceConsistentCasingInFileNames: true(后者正是 runner-hardening Task 1 修复 App.tsx 大小写问题的门禁来源);noEmit: true(tsc 只做检查,构建交给 vite);lib: ["esnext", "dom", "dom.iterable"];types: ["vitest/globals", "node", "vite/client"]——三者的含义分别是:测试全局函数类型(配合运行时 globals)、运行器用到 node:child_process/fs/http 的类型、?worker 等 Vite 特有导入的类型(monacoSetup.ts 的两个 worker import 依赖它);paths 与前述两处 alias 对应;include 精确列出 src/main.tsx、src/app.tsx、vite-env.d.ts、vitest-learn 目录、tests 目录与两个配置文件(不含 scripts/check-lessons.ts——它靠 node --experimental-strip-types 直跑,不受 tsconfig 门禁,改它时类型错误要靠运行暴露 ⚠️)。
仓库当前没有 ESLint 配置(无 .eslintrc*、eslint.config.*)、没有 Prettier 配置;格式约束由 .editorconfig 承担(2 空格、LF、UTF-8、末行换行)。唯一的 ESLint 痕迹是 src/pages/vitest-learn/progressPage.tsx 里一条 // eslint-disable-next-line react-hooks/exhaustive-deps 注释——它指向一个并不存在的检查器,属模板残留(已列入《13-技术债务与演进建议》问题 13)。实际后果:Hooks 依赖数组、未使用变量等静态问题无门禁,风格一致性靠人工与编辑器。补建建议同《13》§2 第二阶段:flat-config ESLint + typescript-eslint + react-hooks,先 warn 后 error 接入 CI quality job。
产品运行时:应用进程本身不读取任何环境变量(vite.config.ts 不读秘密,容器化方案 Global Constraints 明文延续此约束);唯一的消费点是运行器给子进程构造的白名单(buildChildEnv,vitestServerRunner.ts 第 96-108 行):NODE_ENV(硬编码 test,不接受覆盖)、HOME(硬编码为沙箱目录)、LANG(透传宿主,缺省 C.UTF-8)、PATH(透传宿主,缺省 /usr/local/bin:/usr/bin:/bin),Windows 平台追加 SystemRoot/COMSPEC 透传。其余 process.env 一律不继承——防宿主密钥经测试代码外泄,哨兵变量测试锁定(键集合是精确比对,多传一个键 CI 即红)。
质检脚本侧:scripts/check-lessons.ts 第 24 行 const { CI: _ci, GITHUB_ACTIONS: _ga, ...childEnv } = process.env 显式剔除两个 CI 标记——与产品 env 白名单语义对齐,防止 CI 模式改变 vitest 行为(.only 拒绝、快照缺失判失败)造成假失败。
规划中(未实现):VITEST_RUNNER_BACKEND=local|docker——docs/superpowers/plans/2026-09-28-container-isolation.md 定义的后端切换变量,含"严格匹配字面量 'docker',其余一律 local"的防误开设计;当前源码无该变量(⚠️ 计划状态)。除上述外无 secret 类环境变量;runbook 的泄露面核查流程用于确认历史遗留(《08-安全与权限设计》§5)。
.npmrc 仅一行 legacy-peer-deps=true(兼容安装策略)。pnpm-workspace.yaml 内容是 allowBuilds: { esbuild: true }——放行 esbuild 的安装期构建脚本,它不是多包工作区声明(没有 packages 字段),勿被名字误导。.gitignore 的项目特有条目:.vitest-runs(运行临时目录)、.vitest-sandbox(历史遗留目录)、.playwright-mcp/(GUI 自动化产物目录)、/coverage、*bak、.worktrees/.superpowers(SDD 草稿区);package-lock.json 未列入但 AGENTS.md 铁律 3 明文禁止生成(pnpm-only)。
生产拓扑(docs/deploy/runbook.md 2026-09-28 澄清段):NAS 上 Docker 容器 vitest-playground-web(端口映射 4800→容器内 8000)→ frpc 隧道 → VPS 侧 nginx 反代 443 → 4800,域名 vitest-playground.zhangzhengyang.com。逐跳配置要点:
容器内:完整的 pnpm install --frozen-lockfile && pnpm build 产物 + pnpm preview(承载 /api/run-vitest 中间件的 Node 服务);镜像/容器构建与更新流程以 NAS 侧《部署与避雷手册》§3 为准(runbook 开头注明),关键避雷点:长构建必须 setsid 脱离 ssh 会话、验证只认容器/镜像/指纹证据。
frpc 隧道:把 NAS 的 4800 暴露到 VPS 本地回环(具体 frpc 配置在 NAS 侧手册,本仓库无该文件 ⚠️ 待确认——runbook 只描述拓扑不含 frpc.ini)。
VPS nginx(runbook 配置片段原文要点):limit_req_zone $binary_remote_addr zone=vitest_run:10m rate=10r/m;location /api/run-vitest 内 limit_req zone=vitest_run burst=3 nodelay; limit_req_status 429; proxy_pass http://127.0.0.1:4800; proxy_read_timeout 600s;(600s 的推导:2 并发消化 8 深排队最坏约 300s,留缓冲防 504——应用容量参数变更时必须重算,见《03-接口设计》§8);location / 仅透传,注释明确警示勿重复 add_header COEP/COOP(响应头由应用侧 preview.headers 携带,nginx 重复添加可能导致头重复或覆盖不一致)。生效与验证:nginx -t && nginx -s reload;6 连 curl 验证 429;curl -sI 验证 COEP/COOP 两头在 443 响应中存在(完整发布核对清单见《06-部署运行》§10)。这条链路上每一跳的超时预算是串联的(nginx 600s → preview 无独立超时 → 运行器 60s × 排队),任何一跳改动都建议按《12-FAQ与故障排查》§1 的日志入口验证端到端行为。