From a01caba0c83070cfc8a556c6ffecc30f386e23ec Mon Sep 17 00:00:00 2001 From: unanmed <1319491857@qq.com> Date: Wed, 6 May 2026 19:46:54 +0800 Subject: [PATCH] =?UTF-8?q?docs:=20=E4=BC=98=E5=8C=96=E6=8F=90=E7=A4=BA?= =?UTF-8?q?=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Copilot --- dev.md | 126 ++++++++++++++++++++++++++++++++---------------------- prompt.md | 34 ++++++++------- 2 files changed, 92 insertions(+), 68 deletions(-) diff --git a/dev.md b/dev.md index 50b1504..19d9c08 100644 --- a/dev.md +++ b/dev.md @@ -2,12 +2,12 @@ ## 项目结构 -`public`: mota-js 样板所在目录。 -`packages`: 核心引擎代码 monorepo。 -`packages-user`: 用户代码 monorepo。 -`src`: 游戏入口代码。 +- `public`: mota-js 样板所在目录 +- `packages`: 核心引擎代码 monorepo +- `packages-user`: 用户代码 monorepo +- `src`: 游戏入口代码 -`packages` `packages-user` 可以单独打包为库模式,`src` 单向引用 `packages-user`,`packages-user` 单向引用 `packages`,`src` 为游戏的入口代码。 +依赖关系为单向:`src` → `packages-user` → `packages`。`packages` 与 `packages-user` 均可独立打包为库模式,`src` 为游戏的入口代码。 ## 开发环境 @@ -15,66 +15,88 @@ - `pnpm >= 10.0.0` - 任意支持 `ESNext` 特性的浏览器 -**建议使用 `vscode`,搭配 `prettier` `eslint` 插件** +**建议使用 `vscode`,搭配 `prettier` `eslint` 插件。** ## 开发说明 1. 将项目拉取到本地。 -2. 运行 `pnpm i` 安装所有依赖,如有要求运行 `pnpm approve-builds`,请允许全部。 +2. 运行 `pnpm i` 安装所有依赖(如提示运行 `pnpm approve-builds`,请允许全部)。 3. 运行 `pnpm dev` 进入开发环境。 ## 构建说明 -- `pnpm build:packages`: 构建所有 `packages` 文件夹下的内容,使用库模式。 -- `pnpm build:game`: 构建为可以直接部署的构建包。 -- `pnpm build:lib`: 构建所有 `packages` `packages-user` 文件夹下的内容,使用库模式。 -- `pnpm type`: 对仓库执行类型检查 -- `pnpm check:circular`: 对仓库执行循环引用检查 +| 命令 | 说明 | +| --------------------- | ------------------------------------------------------- | +| `pnpm build:packages` | 以库模式构建 `packages` 下的所有内容 | +| `pnpm build:game` | 构建为可直接部署的游戏包 | +| `pnpm build:lib` | 以库模式构建 `packages` 与 `packages-user` 下的所有内容 | +| `pnpm type` | 对仓库执行类型检查 | +| `pnpm check:circular` | 对仓库执行循环引用检查 | ## 开发原则 -- 模块原则: - - 无副作用原则:所有模块不包含副作用内容,全部由函数、类、常量的声明组成,不出现导出的变量声明、代码执行内容,允许但不建议编写类的静态块。 - - 如果需要模块初始化,编写一个 `createXxx` 函数,然后在 `index.ts` 中整合,再逐级向上传递,直至遇到包含 `create` 函数的 `index.ts`,所有初始化将会统一在顶层模块中执行。 - - 不允许一个文件导出不属于当前 `monorepo` 或当前文件夹的内容。 - - 不允许出现循环引用,如果不得不进行循环引用,应当首先考虑接口设计是否有问题。 -- 命名规则: - - 变量、成员、一般常量、方法、函数使用小驼峰。 - - 类、接口、类型别名、命名空间、泛型、枚举、组件使用大驼峰。 - - 不变常量使用全大写命名法,单词之间使用下划线连接。 - - 专有名词缩写如 `HTTP`, `URI` 全部大写。 - - 会被 `implements` 的接口使用大写 `I` 开头。 - - `id`, `class` 等 `HTML/CSS` 内容使用连字符命名法。 - - 不使用下划线命名法。 -- 注释: - - 常用属性成员、方法、接口、类型必须添加 `jsDoc` 注释。 - - 长文件可使用 `#region` 分段,可以写上 `#endretion` 允许折叠。 - - TODO 使用 `// TODO:` 或 `// todo:` 格式。 - - 单行注释的双斜杠与注释内容之间添加一个空格,多行注释只允许出现 `jsDoc` 注释,如果需要多行非 `jsDoc` 注释,使用多个单行注释。 - - 注释进行合理换行,考虑到中文字符较宽,建议 40-60 个字符进行换行。不允许在句中换行,必须在标点符号后换行。参数注释换行后保持对齐。 - - 单行注释结尾不添加句号,对于多行长注释,可以在结尾添加句号。 -- 类型: - - 不允许出现非必要的 `any` 类型。 - - 所有类的成员必须显式声明类型。 - - 如果有无法避免出现类型错误的地方,使用 `// @ts-expect-error` 标记,并填写原因。 - - 没用到的变量、方法使用下划线开头。 - - 合理运用 `readonly` `protected` `private` 关键字。 - - 函数不建议使用过多可选参数,如果可选参数过多,可以考虑换用对象。 - - 尽量少地使用 `as` 关键字进行类型断言,一般情况下不建议进行任何 `as` 类型断言,除非必要。 -- 其他要求: - - 严格遵循 `eslint` 配置,不允许出现 `eslint` 报错。 - - 尽量不使用 `?.` 运算符,一般建议仅在副作用函数调用(如 `this.obj?.func()`,`this.obj.func?.()`),或对象 `Required` 化(如 `{ value: obj?.value ?? 0 }`)中使用 `?.` 运算符。 - - 只进行必要的非空判断,不必要的非空判断直接使用非空断言 `!` 实现。 - - 除非参数要求传入函数等情况,不建议在函数内写任何局部函数。 - - 语句尽量不换行,除非必要,尤其注意三元运算符与 `private readonly` 类成员。 +### 模块原则 + +- **无副作用**:所有模块只包含函数、类、常量的声明,不允许出现导出的变量声明或顶层代码执行,允许但不建议编写类的静态块。 +- **模块初始化**:如需初始化,编写一个 `createXxx` 函数,在 `index.ts` 中整合后逐级向上传递,直至顶层模块统一执行。 +- **不转发导出**:不允许一个文件导出不属于当前 monorepo 或当前文件夹的内容。 +- **无循环引用**:不允许出现循环引用。若遇到不得不循环引用的情况,应首先反思接口设计是否存在问题。 + +### 命名规则 + +| 命名对象 | 规范 | +| ---------------------------------------------- | ------------------------------------------ | +| 变量、成员、一般常量、方法、函数 | 小驼峰 | +| 类、接口、类型别名、命名空间、泛型、枚举、组件 | 大驼峰 | +| 不变常量 | 全大写,单词间下划线分隔(如 `MAX_COUNT`) | +| 专有名词缩写(如 `HTTP`、`URI`) | 全大写 | +| 需被 `implements` 的接口 | 大写 `I` 开头 | +| HTML/CSS 中的 `id`、`class` 等 | 连字符命名法 | + +不使用下划线命名法。 + +### 注释规范 + +- 常用属性成员、方法、接口、类型必须添加 `jsDoc` 注释。 +- 长文件可使用 `#region` / `#endregion` 分段以支持折叠。 +- TODO 使用 `// TODO:` 或 `// todo:` 格式。 +- 单行注释的 `//` 与注释内容之间留一个空格;不允许出现非 jsDoc 的多行注释,如需多行注释,使用多个单行注释代替。 +- 注释合理换行:考虑中文字符较宽,建议每 40–60 个字符在标点符号后换行,不允许在句中换行;参数注释换行后保持对齐。 +- 单行注释结尾不加句号;较长的多行注释结尾可加句号。 +- 一般不建议给接口、类型别名或类本身写注释(不好看),特殊情况除外。 + +### 类型规范 + +- 不允许出现非必要的 `any` 类型。 +- 所有类的成员必须显式声明类型。 +- 无法避免类型错误时,使用 `// @ts-expect-error` 标记并说明原因。 +- 未使用的变量或方法以下划线开头命名。 +- 合理使用 `readonly`、`protected`、`private` 关键字。 +- 可选参数过多时,考虑改用对象参数。 +- 尽量避免 `as` 类型断言,除非必要。 + +### 其他要求 + +- 严格遵循 `eslint` 配置,不允许出现 eslint 报错。 +- 尽量不使用 `?.` 运算符,仅推荐在以下两种场景中使用: + - 副作用函数调用,如 `this.obj?.func()` 或 `this.obj.func?.()` + - 对象 Required 化,如 `{ value: obj?.value ?? 0 }` +- 只进行必要的非空判断,非必要时直接使用非空断言 `!`。 +- 除非参数要求传入函数等情况,不建议在函数内定义局部函数。 +- 语句尽量不换行,除非必要,尤其注意三元运算符与 `private readonly` 类成员。 ## 双端分离 -样板将渲染端与数据端彻底分离,数据端可以单独在 `node` 环境运行,可以直接用于录像验证。渲染端仅负责向数据端发送消息,不负责任何逻辑运算。 +样板将渲染端与数据端彻底分离: -- `@user/data-base`: 数据端的系统层,负责核心系统。 -- `@user/data-state`: 数据端的实现层,依靠系统层实现完整的游戏实例。 -- `@user/client-base`: 渲染端的系统层,负责渲染端的核心系统。 -- `@user/client-modules`: 渲染端的实现层,依靠系统层实现客户端的渲染与用户交互。 +- **数据端**:可在 `node` 环境中单独运行,可直接用于录像验证,不负责任何渲染逻辑。 +- **渲染端**:仅负责向数据端发送消息,不负责任何逻辑运算。 -数据端允许运行渲染端代码,但需要使用全局接口 `Mota.r(() => {})` 包裹。除非必要,否则不建议在数据端调用渲染端代码。 +| 包 | 层级 | 说明 | +| ---------------------- | ------------ | ------------------------------------ | +| `@user/data-base` | 数据端系统层 | 负责数据端核心系统 | +| `@user/data-state` | 数据端实现层 | 依赖系统层实现完整的游戏实例 | +| `@user/client-base` | 渲染端系统层 | 负责渲染端核心系统 | +| `@user/client-modules` | 渲染端实现层 | 依赖系统层实现客户端的渲染与用户交互 | + +数据端允许调用渲染端代码,但必须使用全局接口 `Mota.r(() => {})` 包裹。除非必要,否则不建议在数据端调用渲染端代码。 diff --git a/prompt.md b/prompt.md index e683987..1d7a51f 100644 --- a/prompt.md +++ b/prompt.md @@ -2,32 +2,34 @@ 以下规则必须时刻遵守,任何情况下都不允许违反。 -1. 将我已经写好的代码视为绝对正确,除非我**明确允许**,否则**不允许任何修改**,哪怕因为接口变化或其他原因导致其中出现类型错误。如果你认为我的代码中存在逻辑错误,应当在对话中提出,而不是直接修改。 -2. 我做的任何代码修改都是有原因的,如果我在两次对话期间新增、删除或修改了部分代码,不要将其恢复。 -3. 时刻以目的进行驱动,想明白我为什么要这么设计接口,这个接口设计的目的是什么,而不是简单地以实现接口为目标。 -4. 如果思考或实现时有任何问题,比如我的描述比较模糊,或接口描述比较模糊,或某些地方会产生歧义等等,应该立刻向我提问,而不是按照自己的想法去写。 -5. 如果我的目标是重构某个接口,按照我说的方式进行重构。如果是彻底性的重构(接口完全没有重合),则按照正常的方式进行实现,旧代码仅做逻辑与思路上的参考;如果是结构性的重构(接口基本一致,但有一些细节上的差距),则应该将旧代码搬到新的接口上,然后进行一些微调,**不要**擅自新增任何参数、任何新的方法或接口,**不要**仅仅通过新增一个兼容层兼容旧代码来实现重构。 +1. **不擅自修改已有代码**:将我已经写好的代码视为绝对正确。除非我**明确允许**,否则**不允许任何修改**,哪怕因为接口变化或其他原因导致其中出现类型错误。若认为我的代码存在逻辑错误,应在对话中提出,而不是直接修改。 +2. **不恢复我的修改**:我做的任何代码修改都是有原因的。若我在两次对话期间新增、删除或修改了部分代码,不要将其恢复。 +3. **以目的驱动,而非以接口驱动**:实现前先想清楚我为什么要这样设计接口、这个接口设计的目的是什么,而不是单纯地以将接口填满为目标。 +4. **遇到歧义立即提问**:若思考或实现时遇到任何问题——例如描述模糊、接口不清晰、某些地方存在歧义等——应立即向我提问,而不是按自己的想法去写。 +5. **按我说的方式重构**:若目标是重构某个接口,按照我指定的方式执行: + - **彻底性重构**(新旧接口完全没有重合):按正常方式全新实现,旧代码仅作逻辑与思路上的参考。 + - **结构性重构**(新旧接口基本一致,细节有差距):将旧代码搬移到新接口上后进行微调。**不要**擅自新增任何参数、方法或接口,**不要**仅通过新增兼容层的方式应对重构。 # 建议规则 -以下规则为建议性,尽量遵守,但是一些特殊情况也可以违反,由你自己把控。 +以下规则为建议性,尽量遵守,特殊情况下可灵活处理。 -1. 我有时会在对话中给你提出实现建议,你应该对建议内容进行合理的参考,合理运用建议内容,一定注意不要滥用。 -2. 如果实现与类型标注有冲突,应当以类型标注(一般是 `types.ts`)中的内容为参考来源。 -3. 如果你认为类型标注中的接口设计有问题,或在实现中发现其缺少某些接口,应该向我提问是否添加,我同意后方可添加。 +1. **合理参考建议**:我有时会在对话中给出实现建议,应合理参考,切忌滥用。 +2. **以类型标注为参考依据**:实现与类型标注有冲突时,以类型标注(一般是 `types.ts`)中的内容为准。 +3. **发现接口问题时提问**:若认为类型标注中的接口设计有问题,或在实现中发现缺少某些接口,应向我提问是否添加,经我同意后方可添加。 -**时刻谨记上述要求,避免一个需求写好几次都写不出来,或写出我不满意的代码而挨骂** +**时刻谨记上述要求,避免一个需求反复修改仍无法满足预期。** # 开发流程 -当我提出需求时,如果没有明确说明直接实现或有其他明确要求,则遵循如下开发流程: +当我提出需求时,若没有明确说明直接实现或有其他明确要求,则遵循如下开发流程: -1. 阅读当前代码,分析需求,将需求整理为一个 markdown 文档,文档中明确标记需求细节,以及代码实现的大体思路。这一阶段中应当考虑全面,遇到任何问题应向我提问并确认。文档可以放在 `docs/dev` 目录下。 -2. 我会对文档进行全面的阅读,确保实现细节与思路没有问题后,允许你开始实现。这一步中我可能会对文档进行细微的调整,确保重新仔细阅读文档。如果实现时遇到了任何问题,应该向我提问,而不是按照自己的想法去写。 +1. 阅读当前代码,分析需求,将需求整理为一个 markdown 文档,放在 `docs/dev` 目录下。文档中需明确标注需求细节,以及代码实现的大体思路。此阶段需考虑全面,遇到任何问题应向我提问并确认,不得自行假设。 +2. 我会对文档进行全面阅读,确认实现细节与思路无误后,方允许开始实现。我可能会对文档进行细微调整,请在实现前重新仔细阅读最终版本。实现过程中如有任何问题,应向我提问,而不是自行决定。 ## 示例文档 -大致按照下述示例文档的格式编写,如果某些场景需要详细描述某个东西,可以单独开一个标题来写。 +大致按照以下格式编写,如某部分需要详细描述,可单独开设标题。我会使用引用块的形式在文档中提出建议或回答。 ```md # 需求综述 @@ -50,7 +52,7 @@ ## 需要引用的文件 -按照第三方库-其他包-当前包的其他文件的顺序写。 +按照第三方库 → 其他包 → 当前包的其他文件的顺序写。 - `xxx 库`: 引用第三方库,说明引用目的,以及需要的接口 - `@user/xxx`: 引用的目的,需要这个文件的哪些接口 @@ -77,7 +79,7 @@ # 问题 -如果我的描述中有歧义或比较模糊,可以在这把问题写出来,或者直接向我提问。 +如果描述中有歧义或比较模糊的地方,可以在此列出,或者直接向我提问。 1. xxxxxx? 2. xxxxxx?