@dsh-workbench/module-tools 0.1.14 → 0.1.16
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 +71 -0
- package/bin/dsh-wb-module.mjs +4 -2
- package/package.json +3 -2
- package/src/build.mjs +2 -11
- package/src/check.mjs +88 -7
- package/src/checks/alias.mjs +51 -29
- package/src/checks/bundles.mjs +44 -31
- package/src/checks/cards.mjs +119 -107
- package/src/checks/contracts.mjs +621 -116
- package/src/checks/kit.mjs +127 -64
- package/src/checks/markers.mjs +55 -32
- package/src/checks/primitives.mjs +82 -57
- package/src/checks/source.mjs +106 -92
- package/src/checks/standalone.mjs +54 -24
- package/src/checks/style.mjs +133 -94
- package/src/css-injection.mjs +27 -0
- package/src/design.mjs +424 -0
- package/src/discover.mjs +1 -1
- package/src/init.mjs +6 -1
- package/src/publish.mjs +134 -0
- package/template/scripts/smoke.mjs.tpl +104 -4
- package/template-diy/README.md.tpl +282 -0
- package/template-diy/cordis.patch.yml.tpl +3 -0
- package/template-diy/design.json.tpl +38 -0
- package/template-diy/package.json.tpl +43 -0
- package/template-diy/scripts/smoke.mjs.tpl +232 -0
- package/template-diy/src/client.tsx.tpl +258 -0
- package/template-diy/src/host.ts.tpl +131 -0
- package/template-diy/src/messages.ts.tpl +28 -0
- package/template-diy/src/plugin.test.ts.tpl +359 -0
- package/template-diy/tsconfig.json +20 -0
- package/template-diy/types/globals.d.ts +27 -0
- package/template-diy/vitest.config.ts +14 -0
package/src/checks/kit.mjs
CHANGED
|
@@ -16,6 +16,9 @@
|
|
|
16
16
|
// 用 `DSH_WB_CORE_CLIENT` 指着下限那份外壳跑(且面差通过)= 验过了,规则②不再报。
|
|
17
17
|
//
|
|
18
18
|
// 判据跟着 `dsh.workbench` 自述清单走;没有 client.js 的包这里没事可查(由 build/契约判据管)。
|
|
19
|
+
//
|
|
20
|
+
// 形状(十条判据统一):导出 `title` 与 `run()`,`run()` 返回三态结果;
|
|
21
|
+
// 打印与退出码归 `../check.mjs` 那一层。
|
|
19
22
|
|
|
20
23
|
import { existsSync, readFileSync } from 'node:fs';
|
|
21
24
|
import { createRequire } from 'node:module';
|
|
@@ -25,6 +28,8 @@ import { discoverModulePackages } from '../discover.mjs';
|
|
|
25
28
|
const SHELL = '@dsh-workbench/core';
|
|
26
29
|
const KIT_SPECIFIER = '@dsh-workbench/core/client';
|
|
27
30
|
|
|
31
|
+
export const title = '外壳 kit 面';
|
|
32
|
+
|
|
28
33
|
function kitUsageOf(text) {
|
|
29
34
|
const bindings = new Set();
|
|
30
35
|
const BINDING = /(?:var|const|let)\s+([A-Za-z_$][\w$]*)\s*=\s*(?:__toESM\(\s*)?require\(\s*["']@dsh-workbench\/core\/client["']\s*\)/g;
|
|
@@ -106,78 +111,136 @@ function below(a, b) {
|
|
|
106
111
|
return false;
|
|
107
112
|
}
|
|
108
113
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
114
|
+
// `--only kit` 顺带当"这份外壳的 kit 里有什么"的查询口。
|
|
115
|
+
//
|
|
116
|
+
// 为什么放在这条判据里:它本来就要把外壳真实产物 `client.js` 跑一遍、取出那份 kit ——
|
|
117
|
+
// 这已经是**唯一可信**的取数方式(手抄一份清单就是第二份真相,迟早跟外壳漂)。
|
|
118
|
+
// 造模块的人(尤其 DIY 工坊里那位)要的正是"有哪些成员能用",于是单跑这一条时把它打出来。
|
|
119
|
+
//
|
|
120
|
+
// 只在单跑时打:外壳把整份 `@dsh-workbench/shared` barrel 摊进了每个桶,所以这份名单
|
|
121
|
+
// 有一百多个名字 —— 聚合跑时它只是噪音,查询时才值钱。
|
|
122
|
+
//
|
|
123
|
+
// 返回的是若干 `notes`(每条一行,交给聚合层加缩进),而不是打印 —— 打印归 check.mjs。
|
|
124
|
+
function faceNotes(asked, shells) {
|
|
125
|
+
const faceShell = shells[shells.length - 1] ?? shellClientPathOf(process.cwd());
|
|
126
|
+
if (!asked) {
|
|
127
|
+
return ['(想知道这份外壳的 kit 里有什么:dsh-wb-module check --only kit)'];
|
|
121
128
|
}
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
129
|
+
if (faceShell === undefined || !existsSync(faceShell)) {
|
|
130
|
+
return ['(没找到外壳的 client.js,列不出 kit 面:先确认 node_modules 里装着 @dsh-workbench/core)'];
|
|
131
|
+
}
|
|
132
|
+
try {
|
|
133
|
+
const kit = shellKitOf(faceShell);
|
|
134
|
+
const buckets = Object.keys(kit)
|
|
135
|
+
.filter((bucket) => typeof kit[bucket] === 'object' && kit[bucket] !== null)
|
|
136
|
+
.sort();
|
|
137
|
+
if (buckets.length === 0) {
|
|
138
|
+
return [`(${faceShell} 里没取出 kit:外壳版本可能太旧)`];
|
|
139
|
+
}
|
|
140
|
+
const every = new Map();
|
|
141
|
+
for (const bucket of buckets) {
|
|
142
|
+
for (const name of Object.keys(kit[bucket])) every.set(name, (every.get(name) ?? 0) + 1);
|
|
143
|
+
}
|
|
144
|
+
const notes = [`这份外壳(${faceShell})的 kit 面 —— 只能用这些,别自己造:`];
|
|
145
|
+
for (const bucket of buckets) {
|
|
146
|
+
const names = Object.keys(kit[bucket]);
|
|
147
|
+
const only = names.filter((name) => every.get(name) === 1).sort();
|
|
148
|
+
notes.push(` ${bucket}(${names.length}):${only.length === 0 ? '(全部来自下面的共用面)' : only.join(', ')}`);
|
|
149
|
+
}
|
|
150
|
+
const shared = [...every.entries()].filter(([, count]) => count > 1).map(([name]) => name).sort();
|
|
151
|
+
notes.push(` 共用面(${shared.length}):每个桶里都有 —— ${shared.join(', ')}`);
|
|
152
|
+
return notes;
|
|
153
|
+
} catch (error) {
|
|
154
|
+
return [`(取不出这份外壳的 kit 面:${error instanceof Error ? error.message : String(error)})`];
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
export async function run() {
|
|
159
|
+
const problems = [];
|
|
160
|
+
let inspected = 0;
|
|
161
|
+
let noArtifact = 0;
|
|
162
|
+
let noShell = 0;
|
|
163
|
+
const override = process.env.DSH_WB_CORE_CLIENT;
|
|
164
|
+
const shells = [];
|
|
165
|
+
const modulePackages = discoverModulePackages({ root: process.cwd() });
|
|
166
|
+
|
|
167
|
+
for (const pkg of modulePackages) {
|
|
168
|
+
const name = pkg.manifest.name;
|
|
169
|
+
const client = join(pkg.dir, 'client.js');
|
|
170
|
+
if (!existsSync(client)) {
|
|
171
|
+
noArtifact += 1;
|
|
172
|
+
continue;
|
|
173
|
+
}
|
|
174
|
+
inspected += 1;
|
|
175
|
+
const text = readFileSync(client, 'utf8');
|
|
176
|
+
const used = kitUsageOf(text);
|
|
177
|
+
const uses = used.size > 0 ? text.includes(KIT_SPECIFIER) : false;
|
|
178
|
+
|
|
179
|
+
if (uses) {
|
|
180
|
+
const shellClient = shellClientPathOf(pkg.dir);
|
|
181
|
+
if (shellClient === undefined || !existsSync(shellClient)) {
|
|
182
|
+
noShell += 1;
|
|
183
|
+
} else {
|
|
184
|
+
let kit;
|
|
185
|
+
try {
|
|
186
|
+
kit = shellKitOf(shellClient);
|
|
187
|
+
} catch (error) {
|
|
150
188
|
problems.push(
|
|
151
|
-
`${name}:
|
|
152
|
-
|
|
153
|
-
`而外壳没有错误边界 ⇒ 整块面板会跟着没。要么去掉/换掉这些成员,要么把 peer 下限提到真正需要的那版`,
|
|
189
|
+
`${name}: 取不出这份外壳的 kit(${shellClient})—— ${error instanceof Error ? error.message : String(error)}。` +
|
|
190
|
+
`执行产物是这份判据唯一可信的取数方式;看不懂就别放着不管`,
|
|
154
191
|
);
|
|
192
|
+
kit = undefined;
|
|
193
|
+
}
|
|
194
|
+
if (kit !== undefined) {
|
|
195
|
+
if (!shells.includes(shellClient)) shells.push(shellClient);
|
|
196
|
+
const missing = [];
|
|
197
|
+
for (const [bucket, members] of used) {
|
|
198
|
+
for (const member of [...members].sort()) {
|
|
199
|
+
if (kit[bucket]?.[member] === undefined) missing.push(`kit.${bucket}.${member}`);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
if (missing.length > 0) {
|
|
203
|
+
problems.push(
|
|
204
|
+
`${name}: 产物用了这份外壳(${shellClient})里没有的 ${missing.join('、')} —— ` +
|
|
205
|
+
`顶层解构不会抛(拿到 undefined),卡片首帧渲染才炸 "Element type is invalid",` +
|
|
206
|
+
`而外壳没有错误边界 ⇒ 整块面板会跟着没。要么去掉/换掉这些成员,要么把 peer 下限提到真正需要的那版`,
|
|
207
|
+
);
|
|
208
|
+
}
|
|
155
209
|
}
|
|
156
210
|
}
|
|
157
211
|
}
|
|
212
|
+
|
|
213
|
+
const peerFloor = floorOf(pkg.manifest.peerDependencies?.[SHELL]);
|
|
214
|
+
const devFloor = floorOf(pkg.manifest.devDependencies?.[SHELL]);
|
|
215
|
+
if (peerFloor !== undefined && devFloor !== undefined && below(peerFloor, devFloor) && override === undefined) {
|
|
216
|
+
problems.push(
|
|
217
|
+
`${name}: peer 下限 ${pkg.manifest.peerDependencies[SHELL]} 比 devDependency ${pkg.manifest.devDependencies[SHELL]} 还旧 —— ` +
|
|
218
|
+
`你对用户承诺的那版外壳,产物从没对着验过。把 peer 下限提到与 devDependency 同档;` +
|
|
219
|
+
`确实要支持更旧的外壳,就用 DSH_WB_CORE_CLIENT=<那版外壳的 client.js> 跑一遍 check --only kit 证明面够用`,
|
|
220
|
+
);
|
|
221
|
+
}
|
|
158
222
|
}
|
|
159
223
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
if (peerFloor !== undefined && devFloor !== undefined && below(peerFloor, devFloor) && override === undefined) {
|
|
163
|
-
problems.push(
|
|
164
|
-
`${name}: peer 下限 ${pkg.manifest.peerDependencies[SHELL]} 比 devDependency ${pkg.manifest.devDependencies[SHELL]} 还旧 —— ` +
|
|
165
|
-
`你对用户承诺的那版外壳,产物从没对着验过。把 peer 下限提到与 devDependency 同档;` +
|
|
166
|
-
`确实要支持更旧的外壳,就用 DSH_WB_CORE_CLIENT=<那版外壳的 client.js> 跑一遍 check --only kit 证明面够用`,
|
|
167
|
-
);
|
|
224
|
+
if (problems.length > 0) {
|
|
225
|
+
return { status: 'fail', notes: [], problems };
|
|
168
226
|
}
|
|
169
|
-
}
|
|
170
227
|
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
for (const problem of problems) console.error(` · ${problem}`);
|
|
174
|
-
process.exit(1);
|
|
175
|
-
}
|
|
228
|
+
const asked = process.env.DSH_WB_ONLY === 'kit';
|
|
229
|
+
const face = faceNotes(asked, shells);
|
|
176
230
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
231
|
+
if (inspected === 0) {
|
|
232
|
+
const why =
|
|
233
|
+
modulePackages.length === 0
|
|
234
|
+
? '没有模块包(没有声明 dsh.workbench 的包)—— 没有产物可对'
|
|
235
|
+
: `${noArtifact} 个还没建产物(跳过)`;
|
|
236
|
+
return { status: 'skip', notes: [why, ...face], problems: [] };
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
const notes = [
|
|
240
|
+
`${inspected} 个模块的产物对过外壳 kit 面`,
|
|
241
|
+
override === undefined ? '(按各自装的那份外壳)' : `(DSH_WB_CORE_CLIENT=${override})`,
|
|
242
|
+
];
|
|
243
|
+
if (noArtifact > 0) notes.push(`${noArtifact} 个还没建产物(跳过)`);
|
|
244
|
+
if (noShell > 0) notes.push(`${noShell} 个没装外壳(跳过面差)`);
|
|
245
|
+
return { status: 'pass', notes: [...notes, ...face], problems: [] };
|
|
246
|
+
}
|
package/src/checks/markers.mjs
CHANGED
|
@@ -1,3 +1,12 @@
|
|
|
1
|
+
// 公开包里不许出现公司内部标识(内网域名 / 内网 npm 源 / 企业内部名)。
|
|
2
|
+
//
|
|
3
|
+
// 只扫**工作台自己会发出去的**包:声明了 `dsh.workbench` 的模块,或 `@dsh-workbench/*` 名下的。
|
|
4
|
+
// 不能照搬"本 workspace 里所有 access=public 的包" —— 在外壳仓里等价,但把这个工具拿到
|
|
5
|
+
// 别人的 monorepo(例如 pdv2)里跑时,那条会把人家二十几个自家 SDK 包一起判了。
|
|
6
|
+
//
|
|
7
|
+
// 形状(十条判据统一):导出 `title` 与 `run()`,`run()` 返回三态结果;
|
|
8
|
+
// 打印与退出码归 `../check.mjs` 那一层。
|
|
9
|
+
|
|
1
10
|
import { readdirSync, readFileSync, statSync } from 'node:fs';
|
|
2
11
|
import { join, relative } from 'node:path';
|
|
3
12
|
import { discoverPackages } from '../discover.mjs';
|
|
@@ -12,6 +21,12 @@ const MARKERS = [
|
|
|
12
21
|
|
|
13
22
|
const SKIP = new Set(['node_modules', 'dist', 'coverage', '.git', '.turbo']);
|
|
14
23
|
|
|
24
|
+
const OWN_SCOPE = '@dsh-workbench/';
|
|
25
|
+
|
|
26
|
+
const isOurs = (pkg) =>
|
|
27
|
+
pkg.manifest.dsh?.workbench !== void 0 ||
|
|
28
|
+
String(pkg.manifest.name ?? '').startsWith(OWN_SCOPE);
|
|
29
|
+
|
|
15
30
|
function walk(dir, acc = []) {
|
|
16
31
|
for (const entry of readdirSync(dir)) {
|
|
17
32
|
if (SKIP.has(entry)) continue;
|
|
@@ -22,40 +37,48 @@ function walk(dir, acc = []) {
|
|
|
22
37
|
return acc;
|
|
23
38
|
}
|
|
24
39
|
|
|
25
|
-
|
|
26
|
-
// 不能照搬"本 workspace 里所有 access=public 的包" —— 在外壳仓里等价,但把这个工具拿到
|
|
27
|
-
// 别人的 monorepo(例如 pdv2)里跑时,那条会把人家二十几个自家 SDK 包一起判了。
|
|
28
|
-
const OWN_SCOPE = '@dsh-workbench/';
|
|
29
|
-
const isOurs = (pkg) =>
|
|
30
|
-
pkg.manifest.dsh?.workbench !== void 0 ||
|
|
31
|
-
String(pkg.manifest.name ?? '').startsWith(OWN_SCOPE);
|
|
40
|
+
export const title = '公开包无内部标识';
|
|
32
41
|
|
|
33
|
-
|
|
34
|
-
(
|
|
35
|
-
)
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
for (const
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
text.split('\n').forEach((line, index) => {
|
|
46
|
-
for (const [label, pattern] of MARKERS) {
|
|
47
|
-
if (pattern.test(line)) {
|
|
48
|
-
problems.push(`${pkg.manifest.name}: ${relative(pkg.dir, file)}:${index + 1} 命中${label} —— ${line.trim().slice(0, 120)}`);
|
|
49
|
-
}
|
|
42
|
+
export async function run() {
|
|
43
|
+
const publicPackages = discoverPackages({ root: process.cwd() }).filter(
|
|
44
|
+
(pkg) => isOurs(pkg) && pkg.manifest.publishConfig?.access === 'public',
|
|
45
|
+
);
|
|
46
|
+
const problems = [];
|
|
47
|
+
for (const pkg of publicPackages) {
|
|
48
|
+
for (const file of walk(pkg.dir)) {
|
|
49
|
+
let text;
|
|
50
|
+
try {
|
|
51
|
+
text = readFileSync(file, 'utf8');
|
|
52
|
+
} catch {
|
|
53
|
+
continue;
|
|
50
54
|
}
|
|
51
|
-
|
|
55
|
+
text.split('\n').forEach((line, index) => {
|
|
56
|
+
for (const [label, pattern] of MARKERS) {
|
|
57
|
+
if (pattern.test(line)) {
|
|
58
|
+
problems.push(`${pkg.manifest.name}: ${relative(pkg.dir, file)}:${index + 1} 命中${label} —— ${line.trim().slice(0, 120)}`);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
}
|
|
52
63
|
}
|
|
53
|
-
}
|
|
54
64
|
|
|
55
|
-
if (problems.length > 0) {
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
65
|
+
if (problems.length > 0) {
|
|
66
|
+
return {
|
|
67
|
+
status: 'fail',
|
|
68
|
+
notes: ['(发布前扫一遍:内网源地址、内网域名、企业内部名都不该出现在公网包里)'],
|
|
69
|
+
problems,
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
if (publicPackages.length === 0) {
|
|
73
|
+
return {
|
|
74
|
+
status: 'skip',
|
|
75
|
+
notes: [`没有要发出去的公开包(${OWN_SCOPE}* 名下、或声明了 dsh.workbench 的,且 access=public)—— 没有包要扫`],
|
|
76
|
+
problems: [],
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
return {
|
|
80
|
+
status: 'pass',
|
|
81
|
+
notes: [`${publicPackages.length} 个 access=public 的包扫过`],
|
|
82
|
+
problems: [],
|
|
83
|
+
};
|
|
60
84
|
}
|
|
61
|
-
console.log(`✔ 公开包无内部标识:${publicPackages.length} 个 access=public 的包扫过`);
|
|
@@ -11,6 +11,9 @@
|
|
|
11
11
|
// 由 `checks/source.mjs` 拦,这里对着产物再确认一次)。
|
|
12
12
|
//
|
|
13
13
|
// 判据跟着 `dsh.workbench` 自述清单走:没用 dsh 原语的模块直接跳过(0 命中 = 通过)。
|
|
14
|
+
//
|
|
15
|
+
// 形状(十条判据统一):导出 `title` 与 `run()`,`run()` 返回三态结果;
|
|
16
|
+
// 打印与退出码归 `../check.mjs` 那一层。
|
|
14
17
|
|
|
15
18
|
import { existsSync, readdirSync, readFileSync, statSync } from 'node:fs';
|
|
16
19
|
import { join, relative } from 'node:path';
|
|
@@ -41,76 +44,98 @@ function sourceFiles(dir, acc = []) {
|
|
|
41
44
|
return acc;
|
|
42
45
|
}
|
|
43
46
|
|
|
44
|
-
const
|
|
45
|
-
let modulesUsingPrimitives = 0;
|
|
46
|
-
let modulesUsingReactOnly = 0;
|
|
47
|
+
export const title = 'dsh 原语只从模块表拿';
|
|
47
48
|
|
|
48
|
-
|
|
49
|
-
const
|
|
50
|
-
const
|
|
51
|
-
|
|
49
|
+
export async function run() {
|
|
50
|
+
const modulePackages = discoverModulePackages({ root: process.cwd() });
|
|
51
|
+
const problems = [];
|
|
52
|
+
let modulesUsingPrimitives = 0;
|
|
53
|
+
let modulesUsingReactOnly = 0;
|
|
54
|
+
let modulesTouchingSeeds = 0;
|
|
52
55
|
|
|
53
|
-
for (const
|
|
54
|
-
const
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
if (specifier === undefined || !SEED.has(specifier)) continue;
|
|
58
|
-
if (!used.has(specifier)) used.set(specifier, relative(pkg.dir, file));
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
if (used.size === 0) continue;
|
|
62
|
-
const dshSeeds = [...used.keys()].filter((specifier) => BROWSER_SEED_SPECIFIERS.includes(specifier));
|
|
63
|
-
if (dshSeeds.length === 0) modulesUsingReactOnly += 1;
|
|
64
|
-
else modulesUsingPrimitives += 1;
|
|
56
|
+
for (const pkg of modulePackages) {
|
|
57
|
+
const name = pkg.manifest.name;
|
|
58
|
+
const declared = new Set(pkg.manifest.dsh?.client?.external ?? []);
|
|
59
|
+
const used = new Map();
|
|
65
60
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
`漏声明会被构建内联进来(第二份实例)。加上 "${specifier}"`,
|
|
74
|
-
);
|
|
61
|
+
for (const file of sourceFiles(join(pkg.dir, 'src'))) {
|
|
62
|
+
const text = readFileSync(file, 'utf8');
|
|
63
|
+
for (const match of text.matchAll(IMPORT)) {
|
|
64
|
+
const specifier = match[1] ?? match[2];
|
|
65
|
+
if (specifier === undefined || !SEED.has(specifier)) continue;
|
|
66
|
+
if (!used.has(specifier)) used.set(specifier, relative(pkg.dir, file));
|
|
67
|
+
}
|
|
75
68
|
}
|
|
76
|
-
|
|
69
|
+
if (used.size === 0) continue;
|
|
70
|
+
modulesTouchingSeeds += 1;
|
|
71
|
+
const dshSeeds = [...used.keys()].filter((specifier) => BROWSER_SEED_SPECIFIERS.includes(specifier));
|
|
72
|
+
if (dshSeeds.length === 0) modulesUsingReactOnly += 1;
|
|
73
|
+
else modulesUsingPrimitives += 1;
|
|
77
74
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
} else {
|
|
82
|
-
const text = readFileSync(client, 'utf8');
|
|
75
|
+
// ① 只管 **dsh 的那些浏览器包**:react / react-dom 是构建一直默认外部化的(从第一天就是),
|
|
76
|
+
// 要求每个模块去声明它只会制造噪音。dsh 的包不一样 —— 旧版 module-tools 的默认表里没有它们。
|
|
77
|
+
const needsDeclaration = new Set(BROWSER_SEED_SPECIFIERS);
|
|
83
78
|
for (const [specifier, where] of used) {
|
|
84
|
-
if (
|
|
79
|
+
if (needsDeclaration.has(specifier) && !declares(declared, specifier)) {
|
|
85
80
|
problems.push(
|
|
86
|
-
`${name}: ${where} 用了 ${specifier}
|
|
87
|
-
|
|
81
|
+
`${name}: ${where} 用了 ${specifier},但 package.json 的 dsh.client.external 里没声明它 —— ` +
|
|
82
|
+
`漏声明会被构建内联进来(第二份实例)。加上 "${specifier}"`,
|
|
88
83
|
);
|
|
89
84
|
}
|
|
90
85
|
}
|
|
91
|
-
}
|
|
92
86
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
87
|
+
const client = join(pkg.dir, 'client.js');
|
|
88
|
+
if (!existsSync(client)) {
|
|
89
|
+
problems.push(`${name}: 源码用了 dsh 的种子词,但没有 client.js —— 先跑 dsh-wb-module build`);
|
|
90
|
+
} else {
|
|
91
|
+
const text = readFileSync(client, 'utf8');
|
|
92
|
+
for (const [specifier, where] of used) {
|
|
93
|
+
if (!text.includes(`require(${JSON.stringify(specifier)})`)) {
|
|
94
|
+
problems.push(
|
|
95
|
+
`${name}: ${where} 用了 ${specifier},但产物 client.js 里没有 require("${specifier}") —— ` +
|
|
96
|
+
`它被**内联**进产物了(第二份实例)。检查 dsh.client.external 与构建用的 module-tools 版本`,
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const host = join(pkg.dir, 'dist', 'host.js');
|
|
103
|
+
if (existsSync(host)) {
|
|
104
|
+
const text = readFileSync(host, 'utf8');
|
|
105
|
+
for (const specifier of BROWSER_SEED_SPECIFIERS) {
|
|
106
|
+
if (text.includes(`"${specifier}"`)) {
|
|
107
|
+
problems.push(
|
|
108
|
+
`${name}: 宿主产物 dist/host.js 里出现了 ${specifier} —— 那些包只在浏览器侧存在,` +
|
|
109
|
+
`宿主侧引用它就是起来那一刻炸`,
|
|
110
|
+
);
|
|
111
|
+
}
|
|
102
112
|
}
|
|
103
113
|
}
|
|
104
114
|
}
|
|
105
|
-
}
|
|
106
115
|
|
|
107
|
-
if (problems.length > 0) {
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
116
|
+
if (problems.length > 0) {
|
|
117
|
+
return { status: 'fail', notes: [], problems };
|
|
118
|
+
}
|
|
119
|
+
if (modulePackages.length === 0) {
|
|
120
|
+
return {
|
|
121
|
+
status: 'skip',
|
|
122
|
+
notes: ['没有模块包(没有声明 dsh.workbench 的包)—— 没有 dsh 原语的声明与产物可对'],
|
|
123
|
+
problems: [],
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
if (modulesTouchingSeeds === 0) {
|
|
127
|
+
return {
|
|
128
|
+
status: 'skip',
|
|
129
|
+
notes: ['没有模块用到平台种子词(react / react-dom / dsh 的浏览器包)—— 没有可对照的声明与产物'],
|
|
130
|
+
problems: [],
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
return {
|
|
134
|
+
status: 'pass',
|
|
135
|
+
notes: [
|
|
136
|
+
`${modulesUsingPrimitives} 个模块在用 dsh 的包(都声明了 external、产物里都是 ` +
|
|
137
|
+
`require、宿主产物里没有它们)· 另有 ${modulesUsingReactOnly} 个模块只用到 react/react-dom`,
|
|
138
|
+
],
|
|
139
|
+
problems: [],
|
|
140
|
+
};
|
|
111
141
|
}
|
|
112
|
-
|
|
113
|
-
console.log(
|
|
114
|
-
`✔ dsh 原语通过:${modulesUsingPrimitives} 个模块在用 dsh 的包(都声明了 external、产物里都是 ` +
|
|
115
|
-
`require、宿主产物里没有它们)· 另有 ${modulesUsingReactOnly} 个模块只用到 react/react-dom`,
|
|
116
|
-
);
|