@dsh-workbench/module-tools 1.2.8-beta.0 → 1.2.8-beta.1
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 +1 -1
- package/package.json +2 -2
- package/src/design.mjs +76 -15
- package/template-diy/readme.json.tpl +21 -5
package/README.md
CHANGED
|
@@ -196,7 +196,7 @@ export default [
|
|
|
196
196
|
`dsh-wb-module lint --fix` 先写回再跑一遍看还剩什么。**`check` 永远不动手改文件** ——
|
|
197
197
|
门禁是判据,不是修复器。
|
|
198
198
|
|
|
199
|
-
模板生成三份文档:`readme.json`
|
|
199
|
+
模板生成三份文档:`readme.json` 是终端用户文案源(标题、简介、卡片布局草图 `shots[]`、会看到什么、能做什么、agent 能做什么、数据从哪来、排障、FAQ,后五段是短条目),`README.md` 只由它生成;`DEVELOPING.md` 是作者文档(实现、路由、判据、数据源与三张表),由 `design.json` 生成。`design render` 刷新这两份;`design check` 会拦住空文案、占位符和技术词;旧模块先跑 `design migrate`,它把旧 `README.md` 原样保存为 `DEVELOPING.md`,再生成 `readme.json` 与用户 README。
|
|
200
200
|
|
|
201
201
|
## `design.json`:三张表
|
|
202
202
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dsh-workbench/module-tools",
|
|
3
|
-
"version": "1.2.8-beta.
|
|
3
|
+
"version": "1.2.8-beta.1",
|
|
4
4
|
"description": "模块包的构建与门禁工具:build(单文件 ESM 宿主侧 + 套 loader 壳的浏览器侧)、check(契约/产物边界/源码边界/卡片渲染)。模块包不再各带一份构建脚本。",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
"semver": "^7.8.5",
|
|
34
34
|
"typescript": "5.9.3",
|
|
35
35
|
"typescript-eslint": "^8.71.0",
|
|
36
|
-
"@dsh-workbench/shared": "1.2.8-beta.
|
|
36
|
+
"@dsh-workbench/shared": "1.2.8-beta.1"
|
|
37
37
|
},
|
|
38
38
|
"repository": {
|
|
39
39
|
"type": "git",
|
package/src/design.mjs
CHANGED
|
@@ -432,9 +432,30 @@ export function renderDesign(design) {
|
|
|
432
432
|
].join('\n');
|
|
433
433
|
}
|
|
434
434
|
|
|
435
|
-
function
|
|
436
|
-
|
|
437
|
-
|
|
435
|
+
function bodyLinesOf(value) {
|
|
436
|
+
if (Array.isArray(value)) {
|
|
437
|
+
return value.flatMap((one) => {
|
|
438
|
+
const line = text(one);
|
|
439
|
+
return line === '' ? [] : [`- ${line}`];
|
|
440
|
+
});
|
|
441
|
+
}
|
|
442
|
+
const line = text(value);
|
|
443
|
+
return line === '' ? [] : [line];
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
function sectionOf(heading, value) {
|
|
447
|
+
const lines = bodyLinesOf(value);
|
|
448
|
+
return lines.length === 0 ? [] : [`## ${heading}`, '', ...lines, ''];
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
function shotsOf(source) {
|
|
452
|
+
const listed = Array.isArray(source?.shots) ? source.shots : [];
|
|
453
|
+
const single = text(source?.ascii) === '' ? [] : [{ caption: '', ascii: source.ascii }];
|
|
454
|
+
return [...single, ...listed].flatMap((one) => {
|
|
455
|
+
const drawing = typeof one?.ascii === 'string' ? one.ascii.replace(/\s+$/, '') : '';
|
|
456
|
+
if (drawing.trim() === '') return [];
|
|
457
|
+
return [{ caption: text(one?.caption), drawing }];
|
|
458
|
+
});
|
|
438
459
|
}
|
|
439
460
|
|
|
440
461
|
export function renderUserReadme(source) {
|
|
@@ -443,7 +464,21 @@ export function renderUserReadme(source) {
|
|
|
443
464
|
const faq = asRows(source?.faq);
|
|
444
465
|
const install = text(source?.install) === ''
|
|
445
466
|
? '在市场「已安装」里装或卸。装完在当前项目的面板里勾选这个模块,卡片才会出现;卸掉后卡片消失。'
|
|
446
|
-
:
|
|
467
|
+
: source?.install;
|
|
468
|
+
const shots = shotsOf(source);
|
|
469
|
+
const shotLines = shots.length === 0
|
|
470
|
+
? []
|
|
471
|
+
: [
|
|
472
|
+
'## 卡片长这样',
|
|
473
|
+
'',
|
|
474
|
+
...shots.flatMap((one) => [
|
|
475
|
+
...(one.caption === '' ? [] : [`**${one.caption}**`, '']),
|
|
476
|
+
'```text',
|
|
477
|
+
one.drawing,
|
|
478
|
+
'```',
|
|
479
|
+
'',
|
|
480
|
+
]),
|
|
481
|
+
];
|
|
447
482
|
const faqLines = faq.length === 0
|
|
448
483
|
? []
|
|
449
484
|
: [
|
|
@@ -457,6 +492,7 @@ export function renderUserReadme(source) {
|
|
|
457
492
|
'',
|
|
458
493
|
summary,
|
|
459
494
|
'',
|
|
495
|
+
...shotLines,
|
|
460
496
|
...sectionOf('你会看到什么', source?.see),
|
|
461
497
|
...sectionOf('你能做什么', source?.do),
|
|
462
498
|
...sectionOf('agent 能替你做什么', source?.agent),
|
|
@@ -499,12 +535,18 @@ export function userSourceSkeleton(manifest, design) {
|
|
|
499
535
|
return {
|
|
500
536
|
title: manifestTitleOf(manifest) || text(design?.name) || '模块',
|
|
501
537
|
summary: text(design?.oneLine) || text(manifest?.description),
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
538
|
+
shots: [
|
|
539
|
+
{
|
|
540
|
+
caption: '(可选:图注,比如「卡片」「设置面」「审批」)',
|
|
541
|
+
ascii: '(待填:一张卡片布局草图,用界面真实文案,按屏幕从上到下的顺序画)',
|
|
542
|
+
},
|
|
543
|
+
],
|
|
544
|
+
see: ['(待填:用户在卡片上看到的第一样东西)'],
|
|
545
|
+
do: ['(待填:用户自己能点的一个按钮或能改的一项设置)'],
|
|
546
|
+
agent: ['(待填:agent 能替用户做的一件事;写之前要不要批准)'],
|
|
547
|
+
data: ['(待填:数据从哪来、用谁的登录、读还是写)'],
|
|
506
548
|
install: '',
|
|
507
|
-
trouble: '
|
|
549
|
+
trouble: ['(待填:出问题时用户看到什么、下一步点哪)'],
|
|
508
550
|
faq: [],
|
|
509
551
|
};
|
|
510
552
|
}
|
|
@@ -523,18 +565,37 @@ const USER_COPY_FORBIDDEN = [
|
|
|
523
565
|
['平台内部字段', /\bglobal_key\b/],
|
|
524
566
|
];
|
|
525
567
|
|
|
568
|
+
const BULLET_LIMIT = 70;
|
|
569
|
+
|
|
570
|
+
function copyTextsOf(value) {
|
|
571
|
+
if (Array.isArray(value)) return value.map(text).filter((one) => one !== '');
|
|
572
|
+
const one = text(value);
|
|
573
|
+
return one === '' ? [] : [one];
|
|
574
|
+
}
|
|
575
|
+
|
|
526
576
|
export function userCopyProblemsOf(source) {
|
|
527
577
|
const problems = [];
|
|
528
578
|
if (source === undefined) return [`没有 ${README_SOURCE_FILE} —— 先跑 dsh-wb-module design init 或 design migrate`];
|
|
529
579
|
for (const field of USER_COPY_FIELDS) {
|
|
530
|
-
const
|
|
531
|
-
if (
|
|
532
|
-
|
|
580
|
+
const values = copyTextsOf(source?.[field]);
|
|
581
|
+
if (values.length === 0) problems.push(`${field} 是空的 —— README 的「${field}」还没有用户文案`);
|
|
582
|
+
for (const value of values) {
|
|
583
|
+
if (value.includes('(待填')) problems.push(`${field} 还留着「(待填)」`);
|
|
584
|
+
if (Array.isArray(source?.[field]) && value.length > BULLET_LIMIT) {
|
|
585
|
+
problems.push(`${field} 有一条 ${String(value.length)} 字 —— 超过 ${String(BULLET_LIMIT)} 字就拆成两条,或者把细节放进图里`);
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
const shots = asRows(source?.shots);
|
|
590
|
+
if (shots.length === 0) problems.push('shots 是空的 —— 至少画一张卡片布局图');
|
|
591
|
+
for (const [index, one] of shots.entries()) {
|
|
592
|
+
if (text(one?.ascii).includes('(待填')) problems.push(`shots[${index}] 还留着「(待填)」`);
|
|
533
593
|
}
|
|
534
594
|
for (const field of ['title', 'summary', 'see', 'do', 'agent', 'data', 'install', 'trouble']) {
|
|
535
|
-
const value
|
|
536
|
-
|
|
537
|
-
|
|
595
|
+
for (const value of copyTextsOf(source?.[field])) {
|
|
596
|
+
for (const [label, pattern] of USER_COPY_FORBIDDEN) {
|
|
597
|
+
if (pattern.test(value)) problems.push(`${field} 里有${label} —— 这段只给终端用户看`);
|
|
598
|
+
}
|
|
538
599
|
}
|
|
539
600
|
}
|
|
540
601
|
for (const [index, one] of asRows(source?.faq).entries()) {
|
|
@@ -1,11 +1,27 @@
|
|
|
1
1
|
{
|
|
2
2
|
"title": "__UNIT__ 模块",
|
|
3
3
|
"summary": "__DESC__",
|
|
4
|
-
"
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
4
|
+
"shots": [
|
|
5
|
+
{
|
|
6
|
+
"caption": "卡片",
|
|
7
|
+
"ascii": "(待填:一张卡片布局草图,用界面真实文案,按屏幕从上到下的顺序画)"
|
|
8
|
+
}
|
|
9
|
+
],
|
|
10
|
+
"see": [
|
|
11
|
+
"(待填:用户在卡片上看到的第一样东西)"
|
|
12
|
+
],
|
|
13
|
+
"do": [
|
|
14
|
+
"(待填:用户自己能点的一个按钮或能改的一项设置)"
|
|
15
|
+
],
|
|
16
|
+
"agent": [
|
|
17
|
+
"(待填:agent 能替用户做的一件事;写之前要不要批准)"
|
|
18
|
+
],
|
|
19
|
+
"data": [
|
|
20
|
+
"(待填:数据从哪来、用谁的登录、读还是写)"
|
|
21
|
+
],
|
|
8
22
|
"install": "",
|
|
9
|
-
"trouble":
|
|
23
|
+
"trouble": [
|
|
24
|
+
"(待填:出问题时用户看到什么、下一步点哪)"
|
|
25
|
+
],
|
|
10
26
|
"faq": []
|
|
11
27
|
}
|