news 2026/9/10 6:53:59

Joplin 插件开发入门:使用 generator-joplin 脚手架从零创建、构建并发布你的第一个插件

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Joplin 插件开发入门:使用 generator-joplin 脚手架从零创建、构建并发布你的第一个插件

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插件脚手架工具及其生成的插件工程模板展开,完整覆盖插件工程的目录结构、npm run dist构建流程、.jpl归档与发布条件、插件框架升级,以及extraScripts外部脚本的编译配置,并结合仓库内的真实示例插件(如register_command)进行源码级印证。读完本文,你将掌握从yo joplin生成工程、编写src/index.ts插件入口、注册命令与菜单项,到最终将插件发布进 Joplin 官方插件仓库的完整实战链路。

1. generator-joplin 是什么:Joplin 插件工程的 Yeoman 生成器

Joplin 是一款以隐私为核心、支持 Windows / macOS / Linux / Android / iOS 多端同步的笔记应用,其插件系统让开发者可以扩展编辑器、工具栏、菜单、视图面板等能力。而generator-joplin正是 Joplin 官方维护的 Yeoman(当前仓库版本为3.7.2,依赖yeoman-generatorchalkslugifyyosay等)。

从 生成器主逻辑 可以看出,它本质上是把packages/generator-joplin/generators/app/templates/目录下的一套模板工程拷贝到目标目录,并通过交互式提问收集插件元信息后渲染模板。生成器会询问以下字段:

