Joplin 插件开发快速上手指南:基于 generator-joplin 从脚手架到发布全流程
2026/9/10 4:28:53 网站建设 项目流程

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 是主打隐私保护的开源笔记应用,其插件机制让开发者能够通过官方 API 扩展编辑器、内容脚本、Webview 面板等能力。本指南以 Joplin 仓库内置的 Yeoman 生成器generator-joplin(对应生成器文档 packages/generator-joplin/generators/app/templates/GENERATOR_DOC.md)为核心,完整讲解"环境安装 → 脚手架生成 → 项目结构 → 构建打包 → 版本管理 → 发布上架 → 框架升级 → 外部脚本编译"的插件全生命周期,并结合仓库源码逐层剖析npm run distplugin.config.jsonwebpack.config.js等关键文件的底层实现,帮助你从零开始产出可分发、可发布的 Joplin 插件。

环境准备与项目脚手架生成

1. 安装 Yeoman 与 generator-joplin

生成器依赖 Node.js 与 npm(官方文档假定你已预先安装)。先全局安装 Yeoman 与生成器本体:

npm install -g yo@4.3.1 npm install -g generator-joplin

生成器模板文档中给出的版本为yo@4.3.1,这是与当前生成器(generator-joplin3.7.2,见 packages/generator-joplin/package.json)配套验证过的版本。生成器本身依赖yeoman-generator5.10.0,并额外使用chalkslugifyyosay三个库完成交互提示与包名处理。

2. 生成一个新插件项目

安装完成后,在希望创建插件的目录下运行:

yo --node-package-manager npm joplin

与早期写法yo joplin相比,--node-package-manager npm显式指定包管理器为 npm,避免 Yeoman 因环境差异推断出错。

运行后会进入交互式问答流程。对照生成器源码 packages/generator-joplin/generators/app/index.js 中的prompting()方法,需要依次填写以下信息:

提问字段含义示例
pluginId插件唯一 ID,必须是全局唯一值,如反向域名或 UUIDcom.example.MyPlugin
pluginName插件名称,将显示在 UI 中My TOC Plugin
pluginDescription插件描述Adds a table of contents
pluginAuthor作者名称Your Name
pluginRepositoryUrl源码仓库地址https://example.com/repo
pluginHomepageUrl插件主页地址https://example.com
packageNamenpm 包名默认由插件名推导,见下文

其中packageName不会直接询问,而是由生成器根据pluginName自动推导出默认值后再让你确认。推导逻辑位于 packages/generator-joplin/generators/app/utils.js 的packageNameFromPluginName()

  • *+~.()'"!:@[]等特殊字符替换为-
  • slugify(..., { lower: true })将非字母字符转为小写字母;
  • 去掉首尾多余的-
  • 统一加上joplin-plugin-前缀;
  • 截断到 214 字符以内(npm 包名长度上限)。

例如插件名My TOC会被推导为joplin-plugin-my-toc。这一步很重要:包名是否符合joplin-plugin-前缀,直接决定插件能否被官方插件仓库收录(详见下文"发布插件"小节)。

