zotero-plugin-scaffold 0.2.0-beta.10 → 0.2.0-beta.12

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 CHANGED
@@ -14,132 +14,9 @@ This project is under active development, and some APIs may change. However, it
14
14
 
15
15
  For best practices regarding this package, please refer to [zotero-plugin-template](https://github.com/windingwind/zotero-plugin-template).
16
16
 
17
- ## Using in a blank project
17
+ ## Documentation
18
18
 
19
- <details>
20
-
21
- <summary>WIP: Not yet implemented</summary>
22
-
23
- ```bash
24
- # npm
25
- npx zotero-plugin create
26
- # pnpm
27
- pnpm dlx zotero-plugin create
28
- ```
29
-
30
- </details>
31
-
32
- ## Using in an existing project
33
-
34
- ### 01. Install
35
-
36
- #### From NPM
37
-
38
- ```bash
39
- npm install -D zotero-plugin-scaffold
40
-
41
- yarn add -D zotero-plugin-scaffold
42
-
43
- pnpm add -D zotero-plugin-scaffold
44
- ```
45
-
46
- #### From source code
47
-
48
- ```bash
49
- # clone this repo
50
- git clone https://github.com/northword/zotero-plugin-scaffold.git zotero-plugin-scaffold/
51
- cd zotero-plugin-scaffold/
52
-
53
- # build
54
- pnpm install
55
- pnpm build # or pnpm dev
56
-
57
- # npm link
58
- cd your-plugin-work-dir/
59
- pnpm link ../zotero-plugin-scaffold
60
- ```
61
-
62
- ### 02. Create a config file
63
-
64
- The configuration file needs to be stored in the following location.
65
-
66
- ```bash
67
- zotero-plugin.config.ts # also avaliable in *.js *.mjs *.cjs *.ts
68
- ```
69
-
70
- You can import helper `defineConfig` to get type hints. If no value is specified for an optional property, the default value will be used.
71
-
72
- ```ts
73
- import { defineConfig } from "zotero-plugin-scaffold";
74
-
75
- export default defineConfig({
76
- name: "the plugin name",
77
- id: "the plugin id",
78
- namespace: "the plugin namespace",
79
- build: {
80
- esbuildOptions: [
81
- {
82
- entryPoints: ["src/index.ts"],
83
- bundle: true,
84
- target: "firefox115",
85
- },
86
- ],
87
- },
88
- });
89
- ```
90
-
91
- Full config please refrence in [src/types](./src/types/index.ts).
92
-
93
- ### 03. Create a env file
94
-
95
- This file defines Zotero's runtime configuration such as binary paths, profile paths, and environment variables required for Node scripts to run.
96
-
97
- NOTE: Do not check-in this file to the repository!
98
-
99
- ```bash
100
- .env
101
- ```
102
-
103
- ```ini
104
- # The path of the Zotero binary file.
105
- # The path is `*/Zotero.app/Contents/MacOS/zotero` for macOS.
106
- ZOTERO_PLUGIN_ZOTERO_BIN_PATH = /path/to/zotero.exe
107
-
108
- # The path of the profile used for development.
109
- # Start the profile manager by `/path/to/zotero.exe -p` to create a profile for development.
110
- # @see https://www.zotero.org/support/kb/profile_directory
111
- ZOTERO_PLUGIN_PROFILE_PATH = /path/to/profile
112
- ```
113
-
114
- ### 04. Add scripts to package.json
115
-
116
- ```json
117
- {
118
- "scripts": {
119
- "start": "zotero-plugin serve",
120
- "build": "zotero-plugin build",
121
- "release": "zotero-plugin release"
122
- }
123
- }
124
- ```
125
-
126
- ### 05. Run
127
-
128
- ```bash
129
- pnpm run start
130
- pnpm run build
131
- ```
132
-
133
- ## Using in NodeJS code
134
-
135
- ```ts
136
- import { Build, Config } from "zotero-plugin-scaffold";
137
-
138
- const config = await Config.loadConfig();
139
-
140
- const Builder = new Build(config);
141
- await Builder.run();
142
- ```
19
+ [Read the Docs to Learn More.](https://northword.github.io/zotero-plugin-scaffold)
143
20
 
144
21
  ## Contributing
145
22
 
@@ -156,6 +33,18 @@ pnpm install
156
33
  # and the modified code does not need to be built again.
157
34
  pnpm run dev
158
35
 
36
+ # link local scaffold to your plugin
37
+ cd your-plugin-work-dir/
38
+ pnpm link ../zotero-plugin-scaffold
39
+ cd zotero-plugin-scaffold/
40
+
41
+ ```
42
+
43
+ Now you can make changes to Scaffold and test them in your plugin.
44
+
45
+ When you're done modifying, make sure that you can pass the build and that the code style meets the requirements of this repository.
46
+
47
+ ```bash
159
48
  # Build
160
49
  pnpm run build
161
50
 
@@ -1,11 +1,11 @@
1
1
  import { readFile, stat } from 'node:fs/promises';
2
2
  import { join, basename } from 'node:path';
3
- import { env } from 'node:process';
3
+ import process from 'node:process';
4
4
  import mime from 'mime';
5
5
  import { Octokit } from 'octokit';
6
6
  import { globSync, glob } from 'tinyglobby';
7
7
  import { isCI } from 'std-env';
8
- import { B as Base } from '../shared/zotero-plugin-scaffold.802444a9.mjs';
8
+ import { B as Base } from '../shared/zotero-plugin-scaffold.af8d03c3.mjs';
9
9
  import 'c12';
10
10
  import 'es-toolkit';
11
11
  import 'fs-extra/esm';
@@ -180,10 +180,10 @@ class GitHub extends ReleaseBase {
180
180
  return releaseNote(this.ctx);
181
181
  }
182
182
  getClient() {
183
- if (!env.GITHUB_TOKEN)
183
+ if (!process.env.GITHUB_TOKEN)
184
184
  throw new Error("No GITHUB_TOKEN.");
185
185
  const client = new Octokit({
186
- auth: env.GITHUB_TOKEN,
186
+ auth: process.env.GITHUB_TOKEN,
187
187
  userAgent: "zotero-plugin-scaffold"
188
188
  });
189
189
  return client;
package/dist/cli.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
- import process, { exit, env } from 'node:process';
2
+ import process from 'node:process';
3
3
  import { Command } from '@commander-js/extra-typings';
4
- import { E as ExitSignals, l as logger, C as Config, a as Build, S as Serve, T as Test, R as Release } from './shared/zotero-plugin-scaffold.802444a9.mjs';
4
+ import { E as ExitSignals, l as logger, C as Config, a as Build, S as Serve, T as Test, R as Release } from './shared/zotero-plugin-scaffold.af8d03c3.mjs';
5
5
  import { readFile, writeFile } from 'node:fs/promises';
6
6
  import { pathExists, ensureFile } from 'fs-extra';
7
7
  import tinyUpdateNotifier from 'tiny-update-notifier';
@@ -31,7 +31,7 @@ import 'xvfb-ts';
31
31
 
32
32
  const name = "zotero-plugin-scaffold";
33
33
  const type = "module";
34
- const version = "0.2.0-beta.10";
34
+ const version = "0.2.0-beta.12";
35
35
  const packageManager = "pnpm@9.15.0";
36
36
  const description = "A scaffold for Zotero plugin development.";
37
37
  const author = "northword";
@@ -80,7 +80,10 @@ const scripts = {
80
80
  "lint:fix": "eslint . --fix",
81
81
  release: "bumpp --commit \"chore(release): publish v%s\"",
82
82
  "update:deps": "pnpx taze minor -w -f -l",
83
- test: "echo \"Error: no test specified\" && exit 1"
83
+ test: "echo \"Error: no test specified\" && exit 1",
84
+ "docs:dev": "vitepress dev docs",
85
+ "docs:build": "vitepress build docs",
86
+ "docs:preview": "vitepress preview docs"
84
87
  };
85
88
  const publishConfig = {
86
89
  access: "public",
@@ -125,7 +128,8 @@ const devDependencies = {
125
128
  eslint: "^9.16.0",
126
129
  "eslint-plugin-format": "^0.1.3",
127
130
  typescript: "^5.7.2",
128
- unbuild: "^2.0.0"
131
+ unbuild: "^2.0.0",
132
+ vitepress: "^1.5.0"
129
133
  };
130
134
  const pkg = {
131
135
  name: name,
@@ -186,11 +190,11 @@ async function main() {
186
190
  var _a;
187
191
  const { name, version } = pkg;
188
192
  updateNotifier(name, version);
189
- (_a = env).NODE_ENV ?? (_a.NODE_ENV = "development");
193
+ (_a = process.env).NODE_ENV ?? (_a.NODE_ENV = "development");
190
194
  const cli = new Command();
191
195
  cli.version(version).usage("<command> [options]");
192
196
  cli.command("build").description("Build the plugin").option("--dev", "Builds the plugin in dev mode").option("--dist <dir>", "The relative path for the new output directory (default: build)").action((options) => {
193
- env.NODE_ENV = options.dev ? "development" : "production";
197
+ process.env.NODE_ENV = options.dev ? "development" : "production";
194
198
  Config.loadConfig({
195
199
  dist: options.dist
196
200
  }).then((ctx) => new Build(ctx).run());
@@ -199,7 +203,7 @@ async function main() {
199
203
  Config.loadConfig({}).then((ctx) => new Serve(ctx).run());
200
204
  });
201
205
  cli.command("test").description("Run tests").option("--abort-on-fail", "Abort the test suite on first failure").option("--exit-on-finish", "Exit the test suite after all tests have run").action((options) => {
202
- env.NODE_ENV = "test";
206
+ process.env.NODE_ENV = "test";
203
207
  Config.loadConfig({}).then((ctx) => {
204
208
  if (options.abortOnFail) {
205
209
  ctx.test.abortOnFail = true;
@@ -214,7 +218,7 @@ async function main() {
214
218
  logger.error("The create not yet implemented");
215
219
  });
216
220
  cli.command("release").description("Release the plugin").argument("[version]", "Target version: major, minor, patch, pre*, or specify version").option("--preid <preid>", "ID for prerelease").option("-y, --yes", "Skip confirmation").action(async (version2, options) => {
217
- env.NODE_ENV = "production";
221
+ process.env.NODE_ENV = "production";
218
222
  Config.loadConfig({
219
223
  release: {
220
224
  bumpp: {
@@ -237,7 +241,7 @@ main().then(() => {
237
241
  process.on("uncaughtException", onError);
238
242
  function onError(err) {
239
243
  logger.error(err);
240
- exit(1);
244
+ process.exit(1);
241
245
  }
242
246
 
243
247
  export { main as default };
package/dist/index.d.mts CHANGED
@@ -22,6 +22,7 @@ declare class Log {
22
22
  constructor(level?: LOG_LEVEL);
23
23
  static getInstance(): Log;
24
24
  setLogLevel(level: LogLevelType): void;
25
+ get level(): number;
25
26
  private formatArgs;
26
27
  private logArgs;
27
28
  log(...args: any[]): void;
package/dist/index.d.ts CHANGED
@@ -22,6 +22,7 @@ declare class Log {
22
22
  constructor(level?: LOG_LEVEL);
23
23
  static getInstance(): Log;
24
24
  setLogLevel(level: LogLevelType): void;
25
+ get level(): number;
25
26
  private formatArgs;
26
27
  private logArgs;
27
28
  log(...args: any[]): void;
package/dist/index.mjs CHANGED
@@ -1,4 +1,4 @@
1
- export { a as Build, C as Config, R as Release, S as Serve, T as Test, d as defineConfig } from './shared/zotero-plugin-scaffold.802444a9.mjs';
1
+ export { a as Build, C as Config, R as Release, S as Serve, T as Test, d as defineConfig } from './shared/zotero-plugin-scaffold.af8d03c3.mjs';
2
2
  import 'c12';
3
3
  import 'es-toolkit';
4
4
  import 'fs-extra/esm';
@@ -2,7 +2,7 @@ import { loadConfig as loadConfig$1 } from 'c12';
2
2
  import { isPlainObject, kebabCase, mapValues, toMerged, escapeRegExp, delay, debounce } from 'es-toolkit';
3
3
  import { readJsonSync, emptyDir, copy, readJSON, outputJSON, move, writeJson, pathExists, outputFile, remove } from 'fs-extra/esm';
4
4
  import { createHooks } from 'hookable';
5
- import process, { env, platform, exit, cwd } from 'node:process';
5
+ import process from 'node:process';
6
6
  import readline from 'node:readline';
7
7
  import chalk from 'chalk';
8
8
  import { isDebug, isCI, isWindows, isMacOS, isLinux } from 'std-env';
@@ -43,8 +43,8 @@ const _Log = class _Log {
43
43
  __publicField$7(this, "logLevel");
44
44
  if (isDebug)
45
45
  this.logLevel = 0 /* trace */;
46
- else if (env.ZOTERO_PLUGIN_LOG_LEVEL)
47
- this.logLevel = LOG_LEVEL[env.ZOTERO_PLUGIN_LOG_LEVEL];
46
+ else if (process.env.ZOTERO_PLUGIN_LOG_LEVEL)
47
+ this.logLevel = LOG_LEVEL[process.env.ZOTERO_PLUGIN_LOG_LEVEL];
48
48
  else if (level)
49
49
  this.logLevel = level;
50
50
  else
@@ -59,6 +59,9 @@ const _Log = class _Log {
59
59
  setLogLevel(level) {
60
60
  this.logLevel = LOG_LEVEL[level];
61
61
  }
62
+ get level() {
63
+ return this.logLevel;
64
+ }
62
65
  formatArgs(arg) {
63
66
  if (typeof arg === "string")
64
67
  return arg;
@@ -333,7 +336,7 @@ class Build extends Base {
333
336
  super(ctx);
334
337
  __publicField$5(this, "buildTime");
335
338
  __publicField$5(this, "isPreRelease");
336
- (_a = env).NODE_ENV ?? (_a.NODE_ENV = "production");
339
+ (_a = process.env).NODE_ENV ?? (_a.NODE_ENV = "production");
337
340
  this.buildTime = "";
338
341
  this.isPreRelease = this.ctx.version.includes("-");
339
342
  }
@@ -345,7 +348,7 @@ class Build extends Base {
345
348
  const t = /* @__PURE__ */ new Date();
346
349
  this.buildTime = dateFormat("YYYY-mm-dd HH:MM:SS", t);
347
350
  this.logger.info(
348
- `Building version ${chalk.blue(version)} to ${chalk.blue(dist)} at ${chalk.blue(this.buildTime)} in ${chalk.blue(env.NODE_ENV)} mode.`
351
+ `Building version ${chalk.blue(version)} to ${chalk.blue(dist)} at ${chalk.blue(this.buildTime)} in ${chalk.blue(process.env.NODE_ENV)} mode.`
349
352
  );
350
353
  await this.ctx.hooks.callHook("build:init", this.ctx);
351
354
  await emptyDir(dist);
@@ -362,7 +365,7 @@ class Build extends Base {
362
365
  this.logger.tip("Bundling scripts");
363
366
  await this.esbuild();
364
367
  await this.ctx.hooks.callHook("build:bundle", this.ctx);
365
- if (env.NODE_ENV === "production") {
368
+ if (process.env.NODE_ENV === "production") {
366
369
  this.logger.tip("Packing plugin");
367
370
  await this.pack();
368
371
  await this.ctx.hooks.callHook("build:pack", this.ctx);
@@ -733,7 +736,7 @@ ${changelog}
733
736
 
734
737
  function isRunning(query) {
735
738
  let cmd = "";
736
- switch (platform) {
739
+ switch (process.platform) {
737
740
  case "win32":
738
741
  cmd = `tasklist`;
739
742
  break;
@@ -1069,6 +1072,7 @@ class RemoteFirefox {
1069
1072
  await this.client.connect(port);
1070
1073
  return this.client;
1071
1074
  } catch (error) {
1075
+ logger.fail("Failed to connecte to RDP client, retry...");
1072
1076
  if (isErrorWithCode("ECONNREFUSED", error)) {
1073
1077
  await new Promise((resolve) => setTimeout(resolve, RETRY_INTERVAL));
1074
1078
  lastError = error;
@@ -1275,18 +1279,17 @@ class ZoteroRunner {
1275
1279
  }
1276
1280
  const remotePort = await findFreeTcpPort();
1277
1281
  args.push("-start-debugger-server", String(remotePort));
1278
- const defaultFirefoxEnv = {
1282
+ logger.debug("Zotero start args: ", args);
1283
+ const env = {
1284
+ ...process.env,
1279
1285
  XPCOM_DEBUG_BREAK: "stack",
1280
1286
  NS_TRACE_MALLOC_DISABLE_STACKS: "1"
1281
1287
  };
1282
- this.zotero = spawn(this.options.binaryPath, args, {
1283
- env: {
1284
- ...env,
1285
- ...defaultFirefoxEnv
1286
- }
1287
- });
1288
+ this.zotero = spawn(this.options.binaryPath, args, { env });
1289
+ logger.debug("Zotero started, pid:", this.zotero.pid);
1288
1290
  this.zotero.stdout?.on("data", (_data) => {
1289
1291
  });
1292
+ logger.debug("Connecting to the remote Firefox debugger...");
1290
1293
  await this.remoteFirefox.connect(remotePort);
1291
1294
  logger.debug(`Connected to the remote Firefox debugger on port: ${remotePort}`);
1292
1295
  }
@@ -1412,8 +1415,8 @@ class ZoteroRunner {
1412
1415
  function killZotero() {
1413
1416
  function kill() {
1414
1417
  try {
1415
- if (env.ZOTERO_PLUGIN_KILL_COMMAND) {
1416
- execSync(env.ZOTERO_PLUGIN_KILL_COMMAND);
1418
+ if (process.env.ZOTERO_PLUGIN_KILL_COMMAND) {
1419
+ execSync(process.env.ZOTERO_PLUGIN_KILL_COMMAND);
1417
1420
  } else if (isWindows) {
1418
1421
  execSync("taskkill /f /im zotero.exe");
1419
1422
  } else if (isMacOS) {
@@ -1457,7 +1460,7 @@ class Serve extends Base {
1457
1460
  });
1458
1461
  __publicField$1(this, "onZoteroExit", (_code, _signal) => {
1459
1462
  this.logger.info(`Zotero terminated.`);
1460
- exit();
1463
+ process.exit();
1461
1464
  });
1462
1465
  (_a = process.env).NODE_ENV ?? (_a.NODE_ENV = "development");
1463
1466
  this.builder = new Build(ctx);
@@ -1528,7 +1531,7 @@ class Serve extends Base {
1528
1531
  get zoteroBinPath() {
1529
1532
  if (this._zoteroBinPath)
1530
1533
  return this._zoteroBinPath;
1531
- this._zoteroBinPath = env.ZOTERO_PLUGIN_ZOTERO_BIN_PATH;
1534
+ this._zoteroBinPath = process.env.ZOTERO_PLUGIN_ZOTERO_BIN_PATH;
1532
1535
  if (!this._zoteroBinPath || !existsSync(this._zoteroBinPath))
1533
1536
  throw new Error("The Zotero binary not found.");
1534
1537
  return this._zoteroBinPath;
@@ -1536,13 +1539,13 @@ class Serve extends Base {
1536
1539
  get profilePath() {
1537
1540
  if (this._profilePath)
1538
1541
  return this._profilePath;
1539
- this._profilePath = env.ZOTERO_PLUGIN_PROFILE_PATH;
1542
+ this._profilePath = process.env.ZOTERO_PLUGIN_PROFILE_PATH;
1540
1543
  if (!this._profilePath || !existsSync(this._profilePath))
1541
1544
  throw new Error("The Zotero profile not found.");
1542
1545
  return this._profilePath;
1543
1546
  }
1544
1547
  get dataDir() {
1545
- return env.ZOTERO_PLUGIN_DATA_DIR ?? "";
1548
+ return process.env.ZOTERO_PLUGIN_DATA_DIR ?? "";
1546
1549
  }
1547
1550
  }
1548
1551
 
@@ -1551,42 +1554,63 @@ async function saveResource(url, path) {
1551
1554
  await outputFile(path, await res.text());
1552
1555
  }
1553
1556
 
1557
+ function isPackageInstalled(packageName) {
1558
+ try {
1559
+ execSync(`dpkg-query -W ${packageName}`, { stdio: "ignore" });
1560
+ return true;
1561
+ } catch {
1562
+ return false;
1563
+ }
1564
+ }
1565
+ function installPackage(packageName) {
1566
+ const debug = isDebug || logger.level <= LOG_LEVEL.debug;
1567
+ try {
1568
+ logger.debug(`Installing ${packageName}...`);
1569
+ execSync(`sudo apt update && sudo apt install -y ${packageName}`, { stdio: debug ? "inherit" : "pipe" });
1570
+ logger.debug(`${packageName} installed successfully.`);
1571
+ } catch (error) {
1572
+ logger.fail(`Failed to install ${packageName}.`, error);
1573
+ throw error;
1574
+ }
1575
+ }
1576
+ function checkAndInstallDependencies(packages) {
1577
+ packages.forEach((pkg) => {
1578
+ if (isPackageInstalled(pkg)) {
1579
+ logger.debug(`${pkg} is already installed.`);
1580
+ } else {
1581
+ installPackage(pkg);
1582
+ }
1583
+ });
1584
+ }
1554
1585
  async function installXvfb() {
1555
- logger.debug("Installing xvfb...");
1556
1586
  if (!isLinux) {
1557
1587
  logger.error("Unsupported platform. Please install Xvfb manually.");
1558
- exit(1);
1588
+ return;
1559
1589
  }
1560
- try {
1561
- execSync("which xvfb", { stdio: "ignore" });
1562
- } catch {
1563
- try {
1564
- const osId = execSync("cat /etc/os-release | grep '^ID='").toString();
1565
- if (osId.includes("ubuntu") || osId.includes("debian")) {
1566
- logger.debug("Detected Ubuntu/Debian. Installing Xvfb...");
1567
- execSync("sudo apt-get update && sudo apt-get install -y xvfb", { stdio: "pipe" });
1568
- } else if (osId.includes("centos") || osId.includes("rhel")) {
1569
- logger.debug("Detected CentOS/RHEL. Installing Xvfb...");
1570
- execSync("sudo yum install -y xorg-x11-server-Xvfb", { stdio: "pipe" });
1571
- } else {
1572
- throw new Error("Unsupported Linux distribution.");
1573
- }
1574
- logger.debug("Xvfb installation completed.");
1575
- } catch (error) {
1576
- logger.error("Failed to install Xvfb:", error);
1577
- exit(1);
1578
- }
1590
+ const osId = execSync("cat /etc/os-release | grep '^ID='").toString();
1591
+ if (!(osId.includes("ubuntu") || osId.includes("debian"))) {
1592
+ logger.error("Unsupported Linux distribution.");
1593
+ return;
1594
+ }
1595
+ checkAndInstallDependencies(["xvfb", "x11-xkb-utils", "xkb-data"]);
1596
+ }
1597
+ async function installDepsForUbuntu24() {
1598
+ const version = execSync("cat /etc/os-release | grep '^VERSION_ID='").toString();
1599
+ if (!version.includes("24")) {
1600
+ logger.error("Skip to install deps due to version not 24.04.");
1601
+ return;
1579
1602
  }
1603
+ checkAndInstallDependencies(["libasound2t64", "libdbus-glib-1-2"]);
1580
1604
  }
1581
1605
  async function installZoteroLinux() {
1582
- logger.debug("Installing Zotero...");
1583
- try {
1584
- execSync("wget -O zotero.tar.bz2 'https://www.zotero.org/download/client/dl?platform=linux-x86_64&channel=beta'", { stdio: "pipe" });
1585
- execSync("tar -xvf zotero.tar.bz2", { stdio: "pipe" });
1586
- } catch (e) {
1587
- logger.error(e);
1588
- throw new Error("Zotero extracted failed");
1606
+ if (process.env.ZOTERO_PLUGIN_ZOTERO_BIN_PATH) {
1607
+ logger.debug("Local Zotero found, skip to download.");
1608
+ return;
1589
1609
  }
1610
+ logger.debug("Installing Zotero...");
1611
+ execSync("wget -O zotero.tar.bz2 'https://www.zotero.org/download/client/dl?platform=linux-x86_64&channel=beta'", { stdio: "pipe" });
1612
+ execSync("tar -xvf zotero.tar.bz2", { stdio: "pipe" });
1613
+ process.env.ZOTERO_PLUGIN_ZOTERO_BIN_PATH = `${process.cwd()}/Zotero_linux-x86_64/zotero`;
1590
1614
  }
1591
1615
 
1592
1616
  var __defProp = Object.defineProperty;
@@ -1608,16 +1632,16 @@ class Test extends Base {
1608
1632
  this.ctx.hooks.callHook("test:exit", this.ctx);
1609
1633
  if (code === 1) {
1610
1634
  this.logger.error("Test run failed");
1611
- exit(1);
1635
+ process.exit(1);
1612
1636
  } else if (code === "SIGINT") {
1613
1637
  this.logger.info("Tester shutdown by user request");
1614
- exit();
1638
+ process.exit();
1615
1639
  } else {
1616
1640
  this.logger.success("Test run completed successfully");
1617
- exit();
1641
+ process.exit();
1618
1642
  }
1619
1643
  });
1620
- (_a = env).NODE_ENV ?? (_a.NODE_ENV = "test");
1644
+ (_a = process.env).NODE_ENV ?? (_a.NODE_ENV = "test");
1621
1645
  this.builder = new Build(ctx);
1622
1646
  if (isCI) {
1623
1647
  this.ctx.test.exitOnFinish = true;
@@ -2071,8 +2095,8 @@ class Test extends Base {
2071
2095
  }
2072
2096
  async startZoteroHeadless() {
2073
2097
  await installXvfb();
2098
+ await installDepsForUbuntu24();
2074
2099
  await installZoteroLinux();
2075
- env.ZOTERO_PLUGIN_ZOTERO_BIN_PATH = `${cwd()}/Zotero_linux-x86_64/zotero`;
2076
2100
  const xvfb = new Xvfb({
2077
2101
  timeout: 2e3
2078
2102
  });
@@ -2098,9 +2122,9 @@ class Test extends Base {
2098
2122
  await this.runner.run();
2099
2123
  }
2100
2124
  get zoteroBinPath() {
2101
- if (!env.ZOTERO_PLUGIN_ZOTERO_BIN_PATH)
2125
+ if (!process.env.ZOTERO_PLUGIN_ZOTERO_BIN_PATH)
2102
2126
  throw new Error("No Zotero Found.");
2103
- return env.ZOTERO_PLUGIN_ZOTERO_BIN_PATH;
2127
+ return process.env.ZOTERO_PLUGIN_ZOTERO_BIN_PATH;
2104
2128
  }
2105
2129
  get profilePath() {
2106
2130
  return `.scaffold/test/profile`;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "zotero-plugin-scaffold",
3
3
  "type": "module",
4
- "version": "0.2.0-beta.10",
4
+ "version": "0.2.0-beta.12",
5
5
  "description": "A scaffold for Zotero plugin development.",
6
6
  "author": "northword",
7
7
  "license": "AGPL-3.0-or-later",
@@ -85,7 +85,8 @@
85
85
  "eslint": "^9.16.0",
86
86
  "eslint-plugin-format": "^0.1.3",
87
87
  "typescript": "^5.7.2",
88
- "unbuild": "^2.0.0"
88
+ "unbuild": "^2.0.0",
89
+ "vitepress": "^1.5.0"
89
90
  },
90
91
  "scripts": {
91
92
  "dev": "unbuild --stub",
@@ -94,6 +95,9 @@
94
95
  "lint:fix": "eslint . --fix",
95
96
  "release": "bumpp --commit \"chore(release): publish v%s\"",
96
97
  "update:deps": "pnpx taze minor -w -f -l",
97
- "test": "echo \"Error: no test specified\" && exit 1"
98
+ "test": "echo \"Error: no test specified\" && exit 1",
99
+ "docs:dev": "vitepress dev docs",
100
+ "docs:build": "vitepress build docs",
101
+ "docs:preview": "vitepress preview docs"
98
102
  }
99
103
  }