dsh-plugin-toolkit 0.7.1 → 0.7.3

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,29 @@
1
+ ## 0.7.3
2
+
3
+ The scaffold was two minor versions behind the tool that wrote it.
4
+
5
+ - `init` pins the toolkit it was generated by (`^<its own version>`) instead of
6
+ a number typed once, so `npm install` in a fresh scaffold gets the version
7
+ whose README you just read.
8
+ - The generated workflow uses the composite action with `verify` instead of
9
+ `npx dsh-plugin-toolkit@0.5.0 doctor .` — `doctor` reads the working copy and
10
+ cannot see that `files` left the client bundle out.
11
+ - `test/init.test.mjs` guards both: the pinned range must equal the running
12
+ version, and the workflow must not fall back to `doctor`.
13
+
14
+ ## 0.7.2
15
+
16
+ `verify` runs from npx's cache, so it has to find jsdom and React where the
17
+ plugin is, not where the tool is.
18
+
19
+ - `testkit` resolves `jsdom`, `react` and `react-dom` from the plugin folder
20
+ first, then the working directory, then the tool's own dependencies.
21
+ - `verify` passes the plugin folder through, so
22
+ `uses: WJDdidi-1/dsh-plugin-toolkit@v1` works in a workflow that ran `npm ci`
23
+ in the plugin: before this, the action failed with
24
+ `testkit needs a DOM: npm install --save-dev jsdom react react-dom` even
25
+ though the plugin declared them.
26
+
1
27
  ## 0.7.1
2
28
 
3
29
  The action now checks the tarball, which is the only thing a user installs.
package/README.md CHANGED
@@ -169,7 +169,7 @@ AIignore No workspace yet Could not read the report …
169
169
 
170
170
  ## Start from the example
171
171
 
172
- [`dsh-plugin-example`](https://github.com/WJDdidi-1/dsh-plugin-example) is a ~70-line plugin: one sidebar tab, two languages, one behaviour test, CI that runs `doctor` through the composite action. It is the shape this toolkit expects, so it is also the fastest way to see whether the toolkit fits your plugin.
172
+ [`dsh-plugin-example`](https://github.com/WJDdidi-1/dsh-plugin-example) is a ~70-line plugin: one sidebar tab, two languages, one behaviour test, CI that runs `verify` through the composite action. It is the shape this toolkit expects, so it is also the fastest way to see whether the toolkit fits your plugin. It is a GitHub template too: **Use this template** starts you on a plugin whose first publish already passes.
173
173
 
174
174
  ```sh
175
175
  dsh plugin add dsh-plugin-example
package/lib/init.mjs CHANGED
@@ -6,7 +6,14 @@
6
6
  */
7
7
  import fs from 'node:fs';
8
8
  import path from 'node:path';
9
+ import { fileURLToPath } from 'node:url';
9
10
 
11
+ // The scaffold pins the toolkit it was generated by, not a number typed once:
12
+ // a scaffold that installs two minor versions behind is the first thing a new
13
+ // author sees and the last thing anyone updates.
14
+ const VERSION = JSON.parse(
15
+ fs.readFileSync(path.join(path.dirname(fileURLToPath(import.meta.url)), '..', 'package.json'), 'utf8'),
16
+ ).version;
10
17
  const NPM_NAME = /^(?:@[a-z0-9][a-z0-9._-]*\/)?[a-z0-9][a-z0-9._-]*$/;
11
18
 
12
19
  export function checkName(name) {
@@ -29,7 +36,7 @@ function manifest({ name, title, version }) {
29
36
  files: ['index.js', 'client.js', 'cordis.patch.yml', 'README.md'],
30
37
  scripts: { test: 'node --test', doctor: 'dsh-plugin doctor .' },
31
38
  devDependencies: {
32
- 'dsh-plugin-toolkit': '^0.5.0',
39
+ 'dsh-plugin-toolkit': `^${VERSION}`,
33
40
  jsdom: '^26.1.0',
34
41
  react: '^18.3.1',
35
42
  'react-dom': '^18.3.1',
@@ -136,7 +143,7 @@ export function files({ name, title = 'My panel', version = '0.1.0' }) {
136
143
  'test/panel.test.mjs': test({ name, title }),
137
144
  'README.md': `# ${slug}\n\n${title} — a panel for DeepSeek Harness.\n\n## Requirements\n\n| What | Version | Why |\n| --- | --- | --- |\n| DeepSeek Harness | >= 0.2.0 | provides \`sidebarRightTabs\` |\n| Node | >= 18 | the host half |\n\n## Develop\n\n\`\`\`sh\nnpm install\nnpm test # mounts client.js in Node\nnpm run doctor # checks the manifest before you publish\n\`\`\`\n\n## Install\n\n\`\`\`sh\ndsh plugin add ${name}\n\`\`\`\n`,
138
145
  '.gitignore': 'node_modules/\n',
139
- '.github/workflows/ci.yml': `name: ci\n\non:\n push:\n branches: [main]\n pull_request:\n\njobs:\n test:\n runs-on: ubuntu-latest\n strategy:\n matrix:\n node: [18, 20, 22]\n steps:\n - uses: actions/checkout@v4\n - uses: actions/setup-node@v4\n with:\n node-version: \${{ matrix.node }}\n - run: npm install\n - run: npm test\n - run: npx --yes dsh-plugin-toolkit@0.5.0 doctor .\n`,
146
+ '.github/workflows/ci.yml': `name: ci\n\non:\n push:\n branches: [main]\n pull_request:\n\njobs:\n test:\n runs-on: ubuntu-latest\n strategy:\n matrix:\n node: [18, 20, 22]\n steps:\n - uses: actions/checkout@v4\n - uses: actions/setup-node@v4\n with:\n node-version: \${{ matrix.node }}\n - run: npm install\n - run: npm test\n # verify, not doctor: doctor reads the working copy, so a \`files\` list\n # that forgets client.js passes it and the installed plugin has no panel.\n - uses: WJDdidi-1/dsh-plugin-toolkit@v1\n with:\n dir: .\n version: ${VERSION}\n`,
140
147
  };
141
148
  }
