Matt Travi 9eab1adb9d
feat(esm): convert to esm (#2569)
for #2543

BREAKING CHANGE: semantic-release is now ESM-only. since it is used through its own executable, the impact on consuming projects should be minimal

BREAKING CHANGE: references to plugin files in configs need to include the file extension because of executing in an ESM context
2022-11-11 09:24:06 -06:00

104 lines
3.3 KiB
JavaScript

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