From 22d6b3bac7269bd891b035d32596255ed112f1b0 Mon Sep 17 00:00:00 2001 From: dubisdev Date: Sun, 28 Aug 2022 00:11:01 +0200 Subject: [PATCH 1/2] fix: allow plugins to have scope fix #468 --- app/lib/plugins/index.js | 1 - app/plugins/externalPlugins.js | 22 ++++++++-------------- 2 files changed, 8 insertions(+), 15 deletions(-) diff --git a/app/lib/plugins/index.js b/app/lib/plugins/index.js index e0e8d5d3..4c921e6f 100644 --- a/app/lib/plugins/index.js +++ b/app/lib/plugins/index.js @@ -22,7 +22,6 @@ const EMPTY_PACKAGE_JSON = JSON.stringify({ export const pluginsPath = path.join(getAppDataPath('Cerebro'), 'plugins') export const modulesDirectory = path.join(pluginsPath, 'node_modules') -export const cerebroappModulesDirectory = path.join(pluginsPath, 'node_modules', '@cerebroapp') export const packageJsonPath = path.join(pluginsPath, 'package.json') export const ensureFiles = () => { diff --git a/app/plugins/externalPlugins.js b/app/plugins/externalPlugins.js index e566cabe..4ceba30b 100644 --- a/app/plugins/externalPlugins.js +++ b/app/plugins/externalPlugins.js @@ -2,9 +2,7 @@ import debounce from 'lodash/debounce' import chokidar from 'chokidar' import path from 'path' import { initializePlugin } from 'lib/initializePlugins' -import { - modulesDirectory, cerebroappModulesDirectory, ensureFiles, settings -} from 'lib/plugins' +import { modulesDirectory, ensureFiles, settings } from 'lib/plugins' const requirePlugin = (pluginPath) => { try { @@ -40,15 +38,13 @@ ensureFiles() const plugins = {} -const pluginsWatcher = chokidar.watch([modulesDirectory, cerebroappModulesDirectory], { depth: 0 }) +const pluginsWatcher = chokidar.watch(modulesDirectory, { depth: 1 }) pluginsWatcher.on('unlinkDir', (pluginPath) => { const { base, dir } = path.parse(pluginPath) - const isCerebroappFolder = base === '@cerebroapp' - const notValidModuleFolder = dir !== modulesDirectory && dir !== cerebroappModulesDirectory - if (isCerebroappFolder || notValidModuleFolder) { - return - } + if (base.match(/node_modules/) || base.match(/^@/)) return + if (!dir.match(/node_modules$/) && !dir.match(/@.+$/)) return + const requirePath = window.require.resolve(pluginPath) delete plugins[base] delete window.require.cache[requirePath] @@ -57,11 +53,9 @@ pluginsWatcher.on('unlinkDir', (pluginPath) => { pluginsWatcher.on('addDir', (pluginPath) => { const { base, dir } = path.parse(pluginPath) - const isCerebroappFolder = base === '@cerebroapp' - const notValidModuleFolder = dir !== modulesDirectory && dir !== cerebroappModulesDirectory - if (isCerebroappFolder || notValidModuleFolder) { - return - } + + if (base.match(/node_modules/) || base.match(/^@/)) return + if (!dir.match(/node_modules$/) && !dir.match(/@.+$/)) return setTimeout(() => { console.group(`Load plugin: ${base}`) From 49865a66980c4c3a0f6e97a0793267a068f95a7a Mon Sep 17 00:00:00 2001 From: dubisdev Date: Mon, 29 Aug 2022 14:53:59 +0200 Subject: [PATCH 2/2] fix: scoped plugins in development mode --- .../core/plugins/getDebuggingPlugins.js | 31 +++++++++++++++-- app/plugins/externalPlugins.js | 34 ++++++++++++++----- 2 files changed, 54 insertions(+), 11 deletions(-) diff --git a/app/plugins/core/plugins/getDebuggingPlugins.js b/app/plugins/core/plugins/getDebuggingPlugins.js index 42079f3c..20f1c7d3 100644 --- a/app/plugins/core/plugins/getDebuggingPlugins.js +++ b/app/plugins/core/plugins/getDebuggingPlugins.js @@ -3,6 +3,30 @@ import { modulesDirectory } from 'lib/plugins' import { lstatSync, readdirSync } from 'fs' const isSymlink = (file) => lstatSync(path.join(modulesDirectory, file)).isSymbolicLink() +const isScopeDir = (file) => file.match(/^@/) && lstatSync(path.join(modulesDirectory, file)).isDirectory() + +const getSymlinkedPluginsInFolder = (scope) => { + const files = scope + ? readdirSync(path.join(modulesDirectory, scope)) + : readdirSync(modulesDirectory) + return files.filter((name) => isSymlink(scope ? path.join(scope, name) : name)) +} + +const getNotScopedPluginNames = async () => getSymlinkedPluginsInFolder() + +const getScopedPluginNames = async () => { + // Get all scoped folders + const scopeSubfolders = readdirSync(modulesDirectory).filter(isScopeDir) + + // for each scope, get all plugins + const scopeNames = scopeSubfolders.map((scope) => { + console.log('scopes', scope) + const scopePlugins = getSymlinkedPluginsInFolder(scope) + return scopePlugins.map((plugin) => `${scope}/${plugin}`) + }).flat() // flatten array of arrays + + return scopeNames +} /** * Get list of all plugins that are currently in debugging mode. @@ -11,6 +35,9 @@ const isSymlink = (file) => lstatSync(path.join(modulesDirectory, file)).isSymbo * @return {Promise} */ export default async () => { - const files = readdirSync(modulesDirectory) - return files.filter(isSymlink) + const [notScoppedPluginNames, scopedPluginNames] = await Promise.all([ + getNotScopedPluginNames(), + getScopedPluginNames() + ]) + return [...notScoppedPluginNames, ...scopedPluginNames] } diff --git a/app/plugins/externalPlugins.js b/app/plugins/externalPlugins.js index 4ceba30b..a3a13790 100644 --- a/app/plugins/externalPlugins.js +++ b/app/plugins/externalPlugins.js @@ -36,6 +36,18 @@ const isPluginValid = (plugin) => ( ensureFiles() +/* As we support scoped plugins, using 'base' as plugin name is no longer valid + because it is not unique. '@example/plugin' and '@test/plugin' would both be treated as 'plugin' + So now we must introduce the scope to the plugin name + This function returns the name with the scope if it is present in the path +*/ +const getPluginName = (pluginPath) => { + const { base, dir } = path.parse(pluginPath) + const scope = dir.match(/@.+$/) + if (!scope) return base + return `${scope[0]}/${base}` +} + const plugins = {} const pluginsWatcher = chokidar.watch(modulesDirectory, { depth: 1 }) @@ -45,10 +57,12 @@ pluginsWatcher.on('unlinkDir', (pluginPath) => { if (base.match(/node_modules/) || base.match(/^@/)) return if (!dir.match(/node_modules$/) && !dir.match(/@.+$/)) return + const pluginName = getPluginName(pluginPath) + const requirePath = window.require.resolve(pluginPath) - delete plugins[base] + delete plugins[pluginName] delete window.require.cache[requirePath] - console.log(`[${base}] Plugin removed`) + console.log(`[${pluginName}] Plugin removed`) }) pluginsWatcher.on('addDir', (pluginPath) => { @@ -57,8 +71,10 @@ pluginsWatcher.on('addDir', (pluginPath) => { if (base.match(/node_modules/) || base.match(/^@/)) return if (!dir.match(/node_modules$/) && !dir.match(/@.+$/)) return + const pluginName = getPluginName(pluginPath) + setTimeout(() => { - console.group(`Load plugin: ${base}`) + console.group(`Load plugin: ${pluginName}`) console.log(`Path: ${pluginPath}...`) const plugin = requirePlugin(pluginPath) if (!isPluginValid(plugin)) { @@ -76,15 +92,15 @@ pluginsWatcher.on('addDir', (pluginPath) => { const requirePath = window.require.resolve(pluginPath) const watcher = chokidar.watch(pluginPath, { depth: 0 }) watcher.on('change', debounce(() => { - console.log(`[${base}] Update plugin`) + console.log(`[${pluginName}] Update plugin`) delete window.require.cache[requirePath] - plugins[base] = window.require(pluginPath) - console.log(`[${base}] Plugin updated`) + plugins[pluginName] = window.require(pluginPath) + console.log(`[${pluginName}] Plugin updated`) }, 1000)) - plugins[base] = plugin + plugins[pluginName] = plugin if (!global.isBackground) { - console.log('Initialize async plugin', base) - initializePlugin(base) + console.log('Initialize async plugin', pluginName) + initializePlugin(pluginName) } console.groupEnd() }, 1000)