- Vue 71%
- TypeScript 20.3%
- JavaScript 4.1%
- CSS 3.9%
- Dockerfile 0.5%
- Other 0.2%
- app-client/main/easytier:拉起 easytier-core 子进程,用 easytier-cli -o json 轮询状态;虚拟 IP、对端延迟、p2p/中继、上下行速率都来自真实数据 - 组网凭据与入网节点由服务端在进房时下发,客户端不再自己编 - app-client:发票页(抬头管理、可开票订单、开票、换开) - app-admin-web:发票列表与详情,含红冲链路 - Dockerfile 基础镜像走 build-arg - .forgejo/workflows/ci.yaml:检查 → 构建镜像(sha tag)→ 回写 deploy 仓库 |
||
|---|---|---|
| .forgejo/workflows | ||
| app-admin-web | ||
| app-client | ||
| app-website | ||
| .gitignore | ||
| package.json | ||
| pnpm-lock.yaml | ||
| pnpm-workspace.yaml | ||
| README.md | ||
| vite.config.ts | ||
带你玩 · 前端 monorepo
官网、管理站点、以后的玩家客户端都放在这里,用 pnpm workspace +
Vite+(vp)管理。后端是独立的 Go workspace,在 ../backend。
frontend/
├── package.json # workspace 根,只装工具链
├── pnpm-workspace.yaml # 工作区范围:app-* 和 lib-*
├── vite.config.ts # Vite+ 根配置:格式化 / lint / 任务编排
├── .npmrc .gitignore
├── app-website/ # 官网(Nuxt 4,SSR + 宣传页 SSG)
├── app-admin-web/ # 管理站点(Vue 3 SPA + shadcn-vue)
└── app-client/ # 玩家客户端(Electron 44 + electron-vite)
子项目平铺在本目录下,用前缀区分:app-* 是可独立部署的应用,
lib-* 是跨应用共享的库(还没有)。加新子项目只要建目录、写 package.json,
pnpm-workspace.yaml 的通配符会自动带上它。
需求与排期见 上级目录的 README。
环境要求
Node 和 pnpm 都钉死在 package.json 的 devEngines 里,本机版本不对会自动下载对应版本,
不需要手工装、也不需要 nvm/fnm:
- Node.js 26.8.1
- pnpm 12.3.0
vp env doctor # 看当前解析到哪个 Node、从哪读的
vp env pin 27 # 换大版本:改 devEngines.runtime 并装好
运行时是 Vite+ 在管,不是 pnpm。 装了 vite-plus 之后 node / npm / npx / corepack
在 PATH 上都是 ~/.vite-plus/bin 下的 shim,它按
.node-version → devEngines.runtime → engines.node → .nvmrc 的顺序找版本声明,
就近目录优先。所以别再往 pnpm-workspace.yaml 里写 nodeVersion,两处声明会打架。
engines.node 保持和 devEngines.runtime 同步,是为了 CI 里那些只会读 engines
的工具(比如 actions/setup-node 的 node-version-file: package.json)也能挑对版本。
上手
pnpm install # 装全部子项目的依赖
pnpm dev:website # 官网 http://localhost:3000
pnpm dev:admin # 管理站点 http://localhost:5173
pnpm dev:client # 玩家客户端(拉起 Electron 窗口)
管理站点要连后端,先把 API 起起来(第一次需要建表和灌数据):
cd ../backend && make docker-up && make migrate-up && make seed && make run
常用命令
vp 是 Vite+ 的 CLI,装在 workspace 根,用 pnpm exec vp <cmd> 调用。
| 命令 | 作用 |
|---|---|
pnpm install |
安装依赖 |
pnpm dev:website / pnpm dev:admin |
起单个子项目 |
pnpm build:all |
按依赖顺序构建全部子项目 |
pnpm typecheck:all |
全部子项目跑类型检查 |
pnpm exec vp check |
格式检查 + lint,一遍过 |
pnpm exec vp fmt |
用 oxfmt 格式化 |
pnpm exec vp lint |
用 oxlint 检查 |
pnpm exec vp run -F @dainiwan/admin-web build |
只构建某一个 |
pnpm exec vp cache clean |
清任务缓存 |
vp run 带任务缓存:输入没变就直接复用上次产物。
@dainiwan/website#build 和 @dainiwan/client#build 目前不走缓存——Nuxt 和
electron-vite 在构建过程中都会读回自己刚写进输出目录的文件,自动追踪判定为
「任务改了自己的输入」就放弃缓存了;两者本身都有内部缓存,影响不大。
加一个子项目
mkdir app-foo
# app-foo/package.json 里 name 写 @dainiwan/foo
pnpm install
vp run -r build、vp check 都不用改配置。子项目之间要共享代码时,
建一个 lib-xxx/,在使用方的 package.json 里写 "@dainiwan/xxx": "workspace:*"。
带安装脚本的依赖要在 pnpm-workspace.yaml 的 allowBuilds 里显式写 true 或 false,
少写一个 pnpm 12 会直接报错。(Electron 44 起没有 postinstall 了,
二进制改成首次启动时惰性下载,不需要在这里放行。)
pnpm 配置都在 pnpm-workspace.yaml
pnpm 12 起,package.json 的 "pnpm" 字段不再被读取,.npmrc 里的同名项也在下线,
配置的正式位置是 pnpm-workspace.yaml。写错的键会直接报错而不是静默忽略——
这是好事,但从旧版本迁过来时要留意。
两个需要知道的设置:
allowBuilds:取代了onlyBuiltDependencies,是个包名: true/false的表。 没列到的包如果带安装脚本,安装会失败(旧版本只是警告),所以要显式写 false 表示「就是不给跑」。minimumReleaseAge:供应链冷静期,拒绝安装发布时间不足这个时长的版本, 挡一手「投毒版本发布后被 CI 立刻拉走」。pnpm 12 默认 1440 分钟(24 小时), 这里放宽到了 60,原因写在配置文件的注释里。这是个安全取舍, 想更保守就删掉那一行用回默认值。
代码规范
格式化与 lint 的规则全部集中在根 vite.config.ts,没有单独的配置文件,
也不需要 prettier —— oxfmt 就是格式化器,连忽略规则都写在同一份配置里。
- oxfmt:单引号、无分号、行宽 320(oxfmt 允许的上限),
objectWrap: 'collapse'。合起来的效果是「能不换行就不换行」: 中文字符按两列算,行宽小了会把中文字面量拆得很碎,换行位置交给写代码的人自己把握, 格式化只统一引号、缩进和尾逗号。 - oxlint:typescript / vue / import 三个插件,开了
typeAware(能查出漏 await 的 Promise 这类问题)。 - 没开 oxlint 的
typeCheck——它的类型检查器还不认识.vue单文件组件, 会把所有import X from './X.vue'报成找不到模块。类型检查走各子项目自己的vue-tsc/nuxt typecheck,即pnpm typecheck:all。 - 不参与格式化的路径写在
vite.config.ts的fmt.ignorePatterns(目前只有 shadcn-vue 生成的组件),.gitignore里的路径 oxfmt 自动跳过。
Vue 模板里不要写多语句的内联事件处理器。 oxfmt 会把
@click="a = null; b = null" 的分号去掉、拆成两行,Vue 的表达式解析器接受不了。
两句以上就抽成 <script setup> 里的方法。
待办
- 各子项目的 Dockerfile / 打包流水线与 Forgejo Runner
lib-*:官网、管理站点、客户端共用的类型与常量- 客户端的代码签名(Windows 证书、Apple 公证)与自动更新