From 3ba1f9d40e25eaec38f5808d42f28522cfac0fdf Mon Sep 17 00:00:00 2001 From: windingwind Date: Mon, 12 Jun 2023 23:24:42 +0800 Subject: [PATCH] add: auto hot reload support --- .vscode/launch.json | 7 ++++++ README.md | 26 +++++++++++++++++++- addon/bootstrap.js | 3 +++ package.json | 6 +++-- scripts/reload.mjs | 42 +++++++++++++++++++++++++++++++++ scripts/stop.mjs | 22 ----------------- scripts/zotero-cmd-default.json | 1 - 7 files changed, 81 insertions(+), 26 deletions(-) create mode 100644 scripts/reload.mjs diff --git a/.vscode/launch.json b/.vscode/launch.json index 2c524b2..1663cf3 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -4,6 +4,13 @@ // 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387 "version": "0.2.0", "configurations": [ + { + "type": "node", + "request": "launch", + "name": "StartDev", + "runtimeExecutable": "npm", + "runtimeArgs": ["run", "start-watch"] + }, { "type": "node", "request": "launch", diff --git a/README.md b/README.md index 19c9ffc..63ad870 100644 --- a/README.md +++ b/README.md @@ -42,6 +42,7 @@ If you are using this repo, I recommended that you put this badge ([![Using Zote - Event-driven, functional programming, under extensive skeleton; - Simple and user-friendly, works out-of-the-box. +- ⭐[New!]Auto hot reload! Whenever the source code is modified, automatically compile and reload. [See here→](#auto-hot-reload) - Abundant examples in `src/modules/examples.ts`, covering most of the commonly used APIs in plugins(using [zotero-plugin-toolkit](https://github.com/windingwind/zotero-plugin-toolkit)); - TypeScript support: - Full type definition support for the whole Zotero project, which is written in JavaScript(using [zotero-types](https://github.com/windingwind/zotero-types)); @@ -50,7 +51,7 @@ If you are using this repo, I recommended that you put this badge ([![Using Zote - Automatically generate/update plugin id/version, update configrations, and set environment variables(`development/production`); - Automatically build and reload code in Zotero; - Automatically release to GitHub(using [release-it](https://github.com/release-it/release-it)); -- ⭐[New!]Compatibilities for Zotero 6 & Zotero 7.(using [zotero-plugin-toolkit](https://github.com/windingwind/zotero-plugin-toolkit)) +- Compatibilities for Zotero 6 & Zotero 7.(using [zotero-plugin-toolkit](https://github.com/windingwind/zotero-plugin-toolkit)) ## Examples @@ -211,6 +212,29 @@ vim ./scripts/zotero-cmd.json 11. Click "Inspect Main Process" +### Auto Hot Reload + +Tired of endless restarting? Forget about it! + +1. Run `npm run start-watch`. (If Zotero is already running, use `npm run watch`) +2. Coding. (Yes, that's all) + +When file changes are detected in `src` or `addon`, the plugin will do automatically compiled and reloaded. + + +
+💡 Steps to add this feature to an existing plugin + +1. Add `if (reason == ADDON_DISABLE) {Services.obs.notifyObservers(null, "startupcache-invalidate", null);}` to `shutdown()` in the `addon/bootstrap.js` +2. Copy `scripts/reload.mjs` +3. Copy `reload`, `watch`, and `start-watch` commands in `package.json` +4. Run `npm install --save-dev chokidar-cli` +5. Done. + +
+ + + ### Debug in Zotero You can also: diff --git a/addon/bootstrap.js b/addon/bootstrap.js index 7045b97..b7d2149 100644 --- a/addon/bootstrap.js +++ b/addon/bootstrap.js @@ -106,6 +106,9 @@ function shutdown({ id, version, resourceURI, rootURI }, reason) { if (reason === APP_SHUTDOWN) { return; } + if (reason == ADDON_DISABLE) { + Services.obs.notifyObservers(null, "startupcache-invalidate", null); + } if (typeof Zotero === "undefined") { Zotero = Components.classes["@zotero.org/Zotero;1"].getService( Components.interfaces.nsISupports diff --git a/package.json b/package.json index 871d3e8..f37aa5e 100644 --- a/package.json +++ b/package.json @@ -17,13 +17,14 @@ "build-prod": "cross-env NODE_ENV=production node scripts/build.mjs", "build": "concurrently -c auto npm:build-prod npm:tsc", "tsc": "tsc --noEmit", - "start-z6": "node scripts/start.mjs --z 6", - "start-z7": "node scripts/start.mjs --z 7", "start": "node scripts/start.mjs", + "start-watch": "concurrently -c auto npm:start npm:watch", "stop": "node scripts/stop.mjs", "restart-dev": "npm run build-dev && npm run stop && npm run start", "restart-prod": "npm run build-prod && npm run stop && npm run start", "restart": "npm run restart-dev", + "reload": "npm run build-dev && node scripts/reload.mjs", + "watch": "chokidar \"src/*.*\" \"addon/*.*\" -c \"npm run reload\"", "release": "release-it", "lint": "prettier --write . && eslint . --ext .ts --fix", "test": "echo \"Error: no test specified\" && exit 1", @@ -46,6 +47,7 @@ "@types/node": "^20.1.1", "@typescript-eslint/eslint-plugin": "^5.59.2", "@typescript-eslint/parser": "^5.59.2", + "chokidar-cli": "^3.0.0", "compressing": "^1.9.0", "concurrently": "^8.0.1", "cross-env": "^7.0.3", diff --git a/scripts/reload.mjs b/scripts/reload.mjs new file mode 100644 index 0000000..34e9cbd --- /dev/null +++ b/scripts/reload.mjs @@ -0,0 +1,42 @@ +import { exit, argv } from "process"; +import minimist from "minimist"; +import { execSync } from "child_process"; +import details from "../package.json" assert { type: "json" }; +const { addonID, addonName } = details.config; +const version = details.version; +import cmd from "./zotero-cmd.json" assert { type: "json" }; +const { exec } = cmd; + +// Run node reload.js -h for help +const args = minimist(argv.slice(2)); + +const zoteroPath = exec[args.zotero || args.z || Object.keys(exec)[0]]; +const profile = args.profile || args.p; +const startZotero = `${zoteroPath} --debugger --purgecaches ${ + profile ? `-p ${profile}` : "" +}`; + +const script = ` +(async () => { + const { AddonManager } = ChromeUtils.import("resource://gre/modules/AddonManager.jsm"); + const addon = await AddonManager.getAddonByID("${addonID}"); + addon.disable(); + await Zotero.Promise.delay(1000); + addon.enable(); + const progressWindow = new Zotero.ProgressWindow({ closeOnClick: true }); + progressWindow.changeHeadline("${addonName} Hot Reload"); + progressWindow.progress = new progressWindow.ItemProgress( + "chrome://zotero/skin/tick.png", + "VERSION=${version}, BUILD=${new Date().toLocaleString()}. By zotero-plugin-toolkit" + ); + progressWindow.progress.setProgress(100); + progressWindow.show(); + progressWindow.startCloseTimer(5000); +})()`; + +const url = `zotero://ztoolkit-debug/?run=${encodeURIComponent(script)}`; + +const command = `${startZotero} -url "${url}"`; + +execSync(command); +exit(0); diff --git a/scripts/stop.mjs b/scripts/stop.mjs index c861a6b..197ef04 100644 --- a/scripts/stop.mjs +++ b/scripts/stop.mjs @@ -3,33 +3,11 @@ import { execSync } from "child_process"; import cmd from "./zotero-cmd.json" assert { type: "json" }; const { killZoteroWindows, killZoteroUnix } = cmd; -const MAX_WAIT_TIME = 10000; - -const startTime = new Date().getTime(); - try { if (process.platform === "win32") { execSync(killZoteroWindows); - - // wait until zotero.exe is fully stopped. maximum wait for 10 seconds - while (new Date().getTime() - startTime <= MAX_WAIT_TIME) { - try { - execSync('tasklist | find /i "zotero.exe"'); - } catch (e) { - break; - } - } } else { execSync(killZoteroUnix); - - // wait until zotero is fully stopped. maximum wait for 10 seconds - while (new Date().getTime() - startTime <= MAX_WAIT_TIME) { - try { - execSync("ps aux | grep -i zotero"); - } catch (e) { - break; - } - } } } catch (e) { console.error(e); diff --git a/scripts/zotero-cmd-default.json b/scripts/zotero-cmd-default.json index 743b1d9..3109efa 100644 --- a/scripts/zotero-cmd-default.json +++ b/scripts/zotero-cmd-default.json @@ -3,7 +3,6 @@ "killZoteroWindows": "taskkill /f /im zotero.exe", "killZoteroUnix": "kill -9 $(ps -x | grep zotero)", "exec": { - "6": "/path/to/zotero6.exe", "7": "/path/to/zotero7.exe" } }