epic-state
Version:
Reactive state management for frontend libraries.
63 lines (51 loc) • 2.02 kB
text/typescript
import { type CallPluginOptions, type Plugin, PluginAction, type PluginActions, type ProxyObject } from './types'
const globalPlugins: PluginActions[] = []
export function initializePlugins(state: ProxyObject, plugin: Plugin | Plugin[]) {
if (!plugin) {
return [] // Can also add plugins later using state.addPlugin(plugin)
}
const plugins = Array.isArray(plugin) ? plugin : [plugin]
return plugins.map((item) => item('initialize', state))
}
// NOTE accessing values in here can also lead to recursive calls.
export function callPlugins({ type, target, initial = false, ...options }: CallPluginOptions) {
// Current plugin.
if (target._plugin) {
for (const item of target._plugin) {
const plugin = item[type]
if (plugin && (item.all || options.leaf || type === PluginAction.Delete)) {
// @ts-ignore Apply can also be used on arrow functions to override the this.
plugin.call(item, options)
}
}
}
// TODO make plugin inheritance configurable.
// Recursively invoke plugins found on parents (which are inherited).
if (target.parent) {
callPlugins({ type, target: target.parent, ...options })
}
if (!initial) {
return
}
// Global plugins.
for (const item of globalPlugins) {
const plugin = item[type]
if (plugin && (item.all || options.leaf || type === PluginAction.Delete)) {
// @ts-ignore
plugin.call(item, options)
}
}
}
// Register global plugin.
export function plugin(plugin: Plugin | PluginActions) {
const initializedPlugin = typeof plugin === 'function' ? plugin('initialize') : plugin
globalPlugins.push(initializedPlugin)
return function removePlugin() {
const remainingGlobalPlugins = globalPlugins.filter((currentPlugin) => initializedPlugin !== currentPlugin)
globalPlugins.splice(0, globalPlugins.length, ...remainingGlobalPlugins)
}
}
export function removeAllPlugins() {
globalPlugins.length = 0
}
// TODO destroy an existing state object and it's associated plugins.