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; };