生成器在writing()阶段会通过copyTpl把所有模板文件渲染到目标目录,其中.gitignorepackage.json因 npm 的历史 bug(npm/npm#3763)在模板中命名为.gitignore_TEMPLATEpackage_TEMPLATE.json,安装时再重命名还原。

生成后的项目结构解析

新生成的项目中,最关键的两个文件是:

  • src/index.ts:插件源码入口。生成器给出的默认实现只有几行——通过api别名导入 Joplin 插件 API,并注册一个在启动时打印日志的插件:
import joplin from 'api'; joplin.plugins.register({ onStart: async function() { // eslint-disable-next-line no-console console.info('Hello world. Test plugin started!'); }, });
  • src/manifest.json:插件清单文件,声明插件 ID、版本、名称、描述、作者、主页、仓库地址、分类与截图等元信息。生成器模板(packages/generator-joplin/generators/app/templates/src/manifest.json)默认结构如下:
{ "manifest_version": 1, "id": "com.example.MyPlugin", "app_min_version": "3.7", "version": "1.0.0", "name": "My TOC Plugin", "description": "Adds a table of contents", "author": "Your Name", "homepage_url": "https://example.com", "repository_url": "https://example.com/repo", "keywords": [], "categories": [], "screenshots": [], "icons": {}, "promo_tile": {} }

其中id即前面填写的pluginIdversionpackage.json中的版本号保持一致(updateVersion脚本负责同步,见后文)。app_min_version为生成器当前支持的 Joplin 最低版本 3.7。模板同时会复制整套api/目录(Joplin.d.tsJoplinViews.d.tsJoplinSettings.d.ts等 TypeScript 类型声明,覆盖编辑器、视图面板、菜单、工具栏、对话框、数据访问等全部 API 面),以及script/publish/发布辅助脚本。

此外plugin.config.json值得注意——它默认只包含一个空的extraScripts数组,用于声明需要额外编译的外部脚本(内容脚本、Webview 脚本),详见"外部脚本文件"一节。

构建插件:npm run dist的完整工作流

1. 三条 Webpack 配置链

生成器模板的package.json(packages/generator-joplin/generators/app/templates/package_TEMPLATE.json)把构建命令定义为一串依次执行的三阶段 Webpack 调用:

"dist": "webpack --env joplin-plugin-config=buildMain && webpack --env joplin-plugin-config=buildExtraScripts && webpack --env joplin-plugin-config=createArchive"

之所以要拆成三次串行调用而不是并行,原因写在 webpack.config.js 的注释里:各阶段存在先后依赖,Webpack 并行运行会破坏顺序,因此通过--env joplin-plugin-config参数切换配置。具体三个阶段是:

  1. buildMain:编译主入口src/index.ts,并通过copy-webpack-pluginsrc/下其余资源(非.ts/.tsx文件)原样复制到dist/。此阶段开始时还会清空dist/publish/目录并重建publish/
  2. buildExtraScripts:按plugin.config.json中的extraScripts列表逐个编译外部脚本,编译产物会覆盖第一阶段复制过来的同名 JS 文件——这是有意为之的设计:不需要编译的 JS 直接复制,需要编译的则被替换为编译结果。
  3. createArchive:以dist/index.js为占位入口触发onBuildCompleted钩子,用tar把整个dist/打成 JPL 压缩包,并生成配套的插件信息 JSON。

构建完成后你会得到两类产物:

  • dist/:编译后的代码目录;
  • publish/<插件ID>.jpl:JPL 插件压缩包(可直接在 Joplin 中安装分发),以及publish/<插件ID>.json:插件信息文件。

2. JPL 打包与校验的底层逻辑

打包与校验逻辑全部集中在 webpack.config.js 中:

  • createPluginArchive()glob枚举dist/下所有文件(dist为空会直接报错),然后通过tar.createportable: truestrict: true模式打成.jpl
  • createPluginInfo()会读取manifest.json,追加_publish_hash(JPL 文件的 sha256 哈希,格式为sha256:...)与_publish_commit(当前 git 分支与 commit,若不在 git 仓库中则为空字符串)后写入publish/<ID>.json
  • validatePackageJson()package.json做发布前校验:包名必须以joplin-plugin-开头、keywords 必须包含joplin-plugin,同时警告不要使用postinstall脚本(建议改用prepare,确保发布前一定执行构建);
  • readManifest()校验 manifest 中id必须存在,categories不得重复且必须属于allPossibleCategories(appearance、developer tools、productivity、themes、integrations、viewer、search、tags、editor、files、personal knowledge management 之一,且必须为小写),screenshotssrc类型必须属于 jpg/jpeg/png/gif/webp 且本地截图文件不超过 1MB。

3. 关于 TypeScript 与 Webpack 配置

模板项目默认使用 TypeScript(ts-loader处理.ts/.tsx),但文档明确说明你可以改配置改用纯 JavaScript。Webpack 配置中还有一个细节:由于插件运行在 Electron 的 Node 环境中,模板把 Node 内建模块的fallback全部设为false,避免 Webpack 5 因不再默认 polyfill 而弹出警告。

版本号管理:npm run updateVersion

插件开发中经常需要递增版本号。直接手工同步package.jsonmanifest.json容易出错,因此生成器内置了updateVersion脚本:

"updateVersion": "webpack --env joplin-plugin-config=updateVersion"

其实现updateVersion()(同样在 webpack.config.js 中)会把package.jsonmanifest.json中的版本号patch 位 +1(如 1.0.3 → 1.0.4),保持两者同步;若发现两者版本不一致会打印警告提示手工对齐。发布新版本前先跑这个命令,可以避免"插件版本号没变导致插件仓库不更新"的常见问题。

发布插件到 Joplin 插件仓库

1. 通过 npm publish 发布

构建完成后,把插件发布到 npmjs.com:

npm publish

之后 Joplin 的插件仓库脚本会自动拾取你的插件并收录,前提是包满足以下全部条件:

  • package.jsonnamejoplin-plugin-开头,例如joplin-plugin-toc
  • package.jsonkeywords包含joplin-plugin
  • publish/目录下同时存在.jpl.json两个文件(它们由npm run dist自动生成)。

正常情况下,生成器已自动设置好包名与 keywords,并把正确的文件放进publish/如果插件迟迟没有出现在插件仓库中,优先复查上述三个条件——这是文档特别强调的排错路径,对应validatePackageJson()中的警告逻辑。

2. 生成器自带的 submit 一键发布流程

除了手工npm publish,新版生成器还随模板附赠了一套script/publish/自动化发布脚本,通过npm run submit触发。入口 script/publish/index.ts 将发布拆成四个阶段:

  1. verifyBuild:校验构建产物与元数据;
  2. verifyGitState:校验 git 状态(确保基于干净的提交发布);
  3. authenticate:GitHub OAuth 设备流认证(依赖@octokit/auth-oauth-device);
  4. submitPayload:向 Joplin 插件仓库提交 payload。

整套流程对版本号同步、构建产物存在性、git 干净度做了前置把关,适合希望把发布固化为标准流水线的开发者。

升级插件框架:npm run update

Joplin 插件 API 会随版本演进,生成器提供了框架升级命令:

"update": "npm install -g generator-joplin && yo joplin --node-package-manager npm --update --force"

执行前它会先全局更新generator-joplin,然后以--update模式重新运行生成器。对照 generators/app/index.js 的writing()实现,更新模式下的行为要点如下:

  • 不会动你的业务代码src/index.tssrc/manifest.jsonREADME.md在更新时被跳过,保证已有插件逻辑不受影响;
  • package.json 走智能合并:调用mergePackageKey()(见 utils.js)——以你现有的package.json为基础,框架新出现的键会被补进来;keywords强制确保包含joplin-plugindevDependencies一律以框架版本为准覆盖;scripts中的distprepareupdate三个键也强制采用框架版本(否则插件可能构建失败);其余键尽量保留你的自定义值;
  • .gitignore/.npmignore 走行级合并mergeIgnoreFile()将框架模板与你现有文件的规则按行去重合并,而不是整体覆盖;
  • plugin.config.json 保留原内容:更新时不改动,避免丢失你配置的extraScripts
  • 其余配置文件(如 webpack.config.js)会被覆盖

正因为webpack.config.js每次升级都会被覆盖,文档给出的最佳实践是:不要直接改 webpack.config.js,而是另建一个独立 JS 文件,在 webpack.config.js 中用一行require引入。这样升级后只需恢复那一行引入语句,你的自定义配置就能原样回归。

外部脚本文件:内容脚本与 Webview 脚本的编译

1. 何时需要"额外编译"

默认情况下,Webpack 只编译src/index.ts及其 import 的模块,其余文件只是被原样复制进插件包。这对简单插件已经够用,但遇到以下两类脚本时就必须编译:

  • TypeScript 脚本.ts无法直接被 Joplin 执行,必须编译为 JavaScript;
  • 依赖了 package.json 中第三方模块的脚本:无论 JS 还是 TS,都必须编译,把依赖打包进 JPL 文件,否则运行时找不到模块。

典型场景就是文档中提到的content scripts(内容脚本,用于在笔记编辑器内注入自定义行为)与webview scripts(面板 Webview 中运行的脚本)。

2. 通过 extraScripts 声明并引用

要让某个外部脚本参与编译,把它加入plugin.config.jsonextraScripts数组,路径相对于src/目录。例如源码位于src/webviews/index.ts,则配置为:

{ "extraScripts": ["webviews/index.ts"] }

编译后,脚本永远以.js扩展名输出(类型后缀会被剥离),上例最终产物为插件包内的webviews/index.js——引用脚本时(例如通过joplin.views.panels.addScript())必须使用这个编译后的路径,而不是原始.ts路径。

底层实现见webpack.config.jsresolveExtraScriptPath()buildExtraScriptConfigs():每个 extra script 都会生成一个独立的 Webpack 配置,入口为./src/<name>,输出文件名去掉扩展名后补.js,并以commonjs库模式导出默认值。同时模板还为 extra script 预置了一组 CodeMirror 相关库(@codemirror/*@lezer/*)的externals声明——如果你的内容脚本通过require()joplin.require()引用这些库,它们不会被重复打进 JPL,而是直接复用 Joplin 运行时自带的版本。

总结:插件开发的完整命令流

阶段命令作用
初始化yo --node-package-manager npm joplin交互式生成插件项目
开发编辑src/index.tssrc/manifest.json编写插件逻辑与元信息
构建npm run dist编译代码、打出dist/publish/*.jplpublish/*.json
升级版本npm run updateVersion同步递增 package.json 与 manifest.json 的 patch 版本
发布npm publish(或npm run submit发布到 npm,等待官方仓库自动收录
框架升级npm run update合并更新框架文件,保留业务代码

围绕这条流程,本文涉及的模板与实现均可在仓库中继续深挖:生成器模板目录、生成器交互与文件写入逻辑、包名推导与合并工具、Webpack 构建配置 以及 生成器自身说明。从脚手架到发布,generator-joplin把模板生成、构建打包、校验、发布、升级各环节串成了一条自动化流水线,开发者只需专注于src/下的业务代码即可。

【免费下载链接】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),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询