diff --git a/packages/app-cli/tests/services_PluginService.ts b/packages/app-cli/tests/services_PluginService.ts index 3a4530bd7b..88e74f4f3f 100644 --- a/packages/app-cli/tests/services_PluginService.ts +++ b/packages/app-cli/tests/services_PluginService.ts @@ -4,11 +4,11 @@ import { ContentScriptType } from '@joplin/lib/services/plugins/api/types'; import MdToHtml from '@joplin/renderer/MdToHtml'; import shim from '@joplin/lib/shim'; import Setting from '@joplin/lib/models/Setting'; - -const fs = require('fs-extra'); -const { expectNotThrow, setupDatabaseAndSynchronizer, switchClient, expectThrow, createTempDir } = require('./test-utils.js'); +import * as fs from 'fs-extra'; import Note from '@joplin/lib/models/Note'; import Folder from '@joplin/lib/models/Folder'; +import { newPluginScript } from './test-utils'; +import { expectNotThrow, setupDatabaseAndSynchronizer, switchClient, expectThrow, createTempDir } from './test-utils.js'; const testPluginDir = `${__dirname}/../tests/support/plugins`; @@ -268,7 +268,7 @@ describe('services_PluginService', function() { const pluginPath = `${testPluginDir}/jpl_test/org.joplinapp.FirstJplPlugin.jpl`; await service.installPlugin(pluginPath); const installedPluginPath = `${Setting.value('pluginDir')}/org.joplinapp.FirstJplPlugin.jpl`; - expect(await fs.existsSync(installedPluginPath)).toBe(true); + expect(await fs.pathExists(installedPluginPath)).toBe(true); })); it('should rename the plugin archive to the right name', (async () => { @@ -279,7 +279,30 @@ describe('services_PluginService', function() { await shim.fsDriver().copy(pluginPath, tempPath); const installedPluginPath = `${Setting.value('pluginDir')}/org.joplinapp.FirstJplPlugin.jpl`; await service.installPlugin(tempPath); - expect(await fs.existsSync(installedPluginPath)).toBe(true); + expect(await fs.pathExists(installedPluginPath)).toBe(true); + })); + + it('should create the data directory', (async () => { + const pluginScript = newPluginScript(` + joplin.plugins.register({ + onStart: async function() { + const dataDir = await joplin.plugins.dataDir(); + joplin.data.post(['folders'], null, { title: JSON.stringify(dataDir) }); + }, + }); + `); + + const expectedPath = `${Setting.value('pluginDataDir')}/org.joplinapp.plugins.PluginTest`; + expect(await fs.pathExists(expectedPath)).toBe(false); + + const service = newPluginService(); + const plugin = await service.loadPluginFromJsBundle('', pluginScript); + await service.runPlugin(plugin); + + expect(await fs.pathExists(expectedPath)).toBe(true); + + const folders = await Folder.all(); + expect(JSON.parse(folders[0].title)).toBe(expectedPath); })); }); diff --git a/packages/app-cli/tests/test-utils.ts b/packages/app-cli/tests/test-utils.ts index b95ef08752..1839e38ffd 100644 --- a/packages/app-cli/tests/test-utils.ts +++ b/packages/app-cli/tests/test-utils.ts @@ -182,6 +182,7 @@ Setting.setConstant('appId', 'net.cozic.joplintest-cli'); Setting.setConstant('appType', 'cli'); Setting.setConstant('tempDir', baseTempDir); Setting.setConstant('cacheDir', baseTempDir); +Setting.setConstant('pluginDataDir', `${dataDir}/plugin-data`); Setting.setConstant('env', 'dev'); BaseService.logger_ = logger; diff --git a/packages/lib/BaseApplication.ts b/packages/lib/BaseApplication.ts index 53d376fa47..be2b5f4af0 100644 --- a/packages/lib/BaseApplication.ts +++ b/packages/lib/BaseApplication.ts @@ -676,6 +676,7 @@ export default class BaseApplication { Setting.setConstant('resourceDirName', resourceDirName); Setting.setConstant('resourceDir', resourceDir); Setting.setConstant('tempDir', tempDir); + Setting.setConstant('pluginDataDir', `${profileDir}/plugin-data`); Setting.setConstant('cacheDir', cacheDir); Setting.setConstant('pluginDir', `${profileDir}/plugins`); diff --git a/packages/lib/models/Setting.ts b/packages/lib/models/Setting.ts index f2df41ec9f..12f5b862cf 100644 --- a/packages/lib/models/Setting.ts +++ b/packages/lib/models/Setting.ts @@ -147,6 +147,7 @@ class Setting extends BaseModel { profileDir: '', templateDir: '', tempDir: '', + pluginDataDir: '', cacheDir: '', pluginDir: '', flagOpenDevTools: false, diff --git a/packages/lib/services/plugins/Plugin.ts b/packages/lib/services/plugins/Plugin.ts index bae02c219a..2dc0fb4540 100644 --- a/packages/lib/services/plugins/Plugin.ts +++ b/packages/lib/services/plugins/Plugin.ts @@ -33,12 +33,15 @@ export default class Plugin { private devMode_: boolean = false; private messageListener_: Function = null; private contentScriptMessageListeners_: Record = {}; + private dataDir_: string; + private dataDirCreated_: boolean = false; - constructor(baseDir: string, manifest: PluginManifest, scriptText: string, dispatch: Function) { + constructor(baseDir: string, manifest: PluginManifest, scriptText: string, dispatch: Function, dataDir: string) { this.baseDir_ = shim.fsDriver().resolve(baseDir); this.manifest_ = manifest; this.scriptText_ = scriptText; this.dispatch_ = dispatch; + this.dataDir_ = dataDir; this.eventEmitter_ = new EventEmitter(); } @@ -66,6 +69,17 @@ export default class Plugin { return this.baseDir_; } + public async dataDir(): Promise { + if (this.dataDirCreated_) return this.dataDir_; + + if (!(await shim.fsDriver().exists(this.dataDir_))) { + await shim.fsDriver().mkdir(this.dataDir_); + this.dataDirCreated_ = true; + } + + return this.dataDir_; + } + public get viewCount(): number { return Object.keys(this.viewControllers_).length; } @@ -157,5 +171,4 @@ export default class Plugin { return this.contentScriptMessageListeners_[id](message); } - } diff --git a/packages/lib/services/plugins/PluginService.ts b/packages/lib/services/plugins/PluginService.ts index e2b401ec1d..9564630207 100644 --- a/packages/lib/services/plugins/PluginService.ts +++ b/packages/lib/services/plugins/PluginService.ts @@ -267,7 +267,9 @@ export default class PluginService extends BaseService { const manifest = manifestFromObject(manifestObj); - const plugin = new Plugin(baseDir, manifest, scriptText, (action: any) => this.store_.dispatch(action)); + const dataDir = `${Setting.value('pluginDataDir')}/${manifest.id}`; + + const plugin = new Plugin(baseDir, manifest, scriptText, (action: any) => this.store_.dispatch(action), dataDir); for (const msg of deprecationNotices) { plugin.deprecationNotice('1.5', msg); diff --git a/packages/lib/services/plugins/api/JoplinPlugins.ts b/packages/lib/services/plugins/api/JoplinPlugins.ts index db12217083..407524fbd5 100644 --- a/packages/lib/services/plugins/api/JoplinPlugins.ts +++ b/packages/lib/services/plugins/api/JoplinPlugins.ts @@ -58,12 +58,11 @@ export default class JoplinPlugins { return this.plugin.registerContentScript(type, id, scriptPath); } - // public async onMessage(callback: any) { - // this.plugin.onMessage(callback); - // } - - // public async onContentScriptMessage(id: string, callback: any) { - // this.plugin.onContentScriptMessage(id, callback); - // } + /** + * Gets the plugin own data directory path. Use this to store any plugin-related data. + */ + public async dataDir(): Promise { + return this.plugin.dataDir(); + } }