@bushizhanghan/dsh-publish-gate 0.8.2 → 0.10.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/CHANGELOG.md +14 -0
- package/README.md +3 -2
- package/lib/engine.js +82 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.10.0
|
|
4
|
+
|
|
5
|
+
- `client-double-load` (warn): the client probe now evaluates the bundle a second time against the same
|
|
6
|
+
`window`, with a loader that throws on a repeated registration. A bundle inserted by two patches (its own,
|
|
7
|
+
plus any sidebar plugin that injects `dsh-client-modules`) aborted web boot this way. A window-level guard
|
|
8
|
+
makes it a no-op, which is what the recommended bundle shape now shows.
|
|
9
|
+
## 0.9.0
|
|
10
|
+
|
|
11
|
+
- `checkChangelog()`: `changelog-missing-version` (warn), `changelog-no-versions` (warn), `no-changelog` (info).
|
|
12
|
+
The changelog is the only place a user can learn what a version changed; a bump without an entry is silent
|
|
13
|
+
drift, because npm publishes it anyway and the release notes stay one version behind. Headings accept
|
|
14
|
+
`## 1.2.3`, `### [v1.2.3]` and `# v1.2.3`.
|
|
15
|
+
- An `engines-missing` rule was written and then deleted: `no-engines` already existed. Two rules for one
|
|
16
|
+
condition would have reported the same gap twice.
|
|
3
17
|
## 0.8.2
|
|
4
18
|
|
|
5
19
|
- `publish_gate_verify` now returns `pending: true` when the wait runs out with the version still invisible.
|
package/README.md
CHANGED
|
@@ -72,7 +72,7 @@ dsh plugin --profile desktop add @bushizhanghan/dsh-publish-gate
|
|
|
72
72
|
| `readme-wrong-package` | README 的安装命令写的是**另一个包名**(从别的插件抄过来没改)。照着装的人装到别的插件上,而且不会报错——那个包真的存在 |
|
|
73
73
|
|
|
74
74
|
### 值得在发布前知道的(warn / info)
|
|
75
|
-
`npm-scope-mismatch`(登录的账号不是包 scope 的所有者,PUT 会 403,而错误里既不说 token 也不说 scope——我为一个 403 花了一小时)、`unscoped-name`(granular token 的 403)、`repo-missing`、`patch-name-unresolvable`(junction 放错层级)、`inject-unused-service`(`inject` 里写了个拼错的服务名,cordis 一直等它,插件静默不激活)、`no-publish-config-access`(scoped 包没写 `publishConfig.access`,只是提醒,因为命令里本来就带 `--access public`)、`publish-config-registry`(`publishConfig.registry` 指到内网 registry,版本发到别人装不到的地方)、`no-readme` / `readme-no-install` / `readme-no-profile`、`license-mismatch`、`no-tests` / `no-test-files` / `no-test-script`、`no-engines`、`bad-repo-url`、`no-git` / `no-gitignore`、`readme-tool-undocumented`(README 没提这个工具)/ `readme-test-count`(README
|
|
75
|
+
`npm-scope-mismatch`(登录的账号不是包 scope 的所有者,PUT 会 403,而错误里既不说 token 也不说 scope——我为一个 403 花了一小时)、`unscoped-name`(granular token 的 403)、`repo-missing`、`patch-name-unresolvable`(junction 放错层级)、`inject-unused-service`(`inject` 里写了个拼错的服务名,cordis 一直等它,插件静默不激活)、`no-publish-config-access`(scoped 包没写 `publishConfig.access`,只是提醒,因为命令里本来就带 `--access public`)、`publish-config-registry`(`publishConfig.registry` 指到内网 registry,版本发到别人装不到的地方)、`no-readme` / `readme-no-install` / `readme-no-profile`、`license-mismatch`、`no-tests` / `no-test-files` / `no-test-script`、`no-engines`、`bad-repo-url`、`no-git` / `no-gitignore`、`readme-tool-undocumented`(README 没提这个工具)/ `readme-test-count`(README 写的测试数和真实数不一致)、`changelog-missing-version`(要发的这个版本号在 CHANGELOG 里没有对应标题,npm 照样发,发布说明就永远落后一版)、`changelog-no-versions`(CHANGELOG 里没有版本标题,读者对不上自己装的那版)、`no-changelog`(只是提醒),以及市场条目的 `market-*` 全系列。
|
|
76
76
|
|
|
77
77
|
### 产物层:npm 到底会发出什么
|
|
78
78
|
|
|
@@ -124,6 +124,7 @@ dsh plugin --profile desktop add @bushizhanghan/dsh-publish-gate
|
|
|
124
124
|
| `client-apply-throws` | `apply(ctx)` 抛 |
|
|
125
125
|
| `client-no-registration` | `apply` 跑完一次服务都没调用——面板不会出现 |
|
|
126
126
|
| `client-inject-missing-<service>` | `apply` 用了 `ctx.sidebarRightTabs` 但 `inject` 数组里没写它。cordis 不会把没声明的服务给你,于是 `ctx.sidebarRightTabs` 是 `undefined`,`apply` 一碰就抛(或静默什么都没注册) |
|
|
127
|
+
| `client-double-load` | 同一个 bundle 被注入了两次(它自己的 patch 一次,注入 `dsh-client-modules` 的侧边栏插件再一次)。loader 按 id 建工厂,第二次注册直接抛错,整个 Web 启动被中断——不是这个面板没了,是所有面板都没了。用 `if (window.__xLoaded) return; window.__xLoaded = true` 挡一下 |
|
|
127
128
|
| `client-parse-failed` | 文件有语法错误。**单独判**:`SyntaxError` 说明 bundle 本身是坏的,别的报错说明是沙箱不够真,两者别混为一谈 |
|
|
128
129
|
| `client-unavailable` | 子进程没跑起来(warn)——这一层没跑,别把它当干净 |
|
|
129
130
|
|
|
@@ -216,7 +217,7 @@ dsh plugin --profile desktop add @bushizhanghan/dsh-publish-gate
|
|
|
216
217
|
## 开发
|
|
217
218
|
|
|
218
219
|
```sh
|
|
219
|
-
node --test #
|
|
220
|
+
node --test # 99 个引擎单测
|
|
220
221
|
node test/integration.test.mjs # 27 条集成断言(9 个真实插件作 fixture)
|
|
221
222
|
```
|
|
222
223
|
|
package/lib/engine.js
CHANGED
|
@@ -556,6 +556,56 @@ export function checkDocs(dir) {
|
|
|
556
556
|
return out
|
|
557
557
|
}
|
|
558
558
|
|
|
559
|
+
/**
|
|
560
|
+
* The changelog is the only place a user can learn what a version changed, and
|
|
561
|
+
* a version bump without an entry is invisible drift: `npm publish` says
|
|
562
|
+
* nothing, and the release notes stay one version behind forever.
|
|
563
|
+
*/
|
|
564
|
+
export function checkChangelog(dir) {
|
|
565
|
+
const out = []
|
|
566
|
+
const json = readPkgJson(dir)
|
|
567
|
+
if (!json?.version) return out
|
|
568
|
+
const file = ['CHANGELOG.md', 'CHANGELOG', 'HISTORY.md'].find((name) => existsSync(join(dir, name)))
|
|
569
|
+
if (!file) {
|
|
570
|
+
out.push(
|
|
571
|
+
F(
|
|
572
|
+
'no-changelog',
|
|
573
|
+
'info',
|
|
574
|
+
'no CHANGELOG.md',
|
|
575
|
+
'Every published version is permanent, so users need a place to read what changed. This is a nudge, not a defect.',
|
|
576
|
+
'Add CHANGELOG.md with one `## <version>` heading per release, and put it in `files`.',
|
|
577
|
+
),
|
|
578
|
+
)
|
|
579
|
+
return out
|
|
580
|
+
}
|
|
581
|
+
const text = safeText(join(dir, file)) || ''
|
|
582
|
+
const headings = [...text.matchAll(/^#{1,3}\s*\[?v?(\d+\.\d+\.\d+)/gm)].map((m) => m[1])
|
|
583
|
+
if (!headings.length) {
|
|
584
|
+
out.push(
|
|
585
|
+
F(
|
|
586
|
+
'changelog-no-versions',
|
|
587
|
+
'warn',
|
|
588
|
+
`${file} has no version headings`,
|
|
589
|
+
'A changelog without `## 1.2.3` headings cannot be matched to a release, so a reader cannot tell which entry belongs to the version they installed.',
|
|
590
|
+
'Give every release a `## <version>` heading.',
|
|
591
|
+
),
|
|
592
|
+
)
|
|
593
|
+
return out
|
|
594
|
+
}
|
|
595
|
+
if (!headings.includes(json.version)) {
|
|
596
|
+
out.push(
|
|
597
|
+
F(
|
|
598
|
+
'changelog-missing-version',
|
|
599
|
+
'warn',
|
|
600
|
+
`${file} has no entry for ${json.version} (newest heading is ${headings[0]})`,
|
|
601
|
+
'The version you are about to publish has no changelog entry. npm publishes it anyway, and the notes now describe an older release.',
|
|
602
|
+
`Add a \`## ${json.version}\` section at the top of ${file}.`,
|
|
603
|
+
),
|
|
604
|
+
)
|
|
605
|
+
}
|
|
606
|
+
return out
|
|
607
|
+
}
|
|
608
|
+
|
|
559
609
|
// =====================================================================
|
|
560
610
|
// npm / GitHub / market state
|
|
561
611
|
// =====================================================================
|
|
@@ -1736,6 +1786,24 @@ if (!records.parseError) {
|
|
|
1736
1786
|
else records.evalError = text
|
|
1737
1787
|
}
|
|
1738
1788
|
}
|
|
1789
|
+
// The same bundle can reach the page twice: its own patch inserts it, and any
|
|
1790
|
+
// sidebar-style plugin that injects dsh-client-modules inserts it again. The
|
|
1791
|
+
// loader keys factories by id and throws on the second registration, which
|
|
1792
|
+
// aborts web boot. Evaluate a second time against the same window and see
|
|
1793
|
+
// whether load() is called again; a window-level guard makes it a no-op.
|
|
1794
|
+
records.doubleLoad = null
|
|
1795
|
+
if (!records.parseError && records.loaded > 0) {
|
|
1796
|
+
let second = 0
|
|
1797
|
+
const first = globalThis.window.__ModuleLoader__.load
|
|
1798
|
+
globalThis.window.__ModuleLoader__.load = (spec) => {
|
|
1799
|
+
second += 1
|
|
1800
|
+
throw new Error('duplicate factory registration: ' + String(spec && spec.id))
|
|
1801
|
+
}
|
|
1802
|
+
try { new Function(source)() } catch (e) {
|
|
1803
|
+
records.doubleLoad = second > 0 ? String(e && e.message || e) : null
|
|
1804
|
+
}
|
|
1805
|
+
globalThis.window.__ModuleLoader__.load = first
|
|
1806
|
+
}
|
|
1739
1807
|
process.stdout.write('\\n@@PG@@' + JSON.stringify(records))
|
|
1740
1808
|
process.exit(0)
|
|
1741
1809
|
`
|
|
@@ -1797,6 +1865,17 @@ export async function checkClientRuntime(dir, opts = {}) {
|
|
|
1797
1865
|
if (r.applyError) {
|
|
1798
1866
|
out.push(F('client-apply-throws', 'error', `apply() in ${target} throws: ${String(r.applyError).slice(0, 160)}`, 'Registration happens inside apply(); a throw there leaves the panel unregistered while the host half keeps working.', 'Wrap the failing registration, or fix the cause.'))
|
|
1799
1867
|
}
|
|
1868
|
+
if (r.doubleLoad) {
|
|
1869
|
+
out.push(
|
|
1870
|
+
F(
|
|
1871
|
+
'client-double-load',
|
|
1872
|
+
'warn',
|
|
1873
|
+
`${target} registers a factory again when evaluated a second time (${String(r.doubleLoad).slice(0, 120)})`,
|
|
1874
|
+
'A client bundle can reach the page twice: its own patch inserts it, and any sidebar-style plugin that injects dsh-client-modules inserts it again. The loader keys factories by id and throws on the second registration, which aborts web boot — the whole panel disappears, not just this one.',
|
|
1875
|
+
'Guard the load call with a window-level flag: if (window.__myPluginLoaded) return; window.__myPluginLoaded = true.',
|
|
1876
|
+
),
|
|
1877
|
+
)
|
|
1878
|
+
}
|
|
1800
1879
|
if (r.hasApply && !r.applyError && r.registrations.length === 0) {
|
|
1801
1880
|
out.push(
|
|
1802
1881
|
F(
|
|
@@ -2172,6 +2251,7 @@ export function runStaticChecks(dir) {
|
|
|
2172
2251
|
...checkBuildFreshness(dir),
|
|
2173
2252
|
...checkDependencies(dir),
|
|
2174
2253
|
...checkDocs(dir),
|
|
2254
|
+
...checkChangelog(dir),
|
|
2175
2255
|
...checkTests(dir),
|
|
2176
2256
|
...checkNpmState(dir),
|
|
2177
2257
|
...checkGitState(dir),
|
|
@@ -2183,7 +2263,7 @@ const RULE_ORDER = [
|
|
|
2183
2263
|
'missing-field', 'not-esm', 'files-omits-lib', 'files-omits-patch', 'no-exports', 'client-without-export', 'client-without-platform',
|
|
2184
2264
|
'entry-path-missing', 'bundle-patch-not-declared', 'no-publish-config-access', 'private-field-set', 'publish-config-registry',
|
|
2185
2265
|
'client-file-missing', 'client-no-module-loader', 'client-parse-failed', 'client-load-not-called',
|
|
2186
|
-
'client-factory-throws', 'client-no-apply', 'client-apply-throws', 'client-no-registration',
|
|
2266
|
+
'client-factory-throws', 'client-no-apply', 'client-apply-throws', 'client-no-registration', 'client-double-load',
|
|
2187
2267
|
'client-inject-missing-slots', 'client-inject-missing-sidebarRightTabs', 'client-unavailable',
|
|
2188
2268
|
'no-patch-file', 'no-insert-row', 'insert-missing-name', 'insert-name-mismatch',
|
|
2189
2269
|
'patch-name-unresolvable', 'no-name-export', 'no-apply-export', 'no-inject', 'require-in-esm',
|
|
@@ -2200,6 +2280,7 @@ const RULE_ORDER = [
|
|
|
2200
2280
|
'publish-served-mismatch', 'publish-served', 'market-empty', 'market-no-url', 'market-url-not-github',
|
|
2201
2281
|
'market-no-name', 'market-name-shape', 'market-no-category', 'market-bad-category', 'market-no-desc', 'market-no-en', 'market-no-zh', 'unscoped-name', 'no-git', 'no-readme', 'readme-no-install', 'readme-no-profile', 'readme-wrong-package',
|
|
2202
2282
|
'market-no-repo', 'market-unavailable', 'market-duplicate-entry', 'market-npm-name-collision', 'market-repo-too-new',
|
|
2283
|
+
'no-changelog', 'changelog-no-versions', 'changelog-missing-version',
|
|
2203
2284
|
'git-uncommitted', 'git-tag-missing', 'git-tag-elsewhere',
|
|
2204
2285
|
'readme-tool-undocumented', 'readme-test-count',
|
|
2205
2286
|
'no-tests', 'no-test-files', 'no-test-script', 'no-engines', 'bad-repo-url', 'no-gitignore',
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bushizhanghan/dsh-publish-gate",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.0",
|
|
4
4
|
"description": "Pre-publish gate for DSH plugins: manifest, dependencies, artifact, runtime, browser half, traceability and market readiness — plus an input fuzzer that drives every declared parameter with the wrong type. 发布前的七层门禁 + 入参模糊测试。",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|