Joplin 插件开发入门:使用 generator-joplin 脚手架从零搭建可发布插件
【免费下载链接】joplinJoplin - the privacy-focused note taking app with sync capabilities for Windows, macOS, Linux, Android and iOS.项目地址: https://gitcode.com/GitHub_Trending/jo/joplin
本文是 Joplin 插件脚手架工具 generator-joplin 的完整使用指南,核心面向希望在 Joplin(Windows / macOS / Linux / Android / iOS 全平台笔记应用)上开发插件的开发者。阅读本文后,你将掌握用 Yeoman 生成插件骨架、理解/src/index.ts与manifest.json等关键文件结构、完成 Webpack 构建产出 JPL 安装包、同步版本号、发布到官方插件仓库,以及通过plugin.config.json编译内容脚本(content script)与 Webview 脚本的完整实战流程。文中所有结论均可在 packages/generator-joplin 目录的源码与模板、以及仓库内置的 note_list_renderer 示例插件 中得到验证。
生成器是什么
generator-joplin是一个基于 Yeoman 的 Joplin 插件项目脚手架(scaffold)工具,其官方描述为 "Scaffolds out a new Joplin plugin"。它在 Joplin 仓库中位于 packages/generator-joplin,核心实现是 generators/app/index.js 中继承自yeoman-generator的 Generator 类,依赖chalk、yosay、slugify、yeoman-generator等包(见 package.json)。
它解决的核心痛点包括:
- 手工搭建成本高:插件需要同时维护 TypeScript 入口、Webpack 构建链、manifest 清单、API 类型声明等多套文件,手工初始化极易出错;
- 版本同步繁琐:插件版本号同时存在于
package.json与manifest.json,需要保持一致; - 发布规范隐蔽:官方插件仓库对包名、关键词、publish 目录有硬性要求,生成器会自动把这些规范配置好。
仓库中的 note_list_renderer 示例插件 就是由这类生成器产出的完整样例,其目录结构(src/、api/、plugin.config.json、webpack.config.js、tsconfig.json、package.json)与生成器模板一一对应,可作为本文所有讲解的落地参照。
安装与生成新插件项目
前置条件
使用前需已安装 Node.js 与 npm。生成器对 npm 的版本要求为>= 4.0.0(见 generator-joplin 的 package.json 中engines字段)。
安装 Yeoman 与生成器
npm install -g yo npm install -g generator-joplin生成插件项目
yo --node-package-manager npm joplin--node-package-manager npm明确指定使用 npm 作为包管理器,避免交互式询问。运行后生成器会依次向你询问以下信息(对应 generators/app/index.js 中的prompting()阶段):
| 提问项 | 含义 | 说明 |
|---|---|---|
pluginId | 插件唯一 ID | 必须全局唯一,如com.example.MyPlugin或一段 UUID |
pluginName | 插件显示名称 | 用户友好的字符串,将显示在 Joplin UI 中 |
pluginDescription | 插件描述 | 将写入 manifest 的description字段 |
pluginAuthor | 作者 | 将写入 manifest 的author字段 |
pluginRepositoryUrl | 仓库 URL | 将写入 manifest 的repository_url |
pluginHomepageUrl | 主页 URL | 将写入 manifest 的homepage_url |
packageName | npm 包名 | 默认根据插件名自动推导,可直接回车接受或修改 |
包名自动推导逻辑:在 generators/app/utils.js 的packageNameFromPluginName()中实现——先把*+~.()'"!:@[]等特殊字符替换为-,再用slugify转小写,修剪首尾多余的连字符,最后统一加上joplin-plugin-前缀,并限制在 214 个字符内。例如插件名 "Test List Plugin" 会得到默认包名joplin-plugin-test-list-plugin,这与 note_list_renderer 的 package.json 中的实际包名完全吻合。
注意:npm 存在一个长期未修复的 bug,会特殊对待
.gitignore和package.json,因此生成器模板目录中把它们命名为.gitignore_TEMPLATE、package_TEMPLATE.json,在写入阶段再重命名还原(源码中已有注释说明,见 index.js)。
生成后的目录结构
生成器会产出以下关键文件(可在 note_list_renderer 示例 中看到实际效果):
your-plugin/ ├── src/ │ ├── index.ts # 插件源码入口 │ └── manifest.json # 插件清单 ├── api/ # 官方 Joplin Plugin API 类型声明(.d.ts 等) ├── script/ # 发布脚本(含 publish/ 子目录) ├── plugin.config.json # 构建配置(extraScripts 等) ├── webpack.config.js # 构建配置 ├── tsconfig.json ├── package.json ├── README.md └── .gitignore其中最重要的两个文件是:
/src/index.ts:插件源码入口。默认模板内容(见 templates/src/index.ts)为:
import joplin from 'api'; joplin.plugins.register({ onStart: async function() { // eslint-disable-next-line no-console console.info('Hello world. Test plugin started!'); }, });它从api导入joplin对象(Webpack 配置中为api设置了指向本目录api/的路径别名),并通过joplin.plugins.register()注册插件,onStart是插件启动入口。
/src/manifest.json:插件清单,包含名称、版本、作者等元信息。生成后的初始模板见 templates/src/manifest.json,各字段含义:
| 字段 | 说明 |
|---|---|
manifest_version | 清单格式版本,当前为1 |
id | 全局唯一插件 ID(对应提问的 pluginId) |
app_min_version | 支持该插件所需的最低 Joplin 版本(模板默认3.7) |
version | 插件版本号(初始1.0.0,与 package.json 同步) |
name/description/author | 显示名称、描述、作者 |
homepage_url/repository_url | 主页与仓库地址 |
keywords/categories/screenshots | 关键词、分类、截图(用于插件仓库展示) |
icons/promo_tile | 图标与推广图配置 |
构建插件:产出 dist 与 JPL 安装包
构建命令
npm run dist该命令由三条 Webpack 构建串联而成(见 package_TEMPLATE.json 中的scripts.dist):
webpack --env joplin-plugin-config=buildMain && webpack --env joplin-plugin-config=buildExtraScripts && webpack --env joplin-plugin-config=createArchive对应 webpack.config.js 中main()函数的三个构建阶段(注释明确说明 Webpack 配置并行运行会有问题,因此必须串行执行多次):
- buildMain:编译
src/index.ts为dist/index.js,同时用copy-webpack-plugin把src/下其余非 TS/TSX 文件(CSS、图片、无需编译的 JS 等)原样复制到dist/; - buildExtraScripts:按
plugin.config.json中的extraScripts逐个编译附加脚本(若为空则该阶段直接退出,见buildExtraScriptConfigs的空数组判断); - createArchive:触发
onBuildCompleted钩子,把dist/打包为publish/<pluginId>.jpl归档,并生成对应的<pluginId>.json插件信息文件。
构建完成后,产物为:
dist/:编译后的可分发代码目录;- 根目录(实际为
publish/目录):.jpl归档文件(Joplin Plugin 安装包)与.json信息文件,可直接用于分发或在 Joplin 中安装。
打包细节(见 webpack.config.js 的createPluginArchive与createPluginInfo):.jpl实际是用tar以strict+portable模式把dist/下所有文件打成压缩包;.json则是 manifest 的副本,并额外写入_publish_hash(sha256:<jpl 文件的 SHA-256 摘要>)与_publish_commit(当前git分支与提交号,非 git 仓库时留空)两个字段。若dist/为空,打包会直接抛错 "Plugin archive was not created because the "dist" directory is empty"。
构建配置说明
模板项目默认使用TypeScript,但你可以改配置用纯 JavaScript(把入口改成.js并调整 ts-loader 规则即可)。构建链默认mode: 'production'、target: 'node',TS 由ts-loader编译。值得注意的是,Webpack 5 默认不再为 Node 内置模块提供 polyfill,而插件运行在 Electron 的 Node 环境中,因此配置把所有builtinModules的fallback显式设为false,既避免警告也无需 polyfill(源码注释见 webpack.config.js)。
构建时还会对package.json做合法性校验(validatePackageJson):包名不以joplin-plugin-开头、keywords不含joplin-plugin、存在postinstall脚本(建议改用prepare)时都会打印黄色警告。
更新插件版本号
npm run updateVersion该命令执行webpack --env joplin-plugin-config=updateVersion,核心实现在 webpack.config.js 的updateVersion()函数中:
- 解析当前版本号,取最后一段(patch 位)加 1,例如
1.0.3 → 1.0.4; - 同时更新
package.json与manifest.json两个文件的版本号,保证它们始终同步; - 若更新后两处版本号不一致(例如用户曾手动改过其中一个),会打印警告提示手动对齐。
更新插件框架
npm run update该命令在模板 package.json 中的定义为:
npm install -g generator-joplin && yo joplin --node-package-manager npm --update --force即重新安装最新的generator-joplin,并以--update --force模式运行生成器,把模板文件更新到最新版本。
更新模式的合并策略
更新不是无脑覆盖,而是有精细的合并逻辑(见 generators/app/index.js 与 utils.js):
src/与README.md完全不动:noUpdateFiles列表(src/index.ts、src/manifest.json、README.md)在 update 模式下会被跳过;package.json智能合并:mergePackageKey()递归合并——目标已存在的键默认保留用户值;keywords确保包含joplin-plugin;devDependencies一律采用框架新版本(否则依赖无法随框架升级);scripts中的dist、prepare、update三个键强制采用框架版本(若不对,插件将无法正确构建);.gitignore/.npmignore行级合并:mergeIgnoreFile()把新旧文件按行拼接并去重,保留空行;plugin.config.json保留现有内容:源码注释说明"暂时保留现有内容,未来可能再做合并";webpack.config.js会被覆盖:这是更新时唯一容易出问题的文件,因此官方建议不要在它里面直接做大改动——可以新建一个独立 JS 文件,再在webpack.config.js中require引入,这样更新后只需恢复那一行引入语句即可。模板注释中也明确写着同样的建议(见 webpack.config.js)。
另外,进入 update 模式且未加--silent时,生成器会先弹出一个确认对话框,警告更新会覆盖配置文件、不会改动src/与 README,并提醒先把改动纳入版本控制以便 diff 检查;用户选择不继续则直接退出且不做任何更改。
使用 extraScripts 编译外部脚本
什么时候需要它
默认情况下 Webpack 只编译src/index.ts(以及它 import 的文件),其余文件会被直接复制到插件包中。但以下两类外部脚本必须经过编译(见 GENERATOR_DOC.md 原文档与本仓库模板):
- TypeScript 脚本:
.ts必须编译为.js才能在运行时加载; - 依赖了 package.json 中第三方模块的脚本:无论 JS 还是 TS,都必须编译,使依赖被打包进 JPL 文件,否则运行时无法解析模块。
典型场景是内容脚本(content scripts)与Webview 脚本(webview scripts)。
配置方法
编辑plugin.config.json,把脚本路径加入extraScripts数组:
{ "extraScripts": [ "webviews/index.ts" ] }规则与行为(结合 webpack.config.js 的resolveExtraScriptPath()与模板说明):
- 路径相对于
/src:例如文件在/src/webviews/index.ts,就写webviews/index.ts; - 路径指向的文件必须真实存在,否则抛错
Could not find extra script: ...; - 编译产物始终使用
.js扩展名:webviews/index.ts会输出为webviews/index.js,你在插件代码中引用的就是编译后的这个路径; - 编译后的 JS 会覆盖上一阶段(buildMain)复制过去的同名
.js文件——这正是设计意图:不需要编译的 JS 被直接复制,需要编译的则被替换为编译产物(源码注释见 webpack.config.js)。
另外,Webpack 配置把@codemirror/*、@lezer/*等一系列常用编辑器库声明为 external(extraScriptExternals),这意味着内容脚本可以放心地require('@codemirror/view')等库而不必担心被打包或冲突(完整清单见 webpack.config.js)。
发布插件到官方插件仓库
发布流程
先通过npm publish把插件发布到 npmjs.com。之后会有一个自动脚本扫描 npm,把满足条件的插件收录进 Joplin 官方插件仓库。
必须满足的三个条件
插件要进入官方仓库,必须同时满足(见 GENERATOR_DOC.md):
package.json的name以joplin-plugin-开头,例如joplin-plugin-toc;package.json的keywords包含joplin-plugin;publish/目录中存在.jpl和.json两个文件——它们由npm run dist构建生成。
一般情况下,生成器会自动完成这些配置:包名默认带joplin-plugin-前缀(见上文packageNameFromPluginName逻辑)、keywords初始就包含joplin-plugin(见 package_TEMPLATE.json)、publish/目录由npm run dist自动产出。但如果插件没有出现在仓库中,请按上述三条逐一排查。
补充:仓库模板的
package.json还提供了npm run submit脚本(tsc --project script/publish/tsconfig.json && node ./script/publish/dist/index.js),配合 script/publish 目录下的发布流程脚本(含authenticate、verifyBuild、verifyGitState、submitPayload等步骤)使用,用于向插件仓库提交插件信息。此外,webpack.config.js 的validatePackageJson()在构建时就会预先警告这三类不合规情况,是发布前自查的第一道关卡。
生成器的两个特殊运行模式
generators/app/index.js 定义了silent与update两个命令行选项,理解它们有助于正确使用:
--update:进入"框架更新"模式,跳过交互式提问(pluginId等属性置空),按上文合并策略更新配置文件,跳过src/与README.md;--silent:与--update配合时,跳过更新前的确认对话框,适合脚本化/无人值守更新(如模板中npm run update所执行的yo joplin ... --update --force就是如此)。
更进一步:在仓库中验证与学习
- 查看生成器全部模板文件:packages/generator-joplin/generators/app/templates,其中 GENERATOR_DOC.md 即为本文原始依据;
- 阅读生成器核心源码:generators/app/index.js(交互提问、文件写入与更新合并)与 generators/app/utils.js(包名推导、package.json / ignore 文件合并);
- 研究构建链细节:templates/webpack.config.js 完整展示了 buildMain / buildExtraScripts / createArchive 三阶段与版本更新逻辑;
- 参考真实产物:packages/app-cli/tests/support/plugins/note_list_renderer 是仓库内置的由生成器产出的示例插件(包含
api/类型声明、src/、plugin.config.json、webpack.config.js与package.json),可对照其 manifest.json 与 package.json 理解清单字段与脚本的实际形态; - 了解插件 API 全貌:api 目录下的
Joplin.d.ts、JoplinViews*.d.ts、JoplinContentScripts.d.ts等类型声明文件,是开发插件时最权威的 API 参考。
总结
generator-joplin把 Joplin 插件开发从"零散手工搭建"收敛为"一条命令起步":安装 Yeoman 与生成器后,交互式回答几个问题即可获得包含 TypeScript 入口、manifest、Webpack 构建链与官方 API 类型声明的完整工程;npm run dist一键产出dist/与publish/下的 JPL 安装包;npm run updateVersion保证双版本号同步;npm run update在保留src/与 README 的前提下智能升级框架;extraScripts让内容脚本与 Webview 脚本也能被正确编译;而npm publish加上三条发布规范的自动预配置,让插件可以顺利进入 Joplin 官方插件仓库。掌握这套工具链,你就具备了从零开发、构建、升级到发布 Joplin 插件的完整能力。
【免费下载链接】joplinJoplin - the privacy-focused note taking app with sync capabilities for Windows, macOS, Linux, Android and iOS.项目地址: https://gitcode.com/GitHub_Trending/jo/joplin
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考