import {castArray, identity, isNil, isPlainObject, isString, omit} from 'lodash-es'; import AggregateError from 'aggregate-error'; import getError from '../get-error.js'; import PLUGINS_DEFINITIONS from '../definitions/plugins.js'; import {loadPlugin, parseConfig, validatePlugin, validateStep} from './utils.js'; import pipeline from './pipeline.js'; import normalize from './normalize.js'; export default async (context, pluginsPath) => { let {options, logger} = context; const errors = []; const plugins = options.plugins ? await castArray(options.plugins).reduce(async (eventualPluginsList, plugin) => { const pluginsList = await eventualPluginsList; if (validatePlugin(plugin)) { const [name, config] = parseConfig(plugin); plugin = isString(name) ? await loadPlugin(context, name, pluginsPath) : name; if (isPlainObject(plugin)) { Object.entries(plugin).forEach(([type, func]) => { if (PLUGINS_DEFINITIONS[type]) { Reflect.defineProperty(func, 'pluginName', { value: isPlainObject(name) ? 'Inline plugin' : name, writable: false, enumerable: true, }); pluginsList[type] = [...(pluginsList[type] || []), [func, config]]; } }); } else { errors.push(getError('EPLUGINSCONF', {plugin})); } } else { errors.push(getError('EPLUGINSCONF', {plugin})); } return pluginsList; }, {}) : []; if (errors.length > 0) { throw new AggregateError(errors); } options = {...plugins, ...options}; const pluginsConfig = await Object.entries(PLUGINS_DEFINITIONS).reduce( async ( eventualPluginsConfigAccumulator, [type, {required, default: def, pipelineConfig, postprocess = identity, preprocess = identity}] ) => { let pluginOptions; const pluginsConfigAccumulator = await eventualPluginsConfigAccumulator; if (isNil(options[type]) && def) { pluginOptions = def; } else { // If an object is passed and the path is missing, merge it with step options if (isPlainObject(options[type]) && !options[type].path) { options[type] = castArray(plugins[type]).map((plugin) => plugin ? [plugin[0], Object.assign(plugin[1], options[type])] : plugin ); } if (!validateStep({required}, options[type])) { errors.push(getError('EPLUGINCONF', {type, required, pluginConf: options[type]})); return pluginsConfigAccumulator; } pluginOptions = options[type]; } const steps = await Promise.all( castArray(pluginOptions).map(async (pluginOpt) => normalize( {...context, options: omit(options, Object.keys(PLUGINS_DEFINITIONS), 'plugins')}, type, pluginOpt, pluginsPath ) ) ); pluginsConfigAccumulator[type] = async (input) => postprocess( await pipeline( steps, pipelineConfig && pipelineConfig(pluginsConfigAccumulator, logger) )(await preprocess(input)), input ); return pluginsConfigAccumulator; }, plugins ); if (errors.length > 0) { throw new AggregateError(errors); } return pluginsConfig; }