create-linkdesk-plugin 0.1.7 → 0.1.9

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/index.js CHANGED
@@ -13,7 +13,7 @@
13
13
  * {{date}} 注入 CHANGELOG.md 的初始段标题——格式必须是 `## v<版本>(YYYY-MM-DD)`,
14
14
  * 那是市场「更改日志」页签切段的解析依据(见 docs/02-Electron架构/.../插件规范化层/02)。
15
15
  *
16
- * 🔴 建仓三语义(E6#103 · L7 第 7.6 轮)——照抄 `cargo new`,**不是「一律 git init」**:
16
+ * 🔴 建仓三语义——照抄 `cargo new`,**不是「一律 git init」**:
17
17
  * ① 目标目录**已在某个 git 仓内** ⇒ 不 init(防嵌套仓——在容器目录里生成插件正是这种情形)
18
18
  * ② 不在任何 git 仓内 ⇒ `git init -b main`
19
19
  * ③ `--no-git` ⇒ 不建仓(逃生口,对标 `cargo new --vcs none`)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-linkdesk-plugin",
3
- "version": "0.1.7",
3
+ "version": "0.1.9",
4
4
  "description": "LinkDesk 插件脚手架——`npm create linkdesk-plugin@latest my-cool-plugin` 一行生成你的第一个插件项目(对标 yo code)。",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1,4 +1,4 @@
1
- # LinkDesk 插件仓 CI(E6#102)——「搬出去不等于脱管」。
1
+ # LinkDesk 插件仓 CI——「搬出去不等于脱管」。
2
2
  #
3
3
  # 为什么有这份文件:插件源码住在**自己的仓**里,壳仓的 `npm run check` 够不着它(那是壳的门禁)。
4
4
  # 类型 / lint / 测试 / i18n / 主题审计这些检查必须在**插件仓自己**跑一遍,否则「独立」是拿
@@ -1,33 +1,33 @@
1
1
  #!/usr/bin/env node
2
2
  /**
3
- * ci-verify——插件仓自检门禁(E6#102 · L7 第 7.5 轮)。挂 `npm run verify`,由 `.github/workflows/ci.yml` 调起。
3
+ * ci-verify——插件仓自检门禁。挂 `npm run verify`,由 `.github/workflows/ci.yml` 调起。
4
4
  *
5
5
  * ── 为什么有它 ──
6
- * 插件源码搬出壳仓之后(E6#99),壳仓的 `npm run check` **够不着它们了**(那份门禁只覆盖壳仓)。
6
+ * 插件源码搬出壳仓之后,壳仓的 `npm run check` **够不着它们了**(那份门禁只覆盖壳仓)。
7
7
  * 搬走的一共六类检查:编译图 / eslint(含 linkdesk/* 自定义规则)/ vitest / 体量 / i18n / 主题审计。
8
8
  * 本脚本 + ci.yml + vitest 配置 = 给插件仓装回来的那一份,否则「独立」就是拿「质量真空」换的。
9
9
  *
10
10
  * ── 五段(每段独立判红;**没有对象也要说话**,不许静默绿)──
11
11
  * ① lint 严格腿 —— `@linkdesk/plugin-sdk` 的 eslint 规则腿 + css/font-scale/spacing 三条扫描腿。
12
- * 🔴 SDK 的 `npm run lint` 是 **WARN 级、永不 fail**(07 §六·三档:警告不是封锁,
12
+ * 🔴 SDK 的 `npm run lint` 是 **WARN 级、永不 fail**(三档制:警告不是封锁,
13
13
  * 作者本地不被拦——那是刻意的)。CI 要的是**拦截**,所以本段把同一份报告按
14
14
  * 「零偏离」判定。**这是「lint 会红」的唯一来源**,别把这段删了换成 `npm run lint`。
15
15
  * ② 跨插件 import —— 壳仓 `linkdesk/no-cross-plugin-import` 的**仓外形态**:插件源码不得引用别的插件
16
16
  * 仓库/包(相对路径越出本仓根,或裸包名形如 `linkdesk-plugin-*` / `@linkdesk/plugin-*`),
17
17
  * package.json 也不得依赖别的插件包。共享代码只经 `@linkdesk/ui`,插件间通信走
18
18
  * `window.linkdesk.*`。⚠️ 这条规则**不在** SDK preset 里(preset 只注册 8 条 linkdesk
19
- * 规则、且全 WARN)——故必须自带(「补进 preset」已登记为待收的账,见 06-门禁与CI.md)。
19
+ * 规则、且全 WARN)——故必须自带(「补进 preset」目前仍是缺口)。
20
20
  * ③ 字典完整性 —— `contributes.i18n` / `contributes.languages` 声明的字典:文件在、可解析、
21
21
  * 每个值都是**非空字符串**。另打印「本仓 `t()` key 的自有字典覆盖度」为**黄灯**。
22
- * ④ 声明自洽 —— 声明必须落在**真实存在的文件**上(E6#102f 的仓内等价物:壳侧读的是随包种子 /
22
+ * ④ 声明自洽 —— 声明必须落在**真实存在的文件**上(壳侧读的是随包种子 /
23
23
  * 冻结快照,插件仓该有「直接吃自己源码」的那条):`entry` / `icon` / `views[].render`
24
24
  * 文件在;`contributes.themes` / `iconThemes` 的数据文件在且过各自的 schema;
25
25
  * 主题 recipe 引用的 `linkdesk://<id>/…` 资产在(且 id 就是本插件);floatingPanel
26
26
  * 三向自洽(viewId ↔ views[].id ↔ render)。
27
27
  * ⑤ 目录条目形态 —— **发布产物** `marketplace.json` 里出现的图标字段(`icon` / `marketIcon`)必须是
28
- * 绝对 URL + 来源标 `"url"`(E6#106)。理由:目录条目是**未装用户**看图时的唯一数据源,
28
+ * 绝对 URL + 来源标 `"url"`。理由:目录条目是**未装用户**看图时的唯一数据源,
29
29
  * 而包内相对路径(`resources/icon.svg`)在未装态恒 404(`linkdesk://` 只在本地已装的
30
- * 插件根里找文件)。`publish` 自 E6#106 起自动 URL 化;本段是那条纪律的机械兜底——
30
+ * 插件根里找文件)。`publish` 会自动 URL 化;本段是那条纪律的机械兜底——
31
31
  * 它看不见「谁是图标栏插件」(不看插件类型,只看字段形态,硬约束 10 零 ID 知识)。
32
32
  *
33
33
  * ── 为什么 ③ 的覆盖度只能黄灯(不是漏做)──
@@ -70,7 +70,7 @@ const isSourceFile = (p) =>
70
70
 
71
71
  const sourceFiles = listFiles(join(ROOT, "src")).filter(isSourceFile);
72
72
 
73
- line("插件仓自检(ci-verify · E6#102)");
73
+ line("插件仓自检(ci-verify)");
74
74
  line("────────────────────────────────────────────────────────────");
75
75
 
76
76
  // ── 读 manifest(JSONC——脚手架允许注释,与 SDK validatePluginJson 同一个解析器)──
@@ -127,29 +127,38 @@ if (report) process.stdout.write(renderPluginLintReport(report) + "\n");
127
127
  * 但必须响亮打印——静默放过才是真问题。
128
128
  */
129
129
  const RULE_NOT_FOUND_RE = /^Definition for rule '.*' was not found/;
130
- /** 按腿取偏离数(label 与 lint.ts 的 legs 一致) */
131
- const legCount = (label) => report?.legs.find((l) => l.label === label)?.violations.length ?? 0;
132
130
 
133
131
  const allRows = report?.eslintRows ?? [];
134
132
  const ruleNotFound = allRows.filter((r) => RULE_NOT_FOUND_RE.test(r.message));
135
133
  const strictEslintRows = allRows.filter((r) => !RULE_NOT_FOUND_RE.test(r.message));
136
- /** 判红的三样:真 eslint 偏离 + css 硬编码腿(硬约束 1 的 .css 半边,eslint 到不了 .css)+ 见下 ②③④⑤ */
137
- const cssLegViolations = legCount("check-css-hardcode");
138
- const strictLintViolations = strictEslintRows.length + cssLegViolations;
139
- /** 只报告不拦的两条腿:字号度量与 4px 节奏——属「审美校准」(SDK 07 §六),存量偏离多且修它们要动插件源码 */
140
- const advisoryLintViolations = legCount("check-font-scale") + legCount("check-spacing-grid");
134
+ /**
135
+ * 判红的两样:真 eslint 偏离 + check 腿偏离(见下 ②③④⑤)。
136
+ * 🔴 **严格腿 = 除「报表档」外的全部腿**(fail-closed):SDK 新增一条腿(如 check-css-namespace)
137
+ * 自动进严格档——想放宽必须把腿名写进 ADVISORY_LEGS 并说明理由,不许默默不查。
138
+ */
139
+ const ADVISORY_LEGS = new Set(["check-font-scale", "check-spacing-grid"]);
140
+ const reportLegs = report?.legs ?? [];
141
+ const strictLegs = reportLegs.filter((l) => !ADVISORY_LEGS.has(l.label));
142
+ const strictLegViolations = strictLegs.reduce((sum, l) => sum + l.violations.length, 0);
143
+ const strictLintViolations = strictEslintRows.length + strictLegViolations;
144
+ /** 报表档:字号度量与 4px 节奏——属「审美校准」,存量偏离多且修它们要动插件源码 */
145
+ const advisoryLintViolations = reportLegs
146
+ .filter((l) => ADVISORY_LEGS.has(l.label))
147
+ .reduce((sum, l) => sum + l.violations.length, 0);
141
148
 
142
149
  if (lintNoObject) {
143
150
  line(`⏭ ① lint 严格腿:${lintNoObject}——本仓**无对象**(不是「绿」,是「没有可查的东西」)。`);
144
151
  } else if (strictLintViolations > 0) {
145
152
  fail(
146
- `① lint 严格腿:${strictLintViolations} 处偏离(eslint ${strictEslintRows.length} + css 硬编码腿 ${cssLegViolations})` +
147
- `——SDK 的 \`npm run lint\` 只报告不拦,**CI 拦**。逐条见上方报告。`,
153
+ `① lint 严格腿:${strictLintViolations} 处偏离(eslint ${strictEslintRows.length} + ` +
154
+ strictLegs.map((l) => `${l.label} ${l.violations.length}`).join(" + ") +
155
+ `)——SDK 的 \`npm run lint\` 只报告不拦,**CI 拦**。逐条见上方报告。`,
148
156
  );
149
157
  } else {
150
158
  line(
151
- `✅ ① lint 严格腿:eslint 规则腿 ${report.files} 文件 + css 硬编码腿 零偏离` +
152
- `(本段判红的是「硬约束 1/2 那一档」)。`,
159
+ `✅ ① lint 严格腿:eslint 规则腿 ${report.files} 文件 + ` +
160
+ strictLegs.map((l) => `${l.label} 零偏离`).join(" + ") +
161
+ `(本段判红的是「硬约束」那一档)。`,
153
162
  );
154
163
  }
155
164
  if (ruleNotFound.length > 0) {
@@ -161,9 +170,12 @@ if (ruleNotFound.length > 0) {
161
170
  }
162
171
  if (advisoryLintViolations > 0) {
163
172
  line(
164
- ` ⚠ 附加腿(**报告不拦**):font-scale ${legCount("check-font-scale")} 处 / ` +
165
- `spacing-grid ${legCount("check-spacing-grid")} 处——字号度量与 4px 节奏属审美校准档` +
166
- `(SDK 07 §六),逐条见上方报告;确属有意的用标准 disable 注释写明理由。`,
173
+ ` ⚠ 附加腿(**报告不拦**):` +
174
+ reportLegs
175
+ .filter((l) => ADVISORY_LEGS.has(l.label))
176
+ .map((l) => `${l.label.replace("check-", "")} ${l.violations.length} 处`)
177
+ .join(" / ") +
178
+ `——字号度量与 4px 节奏属审美校准档,逐条见上方报告;确属有意的用标准 disable 注释写明理由。`,
167
179
  );
168
180
  }
169
181
 
@@ -432,7 +444,7 @@ if (!manifest) {
432
444
  }
433
445
  if (themeAssetRefs.size > 0) ok.push(`${themeAssetRefs.size} 处 linkdesk:// 资产引用`);
434
446
 
435
- /* ⑤ E6#106:目录条目的图标字段必须是**未装态可解析**的形态(绝对 URL)。
447
+ /* ⑤ 目录条目的图标字段必须是**未装态可解析**的形态(绝对 URL)。
436
448
  *
437
449
  * 为什么这条能是纯字段断言、不需要知道「谁是图标栏插件」:无论哪种插件,**未装用户**看市场行时
438
450
  * 目录条目是唯一数据源,而包内相对路径(`resources/icon.svg`)在未装态恒 404——「目录里存相对路径」
@@ -469,7 +481,7 @@ if (!manifest) {
469
481
  } else {
470
482
  catProblems.push(
471
483
  `${key} = ${JSON.stringify(v)} 是**包内相对路径**——目录条目是未装用户的唯一图源,` +
472
- `相对路径在未装态恒 404。跑 \`npm run publish\` 让 SDK 自动转绝对 URL(E6#106)。`,
484
+ `相对路径在未装态恒 404。跑 \`npm run publish\` 让 SDK 自动转绝对 URL。`,
473
485
  );
474
486
  }
475
487
  }
@@ -520,7 +532,7 @@ if (!manifest) {
520
532
  line(`✅ ④ 声明自洽:${ok.length > 0 ? ok.join("、") + "——全部兑现。" : "本仓无声明对象(无 entry/views/themes)。"}`);
521
533
  }
522
534
 
523
- // pluginId 缺声明——schema 兜底打黄灯(E6#98g),这里同款提示
535
+ // pluginId 缺声明——schema 兜底打黄灯,这里同款提示
524
536
  if (!manifest.pluginId) {
525
537
  line(
526
538
  ` ⚠ plugin.json 未显式声明 pluginId——身份现按目录名 "${pluginId}" 兜底。发布后身份不可变,` +
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * {{displayName}}——LinkDesk 插件主视图(由 create-linkdesk-plugin 生成)。
3
3
  *
4
- * 视图插件契约(E5.8,见 docs/03-插件制造/01-API契约.md):壳以 { isActive, tabId?, sourceId? }
4
+ * 视图插件契约(作者文档 01-plugin-api-contract.md):壳以 { isActive, tabId?, sourceId? }
5
5
  * 渲染本文件 default 导出的组件:
6
6
  * - isActive 本标签当前是否聚焦。keep-alive 下非聚焦标签仍在渲染,isActive 只用于
7
7
  * gate「聚焦才跑」的副作用(如自动保存),切勿用它整块 blank 掉内容。
@@ -9,7 +9,7 @@
9
9
  * - sourceId 上下文数据(文件路径 / 数据源等),编辑器类插件用它定位内容。
10
10
  *
11
11
  * 样式:LinkDesk 主题色一律走 CSS 变量 var(--xxx)(见 index.css 示例),禁硬编码 hex。
12
- * 文案:用 t() 读——key 就是中文原文,英文译文放 i18n/en.json(见 05-UI写法规约.md)。
12
+ * 文案:用 t() 读——key 就是中文原文,英文译文放 i18n/en.json(见作者文档 05-ui-conventions.md)。
13
13
  * 壳已 external react/react-dom/react-i18next/i18next——构建不会打进包,插件工程无需 npm i 它们。
14
14
  */
15
15
 
@@ -1,7 +1,7 @@
1
1
  import { defineConfig } from "vitest/config";
2
2
 
3
3
  /**
4
- * 插件工程测试环境(E6#102 §二)。
4
+ * 插件工程测试环境。
5
5
  *
6
6
  * 🔴 这份配置**逐项对齐壳仓 `vitest.config.ts`**:`globals` / `environment: "jsdom"` /
7
7
  * `setupFiles` 三条是**静默生效**的配置——缺了会报错(那还算好),配错了则是「本地绿、CI 红」,
@@ -16,7 +16,7 @@ import { defineConfig } from "vitest/config";
16
16
  * `@linkdesk/ui` 解析到**同仓源码**,CSS 由 Vite 顺手处理;插件仓解析到**已发布的 dist**,
17
17
  * 而 `dist/index.js` 里有 `import "./index.css"` —— Node 的外部依赖加载器读不了 `.css`,
18
18
  * 于是凡经 `@linkdesk/ui` 的测试全部倒在
19
- * `TypeError: Unknown file extension ".css"`(E6#102 实测:marketplace 6 个文件 / 12 例)。
19
+ * `TypeError: Unknown file extension ".css"`(实测:marketplace 6 个文件 / 12 例)。
20
20
  * 把 `@linkdesk/ui` 交给 Vite 内联处理后即恢复。**别删这一条**——删了只会在有 UI 组件的
21
21
  * 仓里以「莫名其妙的环境错」重现。
22
22
  *
@@ -3,11 +3,11 @@
3
3
  * 测试跑在 Node.js/jsdom,没有 Electron preload 注入的 window.linkdesk。
4
4
  * 迁移到 linkdesk.* API 后,插件代码直接依赖它——测试环境需提供最小 mock。
5
5
  *
6
- * 🔴 E6#102(L7 第 7.5 轮):**本文件是壳仓 `vitest.setup.ts` 的逐字副本**(除本头注五条)。
6
+ * 🔴 **本文件是壳仓 `vitest.setup.ts` 的逐字副本**(除本头注五条)。
7
7
  * 它不是「配置」,是插件测试的**运行时地基**——下半部的六个命名空间与 `__ldkConfigStore`
8
8
  * 少了任何一个,凡碰 `window.linkdesk` 的测试都会报错、或更糟:静默走错分支。
9
9
  * 改壳仓那份时把这里一起改(两处同源)。「由 @linkdesk/plugin-sdk 提供共享版本、
10
- * 本文件改成一行 re-export」已登记为待收的账——那时这五条注记一并删掉。
10
+ * 本文件改成一行 re-export」是可预见的收敛方向——那时这五条注记一并删掉。
11
11
  */
12
12
 
13
13
  // path 纯函数——直接实现,不走 IPC
@@ -31,7 +31,7 @@ const pathMock = {
31
31
  },
32
32
  };
33
33
 
34
- // E5.7#98:测试全局窄类型 cast——替代 (globalThis as any)(__ldkConfigStore 由本文件声明、测试文件消费)
34
+ // 测试全局窄类型 cast——替代 (globalThis as any)(__ldkConfigStore 由本文件声明、测试文件消费)
35
35
  type TestGlobal = { window?: Window; __ldkConfigStore?: Map<string, unknown> };
36
36
  const _g = globalThis as TestGlobal;
37
37