create-linkdesk-plugin 0.1.13 → 0.1.14
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 +1 -1
- package/template/AGENTS.md +9 -0
- package/template/README.md +1 -1
- package/template/vitest.setup.ts +3 -96
package/README.md
CHANGED
|
@@ -30,7 +30,7 @@ my-cool-plugin/
|
|
|
30
30
|
├── .github/workflows/ci.yml # CI that runs `npm run verify` on every push
|
|
31
31
|
├── scripts/ci-verify.mjs # the strict tier CI runs (lint + tests + declaration self-checks)
|
|
32
32
|
├── vitest.config.ts # test config (jsdom + globals; @linkdesk/ui is inlined so its CSS import resolves)
|
|
33
|
-
├── vitest.setup.ts # test
|
|
33
|
+
├── vitest.setup.ts # one-line pointer to the shared test ground in @linkdesk/plugin-sdk (the window.linkdesk mock)
|
|
34
34
|
├── .vscode/settings.json # plugin.json is treated as jsonc (comments do not light up red)
|
|
35
35
|
├── resources/
|
|
36
36
|
│ └── icon.svg # placeholder icon — replace it with your own
|
package/package.json
CHANGED
package/template/AGENTS.md
CHANGED
|
@@ -47,6 +47,15 @@ npm run publish # ⑧ publish to your own GitHub repo (first step of listing)
|
|
|
47
47
|
- **This project is its own git repo** (the scaffold ran `git init -b main` plus one initial commit). `publish` uses `origin` to create the Release ⇒ pushing to GitHub is just two commands: `git remote add origin <your repo>` + `git push -u origin main`.
|
|
48
48
|
- **Keep the version numbers in step**: `plugin.json`'s `version` and `package.json`'s `version` **must match**; and every bump needs a matching `## v<new version>(YYYY-MM-DD)` section in `CHANGELOG.md`, or the plugin's detail page will show "no changelog provided for this version".
|
|
49
49
|
|
|
50
|
+
## 6. Tests in this repo
|
|
51
|
+
|
|
52
|
+
- **The toolchain is preinstalled** (`vitest` / `jsdom` / `@testing-library/react` are already declared) — write a `*.test.ts` file and run `npm run test`.
|
|
53
|
+
- **The shared test ground comes from the SDK**: the minimal `window.linkdesk` mock lives in `@linkdesk/plugin-sdk/vitest-setup`, and this repo's `vitest.setup.ts` is a **one-line pointer** to it. **Do not paste a second copy in here** — a copied mock drifts from the shared one and nothing would notice.
|
|
54
|
+
- **Pure logic should be tested**: a `src/**/*.ts` unit that touches neither React nor `window.linkdesk` should get a `src/__tests__/<same name>.test.ts`. Views and hooks are worth testing **when it pays off** — nothing measures that today.
|
|
55
|
+
- **Plugin-specific stubs stay in your own test files**: if your code calls something the shared mock does not cover (say `window.linkdesk.serial`), set that stub up with `vi.fn()` in the test. Do not ask the shared mock to grow a branch for your plugin.
|
|
56
|
+
- **Declarative plugins** (pure JSON / themes / language packs) have **no testable units** — their gate is the structure and declaration checks in `npm run verify`. "It has no tests" is not a defect there.
|
|
57
|
+
- **Older projects can migrate whenever they like**: replacing the full mock body with the one-line pointer plus one `npm install` is the whole move (upside: mock upgrades then ride along with the SDK).
|
|
58
|
+
|
|
50
59
|
---
|
|
51
60
|
|
|
52
61
|
> Author: {{author}} | Generated: {{date}}
|
package/template/README.md
CHANGED
|
@@ -26,7 +26,7 @@ You do not need to pre-create empty folders (git does not track them). **Create
|
|
|
26
26
|
| `src/components/` | Components reused inside this plugin | When needed |
|
|
27
27
|
| `src/services/` | Domain logic / IPC wrappers / data layer | When needed |
|
|
28
28
|
| `src/styles/` | **Multiple** CSS files — keep them together here (a single file next to the entry is fine too) | When needed |
|
|
29
|
-
| `src/__tests__/` | Unit tests — **test tooling is preinstalled** (`vitest` / `jsdom` / `@testing-library/react` are already in `devDependencies`); write tests and run `npm run test` | When needed |
|
|
29
|
+
| `src/__tests__/` | Unit tests — **test tooling is preinstalled** (`vitest` / `jsdom` / `@testing-library/react` are already in `devDependencies`); the `window.linkdesk` mock is **not** in this repo — `vitest.setup.ts` is a one-line pointer to the shared ground in `@linkdesk/plugin-sdk`, so just write tests and run `npm run test` | When needed |
|
|
30
30
|
|
|
31
31
|
> 🔴 **Shared things do not belong here** — components/hooks reused across plugins come from `@linkdesk/ui` (the public package the shell provides; it is already declared in `package.json` as `"latest"`, which resolves to the shell's current version line when you install — pin it to a specific shell version if you need a floor). The shell supplies that one instance at runtime, so **do not import its css** and **do not write a second copy inside your plugin**. Only logic that belongs to this plugin stays local.
|
|
32
32
|
> 🔴 **Assets always live in `resources/` — no loose images in the plugin root.** What gets into the install package is what is **referenced by the README** or **declared by `icon` / `marketIcon`**; the directory name itself has no magic.
|
package/template/vitest.setup.ts
CHANGED
|
@@ -1,96 +1,3 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
* 迁移到 linkdesk.* API 后,插件代码直接依赖它——测试环境需提供最小 mock。
|
|
5
|
-
*
|
|
6
|
-
* 🔴 **本文件是壳仓 `vitest.setup.ts` 的逐字副本**(除本头注五条)。
|
|
7
|
-
* 它不是「配置」,是插件测试的**运行时地基**——下半部的六个命名空间与 `__ldkConfigStore`
|
|
8
|
-
* 少了任何一个,凡碰 `window.linkdesk` 的测试都会报错、或更糟:静默走错分支。
|
|
9
|
-
* 改壳仓那份时把这里一起改(两处同源)。「由 @linkdesk/plugin-sdk 提供共享版本、
|
|
10
|
-
* 本文件改成一行 re-export」是可预见的收敛方向——那时这五条注记一并删掉。
|
|
11
|
-
*/
|
|
12
|
-
|
|
13
|
-
// path 纯函数——直接实现,不走 IPC
|
|
14
|
-
const pathMock = {
|
|
15
|
-
normalize: (p: string) => p.replace(/\\/g, "/"),
|
|
16
|
-
join: (...parts: string[]) =>
|
|
17
|
-
parts.map((p) => String(p).replace(/\\/g, "/")).join("/").replace(/\/+/g, "/"),
|
|
18
|
-
basename: (p: string) => {
|
|
19
|
-
const s = p.replace(/\\/g, "/").split("/");
|
|
20
|
-
return s[s.length - 1] || "";
|
|
21
|
-
},
|
|
22
|
-
dirname: (p: string) => {
|
|
23
|
-
const s = p.replace(/\\/g, "/").split("/");
|
|
24
|
-
s.pop();
|
|
25
|
-
return s.join("/") || ".";
|
|
26
|
-
},
|
|
27
|
-
extname: (p: string) => {
|
|
28
|
-
const b = p.replace(/\\/g, "/").split("/").pop() || "";
|
|
29
|
-
const i = b.lastIndexOf(".");
|
|
30
|
-
return i > 0 ? b.slice(i) : "";
|
|
31
|
-
},
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
// 测试全局窄类型 cast——替代 (globalThis as any)(__ldkConfigStore 由本文件声明、测试文件消费)
|
|
35
|
-
type TestGlobal = { window?: Window; __ldkConfigStore?: Map<string, unknown> };
|
|
36
|
-
const _g = globalThis as TestGlobal;
|
|
37
|
-
|
|
38
|
-
// configuration——默认返回 null,测试中按需 mock。
|
|
39
|
-
// __ldkConfigStore 暴露给测试——测试可直接设置值控制 get() 返回。
|
|
40
|
-
const _configStore = (_g.__ldkConfigStore = new Map<string, unknown>());
|
|
41
|
-
const configurationMock = {
|
|
42
|
-
get: async (key: string) => _configStore.get(key) ?? null,
|
|
43
|
-
set: async (key: string, v: unknown) => { _configStore.set(key, v); },
|
|
44
|
-
onChange: (_key: string, _cb: (v: unknown) => void) => {
|
|
45
|
-
return () => {}; // no-op unsubscribe
|
|
46
|
-
},
|
|
47
|
-
};
|
|
48
|
-
|
|
49
|
-
// workspace——默认返回空工作区
|
|
50
|
-
const workspaceMock = {
|
|
51
|
-
getFolders: async () => [] as { uri: string; name: string }[],
|
|
52
|
-
getActive: async () => undefined as string | undefined,
|
|
53
|
-
};
|
|
54
|
-
|
|
55
|
-
// filesystem——可替换的最小 stub。测试可覆盖 lk.filesystem.xxx = vi.fn() 按需定制
|
|
56
|
-
const filesystemMock = {
|
|
57
|
-
readTextFile: async (_p: string) => "",
|
|
58
|
-
writeTextFile: async (_p: string, _d: string) => {},
|
|
59
|
-
readBinaryFile: async (_p: string) => new Uint8Array(),
|
|
60
|
-
writeBinaryFile: async (_p: string, _d: Uint8Array) => {},
|
|
61
|
-
listDir: async (_p: string) => [] as { path: string; name: string; isDirectory: boolean; isFile: boolean }[],
|
|
62
|
-
exists: async (_p: string) => false,
|
|
63
|
-
mkdir: async (_p: string) => {},
|
|
64
|
-
copy: async (_src: string, _dest: string) => {},
|
|
65
|
-
remove: async (_p: string) => {},
|
|
66
|
-
watch: async (_dirPath: string, _onEvent: (e: unknown) => void) => {
|
|
67
|
-
return () => {}; // unsubscribe
|
|
68
|
-
},
|
|
69
|
-
};
|
|
70
|
-
|
|
71
|
-
// tabs——最小 stub
|
|
72
|
-
const tabsMock = {
|
|
73
|
-
create: async (_type: string, _opts?: Record<string, unknown>) => "tab-1",
|
|
74
|
-
openOrFocus: async (_type: string, _opts?: Record<string, unknown>) => "tab-1",
|
|
75
|
-
focus: async (_tabId: string) => {},
|
|
76
|
-
close: async (_tabId: string) => {},
|
|
77
|
-
focusBySourceId: async (_sourceId: string) => {},
|
|
78
|
-
updateLabelBySourceId: async (_sourceId: string, _label: string) => {},
|
|
79
|
-
closeBySourceId: async (_sourceId: string) => {},
|
|
80
|
-
};
|
|
81
|
-
|
|
82
|
-
// 最小 mock——故意不满足 LinkDeskAPI 全契约(测试按需覆盖),经 linkdesk?: object 窄口赋值
|
|
83
|
-
_g.window = _g.window ?? ({} as Window);
|
|
84
|
-
(_g.window as Window & { linkdesk?: object }).linkdesk = {
|
|
85
|
-
path: pathMock,
|
|
86
|
-
configuration: configurationMock,
|
|
87
|
-
config: configurationMock,
|
|
88
|
-
workspace: workspaceMock,
|
|
89
|
-
filesystem: filesystemMock,
|
|
90
|
-
tabs: tabsMock,
|
|
91
|
-
// event stubs
|
|
92
|
-
events: {
|
|
93
|
-
on: () => () => {},
|
|
94
|
-
emit: () => {},
|
|
95
|
-
},
|
|
96
|
-
};
|
|
1
|
+
// Shared test ground: the minimal `window.linkdesk` mock ships with the SDK (`@linkdesk/plugin-sdk/vitest-setup`).
|
|
2
|
+
// One line on purpose — plugin-specific stubs belong in your own test files (`vi.fn()`), not in here.
|
|
3
|
+
import "@linkdesk/plugin-sdk/vitest-setup";
|