@linkdesk/plugin-sdk 0.1.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.
@@ -0,0 +1,166 @@
1
+ /**
2
+ * 插件 Vite 构建配置(E6#4)——`defineLinkdeskPluginConfig()` 一键产出 `.linkdesk-plugin` zip。
3
+ *
4
+ * 作者用法:工程根放 plugin.json + src/index.tsx,vite.config.ts 写
5
+ * `export default defineLinkdeskPluginConfig()`,`npm run build`(= linkdesk-plugin-sdk build)即得
6
+ * `<id>.linkdesk-plugin`。产物两份(对齐脚手架/流水线文档 01/03):
7
+ * - 项目根 `./<id>.linkdesk-plugin`(zip 单文件,分发态)
8
+ * - `dist/<id>.linkdesk-plugin/`(解包目录,#4a 目录产物,调试/壳加载调试面)
9
+ *
10
+ * 构建裁决:
11
+ * - React/react-dom/react-i18next/**i18next** external(壳提供——插件自打 i18next 实例 → 翻译全空,
12
+ * B3 教训);其他依赖 inline 完全自包含。
13
+ * - 打包 = 内嵌私有插件 `linkdesk-plugin-packager` 的 Vite hook 序列(closeBundle 时机 zip,归属唯一,
14
+ * bin 只编排不重复 zip):
15
+ * configResolved → buildStart 跑 validatePluginJson(build 前校验,失败即 abort,#5b)
16
+ * → buildEnd 记 failed → closeBundle(failed 或 bundle 缺失则跳过)拷静态清单 + jszip。
17
+ * - 静态清单从**源码 pluginRoot** 拷贝(非 outDir——outDir 每次 emptyOutDir 清空),含 plugin.json/
18
+ * icon/README.md/CHANGELOG.md(K2 缝隙)/ i18n 声明文件;build 产物 index.bundle.js + assets/ 从
19
+ * outDir 深拷贝进 pkgDir。
20
+ * - zip 条目相对 pkgDir、正斜杠、无外层目录(loader 解压期待 plugin.json 在顶,E6#7 契约)。
21
+ * - dev/serve 不触发 build 系 hook → packager 天然只在 build 跑。
22
+ */
23
+ import { copyFileSync, existsSync, mkdirSync, readdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
24
+ import { basename, dirname, join, relative, resolve } from "node:path";
25
+ import react from "@vitejs/plugin-react";
26
+ import JSZip from "jszip";
27
+ import { collectI18nDecls, derivePluginId, isWithinRoot, readPluginManifest, validatePluginJson, } from "./validate.js";
28
+ /** 壳提供、插件不得重复打包的依赖——i18next 必须 external(B3:自打实例 → 翻译全空) */
29
+ const DEFAULT_EXTERNAL = ["react", "react-dom", "react/jsx-runtime", "react-i18next", "i18next"];
30
+ /**
31
+ * 从 srcDir 深拷贝到 destDir,跳过顶层 skip 名——packager 把 Vite build 产物移入 pkgDir 用。
32
+ * pkgDir 建在 outDir 内,靠 skip 自身名避免递归进 zip 目录。
33
+ */
34
+ function copyTree(srcDir, destDir, skipNames) {
35
+ mkdirSync(destDir, { recursive: true });
36
+ for (const e of readdirSync(srcDir, { withFileTypes: true })) {
37
+ if (skipNames.has(e.name))
38
+ continue;
39
+ const s = join(srcDir, e.name);
40
+ const d = join(destDir, e.name);
41
+ if (e.isDirectory())
42
+ copyTree(s, d, new Set());
43
+ else
44
+ copyFileSync(s, d);
45
+ }
46
+ }
47
+ /** 单个文件安全拷贝:断言 src 在 root 内,落 pkgDir 同相对路径,缺省忽略 */
48
+ function copyFileInto(root, pkgDir, rel) {
49
+ const src = resolve(root, rel);
50
+ if (!isWithinRoot(root, src) || !existsSync(src))
51
+ return false;
52
+ const dest = join(pkgDir, rel);
53
+ mkdirSync(dirname(dest), { recursive: true });
54
+ copyFileSync(src, dest);
55
+ return true;
56
+ }
57
+ /** pkgDir → zip 目录遍历——条目相对 pkgDir、正斜杠归一 */
58
+ function zipTree(zip, dir, prefix) {
59
+ for (const e of readdirSync(dir, { withFileTypes: true })) {
60
+ const rel = prefix ? `${prefix}/${e.name}` : e.name;
61
+ const full = join(dir, e.name);
62
+ if (e.isDirectory())
63
+ zipTree(zip, full, rel);
64
+ else
65
+ zip.file(rel, readFileSync(full));
66
+ }
67
+ }
68
+ export function defineLinkdeskPluginConfig(options = {}) {
69
+ const root = process.cwd();
70
+ const manifestPath = join(root, "plugin.json");
71
+ let manifest;
72
+ try {
73
+ manifest = readPluginManifest(manifestPath);
74
+ }
75
+ catch (err) {
76
+ throw new Error(`defineLinkdeskPluginConfig 须在插件工程根运行——${err instanceof Error ? err.message : String(err)}`);
77
+ }
78
+ const declaredEntry = typeof manifest?.entry === "string"
79
+ ? manifest.entry
80
+ : "src/index.tsx";
81
+ const entry = resolve(root, options.entry ?? declaredEntry);
82
+ if (!existsSync(entry)) {
83
+ throw new Error(`入口不存在:${relative(root, entry)}——在 plugin.json 声明 entry 或传 defineLinkdeskPluginConfig({ entry })`);
84
+ }
85
+ const id = derivePluginId(manifest, basename(root));
86
+ const outDir = resolve(root, options.outDir ?? "dist");
87
+ const pkgName = `${id}.linkdesk-plugin`;
88
+ const pkgDir = join(outDir, pkgName);
89
+ /** 打包器——zip/校验的唯一归属点 */
90
+ let failed = false;
91
+ const packager = {
92
+ name: "linkdesk-plugin-packager",
93
+ buildStart() {
94
+ const res = validatePluginJson(manifestPath);
95
+ if (!res.valid) {
96
+ throw new Error(`plugin.json 验证失败(linkdesk-plugin-sdk validate):\n ${res.errors.join("\n ")}`);
97
+ }
98
+ },
99
+ buildEnd(err) {
100
+ // Rollup 在 build 失败(含 buildStart 抛错)时也调 buildEnd(err) + closeBundle——
101
+ // failed 是权威闸门,防残留 outDir 产物被陈旧 zip(emptyOutDir 只在正常 generate 清 outDir)
102
+ if (err) {
103
+ failed = true;
104
+ this.warn(`[linkdesk-plugin-packager] build 失败(${err.message}),跳过打包`);
105
+ }
106
+ },
107
+ async closeBundle() {
108
+ if (failed)
109
+ return;
110
+ if (!existsSync(join(outDir, "index.bundle.js")))
111
+ return;
112
+ let latest;
113
+ try {
114
+ latest = readPluginManifest(manifestPath);
115
+ }
116
+ catch {
117
+ return;
118
+ }
119
+ // 1) 重建 pkgDir(#4a 目录产物保留;下次 build 由 emptyOutDir 清旧)
120
+ rmSync(pkgDir, { recursive: true, force: true });
121
+ mkdirSync(pkgDir, { recursive: true });
122
+ // 2) Vite build 产物(index.bundle.js + assets/**)→ pkgDir(skip 自身防递归)
123
+ copyTree(outDir, pkgDir, new Set([pkgName]));
124
+ // 3) 静态清单从源码 pluginRoot 拷入(缺省忽略)
125
+ // plugin.json 例外——jsonc 归一为严格 JSON 再落 pkgDir:作者源文件可注释/尾逗号(发布向 jsonc 解析),
126
+ // 但分发态清单须干净(壳加载走严格 JSON,H12;source 里注释是作者便利,不该进产物)
127
+ writeFileSync(join(pkgDir, "plugin.json"), `${JSON.stringify(latest, null, 2)}\n`, "utf8");
128
+ for (const decl of collectI18nDecls(latest))
129
+ copyFileInto(root, pkgDir, decl.rel);
130
+ copyFileInto(root, pkgDir, "icon.svg");
131
+ copyFileInto(root, pkgDir, "README.md");
132
+ copyFileInto(root, pkgDir, "CHANGELOG.md");
133
+ const iconRel = latest?.icon;
134
+ if (typeof iconRel === "string" && !iconRel.includes("\\"))
135
+ copyFileInto(root, pkgDir, iconRel);
136
+ // 4) jszip 打包 → 项目根单文件
137
+ const zip = new JSZip();
138
+ zipTree(zip, pkgDir, "");
139
+ const buf = await zip.generateAsync({ type: "nodebuffer", compression: "DEFLATE" });
140
+ const zipPath = join(root, pkgName);
141
+ writeFileSync(zipPath, buf);
142
+ const kb = (buf.byteLength / 1024).toFixed(1);
143
+ console.log(`[linkdesk-plugin-sdk] ✔ ${pkgName}(${kb} KB)→ ${relative(process.cwd(), zipPath)}`);
144
+ },
145
+ };
146
+ return {
147
+ root,
148
+ plugins: [react(), packager],
149
+ build: {
150
+ lib: {
151
+ entry,
152
+ formats: ["es"],
153
+ // 全名含 .js——fileName 不带后缀时 Rollup 不自动补,closeBundle 的 index.bundle.js 断言会扑空
154
+ fileName: () => "index.bundle.js",
155
+ },
156
+ outDir,
157
+ emptyOutDir: true,
158
+ rollupOptions: {
159
+ external: [...DEFAULT_EXTERNAL, ...(options.external ?? [])],
160
+ },
161
+ sourcemap: false,
162
+ minify: "esbuild",
163
+ },
164
+ };
165
+ }
166
+ //# sourceMappingURL=vite-config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vite-config.js","sourceRoot":"","sources":["../src/vite-config.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,46 @@
1
+ {
2
+ "name": "@linkdesk/plugin-sdk",
3
+ "version": "0.1.0",
4
+ "description": "@linkdesk/plugin-sdk——LinkDesk 插件作者 SDK(对标 @types/vscode):window.linkdesk.* 类型 + 一键 build(.linkdesk-plugin) + plugin.json 验证。作者 npm i 后零壳源码依赖。",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "files": [
9
+ "dist",
10
+ "schemas"
11
+ ],
12
+ "sideEffects": false,
13
+ "bin": {
14
+ "linkdesk-plugin-sdk": "./dist/bin.js"
15
+ },
16
+ "publishConfig": {
17
+ "access": "public"
18
+ },
19
+ "engines": {
20
+ "node": ">=20.0.0"
21
+ },
22
+ "scripts": {
23
+ "build": "tsc",
24
+ "typecheck": "tsc -p tsconfig.json --noEmit",
25
+ "prepack": "tsc"
26
+ },
27
+ "dependencies": {
28
+ "@linkdesk/contracts": "^0.1.0",
29
+ "@vitejs/plugin-react": "^4.3.4",
30
+ "ajv": "^8.17.1",
31
+ "jsonc-parser": "^3.3.1",
32
+ "jszip": "^3.10.1",
33
+ "vite": "^6.0.0"
34
+ },
35
+ "devDependencies": {
36
+ "@types/node": "^24.0.0",
37
+ "typescript": "^5.9.0"
38
+ },
39
+ "keywords": [
40
+ "linkdesk",
41
+ "plugin",
42
+ "sdk",
43
+ "vite"
44
+ ],
45
+ "license": "MIT"
46
+ }