142
149
 
@@ -6,15 +6,34 @@
6
6
  * jsdom plus the real React gives us the same arrangement in Node, which is what
7
7
  * makes panel behaviour assertable in CI instead of by hand.
8
8
  */
9
- let JSDOM;
10
- let VirtualConsole;
11
- try {
12
- ({ JSDOM, VirtualConsole } = await import('jsdom'));
13
- } catch {
14
- throw new Error('testkit needs a DOM: npm install --save-dev jsdom react react-dom');
9
+ import { createRequire } from 'node:module';
10
+ import path from 'node:path';
11
+ import { fileURLToPath } from 'node:url';
12
+
13
+ /**
14
+ * Resolve a dependency the way the *plugin under test* would.
15
+ *
16
+ * `npx dsh-plugin-toolkit verify` installs the tool in npx's own cache, so a
17
+ * bare `import('jsdom')` looks there and not in the project that declared it.
18
+ * The plugin folder is the authority on what is installed.
19
+ */
20
+ export function requireFrom(name, resolveFrom) {
21
+ const anchors = [resolveFrom, process.cwd(), path.dirname(fileURLToPath(import.meta.url))]
22
+ .filter((anchor) => typeof anchor === 'string' && anchor !== '');
23
+ for (const anchor of anchors) {
24
+ try {
25
+ return createRequire(path.join(anchor, 'noop.js'))(name);
26
+ } catch {
27
+ // Try the next place a dependency could legitimately live.
28
+ }
29
+ }
30
+ throw new Error(
31
+ `testkit needs ${name} where the plugin is installed: cd ${anchors[0]} && npm install --save-dev jsdom react react-dom`,
32
+ );
15
33
  }
16
34
 
17
- export function createDom({ html = '<!doctype html><html><body><div id="root"></div></body></html>' } = {}) {
35
+ export function createDom({ html = '<!doctype html><html><body><div id="root"></div></body></html>', resolveFrom } = {}) {
36
+ const { JSDOM, VirtualConsole } = requireFrom('jsdom', resolveFrom);
18
37
  // jsdom's CSS parser predates nesting and `:has()`, so it complains about
19
38
  // stylesheets it cannot parse. That is a jsdom limit, not a panel bug.
20
39
  const virtualConsole = new VirtualConsole();
@@ -4,7 +4,7 @@
4
4
  * The helpers are deliberately blunt — click, type, press — because that is the
5
5
  * vocabulary a bug report uses. Anything cleverer hides which selector was hit.
6
6
  */
7
- import { createDom, installGlobals } from './env.mjs';
7
+ import { createDom, installGlobals, requireFrom } from './env.mjs';
8
8
  import { createHost } from './host.mjs';
9
9
  import { loadBundle } from './load.mjs';
10
10
  import { compare, write } from './snapshot.mjs';
@@ -37,24 +37,29 @@ export async function mountClient(options) {
37
37
  container = 'root',
38
38
  } = options;
39
39
 
40
- const { dom, window, document } = createDom();
40
+ // React is resolved from the plugin's folder too: when the tool runs from
41
+ // npx's cache, its own dependencies are not the ones the panel was written
42
+ // against.
43
+ const resolveFrom = options.resolveFrom ?? process.cwd();
44
+ const need = (name) => requireFrom(name, resolveFrom);
45
+
46
+ const { dom, window, document } = createDom({ resolveFrom });
41
47
  const restoreGlobals = installGlobals(window, document);
42
48
 
43
- const need = async (name) => {
49
+ const React = need('react');
50
+ const reactDom = need('react-dom/client');
51
+ const jsxRuntime = need('react/jsx-runtime');
52
+ const jsxDevRuntime = (() => {
44
53
  try {
45
- return await import(name);
54
+ return need('react/jsx-dev-runtime');
46
55
  } catch {
47
- throw new Error(`testkit needs ${name}: npm install --save-dev jsdom react react-dom`);
56
+ return jsxRuntime;
48
57
  }
49
- };
50
- const React = await need('react');
51
- const reactDom = await need('react-dom/client');
52
- const jsxRuntime = await need('react/jsx-runtime');
53
- const jsxDevRuntime = await import('react/jsx-dev-runtime').catch(() => jsxRuntime);
58
+ })();
54
59
 
55
60
  const modules = {
56
61
  react: React.default ?? React,
57
- 'react-dom': await import('react-dom'),
62
+ 'react-dom': need('react-dom'),
58
63
  'react-dom/client': reactDom.default ?? reactDom,
59
64
  'react/jsx-runtime': jsxRuntime,
60
65
  'react/jsx-dev-runtime': jsxDevRuntime,
package/lib/verify.mjs CHANGED
@@ -121,7 +121,9 @@ export async function verify({ dir, mount = true }) {
121
121
  }
122
122
  try {
123
123
  const { mountClient } = await import('./testkit/index.mjs');
124
- const panel = await mountClient({ file: entryFile });
124
+ // Resolve jsdom/React from the plugin's own folder: `verify` may be
125
+ // running out of npx's cache, whose dependencies are not the panel's.
126
+ const panel = await mountClient({ file: entryFile, resolveFrom: dir });
125
127
  const text = panel.text();
126
128
  await panel.unmount();
127
129
  return { ok: true, stage: 'mount', failures: [], files: packed.files, size: packed.size, installed, text };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-plugin-toolkit",
3
- "version": "0.7.1",
3
+ "version": "0.7.3",
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": {