Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: allow plugins to have scope #578

Merged
merged 3 commits into from
Aug 30, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion app/lib/plugins/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 = () => {
Expand Down
31 changes: 29 additions & 2 deletions app/plugins/core/plugins/getDebuggingPlugins.js
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -11,6 +35,9 @@ const isSymlink = (file) => lstatSync(path.join(modulesDirectory, file)).isSymbo
* @return {Promise<String[]>}
*/
export default async () => {
const files = readdirSync(modulesDirectory)
return files.filter(isSymlink)
const [notScoppedPluginNames, scopedPluginNames] = await Promise.all([
getNotScopedPluginNames(),
getScopedPluginNames()
])
return [...notScoppedPluginNames, ...scopedPluginNames]
}
56 changes: 33 additions & 23 deletions app/plugins/externalPlugins.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -38,33 +36,45 @@ 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, 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 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) => {
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 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)) {
Expand All @@ -82,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)
Expand Down