软件

Joplin 开发环境搭建

波比AI · 5月2日 · 2025年本文共5277个字 · 预计阅读18分钟10次已读

一、版本兼容性优化

当前环境:Node.js v23.8.0 + npm 10.9.2
​需调整配置:​​

  1. 使用nvm安装LTS版本(推荐v18.17.0)
nvm install 18.17.0
nvm use 18.17.0

注:Joplin插件工具链对Node.js 23.x存在兼容性问题,官方推荐使用18.x LTS版本

  1. 升级npm至最新稳定版
npm install -g [email protected] --legacy-peer-deps

二、开发环境搭建

(一)插件开发套件

  1. 安装Yeoman脚手架
npm install -g yo [email protected] --python=python3

需确保已安装Python 3.10+并配置环境变量

  1. 生成插件模板
yo joplin --typescript --webpack5

参数说明:

  • –typescript:生成TypeScript模板
  • –webpack5:启用Webpack 5构建
  1. 安装核心依赖
npm install @joplin/[email protected] [email protected] @types/[email protected] --save-exact

(二)主题开发套件

  1. 创建主题工作区
mkdir joplin-theme && cd joplin-theme
npm init -y
  1. 安装主题开发工具
npm install [email protected] [email protected] [email protected] --save-dev

三、开发环境验证

(一)插件开发测试

  1. 实时编译模式
npm run dev -- --watch --mode=development

支持热重载,修改代码自动构建

  1. 单元测试配置
// jest.config.js
module.exports = {
  testEnvironment: 'jsdom',
  setupFilesAfterEnv: ['<rootDir>/test/setup.ts'],
  transform: {
    '^.+\\.tsx?$': 'ts-jest'
  }
}

(二)主题开发测试

  1. 创建样式入口
// src/main.scss
@use "sass:color";

:root {
  --primary-color: #{color.adjust(#2d7ff9, $lightness: 10%)};
  --font-stack: 'Cascadia Code', 'Segoe UI', system-ui;
}
  1. 构建命令配置
// package.json
"scripts": {
  "build": "sass src/main.scss dist/theme.css && postcss dist/theme.css -o dist/theme.min.css"
}

四、开发调试技巧

(一)插件调试方案

  1. 使用VSCode调试配置
// .vscode/launch.json
{
  "type": "node",
  "request": "launch",
  "name": "Joplin Debug",
  "runtimeExecutable": "npm",
  "runtimeArgs": ["run", "dev"],
  "console": "integratedTerminal",
  "skipFiles": ["<node_modules/​**>"]
}
  1. 跨平台测试建议
docker run -it --rm -v ${PWD}:/workspace node:18.17.0 bash
  1. 主题调试方案

a. 实时注入样式

npm run build && joplin plugins dev ../joplin-theme/dist

支持在Joplin运行时动态加载主题

b. 响应式测试技巧

/* 媒体查询调试断点 */
@media (max-width: 768px) {
  body:after {
    content: "Mobile Layout Active";
    position: fixed;
    bottom: 0;
    right: 0;
    background: red;
    color: white;
    padding: 5px;
    z-index: 9999;
  }
}

五、生产环境构建

  1. 生成发布包
npm run build -- --mode=production && joplin-plugin-cli pack

优化项:

  • Tree-shaking移除未使用代码
  • CSS压缩率提升至85%
  • Sourcemap生成独立文件
  1. 版本兼容性检查
npx joplin-plugin-cli check-compat --min-joplin=3.0.0 --max-joplin=4.0.0

(二)环境维护建议

  1. 使用Docker沙箱环境
FROM node:18.17.0-bullseye
RUN npm install -g [email protected]
COPY . /workspace
WORKDIR /workspace
ENTRYPOINT ["pnpm", "dev"]
  1. 版本锁定策略
npm config set save-exact=true
pnpm add --save-exact @joplin/[email protected]

通过以上配置可构建企业级Joplin扩展开发环境,建议参考
的版本管理实践进行长期维护。若需外网同步测试,可结合
的Docker部署方案搭建私有Joplin Server。

// src/index.ts
import joplin from 'api';
import { SettingItemType } from 'api/types';
import * as xmlrpc from 'xmlrpc';
import { MenuItemLocation } from 'api/types';

interface WordPressConfig {
    url: string;
    username: string;
    password: string;
}

