dsh-plugin-toolkit 0.8.0 → 0.8.2

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 CHANGED
@@ -1,3 +1,26 @@
1
+ ## 0.8.2
2
+
3
+ `doctor` now refuses the failure that shipped in 0.8.0.
4
+
5
+ - New check `root export`: when a package declares `dsh.bundle`, its root module
6
+ has to be the plugin. A root that is also a `bin` target — a CLI wearing a
7
+ plugin's clothes — is reported, and so is a root module with no `apply`.
8
+ pnpm reports success either way; the row just mounts nothing.
9
+ - `test/doctor.test.mjs` covers both shapes, and a bundle with no root module
10
+ (a subpath plugin) stays legal.
11
+
12
+ ## 0.8.1
13
+
14
+ `0.8.0` installed and then did nothing: the loader starts a plugin by importing
15
+ the bare package name, and `exports["."]` still pointed at the CLI, so the row
16
+ had no `apply` to run.
17
+
18
+ - `exports["."]` is `./index.js`; the CLI keeps `./cli` and `bin`, the testkit
19
+ keeps `./testkit`.
20
+ - `test/entry.test.mjs` guards the shape: the root export is the plugin, the
21
+ patch file names the package once as `- insert:`, and every path
22
+ `package.json` points at is in `files`.
23
+
1
24
  ## 0.8.0
2
25
 
3
26
  The toolkit can now be installed as a plugin, because a tool that cannot be
package/README.md CHANGED
@@ -18,6 +18,15 @@ npx dsh-plugin-toolkit@0.7.0 release --pr --merge # tag, wait for n
18
18
  | changing a panel that is already installed | [`dev`](#2-develop-a-panel) | `mount`, the testkit |
19
19
  | shipping a version | [`verify`](#3-ship-a-version) | `release`, `index` |
20
20
 
21
+ It also installs as a plugin. `dsh plugin add dsh-plugin-toolkit` adds a
22
+ **Plugin doctor** tab to the right sidebar: name the folder that holds a
23
+ plugin's `package.json`, and it runs the same checks `verify` runs and lists
24
+ what would fail — the same answers as the CLI, without leaving the app.
25
+
26
+ ```
27
+ $ dsh plugin add dsh-plugin-toolkit
28
+ ```
29
+
21
30
  ### 1. Write a plugin
22
31
 
23
32
  [`init`](#start-from-a-plugin-that-already-passes) writes the files whose absence shows up as a failed first publish — `engines.dsh`, the patch file, a `files` list that ships the code, a client bundle, a test for it, and the CI that runs both. `doctor` then checks the same things without writing anything, and [`verify`](#prove-the-tarball-works-not-just-the-repository) checks the tarball instead of the working copy.
package/lib/doctor.mjs CHANGED
@@ -138,6 +138,34 @@ export function checkFilesList(manifest) {
138
138
  return null;
139
139
  }
140
140
 
141
+ /**
142
+ * The loader starts a plugin by importing the bare package name. A package that
143
+ * ships both a CLI and a panel is one `exports` edit away from having its root
144
+ * point at the CLI — the row then mounts nothing, and pnpm reports success.
145
+ */
146
+ export function checkRootExport(dir, manifest) {
147
+ if (manifest.dsh === undefined || manifest.dsh === null) return null;
148
+ const root = typeof manifest.exports === 'object' && manifest.exports !== null ? manifest.exports['.'] : undefined;
149
+ const entry = typeof root === 'string' ? root : manifest.main;
150
+ // A bundle that inserts a subpath plugin needs no root module at all.
151
+ if (typeof entry !== 'string') return null;
152
+
153
+ const target = entry.replace(/^\.\//, '');
154
+ const bins = Object.values(manifest.bin ?? {}).filter((value) => typeof value === 'string');
155
+ if (bins.some((bin) => bin.replace(/^\.\//, '') === target) === true) {
156
+ return `exports["."] points at the CLI (${entry}), so the loader's row has no plugin to apply; point it at the module that exports apply`;
157
+ }
158
+
159
+ const file = path.resolve(dir, target);
160
+ if (fs.existsSync(file) === false) return null;
161
+ const source = fs.readFileSync(file, 'utf8');
162
+ const exported = /export\s+(?:async\s+)?function\s+apply\b|export\s+(?:const|let|var)\s+apply\b|export\s*\{[^}]*\bapply\b|\bapply\s*[:=]\s*(?:async\s*)?(?:function|\()/.test(source);
163
+ if (exported === false) {
164
+ return `the root export ${entry} does not export apply, so the loader imports the package and mounts nothing`;
165
+ }
166
+ return null;
167
+ }
168
+
141
169
  /** Runs every check and returns `{ failures, notes }`. */
142
170
  export function doctor(dir) {
143
171
  const { manifest } = readManifest(dir);
@@ -149,6 +177,7 @@ export function doctor(dir) {
149
177
  ['patch file', checkPatchFile(dir, manifest)],
150
178
  ['client entry', checkClientEntry(dir, manifest)],
151
179
  ['files list', checkFilesList(manifest)],
180
+ ['root export', checkRootExport(dir, manifest)],
152
181
  ];
153
182
  const failures = checks.filter(([, result]) => result !== null && typeof result === 'string');
154
183
  return {
package/package.json CHANGED
@@ -1,13 +1,14 @@
1
1
  {
2
2
  "name": "dsh-plugin-toolkit",
3
- "version": "0.8.0",
3
+ "version": "0.8.2",
4
4
  "description": "Preflight, release and test DeepSeek Harness plugins: checks what the host needs, waits for npm to actually serve the version, and keeps the market index honest.",
5
5
  "type": "module",
6
6
  "bin": {
7
7
  "dsh-plugin": "bin/dsh-plugin.mjs"
8
8
  },
9
9
  "exports": {
10
- ".": "./lib/cli.mjs",
10
+ ".": "./index.js",
11
+ "./cli": "./lib/cli.mjs",
11
12
  "./testkit": "./lib/testkit/index.mjs"
12
13
  },
13
14
  "files": [
@@ -74,5 +75,6 @@
74
75
  "bundle": {
75
76
  "patch": "./cordis.patch.yml"
76
77
  }
77
- }
78
+ },
79
+ "main": "index.js"
78
80
  }