diff --git a/packages/saber/lib/index.js b/packages/saber/lib/index.js index 398c52509..a65a482d7 100644 --- a/packages/saber/lib/index.js +++ b/packages/saber/lib/index.js @@ -168,7 +168,7 @@ class Saber { } for (const plugin of userPlugins) { - this.applyPlugin(plugin, plugin.options, plugin.__path) + this.applyPlugin(plugin, plugin.options, plugin.location) } await this.hooks.afterPlugins.promise() @@ -214,32 +214,46 @@ class Saber { getUserPlugins() { // Plugins that are specified in user config, a.k.a. saber-config.js etc - let plugins = + const plugins = this.configDir && this.config.plugins ? this.config.plugins.map(p => { if (typeof p === 'string') { p = { resolve: p } } - p.resolve = resolveFrom(this.configDir, p.resolve) - return p + const location = resolveFrom(this.configDir, p.resolve) + + const plugin = require(location) + plugin.location = location + plugin.options = p.option + + return plugin }) : [] - plugins = plugins.map(({ resolve, options }) => { - const plugin = require(resolve) - plugin.__path = resolve - plugin.options = options - if (plugin.filterPlugins) { - this.hooks.filterPlugins.tap(plugin.name, plugins => - plugin.filterPlugins(plugins, options) - ) + const applyFilterPlugins = plugins => { + const handlers = new Set() + + for (const plugin of plugins) { + const { filterPlugins, options } = plugin + if (filterPlugins) { + delete plugin.filterPlugins + handlers.add(plugins => filterPlugins(plugins, options)) + } } - return plugin - }) + if (handlers.size > 0) { + for (const handler of handlers) { + plugins = handler(plugins) + } + + return applyFilterPlugins(plugins) + } + + return plugins + } - return this.hooks.filterPlugins.call(plugins) + return applyFilterPlugins(this.hooks.filterPlugins.call(plugins)) } resolveCache(...args) { diff --git a/packages/saber/lib/plugins/extend-node-api.js b/packages/saber/lib/plugins/extend-node-api.js index c67f736db..cdd087045 100644 --- a/packages/saber/lib/plugins/extend-node-api.js +++ b/packages/saber/lib/plugins/extend-node-api.js @@ -22,40 +22,39 @@ exports.apply = api => { updateNodeApi() const getHookHandler = hookName => nodeApi[hookName] || __noopHandler__ - - api.hooks.beforePlugins.tapPromise(nodeApiId, () => { - const hookHandler = getHookHandler('beforePlugins') - if (hookHandler.name !== '__noopHandler__') { - log.verbose(() => `beforePlugins ${colors.dim(`(${nodeApiId})`)}`) + const addHook = hookName => { + const hook = api.hooks[hookName] + if (hook) { + const tapType = hook.call ? 'tap' : 'tapPromise' + hook[tapType](nodeApiId, (...args) => { + const hookHandler = getHookHandler(hookName) + if (hookHandler.name !== '__noopHandler__') { + log.verbose(() => `${hookName} ${colors.dim(`(${nodeApiId})`)}`) + } + + if (tapType === 'tap') { + return hookHandler.call(api, ...args) + } + + return Promise.resolve(hookHandler.call(api, ...args)) + }) } + } - return Promise.resolve(hookHandler.call(api)) - }) + // Hooks that should be added before `afterPlugins` hook + const preHooks = ['beforePlugins', 'filterPlugins'] + + for (const preHook of preHooks) { + addHook(preHook) + } api.hooks.afterPlugins.tap(nodeApiId, () => { for (const hookName of Object.keys(api.hooks)) { - if (hookName === 'beforePlugins') { + if (preHooks.includes(hookName)) { continue } - const hook = api.hooks[hookName] - if (hook) { - const tapType = hook.call ? 'tap' : 'tapPromise' - hook[tapType](nodeApiId, (...args) => { - const hookHandler = getHookHandler(hookName) - const result = hookHandler.call(api, ...args) - - if (hookHandler.name !== '__noopHandler__') { - log.verbose(() => `${hookName} ${colors.dim(`(${nodeApiId})`)}`) - } - - if (tapType === 'tapPromise') { - return Promise.resolve(result) - } - - return result - }) - } + addHook(hookName) } }) diff --git a/website/pages/docs/plugin-api.md b/website/pages/docs/plugin-api.md index d272f6620..08d6247c8 100644 --- a/website/pages/docs/plugin-api.md +++ b/website/pages/docs/plugin-api.md @@ -23,7 +23,22 @@ A function to invoke. ### filterPlugins -- Type: `(plugins: Plugins[], options: any) => Plugins[]` +- Type: `FilterPlugins` - Required: `false` Filter the plugins, you can use it to add or remove plugins. + +```ts +type FilterPlugins = (plugins: Plugin[], options: any) => Plugins[] + +interface Plugin { + /* Plugin name */ + name: string + apply: (api: SaberInstance, options?: any) => void + filterPlugins: FilterPlugins + /* Plugin options */ + options?: any + /* The path to the plugin, only used in logs */ + location?: string +} +``` diff --git a/website/pages/docs/saber-instance.md b/website/pages/docs/saber-instance.md index 1f863195d..2d9caa6b4 100644 --- a/website/pages/docs/saber-instance.md +++ b/website/pages/docs/saber-instance.md @@ -99,18 +99,27 @@ Depending on the hook type, `tapAsync` and `tapPromise` may also be available. H Called to filter plugins. +This hook is __only__ available `saber-node.js`. + ```ts interface Plugin { + /* Plugin name */ name: string apply: (api: SaberInstance, options?: any) => void + /* Plugin options */ options?: any + /* The path to the plugin, only used in logs */ + location?: string } ``` + ### `beforePlugins` - Hook Type: `AsyncSeriesHook` +This hook is __only__ available `saber-node.js`. + Called before loading user plugins. ### `afterPlugins`