提问字段含义生成后写入的位置
pluginId全局唯一插件 ID,如com.example.MyPlugin或 UUIDsrc/manifest.jsonid字段
pluginName面向用户的插件显示名src/manifest.jsonname字段
pluginDescription插件描述src/manifest.jsondescription字段
pluginAuthor作者src/manifest.jsonauthor字段
pluginRepositoryUrl仓库地址package.json/manifest.json
pluginHomepageUrl主页地址src/manifest.jsonhomepage_url字段
packageNamenpm 包名(默认由插件名推导,形如joplin-plugin-xxxpackage.jsonname字段

生成器还支持--update--silent两个选项:--update用于升级插件框架时覆盖配置文件;--silent跳过确认提示,便于脚本化。需要特别说明的是,由于 npm 存在一个 WONTFIX 级 bug(npm/npm#3763),.gitignorepackage.json在模板目录中以_TEMPLATE后缀命名,生成时才被重命名为正式文件名。

2. 安装与生成:一条命令拉出完整插件工程

generator-joplin通过 npm 全局安装,前置条件是你已安装 node.js(生成器package.json声明npm >= 4.0.0)。安装 Yeoman 与生成器后即可创建新工程:

npm install -g yo npm install -g generator-joplin # 进入你想创建插件的目录,然后执行: yo joplin

执行yo joplin后,按照上一节的字段提示依次填写即可。生成器会输出一个 yosay 欢迎横幅("Welcome to the fine Joplin Plugin generator!",见 生成器源码),随后在 writing() 阶段 完成文件拷贝与模板渲染。

3. 生成工程的目录结构解析

生成的插件工程中,最核心的两个文件是:

  • src/index.ts:插件源码的入口文件,插件逻辑(注册命令、创建视图、挂载菜单等)都在这里编写;
  • src/manifest.json:插件清单,声明插件的 ID、名称、版本、最低 Joplin 版本、作者等信息。

此外,plugin.config.json用于配置外部脚本(content scripts / webview scripts)的编译列表。以仓库内真实示例插件 register_command 为例,其工程结构为:

register_command/ ├── api/ # Joplin 插件 API 的 TypeScript 类型声明(由生成器统一提供) │ ├── index.ts │ ├── Joplin.d.ts │ ├── JoplinCommands.d.ts │ ├── JoplinViews.d.ts │ ├── ...(各 API 子模块 .d.ts) │ └── types.ts # Command、MenuItemLocation、ToolbarButtonLocation 等公共类型 ├── src/ │ ├── index.ts # 插件入口 │ └── manifest.json # 插件清单 ├── package.json ├── plugin.config.json # extraScripts 配置 ├── tsconfig.json ├── webpack.config.js # 由生成器生成的构建脚本(升级时会被覆盖) └── GENERATOR_DOC.md # 本文对应的官方说明文档

该示例插件的 manifest.json 展示了清单的标准写法:

{ "id": "org.joplinapp.plugins.RegisterCommandDemo", "manifest_version": 1, "app_min_version": "1.4", "name": "Register Command Test", "description": "To test registering commands", "version": "1.0.3", "author": "Laurent Cozic", "homepage_url": "https://joplinapp.org" }

字段含义说明:

  • id:插件全局唯一 ID,必须以域名反向或 UUID 形式给出,构建时用于生成.jpl文件名(<id>.jpl);
  • manifest_version:清单格式版本,当前为1
  • app_min_version:插件要求的最低 Joplin 应用版本,低于该版本的客户端会拒绝加载;
  • version:插件版本号,遵循语义化版本约定。

工程默认使用 TypeScript,但你也可以修改配置改用纯 JavaScript。api/目录由生成器从模板统一拷贝(见 生成器 writing 阶段),里面是joplin全局对象的类型声明,src/index.ts通过import joplin from 'api'引入。

4. 构建插件:npm run dist与 Webpack 三步流水线

生成工程在package.json中预置了构建脚本,构建产物分两处:

  • 编译后的代码输出到dist/目录;
  • 根目录会同时生成一个.jpl归档(JPL 即 Joplin Plugin 归档包),用于分发插件。

执行构建只需一条命令:

npm run dist

从 示例插件 package.json 可以看到dist脚本的实际内容:

"scripts": { "dist": "webpack --joplin-plugin-config buildMain && webpack --joplin-plugin-config buildExtraScripts && webpack --joplin-plugin-config createArchive", "prepare": "npm run dist", "update": "npm install -g generator-joplin && yo joplin --update" }

对照 webpack.config.js 源码,dist由三个串行步骤组成:

  1. buildMain:编译src/index.tsdist/index.js,同时通过CopyPluginsrc/下除.ts/.tsx之外的文件(CSS、图片、无需编译的 JS 等资源)原样拷贝到dist/。此步骤还会先清理并重建dist/publish/目录;
  2. buildExtraScripts:按plugin.config.json中的extraScripts列表逐个编译外部脚本,编译产物会覆盖上一步拷贝的同名 JS 文件——这正是设计意图:无需编译的 JS 直接拷贝,需要编译的则被编译版本替换;
  3. createArchive:以dist/index.js为入口触发onBuildCompleted回调,其内部(webpack.config.js#L116-L125)执行createPluginArchive(用tardist/打包为<id>.jpl)与createPluginInfo(生成<id>.json插件信息文件,内含_publish_hashsha256:<jpl哈希>)与_publish_commit(当前 git 分支与提交号)),随后validatePackageJson()校验发布条件并给出警告。

构建所需的 TypeScript 编译选项由 tsconfig.json 提供,采用commonjs模块、es2015目标并允许 JS 混用(allowJs: true)。

4.1 一个真实示例:在入口文件中注册命令

为直观理解"构建什么",可以看 register_command 插件的 src/index.ts,它在onStart中通过joplin.commands.register()注册了 6 个命令:

import joplin from 'api'; import { MenuItemLocation, ToolbarButtonLocation } from 'api/types'; joplin.plugins.register({ onStart: async function() { // 普通命令:带名称、标签、图标 await joplin.commands.register({ name: 'testCommand1', label: 'My Test Command 1', iconName: 'fas fa-music', execute: async () => { alert('Testing plugin command 1'); }, }); // 携带参数的命令:接收选中的笔记 ID 数组 await joplin.commands.register({ name: 'contextMenuCommandExample', label: 'My Context Menu command', execute: async (noteIds: string[]) => { const notes = []; for (const noteId of noteIds) { notes.push(await joplin.data.get(['notes', noteId])); } const noteTitles = notes.map((note: any) => note.title); alert('The following notes will be processed:\n\n' + noteTitles.join(', ')); }, }); // 只可编程调用的命令:返回结果、接收参数,无需 label 与 icon await joplin.commands.register({ name: 'commandWithResult', execute: async (arg1: string, arg2: number) => { return 'I got: ' + arg1 + ' and ' + arg2; }, }); // ... 更多命令注册 }, });

注册命令后,通过joplin.views.toolbarButtons.create()joplin.views.menuItems.create()将命令挂到工具栏和菜单(ToolbarButtonLocation.NoteToolbarEditorToolbarMenuItemLocation.ToolsNoteListContextMenuFolderContextMenuTagContextMenu等),并可用accelerator绑定快捷键:

await joplin.views.toolbarButtons.create('myButton1', 'testCommand1', ToolbarButtonLocation.NoteToolbar); await joplin.views.menuItems.create('myMenuItem1', 'testCommand1', MenuItemLocation.Tools, { accelerator: 'CmdOrCtrl+Alt+Shift+B' });

命令的编程式调用通过joplin.commands.execute(commandName, ...args)完成,API 类型声明 中对executeregister给出了完整签名与示例:execute(commandName: string, ...args: any[]): Promise<any | void>register(command: Command): Promise<void>。除自定义命令外,joplin.commands.execute还可以调用 Joplin 内置命令(如newNotenewFolder),以及通过editor.execCommand间接驱动 CodeMirror / TinyMCE 编辑器命令。

5. 发布插件:npm publish 与官方插件仓库收录条件

插件开发完成后,发布到 npm 即可。后续会有一个官方脚本自动把你的插件收录进 Joplin 插件仓库,前提是满足以下三个条件(详见 GENERATOR_DOC.md):

  1. package.json中的namejoplin-plugin-开头,例如joplin-plugin-toc
  2. package.json中的keywords包含joplin-plugin
  3. publish/目录下存在.jpl.json文件(由npm run dist构建产生)。
npm publish

上述三个条件在构建时会被 webpack.config.js 的 validatePackageJson() 自动校验:若包名不以joplin-plugin-开头或keywords缺少joplin-plugin,构建会输出黄色警告。此外它还会提示:如果package.jsonpostinstall脚本,建议改用prepare脚本(prepare会在 publish 前执行)。正常流程下,生成器已经替你设好了包名与关键词,并把构建产物放进了publish/目录;若插件未出现在仓库中,可对照上述条件逐一排查。

仓库内还提供了一个发布辅助脚本示例:生成器模板中的 script/publish/index.ts(含authenticateverifyBuildverifyGitStatesubmitPayload等步骤,用于验证构建产物与 git 状态后提交发布负载)。

6. 升级插件框架:npm run update

当 Joplin 插件框架更新后,可用生成器提供的升级命令同步框架代码:

npm run update

该命令本质上执行npm install -g generator-joplin && yo joplin --update(见 示例插件 package.json 的update脚本)。升级策略(由 生成器 index.js 实现):

  • 智能合并package.json通过mergePackageKey合并而非覆盖,.gitignore/.npmignore通过mergeIgnoreFile合并;
  • 保留不动src/目录以及README.md不会被修改;
  • 覆盖重写webpack.config.js会被直接覆盖,这是唯一需要小心的文件。因此官方建议:如需定制构建逻辑,新建一个独立 JS 文件并在webpack.config.jsrequire引入,这样升级后只需恢复那一行 include 语句即可。

yo joplin --update首次运行会弹出确认提示,明确告知"升级将覆盖配置文件、不会改动 /src 与 README.md",并建议在改动过配置的情况下先纳入版本控制以便事后检查 diff(用--silent可跳过确认)。

7. 外部脚本文件(extraScripts):content scripts 与 webview scripts 的编译

默认情况下,Webpack 只编译src/index.ts(及其 import 的模块),其余文件会被原样拷贝进插件包。多数场景这样已足够,但有两种情况需要额外编译外部脚本:

  1. 脚本本身是 TypeScript 文件——必须编译为 JavaScript 才能运行;
  2. 脚本 import 了你在 package.json 中新增的 npm 模块——此时无论脚本是 JS 还是 TS,都必须编译,才能把依赖一并打进 JPL 包。

配置方式是在plugin.config.jsonextraScripts数组中列出脚本路径,路径相对src/目录书写。例如src/webviews/index.ts应写为:

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

编译规则(见 webpack.config.js 的 resolveExtraScriptPath()):

  • 入口解析为./src/<name>,文件必须存在,否则构建报错Could not find extra script
  • 输出文件名固定为.js扩展名,即webviews/index.ts编译后得到插件包内的webviews/index.js
  • 输出采用library: 'default'libraryTarget: 'commonjs'libraryExport: 'default'的 CommonJS 包装,便于 Joplin 运行时按模块加载;
  • 编译产物会覆盖buildMain阶段从src/拷贝的同名 JS 文件。

因此,在你的代码(如joplin.contentScripts.registerjoplin.views.panelsaddScript)中引用这些脚本时,应使用编译后的.js路径(即webviews/index.js)。

仓库中所有支持插件的测试工程都使用这一配置约定,可参考 register_command(其extraScripts为空数组[]),以及生成器模板 templates/plugin.config.json。

8. 参考与进一步阅读

  • 本指南对应的官方说明文档:GENERATOR_DOC.md
  • 生成器源码与 README:packages/generator-joplin 与 generator-joplin README
  • 生成器模板工程(含src/index.tsmanifest.jsonwebpack.config.jsapi/类型声明):packages/generator-joplin/generators/app/templates
  • 命令注册示例插件(含 6 个命令与工具栏/菜单挂载示例):register_command 插件
  • 命令 API 类型声明:JoplinCommands.d.ts
  • 构建与发布辅助脚本:webpack.config.js、publish 脚本模板

本文所有内容均以当前仓库中generator-joplin生成器源码、register_command示例插件及其官方说明文档为依据,你可以直接进入上述目录阅读源码、运行yo joplin生成属于自己的插件工程,并通过npm run dist验证构建与发布链路。

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

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/10 6:52:20

轻量级AI Agent框架实践:从零搭建大模型工具调用与任务规划系统

Hermes 这名字&#xff0c;懂点希腊神话的朋友应该不陌生&#xff0c;就是那位脚上长翅膀、整天忙着传信的使者。把 agent 接在它后面&#xff0c;想表达的意思很直接&#xff1a;我想做一个负责“传递意图、调度工具”的智能体&#xff0c;让大模型不只会聊天&#xff0c;还能…

作者头像 李华
网站建设 2026/9/10 6:49:35

Linux磁盘与文件系统从入门到排查:分区、LVM、NFS与常见故障

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/10 6:49:32

共享储能下多微电网优化调度:Stackelberg博弈与Matlab仿真实现

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/10 6:48:22

danswer(Onyx)Box 连接器每日集成测试环境搭建与运行指南

danswer&#xff08;Onyx&#xff09;Box 连接器每日集成测试环境搭建与运行指南 【免费下载链接】danswer Open Source AI Platform - AI Chat with advanced features that works with every LLM 项目地址: https://gitcode.com/GitHub_Trending/da/danswer 本文以仓库…

作者头像 李华
网站建设 2026/9/10 6:47:11

伴随灵敏度分析在肿瘤生长模型与时空放疗优化中的应用

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华