From 526a18cc37496431e57edcfcae28815f9a9a0294 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 14 Aug 2023 23:12:41 +0000 Subject: [PATCH 01/36] build(deps-dev): bump esbuild from 0.18.20 to 0.19.2 Bumps [esbuild](https://github.com/evanw/esbuild) from 0.18.20 to 0.19.2. - [Release notes](https://github.com/evanw/esbuild/releases) - [Changelog](https://github.com/evanw/esbuild/blob/main/CHANGELOG.md) - [Commits](https://github.com/evanw/esbuild/compare/v0.18.20...v0.19.2) --- updated-dependencies: - dependency-name: esbuild dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index eabeae3..5e3bc39 100644 --- a/package.json +++ b/package.json @@ -51,7 +51,7 @@ "compressing": "^1.9.0", "concurrently": "^8.2.0", "cross-env": "^7.0.3", - "esbuild": "^0.18.12", + "esbuild": "^0.19.2", "eslint": "^8.44.0", "eslint-config-prettier": "^9.0.0", "prettier": "^3.0.0", From 6d4e9628a554f9635c169da0063488babbb5f742 Mon Sep 17 00:00:00 2001 From: windingwind <33902321+windingwind@users.noreply.github.com> Date: Sat, 26 Aug 2023 23:01:58 +0800 Subject: [PATCH 02/36] update: deps --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 5e3bc39..00d247d 100644 --- a/package.json +++ b/package.json @@ -41,7 +41,7 @@ }, "homepage": "https://github.com/windingwind/zotero-addon-template#readme", "dependencies": { - "zotero-plugin-toolkit": "^2.2.2" + "zotero-plugin-toolkit": "^2.2.8" }, "devDependencies": { "@types/node": "^20.4.2", From 10e57afadbfcaf69f9c4a28e15debb4a8069097d Mon Sep 17 00:00:00 2001 From: windingwind <33902321+windingwind@users.noreply.github.com> Date: Mon, 11 Sep 2023 19:52:18 +0800 Subject: [PATCH 03/36] fix: #70 --- README.md | 1 - package.json | 22 +++++++++++----------- src/hooks.ts | 2 -- src/modules/examples.ts | 19 +++---------------- 4 files changed, 14 insertions(+), 30 deletions(-) diff --git a/README.md b/README.md index 05ef05a..f1ef70a 100644 --- a/README.md +++ b/README.md @@ -88,7 +88,6 @@ Search `@example` in `src/examples.ts`. The examples are called in `src/hooks.ts - registerExtraColumn - registerExtraColumnWithCustomCell - registerCustomItemBoxRow -- registerCustomCellRenderer - registerLibraryTabPanel - registerReaderTabPanel diff --git a/package.json b/package.json index 00d247d..23e3f5f 100644 --- a/package.json +++ b/package.json @@ -41,23 +41,23 @@ }, "homepage": "https://github.com/windingwind/zotero-addon-template#readme", "dependencies": { - "zotero-plugin-toolkit": "^2.2.8" + "zotero-plugin-toolkit": "^2.3.6" }, "devDependencies": { - "@types/node": "^20.4.2", - "@typescript-eslint/eslint-plugin": "^6.0.0", - "@typescript-eslint/parser": "^6.0.0", + "@types/node": "^20.6.0", + "@typescript-eslint/eslint-plugin": "^6.6.0", + "@typescript-eslint/parser": "^6.6.0", "chokidar-cli": "^3.0.0", - "compressing": "^1.9.0", - "concurrently": "^8.2.0", + "compressing": "^1.10.0", + "concurrently": "^8.2.1", "cross-env": "^7.0.3", "esbuild": "^0.19.2", - "eslint": "^8.44.0", + "eslint": "^8.49.0", "eslint-config-prettier": "^9.0.0", - "prettier": "^3.0.0", - "release-it": "^16.1.0", + "prettier": "^3.0.3", + "release-it": "^16.1.5", "replace-in-file": "^7.0.1", - "typescript": "^5.1.6", - "zotero-types": "^1.0.16" + "typescript": "^5.2.2", + "zotero-types": "^1.2.3" } } diff --git a/src/hooks.ts b/src/hooks.ts index 7b4efa2..7b000f9 100644 --- a/src/hooks.ts +++ b/src/hooks.ts @@ -60,8 +60,6 @@ async function onMainWindowLoad(win: Window): Promise { await UIExampleFactory.registerExtraColumnWithCustomCell(); - await UIExampleFactory.registerCustomCellRenderer(); - await UIExampleFactory.registerCustomItemBoxRow(); UIExampleFactory.registerLibraryTabPanel(); diff --git a/src/modules/examples.ts b/src/modules/examples.ts index 79ea9cd..d493760 100644 --- a/src/modules/examples.ts +++ b/src/modules/examples.ts @@ -277,11 +277,13 @@ export class UIExampleFactory { return String(item.id); }, { - renderCellHook(index, data, column) { + renderCell(index, data, column) { + ztoolkit.log("Custom column cell is rendered!"); const span = document.createElementNS( "http://www.w3.org/1999/xhtml", "span", ); + span.className = `cell ${column.className}`; span.style.background = "#0dd068"; span.innerText = "⭐" + data; return span; @@ -290,21 +292,6 @@ export class UIExampleFactory { ); } - @example - static async registerCustomCellRenderer() { - await ztoolkit.ItemTree.addRenderCellHook( - "title", - // eslint-disable-next-line @typescript-eslint/ban-types - (index: number, data: string, column: any, original: Function) => { - const span = original(index, data, column) as HTMLSpanElement; - span.style.background = "rgb(30, 30, 30)"; - span.style.color = "rgb(156, 220, 240)"; - return span; - }, - ); - await ztoolkit.ItemTree.refresh(); - } - @example static async registerCustomItemBoxRow() { await ztoolkit.ItemBox.register( From 25990fd552764f9f60b4fc0086534034dee687b0 Mon Sep 17 00:00:00 2001 From: windingwind <33902321+windingwind@users.noreply.github.com> Date: Mon, 11 Sep 2023 20:12:08 +0800 Subject: [PATCH 04/36] update: readme --- README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index f1ef70a..8494968 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ This is a plugin template for [Zotero](https://www.zotero.org/). -Plugins created with this template: +Some plugins created with this template: [![GitHub Repo stars](https://img.shields.io/github/stars/windingwind/zotero-better-notes?label=zotero-better-notes&style=flat-square)](https://github.com/windingwind/zotero-better-notes) [![GitHub Repo stars](https://img.shields.io/github/stars/windingwind/zotero-pdf-preview?label=zotero-pdf-preview&style=flat-square)](https://github.com/windingwind/zotero-pdf-preview) @@ -24,6 +24,9 @@ Plugins created with this template: [![GitHub Repo stars](https://img.shields.io/github/stars/MuiseDestiny/zotero-gpt?label=zotero-gpt&style=flat-square)](https://github.com/MuiseDestiny/zotero-gpt) [![GitHub Repo stars](https://img.shields.io/github/stars/zoushucai/zotero-journalabbr?label=zotero-journalabbr&style=flat-square)](https://github.com/zoushucai/zotero-journalabbr) [![GitHub Repo stars](https://img.shields.io/github/stars/MuiseDestiny/zotero-figure?label=zotero-figure&style=flat-square)](https://github.com/MuiseDestiny/zotero-figure) +[![GitHub Repo stars](https://img.shields.io/github/stars/MuiseDestiny/zotero-file?label=WanderingFile&style=flat-square)](https://github.com/MuiseDestiny/zotero-file) +[![GitHub Repo stars](https://img.shields.io/github/stars/l0o0/jasminum?label=jasminum&style=flat-square)](https://github.com/l0o0/jasminum) +[![GitHub Repo stars](https://img.shields.io/github/stars/lifan0127/ai-research-assistant?label=ai-research-assistant&style=flat-square)](https://github.com/lifan0127/ai-research-assistant) 📖 [Plugin Development Documentation](https://zotero.yuque.com/books/share/8d230829-6004-4934-b4c6-685a7001bfa0/vec88d) (Chinese, provides English translation) From 23ffc5fdf841cb8b0610f20e1a5a5662412add9d Mon Sep 17 00:00:00 2001 From: windingwind <33902321+windingwind@users.noreply.github.com> Date: Fri, 15 Sep 2023 15:33:02 +0800 Subject: [PATCH 05/36] add: support beta (prerelease) auto update --- addon/manifest.json | 2 +- package.json | 5 +++-- scripts/build.mjs | 30 +++++++++++++++++++----------- update-beta.json | 17 +++++++++++++++++ update-template.json | 2 +- 5 files changed, 41 insertions(+), 15 deletions(-) create mode 100644 update-beta.json diff --git a/addon/manifest.json b/addon/manifest.json index a92439a..0c356c6 100644 --- a/addon/manifest.json +++ b/addon/manifest.json @@ -12,7 +12,7 @@ "applications": { "zotero": { "id": "__addonID__", - "update_url": "__updaterdf__", + "update_url": "__updateURL__", "strict_min_version": "6.999", "strict_max_version": "7.0.*" } diff --git a/package.json b/package.json index 23e3f5f..00ee0d1 100644 --- a/package.json +++ b/package.json @@ -8,8 +8,9 @@ "addonRef": "addontemplate", "addonInstance": "AddonTemplate", "prefsPrefix": "extensions.zotero.addontemplate", - "releasepage": "https://github.com/windingwind/zotero-addon-template/releases/latest/download/zotero-addon-template.xpi", - "updaterdf": "https://raw.githubusercontent.com/windingwind/zotero-addon-template/main/update.json" + "releasePage": "https://github.com/windingwind/zotero-addon-template/releases", + "updateJSON": "https://raw.githubusercontent.com/windingwind/zotero-addon-template/main/update.json", + "updateBetaJSON": "https://raw.githubusercontent.com/windingwind/zotero-addon-template/main/update-beta.json" }, "main": "src/index.ts", "scripts": { diff --git a/scripts/build.mjs b/scripts/build.mjs index c747a9d..7fcf720 100644 --- a/scripts/build.mjs +++ b/scripts/build.mjs @@ -24,6 +24,14 @@ const buildDir = "build"; const isPreRelease = version.includes("-"); +// If it is a pre-release, use update-beta.json +config.updateURL = isPreRelease ? config.updateJSONBeta : config.updateBetaJSON; + +const updateJSONFile = isPreRelease ? "update-beta.json" : "update.json"; +const updateLink = isPreRelease + ? `${config.releasePage}/download/v${version}/${name}.xpi` + : `${config.releasePage}/latest/download/${name}.xpi`; + function copyFileSync(source, target) { var targetFile = target; @@ -122,8 +130,16 @@ function replaceString() { /__homepage__/g, /__buildVersion__/g, /__buildTime__/g, + /__updateLink__/g, + ]; + const replaceTo = [ + author, + description, + homepage, + version, + buildTime, + updateLink, ]; - const replaceTo = [author, description, homepage, version, buildTime]; replaceFrom.push( ...Object.keys(config).map((k) => new RegExp(`__${k}__`, "g")), @@ -144,9 +160,7 @@ function replaceString() { countMatches: true, }; - if (!isPreRelease) { - optionsAddon.files.push("update.json"); - } + optionsAddon.files.push(updateJSONFile); const replaceResult = replaceInFileSync(optionsAddon); @@ -237,13 +251,7 @@ async function main() { copyFolderRecursiveSync("addon", buildDir); - if (isPreRelease) { - console.log( - "[Build] [Warn] Running in pre-release mode. update.json will not be replaced.", - ); - } else { - copyFileSync("update-template.json", "update.json"); - } + copyFileSync("update-template.json", updateJSONFile); await esbuild(); diff --git a/update-beta.json b/update-beta.json new file mode 100644 index 0000000..146d3d7 --- /dev/null +++ b/update-beta.json @@ -0,0 +1,17 @@ +{ + "addons": { + "addontemplate@euclpts.com": { + "updates": [ + { + "version": "1.0.0", + "update_link": "https://github.com/windingwind/zotero-addon-template/releases/latest/download/zotero-addon-template.xpi", + "applications": { + "zotero": { + "strict_min_version": "6.999" + } + } + } + ] + } + } +} diff --git a/update-template.json b/update-template.json index ad674e8..8f65a4c 100644 --- a/update-template.json +++ b/update-template.json @@ -4,7 +4,7 @@ "updates": [ { "version": "__buildVersion__", - "update_link": "__releasepage__", + "update_link": "__updateLink__", "applications": { "zotero": { "strict_min_version": "6.999" From ddb2bc6898b492120e78e5a0ef2706c06b8c2027 Mon Sep 17 00:00:00 2001 From: windingwind <33902321+windingwind@users.noreply.github.com> Date: Fri, 15 Sep 2023 16:01:13 +0800 Subject: [PATCH 06/36] fix: build update url bug --- scripts/build.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/build.mjs b/scripts/build.mjs index 7fcf720..8e61172 100644 --- a/scripts/build.mjs +++ b/scripts/build.mjs @@ -25,7 +25,7 @@ const buildDir = "build"; const isPreRelease = version.includes("-"); // If it is a pre-release, use update-beta.json -config.updateURL = isPreRelease ? config.updateJSONBeta : config.updateBetaJSON; +config.updateURL = isPreRelease ? config.updateBetaJSON : config.updateJSON; const updateJSONFile = isPreRelease ? "update-beta.json" : "update.json"; const updateLink = isPreRelease From 1572a57f2287855ef8731ecd5543679b479562bf Mon Sep 17 00:00:00 2001 From: plin <116143761+plin349@users.noreply.github.com> Date: Sun, 5 Nov 2023 10:45:47 -0500 Subject: [PATCH 07/36] fix grammar - 'conflict' instead of 'confliction' --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 8494968..382bef9 100644 --- a/README.md +++ b/README.md @@ -166,14 +166,14 @@ Replace the steps above and build you first plugin in 30 seconds! releasepage, // URL to releases(`.xpi`) updaterdf, // URL to update.json addonName, // name to be displayed in the plugin manager - addonID, // ID to avoid confliction. IMPORTANT! + addonID, // ID to avoid conflict. IMPORTANT! addonRef, // e.g. Element ID prefix addonInstance // the plugin's root instance: Zotero.${addonInstance} } } ``` - > Be careful to set the addonID and addonRef to avoid confliction. + > Be careful to set the addonID and addonRef to avoid conflict. - Run `npm install` to set up the plugin and install dependencies. If you don't have NodeJS installed, please download it [here](https://nodejs.org/en/); - Run `npm run build` to build the plugin in production mode. Run `npm run build-dev` to build the plugin in development mode. The xpi for installation and the built code is under `build` folder. From df7bde4757e5add8e72234ef522bace7695e045b Mon Sep 17 00:00:00 2001 From: windingwind <33902321+windingwind@users.noreply.github.com> Date: Fri, 10 Nov 2023 23:23:22 +0800 Subject: [PATCH 08/36] Release 1.0.1 --- package.json | 2 +- update.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 00ee0d1..7b3fdc5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "zotero-addon-template", - "version": "1.0.0", + "version": "1.0.1", "description": "Zotero Addon Template", "config": { "addonName": "Zotero Addon Template", diff --git a/update.json b/update.json index 146d3d7..8436e90 100644 --- a/update.json +++ b/update.json @@ -3,7 +3,7 @@ "addontemplate@euclpts.com": { "updates": [ { - "version": "1.0.0", + "version": "1.0.1", "update_link": "https://github.com/windingwind/zotero-addon-template/releases/latest/download/zotero-addon-template.xpi", "applications": { "zotero": { From 57319f498f8fb5da416736aaf5d373289d4eb967 Mon Sep 17 00:00:00 2001 From: windingwind <33902321+windingwind@users.noreply.github.com> Date: Sat, 11 Nov 2023 01:01:52 +0800 Subject: [PATCH 09/36] fix: #74 --- addon/locale/en-US/preferences.ftl | 1 + 1 file changed, 1 insertion(+) diff --git a/addon/locale/en-US/preferences.ftl b/addon/locale/en-US/preferences.ftl index 41100f9..57189e0 100644 --- a/addon/locale/en-US/preferences.ftl +++ b/addon/locale/en-US/preferences.ftl @@ -1,4 +1,5 @@ pref-title = Addon Template Example pref-enable = .label = Enable +pref-input = Input pref-help = { $name } Build { $version } { $time } \ No newline at end of file From 77e43bf3782931c5aa5cf7e441564bac009dffe2 Mon Sep 17 00:00:00 2001 From: windingwind <33902321+windingwind@users.noreply.github.com> Date: Sat, 11 Nov 2023 01:02:33 +0800 Subject: [PATCH 10/36] Release 1.0.2 --- package.json | 2 +- update.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 7b3fdc5..6dd403e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "zotero-addon-template", - "version": "1.0.1", + "version": "1.0.2", "description": "Zotero Addon Template", "config": { "addonName": "Zotero Addon Template", diff --git a/update.json b/update.json index 8436e90..f5c857f 100644 --- a/update.json +++ b/update.json @@ -3,7 +3,7 @@ "addontemplate@euclpts.com": { "updates": [ { - "version": "1.0.1", + "version": "1.0.2", "update_link": "https://github.com/windingwind/zotero-addon-template/releases/latest/download/zotero-addon-template.xpi", "applications": { "zotero": { From 12e7cb9641d9f19c8b2bf5a6e662b5f563ae7b9a Mon Sep 17 00:00:00 2001 From: KikkiZ <64997288+KikkiZ@users.noreply.github.com> Date: Mon, 13 Nov 2023 16:37:28 +0800 Subject: [PATCH 11/36] feat: add chinese readme document (#75) * feat: add chinese readme document * docs: fixed some translation errors * move: readme-zhCN --------- Co-authored-by: windingwind <33902321+windingwind@users.noreply.github.com> --- README.md | 2 + doc/README-zhCN.md | 396 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 398 insertions(+) create mode 100644 doc/README-zhCN.md diff --git a/README.md b/README.md index 382bef9..bb0c6ee 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,8 @@ This is a plugin template for [Zotero](https://www.zotero.org/). +[English](README.md) | [简体中文](doc/README-zhCN.md) + Some plugins created with this template: [![GitHub Repo stars](https://img.shields.io/github/stars/windingwind/zotero-better-notes?label=zotero-better-notes&style=flat-square)](https://github.com/windingwind/zotero-better-notes) diff --git a/doc/README-zhCN.md b/doc/README-zhCN.md new file mode 100644 index 0000000..d0c57f5 --- /dev/null +++ b/doc/README-zhCN.md @@ -0,0 +1,396 @@ +# Zotero Plugin Template + +[![zotero target version](https://img.shields.io/badge/Zotero-7-green?style=flat-square&logo=zotero&logoColor=CC2936)](https://www.zotero.org) +[![Using Zotero Plugin Template](https://img.shields.io/badge/Using-Zotero%20Plugin%20Template-blue?style=flat-square&logo=github)](https://github.com/windingwind/zotero-plugin-template) + +这是 [Zotero](https://www.zotero.org/) 的插件模板. + +[English](../README.md) | [简体中文](./README-zhCN.md) + +使用此模板创建的一些插件: + +[![GitHub Repo stars](https://img.shields.io/github/stars/windingwind/zotero-better-notes?label=zotero-better-notes&style=flat-square)](https://github.com/windingwind/zotero-better-notes) +[![GitHub Repo stars](https://img.shields.io/github/stars/windingwind/zotero-pdf-preview?label=zotero-pdf-preview&style=flat-square)](https://github.com/windingwind/zotero-pdf-preview) +[![GitHub Repo stars](https://img.shields.io/github/stars/windingwind/zotero-pdf-translate?label=zotero-pdf-translate&style=flat-square)](https://github.com/windingwind/zotero-pdf-translate) +[![GitHub Repo stars](https://img.shields.io/github/stars/windingwind/zotero-tag?label=zotero-tag&style=flat-square)](https://github.com/windingwind/zotero-tag) +[![GitHub Repo stars](https://img.shields.io/github/stars/iShareStuff/ZoteroTheme?label=zotero-theme&style=flat-square)](https://github.com/iShareStuff/ZoteroTheme) +[![GitHub Repo stars](https://img.shields.io/github/stars/MuiseDestiny/zotero-reference?label=zotero-reference&style=flat-square)](https://github.com/MuiseDestiny/zotero-reference) +[![GitHub Repo stars](https://img.shields.io/github/stars/MuiseDestiny/zotero-citation?label=zotero-citation&style=flat-square)](https://github.com/MuiseDestiny/zotero-citation) +[![GitHub Repo stars](https://img.shields.io/github/stars/MuiseDestiny/ZoteroStyle?label=zotero-style&style=flat-square)](https://github.com/MuiseDestiny/ZoteroStyle) +[![GitHub Repo stars](https://img.shields.io/github/stars/volatile-static/Chartero?label=Chartero&style=flat-square)](https://github.com/volatile-static/Chartero) +[![GitHub Repo stars](https://img.shields.io/github/stars/l0o0/tara?label=tara&style=flat-square)](https://github.com/l0o0/tara) +[![GitHub Repo stars](https://img.shields.io/github/stars/redleafnew/delitemwithatt?label=delitemwithatt&style=flat-square)](https://github.com/redleafnew/delitemwithatt) +[![GitHub Repo stars](https://img.shields.io/github/stars/redleafnew/zotero-updateifsE?label=zotero-updateifsE&style=flat-square)](https://github.com/redleafnew/zotero-updateifsE) +[![GitHub Repo stars](https://img.shields.io/github/stars/northword/zotero-format-metadata?label=zotero-format-metadata&style=flat-square)](https://github.com/northword/zotero-format-metadata) +[![GitHub Repo stars](https://img.shields.io/github/stars/inciteful-xyz/inciteful-zotero-plugin?label=inciteful-zotero-plugin&style=flat-square)](https://github.com/inciteful-xyz/inciteful-zotero-plugin) +[![GitHub Repo stars](https://img.shields.io/github/stars/MuiseDestiny/zotero-gpt?label=zotero-gpt&style=flat-square)](https://github.com/MuiseDestiny/zotero-gpt) +[![GitHub Repo stars](https://img.shields.io/github/stars/zoushucai/zotero-journalabbr?label=zotero-journalabbr&style=flat-square)](https://github.com/zoushucai/zotero-journalabbr) +[![GitHub Repo stars](https://img.shields.io/github/stars/MuiseDestiny/zotero-figure?label=zotero-figure&style=flat-square)](https://github.com/MuiseDestiny/zotero-figure) +[![GitHub Repo stars](https://img.shields.io/github/stars/MuiseDestiny/zotero-file?label=WanderingFile&style=flat-square)](https://github.com/MuiseDestiny/zotero-file) +[![GitHub Repo stars](https://img.shields.io/github/stars/l0o0/jasminum?label=jasminum&style=flat-square)](https://github.com/l0o0/jasminum) +[![GitHub Repo stars](https://img.shields.io/github/stars/lifan0127/ai-research-assistant?label=ai-research-assistant&style=flat-square)](https://github.com/lifan0127/ai-research-assistant) + +📖 [插件开发文档](https://zotero.yuque.com/books/share/8d230829-6004-4934-b4c6-685a7001bfa0/vec88d) (中文版) + +🛠️ [Zotero 插件工具包](https://github.com/windingwind/zotero-plugin-toolkit) | [API 文档](https://github.com/windingwind/zotero-plugin-toolkit/blob/master/docs/zotero-plugin-toolkit.md) + +ℹ️ [Zotero 类型定义](https://github.com/windingwind/zotero-types) + +📜 [Zotero 源代码](https://github.com/zotero/zotero) + +📌 [Zotero 插件模板](https://github.com/windingwind/zotero-plugin-template) (即当前库) + +> 👁 关注此库,以便在有修复或更新时及时收到通知. + +如果你正在使用此库,我建议你将这个标志 ([![Using Zotero Plugin Template](https://img.shields.io/badge/Using-Zotero%20Plugin%20Template-blue?style=flat-square&logo=github)](https://github.com/windingwind/zotero-plugin-template)) 放在 README 文件中: + +```md +[![Using Zotero Plugin Template](https://img.shields.io/badge/Using-Zotero%20Plugin%20Template-blue?style=flat-square&logo=github)](https://github.com/windingwind/zotero-plugin-template) +``` + +## Features 特性 + +> ❗Zotero系统已升级(dtd 已弃用,我们将不在使用 .properties). 主分支将只支持 Zotero 7.0.0-beta.12 或更高版本. 如果需要支持 Zotero 6,可能需要同时使用`dtd`、`properties` 和`ftl`. 请参考此库的 `zotero6-bootstrap` 分支. + +- 事件驱动、函数式编程的可扩展框架; +- 简单易用,开箱即用; +- ⭐[新特性!]自动热重载!每当修改源码时,都会自动编译并重新加载插件;[详情请跳转→](#auto-hot-reload) +- `src/modules/examples.ts` 中有丰富的示例,涵盖了插件中常用的大部分API(使用的插件工具包 [zotero-plugin-toolkit](https://github.com/windingwind/zotero-plugin-toolkit)); +- TypeScript 支持: + - 为使用 JavaScript 编写的Zotero源码提供全面的类型定义支持(使用类型定义包[zotero-types](https://github.com/windingwind/zotero-types)); + - 全局变量和环境设置; +- 插件构建/测试/发布工作流: + - 自动生成/更新插件id和版本、更新配置和设置环境变量 (`development/production`); + - 自动在 Zotero 中构建和重新加载代码; + - 自动发布到GitHub (使用[release-it](https://github.com/release-it/release-it)); + +## Examples 示例 + +此库提供了 [zotero-plugin-toolkit](https://github.com/windingwind/zotero-plugin-toolkit) 中API的示例. + +在 `src/examples.ts` 中搜索`@example` 查看示例. 这些示例在 `src/hooks.ts` 中调用演示. + +### 基本示例(Basic Examples) + +- registerNotifier +- registerPrefs, unregisterPrefs + +### 快捷键示例(Shortcut Keys Examples) + +- registerShortcuts +- exampleShortcutLargerCallback +- exampleShortcutSmallerCallback +- exampleShortcutConflictionCallback + +### UI示例(UI Examples) + +![image](https://user-images.githubusercontent.com/33902321/211739774-cc5c2df8-5fd9-42f0-9cdf-0f2e5946d427.png) + +- registerStyleSheet(the official make-it-red example) +- registerRightClickMenuItem +- registerRightClickMenuPopup +- registerWindowMenuWithSeprator +- registerExtraColumn +- registerExtraColumnWithCustomCell +- registerCustomItemBoxRow +- registerLibraryTabPanel +- registerReaderTabPanel + +### 首选项面板示例(Preference Pane Examples) + +![image](https://user-images.githubusercontent.com/33902321/211737987-cd7c5c87-9177-4159-b975-dc67690d0490.png) + +- Preferences bindings +- UI Events +- Tabel +- Locale + +详情参见 [`src/modules/preferenceScript.ts`](./src/modules/preferenceScript.ts) + +### 帮助示例(HelperExamples) + +![image](https://user-images.githubusercontent.com/33902321/215119473-e7d0d0ef-6d96-437e-b989-4805ffcde6cf.png) + +- dialogExample +- clipboardExample +- filePickerExample +- progressWindowExample +- vtableExample(See Preference Pane Examples) + +### 指令行示例(PromptExamples) + +Obsidian风格的指令输入模块,它通过接受文本来运行插件,并在弹出窗口中显示可选项. + +使用 `Shift+P` 激活. + +![image](https://user-images.githubusercontent.com/33902321/215120009-e7c7ed27-33a0-44fe-b021-06c272481a92.png) + +- registerAlertPromptExample + +## Quick Start Guide 快速入门指南 + +### 安装预构建 `xpi` + +通过直接在GitHub中下载构建好的 `xpi` 文件并将其安装到Zotero中来了解示例的工作原理. + +这也是你发布插件的格式,同时这也将是其他人可以直接使用的版本. + +> 该库构建好的xpi文件不具有任何实际功能,它可能不随Zotero更新而随时更新. +> +> `xpi` 文件实际上是一个zip压缩包,然而,请不要直接修改它,而是修改源代码并重新构建它. + +### 从源码构建(Build from Source) + +- Fork 此库或者使用 `Use this template`; +- 使用 `git clone ` 克隆此库; +- 进入项目文件夹; +
+💡 从 GitHub Codespace 开始 + +_GitHub CodeSpace_ 使你可以直接开始开发而无需在本地下载代码/IDE/依赖. + +重复下列步骤,仅需三十秒即可开始构建你的第一个插件! + +- 去 [homepage](https://github.com/windingwind/zotero-plugin-template)顶部,点击绿色按钮`Use this template`,点击 `Open in codespace`, 你需要登录你的GitHub账号. +- 等待 codespace 加载. +- 修改 `./package.json` 中的设置,包括: +
+ + ```json5 + { + version, + author, + description, + homepage, + config { + releasepage, // URL to releases(`.xpi`) + updaterdf, // URL to update.json + addonName, // name to be displayed in the plugin manager + addonID, // ID to avoid conflict. IMPORTANT! + addonRef, // e.g. Element ID prefix + addonInstance // the plugin's root instance: Zotero.${addonInstance} + } + } + ``` + + > 注意设置 addonID 和 addonRef 以避免冲突. + +- 运行 `npm install` 以设置插件并安装相关依赖. 如果你没有安装 Node.js,请在[此处下载](https://nodejs.org/en/); +- 运行 `npm run build` 以在生产模式下构建插件,运行 `npm run build-dev` 以在开发模式下构建插件. 用于安装的 xpi 文件和用于构建的代码在 `build` 文件夹下. + + > Dev & prod 两者有什么区别? + > + > - 此环境变量存储在 `Zotero.${addonInstance}.data.env` 中,控制台输出在生产模式下被禁用. + > - 你可以根据此变量决定用户无法查看/使用的内容. + +### 发布(Release) + +如果要构建和发布插件,运行如下指令: + +```shell +# A release-it command: version increase, npm run build, git push, and GitHub release +# You need to set the environment variable GITHUB_TOKEN https://github.com/settings/tokens +# release-it: https://github.com/release-it/release-it +npm run release +``` + +### 设置开发环境(Setup Development Environment) + +1. 安装 Zotero: (Zotero 7 beta: ) + +2. 安装 Firefox 102 (适用于 Zotero 7) + +3. 复制 zotero 命令行配置文件,修改开始安装 beta Zotero 的命令. + + > (可选项) 此操作仅需执行一次: 使用 `/path/to/zotero -p` 启动 Zotero,创建一个新的配置文件并用作开发配置文件. + > 将配置文件的路径 `profilePath` 放入 `zotero-cmd.json` 中,以指定要使用的配置文件. + + ```sh + cp ./scripts/zotero-cmd-default.json ./scripts/zotero-cmd.json + vim ./scripts/zotero-cmd.json + ``` + +4. 构建插件并使用 `npm run restart` 重启 Zotero. + +5. 启动 Firefox 102 (Zotero 7) + +6. 在 Firefox 中,转到devtools,转到设置,单击 "enable remote debugging" ,同时,旁边的按钮也是关于调试的。 + + > 在 FirFox 102 中输入 `about:debugging#/setup` . + +7. 在 Zotero 中,进入设置-高级-编辑器,搜索 "debugging" 然后单击 "allow remote debugging". + +8. 在 Firefox 中连接 Zotero. 在 FireFox 102中,在远程调试页面底部输入 `localhost:6100` 然后单击 `add`. + +9. 在远程调试页面左侧栏点击 `connect`. + +10. 点击 "Inspect Main Process" + +### 自动热重载(Auto Hot Reload) + +厌倦了无休止的重启吗?忘掉它,拥抱热加载! + +1. 运行 `npm run start-watch`. (如果Zotero已经在运行,请使用 `npm run watch`) +2. 编码. (是的,就这么简单) + +当检测到 `src` 或 `addon` 中的文件修改时,插件将自动编译并重新加载. + +
+💡 将此功能添加到现有插件的步骤 + +1. 复制 `scripts/reload.mjs` +2. 复制 `reload` 、`watch` 和 `start-watch` 命令 `package.json` +3. 运行 `npm install --save-dev chokidar-cli` +4. 结束. + +
+ +### 在 Zotero 中调试 + +你还可以: + +- 在 Tools->Developer->Run Javascript 中测试代码片段; +- 使用 `Zotero.debug()` 调试输出. 在 Help->Debug Output Logging->View Output 查看输出; +- 调试 UI. Zotero 建立在 Firefox XUL 框架之上. 使用 [XUL Explorer](https://udn.realityripple.com/docs/Archive/Mozilla/XUL_Explorer) 等软件调试 XUL UI. + > XUL 文档: + +## Details 更多细节 + +### 关于Hooks(About Hooks) + +> 可以在 [`src/hooks.ts`](https://github.com/windingwind/zotero-plugin-template/blob/bootstrap/src/hooks.ts) 中查看更多 + +1. 当在 Zotero 中触发安装/启用/启动时,`bootstrap.js` > `startup` 被调用 + - 等待 Zotero 就绪 + - 加载 `index.js` (插件代码的主入口,从 `index.ts` 中构建) + - 如果是 Zotero 7 以上的版本则注册资源 +2. 主入口 `index.js` 中,插件对象被注入到 `Zotero` ,并且 `hooks.ts` > `onStartup` 被调用. + - 初始化插件需要的资源,包括通知监听器、首选项面板和UI元素. +3. 当在 Zotero 中触发卸载/禁用时,`bootstrap.js` > `shutdown` 被调用. + - `events.ts` > `onShutdown` 被调用. 移除 UI 元素、首选项面板或插件创建的任何内容. + - 移除脚本并释放资源. + +### 关于全局变量(About Global Variables) + +> 可以在 [`src/index.ts`](https://github.com/windingwind/zotero-plugin-template/blob/bootstrap/src/index.ts) 中查看更多 + +引导插件在沙盒中运行,但沙盒中没有默认的全局变量,例如 `Zotero` 或 `window` 等我们曾在覆盖插件环境中使用的变量. + +此模板将以下变量注册到全局范围: + +```ts +Zotero, ZoteroPane, Zotero_Tabs, window, document, rootURI, ztoolkit, addon; +``` + +### 创建元素 API(Create Elements API) + +插件模板为 bootstrap 插件提供了一些新的API. 我们有两个原因使用这些 API,而不是使用 `createElement/createElementNS`: + +- 在 bootstrap 模式下,插件必须在推出(禁用或卸载)时清理所有 UI 元素,这非常麻烦. 使用 `createElement`,插件模板将维护这些元素. 仅仅在退出时 `unregisterAll` . +- Zotero 7 需要 createElement()/createElementNS() → createXULElement() 来表示其他的 XUL 元素,而 Zotero 6 并不支持 `createXULElement`. 类似于 React.createElement 的API `createElement` 检测 namespace(xul/html/svg) 并且自动创建元素,返回元素为对应的 TypeScript 元素类型. + +```ts +createElement(document, "div"); // returns HTMLDivElement +createElement(document, "hbox"); // returns XUL.Box +createElement(document, "button", { namespace: "xul" }); // manually set namespace. returns XUL.Button +``` + +### 关于构建(About Build) + +使用 Esbuild 将 `.ts` 源代码构建为 `.js`. + +使用 `replace-in-file` 去替换在 `package.json` 中定义的关键字和配置 (`xhtml`、`.flt` 等). + +步骤 `scripts/build.mjs`: + +1. 清理 `./build` +2. 复制 `./addon` 到 `./build` +3. Esbuild 到 `./build/addon/chrome/content/scripts` +4. 替换`__buildVersion__` 和 `__buildTime__` 在 `./build/addon` +5. 压缩 `./build/addon` 到 `./build/*.xpi` + +### 关于 Zotero API(About Zotero API) + +Zotero 文档已过时且不完整,git clone https://github.com/zotero/zotero 并全局搜索关键字. + +> ⭐[zotero-types](https://github.com/windingwind/zotero-types) 提供了最常用的 Zotero API,在默认情况下它被包含在此模板中. 你的 IDE 将为大多数的 API 提供提醒. + +猜你需要:查找所需 API的技巧 + +在 `.xhtml`/`.flt` 文件中搜索 UI 标签,然后在 locale 文件中找到对应的键. ,然后在 `.js`/`.jsx` 文件中搜索此键. + +### 目录结构(Directory Structure) + +本部分展示了模板的目录结构. + +- 所有的 `.js/.ts` 代码都在 `./src`; +- 插件配置文件:`./addon/manifest.json`; +- UI 文件: `./addon/chrome/content/*.xhtml`. +- 区域设置文件: `./addon/locale/**/*.flt`; +- 首选项文件: `./addon/prefs.js`; + > 不要在 `prefs.js` 中换行 + +```shell +. +|-- .eslintrc.json # eslint conf +|-- .gitattributes # git conf +|-- .github/ # github conf +|-- .gitignore # git conf +|-- .prettierrc # prettier conf +|-- .release-it.json # release-it conf +|-- .vscode # vs code conf +| |-- extensions.json +| |-- launch.json +| |-- setting.json +| `-- toolkit.code-snippets +|-- package-lock.json # npm conf +|-- package.json # npm conf +|-- LICENSE +|-- README.md +|-- addon +| |-- bootstrap.js # addon load/unload script, like a main.c +| |-- chrome +| | `-- content +| | |-- icons/ +| | |-- preferences.xhtml # preference panel +| | `-- zoteroPane.css +| |-- locale # locale +| | |-- en-US +| | | |-- addon.ftl +| | | `-- preferences.ftl +| | `-- zh-CN +| | |-- addon.ftl +| | `-- preferences.ftl +| |-- manifest.json # addon config +| `-- prefs.js +|-- build/ # build dir +|-- scripts # scripts for dev +| |-- build.mjs # esbuild and replace +| |-- reload.mjs +| |-- start.mjs +| |-- stop.mjs +| `-- zotero-cmd-default.json +|-- src # source code +| |-- addon.ts # base class +| |-- hooks.ts # lifecycle hooks +| |-- index.ts # main entry +| |-- modules # sub modules +| | |-- examples.ts +| | `-- preferenceScript.ts +| `-- utils # utilities +| |-- locale.ts +| |-- prefs.ts +| |-- wait.ts +| `-- window.ts +|-- tsconfig.json # https://code.visualstudio.com/docs/languages/jsconfig +|-- typings # ts typings +| `-- global.d.ts +|-- update-template.json # template of `update.json` +`-- update.json +``` + +## Disclaimer 免责声明 + +在 AGPL 下使用此代码. 不提供任何保证. 遵守你所在地区的法律! + +如果你想更改许可,请通过 与我联系. From a56e31fa0d683489c8acdb8d4a211a675a5b9912 Mon Sep 17 00:00:00 2001 From: windingwind <33902321+windingwind@users.noreply.github.com> Date: Thu, 30 Nov 2023 20:39:44 +0800 Subject: [PATCH 12/36] chore: onStartup call --- addon/bootstrap.js | 1 + src/index.ts | 2 -- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/addon/bootstrap.js b/addon/bootstrap.js index 1b50280..a96e40e 100644 --- a/addon/bootstrap.js +++ b/addon/bootstrap.js @@ -40,6 +40,7 @@ async function startup({ id, version, resourceURI, rootURI }, reason) { `${rootURI}/chrome/content/scripts/__addonRef__.js`, ctx, ); + Zotero.__addonInstance__.hooks.onStartup(); } async function onMainWindowLoad({ window }, reason) { diff --git a/src/index.ts b/src/index.ts index 86c6a19..c6fcdef 100644 --- a/src/index.ts +++ b/src/index.ts @@ -16,8 +16,6 @@ if (!basicTool.getGlobal("Zotero")[config.addonInstance]) { return _globalThis.addon.data.ztoolkit; }); Zotero[config.addonInstance] = addon; - // Trigger addon hook for initialization - addon.hooks.onStartup(); } function defineGlobal(name: Parameters[0]): void; From 1208ad54a9afc277130e0f7fc397506d6359a3ca Mon Sep 17 00:00:00 2001 From: windingwind <33902321+windingwind@users.noreply.github.com> Date: Thu, 7 Dec 2023 21:48:02 +0800 Subject: [PATCH 13/36] add: devtool support --- package.json | 3 ++- scripts/devtool.mjs | 61 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+), 1 deletion(-) create mode 100644 scripts/devtool.mjs diff --git a/package.json b/package.json index 6dd403e..93bcf23 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,7 @@ "build-prod": "cross-env NODE_ENV=production node scripts/build.mjs", "build": "concurrently -c auto npm:build-prod npm:tsc", "tsc": "tsc --noEmit", - "start": "node scripts/start.mjs", + "start": "concurrently 'node scripts/start.mjs' 'node -e \"setTimeout(()=>{},2000)\" && npm run devtool'", "start-watch": "npm run build-dev && concurrently -c auto npm:start npm:watch", "stop": "node scripts/stop.mjs", "restart-dev": "npm run build-dev && npm run stop && npm run start", @@ -26,6 +26,7 @@ "restart": "npm run restart-dev", "reload": "npm run build-dev && node scripts/reload.mjs", "watch": "chokidar \"src/**\" \"addon/**\" -c \"npm run reload\"", + "devtool": "node scripts/devtool.mjs", "release": "release-it", "lint": "prettier --write . && eslint . --ext .ts --fix", "test": "echo \"Error: no test specified\" && exit 1", diff --git a/scripts/devtool.mjs b/scripts/devtool.mjs new file mode 100644 index 0000000..ad36818 --- /dev/null +++ b/scripts/devtool.mjs @@ -0,0 +1,61 @@ +import { exit } from "process"; +import { execSync } from "child_process"; +import cmd from "./zotero-cmd.json" assert { type: "json" }; + +const { zoteroBinPath, profilePath } = cmd.exec; + +const startZotero = `"${zoteroBinPath}" --debugger --purgecaches -profile "${profilePath}"`; + +const script = ` +(async () => { + Zotero.Prefs.set("devtools.debugger.remote-enabled", true, true); + Zotero.Prefs.set("devtools.debugger.remote-port", 6100, true); + Zotero.Prefs.set("devtools.debugger.prompt-connection", false, true); + Zotero.Prefs.set("devtools.debugger.chrome-debugging-websocket", false, true); + + env = + Services.env || + Cc["@mozilla.org/process/environment;1"].getService(Ci.nsIEnvironment); + + env.set("MOZ_BROWSER_TOOLBOX_PORT", 6100); + Zotero.openInViewer( + "chrome://devtools/content/framework/browser-toolbox/window.html", + { + onLoad: (doc) => { + doc.querySelector("#status-message-container").style.visibility = + "collapse"; + let toolboxBody; + waitUntil( + () => { + toolboxBody = doc + .querySelector(".devtools-toolbox-browsertoolbox-iframe") + ?.contentDocument?.querySelector(".theme-body"); + return toolboxBody; + }, + () => { + toolboxBody.style = "pointer-events: all !important"; + } + ); + }, + } + ); + + function waitUntil(condition, callback, interval = 100, timeout = 10000) { + const start = Date.now(); + const intervalId = setInterval(() => { + if (condition()) { + clearInterval(intervalId); + callback(); + } else if (Date.now() - start > timeout) { + clearInterval(intervalId); + } + }, interval); + } +})()`; + +const url = `zotero://ztoolkit-debug/?run=${encodeURIComponent(script)}`; + +const command = `${startZotero} -url "${url}"`; + +execSync(command); +exit(0); From ade49628ff10d5f6e39b5fd205165d65ec9e3fb1 Mon Sep 17 00:00:00 2001 From: windingwind <33902321+windingwind@users.noreply.github.com> Date: Fri, 8 Dec 2023 11:15:09 +0800 Subject: [PATCH 14/36] update: bundle devtool --- package.json | 29 +++++++++++---------- scripts/devtool.mjs | 61 --------------------------------------------- src/index.ts | 49 ++++++++++++++++++++++++++++++++++-- 3 files changed, 61 insertions(+), 78 deletions(-) delete mode 100644 scripts/devtool.mjs diff --git a/package.json b/package.json index 93bcf23..1a26e16 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,7 @@ "build-prod": "cross-env NODE_ENV=production node scripts/build.mjs", "build": "concurrently -c auto npm:build-prod npm:tsc", "tsc": "tsc --noEmit", - "start": "concurrently 'node scripts/start.mjs' 'node -e \"setTimeout(()=>{},2000)\" && npm run devtool'", + "start": "node scripts/start.mjs", "start-watch": "npm run build-dev && concurrently -c auto npm:start npm:watch", "stop": "node scripts/stop.mjs", "restart-dev": "npm run build-dev && npm run stop && npm run start", @@ -26,7 +26,6 @@ "restart": "npm run restart-dev", "reload": "npm run build-dev && node scripts/reload.mjs", "watch": "chokidar \"src/**\" \"addon/**\" -c \"npm run reload\"", - "devtool": "node scripts/devtool.mjs", "release": "release-it", "lint": "prettier --write . && eslint . --ext .ts --fix", "test": "echo \"Error: no test specified\" && exit 1", @@ -43,23 +42,23 @@ }, "homepage": "https://github.com/windingwind/zotero-addon-template#readme", "dependencies": { - "zotero-plugin-toolkit": "^2.3.6" + "zotero-plugin-toolkit": "^2.3.11" }, "devDependencies": { - "@types/node": "^20.6.0", - "@typescript-eslint/eslint-plugin": "^6.6.0", - "@typescript-eslint/parser": "^6.6.0", + "@types/node": "^20.10.4", + "@typescript-eslint/eslint-plugin": "^6.13.2", + "@typescript-eslint/parser": "^6.13.2", "chokidar-cli": "^3.0.0", "compressing": "^1.10.0", - "concurrently": "^8.2.1", + "concurrently": "^8.2.2", "cross-env": "^7.0.3", - "esbuild": "^0.19.2", - "eslint": "^8.49.0", - "eslint-config-prettier": "^9.0.0", - "prettier": "^3.0.3", - "release-it": "^16.1.5", - "replace-in-file": "^7.0.1", - "typescript": "^5.2.2", - "zotero-types": "^1.2.3" + "esbuild": "^0.19.8", + "eslint": "^8.55.0", + "eslint-config-prettier": "^9.1.0", + "prettier": "^3.1.0", + "release-it": "^16.3.0", + "replace-in-file": "^7.0.2", + "typescript": "^5.3.3", + "zotero-types": "^1.3.7" } } diff --git a/scripts/devtool.mjs b/scripts/devtool.mjs deleted file mode 100644 index ad36818..0000000 --- a/scripts/devtool.mjs +++ /dev/null @@ -1,61 +0,0 @@ -import { exit } from "process"; -import { execSync } from "child_process"; -import cmd from "./zotero-cmd.json" assert { type: "json" }; - -const { zoteroBinPath, profilePath } = cmd.exec; - -const startZotero = `"${zoteroBinPath}" --debugger --purgecaches -profile "${profilePath}"`; - -const script = ` -(async () => { - Zotero.Prefs.set("devtools.debugger.remote-enabled", true, true); - Zotero.Prefs.set("devtools.debugger.remote-port", 6100, true); - Zotero.Prefs.set("devtools.debugger.prompt-connection", false, true); - Zotero.Prefs.set("devtools.debugger.chrome-debugging-websocket", false, true); - - env = - Services.env || - Cc["@mozilla.org/process/environment;1"].getService(Ci.nsIEnvironment); - - env.set("MOZ_BROWSER_TOOLBOX_PORT", 6100); - Zotero.openInViewer( - "chrome://devtools/content/framework/browser-toolbox/window.html", - { - onLoad: (doc) => { - doc.querySelector("#status-message-container").style.visibility = - "collapse"; - let toolboxBody; - waitUntil( - () => { - toolboxBody = doc - .querySelector(".devtools-toolbox-browsertoolbox-iframe") - ?.contentDocument?.querySelector(".theme-body"); - return toolboxBody; - }, - () => { - toolboxBody.style = "pointer-events: all !important"; - } - ); - }, - } - ); - - function waitUntil(condition, callback, interval = 100, timeout = 10000) { - const start = Date.now(); - const intervalId = setInterval(() => { - if (condition()) { - clearInterval(intervalId); - callback(); - } else if (Date.now() - start > timeout) { - clearInterval(intervalId); - } - }, interval); - } -})()`; - -const url = `zotero://ztoolkit-debug/?run=${encodeURIComponent(script)}`; - -const command = `${startZotero} -url "${url}"`; - -execSync(command); -exit(0); diff --git a/src/index.ts b/src/index.ts index c6fcdef..cbc1423 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,12 +1,14 @@ import { BasicTool } from "zotero-plugin-toolkit/dist/basic"; import Addon from "./addon"; import { config } from "../package.json"; +import { waitUntil } from "./utils/wait"; const basicTool = new BasicTool(); if (!basicTool.getGlobal("Zotero")[config.addonInstance]) { - // Set global variables - _globalThis.Zotero = basicTool.getGlobal("Zotero"); + if (__env__ === "development") { + openDevTool(); + } defineGlobal("window"); defineGlobal("document"); defineGlobal("ZoteroPane"); @@ -27,3 +29,46 @@ function defineGlobal(name: string, getter?: () => any) { }, }); } + +function openDevTool() { + // const { BrowserToolboxLauncher } = ChromeUtils.import( + // "resource://devtools/client/framework/browser-toolbox/Launcher.jsm", + // ); + // BrowserToolboxLauncher.init(); + // TODO: Use the above code to open the devtool after https://github.com/zotero/zotero/pull/3387 + Zotero.Prefs.set("devtools.debugger.remote-enabled", true, true); + Zotero.Prefs.set("devtools.debugger.remote-port", 6100, true); + Zotero.Prefs.set("devtools.debugger.prompt-connection", false, true); + Zotero.Prefs.set("devtools.debugger.chrome-debugging-websocket", false, true); + + const env = + Services.env || + // @ts-ignore - mozIEnvironment is not in the types + Cc["@mozilla.org/process/environment;1"].getService(Ci.nsIEnvironment); + + env.set("MOZ_BROWSER_TOOLBOX_PORT", 6100); + Zotero.openInViewer( + "chrome://devtools/content/framework/browser-toolbox/window.html", + { + // @ts-ignore - onLoad is not in the types + onLoad: (doc: Document) => { + ( + doc.querySelector("#status-message-container") as HTMLDivElement + ).style.visibility = "collapse"; + let toolboxBody: HTMLIFrameElement; + waitUntil( + () => { + toolboxBody = doc + .querySelector(".devtools-toolbox-browsertoolbox-iframe") + // @ts-ignore - contentDocument is not in the types + ?.contentDocument?.querySelector(".theme-body"); + return !!toolboxBody; + }, + () => { + toolboxBody.setAttribute("style", "pointer-events: all !important"); + }, + ); + }, + }, + ); +} From e2885ec56352b0224eb9eaaa4cb55a618204e290 Mon Sep 17 00:00:00 2001 From: Denis Maier Date: Fri, 8 Dec 2023 13:21:56 +0100 Subject: [PATCH 15/36] Correct typo (#83) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index bb0c6ee..692f38d 100644 --- a/README.md +++ b/README.md @@ -102,7 +102,7 @@ Search `@example` in `src/examples.ts`. The examples are called in `src/hooks.ts - Preferences bindings - UI Events -- Tabel +- Table - Locale See [`src/modules/preferenceScript.ts`](./src/modules/preferenceScript.ts) From fb9109d87d06676c1c4be07907b9d6adc9387ed3 Mon Sep 17 00:00:00 2001 From: Northword <44738481+northword@users.noreply.github.com> Date: Tue, 12 Dec 2023 15:55:00 +0800 Subject: [PATCH 16/36] refactor: rebuild development scripts and release workflow (#84) * refactor: add dev server and fix `update-beta.json` bug * chore: add renovate config * feat: release plugin to GitHub release via action * chore: update vsc settings * Cancel release by action * docs: update readme * Update README.md * Update README.md fix format * Update README.md * Update README.md fix hint * tweak * Merge #81 * Fix indent * Merge https://github.com/windingwind/zotero-plugin-template/commit/ade49628ff10d5f6e39b5fd205165d65ec9e3fb1 * Revent delete env * tweak * feat: release via GitHub action * docs: update readme * Update README.md * style: fix prettier * add: stdout log to file * Update README.md fix typo * Update release.yml fix typo * write Zotero log to `logs/zotero.log` * do not provide update-beta.json default * fix prettier in readme * tweak * fix wrong link in readme * Move 3rd package config to package.json --------- Co-authored-by: windingwind <33902321+windingwind@users.noreply.github.com> --- .eslintrc.json | 41 --- .github/renovate.json | 21 ++ .github/workflows/release.yml | 33 ++ .gitignore | 5 +- .prettierignore | 7 + .prettierrc | 3 - .release-it.json | 14 - .vscode/launch.json | 15 +- .vscode/settings.json | 2 +- README.md | 261 +++++++------- package.json | 105 +++++- scripts/build.mjs | 320 ++++++++---------- scripts/reload.mjs | 34 -- scripts/scripts.mjs | 75 ++++ scripts/server.mjs | 90 +++++ scripts/start.mjs | 85 +++-- scripts/stop.mjs | 28 +- .../update-template.json | 0 scripts/utils.mjs | 129 +++++++ src/index.ts | 47 --- update-beta.json | 17 - 21 files changed, 811 insertions(+), 521 deletions(-) delete mode 100644 .eslintrc.json create mode 100644 .github/renovate.json create mode 100644 .github/workflows/release.yml create mode 100644 .prettierignore delete mode 100644 .prettierrc delete mode 100644 .release-it.json delete mode 100644 scripts/reload.mjs create mode 100644 scripts/scripts.mjs create mode 100644 scripts/server.mjs rename update-template.json => scripts/update-template.json (100%) create mode 100644 scripts/utils.mjs delete mode 100644 update-beta.json diff --git a/.eslintrc.json b/.eslintrc.json deleted file mode 100644 index bd779ad..0000000 --- a/.eslintrc.json +++ /dev/null @@ -1,41 +0,0 @@ -{ - "env": { - "browser": true, - "es2021": true - }, - "root": true, - "extends": [ - "eslint:recommended", - "plugin:@typescript-eslint/recommended", - "prettier" - ], - "overrides": [], - "parser": "@typescript-eslint/parser", - "parserOptions": { - "ecmaVersion": "latest", - "sourceType": "module" - }, - "plugins": ["@typescript-eslint"], - "rules": { - "@typescript-eslint/ban-ts-comment": [ - "warn", - { - "ts-expect-error": "allow-with-description", - "ts-ignore": "allow-with-description", - "ts-nocheck": "allow-with-description", - "ts-check": "allow-with-description" - } - ], - "@typescript-eslint/no-unused-vars": "off", - "@typescript-eslint/no-explicit-any": ["off", { "ignoreRestArgs": true }], - "@typescript-eslint/no-non-null-assertion": "off" - }, - "ignorePatterns": [ - "**/build/**", - "**/dist/**", - "**/node_modules/**", - "**/scripts/**", - "**/*.js", - "**/*.bak" - ] -} diff --git a/.github/renovate.json b/.github/renovate.json new file mode 100644 index 0000000..1289413 --- /dev/null +++ b/.github/renovate.json @@ -0,0 +1,21 @@ +{ + "$schema": "https://docs.renovatebot.com/renovate-schema.json", + "extends": [ + "config:recommended", + ":semanticPrefixChore", + ":prHourlyLimitNone", + ":prConcurrentLimitNone", + ":enableVulnerabilityAlerts", + ":dependencyDashboard", + "schedule:weekends" + ], + "packageRules": [ + { + "matchPackageNames": ["zotero-plugin-toolkit", "zotero-types"], + "automerge": true + } + ], + "git-submodules": { + "enabled": true + } +} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..b533acf --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,33 @@ +name: Release + +on: + push: + tags: + - v** + +permissions: + contents: write + +jobs: + release-it: + runs-on: ubuntu-latest + env: + GITHUB_TOKEN: ${{ secrets.GitHub_TOKEN }} + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Setup Node.js + uses: actions/setup-node@v3 + with: + node-version: 20 + # cache: npm + + - name: Install deps + run: npm install + + - name: Release to GitHub + # if: github.event_name == 'push' && github.ref_type == 'tag' && startsWith(github.ref, 'refs/tags/v') + run: | + npm run release -- --no-increment --no-git --github.release --ci --verbose diff --git a/.gitignore b/.gitignore index d277962..6dfac6b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,7 @@ -**/build +build +logs node_modules package-lock.json +pnpm-lock.yaml +yarn.lock zotero-cmd.json \ No newline at end of file diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..153e309 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,7 @@ +build +logs +node_modules +package-lock.json +yarn.lock +pnpm-lock.yaml +# zotero-cmd.json diff --git a/.prettierrc b/.prettierrc deleted file mode 100644 index 75fa134..0000000 --- a/.prettierrc +++ /dev/null @@ -1,3 +0,0 @@ -{ - "tabWidth": 2 -} diff --git a/.release-it.json b/.release-it.json deleted file mode 100644 index 69da605..0000000 --- a/.release-it.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "npm": { - "publish": false - }, - "github": { - "release": true, - "assets": ["build/*.xpi"] - }, - "hooks": { - "before:init": "npm run lint", - "after:bump": "npm run build", - "after:release": "echo Successfully released ${name} v${version} to ${repo.repository}." - } -} diff --git a/.vscode/launch.json b/.vscode/launch.json index 1663cf3..8b7f88b 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -7,23 +7,16 @@ { "type": "node", "request": "launch", - "name": "StartDev", + "name": "Start", "runtimeExecutable": "npm", - "runtimeArgs": ["run", "start-watch"] + "runtimeArgs": ["run", "start"] }, { "type": "node", "request": "launch", - "name": "Restart", + "name": "Build", "runtimeExecutable": "npm", - "runtimeArgs": ["run", "restart"] - }, - { - "type": "node", - "request": "launch", - "name": "Restart in Prod Mode", - "runtimeExecutable": "npm", - "runtimeArgs": ["run", "restart-prod"] + "runtimeArgs": ["run", "build"] } ] } diff --git a/.vscode/settings.json b/.vscode/settings.json index be8156d..ee7b675 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -2,6 +2,6 @@ "editor.formatOnType": false, "editor.formatOnSave": true, "editor.codeActionsOnSave": { - "source.fixAll.eslint": true + "source.fixAll.eslint": "explicit" } } diff --git a/README.md b/README.md index 692f38d..811d9a7 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,22 @@ This is a plugin template for [Zotero](https://www.zotero.org/). [English](README.md) | [简体中文](doc/README-zhCN.md) -Some plugins created with this template: +[📖 Plugin Development Documentation](https://zotero.yuque.com/books/share/8d230829-6004-4934-b4c6-685a7001bfa0/vec88d) (Chinese, outdated) + +[📖 Plugin Development Documentation for Zotero 7](https://www.zotero.org/support/dev/zotero_7_for_developers) + +[🛠️ Zotero Plugin Toolkit](https://github.com/windingwind/zotero-plugin-toolkit) | [API Documentation](https://github.com/windingwind/zotero-plugin-toolkit/blob/master/docs/zotero-plugin-toolkit.md) + +[ℹ️ Zotero Type Definitions](https://github.com/windingwind/zotero-types) + +[📜 Zotero Source Code](https://github.com/zotero/zotero) + +[📌 Zotero Plugin Template](https://github.com/windingwind/zotero-plugin-template) (This repo) + +> [!tip] +> 👁 Watch this repo so that you can be notified whenever there are fixes & updates. + +## Plugins built with this template [![GitHub Repo stars](https://img.shields.io/github/stars/windingwind/zotero-better-notes?label=zotero-better-notes&style=flat-square)](https://github.com/windingwind/zotero-better-notes) [![GitHub Repo stars](https://img.shields.io/github/stars/windingwind/zotero-pdf-preview?label=zotero-pdf-preview&style=flat-square)](https://github.com/windingwind/zotero-pdf-preview) @@ -26,23 +41,12 @@ Some plugins created with this template: [![GitHub Repo stars](https://img.shields.io/github/stars/MuiseDestiny/zotero-gpt?label=zotero-gpt&style=flat-square)](https://github.com/MuiseDestiny/zotero-gpt) [![GitHub Repo stars](https://img.shields.io/github/stars/zoushucai/zotero-journalabbr?label=zotero-journalabbr&style=flat-square)](https://github.com/zoushucai/zotero-journalabbr) [![GitHub Repo stars](https://img.shields.io/github/stars/MuiseDestiny/zotero-figure?label=zotero-figure&style=flat-square)](https://github.com/MuiseDestiny/zotero-figure) -[![GitHub Repo stars](https://img.shields.io/github/stars/MuiseDestiny/zotero-file?label=WanderingFile&style=flat-square)](https://github.com/MuiseDestiny/zotero-file) [![GitHub Repo stars](https://img.shields.io/github/stars/l0o0/jasminum?label=jasminum&style=flat-square)](https://github.com/l0o0/jasminum) [![GitHub Repo stars](https://img.shields.io/github/stars/lifan0127/ai-research-assistant?label=ai-research-assistant&style=flat-square)](https://github.com/lifan0127/ai-research-assistant) -📖 [Plugin Development Documentation](https://zotero.yuque.com/books/share/8d230829-6004-4934-b4c6-685a7001bfa0/vec88d) (Chinese, provides English translation) +If you are using this repo, I recommended that you put the following badge on your README: -🛠️ [Zotero Plugin Toolkit](https://github.com/windingwind/zotero-plugin-toolkit) | [API Documentation](https://github.com/windingwind/zotero-plugin-toolkit/blob/master/docs/zotero-plugin-toolkit.md) - -ℹ️ [Zotero Type Definitions](https://github.com/windingwind/zotero-types) - -📜 [Zotero Source Code](https://github.com/zotero/zotero) - -📌 [Zotero Plugin Template](https://github.com/windingwind/zotero-plugin-template) (This repo) - -> 👁 Watch this repo so that you can be notified whenever there are fixes & updates. - -If you are using this repo, I recommended that you put this badge ([![Using Zotero Plugin Template](https://img.shields.io/badge/Using-Zotero%20Plugin%20Template-blue?style=flat-square&logo=github)](https://github.com/windingwind/zotero-plugin-template)) on your README: +[![Using Zotero Plugin Template](https://img.shields.io/badge/Using-Zotero%20Plugin%20Template-blue?style=flat-square&logo=github)](https://github.com/windingwind/zotero-plugin-template) ```md [![Using Zotero Plugin Template](https://img.shields.io/badge/Using-Zotero%20Plugin%20Template-blue?style=flat-square&logo=github)](https://github.com/windingwind/zotero-plugin-template) @@ -50,19 +54,21 @@ If you are using this repo, I recommended that you put this badge ([![Using Zote ## Features -> ❗The localization system is upgraded (dtd is deprecated and we do not use .properties anymore). Only supports Zotero 7.0.0-beta.12 or higher now. If you want to support Zotero 6, you may need to use `dtd`, `properties`, and `ftl` at the same time. See the staled branch `zotero6-bootstrap`. - - 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)); +- ⭐ [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)); + - Full type definition support for the whole Zotero project, which is written in JavaScript (using [zotero-types](https://github.com/windingwind/zotero-types)); - Global variables and environment setup; -- Plugin build/test/release workflow: - - Automatically generate/update plugin id/version, update configrations, and set environment variables(`development/production`); +- Plugin develop/build/release workflow: + - 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)); + - Automatically release to GitHub (using [release-it](https://github.com/release-it/release-it)); +- Prettier and ES Lint integration. + +> [!warning] +> The localization system is upgraded (dtd is deprecated and we do not use .properties anymore). Only supports Zotero 7.0.0-beta.12 or higher now. If you want to support Zotero 6, you may need to use `dtd`, `properties`, and `ftl` at the same time. See the staled branch `zotero6-bootstrap`. ## Examples @@ -129,80 +135,60 @@ Activate with `Shift+P`. ## Quick Start Guide -### Install Pre-built `xpi` +### 0 Requirement -See how the examples work by directly downloading the `xpi` file from GitHub release and install it to your Zotero. +1. Install a beta version of Zotero: +2. Install [Node.js](https://nodejs.org/en/) and Git -This is also how your plugin will be released and used by others. +> [!note] +> This guide assumes that you have an initial understanding of the basic structure and workings of the Zotero plugin. If you don't, please refer to the [documentation](https://www.zotero.org/support/dev/zotero_7_for_developers) and official plugin examples [Make It Red](https://github.com/zotero/make-it-red) first. -> The release do not promise any real functions. It is probably not up-to-date. -> -> The `xpi` package is a zip file. However, please don't modify it directly. Modify the source code and build it. +### 1 Creat Your Repo -### Build from Source +1. Click `Use this template` +2. Git clone your new repo +
+ 💡 Start with GitHub Codespace -- Fork this repo/Click `Use this template`; -- Git clone the forked repo; -- Enter the repo folder; -
-💡 Start with GitHub Codespace + _GitHub CodeSpace_ enables you getting started without the need to download code/IDE/dependencies locally. -_GitHub CodeSpace_ enables you getting started without the need to download code/IDE/dependencies locally. + Replace the steps above and build you first plugin in 30 seconds! -Replace the steps above and build you first plugin in 30 seconds! + - Goto top of the [homepage](https://github.com/windingwind/zotero-plugin-template), click the green button `Use this template`, click `Open in codespace`. You may need to login to your GitHub account. + - Wait for codespace to load. -- Goto top of the [homepage](https://github.com/windingwind/zotero-plugin-template), click the green button `Use this template`, click `Open in codespace`. You may need to login to your GitHub account. -- Wait for codespace to load. +
-
+3. Enter the repo folder -- Modify the settings in `./package.json`, including: +### 2 Config Template Settings and Enviroment - ```json5 - { - version, - author, - description, - homepage, - config { - releasepage, // URL to releases(`.xpi`) - updaterdf, // URL to update.json - addonName, // name to be displayed in the plugin manager - addonID, // ID to avoid conflict. IMPORTANT! - addonRef, // e.g. Element ID prefix - addonInstance // the plugin's root instance: Zotero.${addonInstance} - } - } - ``` +1. Modify the settings in `./package.json`, including: - > Be careful to set the addonID and addonRef to avoid conflict. + ```json5 + { + version: "", // to 0.0.0 + author: "", + description: "", + homepage: "", + config: { + addonName: "", // name to be displayed in the plugin manager + addonID: "", // ID to avoid conflict. IMPORTANT! + addonRef: "", // e.g. Element ID prefix + addonInstance: "", // the plugin's root instance: Zotero.${addonInstance} + prefsPrefix: "extensions.zotero.${addonRef}", // the prefix of prefs + releasePage: "", // URL to releases + updateJSON: "", // URL to update.json + }, + } + ``` -- Run `npm install` to set up the plugin and install dependencies. If you don't have NodeJS installed, please download it [here](https://nodejs.org/en/); -- Run `npm run build` to build the plugin in production mode. Run `npm run build-dev` to build the plugin in development mode. The xpi for installation and the built code is under `build` folder. + > [!warning] + > Be careful to set the addonID and addonRef to avoid conflict. - > What the difference between dev & prod? - > - > - This environment variable is stored in `Zotero.${addonInstance}.data.env`. The outputs to console is disabled in prod mode. - > - You can decide what users cannot see/use based on this variable. + If you need to host your XPI packages outside of GitHub, remove `releasePage` and add `updateLink` with the value set to your XPI download URL. -### Release - -To build and release, use - -```shell -# A release-it command: version increase, npm run build, git push, and GitHub release -# You need to set the environment variable GITHUB_TOKEN https://github.com/settings/tokens -# release-it: https://github.com/release-it/release-it -npm run release -``` - -### Setup Development Environment - -1. Install a beta version of Zotero: (Zotero 7 beta: ) - -2. Install Firefox 102 (for Zotero 7) - -3. Copy zotero command line config file. Modify the commands that starts your installation of the beta Zotero. +2. Copy zotero command line config file. Modify the commands that starts your installation of the beta Zotero. > (Optional) Do this only once: Start the beta Zotero with `/path/to/zotero -p`. Create a new profile and use it as your development profile. > Put the path of the profile into the `profilePath` in `zotero-cmd.json` to specify which profile to use. @@ -212,27 +198,26 @@ npm run release vim ./scripts/zotero-cmd.json ``` -4. Build plugin and restart Zotero with `npm run restart`. +3. Install dependencies with `npm install` -5. Launch Firefox 102 (Zotero 7) + > If you are using `pnpm` as the package manager for your project, you need to add `public-hoist-pattern[]=*@types/bluebird*` to `.npmrc`, see . -6. In Firefox, go to devtools, go to settings, click "enable remote debugging" and the one next to it that's also about debugging +### 3 Coding - > Enter `about:debugging#/setup` in FF 102. +Start development server with `npm start`, it will: -7. In Zotero, go to setting, advanced, config editor, look up "debugging" and click on "allow remote debugging". +- Prebuild the plugin in development mode +- Start Zotero with plugin loaded from `build/` +- Open devtool +- Watch `src/**` and `addon/**`. + - If `src/**` changed, run esbuild and reload + - If `addon/**` has changed, rebuild the plugin (in development mode) and reload -8. Connect to Zotero in Firefox. In FF 102, enter `localhost:6100` in the bottom input of remote-debugging page and click `add`. - -9. Click `connect` in the leftside-bar of Firefox remote-debugging page. - -10. Click "Inspect Main Process" - -### Auto Hot Reload +#### 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`) +1. Run `npm start`. 2. Coding. (Yes, that's all) When file changes are detected in `src` or `addon`, the plugin will be automatically compiled and reloaded. @@ -240,27 +225,73 @@ When file changes are detected in `src` or `addon`, the plugin will be automatic
💡 Steps to add this feature to an existing plugin -1. Copy `scripts/reload.mjs` -2. Copy `reload`, `watch`, and `start-watch` commands in `package.json` -3. Run `npm install --save-dev chokidar-cli` +1. Copy `scripts/**.mjs` +2. Copy `server`, `build`, and `stop` commands in `package.json` +3. Run `npm install --save-dev chokidar` 4. Done.
-### Debug in Zotero +#### Debug in Zotero You can also: -- Test code snipastes in Tools->Developer->Run Javascript; +- Test code snipastes in Tools -> Developer -> Run Javascript; - Debug output with `Zotero.debug()`. Find the outputs in Help->Debug Output Logging->View Output; - Debug UI. Zotero is built on the Firefox XUL framework. Debug XUL UI with software like [XUL Explorer](https://udn.realityripple.com/docs/Archive/Mozilla/XUL_Explorer). > XUL Documentation: +### 4 Build + +Run `npm run build` to build the plugin in production mode, and the xpi for installation and the built code is under `build` folder. + +Steps in `scripts/build.mjs`: + +- Create/empty `build/`. +- Copy `addon/**` to `build/addon/**` +- Replace placeholders: use `replace-in-file` to replace keywords and configurations defined in `package.json` in non-build files (`xhtml`, `json`, et al.). +- Prepare locale files to [avaid conflict](https://www.zotero.org/support/dev/zotero_7_for_developers#avoiding_localization_conflicts) + - Rename `**/*.flt` to `**/${addonRef}-*.flt` + - Prefix each fluent message with `addonRef-` +- Use Esbuild to build `.ts` source code to `.js`, build `src/index.ts` to `./build/addon/chrome/content/scripts`. +- (Production mode only) Zip the `./build/addon` to `./build/*.xpi` +- (Production mode only) Prepare `update.json` or `update-beta.json` + +> [!note] +> +> **What's the difference between dev & prod?** +> +> - This environment variable is stored in `Zotero.${addonInstance}.data.env`. The outputs to console is disabled in prod mode. +> - You can decide what users cannot see/use based on this variable. +> - In production mode, the build script will pack the plugin and update the `update.json` + +### 5 Release + +To build and release, use + +```shell +# A release-it command: version increase, npm run build, git push, and GitHub release +# release-it: https://github.com/release-it/release-it +npm run release +``` + +> [!note] +> In this template, release-it is configured to locally bump the version, build, and push commits and git.tags, subsequently GitHub Action will rebuild the plugin and publish the XPI to GitHub Release. +> +> If you need to release a locally built XPI, set `release-it.github.release` to `true` in `package.json` and remove `.github/workflows/release.yml`. Besides that, you need to set the environment variable `GITHUB_TOKEN`, get it in + +#### About Prerelease + +The template defines `prerelease` as the beta version of the plugin, when you select a `prerelease` version in release-it (with `-` in the version number), the build script will create a new `update-beta.json` for prerelease use, which ensures that users of the regular version won't be able to update to the beta, only users who have manually downloaded and installed the beta will be able to update to the next beta automatically. When the next regular release is updated, both `update.json` and `update-beta.json` will be updated so that both regular and beta users can update to the new regular release. + +> [!warning] +> Strictly, distinguishing between Zotero 6 and Zotero 7 compatible plugin versions should be done by configuring `applications.zotero.strict_min_version` in `addons.__addonID__.updates[]` of `update.json` respectively, so that Zotero recognizes it properly, see . + ## Details ### About Hooks -> See also [`src/hooks.ts`](https://github.com/windingwind/zotero-plugin-template/blob/bootstrap/src/hooks.ts) +> See also [`src/hooks.ts`](https://github.com/windingwind/zotero-plugin-template/blob/main/src/hooks.ts) 1. When install/enable/startup triggered from Zotero, `bootstrap.js` > `startup` is called - Wait for Zotero ready @@ -274,7 +305,7 @@ You can also: ### About Global Variables -> See also [`src/index.ts`](https://github.com/windingwind/zotero-plugin-template/blob/bootstrap/src/index.ts) +> See also [`src/index.ts`](https://github.com/windingwind/zotero-plugin-template/blob/main/src/index.ts) The bootstrapped plugin runs in a sandbox, which does not have default global variables like `Zotero` or `window`, which we used to have in the overlay plugins' window environment. @@ -297,20 +328,6 @@ createElement(document, "hbox"); // returns XUL.Box createElement(document, "button", { namespace: "xul" }); // manually set namespace. returns XUL.Button ``` -### About Build - -Use Esbuild to build `.ts` source code to `.js`. - -Use `replace-in-file` to replace keywords and configurations defined in `package.json` in non-build files (`xhtml`, `.flt`, et. al.). - -Steps in `scripts/build.mjs`: - -1. Clean `./build` -2. Copy `./addon` to `./build` -3. Esbuild to `./build/addon/chrome/content/scripts` -4. Replace `__buildVersion__` and `__buildTime__` in `./build/addon` -5. Zip the `./build/addon` to `./build/*.xpi` - ### About Zotero API Zotero docs are outdated and incomplete. Clone and search the keyword globally. @@ -367,11 +384,14 @@ This section shows the directory structure of a template. | `-- prefs.js |-- build/ # build dir |-- scripts # scripts for dev -| |-- build.mjs # esbuild and replace -| |-- reload.mjs -| |-- start.mjs -| |-- stop.mjs -| `-- zotero-cmd-default.json +| |-- build.mjs # script to build plugin +| |-- scripts.mjs # scripts send to Zotero, such as reload, openDevTool, etc +| |-- server.mjs # script to start a development server +| |-- start.mjs # script to start Zotero process +| |-- stop.mjs # script to kill Zotero process +| |-- utils.mjs # utils functions for dev scripts +| |-- update-template.json # template of `update.json` +| `-- zotero-cmd-default.json # example of local env |-- src # source code | |-- addon.ts # base class | |-- hooks.ts # lifecycle hooks @@ -387,7 +407,6 @@ This section shows the directory structure of a template. |-- tsconfig.json # https://code.visualstudio.com/docs/languages/jsconfig |-- typings # ts typings | `-- global.d.ts -|-- update-template.json # template of `update.json` `-- update.json ``` diff --git a/package.json b/package.json index 1a26e16..86a2b4b 100644 --- a/package.json +++ b/package.json @@ -9,26 +9,16 @@ "addonInstance": "AddonTemplate", "prefsPrefix": "extensions.zotero.addontemplate", "releasePage": "https://github.com/windingwind/zotero-addon-template/releases", - "updateJSON": "https://raw.githubusercontent.com/windingwind/zotero-addon-template/main/update.json", - "updateBetaJSON": "https://raw.githubusercontent.com/windingwind/zotero-addon-template/main/update-beta.json" + "updateJSON": "https://raw.githubusercontent.com/windingwind/zotero-addon-template/main/update.json" }, "main": "src/index.ts", "scripts": { - "build-dev": "cross-env NODE_ENV=development node scripts/build.mjs", - "build-prod": "cross-env NODE_ENV=production node scripts/build.mjs", - "build": "concurrently -c auto npm:build-prod npm:tsc", - "tsc": "tsc --noEmit", - "start": "node scripts/start.mjs", - "start-watch": "npm run build-dev && concurrently -c auto npm:start npm:watch", + "start": "node scripts/server.mjs", + "build": "tsc --noEmit && node scripts/build.mjs production", "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", + "release": "release-it", "update-deps": "npm update --save" }, "repository": { @@ -48,10 +38,8 @@ "@types/node": "^20.10.4", "@typescript-eslint/eslint-plugin": "^6.13.2", "@typescript-eslint/parser": "^6.13.2", - "chokidar-cli": "^3.0.0", + "chokidar": "^3.5.3", "compressing": "^1.10.0", - "concurrently": "^8.2.2", - "cross-env": "^7.0.3", "esbuild": "^0.19.8", "eslint": "^8.55.0", "eslint-config-prettier": "^9.1.0", @@ -60,5 +48,88 @@ "replace-in-file": "^7.0.2", "typescript": "^5.3.3", "zotero-types": "^1.3.7" + }, + "eslintConfig": { + "env": { + "browser": true, + "es2021": true + }, + "root": true, + "extends": [ + "eslint:recommended", + "plugin:@typescript-eslint/recommended", + "prettier" + ], + "overrides": [], + "parser": "@typescript-eslint/parser", + "parserOptions": { + "ecmaVersion": "latest", + "sourceType": "module" + }, + "plugins": [ + "@typescript-eslint" + ], + "rules": { + "@typescript-eslint/ban-ts-comment": [ + "warn", + { + "ts-expect-error": "allow-with-description", + "ts-ignore": "allow-with-description", + "ts-nocheck": "allow-with-description", + "ts-check": "allow-with-description" + } + ], + "@typescript-eslint/no-unused-vars": "off", + "@typescript-eslint/no-explicit-any": [ + "off", + { + "ignoreRestArgs": true + } + ], + "@typescript-eslint/no-non-null-assertion": "off" + }, + "ignorePatterns": [ + "**/build/**", + "**/logs/**", + "**/dist/**", + "**/node_modules/**", + "**/scripts/**", + "**/*.js", + "**/*.bak" + ] + }, + "prettier": { + "printWidth": 80, + "tabWidth": 2, + "endOfLine": "lf", + "overrides": [ + { + "files": [ + "*.xhtml" + ], + "options": { + "htmlWhitespaceSensitivity": "css" + } + } + ] + }, + "release-it": { + "git": { + "tagName": "v${version}" + }, + "npm": { + "publish": false + }, + "github": { + "release": false, + "assets": [ + "build/*.xpi" + ] + }, + "hooks": { + "before:init": "npm run lint", + "after:bump": "npm run build", + "after:release": "echo Successfully released ${name} v${version} to ${repo.repository}." + } } } diff --git a/scripts/build.mjs b/scripts/build.mjs index 8e61172..c1c8521 100644 --- a/scripts/build.mjs +++ b/scripts/build.mjs @@ -1,104 +1,68 @@ -import { build } from "esbuild"; -import { zip } from "compressing"; -import path from "path"; +import details from "../package.json" assert { type: "json" }; import { - existsSync, - lstatSync, - writeFileSync, - readFileSync, - mkdirSync, - readdirSync, - rmSync, - renameSync, -} from "fs"; + Logger, + clearFolder, + copyFileSync, + copyFolderRecursiveSync, + dateFormat, +} from "./utils.mjs"; +import { zip } from "compressing"; +import { build } from "esbuild"; +import { existsSync, readdirSync, renameSync } from "fs"; +import path from "path"; import { env, exit } from "process"; import replaceInFile from "replace-in-file"; + const { replaceInFileSync } = replaceInFile; -import details from "../package.json" assert { type: "json" }; -const { name, author, description, homepage, version, config } = details; +process.env.NODE_ENV = + process.argv[2] === "production" ? "production" : "development"; -const t = new Date(); -const buildTime = dateFormat("YYYY-mm-dd HH:MM:SS", new Date()); const buildDir = "build"; +const { name, author, description, homepage, version, config } = details; const isPreRelease = version.includes("-"); -// If it is a pre-release, use update-beta.json -config.updateURL = isPreRelease ? config.updateBetaJSON : config.updateJSON; +function replaceString(buildTime) { + const replaceFrom = [ + /__author__/g, + /__description__/g, + /__homepage__/g, + /__buildVersion__/g, + /__buildTime__/g, + ]; + const replaceTo = [author, description, homepage, version, buildTime]; -const updateJSONFile = isPreRelease ? "update-beta.json" : "update.json"; -const updateLink = isPreRelease - ? `${config.releasePage}/download/v${version}/${name}.xpi` - : `${config.releasePage}/latest/download/${name}.xpi`; + config.updateURL = isPreRelease + ? config.updateJSON.replace("update.json", "update-beta.json") + : config.updateJSON; -function copyFileSync(source, target) { - var targetFile = target; + replaceFrom.push( + ...Object.keys(config).map((k) => new RegExp(`__${k}__`, "g")), + ); + replaceTo.push(...Object.values(config)); - // If target is a directory, a new file with the same name will be created - if (existsSync(target)) { - if (lstatSync(target).isDirectory()) { - targetFile = path.join(target, path.basename(source)); - } - } + const replaceResult = replaceInFileSync({ + files: [ + `${buildDir}/addon/**/*.xhtml`, + `${buildDir}/addon/**/*.json`, + `${buildDir}/addon/prefs.js`, + `${buildDir}/addon/manifest.json`, + `${buildDir}/addon/bootstrap.js`, + ], + from: replaceFrom, + to: replaceTo, + countMatches: true, + }); - writeFileSync(targetFile, readFileSync(source)); + // Logger.debug( + // "[Build] Run replace in ", + // replaceResult.filter((f) => f.hasChanged).map((f) => `${f.file} : ${f.numReplacements} / ${f.numMatches}`), + // ); } -function copyFolderRecursiveSync(source, target) { - var files = []; - - // Check if folder needs to be created or integrated - var targetFolder = path.join(target, path.basename(source)); - if (!existsSync(targetFolder)) { - mkdirSync(targetFolder); - } - - // Copy - if (lstatSync(source).isDirectory()) { - files = readdirSync(source); - files.forEach(function (file) { - var curSource = path.join(source, file); - if (lstatSync(curSource).isDirectory()) { - copyFolderRecursiveSync(curSource, targetFolder); - } else { - copyFileSync(curSource, targetFolder); - } - }); - } -} - -function clearFolder(target) { - if (existsSync(target)) { - rmSync(target, { recursive: true, force: true }); - } - - mkdirSync(target, { recursive: true }); -} - -function dateFormat(fmt, date) { - let ret; - const opt = { - "Y+": date.getFullYear().toString(), - "m+": (date.getMonth() + 1).toString(), - "d+": date.getDate().toString(), - "H+": date.getHours().toString(), - "M+": date.getMinutes().toString(), - "S+": date.getSeconds().toString(), - }; - for (let k in opt) { - ret = new RegExp("(" + k + ")").exec(fmt); - if (ret) { - fmt = fmt.replace( - ret[1], - ret[1].length == 1 ? opt[k] : opt[k].padStart(ret[1].length, "0"), - ); - } - } - return fmt; -} - -function renameLocaleFiles() { +function prepareLocaleFiles() { + // Walk the builds/addon/locale folder's sub folders and rename *.ftl to addonRef-*.ftl const localeDir = path.join(buildDir, "addon/locale"); const localeFolders = readdirSync(localeDir, { withFileTypes: true }) .filter((dirent) => dirent.isDirectory()) @@ -121,48 +85,6 @@ function renameLocaleFiles() { } } } -} - -function replaceString() { - const replaceFrom = [ - /__author__/g, - /__description__/g, - /__homepage__/g, - /__buildVersion__/g, - /__buildTime__/g, - /__updateLink__/g, - ]; - const replaceTo = [ - author, - description, - homepage, - version, - buildTime, - updateLink, - ]; - - replaceFrom.push( - ...Object.keys(config).map((k) => new RegExp(`__${k}__`, "g")), - ); - replaceTo.push(...Object.values(config)); - - const optionsAddon = { - files: [ - `${buildDir}/addon/**/*.xhtml`, - `${buildDir}/addon/**/*.html`, - `${buildDir}/addon/**/*.json`, - `${buildDir}/addon/prefs.js`, - `${buildDir}/addon/manifest.json`, - `${buildDir}/addon/bootstrap.js`, - ], - from: replaceFrom, - to: replaceTo, - countMatches: true, - }; - - optionsAddon.files.push(updateJSONFile); - - const replaceResult = replaceInFileSync(optionsAddon); const localeMessage = new Set(); const localeMessageMiss = new Set(); @@ -205,43 +127,83 @@ function replaceString() { }, }); - console.log( - "[Build] Run replace in ", - replaceResult - .filter((f) => f.hasChanged) - .map((f) => `${f.file} : ${f.numReplacements} / ${f.numMatches}`), - replaceResultFlt.filter((f) => f.hasChanged).map((f) => `${f.file} : OK`), - replaceResultXhtml.filter((f) => f.hasChanged).map((f) => `${f.file} : OK`), + Logger.debug( + "[Build] Prepare locale files OK", + // replaceResultFlt.filter((f) => f.hasChanged).map((f) => `${f.file} : OK`), + // replaceResultXhtml.filter((f) => f.hasChanged).map((f) => `${f.file} : OK`), ); if (localeMessageMiss.size !== 0) { - console.warn( - `[Build] [Warn] Fluent message [${new Array( + Logger.warn( + `[Build] Fluent message [${new Array( ...localeMessageMiss, )}] do not exsit in addon's locale files.`, ); } } -async function esbuild() { - await build({ - entryPoints: ["src/index.ts"], - define: { - __env__: `"${env.NODE_ENV}"`, - }, - bundle: true, - target: "firefox102", - outfile: path.join( - buildDir, - `addon/chrome/content/scripts/${config.addonRef}.js`, - ), - // Don't turn minify on - // minify: true, - }).catch(() => exit(1)); +function prepareUpdateJson() { + // If it is a pre-release, use update-beta.json + if (!isPreRelease) { + copyFileSync("scripts/update-template.json", "update.json"); + } + if (existsSync("update-beta.json") || isPreRelease) { + copyFileSync("scripts/update-template.json", "update-beta.json"); + } + + const updateLink = + config.updateLink ?? isPreRelease + ? `${config.releasePage}/download/v${version}/${name}.xpi` + : `${config.releasePage}/latest/download/${name}.xpi`; + + const replaceResult = replaceInFileSync({ + files: [ + "update-beta.json", + isPreRelease ? "pass" : "update.json", + `${buildDir}/addon/manifest.json`, + ], + from: [ + /__addonID__/g, + /__buildVersion__/g, + /__updateLink__/g, + /__updateURL__/g, + ], + to: [config.addonID, version, updateLink, config.updateURL], + countMatches: true, + }); + + Logger.debug( + `[Build] Prepare Update.json for ${ + isPreRelease + ? "\u001b[31m Prerelease \u001b[0m" + : "\u001b[32m Release \u001b[0m" + }`, + replaceResult + .filter((f) => f.hasChanged) + .map((f) => `${f.file} : ${f.numReplacements} / ${f.numMatches}`), + ); } -async function main() { - console.log( +export const esbuildOptions = { + entryPoints: ["src/index.ts"], + define: { + __env__: `"${env.NODE_ENV}"`, + }, + bundle: true, + target: "firefox102", + outfile: path.join( + buildDir, + `addon/chrome/content/scripts/${config.addonRef}.js`, + ), + // Don't turn minify on + minify: env.NODE_ENV === "production", +}; + +export async function main() { + const t = new Date(); + const buildTime = dateFormat("YYYY-mm-dd HH:MM:SS", new Date()); + + Logger.info( `[Build] BUILD_DIR=${buildDir}, VERSION=${version}, BUILD_TIME=${buildTime}, ENV=${[ env.NODE_ENV, ]}`, @@ -250,37 +212,37 @@ async function main() { clearFolder(buildDir); copyFolderRecursiveSync("addon", buildDir); + replaceString(buildTime); + Logger.debug("[Build] Replace OK"); - copyFileSync("update-template.json", updateJSONFile); + prepareLocaleFiles(); - await esbuild(); + await build(esbuildOptions); + Logger.debug("[Build] Run esbuild OK"); - console.log("[Build] Run esbuild OK"); + Logger.debug("[Build] Addon prepare OK"); - replaceString(); + if (process.env.NODE_ENV === "production") { + await zip.compressDir( + path.join(buildDir, "addon"), + path.join(buildDir, `${name}.xpi`), + { + ignoreBase: true, + }, + ); + Logger.debug("[Build] Addon pack OK"); - console.log("[Build] Replace OK"); + prepareUpdateJson(); - // Walk the builds/addon/locale folder's sub folders and rename *.ftl to addonRef-*.ftl - renameLocaleFiles(); - - console.log("[Build] Addon prepare OK"); - - await zip.compressDir( - path.join(buildDir, "addon"), - path.join(buildDir, `${name}.xpi`), - { - ignoreBase: true, - }, - ); - - console.log("[Build] Addon pack OK"); - console.log( - `[Build] Finished in ${(new Date().getTime() - t.getTime()) / 1000} s.`, - ); + Logger.debug( + `[Build] Finished in ${(new Date().getTime() - t.getTime()) / 1000} s.`, + ); + } } -main().catch((err) => { - console.log(err); - exit(1); -}); +if (process.env.NODE_ENV === "production") { + main().catch((err) => { + Logger.error(err); + exit(1); + }); +} diff --git a/scripts/reload.mjs b/scripts/reload.mjs deleted file mode 100644 index 5cf84c7..0000000 --- a/scripts/reload.mjs +++ /dev/null @@ -1,34 +0,0 @@ -import { exit } from "process"; -import { execSync } from "child_process"; -import details from "../package.json" assert { type: "json" }; -import cmd from "./zotero-cmd.json" assert { type: "json" }; - -const { addonID, addonName } = details.config; -const { version } = details; -const { zoteroBinPath, profilePath } = cmd.exec; - -const startZotero = `"${zoteroBinPath}" --debugger --purgecaches -profile "${profilePath}"`; - -const script = ` -(async () => { - Services.obs.notifyObservers(null, "startupcache-invalidate", null); - const { AddonManager } = ChromeUtils.import("resource://gre/modules/AddonManager.jsm"); - const addon = await AddonManager.getAddonByID("${addonID}"); - await addon.reload(); - 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/scripts.mjs b/scripts/scripts.mjs new file mode 100644 index 0000000..126d8e6 --- /dev/null +++ b/scripts/scripts.mjs @@ -0,0 +1,75 @@ +import details from "../package.json" assert { type: "json" }; + +const { addonID, addonName } = details.config; +const { version } = details; + +export const reloadScript = ` +(async () => { +Services.obs.notifyObservers(null, "startupcache-invalidate", null); +const { AddonManager } = ChromeUtils.import("resource://gre/modules/AddonManager.jsm"); +const addon = await AddonManager.getAddonByID("${addonID}"); +await addon.reload(); +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); +})()`; + +export const openDevToolScript = ` +(async () => { + +// const { BrowserToolboxLauncher } = ChromeUtils.import( +// "resource://devtools/client/framework/browser-toolbox/Launcher.jsm", +// ); +// BrowserToolboxLauncher.init(); +// TODO: Use the above code to open the devtool after https://github.com/zotero/zotero/pull/3387 + +Zotero.Prefs.set("devtools.debugger.remote-enabled", true, true); +Zotero.Prefs.set("devtools.debugger.remote-port", 6100, true); +Zotero.Prefs.set("devtools.debugger.prompt-connection", false, true); +Zotero.Prefs.set("devtools.debugger.chrome-debugging-websocket", false, true); + +env = + Services.env || + Cc["@mozilla.org/process/environment;1"].getService(Ci.nsIEnvironment); + +env.set("MOZ_BROWSER_TOOLBOX_PORT", 6100); +Zotero.openInViewer( + "chrome://devtools/content/framework/browser-toolbox/window.html", + { + onLoad: (doc) => { + doc.querySelector("#status-message-container").style.visibility = + "collapse"; + let toolboxBody; + waitUntil( + () => { + toolboxBody = doc + .querySelector(".devtools-toolbox-browsertoolbox-iframe") + ?.contentDocument?.querySelector(".theme-body"); + return toolboxBody; + }, + () => { + toolboxBody.style = "pointer-events: all !important"; + } + ); + }, + } +); + +function waitUntil(condition, callback, interval = 100, timeout = 10000) { + const start = Date.now(); + const intervalId = setInterval(() => { + if (condition()) { + clearInterval(intervalId); + callback(); + } else if (Date.now() - start > timeout) { + clearInterval(intervalId); + } + }, interval); +} +})()`; diff --git a/scripts/server.mjs b/scripts/server.mjs new file mode 100644 index 0000000..8a94805 --- /dev/null +++ b/scripts/server.mjs @@ -0,0 +1,90 @@ +import { main as build, esbuildOptions } from "./build.mjs"; +import { openDevToolScript, reloadScript } from "./scripts.mjs"; +import { main as startZotero } from "./start.mjs"; +import { Logger } from "./utils.mjs"; +import cmd from "./zotero-cmd.json" assert { type: "json" }; +import { execSync } from "child_process"; +import chokidar from "chokidar"; +import { context } from "esbuild"; +import { exit } from "process"; + +process.env.NODE_ENV = "development"; + +const { zoteroBinPath, profilePath } = cmd.exec; + +const startZoteroCmd = `"${zoteroBinPath}" --debugger --purgecaches -profile "${profilePath}"`; + +async function watch() { + const watcher = chokidar.watch(["src/**", "addon/**"], { + ignored: /(^|[\/\\])\../, // ignore dotfiles + persistent: true, + }); + + let esbuildCTX = await context(esbuildOptions); + + watcher + .on("ready", () => { + Logger.info("Server Ready! \n"); + }) + .on("change", async (path) => { + Logger.info(`${path} changed.`); + if (path.startsWith("src")) { + await esbuildCTX.rebuild(); + } else if (path.startsWith("addon")) { + await build() + // Do not abort the watcher when errors occur in builds triggered by the watcher. + .catch((err) => { + Logger.error(err); + }); + } + // reload + reload(); + }) + .on("error", (err) => { + Logger.error("Server start failed!", err); + }); +} + +function reload() { + Logger.debug("Reloading..."); + const url = `zotero://ztoolkit-debug/?run=${encodeURIComponent( + reloadScript, + )}`; + const command = `${startZoteroCmd} -url "${url}"`; + execSync(command); +} + +function openDevTool() { + Logger.debug("Open dev tools..."); + const url = `zotero://ztoolkit-debug/?run=${encodeURIComponent( + openDevToolScript, + )}`; + const command = `${startZoteroCmd} -url "${url}"`; + execSync(command); +} + +async function main() { + // build + await build(); + + // start Zotero + startZotero(); + setTimeout(() => { + openDevTool(); + }, 2000); + + // watch + await watch(); +} + +main().catch((err) => { + Logger.error(err); + // execSync("node scripts/stop.mjs"); + exit(1); +}); + +process.on("SIGINT", (code) => { + execSync("node scripts/stop.mjs"); + Logger.info(`Server terminated with signal ${code}.`); + exit(0); +}); diff --git a/scripts/start.mjs b/scripts/start.mjs index 118b5a0..2b9782b 100644 --- a/scripts/start.mjs +++ b/scripts/start.mjs @@ -1,33 +1,36 @@ -import { execSync } from "child_process"; -import { exit } from "process"; -import { existsSync, writeFileSync, readFileSync, mkdirSync } from "fs"; -import path from "path"; import details from "../package.json" assert { type: "json" }; +import { Logger } from "./utils.mjs"; import cmd from "./zotero-cmd.json" assert { type: "json" }; +import { spawn } from "child_process"; +import { existsSync, readFileSync, writeFileSync } from "fs"; +import { clearFolder } from "./utils.mjs"; +import path from "path"; +import { exit } from "process"; const { addonID } = details.config; const { zoteroBinPath, profilePath, dataDir } = cmd.exec; +const logPath = "logs"; +const logFilePath = path.join(logPath, "zotero.log"); + if (!existsSync(zoteroBinPath)) { throw new Error("Zotero binary does not exist."); } -if (existsSync(profilePath)) { +if (!existsSync(profilePath)) { + throw new Error("The given Zotero profile does not exist."); +} + +function prepareDevEnv() { const addonProxyFilePath = path.join(profilePath, `extensions/${addonID}`); const buildPath = path.resolve("build/addon"); - if (!existsSync(path.join(buildPath, "./manifest.json"))) { - throw new Error( - `The built file does not exist, maybe you need to build the addon first.`, - ); - } - function writeAddonProxyFile() { writeFileSync(addonProxyFilePath, buildPath); - console.log( - `[info] Addon proxy file has been updated. - File path: ${addonProxyFilePath} - Addon path: ${buildPath} `, + Logger.debug( + `Addon proxy file has been updated. + File path: ${addonProxyFilePath} + Addon path: ${buildPath} `, ); } @@ -36,12 +39,6 @@ if (existsSync(profilePath)) { writeAddonProxyFile(); } } else { - if ( - existsSync(profilePath) && - !existsSync(path.join(profilePath, "extensions")) - ) { - mkdirSync(path.join(profilePath, "extensions")); - } writeAddonProxyFile(); } @@ -62,13 +59,47 @@ if (existsSync(profilePath)) { }); const updatedPrefs = filteredLines.join("\n"); writeFileSync(prefsPath, updatedPrefs, "utf-8"); - console.log("[info] The /prefs.js has been modified."); + Logger.debug("The /prefs.js has been modified."); } -} else { - throw new Error("The given Zotero profile does not exist."); } -const startZotero = `"${zoteroBinPath}" --debugger --purgecaches -profile "${profilePath}"`; +function prepareLog() { + clearFolder(logPath); + writeFileSync(logFilePath, ""); +} -execSync(startZotero); -exit(0); +export function main() { + prepareDevEnv(); + + prepareLog(); + + const zoteroProcess = spawn(zoteroBinPath, [ + "--debugger", + "--purgecaches", + "-profile", + profilePath, + ]); + + zoteroProcess.stdout.on("data", (data) => { + writeFileSync(logFilePath, data, { + flag: "a", + }); + }); + + zoteroProcess.stderr.on("data", (data) => { + writeFileSync(logFilePath, data, { + flag: "a", + }); + }); + + zoteroProcess.on("close", (code) => { + Logger.info(`Zotero terminated with code ${code}.`); + exit(0); + }); + + process.on("SIGINT", () => { + // Handle interrupt signal (Ctrl+C) to gracefully terminate Zotero process + zoteroProcess.kill(); + exit(); + }); +} diff --git a/scripts/stop.mjs b/scripts/stop.mjs index 197ef04..3b44ef2 100644 --- a/scripts/stop.mjs +++ b/scripts/stop.mjs @@ -1,14 +1,26 @@ -import process from "process"; -import { execSync } from "child_process"; +import { Logger, isRunning } from "./utils.mjs"; import cmd from "./zotero-cmd.json" assert { type: "json" }; +import { execSync } from "child_process"; +import process from "process"; + const { killZoteroWindows, killZoteroUnix } = cmd; -try { - if (process.platform === "win32") { - execSync(killZoteroWindows); +isRunning("zotero", (status) => { + if (status) { + killZotero(); } else { - execSync(killZoteroUnix); + Logger.warn("No Zotero running."); + } +}); + +function killZotero() { + try { + if (process.platform === "win32") { + execSync(killZoteroWindows); + } else { + execSync(killZoteroUnix); + } + } catch (e) { + Logger.error(e); } -} catch (e) { - console.error(e); } diff --git a/update-template.json b/scripts/update-template.json similarity index 100% rename from update-template.json rename to scripts/update-template.json diff --git a/scripts/utils.mjs b/scripts/utils.mjs new file mode 100644 index 0000000..d17288a --- /dev/null +++ b/scripts/utils.mjs @@ -0,0 +1,129 @@ +import { exec } from "child_process"; +import { + existsSync, + lstatSync, + mkdirSync, + readFileSync, + readdirSync, + rmSync, + writeFileSync, +} from "fs"; +import path from "path"; + +export function copyFileSync(source, target) { + var targetFile = target; + + // If target is a directory, a new file with the same name will be created + if (existsSync(target)) { + if (lstatSync(target).isDirectory()) { + targetFile = path.join(target, path.basename(source)); + } + } + + writeFileSync(targetFile, readFileSync(source)); +} + +export function copyFolderRecursiveSync(source, target) { + var files = []; + + // Check if folder needs to be created or integrated + var targetFolder = path.join(target, path.basename(source)); + if (!existsSync(targetFolder)) { + mkdirSync(targetFolder); + } + + // Copy + if (lstatSync(source).isDirectory()) { + files = readdirSync(source); + files.forEach(function (file) { + var curSource = path.join(source, file); + if (lstatSync(curSource).isDirectory()) { + copyFolderRecursiveSync(curSource, targetFolder); + } else { + copyFileSync(curSource, targetFolder); + } + }); + } +} + +export function clearFolder(target) { + if (existsSync(target)) { + rmSync(target, { recursive: true, force: true }); + } + + mkdirSync(target, { recursive: true }); +} + +export function dateFormat(fmt, date) { + let ret; + const opt = { + "Y+": date.getFullYear().toString(), + "m+": (date.getMonth() + 1).toString(), + "d+": date.getDate().toString(), + "H+": date.getHours().toString(), + "M+": date.getMinutes().toString(), + "S+": date.getSeconds().toString(), + }; + for (let k in opt) { + ret = new RegExp("(" + k + ")").exec(fmt); + if (ret) { + fmt = fmt.replace( + ret[1], + ret[1].length == 1 ? opt[k] : opt[k].padStart(ret[1].length, "0"), + ); + } + } + return fmt; +} + +export class Logger { + static log(...args) { + console.log(...args); + } + + // red + static error(...args) { + console.error("\u001b[31m [ERROR]", ...args, "\u001b[0m"); + } + + // yellow + static warn(...args) { + console.warn("\u001b[33m [WARN]", ...args, "\u001b[0m"); + } + + // blue + static debug(...args) { + console.log("\u001b[34m [DEBUG]\u001b[0m", ...args); + } + + // green + static info(...args) { + console.log("\u001b[32m [INFO]", ...args, "\u001b[0m"); + } + + // cyan + static trace(...args) { + console.log("\u001b[36m [TRACE]\u001b[0m", ...args); + } +} + +export function isRunning(query, cb) { + let platform = process.platform; + let cmd = ""; + switch (platform) { + case "win32": + cmd = `tasklist`; + break; + case "darwin": + cmd = `ps -ax | grep ${query}`; + break; + case "linux": + cmd = `ps -A`; + break; + default: + break; + } + exec(cmd, (err, stdout, stderr) => { + cb(stdout.toLowerCase().indexOf(query.toLowerCase()) > -1); + }); +} diff --git a/src/index.ts b/src/index.ts index cbc1423..f413754 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,14 +1,10 @@ import { BasicTool } from "zotero-plugin-toolkit/dist/basic"; import Addon from "./addon"; import { config } from "../package.json"; -import { waitUntil } from "./utils/wait"; const basicTool = new BasicTool(); if (!basicTool.getGlobal("Zotero")[config.addonInstance]) { - if (__env__ === "development") { - openDevTool(); - } defineGlobal("window"); defineGlobal("document"); defineGlobal("ZoteroPane"); @@ -29,46 +25,3 @@ function defineGlobal(name: string, getter?: () => any) { }, }); } - -function openDevTool() { - // const { BrowserToolboxLauncher } = ChromeUtils.import( - // "resource://devtools/client/framework/browser-toolbox/Launcher.jsm", - // ); - // BrowserToolboxLauncher.init(); - // TODO: Use the above code to open the devtool after https://github.com/zotero/zotero/pull/3387 - Zotero.Prefs.set("devtools.debugger.remote-enabled", true, true); - Zotero.Prefs.set("devtools.debugger.remote-port", 6100, true); - Zotero.Prefs.set("devtools.debugger.prompt-connection", false, true); - Zotero.Prefs.set("devtools.debugger.chrome-debugging-websocket", false, true); - - const env = - Services.env || - // @ts-ignore - mozIEnvironment is not in the types - Cc["@mozilla.org/process/environment;1"].getService(Ci.nsIEnvironment); - - env.set("MOZ_BROWSER_TOOLBOX_PORT", 6100); - Zotero.openInViewer( - "chrome://devtools/content/framework/browser-toolbox/window.html", - { - // @ts-ignore - onLoad is not in the types - onLoad: (doc: Document) => { - ( - doc.querySelector("#status-message-container") as HTMLDivElement - ).style.visibility = "collapse"; - let toolboxBody: HTMLIFrameElement; - waitUntil( - () => { - toolboxBody = doc - .querySelector(".devtools-toolbox-browsertoolbox-iframe") - // @ts-ignore - contentDocument is not in the types - ?.contentDocument?.querySelector(".theme-body"); - return !!toolboxBody; - }, - () => { - toolboxBody.setAttribute("style", "pointer-events: all !important"); - }, - ); - }, - }, - ); -} diff --git a/update-beta.json b/update-beta.json deleted file mode 100644 index 146d3d7..0000000 --- a/update-beta.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "addons": { - "addontemplate@euclpts.com": { - "updates": [ - { - "version": "1.0.0", - "update_link": "https://github.com/windingwind/zotero-addon-template/releases/latest/download/zotero-addon-template.xpi", - "applications": { - "zotero": { - "strict_min_version": "6.999" - } - } - } - ] - } - } -} From 3c8f1b3db3358a553e62e6e11556decb7d3c4f9b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 12 Dec 2023 15:55:24 +0800 Subject: [PATCH 17/36] build(deps-dev): bump release-it from 16.3.0 to 17.0.1 (#85) Bumps [release-it](https://github.com/release-it/release-it) from 16.3.0 to 17.0.1. - [Release notes](https://github.com/release-it/release-it/releases) - [Changelog](https://github.com/release-it/release-it/blob/main/CHANGELOG.md) - [Commits](https://github.com/release-it/release-it/compare/16.3.0...17.0.1) --- updated-dependencies: - dependency-name: release-it dependency-type: direct:development update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 86a2b4b..276063d 100644 --- a/package.json +++ b/package.json @@ -44,7 +44,7 @@ "eslint": "^8.55.0", "eslint-config-prettier": "^9.1.0", "prettier": "^3.1.0", - "release-it": "^16.3.0", + "release-it": "^17.0.1", "replace-in-file": "^7.0.2", "typescript": "^5.3.3", "zotero-types": "^1.3.7" From 133dde03c762f81a03ebc6f4fa67b4e919faa414 Mon Sep 17 00:00:00 2001 From: windingwind <33902321+windingwind@users.noreply.github.com> Date: Tue, 12 Dec 2023 15:58:01 +0800 Subject: [PATCH 18/36] Release 1.1.0 --- package.json | 2 +- update.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 276063d..d33f59b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "zotero-addon-template", - "version": "1.0.2", + "version": "1.1.0", "description": "Zotero Addon Template", "config": { "addonName": "Zotero Addon Template", diff --git a/update.json b/update.json index f5c857f..e5925c3 100644 --- a/update.json +++ b/update.json @@ -3,7 +3,7 @@ "addontemplate@euclpts.com": { "updates": [ { - "version": "1.0.2", + "version": "1.1.0", "update_link": "https://github.com/windingwind/zotero-addon-template/releases/latest/download/zotero-addon-template.xpi", "applications": { "zotero": { From 56c427b26acee7d19057aa419312ce12268e691f Mon Sep 17 00:00:00 2001 From: windingwind <33902321+windingwind@users.noreply.github.com> Date: Tue, 12 Dec 2023 22:30:41 +0800 Subject: [PATCH 19/36] add: DS_Store to gitignore --- .gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 6dfac6b..4c3136c 100644 --- a/.gitignore +++ b/.gitignore @@ -4,4 +4,5 @@ node_modules package-lock.json pnpm-lock.yaml yarn.lock -zotero-cmd.json \ No newline at end of file +zotero-cmd.json +.DS_Store \ No newline at end of file From 4eb662ea2f42e7ffa05da00a782af28ef387dcd5 Mon Sep 17 00:00:00 2001 From: windingwind <33902321+windingwind@users.noreply.github.com> Date: Tue, 12 Dec 2023 23:01:33 +0800 Subject: [PATCH 20/36] rename: cmd template --- README.md | 4 ++-- scripts/{zotero-cmd-default.json => zotero-cmd-template.json} | 0 2 files changed, 2 insertions(+), 2 deletions(-) rename scripts/{zotero-cmd-default.json => zotero-cmd-template.json} (100%) diff --git a/README.md b/README.md index 811d9a7..5937da4 100644 --- a/README.md +++ b/README.md @@ -194,7 +194,7 @@ Activate with `Shift+P`. > Put the path of the profile into the `profilePath` in `zotero-cmd.json` to specify which profile to use. ```sh - cp ./scripts/zotero-cmd-default.json ./scripts/zotero-cmd.json + cp ./scripts/zotero-cmd-template.json ./scripts/zotero-cmd.json vim ./scripts/zotero-cmd.json ``` @@ -391,7 +391,7 @@ This section shows the directory structure of a template. | |-- stop.mjs # script to kill Zotero process | |-- utils.mjs # utils functions for dev scripts | |-- update-template.json # template of `update.json` -| `-- zotero-cmd-default.json # example of local env +| `-- zotero-cmd-template.json # template of local env |-- src # source code | |-- addon.ts # base class | |-- hooks.ts # lifecycle hooks diff --git a/scripts/zotero-cmd-default.json b/scripts/zotero-cmd-template.json similarity index 100% rename from scripts/zotero-cmd-default.json rename to scripts/zotero-cmd-template.json From 57028c7ba883860e77b1dda02a46e9cff8fd10fb Mon Sep 17 00:00:00 2001 From: windingwind <33902321+windingwind@users.noreply.github.com> Date: Wed, 13 Dec 2023 21:23:19 +0800 Subject: [PATCH 21/36] chore: change default dev tool start delay --- scripts/server.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/server.mjs b/scripts/server.mjs index 8a94805..542ca44 100644 --- a/scripts/server.mjs +++ b/scripts/server.mjs @@ -71,7 +71,7 @@ async function main() { startZotero(); setTimeout(() => { openDevTool(); - }, 2000); + }, 5000); // watch await watch(); From 032aa74f6edd82d9834b9d1f61834f60bb563de3 Mon Sep 17 00:00:00 2001 From: windingwind <33902321+windingwind@users.noreply.github.com> Date: Wed, 13 Dec 2023 22:21:26 +0800 Subject: [PATCH 22/36] fix: devtool startup --- scripts/server.mjs | 5 +---- scripts/start.mjs | 11 ++++++++++- src/hooks.ts | 8 ++++++++ 3 files changed, 19 insertions(+), 5 deletions(-) diff --git a/scripts/server.mjs b/scripts/server.mjs index 542ca44..3f35430 100644 --- a/scripts/server.mjs +++ b/scripts/server.mjs @@ -68,10 +68,7 @@ async function main() { await build(); // start Zotero - startZotero(); - setTimeout(() => { - openDevTool(); - }, 5000); + startZotero(openDevTool); // watch await watch(); diff --git a/scripts/start.mjs b/scripts/start.mjs index 2b9782b..b916b5a 100644 --- a/scripts/start.mjs +++ b/scripts/start.mjs @@ -10,6 +10,9 @@ import { exit } from "process"; const { addonID } = details.config; const { zoteroBinPath, profilePath, dataDir } = cmd.exec; +// Keep in sync with the addon's onStartup +const loadDevToolWhen = `Plugin ${addonID} startup`; + const logPath = "logs"; const logFilePath = path.join(logPath, "zotero.log"); @@ -68,7 +71,9 @@ function prepareLog() { writeFileSync(logFilePath, ""); } -export function main() { +export function main(callback) { + let isZoteroReady = false; + prepareDevEnv(); prepareLog(); @@ -81,6 +86,10 @@ export function main() { ]); zoteroProcess.stdout.on("data", (data) => { + if (!isZoteroReady && data.toString().includes(loadDevToolWhen)) { + isZoteroReady = true; + callback(); + } writeFileSync(logFilePath, data, { flag: "a", }); diff --git a/src/hooks.ts b/src/hooks.ts index 7b000f9..9b781f3 100644 --- a/src/hooks.ts +++ b/src/hooks.ts @@ -16,6 +16,14 @@ async function onStartup() { Zotero.unlockPromise, Zotero.uiReadyPromise, ]); + + // TODO: Remove this after zotero#3387 is merged + if (__env__ === "development") { + // Keep in sync with the scripts/startup.mjs + const loadDevToolWhen = `Plugin ${config.addonID} startup`; + ztoolkit.log(loadDevToolWhen); + } + initLocale(); BasicExampleFactory.registerPrefs(); From 3d9f8a8a0df3410ac7f20819429c08b59d8df4d9 Mon Sep 17 00:00:00 2001 From: windingwind <33902321+windingwind@users.noreply.github.com> Date: Thu, 14 Dec 2023 22:33:10 +0800 Subject: [PATCH 23/36] update: registerShortcuts --- package.json | 12 +++---- src/hooks.ts | 7 ++--- src/modules/examples.ts | 70 +++++------------------------------------ 3 files changed, 16 insertions(+), 73 deletions(-) diff --git a/package.json b/package.json index d33f59b..7c50f07 100644 --- a/package.json +++ b/package.json @@ -32,22 +32,22 @@ }, "homepage": "https://github.com/windingwind/zotero-addon-template#readme", "dependencies": { - "zotero-plugin-toolkit": "^2.3.11" + "zotero-plugin-toolkit": "^2.3.15" }, "devDependencies": { "@types/node": "^20.10.4", - "@typescript-eslint/eslint-plugin": "^6.13.2", - "@typescript-eslint/parser": "^6.13.2", + "@typescript-eslint/eslint-plugin": "^6.14.0", + "@typescript-eslint/parser": "^6.14.0", "chokidar": "^3.5.3", "compressing": "^1.10.0", - "esbuild": "^0.19.8", + "esbuild": "^0.19.9", "eslint": "^8.55.0", "eslint-config-prettier": "^9.1.0", - "prettier": "^3.1.0", + "prettier": "^3.1.1", "release-it": "^17.0.1", "replace-in-file": "^7.0.2", "typescript": "^5.3.3", - "zotero-types": "^1.3.7" + "zotero-types": "^1.3.10" }, "eslintConfig": { "env": { diff --git a/src/hooks.ts b/src/hooks.ts index 9b781f3..91b9350 100644 --- a/src/hooks.ts +++ b/src/hooks.ts @@ -30,6 +30,8 @@ async function onStartup() { BasicExampleFactory.registerNotifier(); + KeyExampleFactory.registerShortcuts(); + await onMainWindowLoad(window); } @@ -48,8 +50,6 @@ async function onMainWindowLoad(win: Window): Promise { }) .show(); - KeyExampleFactory.registerShortcuts(); - await Zotero.Promise.delay(1000); popupWin.changeLine({ progress: 30, @@ -151,9 +151,6 @@ function onShortcuts(type: string) { case "smaller": KeyExampleFactory.exampleShortcutSmallerCallback(); break; - case "confliction": - KeyExampleFactory.exampleShortcutConflictingCallback(); - break; default: break; } diff --git a/src/modules/examples.ts b/src/modules/examples.ts index d493760..f69fba7 100644 --- a/src/modules/examples.ts +++ b/src/modules/examples.ts @@ -86,55 +86,17 @@ export class BasicExampleFactory { export class KeyExampleFactory { @example static registerShortcuts() { - const keysetId = `${config.addonRef}-keyset`; - const cmdsetId = `${config.addonRef}-cmdset`; - const cmdSmallerId = `${config.addonRef}-cmd-smaller`; // Register an event key for Alt+L - ztoolkit.Shortcut.register("event", { - id: `${config.addonRef}-key-larger`, - key: "L", - modifiers: "alt", - callback: (keyOptions) => { + ztoolkit.Keyboard.register((ev, keyOptions) => { + ztoolkit.log(ev, keyOptions.keyboard); + if (keyOptions.keyboard.equals("shift,l")) { addon.hooks.onShortcuts("larger"); - }, - }); - // Register an element key using for Alt+S - ztoolkit.Shortcut.register("element", { - id: `${config.addonRef}-key-smaller`, - key: "S", - modifiers: "alt", - xulData: { - document, - command: cmdSmallerId, - _parentId: keysetId, - _commandOptions: { - id: cmdSmallerId, - document, - _parentId: cmdsetId, - oncommand: `Zotero.${config.addonInstance}.hooks.onShortcuts('smaller')`, - }, - }, - }); - // Here we register an conflict key for Alt+S - // just to show how the confliction check works. - // This is something you should avoid in your plugin. - ztoolkit.Shortcut.register("event", { - id: `${config.addonRef}-key-smaller-conflict`, - key: "S", - modifiers: "alt", - callback: (keyOptions) => { - ztoolkit.getGlobal("alert")("Smaller! This is a conflict key."); - }, - }); - // Register an event key to check confliction - ztoolkit.Shortcut.register("event", { - id: `${config.addonRef}-key-check-conflict`, - key: "C", - modifiers: "alt", - callback: (keyOptions) => { - addon.hooks.onShortcuts("confliction"); - }, + } + if (ev.shiftKey && ev.key === "S") { + addon.hooks.onShortcuts("smaller"); + } }); + new ztoolkit.ProgressWindow(config.addonName) .createLine({ text: "Example Shortcuts: Alt+L/S/C", @@ -162,22 +124,6 @@ export class KeyExampleFactory { }) .show(); } - - @example - static exampleShortcutConflictingCallback() { - const conflictingGroups = ztoolkit.Shortcut.checkAllKeyConflicting(); - new ztoolkit.ProgressWindow("Check Key Conflicting") - .createLine({ - text: `${conflictingGroups.length} groups of conflicting keys found. Details are in the debug output/console.`, - }) - .show(-1); - ztoolkit.log( - "Conflicting:", - conflictingGroups, - "All keys:", - ztoolkit.Shortcut.getAll(), - ); - } } export class UIExampleFactory { From 6f3a6d112e27610ead8aa99cea8a3a9ec2c2dd40 Mon Sep 17 00:00:00 2001 From: Dae Date: Sat, 16 Dec 2023 21:48:07 -0500 Subject: [PATCH 24/36] update killZoteroUnix command to ignore grep pipe (#89) --- README.md | 1 + scripts/zotero-cmd-template.json | 2 +- src/hooks.ts | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 5937da4..5bbf905 100644 --- a/README.md +++ b/README.md @@ -43,6 +43,7 @@ This is a plugin template for [Zotero](https://www.zotero.org/). [![GitHub Repo stars](https://img.shields.io/github/stars/MuiseDestiny/zotero-figure?label=zotero-figure&style=flat-square)](https://github.com/MuiseDestiny/zotero-figure) [![GitHub Repo stars](https://img.shields.io/github/stars/l0o0/jasminum?label=jasminum&style=flat-square)](https://github.com/l0o0/jasminum) [![GitHub Repo stars](https://img.shields.io/github/stars/lifan0127/ai-research-assistant?label=ai-research-assistant&style=flat-square)](https://github.com/lifan0127/ai-research-assistant) +[![GitHub Repo stars](https://img.shields.io/github/stars/daeh/zotero-markdb-connect?label=zotero-markdb-connect&style=flat-square)](https://github.com/daeh/zotero-markdb-connect) If you are using this repo, I recommended that you put the following badge on your README: diff --git a/scripts/zotero-cmd-template.json b/scripts/zotero-cmd-template.json index c02a99f..27143f6 100644 --- a/scripts/zotero-cmd-template.json +++ b/scripts/zotero-cmd-template.json @@ -1,7 +1,7 @@ { "usage": "Copy and rename this file to zotero-cmd.json. Edit the cmd.", "killZoteroWindows": "taskkill /f /im zotero.exe", - "killZoteroUnix": "kill -9 $(ps -x | grep zotero)", + "killZoteroUnix": "kill -9 $(ps -x | grep '[z]otero' | awk '{print $1}')", "exec": { "@comment-zoteroBinPath": "Please input the path of the Zotero binary file in `zoteroBinPath`.", "@comment-zoteroBinPath-tip": "The path delimiter should be escaped as `\\` for win32. The path is `*/Zotero.app/Contents/MacOS/zotero` for MacOS.", diff --git a/src/hooks.ts b/src/hooks.ts index 91b9350..0f3faa6 100644 --- a/src/hooks.ts +++ b/src/hooks.ts @@ -180,7 +180,7 @@ function onDialogEvents(type: string) { // Add your hooks here. For element click, etc. // Keep in mind hooks only do dispatch. Don't add code that does real jobs in hooks. -// Otherwise the code would be hard to read and maintian. +// Otherwise the code would be hard to read and maintain. export default { onStartup, From be0b82f23de10c0582a7ddeb1f771d657622361b Mon Sep 17 00:00:00 2001 From: KikkiZ <64997288+KikkiZ@users.noreply.github.com> Date: Sun, 17 Dec 2023 19:19:26 +0800 Subject: [PATCH 25/36] Fixes issue #86 (#87) --- README.md | 8 +- doc/README-zhCN.md | 280 ++++++++++++++++++++++++--------------------- 2 files changed, 156 insertions(+), 132 deletions(-) diff --git a/README.md b/README.md index 5bbf905..9671d8b 100644 --- a/README.md +++ b/README.md @@ -69,7 +69,7 @@ If you are using this repo, I recommended that you put the following badge on yo - Prettier and ES Lint integration. > [!warning] -> The localization system is upgraded (dtd is deprecated and we do not use .properties anymore). Only supports Zotero 7.0.0-beta.12 or higher now. If you want to support Zotero 6, you may need to use `dtd`, `properties`, and `ftl` at the same time. See the staled branch `zotero6-bootstrap`. +> The localization system is upgraded (`dtd` is deprecated and we do not use `.properties` anymore). Only supports Zotero 7.0.0-beta.12 or higher now. If you want to support Zotero 6, you may need to use `dtd`, `properties`, and `ftl` at the same time. See the staled branch `zotero6-bootstrap`. ## Examples @@ -139,7 +139,7 @@ Activate with `Shift+P`. ### 0 Requirement 1. Install a beta version of Zotero: -2. Install [Node.js](https://nodejs.org/en/) and Git +2. Install [Node.js](https://nodejs.org/en/) and [Git](https://git-scm.com/) > [!note] > This guide assumes that you have an initial understanding of the basic structure and workings of the Zotero plugin. If you don't, please refer to the [documentation](https://www.zotero.org/support/dev/zotero_7_for_developers) and official plugin examples [Make It Red](https://github.com/zotero/make-it-red) first. @@ -264,7 +264,7 @@ Steps in `scripts/build.mjs`: > > - This environment variable is stored in `Zotero.${addonInstance}.data.env`. The outputs to console is disabled in prod mode. > - You can decide what users cannot see/use based on this variable. -> - In production mode, the build script will pack the plugin and update the `update.json` +> - In production mode, the build script will pack the plugin and update the `update.json`. ### 5 Release @@ -279,7 +279,7 @@ npm run release > [!note] > In this template, release-it is configured to locally bump the version, build, and push commits and git.tags, subsequently GitHub Action will rebuild the plugin and publish the XPI to GitHub Release. > -> If you need to release a locally built XPI, set `release-it.github.release` to `true` in `package.json` and remove `.github/workflows/release.yml`. Besides that, you need to set the environment variable `GITHUB_TOKEN`, get it in +> If you need to release a locally built XPI, set `release-it.github.release` to `true` in `package.json` and remove `.github/workflows/release.yml`. Besides that, you need to set the environment variable `GITHUB_TOKEN`, get it in . #### About Prerelease diff --git a/doc/README-zhCN.md b/doc/README-zhCN.md index d0c57f5..4b1994f 100644 --- a/doc/README-zhCN.md +++ b/doc/README-zhCN.md @@ -7,7 +7,22 @@ [English](../README.md) | [简体中文](./README-zhCN.md) -使用此模板创建的一些插件: +📖 [插件开发文档](https://zotero.yuque.com/books/share/8d230829-6004-4934-b4c6-685a7001bfa0/vec88d) (中文版,已过时) + +[📖 Zotero 7 插件开发文档](https://www.zotero.org/support/dev/zotero_7_for_developers) + +🛠️ [Zotero 插件工具包](https://github.com/windingwind/zotero-plugin-toolkit) | [API 文档](https://github.com/windingwind/zotero-plugin-toolkit/blob/master/docs/zotero-plugin-toolkit.md) + +ℹ️ [Zotero 类型定义](https://github.com/windingwind/zotero-types) + +📜 [Zotero 源代码](https://github.com/zotero/zotero) + +📌 [Zotero 插件模板](https://github.com/windingwind/zotero-plugin-template) (即本仓库) + +> [!tip] +> 👁 Watch 本仓库,以及时收到修复或更新的通知. + +## 使用此模板构建的插件 [![GitHub Repo stars](https://img.shields.io/github/stars/windingwind/zotero-better-notes?label=zotero-better-notes&style=flat-square)](https://github.com/windingwind/zotero-better-notes) [![GitHub Repo stars](https://img.shields.io/github/stars/windingwind/zotero-pdf-preview?label=zotero-pdf-preview&style=flat-square)](https://github.com/windingwind/zotero-pdf-preview) @@ -26,21 +41,10 @@ [![GitHub Repo stars](https://img.shields.io/github/stars/MuiseDestiny/zotero-gpt?label=zotero-gpt&style=flat-square)](https://github.com/MuiseDestiny/zotero-gpt) [![GitHub Repo stars](https://img.shields.io/github/stars/zoushucai/zotero-journalabbr?label=zotero-journalabbr&style=flat-square)](https://github.com/zoushucai/zotero-journalabbr) [![GitHub Repo stars](https://img.shields.io/github/stars/MuiseDestiny/zotero-figure?label=zotero-figure&style=flat-square)](https://github.com/MuiseDestiny/zotero-figure) -[![GitHub Repo stars](https://img.shields.io/github/stars/MuiseDestiny/zotero-file?label=WanderingFile&style=flat-square)](https://github.com/MuiseDestiny/zotero-file) [![GitHub Repo stars](https://img.shields.io/github/stars/l0o0/jasminum?label=jasminum&style=flat-square)](https://github.com/l0o0/jasminum) [![GitHub Repo stars](https://img.shields.io/github/stars/lifan0127/ai-research-assistant?label=ai-research-assistant&style=flat-square)](https://github.com/lifan0127/ai-research-assistant) -📖 [插件开发文档](https://zotero.yuque.com/books/share/8d230829-6004-4934-b4c6-685a7001bfa0/vec88d) (中文版) - -🛠️ [Zotero 插件工具包](https://github.com/windingwind/zotero-plugin-toolkit) | [API 文档](https://github.com/windingwind/zotero-plugin-toolkit/blob/master/docs/zotero-plugin-toolkit.md) - -ℹ️ [Zotero 类型定义](https://github.com/windingwind/zotero-types) - -📜 [Zotero 源代码](https://github.com/zotero/zotero) - -📌 [Zotero 插件模板](https://github.com/windingwind/zotero-plugin-template) (即当前库) - -> 👁 关注此库,以便在有修复或更新时及时收到通知. +[![GitHub Repo stars](https://img.shields.io/github/stars/daeh/zotero-markdb-connect?label=zotero-markdb-connect&style=flat-square)](https://github.com/daeh/zotero-markdb-connect) 如果你正在使用此库,我建议你将这个标志 ([![Using Zotero Plugin Template](https://img.shields.io/badge/Using-Zotero%20Plugin%20Template-blue?style=flat-square&logo=github)](https://github.com/windingwind/zotero-plugin-template)) 放在 README 文件中: @@ -50,19 +54,21 @@ ## Features 特性 -> ❗Zotero系统已升级(dtd 已弃用,我们将不在使用 .properties). 主分支将只支持 Zotero 7.0.0-beta.12 或更高版本. 如果需要支持 Zotero 6,可能需要同时使用`dtd`、`properties` 和`ftl`. 请参考此库的 `zotero6-bootstrap` 分支. - - 事件驱动、函数式编程的可扩展框架; - 简单易用,开箱即用; -- ⭐[新特性!]自动热重载!每当修改源码时,都会自动编译并重新加载插件;[详情请跳转→](#auto-hot-reload) -- `src/modules/examples.ts` 中有丰富的示例,涵盖了插件中常用的大部分API(使用的插件工具包 [zotero-plugin-toolkit](https://github.com/windingwind/zotero-plugin-toolkit)); +- ⭐[新特性!]自动热重载!每当修改源码时,都会自动编译并重新加载插件;[详情请跳转→](#自动热重载) +- `src/modules/examples.ts` 中有丰富的示例,涵盖了插件中常用的大部分API (使用的插件工具包 zotero-plugin-toolkit,仓库地址 https://github.com/windingwind/zotero-plugin-toolkit); - TypeScript 支持: - - 为使用 JavaScript 编写的Zotero源码提供全面的类型定义支持(使用类型定义包[zotero-types](https://github.com/windingwind/zotero-types)); + - 为使用 JavaScript 编写的Zotero源码提供全面的类型定义支持 (使用类型定义包 zotero-types,仓库地址 https://github.com/windingwind/zotero-types); - 全局变量和环境设置; -- 插件构建/测试/发布工作流: - - 自动生成/更新插件id和版本、更新配置和设置环境变量 (`development/production`); +- 插件开发/构建/发布工作流: + - 自动生成/更新插件id和版本、更新配置和设置环境变量 (`development`/`production`); - 自动在 Zotero 中构建和重新加载代码; - 自动发布到GitHub (使用[release-it](https://github.com/release-it/release-it)); +- 集成Prettier和ES Lint; + +> [!warning] +> Zotero本地化已升级(`dtd` 已弃用,我们将不再使用 `.properties`). 主分支将只支持 Zotero 7.0.0-beta.12 或更高版本. 如果需要支持 Zotero 6,你可能需要同时使用`dtd`、`properties` 和`ftl`. 请参考此库的 `zotero6-bootstrap` 分支. ## Examples 示例 @@ -102,7 +108,7 @@ - Preferences bindings - UI Events -- Tabel +- Table - Locale 详情参见 [`src/modules/preferenceScript.ts`](./src/modules/preferenceScript.ts) @@ -129,108 +135,89 @@ Obsidian风格的指令输入模块,它通过接受文本来运行插件,并 ## Quick Start Guide 快速入门指南 -### 安装预构建 `xpi` +### 0 前置要求(Requirement) -通过直接在GitHub中下载构建好的 `xpi` 文件并将其安装到Zotero中来了解示例的工作原理. +1. 安装测试版 Zotero:https://www.zotero.org/support/beta_builds +2. 安装 Node.js(https://nodejs.org/en/)和 Git(https://git-scm.com/) -这也是你发布插件的格式,同时这也将是其他人可以直接使用的版本. +> [!note] +> 本指南假定你已经对 Zotero 插件的基本结构和工作原理有初步的了解. 如果你还不了解,请先参考官方文档(https://www.zotero.org/support/dev/zotero_7_for_developers)和官方插件样例 Make It Red(仓库地址 https://github.com/zotero/make-it-red). -> 该库构建好的xpi文件不具有任何实际功能,它可能不随Zotero更新而随时更新. -> -> `xpi` 文件实际上是一个zip压缩包,然而,请不要直接修改它,而是修改源代码并重新构建它. +### 1 创建你的仓库(Create Your Repo) -### 从源码构建(Build from Source) +1. 点击 `Use this template`; +2. 使用 `git clone` 克隆上一步生成的仓库; +
+ 💡 从 GitHub Codespace 开始 -- Fork 此库或者使用 `Use this template`; -- 使用 `git clone ` 克隆此库; -- 进入项目文件夹; -
-💡 从 GitHub Codespace 开始 + _GitHub CodeSpace_ 使你可以直接开始开发而无需在本地下载代码/IDE/依赖. -_GitHub CodeSpace_ 使你可以直接开始开发而无需在本地下载代码/IDE/依赖. + 重复下列步骤,仅需三十秒即可开始构建你的第一个插件! -重复下列步骤,仅需三十秒即可开始构建你的第一个插件! + - 去 [homepage](https://github.com/windingwind/zotero-plugin-template)顶部,点击绿色按钮`Use this template`,点击 `Open in codespace`, 你需要登录你的GitHub账号. + - 等待 codespace 加载. -- 去 [homepage](https://github.com/windingwind/zotero-plugin-template)顶部,点击绿色按钮`Use this template`,点击 `Open in codespace`, 你需要登录你的GitHub账号. -- 等待 codespace 加载. -- 修改 `./package.json` 中的设置,包括: -
+
- ```json5 - { - version, - author, - description, - homepage, - config { - releasepage, // URL to releases(`.xpi`) - updaterdf, // URL to update.json - addonName, // name to be displayed in the plugin manager - addonID, // ID to avoid conflict. IMPORTANT! - addonRef, // e.g. Element ID prefix - addonInstance // the plugin's root instance: Zotero.${addonInstance} +3. 进入项目文件夹; + +### 2 配置模板和开发环境(Config Template Settings and Enviroment) + +1. 修改 `./package.json` 中的设置,包括: + + ```json5 + { + version: "", // to 0.0.0 + author: "", + description: "", + homepage: "", + config: { + addonName: "", // name to be displayed in the plugin manager + addonID: "", // ID to avoid conflict. IMPORTANT! + addonRef: "", // e.g. Element ID prefix + addonInstance: "", // the plugin's root instance: Zotero.${addonInstance} + prefsPrefix: "extensions.zotero.${addonRef}", // the prefix of prefs + releasePage: "", // URL to releases + updateJSON: "", // URL to update.json + }, } - } - ``` + ``` - > 注意设置 addonID 和 addonRef 以避免冲突. + > [!warning] + > 注意设置 addonID 和 addonRef 以避免冲突. -- 运行 `npm install` 以设置插件并安装相关依赖. 如果你没有安装 Node.js,请在[此处下载](https://nodejs.org/en/); -- 运行 `npm run build` 以在生产模式下构建插件,运行 `npm run build-dev` 以在开发模式下构建插件. 用于安装的 xpi 文件和用于构建的代码在 `build` 文件夹下. +如果你需要在GitHub以外的地方托管你的 XPI 包,请删除 `releasePage` 并添加 `updateLink`,并将值设置为你的 XPI 下载地址. - > Dev & prod 两者有什么区别? - > - > - 此环境变量存储在 `Zotero.${addonInstance}.data.env` 中,控制台输出在生产模式下被禁用. - > - 你可以根据此变量决定用户无法查看/使用的内容. +2. 复制 Zotero 启动配置,填入 Zotero 可执行文件路径和 profile 路径. -### 发布(Release) + > (可选项) 此操作仅需执行一次: 使用 `/path/to/zotero -p` 启动 Zotero,创建一个新的配置文件并用作开发配置文件. + > 将配置文件的路径 `profilePath` 放入 `zotero-cmd.json` 中,以指定要使用的配置文件. -如果要构建和发布插件,运行如下指令: + ```sh + cp ./scripts/zotero-cmd-template.json ./scripts/zotero-cmd.json + vim ./scripts/zotero-cmd.json + ``` -```shell -# A release-it command: version increase, npm run build, git push, and GitHub release -# You need to set the environment variable GITHUB_TOKEN https://github.com/settings/tokens -# release-it: https://github.com/release-it/release-it -npm run release -``` +3. 运行 `npm install` 以安装相关依赖 -### 设置开发环境(Setup Development Environment) + > 如果你使用 `pnpm` 作为包管理器,你需要添加 `public-hoist-pattern[]=*@types/bluebird*` 到`.npmrc`, 详情请查看 zotero-types(https://github.com/windingwind/zotero-types?tab=readme-ov-file#usage)的文档. -1. 安装 Zotero: (Zotero 7 beta: ) +### 3 开始开发(Coding) -2. 安装 Firefox 102 (适用于 Zotero 7) +使用 `npm start` 启动开发服务器,它将: -3. 复制 zotero 命令行配置文件,修改开始安装 beta Zotero 的命令. +* 在开发模式下预构建插件 +* 启动 Zotero ,并让其从 `build/` 中加载插件 +* 打开开发者工具(devtool) +* 监听 `src/**` 和 `addon/**`. + - 如果 `src/**` 修改了,运行 esbuild 并且重新加载 + - 如果 `addon/**` 修改了,(在开发模式下)重新构建插件并且重新加载 - > (可选项) 此操作仅需执行一次: 使用 `/path/to/zotero -p` 启动 Zotero,创建一个新的配置文件并用作开发配置文件. - > 将配置文件的路径 `profilePath` 放入 `zotero-cmd.json` 中,以指定要使用的配置文件. - - ```sh - cp ./scripts/zotero-cmd-default.json ./scripts/zotero-cmd.json - vim ./scripts/zotero-cmd.json - ``` - -4. 构建插件并使用 `npm run restart` 重启 Zotero. - -5. 启动 Firefox 102 (Zotero 7) - -6. 在 Firefox 中,转到devtools,转到设置,单击 "enable remote debugging" ,同时,旁边的按钮也是关于调试的。 - - > 在 FirFox 102 中输入 `about:debugging#/setup` . - -7. 在 Zotero 中,进入设置-高级-编辑器,搜索 "debugging" 然后单击 "allow remote debugging". - -8. 在 Firefox 中连接 Zotero. 在 FireFox 102中,在远程调试页面底部输入 `localhost:6100` 然后单击 `add`. - -9. 在远程调试页面左侧栏点击 `connect`. - -10. 点击 "Inspect Main Process" - -### 自动热重载(Auto Hot Reload) +#### 自动热重载 厌倦了无休止的重启吗?忘掉它,拥抱热加载! -1. 运行 `npm run start-watch`. (如果Zotero已经在运行,请使用 `npm run watch`) +1. 运行 `npm start`. 2. 编码. (是的,就这么简单) 当检测到 `src` 或 `addon` 中的文件修改时,插件将自动编译并重新加载. @@ -238,27 +225,76 @@ npm run release
💡 将此功能添加到现有插件的步骤 -1. 复制 `scripts/reload.mjs` -2. 复制 `reload` 、`watch` 和 `start-watch` 命令 `package.json` -3. 运行 `npm install --save-dev chokidar-cli` +1. 复制 `scripts/**.mjs` +2. 复制 `server` 、`build` 和 `stop` 命令到 `package.json` +3. 运行 `npm install --save-dev chokidar` 4. 结束.
-### 在 Zotero 中调试 +#### 在 Zotero 中 Debug 你还可以: - 在 Tools->Developer->Run Javascript 中测试代码片段; + - 使用 `Zotero.debug()` 调试输出. 在 Help->Debug Output Logging->View Output 查看输出; + - 调试 UI. Zotero 建立在 Firefox XUL 框架之上. 使用 [XUL Explorer](https://udn.realityripple.com/docs/Archive/Mozilla/XUL_Explorer) 等软件调试 XUL UI. - > XUL 文档: + + > XUL 文档: + +### 4 构建(Build) + +运行 `npm run build` 在生产模式下构建插件,构建的结果位于 `build/` 目录中. + +`scripts/build.mjs` 的运行步骤: + +* 创建/清空 `build/` +* 复制 `addon/**` 到 `build/addon/**` +* 替换占位符:使用 `replace-in-file` 去替换在 `package.json` 中定义的关键字和配置 (`xhtml`、`.flt` 等) +* 准备本地化文件以避免冲突,查看官方文档了解更多(https://www.zotero.org/support/dev/zotero_7_for_developers#avoiding_localization_conflicts) + * 重命名`**/*.flt` 为 `**/${addonRef}-*.flt` + * 在每个消息前加上 `addonRef-` +* 使用 Esbuild 来将 `.ts` 源码构建为 `.js`,从 `src/index.ts` 构建到`./build/addon/chrome/content/scripts` +* (仅在生产模式下工作) 压缩 `./build/addon` 目录为 `./build/*.xpi` +* (仅在生产模式下工作) 准备 `update.json` 或 `update-beta.json` + +> [!note] +> +> **Dev & prod 两者有什么区别?** +> +> - 此环境变量存储在 `Zotero.${addonInstance}.data.env` 中,控制台输出在生产模式下被禁用. +> - 你可以根据此变量决定用户无法查看/使用的内容. +> - 在生产模式下,构建脚本将自动打包插件并更新 `update.json`. + +### 5 发布(Release) + +如果要构建和发布插件,运行如下指令: + +```shell +# A release-it command: version increase, npm run build, git push, and GitHub release +# release-it: https://github.com/release-it/release-it +npm run release +``` + +> [!note] +> 在此模板中,release-it 被配置为在本地升级版本、构建、推送提交和 git 标签,随后GitHub Action 将重新构建插件并将 XPI 发布到 GitHub Release. +> +> 如果你需要发布一个本地构建的 XPI,将 `package.json` 中的 `release-it.github.release` 设置为 `true`,然后移除 `.github/workflows/release.yml`. 此外,你还需要设置环境变量 `GITHUB_TOKEN`,获取 GitHub Token(https://github.com/settings/tokens). + +#### 关于预发布 + +该模板将 `prerelease` 定义为插件的测试版,当你在 release-it 中选择 `prerelease` 版本 (版本号中带有 `-` ),构建脚本将创建一个 `update-beta.json` 给预发布版本使用,这将确保常规版本的用户不会自动更新到测试版,只有手动下载并安装了测试版的用户才能自动更新到下一个测试版. 当下一个正式版本更新时,脚本将同步更新 `update.json` 和 `update-beta.json`,这将使正式版和测试版用户都可以更新到最新的正式版. + +> [!warning] +> 严格来说,区分 Zotero 6 和 Zotero 7 兼容的插件版本应该通过 `update.json` 的 `addons.__addonID__.updates[]` 中分别配置 `applications.zotero.strict_min_version`,这样 Zotero 才能正确识别,详情在 Zotero 7 开发文档(https://www.zotero.org/support/dev/zotero_7_for_developers#updaterdf_updatesjson)获取. ## Details 更多细节 ### 关于Hooks(About Hooks) -> 可以在 [`src/hooks.ts`](https://github.com/windingwind/zotero-plugin-template/blob/bootstrap/src/hooks.ts) 中查看更多 +> 可以在 [`src/hooks.ts`](https://github.com/windingwind/zotero-plugin-template/blob/main/src/hooks.ts) 中查看更多 1. 当在 Zotero 中触发安装/启用/启动时,`bootstrap.js` > `startup` 被调用 - 等待 Zotero 就绪 @@ -272,9 +308,9 @@ npm run release ### 关于全局变量(About Global Variables) -> 可以在 [`src/index.ts`](https://github.com/windingwind/zotero-plugin-template/blob/bootstrap/src/index.ts) 中查看更多 +> 可以在 [`src/index.ts`](https://github.com/windingwind/zotero-plugin-template/blob/main/src/index.ts)中查看更多 -引导插件在沙盒中运行,但沙盒中没有默认的全局变量,例如 `Zotero` 或 `window` 等我们曾在覆盖插件环境中使用的变量. +bootstrap插件在沙盒中运行,但沙盒中没有默认的全局变量,例如 `Zotero` 或 `window` 等我们曾在overlay插件环境中使用的变量. 此模板将以下变量注册到全局范围: @@ -295,23 +331,9 @@ createElement(document, "hbox"); // returns XUL.Box createElement(document, "button", { namespace: "xul" }); // manually set namespace. returns XUL.Button ``` -### 关于构建(About Build) - -使用 Esbuild 将 `.ts` 源代码构建为 `.js`. - -使用 `replace-in-file` 去替换在 `package.json` 中定义的关键字和配置 (`xhtml`、`.flt` 等). - -步骤 `scripts/build.mjs`: - -1. 清理 `./build` -2. 复制 `./addon` 到 `./build` -3. Esbuild 到 `./build/addon/chrome/content/scripts` -4. 替换`__buildVersion__` 和 `__buildTime__` 在 `./build/addon` -5. 压缩 `./build/addon` 到 `./build/*.xpi` - ### 关于 Zotero API(About Zotero API) -Zotero 文档已过时且不完整,git clone https://github.com/zotero/zotero 并全局搜索关键字. +Zotero 文档已过时且不完整,克隆 https://github.com/zotero/zotero 并全局搜索关键字. > ⭐[zotero-types](https://github.com/windingwind/zotero-types) 提供了最常用的 Zotero API,在默认情况下它被包含在此模板中. 你的 IDE 将为大多数的 API 提供提醒. @@ -365,11 +387,14 @@ Zotero 文档已过时且不完整,git clone https://github.com/zotero/zotero | `-- prefs.js |-- build/ # build dir |-- scripts # scripts for dev -| |-- build.mjs # esbuild and replace -| |-- reload.mjs -| |-- start.mjs -| |-- stop.mjs -| `-- zotero-cmd-default.json +| |-- build.mjs # script to build plugin +| |-- scripts.mjs # scripts send to Zotero, such as reload, openDevTool, etc +| |-- server.mjs # script to start a development server +| |-- start.mjs # script to start Zotero process +| |-- stop.mjs # script to kill Zotero process +| |-- utils.mjs # utils functions for dev scripts +| |-- update-template.json # template of `update.json` +| `-- zotero-cmd-template.json # template of local env |-- src # source code | |-- addon.ts # base class | |-- hooks.ts # lifecycle hooks @@ -385,7 +410,6 @@ Zotero 文档已过时且不完整,git clone https://github.com/zotero/zotero |-- tsconfig.json # https://code.visualstudio.com/docs/languages/jsconfig |-- typings # ts typings | `-- global.d.ts -|-- update-template.json # template of `update.json` `-- update.json ``` From f5f2fd3297c59b1a9335d1636901a637bf70b7c8 Mon Sep 17 00:00:00 2001 From: windingwind <33902321+windingwind@users.noreply.github.com> Date: Mon, 18 Dec 2023 00:03:28 +0800 Subject: [PATCH 26/36] add: build replace-in-file includes html and css --- scripts/build.mjs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/scripts/build.mjs b/scripts/build.mjs index c1c8521..333449f 100644 --- a/scripts/build.mjs +++ b/scripts/build.mjs @@ -45,6 +45,8 @@ function replaceString(buildTime) { const replaceResult = replaceInFileSync({ files: [ `${buildDir}/addon/**/*.xhtml`, + `${buildDir}/addon/**/*.html`, + `${buildDir}/addon/**/*.css`, `${buildDir}/addon/**/*.json`, `${buildDir}/addon/prefs.js`, `${buildDir}/addon/manifest.json`, From fc8a4ac9d9b1909640c230b1de524a36d08d35fe Mon Sep 17 00:00:00 2001 From: syt Date: Mon, 18 Dec 2023 15:49:00 +0800 Subject: [PATCH 27/36] Remove original xpi file if exists in `extensions` folder (#90) * remove original xpi file in extensions folder to avoid load add-on from xpi * Update README.md --- README.md | 2 +- scripts/start.mjs | 7 ++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 9671d8b..33367b2 100644 --- a/README.md +++ b/README.md @@ -251,7 +251,7 @@ Steps in `scripts/build.mjs`: - Create/empty `build/`. - Copy `addon/**` to `build/addon/**` - Replace placeholders: use `replace-in-file` to replace keywords and configurations defined in `package.json` in non-build files (`xhtml`, `json`, et al.). -- Prepare locale files to [avaid conflict](https://www.zotero.org/support/dev/zotero_7_for_developers#avoiding_localization_conflicts) +- Prepare locale files to [avoid conflict](https://www.zotero.org/support/dev/zotero_7_for_developers#avoiding_localization_conflicts) - Rename `**/*.flt` to `**/${addonRef}-*.flt` - Prefix each fluent message with `addonRef-` - Use Esbuild to build `.ts` source code to `.js`, build `src/index.ts` to `./build/addon/chrome/content/scripts`. diff --git a/scripts/start.mjs b/scripts/start.mjs index b916b5a..d45fea4 100644 --- a/scripts/start.mjs +++ b/scripts/start.mjs @@ -2,7 +2,7 @@ import details from "../package.json" assert { type: "json" }; import { Logger } from "./utils.mjs"; import cmd from "./zotero-cmd.json" assert { type: "json" }; import { spawn } from "child_process"; -import { existsSync, readFileSync, writeFileSync } from "fs"; +import { existsSync, readFileSync, writeFileSync, rmSync } from "fs"; import { clearFolder } from "./utils.mjs"; import path from "path"; import { exit } from "process"; @@ -45,6 +45,11 @@ function prepareDevEnv() { writeAddonProxyFile(); } + const addonXpiFilePath = path.join(profilePath, `extensions/${addonID}.xpi`); + if (existsSync(addonXpiFilePath)) { + rmSync(addonXpiFilePath); + } + const prefsPath = path.join(profilePath, "prefs.js"); if (existsSync(prefsPath)) { const PrefsLines = readFileSync(prefsPath, "utf-8").split("\n"); From 7f6b7c54b2fbbf10a62c6cdf5be650af85123f98 Mon Sep 17 00:00:00 2001 From: Northword <44738481+northword@users.noreply.github.com> Date: Tue, 26 Dec 2023 21:43:26 +0800 Subject: [PATCH 28/36] fix: only prompt version when run release, and submit comment to closed issues, fix prerelease version number breaks auto update (#92) * chore: only prompt version when run release * chore: submit comment in issue when new version release closes: #91 * fix: set default prerelease id to beta to resolve auto update wrong --- package.json | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 7c50f07..f035eb1 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,7 @@ "stop": "node scripts/stop.mjs", "lint": "prettier --write . && eslint . --ext .ts --fix", "test": "echo \"Error: no test specified\" && exit 1", - "release": "release-it", + "release": "release-it --only-version --preReleaseId=beta", "update-deps": "npm update --save" }, "repository": { @@ -124,7 +124,12 @@ "release": false, "assets": [ "build/*.xpi" - ] + ], + "comments": { + "submit": true, + "issue": ":rocket: _This issue has been resolved in v${version}. See [${releaseName}](${releaseUrl}) for release notes._", + "pr": ":rocket: _This pull request is included in v${version}. See [${releaseName}](${releaseUrl}) for release notes._" + } }, "hooks": { "before:init": "npm run lint", From 2839b3ac53bfa29e262190b20349c8e7e6e95519 Mon Sep 17 00:00:00 2001 From: windingwind <33902321+windingwind@users.noreply.github.com> Date: Thu, 28 Dec 2023 10:43:23 +0800 Subject: [PATCH 29/36] chore: doc lint --- doc/README-zhCN.md | 104 ++++++++++++++++++++++----------------------- 1 file changed, 52 insertions(+), 52 deletions(-) diff --git a/doc/README-zhCN.md b/doc/README-zhCN.md index 4b1994f..606de38 100644 --- a/doc/README-zhCN.md +++ b/doc/README-zhCN.md @@ -147,17 +147,17 @@ Obsidian风格的指令输入模块,它通过接受文本来运行插件,并 1. 点击 `Use this template`; 2. 使用 `git clone` 克隆上一步生成的仓库; -
- 💡 从 GitHub Codespace 开始 +
+ 💡 从 GitHub Codespace 开始 - _GitHub CodeSpace_ 使你可以直接开始开发而无需在本地下载代码/IDE/依赖. + _GitHub CodeSpace_ 使你可以直接开始开发而无需在本地下载代码/IDE/依赖. - 重复下列步骤,仅需三十秒即可开始构建你的第一个插件! + 重复下列步骤,仅需三十秒即可开始构建你的第一个插件! - - 去 [homepage](https://github.com/windingwind/zotero-plugin-template)顶部,点击绿色按钮`Use this template`,点击 `Open in codespace`, 你需要登录你的GitHub账号. - - 等待 codespace 加载. + - 去 [homepage](https://github.com/windingwind/zotero-plugin-template)顶部,点击绿色按钮`Use this template`,点击 `Open in codespace`, 你需要登录你的GitHub账号. + - 等待 codespace 加载. -
+
3. 进入项目文件夹; @@ -165,53 +165,53 @@ Obsidian风格的指令输入模块,它通过接受文本来运行插件,并 1. 修改 `./package.json` 中的设置,包括: - ```json5 - { - version: "", // to 0.0.0 - author: "", - description: "", - homepage: "", - config: { - addonName: "", // name to be displayed in the plugin manager - addonID: "", // ID to avoid conflict. IMPORTANT! - addonRef: "", // e.g. Element ID prefix - addonInstance: "", // the plugin's root instance: Zotero.${addonInstance} - prefsPrefix: "extensions.zotero.${addonRef}", // the prefix of prefs - releasePage: "", // URL to releases - updateJSON: "", // URL to update.json - }, - } - ``` + ```json5 + { + version: "", // to 0.0.0 + author: "", + description: "", + homepage: "", + config: { + addonName: "", // name to be displayed in the plugin manager + addonID: "", // ID to avoid conflict. IMPORTANT! + addonRef: "", // e.g. Element ID prefix + addonInstance: "", // the plugin's root instance: Zotero.${addonInstance} + prefsPrefix: "extensions.zotero.${addonRef}", // the prefix of prefs + releasePage: "", // URL to releases + updateJSON: "", // URL to update.json + }, + } + ``` - > [!warning] - > 注意设置 addonID 和 addonRef 以避免冲突. + > [!warning] + > 注意设置 addonID 和 addonRef 以避免冲突. 如果你需要在GitHub以外的地方托管你的 XPI 包,请删除 `releasePage` 并添加 `updateLink`,并将值设置为你的 XPI 下载地址. -2. 复制 Zotero 启动配置,填入 Zotero 可执行文件路径和 profile 路径. +2. 复制 Zotero 启动配置,填入 Zotero 可执行文件路径和 profile 路径. - > (可选项) 此操作仅需执行一次: 使用 `/path/to/zotero -p` 启动 Zotero,创建一个新的配置文件并用作开发配置文件. - > 将配置文件的路径 `profilePath` 放入 `zotero-cmd.json` 中,以指定要使用的配置文件. + > (可选项) 此操作仅需执行一次: 使用 `/path/to/zotero -p` 启动 Zotero,创建一个新的配置文件并用作开发配置文件. + > 将配置文件的路径 `profilePath` 放入 `zotero-cmd.json` 中,以指定要使用的配置文件. - ```sh - cp ./scripts/zotero-cmd-template.json ./scripts/zotero-cmd.json - vim ./scripts/zotero-cmd.json - ``` + ```sh + cp ./scripts/zotero-cmd-template.json ./scripts/zotero-cmd.json + vim ./scripts/zotero-cmd.json + ``` 3. 运行 `npm install` 以安装相关依赖 - > 如果你使用 `pnpm` 作为包管理器,你需要添加 `public-hoist-pattern[]=*@types/bluebird*` 到`.npmrc`, 详情请查看 zotero-types(https://github.com/windingwind/zotero-types?tab=readme-ov-file#usage)的文档. + > 如果你使用 `pnpm` 作为包管理器,你需要添加 `public-hoist-pattern[]=*@types/bluebird*` 到`.npmrc`, 详情请查看 zotero-types(https://github.com/windingwind/zotero-types?tab=readme-ov-file#usage)的文档. ### 3 开始开发(Coding) 使用 `npm start` 启动开发服务器,它将: -* 在开发模式下预构建插件 -* 启动 Zotero ,并让其从 `build/` 中加载插件 -* 打开开发者工具(devtool) -* 监听 `src/**` 和 `addon/**`. - - 如果 `src/**` 修改了,运行 esbuild 并且重新加载 - - 如果 `addon/**` 修改了,(在开发模式下)重新构建插件并且重新加载 +- 在开发模式下预构建插件 +- 启动 Zotero ,并让其从 `build/` 中加载插件 +- 打开开发者工具(devtool) +- 监听 `src/**` 和 `addon/**`. + - 如果 `src/**` 修改了,运行 esbuild 并且重新加载 + - 如果 `addon/**` 修改了,(在开发模式下)重新构建插件并且重新加载 #### 自动热重载 @@ -242,7 +242,7 @@ Obsidian风格的指令输入模块,它通过接受文本来运行插件,并 - 调试 UI. Zotero 建立在 Firefox XUL 框架之上. 使用 [XUL Explorer](https://udn.realityripple.com/docs/Archive/Mozilla/XUL_Explorer) 等软件调试 XUL UI. - > XUL 文档: + > XUL 文档: ### 4 构建(Build) @@ -250,15 +250,15 @@ Obsidian风格的指令输入模块,它通过接受文本来运行插件,并 `scripts/build.mjs` 的运行步骤: -* 创建/清空 `build/` -* 复制 `addon/**` 到 `build/addon/**` -* 替换占位符:使用 `replace-in-file` 去替换在 `package.json` 中定义的关键字和配置 (`xhtml`、`.flt` 等) -* 准备本地化文件以避免冲突,查看官方文档了解更多(https://www.zotero.org/support/dev/zotero_7_for_developers#avoiding_localization_conflicts) - * 重命名`**/*.flt` 为 `**/${addonRef}-*.flt` - * 在每个消息前加上 `addonRef-` -* 使用 Esbuild 来将 `.ts` 源码构建为 `.js`,从 `src/index.ts` 构建到`./build/addon/chrome/content/scripts` -* (仅在生产模式下工作) 压缩 `./build/addon` 目录为 `./build/*.xpi` -* (仅在生产模式下工作) 准备 `update.json` 或 `update-beta.json` +- 创建/清空 `build/` +- 复制 `addon/**` 到 `build/addon/**` +- 替换占位符:使用 `replace-in-file` 去替换在 `package.json` 中定义的关键字和配置 (`xhtml`、`.flt` 等) +- 准备本地化文件以避免冲突,查看官方文档了解更多(https://www.zotero.org/support/dev/zotero_7_for_developers#avoiding_localization_conflicts) + - 重命名`**/*.flt` 为 `**/${addonRef}-*.flt` + - 在每个消息前加上 `addonRef-` +- 使用 Esbuild 来将 `.ts` 源码构建为 `.js`,从 `src/index.ts` 构建到`./build/addon/chrome/content/scripts` +- (仅在生产模式下工作) 压缩 `./build/addon` 目录为 `./build/*.xpi` +- (仅在生产模式下工作) 准备 `update.json` 或 `update-beta.json` > [!note] > @@ -279,9 +279,9 @@ npm run release ``` > [!note] -> 在此模板中,release-it 被配置为在本地升级版本、构建、推送提交和 git 标签,随后GitHub Action 将重新构建插件并将 XPI 发布到 GitHub Release. +> 在此模板中,release-it 被配置为在本地升级版本、构建、推送提交和 git 标签,随后GitHub Action 将重新构建插件并将 XPI 发布到 GitHub Release. > -> 如果你需要发布一个本地构建的 XPI,将 `package.json` 中的 `release-it.github.release` 设置为 `true`,然后移除 `.github/workflows/release.yml`. 此外,你还需要设置环境变量 `GITHUB_TOKEN`,获取 GitHub Token(https://github.com/settings/tokens). +> 如果你需要发布一个本地构建的 XPI,将 `package.json` 中的 `release-it.github.release` 设置为 `true`,然后移除 `.github/workflows/release.yml`. 此外,你还需要设置环境变量 `GITHUB_TOKEN`,获取 GitHub Token(https://github.com/settings/tokens). #### 关于预发布 From d1f6367825bfffd6e9491a3e3db45510bf133be2 Mon Sep 17 00:00:00 2001 From: windingwind <33902321+windingwind@users.noreply.github.com> Date: Thu, 28 Dec 2023 10:43:50 +0800 Subject: [PATCH 30/36] Release 1.1.1 --- package.json | 2 +- update.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index f035eb1..5cfb21a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "zotero-addon-template", - "version": "1.1.0", + "version": "1.1.1", "description": "Zotero Addon Template", "config": { "addonName": "Zotero Addon Template", diff --git a/update.json b/update.json index e5925c3..cf50a01 100644 --- a/update.json +++ b/update.json @@ -3,7 +3,7 @@ "addontemplate@euclpts.com": { "updates": [ { - "version": "1.1.0", + "version": "1.1.1", "update_link": "https://github.com/windingwind/zotero-addon-template/releases/latest/download/zotero-addon-template.xpi", "applications": { "zotero": { From 773841068065f388009bae2f0523e2d5b73869a9 Mon Sep 17 00:00:00 2001 From: Northword <44738481+northword@users.noreply.github.com> Date: Sat, 6 Jan 2024 00:13:29 +0800 Subject: [PATCH 31/36] fix: comment for release (#93) * fix: comment for release * chore: update deps * Delete release-it after:release hook --- .github/workflows/release.yml | 22 ++++++++++++++++------ package.json | 10 ++-------- 2 files changed, 18 insertions(+), 14 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index b533acf..a57bde9 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -7,27 +7,37 @@ on: permissions: contents: write + issues: write + pull-requests: write jobs: - release-it: + release: runs-on: ubuntu-latest env: GITHUB_TOKEN: ${{ secrets.GitHub_TOKEN }} steps: - - uses: actions/checkout@v4 + - name: Checkout + uses: actions/checkout@v4 with: fetch-depth: 0 - name: Setup Node.js - uses: actions/setup-node@v3 + uses: actions/setup-node@v4 with: node-version: 20 - # cache: npm - name: Install deps run: npm install - name: Release to GitHub - # if: github.event_name == 'push' && github.ref_type == 'tag' && startsWith(github.ref, 'refs/tags/v') run: | - npm run release -- --no-increment --no-git --github.release --ci --verbose + npm run release -- --no-increment --no-git --github.release --ci --VV + sleep 1s + + - name: Notify release + uses: apexskier/github-release-commenter@v1 + continue-on-error: true + with: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + comment-template: | + :rocket: _This ticket has been resolved in {release_tag}. See {release_link} for release notes._ diff --git a/package.json b/package.json index 5cfb21a..dc9d2cf 100644 --- a/package.json +++ b/package.json @@ -124,17 +124,11 @@ "release": false, "assets": [ "build/*.xpi" - ], - "comments": { - "submit": true, - "issue": ":rocket: _This issue has been resolved in v${version}. See [${releaseName}](${releaseUrl}) for release notes._", - "pr": ":rocket: _This pull request is included in v${version}. See [${releaseName}](${releaseUrl}) for release notes._" - } + ] }, "hooks": { "before:init": "npm run lint", - "after:bump": "npm run build", - "after:release": "echo Successfully released ${name} v${version} to ${repo.repository}." + "after:bump": "npm run build" } } } From 0663e5b0b1b53db29d5374601a8dc10f24bdbdf4 Mon Sep 17 00:00:00 2001 From: Northword <44738481+northword@users.noreply.github.com> Date: Sat, 6 Jan 2024 00:14:07 +0800 Subject: [PATCH 32/36] fix: log miss ftl messages in build (#95) --- scripts/build.mjs | 139 ++++++++++++++++++++++------------------------ 1 file changed, 66 insertions(+), 73 deletions(-) diff --git a/scripts/build.mjs b/scripts/build.mjs index 333449f..aab5c37 100644 --- a/scripts/build.mjs +++ b/scripts/build.mjs @@ -64,83 +64,75 @@ function replaceString(buildTime) { } function prepareLocaleFiles() { - // Walk the builds/addon/locale folder's sub folders and rename *.ftl to addonRef-*.ftl - const localeDir = path.join(buildDir, "addon/locale"); - const localeFolders = readdirSync(localeDir, { withFileTypes: true }) - .filter((dirent) => dirent.isDirectory()) - .map((dirent) => dirent.name); - - for (const localeSubFolder of localeFolders) { - const localeSubDir = path.join(localeDir, localeSubFolder); - const localeSubFiles = readdirSync(localeSubDir, { - withFileTypes: true, - }) - .filter((dirent) => dirent.isFile()) - .map((dirent) => dirent.name); - - for (const localeSubFile of localeSubFiles) { - if (localeSubFile.endsWith(".ftl")) { - renameSync( - path.join(localeSubDir, localeSubFile), - path.join(localeSubDir, `${config.addonRef}-${localeSubFile}`), - ); - } - } - } - - const localeMessage = new Set(); - const localeMessageMiss = new Set(); - - const replaceResultFlt = replaceInFileSync({ - files: [`${buildDir}/addon/locale/**/*.ftl`], - processor: (fltContent) => { - const lines = fltContent.split("\n"); - const prefixedLines = lines.map((line) => { - // https://regex101.com/r/lQ9x5p/1 - const match = line.match( - /^(?[a-zA-Z]\S*)([ ]*=[ ]*)(?.*)$/m, - ); - if (match) { - localeMessage.add(match.groups.message); - return `${config.addonRef}-${line}`; - } else { - return line; - } - }); - return prefixedLines.join("\n"); - }, - }); - - const replaceResultXhtml = replaceInFileSync({ - files: [`${buildDir}/addon/**/*.xhtml`], + // Prefix Fluent messages in xhtml + const MessagesInHTML = new Set(); + replaceInFileSync({ + files: [`${buildDir}/addon/**/*.xhtml`, `${buildDir}/addon/**/*.html`], processor: (input) => { const matchs = [...input.matchAll(/(data-l10n-id)="(\S*)"/g)]; matchs.map((match) => { - if (localeMessage.has(match[2])) { - input = input.replace( - match[0], - `${match[1]}="${config.addonRef}-${match[2]}"`, - ); - } else { - localeMessageMiss.add(match[2]); - } + input = input.replace( + match[0], + `${match[1]}="${config.addonRef}-${match[2]}"`, + ); + MessagesInHTML.add(match[2]); }); return input; }, }); - Logger.debug( - "[Build] Prepare locale files OK", - // replaceResultFlt.filter((f) => f.hasChanged).map((f) => `${f.file} : OK`), - // replaceResultXhtml.filter((f) => f.hasChanged).map((f) => `${f.file} : OK`), - ); + // Walk the sub folders of `build/addon/locale` + const localesPath = path.join(buildDir, "addon/locale"), + localeNames = readdirSync(localesPath, { withFileTypes: true }) + .filter((dirent) => dirent.isDirectory()) + .map((dirent) => dirent.name); - if (localeMessageMiss.size !== 0) { - Logger.warn( - `[Build] Fluent message [${new Array( - ...localeMessageMiss, - )}] do not exsit in addon's locale files.`, - ); + for (const localeName of localeNames) { + const localePath = path.join(localesPath, localeName); + const ftlFiles = readdirSync(localePath, { + withFileTypes: true, + }) + .filter((dirent) => dirent.isFile()) + .map((dirent) => dirent.name); + + // rename *.ftl to addonRef-*.ftl + for (const ftlFile of ftlFiles) { + if (ftlFile.endsWith(".ftl")) { + renameSync( + path.join(localePath, ftlFile), + path.join(localePath, `${config.addonRef}-${ftlFile}`), + ); + } + } + + // Prefix Fluent messages in each ftl + const MessageInThisLang = new Set(); + replaceInFileSync({ + files: [`${buildDir}/addon/locale/${localeName}/*.ftl`], + processor: (fltContent) => { + const lines = fltContent.split("\n"); + const prefixedLines = lines.map((line) => { + // https://regex101.com/r/lQ9x5p/1 + const match = line.match( + /^(?[a-zA-Z]\S*)([ ]*=[ ]*)(?.*)$/m, + ); + if (match) { + MessageInThisLang.add(match.groups.message); + return `${config.addonRef}-${line}`; + } else { + return line; + } + }); + return prefixedLines.join("\n"); + }, + }); + + // If a message in xhtml but not in ftl of current language, log it + MessagesInHTML.forEach((message) => { + if (!MessageInThisLang.has(message)) { + Logger.error(`[Build] ${message} don't exist in ${localeName}`); + } + }); } } @@ -212,19 +204,21 @@ export async function main() { ); clearFolder(buildDir); - copyFolderRecursiveSync("addon", buildDir); - replaceString(buildTime); - Logger.debug("[Build] Replace OK"); + Logger.debug("[Build] Replacing"); + replaceString(buildTime); + + Logger.debug("[Build] Preparing locale files"); prepareLocaleFiles(); + Logger.debug("[Build] Running esbuild"); await build(esbuildOptions); - Logger.debug("[Build] Run esbuild OK"); Logger.debug("[Build] Addon prepare OK"); if (process.env.NODE_ENV === "production") { + Logger.debug("[Build] Packing Addon"); await zip.compressDir( path.join(buildDir, "addon"), path.join(buildDir, `${name}.xpi`), @@ -232,7 +226,6 @@ export async function main() { ignoreBase: true, }, ); - Logger.debug("[Build] Addon pack OK"); prepareUpdateJson(); From 2cc3ac7622d4eb8008cd9f514009908611a78c8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ch=C3=A9ngl=C3=B3ng=20M=C7=8E?= <33501679+ChenglongMa@users.noreply.github.com> Date: Fri, 12 Jan 2024 13:06:01 +1100 Subject: [PATCH 33/36] Update start.mjs (#97) --- scripts/start.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/start.mjs b/scripts/start.mjs index d45fea4..0229d2b 100644 --- a/scripts/start.mjs +++ b/scripts/start.mjs @@ -61,7 +61,7 @@ function prepareDevEnv() { return; } if (line.includes("extensions.zotero.dataDir") && dataDir !== "") { - return `user_pref("extensions.zotero.dataDir", "${dataDir}");`; + return `user_pref("extensions.zotero.dataDir", "${dataDir.replace(/\\\\?/g, "\\\\")}");`; } return line; }); From cd14a26c115d398090b629b5f037ca65ea588ac5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 19 Feb 2024 23:37:21 +0000 Subject: [PATCH 34/36] build(deps-dev): bump esbuild from 0.19.12 to 0.20.1 Bumps [esbuild](https://github.com/evanw/esbuild) from 0.19.12 to 0.20.1. - [Release notes](https://github.com/evanw/esbuild/releases) - [Changelog](https://github.com/evanw/esbuild/blob/main/CHANGELOG.md) - [Commits](https://github.com/evanw/esbuild/compare/v0.19.12...v0.20.1) --- updated-dependencies: - dependency-name: esbuild dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index dc9d2cf..dda76f6 100644 --- a/package.json +++ b/package.json @@ -40,7 +40,7 @@ "@typescript-eslint/parser": "^6.14.0", "chokidar": "^3.5.3", "compressing": "^1.10.0", - "esbuild": "^0.19.9", + "esbuild": "^0.20.1", "eslint": "^8.55.0", "eslint-config-prettier": "^9.1.0", "prettier": "^3.1.1", From 0075dc8a430ce3053ee7d7e05a6bf51df9916c72 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 4 Mar 2024 23:31:03 +0000 Subject: [PATCH 35/36] build(deps-dev): bump @typescript-eslint/parser from 6.21.0 to 7.1.1 Bumps [@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser) from 6.21.0 to 7.1.1. - [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases) - [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/parser/CHANGELOG.md) - [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v7.1.1/packages/parser) --- updated-dependencies: - dependency-name: "@typescript-eslint/parser" dependency-type: direct:development update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index dc9d2cf..e156f5a 100644 --- a/package.json +++ b/package.json @@ -37,7 +37,7 @@ "devDependencies": { "@types/node": "^20.10.4", "@typescript-eslint/eslint-plugin": "^6.14.0", - "@typescript-eslint/parser": "^6.14.0", + "@typescript-eslint/parser": "^7.1.1", "chokidar": "^3.5.3", "compressing": "^1.10.0", "esbuild": "^0.19.9", From df847e59be987750dfb784261c598f8d4df55a3e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 18 Mar 2024 23:25:48 +0000 Subject: [PATCH 36/36] build(deps-dev): bump @typescript-eslint/eslint-plugin Bumps [@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin) from 6.21.0 to 7.3.1. - [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases) - [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/CHANGELOG.md) - [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v7.3.1/packages/eslint-plugin) --- updated-dependencies: - dependency-name: "@typescript-eslint/eslint-plugin" dependency-type: direct:development update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index ed9259f..87147ed 100644 --- a/package.json +++ b/package.json @@ -36,7 +36,7 @@ }, "devDependencies": { "@types/node": "^20.10.4", - "@typescript-eslint/eslint-plugin": "^6.14.0", + "@typescript-eslint/eslint-plugin": "^7.3.1", "@typescript-eslint/parser": "^7.1.1", "chokidar": "^3.5.3", "compressing": "^1.10.0",