@dsh-workbench/module-tools 0.3.11 → 1.0.0-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +223 -177
- package/bin/dsh-wb-module.mjs +89 -34
- package/package.json +18 -3
- package/src/arch.mjs +158 -129
- package/src/build.mjs +6 -30
- package/src/changelog.mjs +167 -0
- package/src/check-flags.mjs +0 -7
- package/src/check.mjs +115 -53
- package/src/checks/alias.mjs +0 -9
- package/src/checks/arch.mjs +3 -25
- package/src/checks/bundles.mjs +2 -34
- package/src/checks/cards.mjs +26 -30
- package/src/checks/contracts.mjs +54 -219
- package/src/checks/kit.mjs +2 -42
- package/src/checks/lint.mjs +9 -82
- package/src/checks/markers.mjs +48 -17
- package/src/checks/primitives.mjs +0 -39
- package/src/checks/protocol.mjs +0 -18
- package/src/checks/source.mjs +103 -51
- package/src/checks/standalone.mjs +4 -14
- package/src/checks/style.mjs +71 -241
- package/src/css-injection.mjs +0 -11
- package/src/css-shell.mjs +3 -31
- package/src/datasource-file.mjs +104 -0
- package/src/declared-tools.mjs +0 -24
- package/src/design.mjs +116 -38
- package/src/discover.mjs +2 -10
- package/src/font-scale.mjs +1 -61
- package/src/init.mjs +2 -17
- package/src/lint.mjs +24 -144
- package/src/market-index.mjs +569 -0
- package/src/market-init.mjs +80 -0
- package/src/primitives-surface.mjs +2 -28
- package/src/publish.mjs +6 -24
- package/src/release-changelog.mjs +29 -0
- package/src/release-journal.mjs +131 -0
- package/src/release-report.mjs +99 -0
- package/src/release-util.mjs +80 -0
- package/src/release.mjs +1251 -0
- package/src/seed.mjs +0 -25
- package/src/shell-type-files.mjs +3 -30
- package/src/sync.mjs +41 -24
- package/template/AGENTS.md.tpl +33 -0
- package/template/CHANGELOG.md.tpl +7 -0
- package/template/README.md.tpl +6 -33
- package/template/package.json.tpl +2 -2
- package/template/src/client.tsx.tpl +1 -18
- package/template/src/host.ts.tpl +2 -15
- package/template/types/globals.d.ts +1 -14
- package/template-diy/AGENTS.md.tpl +33 -0
- package/template-diy/CHANGELOG.md.tpl +7 -0
- package/template-diy/README.md.tpl +4 -133
- package/template-diy/package.json.tpl +2 -2
- package/template-diy/src/client.tsx.tpl +1 -29
- package/template-diy/src/host.ts.tpl +1 -15
- package/template-diy/src/plugin.test.ts.tpl +1 -1
- package/template-diy/types/globals.d.ts +1 -14
- package/template-diy/types/styles.d.ts +0 -10
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 dsh-workbench contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,246 +1,284 @@
|
|
|
1
1
|
# @dsh-workbench/module-tools
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
工作台**模块**的工具箱:建骨架、构建、过门禁、管设计底稿、发货前核清单。
|
|
4
|
+
模块作者只跟这几条命令打交道 —— 模块包里不再各带一份构建脚本。
|
|
5
5
|
|
|
6
|
-
##
|
|
6
|
+
## 六条命令走一遍
|
|
7
7
|
|
|
8
8
|
```bash
|
|
9
|
-
dsh-wb-module init
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
dsh-wb-module publish # 发货前的清单:门禁 + 三张表 + npm pack 清单(只 dry run)
|
|
9
|
+
pnpm exec dsh-wb-module init my-unit --template diy # 建骨架(minimal 是默认)
|
|
10
|
+
cd my-unit && pnpm install
|
|
11
|
+
pnpm build # dist/host.js + 套了 loader 壳的 client.js
|
|
12
|
+
pnpm check # 十三条判据
|
|
13
|
+
pnpm design check # 设计底稿有没有空格、README 有没有落后
|
|
15
14
|
```
|
|
16
15
|
|
|
17
|
-
|
|
18
|
-
`pnpm
|
|
16
|
+
要发货再加一步 `pnpm publish`:跑门禁、核 `files` 与 `npm pack` 清单,**只 dry run,不真发**。
|
|
17
|
+
维护者确认要真发时跑 `pnpm exec dsh-wb-module release`:全门禁(build / check / design / typecheck / test / selfcheck)、
|
|
18
|
+
验收必须全勾、预检(登录 / license / git 远端)、按 `--target` 发到团队源 / 公网 npm / 安装包 / git,
|
|
19
|
+
最后从出口侧核包(`--dry-run` 只预览,`--verify` 只核已发布的包)。配了可写 git 索引源时,
|
|
20
|
+
发完还会更新 `market.json`(`--index auto|none|<目录|git 地址>`,`artifacts-in-repo` 连产物一起入库,
|
|
21
|
+
`catalog-only` 只更索引);索引没更上不影响包已发布这件事,结果里会如实写。
|
|
22
|
+
空仓库先跑 `dsh-wb-module market-init <git 地址>`:生成 `market.json` 与 README 并推首个 commit
|
|
23
|
+
(用仓库声明的默认分支,读不到时用 `--ref` 指定)。在工作台里配了可写索引源之后,
|
|
24
|
+
`release` 默认出口就是 `market`:产物与索引一起进那个仓库。
|
|
25
|
+
发布过程中每一步都写进 `~/.dsh/workbench/releases/<unit>.json`;中断后带 `--resume` 接着发
|
|
26
|
+
(沿用记录里的出口、版本与索引计划,产物没变就不重跑门禁)。
|
|
27
|
+
这一版在 `CHANGELOG.md` 里没有记录、或 `package.json` 缺 `files` 里的 `CHANGELOG.md` 时,
|
|
28
|
+
`release` 直接拦下并给出补的命令;`author` 没写时从本机 `git config` 补进 manifest,索引与产物都带上。
|
|
29
|
+
|
|
30
|
+
骨架自带脚本,日常不用记长命令:`build` · `typecheck` · `test` · `selfcheck` · `check` · `lint` · `design`。
|
|
19
31
|
|
|
20
|
-
##
|
|
32
|
+
## 命令
|
|
21
33
|
|
|
22
|
-
|
|
|
34
|
+
| 命令 | 作用 |
|
|
23
35
|
|---|---|
|
|
24
|
-
| `
|
|
25
|
-
| `
|
|
26
|
-
| `
|
|
27
|
-
| `
|
|
28
|
-
| `
|
|
29
|
-
| `
|
|
30
|
-
| `
|
|
31
|
-
| `
|
|
32
|
-
| `
|
|
33
|
-
| `
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
36
|
+
| `dsh-wb-module init <unit> [--dir <目录>] [--template minimal\|diy] [--description "一句话"]` | 建模块骨架 |
|
|
37
|
+
| `dsh-wb-module market-init <git 地址\|目录> [--ref <分支>] [--path <索引路径>]` | 初始化市场索引仓库:写 `market.json` + README,推首个 commit |
|
|
38
|
+
| `dsh-wb-module build` | 出 `dist/host.js`(单文件 ESM)与 `client.js`(套 loader 壳) |
|
|
39
|
+
| `dsh-wb-module check [--only <判据>]` | 十三条判据;`--only` 单跑,也当查询口用;`--list [--json\|--markdown]` 只列判据元数据 |
|
|
40
|
+
| `dsh-wb-module lint [--fix]` | 用模块自己那份 `eslint.config.mjs`;`--fix` 写回文件 |
|
|
41
|
+
| `dsh-wb-module sync` | 按清单生成 `src/declared-tools.ts`,按 `design.json` 的 `key + recipe` 生成 `src/datasource.ts` |
|
|
42
|
+
| `dsh-wb-module design <init\|check\|render\|accept>` | 设计底稿:填、查空格、生成 README、验收打勾 |
|
|
43
|
+
| `dsh-wb-module changelog <init\|add\|check>` | 更新日志:建 `CHANGELOG.md`、记一版、查这一版有没有;`release` 要求当前版本必须有记录 |
|
|
44
|
+
| `dsh-wb-module publish` | 发货前清单:门禁 + `files` + `npm pack`(只 dry run) |
|
|
45
|
+
| `dsh-wb-module release` | 真发布:team / public / tarball / git;全门禁 + 验收闸 + 预检接管 + 发后核包 + 更新市场索引(`--dry-run` / `--verify`) |
|
|
46
|
+
|
|
47
|
+
`--cwd <目录>` 指定包目录,默认当前目录。
|
|
48
|
+
|
|
49
|
+
## 十三条判据
|
|
50
|
+
|
|
51
|
+
`dsh-wb-module check` 一次跑完,三态输出:`✔ 通过` / `○ 不适用` / `✖ 不过`;有任何不过就退出 1。
|
|
52
|
+
|
|
53
|
+
表由 `dsh-wb-module check --list --markdown` 生成;`层` 是 manifest / source / build / render / meta。
|
|
54
|
+
|
|
55
|
+
<!-- dsh-wb-module:begin checks -->
|
|
56
|
+
| 判据 | 层 | 摘要 |
|
|
57
|
+
|---|---|---|
|
|
58
|
+
| `contracts` | manifest | 单元 id、两个面、四个脚本、files、peer 下限,以及清单与代码注册对账 |
|
|
59
|
+
| `protocol` | manifest | dsh.workbench 的 apiVersion、静态半形状与未知键 |
|
|
60
|
+
| `bundles` | build | 宿主产物不含浏览器侧、浏览器产物不内联外壳 CSS 与夹具 |
|
|
61
|
+
| `source` | source | 零注释、包与运行时入口分开、不手写进程/锁/原子写 |
|
|
62
|
+
| `arch` | source | 模块自己的层序、运行时边界与设计系统配置 |
|
|
63
|
+
| `lint` | source | 模块自己的 eslint.config.mjs 与规则集 |
|
|
64
|
+
| `primitives` | build | dsh 包只从 kit 取,模块自己声明 external |
|
|
65
|
+
| `kit` | build | 模块产物对得上所装外壳的 kit 面与 peer 下限 |
|
|
66
|
+
| `cards` | render | 八态 × zh/en 渲染:不抛、不空白、不冒裸键名 |
|
|
67
|
+
| `standalone` | build | 外壳源码不在场时仍能 build + typecheck |
|
|
68
|
+
| `markers` | meta | 公开包不含私有源、私网 IP、内部域名后缀 |
|
|
69
|
+
| `style` | source | 不定义 wb-* 规则;裸标签控件改走 kit;令牌名对现读的清单校验 |
|
|
70
|
+
| `alias` | source | 模块源码里没有指向外壳 @/ 的引用 |
|
|
71
|
+
<!-- dsh-wb-module:end checks -->
|
|
72
|
+
|
|
73
|
+
`style` 还查**令牌名**:模块源码里每个 `var(--dsw-* | --wb-*)` 都要在现读的清单里 ——
|
|
74
|
+
`--dsw-*` 从装着的 dsh 主题读、`--wb-*` 从外壳出货的 `client.js` 读;读不到的那一份不判红,只记一条
|
|
75
|
+
「这次没验」。拼错的名字按最近的几个候选点名(`workbench_diy_components` 的 `scope: 'tokens'` 是同一个来源)。
|
|
76
|
+
|
|
77
|
+
`style` 还有两族更窄的判据:**手写的「标签 + 控件」行与手写的操作区**判红(归 kit 的
|
|
78
|
+
`FormRow(s)` 与 `FormActions`),**裸 `<form>` 与手写整张表单**也判红(归 `Form`)。
|
|
79
|
+
现存违规收在各自的豁免表里,是棘轮:迁一处删一行,表里已经不再命中的行也会当场报红。
|
|
80
|
+
判据**故意窄** —— 模块自己用 div 拼一张不借槽类的表单不判,判宽了就是每次改界面都先撞门禁。
|
|
81
|
+
|
|
82
|
+
### `arch`:模块包的分层方言
|
|
83
|
+
|
|
84
|
+
卡片一复杂就往 `src/client.tsx` 里堆,宿主侧顺手 import 一个组件(`dist/host.js` 在 Node 里
|
|
85
|
+
根本起不来)—— 边界判据拦不住这些,`arch` 补这一层。模板里的配置只有一行:
|
|
47
86
|
|
|
48
87
|
```js
|
|
49
88
|
import { moduleArch } from '@dsh-workbench/module-tools/arch';
|
|
50
89
|
export default moduleArch();
|
|
51
90
|
```
|
|
52
91
|
|
|
53
|
-
|
|
54
|
-
`@dsh-workbench/module-tools/arch` 的 `moduleArch()` 里,随工具链与外壳同代更新 —— 不要求每个模块
|
|
55
|
-
抄一份配置,也不认 arch-guard 的「应用 / 库 / FSD」范式(模块是一个插件包 + 两个面的小库,
|
|
56
|
-
`src/messages.ts` 这种根文件在库范式下会报 S01「无处安放」)。
|
|
57
|
-
|
|
58
|
-
层序(数字越小越底层,S21:只许依赖层号 ≤ 自己的文件):
|
|
92
|
+
方言只有一份,随工具链与外壳同代,不要求每个模块抄一份。层序:
|
|
59
93
|
|
|
60
94
|
| 落点 | 层 | 是什么 |
|
|
61
95
|
|---|---|---|
|
|
62
|
-
| `src/model/**` | 1 | 纯逻辑:类型 / 换算 /
|
|
96
|
+
| `src/model/**` | 1 | 纯逻辑:类型 / 换算 / 状态机。不碰 react / kit / dsh / `node:*` |
|
|
63
97
|
| `src/messages.ts` | 1 | 文案(zh / en) |
|
|
64
|
-
| `src/components/**` | 2 |
|
|
65
|
-
| `src/host.ts` | 10 |
|
|
66
|
-
|
|
|
67
|
-
| `**/*.test.ts(x)` | 99 | 测试(S21 跳过) |
|
|
68
|
-
|
|
69
|
-
目录**按需要出现**(空壳模块可以只有 `components/`),但多一个目录就是多一处 S01 —— 要加层就在
|
|
70
|
-
自己那份配置里明写。两个运行时靠 S23 隔离:**`src/components/**` 整片算 client 面**,所以宿主侧
|
|
71
|
-
import 一个组件是硬红(层号拦不住它:components 第 2 层、host 第 10 层,S21 认为合法)。
|
|
98
|
+
| `src/components/**` | 2 | 浏览器侧视图件;样式与它同目录(`X.module.css`) |
|
|
99
|
+
| `src/host.ts` · `src/client.tsx` | 10 | 两个面 |
|
|
100
|
+
| `**/*.test.ts(x)` | 99 | 测试 |
|
|
72
101
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
(不声明令牌表,`--dsw-*` 由 dsh 运行时给)。
|
|
102
|
+
两个运行时靠另一条规则隔离:`src/components/**` 整片算浏览器面,宿主侧 import 一个组件是硬红
|
|
103
|
+
(层号拦不住它:components 第 2 层、host 第 10 层)。内置阈值比 arch-guard 默认更严:
|
|
104
|
+
文件与视图各 200 行、函数 80 行;`src/model/**` 有文件就必须有测试。
|
|
77
105
|
|
|
78
106
|
要开口子**从参数进**(别改判据;每条口子都有名字,评审看得见):
|
|
79
107
|
|
|
80
108
|
```js
|
|
81
|
-
import { moduleArch } from '@dsh-workbench/module-tools/arch';
|
|
82
109
|
export default moduleArch({
|
|
83
|
-
fileLines: 300,
|
|
84
|
-
deps: ['some-sdk'],
|
|
85
|
-
layers: { adapters: 2 },
|
|
86
|
-
roles: [{ id: 'lib:version', pattern: 'src/version.ts', layer: 1 }],
|
|
87
|
-
// structure: { … }, // 整体替换结构声明(面在别处 / 只有一个运行时)
|
|
110
|
+
fileLines: 300,
|
|
111
|
+
deps: ['some-sdk'],
|
|
112
|
+
layers: { adapters: 2 },
|
|
113
|
+
roles: [{ id: 'lib:version', pattern: 'src/version.ts', layer: 1 }],
|
|
88
114
|
exceptions: [{ rule: 'S16', glob: 'src/components/Legacy.tsx', reason: '待拆,见 #123' }],
|
|
89
115
|
});
|
|
90
116
|
```
|
|
91
117
|
|
|
92
|
-
`exceptions`
|
|
93
|
-
|
|
118
|
+
`exceptions` 每条必须写 `reason`。怎么读红、常见红、还有哪些口子,见
|
|
119
|
+
[REVIEW.md](../core/skills/workbench-module-diy/REVIEW.md)。
|
|
94
120
|
|
|
95
121
|
### `lint`:模块包的语法卫生方言
|
|
96
122
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
react-hooks 那几条(渲染期写 ref / 漏依赖 / 条件调用)。两半合起来"手搓外观、绕过契约"才闭合 ——
|
|
100
|
-
`*.module.css` 那一半归 arch-guard 的 D 族,TS 里的这一半归这里。
|
|
101
|
-
|
|
102
|
-
模板里那份 `eslint.config.mjs` **只有两行**:
|
|
123
|
+
`arch` 管结构,`lint` 管单文件:`as any` 绕过契约、残留的 `console.log` / `debugger`、
|
|
124
|
+
TS 内联样式里的颜色字面量与 `!important`、react-hooks 那几条。模板里的配置只有两行:
|
|
103
125
|
|
|
104
126
|
```js
|
|
105
127
|
import { moduleLint } from '@dsh-workbench/module-tools/lint';
|
|
106
128
|
export default moduleLint({ rootDir: import.meta.dirname });
|
|
107
129
|
```
|
|
108
130
|
|
|
109
|
-
`moduleLint(
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
依赖(`eslint` / `@eslint/js` / `typescript-eslint` / 两个 react 插件)装在 **module-tools 一家**,
|
|
113
|
-
模块与工坊根的清单**一个字都不用改**。
|
|
114
|
-
|
|
115
|
-
`rootDir` 把 typescript-eslint 的 `tsconfigRootDir` 钉在这份配置所在的目录。**不传也能跑**(一个
|
|
116
|
-
进程里只有一份配置时 parser 自己猜得对);但本仓根上还有一份 `eslint.config.mjs`,ESLint 10 的扁平
|
|
117
|
-
配置按目录就近选,一个进程里同时出现多份候选时它猜不出来(每个文件一条
|
|
118
|
-
`No tsconfigRootDir was set, and multiple candidate TSConfigRootDirs are present`),所以模板与骨架
|
|
119
|
-
都带着它。
|
|
131
|
+
`moduleLint()` 返回一份**标准 flat config**:它同时是判据跑的那份与编辑器 / `eslint` 认的那份,
|
|
132
|
+
没有第二份真相。依赖(`eslint`、`typescript-eslint`、两个 react 插件)装在 module-tools 一家,
|
|
133
|
+
模块与工坊根的清单一个字都不用改。
|
|
120
134
|
|
|
121
|
-
|
|
135
|
+
要开口子从命令参数进:`moduleLint({ rootDir, ignores, rules })`,或者展开它再追自己的一块:
|
|
122
136
|
|
|
123
137
|
```js
|
|
124
|
-
import { moduleLint } from '@dsh-workbench/module-tools/lint';
|
|
125
138
|
export default [
|
|
126
139
|
...moduleLint({ rootDir: import.meta.dirname, rules: { 'no-console': 'off' } }),
|
|
127
140
|
{ files: ['src/legacy/**'], rules: { '@typescript-eslint/no-explicit-any': 'off' } },
|
|
128
141
|
];
|
|
129
142
|
```
|
|
130
143
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
`
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
`
|
|
145
|
-
`
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
144
|
+
指纹是逐块比整份方言:方言那 8 块在模块配置里必须一块不少、逐项一致;少一块、同名两次、
|
|
145
|
+
删一条规则或改一个值都红。只写一个同名块、`export default []`、语法错的配置都会红。
|
|
146
|
+
`scripts/**` 与 `*.config.*` 不判 `no-console`(脚本打印是本职工作)。
|
|
147
|
+
|
|
148
|
+
`dsh-wb-module lint --fix` 先写回再跑一遍看还剩什么。**`check` 永远不动手改文件** ——
|
|
149
|
+
门禁是判据,不是修复器。
|
|
150
|
+
|
|
151
|
+
## `design.json`:三张表
|
|
152
|
+
|
|
153
|
+
面板 / 能力 / 联动三张表、状态矩阵、数据源记录、写通道记录、验收清单都住在模块根目录的
|
|
154
|
+
`design.json`,README 里那一段由它生成(夹在 `<!-- dsh-wb:design:start/end -->` 之间)。
|
|
155
|
+
|
|
156
|
+
它是**设计底稿**:三张表与探明记录装人话(`oneLine` / `shape` / `approvalText` / 三张表的文字);
|
|
157
|
+
读源那一条可以多带 `key` 与 `recipe` 两个机器字段 —— 它们只用于生成 `src/datasource.ts`,
|
|
158
|
+
不改变"人话那一份"(`read` / `params` / `credentials` / `notLoggedIn` / `timeout`)照写。「这条能力在哪些工作区出现」不在底稿、也不在清单 —— 那是用户的面板板说了算。
|
|
159
|
+
`contracts` 会把底稿的能力名与清单的工具名做键集合对账(不重不漏,只对名字);没有
|
|
160
|
+
`design.json` 的模块不查。
|
|
161
|
+
|
|
162
|
+
有写能力的模块另在 `writes[]` 里落命令 / 入参 / 出参 / 预览档 / 回读 / 失败形状,
|
|
163
|
+
渲染成 README 里的「写通道探明记录」。没有写通道就不用写,不会因此变红。
|
|
164
|
+
写也能落配方:`writes[]` 里加一对 `key` + `recipe`,`sync` 把它和读一起生成进
|
|
165
|
+
`DATASOURCE`(`data.submit(DATASOURCE.<key>)`);配方盖不住的写写 `custom: { why }`(warn)。
|
|
166
|
+
|
|
167
|
+
### 读源配方
|
|
168
|
+
|
|
169
|
+
一条读可以带 `key`(lowerCamelCase)和 `recipe`:
|
|
170
|
+
|
|
171
|
+
```json
|
|
172
|
+
"datasource": {
|
|
173
|
+
"key": "balance",
|
|
174
|
+
"recipe": {
|
|
175
|
+
"origin": "https://x.example.com",
|
|
176
|
+
"request": { "method": "GET", "path": "/api/balance" },
|
|
177
|
+
"response": { "kind": "json", "path": "data" }
|
|
178
|
+
},
|
|
179
|
+
"read": "GET /api/balance(登录态 cookie)",
|
|
180
|
+
"params": "无",
|
|
181
|
+
"credentials": "同源 cookie",
|
|
182
|
+
"notLoggedIn": "401 → auth",
|
|
183
|
+
"timeout": "25 秒"
|
|
184
|
+
}
|
|
185
|
+
```
|
|
157
186
|
|
|
158
|
-
`
|
|
159
|
-
`
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
187
|
+
`sync` 会把它们生成成 `src/datasource.ts`(`export const DATASOURCE = { balance: … } as const`),
|
|
188
|
+
模块代码只写 `data.request(DATASOURCE.balance)`。一条读要一次打多条同源接口时,再加一格 `calls`:
|
|
189
|
+
|
|
190
|
+
```json
|
|
191
|
+
"datasource": {
|
|
192
|
+
"key": "wallet",
|
|
193
|
+
"recipe": { "origin": "https://x.example.com", "request": { "method": "GET", "path": "/api/subscription/self" } },
|
|
194
|
+
"calls": {
|
|
195
|
+
"balance": { "path": "/api/wallet/balance", "response": { "path": "data.balance" } },
|
|
196
|
+
"usage": { "query": { "days": 7 } }
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
```
|
|
164
200
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
201
|
+
生成的 `src/datasource.ts` 多一个 `export const DATASOURCE_CALLS = { wallet: { … } } as const`,
|
|
202
|
+
代码写 `data.requestMany(DATASOURCE.wallet, DATASOURCE_CALLS.wallet)`:一次换登录态、并发打,
|
|
203
|
+
每格各自成败。`calls` 的键就是结果名;`path` / `query` / `headers` / `response` 覆盖配方同名默认值。
|
|
204
|
+
几条规矩:
|
|
205
|
+
|
|
206
|
+
- `key` 与 `recipe` **要么都写、要么都不写**;只写一个不生成、也不校验(旧条目一字不动)。
|
|
207
|
+
- 写走同一份生成物:`writes[]` 的 `key + recipe` 也进 `DATASOURCE`;读写的 `key` 不许重复,
|
|
208
|
+
`design check` 点名。
|
|
209
|
+
- `recipe` 与 `custom` 二选一。`custom: { why }` 表示"配方盖不住,仍用旧的 `browser` 脚本",
|
|
210
|
+
`design check` 记 warn,脚本留在代码里。
|
|
211
|
+
- `design check` 校验配方与批量:枚举、`origin` / `path`、`request.headers` 里不许写凭据头、
|
|
212
|
+
`maxBytes`、`GET` 不带 body;`calls` 非空、≤ 8 条、逐格同一套校验,且必须配 `recipe`。
|
|
213
|
+
生成物过期会被 `contracts` 点名,跑 `sync` 刷新。
|
|
214
|
+
- 形状与六种配方见模块 DIY 的 [`DATASOURCE.md`](../core/skills/workbench-module-diy/DATASOURCE.md)。
|
|
168
215
|
|
|
169
216
|
```bash
|
|
170
217
|
dsh-wb-module design check # 空着的格一格格点名叫出来 + README 有没有落后
|
|
171
218
|
dsh-wb-module design render # 用 design.json 重新生成 README 那一段
|
|
172
|
-
dsh-wb-module design accept #
|
|
219
|
+
dsh-wb-module design accept # 在真卡片上逐条走通后打勾
|
|
173
220
|
```
|
|
174
221
|
|
|
175
|
-
## 协议:`
|
|
176
|
-
|
|
177
|
-
一个模块包在 `package.json` 里声明五件事,缺哪一段在哪一层炸都不一样:
|
|
222
|
+
## 协议:`package.json` 里的五段
|
|
178
223
|
|
|
179
224
|
| 段 | 是什么 | 谁读 |
|
|
180
225
|
|---|---|---|
|
|
181
|
-
| `dsh.bundle.patch` | 把自己插进 profile 组合图的 cordis 补丁 | dsh
|
|
182
|
-
| `dsh.client{platform,inject,external}` | 浏览器侧接线 | dsh
|
|
183
|
-
| `dsh.workbench` |
|
|
184
|
-
| `dsh.workbenchFaces{host,client}` |
|
|
185
|
-
| `peerDependencies['@dsh-workbench/core']` | 服务面:外壳 provide 的服务在不在 | dsh
|
|
186
|
-
|
|
187
|
-
### `dsh.workbench`(协议 v1)
|
|
226
|
+
| `dsh.bundle.patch` | 把自己插进 profile 组合图的 cordis 补丁 | dsh 的组合器(装载) |
|
|
227
|
+
| `dsh.client{platform,inject,external}` | 浏览器侧接线 | dsh 的客户端组合器(dsh 不校验,错的声明要等浏览器 `require` 才炸 —— 所以 `contracts` 替你查) |
|
|
228
|
+
| `dsh.workbench` | **模块声明协议** | 门禁 · 市场 · 外壳 |
|
|
229
|
+
| `dsh.workbenchFaces{host,client}` | **工具链元数据**(不在协议里):两个面的源码入口 | 门禁:`source` 拿它算可达性、`contracts` 查它在位;运行期不读 |
|
|
230
|
+
| `peerDependencies['@dsh-workbench/core']` | 服务面:外壳 provide 的服务在不在 | dsh 的兼容性预检 |
|
|
188
231
|
|
|
189
232
|
```jsonc
|
|
190
233
|
"workbench": {
|
|
191
|
-
"apiVersion":
|
|
192
|
-
"unit": "hello",
|
|
193
|
-
"card": { "titleKey": "hello.cardTitle", "order": 90 },
|
|
194
|
-
"tools": [{ "name": "hello_read", "writes": false }],
|
|
195
|
-
"host": { "inject": [] },
|
|
196
|
-
"
|
|
234
|
+
"apiVersion": 2,
|
|
235
|
+
"unit": "hello",
|
|
236
|
+
"card": { "titleKey": "hello.cardTitle", "order": 90 },
|
|
237
|
+
"tools": [{ "name": "hello_read", "writes": false }],
|
|
238
|
+
"host": { "inject": [] },
|
|
239
|
+
"needs": [
|
|
240
|
+
{
|
|
241
|
+
"kind": "extension",
|
|
242
|
+
"runtime": "opencli",
|
|
243
|
+
"name": "acme-workflow",
|
|
244
|
+
"commands": ["acme-read"],
|
|
245
|
+
"purpose": "读写工单",
|
|
246
|
+
"sources": [{ "kind": "npm", "package": "opencli-plugin-acme", "registryEnv": "ACME_REGISTRY" }]
|
|
247
|
+
}
|
|
248
|
+
]
|
|
197
249
|
},
|
|
198
|
-
"workbenchFaces": { "host": "src/host.ts", "client": "src/client.tsx" }
|
|
250
|
+
"workbenchFaces": { "host": "src/host.ts", "client": "src/client.tsx" }
|
|
199
251
|
```
|
|
200
252
|
|
|
201
253
|
五条口径:
|
|
202
254
|
|
|
203
|
-
1. **声明了 `apiVersion` =
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
3. **清单声明的工具 =
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
`
|
|
213
|
-
的模块不查 —— 底稿是可选的设计材料。
|
|
214
|
-
5. **`dsh.workbenchFaces` 不是协议的一部分,但门禁必填**(A-2 起):它是 `dsh.workbench` 的
|
|
215
|
-
**兄弟键**,写两半的**源码**入口;`source` 判据靠它算"这段源码属于哪一半"。
|
|
216
|
-
写进 `dsh.workbench` 里会被判**生字段**(不留兼容),它也不能指向出货产物
|
|
217
|
-
—— 从 `dist/host.js` 反推不出是哪份源码把 dsh 的包拖进来的。
|
|
255
|
+
1. **声明了 `apiVersion` = 静态半写全了**:`unit` / `card` / `tools` / `host` 四个键都要有
|
|
256
|
+
(没有卡片写 `null`),`needs` 没有外部需要时省略。
|
|
257
|
+
`needs` 四种 `kind`:`bin` / `extension` / `lib` / `env`;每条必写 `purpose`;
|
|
258
|
+
未知 `kind` 或 `sources[].kind` 判红,need 里未知字段只算警告(`x-` 前缀忽略)。
|
|
259
|
+
2. **不许有协议不认识的键**:生字段会被外壳静默忽略(拼错一个键就等于那句话没写),
|
|
260
|
+
`protocol` 先拦。`scope` 不在协议里 —— 能力出现在哪些工作区只由用户的板决定。
|
|
261
|
+
3. **清单声明的工具 = 代码真正注册的工具**:清单是工具面的唯一来源,两边漂了就红。
|
|
262
|
+
4. **底稿 ↔ 清单只对账键集合**,只对名字,内容各写一处。
|
|
263
|
+
5. **`dsh.workbenchFaces` 不是协议的一部分,但门禁必填**:它指向两半的**源码**,不能指向出货
|
|
264
|
+
产物 —— 从 `dist/host.js` 反推不出是哪份源码把 dsh 的包拖进来的。
|
|
218
265
|
|
|
219
266
|
## 两个模板
|
|
220
267
|
|
|
221
268
|
| 模板 | 是什么 |
|
|
222
269
|
|---|---|
|
|
223
|
-
| `minimal`(默认) |
|
|
224
|
-
| `diy` |
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
`
|
|
229
|
-
`
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
`
|
|
234
|
-
|
|
235
|
-
再用 `design.json` 生成 README 那一段。
|
|
236
|
-
|
|
237
|
-
- `__UNIT__` / `__NAME__` / `__DESC__`:单元 id / 包名 / 一句描述。
|
|
238
|
-
- `__SHELL_RANGE__`:模板里那三处 `@dsh-workbench/*` 声明的**范围**(peer 与 devDependency 用同一个值,
|
|
239
|
-
否则 `check:kit` 的"peer 下限不许比 devDependency 旧"当场红)。取 **init 所在工程里实装的那份**
|
|
240
|
-
`@dsh-workbench/core` 的版本(`node_modules/@dsh-workbench/core/package.json`);读不到就退回
|
|
241
|
-
`module-tools` 自己的版本(同一次发布同版本);两者都读不到才保持 `^0.1.0`,并在 stderr 上说一句。
|
|
242
|
-
- 按 token 名盖章而不是按 `__[A-Z]+__` 的形状:名字里带下划线的 `__SHELL_RANGE__` 也要认,
|
|
243
|
-
DIY 模板 README 里真写着的 `workbench___UNIT___rebuild` 则仍只认内层那一个。
|
|
270
|
+
| `minimal`(默认) | 空壳:卡壳、文案、测试都接好了,没有数据源 |
|
|
271
|
+
| `diy` | 一份真能跑的样板:宿主侧一条只读路由真读盘、卡片五态、三张表也是填好的 |
|
|
272
|
+
|
|
273
|
+
两个模板都是**分层多文件**骨架:纯逻辑住 `src/model/**`、视图件与同目录样式住
|
|
274
|
+
`src/components/**`、文案住 `src/messages.ts`,两个面只剩注册与交 props。目录按需要出现 ——
|
|
275
|
+
`minimal` 没有纯逻辑就不硬造 `model/`。各自带一份一行工厂的 `arch.config.mjs` 与两行的
|
|
276
|
+
`eslint.config.mjs`。
|
|
277
|
+
|
|
278
|
+
`init` 连模板里的 `*.tpl` 一起拷、按已知 token 名逐个盖章(`__UNIT__` / `__NAME__` / `__DESC__` /
|
|
279
|
+
`__SHELL_RANGE__`)、剥掉后缀,再用 `design.json` 生成 README 那一段。
|
|
280
|
+
`__SHELL_RANGE__` 取 init 所在工程实装的那份外壳版本;读不到就退回 module-tools 自己的版本;
|
|
281
|
+
两者都读不到才保持 `^0.1.0` 并在 stderr 上说一句。
|
|
244
282
|
|
|
245
283
|
## 自己的自检
|
|
246
284
|
|
|
@@ -248,11 +286,19 @@ dsh-wb-module design accept # 自己在真卡片上逐条走通后,把验收
|
|
|
248
286
|
node scripts/selfcheck/index.mjs
|
|
249
287
|
```
|
|
250
288
|
|
|
251
|
-
工具箱的回归网:两个模板各 `init`
|
|
252
|
-
|
|
289
|
+
工具箱的回归网:两个模板各 `init` 一遍、各跑全套;判据的正例与负例都要对;打包清单不含
|
|
290
|
+
`.tpl` 残留;`design` 的空格、README 同步、`accept`;`publish` 的清单;`release` 的验收闸、
|
|
291
|
+
dry-run、真发后的元数据与发布失败回滚。
|
|
253
292
|
|
|
254
|
-
##
|
|
293
|
+
## 相关文档
|
|
255
294
|
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
295
|
+
| 想了解 | 读 |
|
|
296
|
+
|---|---|
|
|
297
|
+
| 模块怎么写、DIY 整套流程 | [workbench-module-diy](../core/skills/workbench-module-diy/SKILL.md) |
|
|
298
|
+
| 构建骨架、接线(bundle / client / 注册) | [BUILD.md](../core/skills/workbench-module-diy/BUILD.md) · [WIRING.md](../core/skills/workbench-module-diy/WIRING.md) |
|
|
299
|
+
| kit 组件与 hooks | [KIT.md](../core/skills/workbench-module-diy/KIT.md) |
|
|
300
|
+
| 面板、状态矩阵、界面规则 | [PANEL.md](../core/skills/workbench-module-diy/PANEL.md) |
|
|
301
|
+
| 工具、审批、长任务 | [TOOLS.md](../core/skills/workbench-module-diy/TOOLS.md) |
|
|
302
|
+
| 判据红了怎么读 | [REVIEW.md](../core/skills/workbench-module-diy/REVIEW.md) |
|
|
303
|
+
| 最小可安装样本 | [module-hello](../module-hello/README.md) |
|
|
304
|
+
| 契约类型与共享组件 | [shared](../shared/README.md) |
|