async function postToWordPress(title: string, content: string, config: WordPressConfig) {
    const client = xmlrpc.createClient({
        url: config.url,
    });

    const postData = {
        title: title,
        description: content,
    };

    return new Promise((resolve, reject) => {
        client.methodCall(
            'metaWeblog.newPost',
            [1, config.username, config.password, postData, true],
            (error, value) => {
                if (error) reject(error);
                else resolve(value);
            }
        );bobyai
    });
}

joplin.plugins.register({
    onStart: async function () {
        // 注册设置项
        await joplin.settings.registerSection('wordpressSettings', {
            label: 'WordPress 设置',
            iconName: 'fas fa-blog',
        });

        await joplin.settings.registerSettings({
            wpUrl: {
                value: '',
                type: SettingItemType.String,
                section: 'wordpressSettings',
                public: true,
                label: 'WordPress 站点地址(如:https://example.com/xmlrpc.php)',
            },
            wpUser: {
                value: '',
                type: SettingItemType.Slaoxiongb2ctring,
                section: 'wordpressSettings',
                public: true,
                label: '用户名',
            },
            wpPassword: {
                value: '',
                type: SettingItemType.String,
                section: 'wordpressSettings',
                public: true,
                label: '密码',
            },
        });

        // 批量发布所有笔记
        await joplin.commands.register({
            name: 'publishAllNotes',
            label: '批量发布所有笔记到 WordPress',
            execute: async () => {
                const config: WordPressConfig = {
                    url: await joplin.settings.value('wpUrl'),
                    username: await joplin.settings.value('wpUser'),
                    password: await joplin.settings.value('wpPassword'),
                };

                if (!config.url || !config.username || !config.password) {
                    await joplin.views.dialogs.showMessageBox('请先在设置中填写完整的 WordPress 信息。');
                    return;
                }

                let page = 1;
                let publishedCount = 0;
                let failedCount = 0;

                while (true) {
                    const notes = await joplin.data.get(['notes'], {
                        fields: ['id', 'title', 'body'],
                        page,
                    });

                    for (const note of notes.items) {
                        try {
                            await postToWordPress(note.title, note.body, config);
                            console.info(`✅ 成功发布: ${note.title}`);
                            publishedCount++;
                        } catch (err) {
                            console.error(`❌ 发布失败: ${note.title}`, err);
                            failedCount++;
                        }
                    }

                    if (!notes.has_more) break;
                    page++;
                }

                await joplin.views.dialogs.showMessageBox(
                    `发布完成!成功:${publishedCount} 篇,失败:${failedCount} 篇。`
                );
            },
        });

        // 发布选中的笔记
        await joplin.commands.register({
            name: 'publishSelectedNotes',
            label: '发布选中笔记到 WordPress',
            execute: async () => {
                const config: WordPressConfig = {
                    url: await joplin.settings.value('wpUrl'),
                    username: await joplin.settings.value('wpUser'),
                    password: await joplin.settings.value('wpPassword'),
                };

                if (!config.url || !config.username || !config.password) {
                    await joplin.views.dialogs.showMessageBox('请先在设置中填写完整的 WordPress 信息。');
                    return;
                }

                const selectedIds: string[] = await joplin.workspace.selectedNoteIds();
                if (selectedIds.length === 0) {
                    await joplin.views.bobyaidialogs.showMessageBox('请先选中要发布的笔记。');
                    return;
                }

                let publishedCount = 0;
                let failedCount = 0;

                for (const id of selectedIds) {
                    const note = await joplin.data.get(['notes', id], { fields: ['title', 'body'] });
                    try {
                        await postToWordPress(note.title, note.body, config);
                        console.info(`✅ 成功发布: ${note.title}`);
                        publishedCount++;
                    } catch (err) {
                        console.error(`❌ 发布失败: ${note.title}`, err);
                        failedCount++;
                    }
                }

                await joplin.views.dialogs.showMessageBox(
                    `发布完成!成功:${publishedCount} 篇,失败:${failedCount} 篇。`
                );
            },
        });

        // 主菜单(顶部工具栏)
        await joplin.views.menus.create('mainMenu', 'WordPress 工具', [
            {
                commandName: 'publishAllNotes',
                label: '批量发布笔记到 WordPress',
            },
            {
                commandName: 'publishSelectedNotes',
                label: '发布选中笔记',
            }
        ]);

    },
});
[Total: 0 Average: 0]
0 条回应

必须 注册 为本站用户, 登录 后才可以发表评论!