@dsh-workbench/module-tools 0.2.15 → 0.3.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/README.md +6 -9
- package/package.json +2 -2
- package/src/build.mjs +15 -2
- package/src/checks/bundles.mjs +3 -3
- package/src/checks/source.mjs +1 -1
- package/src/checks/style.mjs +9 -7
- package/src/font-scale.mjs +6 -3
- package/src/seed.mjs +16 -2
- package/src/sync.mjs +1 -1
package/README.md
CHANGED
|
@@ -22,14 +22,14 @@ dsh-wb-module publish # 发货前的清单:门禁 + 三张表 +
|
|
|
22
22
|
|---|---|
|
|
23
23
|
| `contracts` | 模块契约:单元 id 唯一、两个面、四个脚本、`files`、peer 下限;以及**清单 ↔ 代码**(真正注册的工具)与**底稿 ↔ 清单**(`design.json` 的能力名)两处对账(见下) |
|
|
24
24
|
| `protocol` | 模块声明协议:`dsh.workbench` 的 `apiVersion` 要在、静态半形状要对、不许有协议不认识的键(`scope` 现在**就是**不认识的键之一 —— 见下) |
|
|
25
|
-
| `bundles` | 产物边界:宿主面里不许出现浏览器侧的东西;浏览器产物里不许出现**内联的外壳 CSS**(模块从 `@dsh-workbench/shared`
|
|
25
|
+
| `bundles` | 产物边界:宿主面里不许出现浏览器侧的东西;浏览器产物里不许出现**内联的外壳 CSS**(模块从 `@dsh-workbench/shared` 根入口取运行期值就会这样);**产物里出现夹具标记(`wbMock` / `MockStrip` / `useFixturePick` / `-mock'` / `SCENES`…)判红** —— 模块不带夹具:夹具只服务工作台开发者的调试,模块自己的 vitest 把几态各喂一遍就够 |
|
|
26
26
|
| `source` | 源码边界:不直接引 dsh 的包、不引不是种子词的 `@deepseek-ai/*` |
|
|
27
27
|
| `primitives` | dsh 原语只从模块表(`kit`)拿 |
|
|
28
28
|
| `kit` | 这份外壳的 kit 成员到底有哪些(缺什么会当场点名) |
|
|
29
29
|
| `cards` | 卡片渲染:**八态 × zh/en 各渲一遍**(外壳侧输入 idle / loading / loading-刷新保留旧值(含旧时间戳 `at`)/ ready / ready-空容器 / unreadable-error / unreadable-能力闸缺插件 / unreadable-能力闸缺命令;`useApiResource` 与 `useCapabilityGate` 由判据喂,`useUnitBoard` 用模块仓里装的那份**真壳**)—— 不许抛、不许空白、文字里不许出现 `undefined` / `NaN` / `[object Object]` / `null`、不许裸键名、要套外壳的卡壳、清单 `titleKey` 在两语字典里都有落点。**不断言"画的是哪一态那一屏"**(那归模块自己的逐态测试与 `design.json` 状态矩阵);模块没装外壳时逐态那半跳过、单次全桩渲染照旧跑 |
|
|
30
30
|
| `standalone` | 独立构建:产物不依赖工坊里跑 |
|
|
31
31
|
| `markers` | 公开包无内部标识(内网域名、企业内部名) |
|
|
32
|
-
| `style` | 样式归属:模块不许定义外壳类(`wb-*`)的规则(判据看**复合选择器**,`:global(.wb-x)` 也算,只有同一个复合选择器里带模块自己的类才放行);**裸 `<a>` 与裸交互控件(`<button>` / `<input>` / `<select>`)都判红** —— 前者用 kit 的 `Link`,后者用 `Button` / `TextField` / `SelectField` / `ChoiceGroup`;只有 `<textarea>` 与 `input[type=hidden\|file]` 例外(两处都没有那种形态);**手写的「标签 + 控件」行(标签元素紧邻 `SelectField` / `TextField` / `ChoiceGroup` / `Input`)与模块自己给 `field`/`form` 容器设的行距(`gap` / 上下 `margin`)也判红** —— 这一层归 kit 的 `FormRow(s)
|
|
32
|
+
| `style` | 样式归属:模块不许定义外壳类(`wb-*`)的规则(判据看**复合选择器**,`:global(.wb-x)` 也算,只有同一个复合选择器里带模块自己的类才放行);**裸 `<a>` 与裸交互控件(`<button>` / `<input>` / `<select>`)都判红** —— 前者用 kit 的 `Link`,后者用 `Button` / `TextField` / `SelectField` / `ChoiceGroup`;只有 `<textarea>` 与 `input[type=hidden\|file]` 例外(两处都没有那种形态);**手写的「标签 + 控件」行(标签元素紧邻 `SelectField` / `TextField` / `ChoiceGroup` / `Input`)与模块自己给 `field`/`form` 容器设的行距(`gap` / 上下 `margin`)也判红** —— 这一层归 kit 的 `FormRow(s)`(面 → 行 → 控件);现存违规收进 `FORM_ROW_EXEMPT`(棘轮:迁一处删一行,且表里已经不再命中的行会当场报红);**手写的操作区(借了 `wb-formActions` 槽类手摆按钮)也判红** —— 操作区归 kit 的 `FormActions`(顺序 / 一个区一颗 `primary` / `danger` 的位置 / 间距 / 窄容器换行都由它定,调用点只给数据与语义),现存违规收进 `FORM_ACTIONS_EXEMPT`(同一套棘轮:迁一处删一行,表里不再命中的行当场报红) |
|
|
33
33
|
| `alias` | 模块不指外壳:源码里没有指向外壳 `@/` 的引用 |
|
|
34
34
|
|
|
35
35
|
三态输出:`✔ 通过` / `○ 不适用` / `✖ 不过`;有任何不过就退出 1。
|
|
@@ -42,8 +42,7 @@ README 里那一段由它生成(夹在 `<!-- dsh-wb:design:start/end -->` 之
|
|
|
42
42
|
`design.json` 是**设计底稿**:只装人话(`oneLine` / `shape` / `approvalText` / 面板那几张表的文字 /
|
|
43
43
|
`datasource`),**不装机器字段**。**"这条能力在哪些工作区出现"不在底稿、也不在清单**:
|
|
44
44
|
那是**用户**的事(他的面板板),模块作者声明不了(用户口径「模块开发者不能定义 scope,
|
|
45
|
-
scope
|
|
46
|
-
§2.4 / §6.16)。
|
|
45
|
+
scope 永远是用户来决定的」)。
|
|
47
46
|
`contracts` 会把底稿的能力名与清单的工具名做**键集合对账**(不重不漏,只对名字):
|
|
48
47
|
底稿多一条、清单多一把都点名判红;没有 `design.json` 的模块不查。
|
|
49
48
|
|
|
@@ -86,17 +85,16 @@ dsh-wb-module design accept # 用户逐条点头之后,把验收清单打勾
|
|
|
86
85
|
2. **不许有协议不认识的键**:生字段会被外壳**静默忽略**(拼错一个键就等于那句话没写),
|
|
87
86
|
所以 `protocol` 判据先拦。**`scope` 不在协议里**:它曾经是(`card.scope` / `tools[].scope`),
|
|
88
87
|
现已删除 —— 一个能力在哪些工作区出现**只由用户的板决定**,作者写它 = 写了一个生字段、当场红
|
|
89
|
-
(`protocol` 会点名 `"scope"
|
|
88
|
+
(`protocol` 会点名 `"scope"`)。
|
|
90
89
|
3. **清单声明的工具 = 代码真正注册的工具**(U1b 起):清单是工具面的**唯一**来源,
|
|
91
90
|
`contracts` 拿 `mount()` 里 `registerTools()` 真正注册的名字对账,两边漂了就红。
|
|
92
91
|
4. **底稿 ↔ 清单只对账键集合**(U1c 起):`design.json` 的 `capabilities[].name` 与清单的
|
|
93
92
|
`tools[].name` 不重不漏,**只对名字**(内容各写一处,没有可比的东西);没有 `design.json`
|
|
94
|
-
的模块不查 ——
|
|
93
|
+
的模块不查 —— 底稿是可选的设计材料。
|
|
95
94
|
5. **`dsh.workbenchFaces` 不是协议的一部分,但门禁必填**(A-2 起):它是 `dsh.workbench` 的
|
|
96
95
|
**兄弟键**,写两半的**源码**入口;`source` 判据靠它算"这段源码属于哪一半"。
|
|
97
96
|
写进 `dsh.workbench` 里会被判**生字段**(不留兼容),它也不能指向出货产物
|
|
98
|
-
—— 从 `dist/host.js` 反推不出是哪份源码把 dsh
|
|
99
|
-
[ADR-0038](../../docs/adr/0038-module-declaration-protocol.md) §7 第 7 条。
|
|
97
|
+
—— 从 `dist/host.js` 反推不出是哪份源码把 dsh 的包拖进来的。
|
|
100
98
|
|
|
101
99
|
## 两个模板
|
|
102
100
|
|
|
@@ -122,4 +120,3 @@ node scripts/selfcheck.mjs
|
|
|
122
120
|
模块怎么写、DIY 那套流程怎么走,见外壳包里的指引:`@dsh-workbench/core` 的
|
|
123
121
|
`skills/workbench-module-diy/**`(`SKILL.md` / `BUILD.md` / `WIRING.md` / `DESIGN.md` / `PANEL.md` /
|
|
124
122
|
`TOOLS.md` / `LINKAGE.md` / `DATASOURCE.md` / `REVIEW.md` / `TOOLING.md` / `STEPS.md`)。
|
|
125
|
-
架构上的取舍写在工作台仓库的 `docs/adr/**`。
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dsh-workbench/module-tools",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "模块包的构建与门禁工具:build(单文件 ESM 宿主侧 + 套 loader 壳的浏览器侧)、check(契约/产物边界/源码边界/卡片渲染)。模块包不再各带一份构建脚本。",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
},
|
|
22
22
|
"dependencies": {
|
|
23
23
|
"esbuild": "^0.28.2",
|
|
24
|
-
"@dsh-workbench/shared": "0.
|
|
24
|
+
"@dsh-workbench/shared": "0.3.0"
|
|
25
25
|
},
|
|
26
26
|
"scripts": {
|
|
27
27
|
"selfcheck": "node scripts/selfcheck.mjs"
|
package/src/build.mjs
CHANGED
|
@@ -32,7 +32,7 @@ function aliasResolver({ root, unit }) {
|
|
|
32
32
|
context.onResolve({ filter: /^@\// }, (args) => {
|
|
33
33
|
if (SINGLETON.test(args.path)) {
|
|
34
34
|
throw new Error(
|
|
35
|
-
`${args.path} 必须从 kit 拿(运行时要单例),不能内联进模块包 ——
|
|
35
|
+
`${args.path} 必须从 kit 拿(运行时要单例),不能内联进模块包 —— 内联就是第二份实例`,
|
|
36
36
|
);
|
|
37
37
|
}
|
|
38
38
|
let base;
|
|
@@ -71,6 +71,19 @@ function cssInjector({ root }) {
|
|
|
71
71
|
namespace: 'dsh-wb-css',
|
|
72
72
|
}));
|
|
73
73
|
context.onLoad({ filter: /.*/, namespace: 'dsh-wb-css' }, (args) => {
|
|
74
|
+
// 来自被 external 的那份 kit(@dsh-workbench/shared)的样式一律不收:外壳运行时自己会注入
|
|
75
|
+
// 那一份,产物里再带一份就变成"两份 kit 样式按选择器混合"(2026-10-18 实测:模块内联了 145
|
|
76
|
+
// 条外壳规则、冻结构建时的版本,与运行的外壳混用 ⇒ 折行 / 塌列 / 标签压值)。见 ADR-0028 §5.1。
|
|
77
|
+
// 两条路径都要认:
|
|
78
|
+
// · 装了依赖的模块仓 —— `node_modules/@dsh-workbench/{shared,core}/…`(pnpm 的 store 里也含这段);
|
|
79
|
+
// · 本仓自己开发时 —— `packages/{shared,workbench}/dist/…`。**是 dist 不是 src**:esbuild 默认
|
|
80
|
+
// 跟随软链(`preserveSymlinks:false`),workspace 的 `node_modules/@dsh-workbench/shared`
|
|
81
|
+
// 软链会被解成真实源码包路径,而 `exports."."` 指的是编译产物 `dist/index.js`,组件样式
|
|
82
|
+
// 与它同住 `dist/components/**`(只写 `src` 会在本仓场景漏收:实测 148 条规则照进产物)。
|
|
83
|
+
const outsourcedKitCss =
|
|
84
|
+
/node_modules\/@dsh-workbench\/(shared|core)\//.test(args.path) ||
|
|
85
|
+
/\/packages\/(shared|workbench)\/(src|dist)\//.test(args.path);
|
|
86
|
+
if (outsourcedKitCss) return { contents: 'export default {};', loader: 'js' };
|
|
74
87
|
const raw = readFileSync(args.path, 'utf8');
|
|
75
88
|
const key = relative(root, args.path).split('/').reverse()[0] + ':' + args.path;
|
|
76
89
|
const scoped = /\.module\.css$/.test(args.path);
|
|
@@ -163,7 +176,7 @@ export async function build({ root }) {
|
|
|
163
176
|
if (inlinedDsh.length > 0) {
|
|
164
177
|
throw new Error(
|
|
165
178
|
`${id}: dsh 的包被打进了浏览器产物 —— ${inlinedDsh.join('、')}\n` +
|
|
166
|
-
'这些应当由外壳的模块表提供(标成 external 才是"用外壳那一份"
|
|
179
|
+
'这些应当由外壳的模块表提供(标成 external 才是"用外壳那一份")。',
|
|
167
180
|
);
|
|
168
181
|
}
|
|
169
182
|
|
package/src/checks/bundles.mjs
CHANGED
|
@@ -96,8 +96,8 @@ export async function run() {
|
|
|
96
96
|
const hits = fixtureMarkersIn(source);
|
|
97
97
|
problems.push(
|
|
98
98
|
`${name}: ${artifact.what} ${artifact.file} 里有夹具(${hits.slice(0, 4).join(' · ')})—— ` +
|
|
99
|
-
'
|
|
100
|
-
'模块自己的 vitest
|
|
99
|
+
'模块不带夹具:夹具只服务工作台开发者的调试(发布产物里没有它),' +
|
|
100
|
+
'模块自己的 vitest 把几态各喂一遍就够。',
|
|
101
101
|
);
|
|
102
102
|
}
|
|
103
103
|
// 外壳的组件样式**只有一份**(外壳注入那一次)。模块从 `@dsh-workbench/shared` 的**根入口**
|
|
@@ -111,7 +111,7 @@ export async function run() {
|
|
|
111
111
|
`${name}: ${artifact.what} ${artifact.file} 里内联了 ${inlined.length} 条外壳的 CSS 规则` +
|
|
112
112
|
`(${inlined.slice(0, 3).join(' · ')})—— 外壳的组件样式只有一份:` +
|
|
113
113
|
'React 原语从 \'react\' 拿、外壳给的东西从 kit 拿(`@dsh-workbench/shared` 根入口是浏览器 barrel,' +
|
|
114
|
-
'它会急切地把 components/**
|
|
114
|
+
'它会急切地把 components/** 连样式一起拉进来)。',
|
|
115
115
|
);
|
|
116
116
|
}
|
|
117
117
|
}
|
package/src/checks/source.mjs
CHANGED
|
@@ -159,7 +159,7 @@ export async function run() {
|
|
|
159
159
|
}
|
|
160
160
|
problems.push(
|
|
161
161
|
`${name}: ${rel} 里 import 了 ${specifier} —— 它不是平台种子词,模块表里没有它,` +
|
|
162
|
-
`浏览器侧要用 dsh 的组件只能 import
|
|
162
|
+
`浏览器侧要用 dsh 的组件只能 import 种子词那几个;其余平台契约按需自己声明`,
|
|
163
163
|
);
|
|
164
164
|
continue;
|
|
165
165
|
}
|
package/src/checks/style.mjs
CHANGED
|
@@ -233,7 +233,9 @@ const INSTALLED_COMPONENTS = join('node_modules', '@dsh-workbench', 'shared', 's
|
|
|
233
233
|
|
|
234
234
|
// ── 字号口径(S5,用户 2026-10-17 定的那条):只报「刻度外手搓字面值」,**黄,不判红** ─────────
|
|
235
235
|
//
|
|
236
|
-
// 口径、刻度出处与"将来怎么变红"写在 `.scratch/font-scale/spec.md
|
|
236
|
+
// 口径、刻度出处与"将来怎么变红"写在 `.scratch/font-scale/spec.md`(我们自己那份现场)。这份 note
|
|
237
|
+
// 是**打印给模块作者**的,所以指路不许指到作者打不开的地方(`.scratch/**` 不随 module-tools 出货):
|
|
238
|
+
// `FONT_DOC` 指 `@dsh-workbench/core` 随包出货的那份给模块作者的口径(作者装了外壳就有)。
|
|
237
239
|
// 扫三个来源:外壳自己的 `src`、shared 的 `src`、本次发现的模块包;外壳两处与上面那份契约
|
|
238
240
|
// (`SOURCE_BOARD` / `SOURCE_COMPONENTS`)走同一条路 —— 源码在隔壁时读源码,仓外的模块工作区
|
|
239
241
|
// 读不到就少扫那一半(note 里只报实际扫到的)。测试文件不扫(用例里那些是断言字符串,不是界面字号)。
|
|
@@ -248,7 +250,7 @@ const SHELL_FONT_SCALE_FILES = [
|
|
|
248
250
|
fileURLToPath(new URL('../../../workbench/src/shared/styles/tokens/scale.css', import.meta.url)),
|
|
249
251
|
fileURLToPath(new URL('../../../workbench/src/shared/styles/tokens.css', import.meta.url)),
|
|
250
252
|
];
|
|
251
|
-
const FONT_DOC = '
|
|
253
|
+
const FONT_DOC = '`@dsh-workbench/core` 的 `skills/workbench-module-diy/PANEL.md` §6「字号与重点」';
|
|
252
254
|
|
|
253
255
|
/** 包名(读 package.json;读不到就用兜底名,别让一条 note 把判据带崩)。 */
|
|
254
256
|
function packageNameOf(dir, fallback) {
|
|
@@ -372,7 +374,7 @@ export async function run() {
|
|
|
372
374
|
if (exemptRows) continue;
|
|
373
375
|
rows.push(
|
|
374
376
|
`${pkg.manifest.name}: ${relative(pkg.dir, file)}:${hit.line} 模块自己管了行距(${hit.selector} 的 ${hit.decl})—— ` +
|
|
375
|
-
`行距归 FormRow(s)
|
|
377
|
+
`行距归 FormRow(s)(行距只有这一档,模块不许自己写):` +
|
|
376
378
|
`${(lines[hit.line - 1] ?? '').trim().slice(0, 80)}`,
|
|
377
379
|
);
|
|
378
380
|
}
|
|
@@ -405,7 +407,7 @@ export async function run() {
|
|
|
405
407
|
if (exemptRows) continue;
|
|
406
408
|
rows.push(
|
|
407
409
|
`${pkg.manifest.name}: ${relative(pkg.dir, file)}:${hit.line} 手写的「标签 + 控件」行(标签在 ${hit.labelLine} 行,控件是 ${hit.control})—— ` +
|
|
408
|
-
`用 FormRow
|
|
410
|
+
`用 FormRow:标签归它(面 → 行 → 控件,中间那层归 FormRow(s)):` +
|
|
409
411
|
`${(lines[hit.line - 1] ?? '').trim().slice(0, 80)}`,
|
|
410
412
|
);
|
|
411
413
|
}
|
|
@@ -418,7 +420,7 @@ export async function run() {
|
|
|
418
420
|
if (exemptActions) return;
|
|
419
421
|
actionAreas.push(
|
|
420
422
|
`${pkg.manifest.name}: ${relative(pkg.dir, file)}:${index + 1} 手写了操作区(借了外壳的槽类 wb-formActions)—— ` +
|
|
421
|
-
`用 kit 的 FormActions
|
|
423
|
+
`用 kit 的 FormActions(主操作在右、一个区一颗 primary、danger 紧挨它左侧且有二次确认、` +
|
|
422
424
|
'间距与窄容器换行都归它):' +
|
|
423
425
|
`${line.trim().slice(0, 80)}`,
|
|
424
426
|
);
|
|
@@ -523,7 +525,7 @@ export async function run() {
|
|
|
523
525
|
}
|
|
524
526
|
if (rows.length > 0) {
|
|
525
527
|
notes.push(
|
|
526
|
-
`${rows.length} 处是「手写的标签+控件行 或
|
|
528
|
+
`${rows.length} 处是「手写的标签+控件行 或 模块自己管的行距」:(面 → 行 → 控件,` +
|
|
527
529
|
'中间那层归 `FormRow(s)`。行里自己写 `<span className=…label>` 配 `SelectField` / `TextField` / ' +
|
|
528
530
|
'`ChoiceGroup` / `Input`、或给 field/form 容器设 `gap` / 上下 `margin`,就是这一层没有主人的信号 —— ' +
|
|
529
531
|
'换 `<FormRow label={…}>{控件}</FormRow>`,行距一起归它。窄口径与已知漏报见 style.mjs 里那两段注释)',
|
|
@@ -531,7 +533,7 @@ export async function run() {
|
|
|
531
533
|
}
|
|
532
534
|
if (actionAreas.length > 0) {
|
|
533
535
|
notes.push(
|
|
534
|
-
`${actionAreas.length} 处是「手写了操作区(借了外壳的槽类 wb-formActions
|
|
536
|
+
`${actionAreas.length} 处是「手写了操作区(借了外壳的槽类 wb-formActions)」:(` +
|
|
535
537
|
'操作区那一层归 `FormActions` —— 顺序(主操作在右、danger 紧挨它左侧)、一个区一颗 `primary`、' +
|
|
536
538
|
'间距、窄容器换行都由组件定;调用点只给"有哪些动作、哪个是主要、点了做什么"。' +
|
|
537
539
|
'`wb-formActions` 这个槽类只为操作区存在,出现它就是自己摆了一套:换 `<FormActions actions={…} />`。' +
|
package/src/font-scale.mjs
CHANGED
|
@@ -187,8 +187,11 @@ function detailOf(hit, verdict) {
|
|
|
187
187
|
/**
|
|
188
188
|
* 拼成 `checks/style.mjs` 通过时的那几行 note:第一行汇总(各类条数 + 涉及文件数 + 指路),
|
|
189
189
|
* 后面最多 `DETAIL_LIMIT` 行明细。零命中时只留一行"都是 0"。
|
|
190
|
+
*
|
|
191
|
+
* `doc` 是**必填**的:这几行 note 打印给模块作者,指路必须是他打得到的地方(调用方给,见
|
|
192
|
+
* `checks/style.mjs` 的 `FONT_DOC`)—— 不留默认值,免得默认值哪天又变成一条跨仓路径印出去。
|
|
190
193
|
*/
|
|
191
|
-
export function fontScaleNotes(report, scale, { doc
|
|
194
|
+
export function fontScaleNotes(report, scale, { doc }) {
|
|
192
195
|
const sizes = [...scale].sort((a, b) => a - b).map(formatSize).join(' / ');
|
|
193
196
|
const head =
|
|
194
197
|
`字号口径(黄,不判红):刻度 ${sizes}(从 --dsw-font-* 与 --wb-font-* 的**定义**读出来,` +
|
|
@@ -205,8 +208,8 @@ export function fontScaleNotes(report, scale, { doc = '.scratch/font-scale/spec.
|
|
|
205
208
|
const notes = [head, ...all.slice(0, DETAIL_LIMIT)];
|
|
206
209
|
if (all.length > DETAIL_LIMIT) {
|
|
207
210
|
notes.push(
|
|
208
|
-
`…另有 ${all.length - DETAIL_LIMIT} 处(刻度外 ${report.offScale.length} / 刻度上 ${report.onScale.length}
|
|
209
|
-
|
|
211
|
+
`…另有 ${all.length - DETAIL_LIMIT} 处(刻度外 ${report.offScale.length} / 刻度上 ${report.onScale.length})` +
|
|
212
|
+
`没列出(这里最多列 ${DETAIL_LIMIT} 行);口径与出处见 ${doc}`,
|
|
210
213
|
);
|
|
211
214
|
}
|
|
212
215
|
return notes;
|
package/src/seed.mjs
CHANGED
|
@@ -1,11 +1,21 @@
|
|
|
1
1
|
// dsh 客户端模块表的**平台种子词** —— 一个模块的浏览器产物**天生**能 require 到它们。
|
|
2
2
|
//
|
|
3
|
-
// 名单真值出处:dsh
|
|
3
|
+
// 名单真值出处:dsh 外壳声明平台模块的地方(rc.2 `packages/client/web/src/platform.ts` 的
|
|
4
|
+
// `PLATFORM_MODULES`,装种子的 `seed.ts` 的 `getStaticModules()` 与它 `satisfies` 互锁);
|
|
5
|
+
// 装机产物里那张表在 `@deepseek-ai/dsh-web-frontend/dist/assets/index-*.js` 的 `SZ()`。
|
|
4
6
|
// 证据与推理见 `.scratch/dsh-ui-alignment/spec.md` §0;「模块可以直接用它」的决定见 ADR-0028。
|
|
5
7
|
//
|
|
6
8
|
// 这份表是**唯一真相**:构建的 external 默认表(`build.mjs`)、契约判据(`contracts.mjs`)、
|
|
7
9
|
// 源码边界(`checks/source.mjs`)与新增的原语判据(`checks/primitives.mjs`)都从这里取。
|
|
8
10
|
// dsh 升版改了种子表,只改这一处。
|
|
11
|
+
//
|
|
12
|
+
// ⚠️ 2026-10-18(dsh 0.2.1-rc.2 对账):这张表**漏过两个词** —— `zod` 与
|
|
13
|
+
// `@tanstack/react-virtual`(dsh 那边一共 11 个,我们只抄了 9 个,`0.2.1-alpha.3` 就少,
|
|
14
|
+
// 不是这次升级引入的)。漏的代价不对称:`build.mjs:17,161` 拿它当模块浏览器产物的默认
|
|
15
|
+
// external 表 ⇒ 模块写 `import 'zod'` 会被**内联出第二份**;`contracts.mjs:39,551` 拿
|
|
16
|
+
// `SEED_SHORT` 判 `dsh.client.external` 能不能落地 ⇒ 声明 `zod` 反而被判「落不到图里任何一行」。
|
|
17
|
+
// 对账脚本:把 dsh 那份 `platform.ts` 的数组原样抄下来与这里逐字比(见两份报告
|
|
18
|
+
// `.scratch/dsh-0.2.1-rc.2/report-client-surface.md` §2.1)。
|
|
9
19
|
|
|
10
20
|
export const SEED_SPECIFIERS = [
|
|
11
21
|
'react',
|
|
@@ -13,6 +23,8 @@ export const SEED_SPECIFIERS = [
|
|
|
13
23
|
'react-dom',
|
|
14
24
|
'react-dom/client',
|
|
15
25
|
'@deepseek-ai/cordis',
|
|
26
|
+
'zod',
|
|
27
|
+
'@tanstack/react-virtual',
|
|
16
28
|
'@deepseek-ai/dsh-client-store',
|
|
17
29
|
'@deepseek-ai/dsh-client-ui-slots',
|
|
18
30
|
'@deepseek-ai/dsh-client-ui-primitives',
|
|
@@ -20,13 +32,15 @@ export const SEED_SPECIFIERS = [
|
|
|
20
32
|
];
|
|
21
33
|
|
|
22
34
|
/**
|
|
23
|
-
* 外部声明里允许的**短名**(历史写法:`"ui-primitives"`
|
|
35
|
+
* 外部声明里允许的**短名**(历史写法:`"ui-primitives"` 这类)与**全名**。
|
|
24
36
|
* 契约判据按它放行,免得老包只因为写法短就被判红。
|
|
25
37
|
*/
|
|
26
38
|
export const SEED_SHORT = new Set([
|
|
27
39
|
'react',
|
|
28
40
|
'react-dom',
|
|
29
41
|
'cordis',
|
|
42
|
+
'zod',
|
|
43
|
+
'@tanstack/react-virtual',
|
|
30
44
|
'dsh-client-store',
|
|
31
45
|
'ui-slots',
|
|
32
46
|
'ui-primitives',
|
package/src/sync.mjs
CHANGED
|
@@ -36,7 +36,7 @@ export async function sync({ root }) {
|
|
|
36
36
|
throw new Error(
|
|
37
37
|
`${unit}: 清单里 ${missing.join(' / ')} 没有 key —— ${hint}。例如:\n` +
|
|
38
38
|
`${tools.map((tool) => ` "key": "…", // ${JSON.stringify(tool.name)}`).join('\n')}\n` +
|
|
39
|
-
'(key 是代码里那个短名:`DECLARED_TOOLS.<key>.name
|
|
39
|
+
'(key 是代码里那个短名:`DECLARED_TOOLS.<key>.name`;生成物按清单生成它,代码里引用工具名就写它、不写字面量)',
|
|
40
40
|
);
|
|
41
41
|
}
|
|
42
42
|
|