feat: add new plugins option

This commit is contained in:
Pierre Vanduynslager
2018-10-08 13:24:51 -04:00
parent 9930dac69e
commit 5ba5010c80
18 changed files with 717 additions and 213 deletions
+55 -18
View File
@@ -1,52 +1,89 @@
const {identity, isPlainObject, omit, castArray, isUndefined} = require('lodash');
const {identity, isPlainObject, omit, castArray, isNil, isString} = require('lodash');
const AggregateError = require('aggregate-error');
const getError = require('../get-error');
const PLUGINS_DEFINITIONS = require('../definitions/plugins');
const {validateConfig} = require('./utils');
const {validatePlugin, validateStep, loadPlugin, parseConfig} = require('./utils');
const pipeline = require('./pipeline');
const normalize = require('./normalize');
module.exports = (context, pluginsPath) => {
const {options, logger} = context;
let {options, logger} = context;
const errors = [];
const plugins = Object.entries(PLUGINS_DEFINITIONS).reduce(
const plugins = options.plugins
? castArray(options.plugins).reduce((plugins, plugin) => {
if (validatePlugin(plugin)) {
const [name, config] = parseConfig(plugin);
plugin = isString(name) ? loadPlugin(context, name, pluginsPath) : name;
if (isPlainObject(plugin)) {
Object.entries(plugin).forEach(([type, func]) => {
if (PLUGINS_DEFINITIONS[type]) {
plugins[type] = [...(plugins[type] || []), [func, config]];
}
});
} else {
errors.push(getError('EPLUGINSCONF', {plugin}));
}
} else {
errors.push(getError('EPLUGINSCONF', {plugin}));
}
return plugins;
}, {})
: [];
if (errors.length > 0) {
throw new AggregateError(errors);
}
options = {...plugins, ...options};
const pluginsConf = Object.entries(PLUGINS_DEFINITIONS).reduce(
(
plugins,
pluginsConf,
[type, {multiple, required, default: def, pipelineConfig, postprocess = identity, preprocess = identity}]
) => {
let pluginOpts;
if (isUndefined(options[type])) {
if (isNil(options[type]) && def) {
pluginOpts = def;
} else {
const defaultPaths = castArray(def);
// If an object is passed and the path is missing, set the default one for single plugins
if (isPlainObject(options[type]) && !options[type].path && defaultPaths.length === 1) {
[options[type].path] = defaultPaths;
// 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 (!validateConfig({multiple, required}, options[type])) {
if (!validateStep({multiple, required}, options[type])) {
errors.push(getError('EPLUGINCONF', {type, multiple, required, pluginConf: options[type]}));
return plugins;
return pluginsConf;
}
pluginOpts = options[type];
}
const steps = castArray(pluginOpts).map(pluginOpt =>
normalize({...context, options: omit(options, Object.keys(PLUGINS_DEFINITIONS))}, type, pluginOpt, pluginsPath)
normalize(
{...context, options: omit(options, Object.keys(PLUGINS_DEFINITIONS), 'plugins')},
type,
pluginOpt,
pluginsPath
)
);
plugins[type] = async input =>
pluginsConf[type] = async input =>
postprocess(
await pipeline(steps, pipelineConfig && pipelineConfig(plugins, logger))(await preprocess(input)),
await pipeline(steps, pipelineConfig && pipelineConfig(pluginsConf, logger))(await preprocess(input)),
input
);
return plugins;
return pluginsConf;
},
{}
plugins
);
if (errors.length > 0) {
throw new AggregateError(errors);
}
return plugins;
return pluginsConf;
};
+6 -10
View File
@@ -1,22 +1,18 @@
const {dirname} = require('path');
const {isString, isPlainObject, isFunction, noop, cloneDeep, omit} = require('lodash');
const resolveFrom = require('resolve-from');
const {isPlainObject, isFunction, noop, cloneDeep, omit} = require('lodash');
const getError = require('../get-error');
const {extractErrors} = require('../utils');
const PLUGINS_DEFINITIONS = require('../definitions/plugins');
const {loadPlugin, parseConfig} = require('./utils');
module.exports = ({cwd, stdout, stderr, options, logger}, type, pluginOpt, pluginsPath) => {
module.exports = (context, type, pluginOpt, pluginsPath) => {
const {stdout, stderr, options, logger} = context;
if (!pluginOpt) {
return noop;
}
const {path, ...config} = isString(pluginOpt) || isFunction(pluginOpt) ? {path: pluginOpt} : pluginOpt;
const [path, config] = parseConfig(pluginOpt);
const pluginName = isFunction(path) ? `[Function: ${path.name}]` : path;
const basePath = pluginsPath[path]
? dirname(resolveFrom.silent(__dirname, pluginsPath[path]) || resolveFrom(cwd, pluginsPath[path]))
: __dirname;
const plugin = isFunction(path) ? path : require(resolveFrom.silent(basePath, path) || resolveFrom(cwd, path));
const plugin = loadPlugin(context, path, pluginsPath);
let func;
if (isFunction(plugin)) {
+59 -9
View File
@@ -1,18 +1,68 @@
const {isString, isFunction, castArray} = require('lodash');
const {dirname} = require('path');
const {isString, isFunction, castArray, isArray, isPlainObject, isNil} = require('lodash');
const resolveFrom = require('resolve-from');
const validateSingleConfig = conf => {
const validateStepArrayDefinition = conf =>
isArray(conf) &&
(conf.length === 1 || conf.length === 2) &&
(isString(conf[0]) || isFunction(conf[0])) &&
(isNil(conf[1]) || isPlainObject(conf[1]));
const validateSingleStep = conf => {
if (validateStepArrayDefinition(conf)) {
return true;
}
conf = castArray(conf);
return conf.length === 1 && (isString(conf[0]) || isString(conf[0].path) || isFunction(conf[0]));
if (conf.length !== 1) {
return false;
}
const [path, config] = parseConfig(conf[0]);
return (isString(path) || isFunction(path)) && isPlainObject(config);
};
const validateMultipleConfig = conf => castArray(conf).every(conf => validateSingleConfig(conf));
const validateMultipleStep = conf => {
return conf.every(conf => validateSingleStep(conf));
};
const validateConfig = ({multiple, required}, conf) => {
function validatePlugin(conf) {
return (
isString(conf) ||
(isArray(conf) &&
(conf.length === 1 || conf.length === 2) &&
(isString(conf[0]) || isPlainObject(conf[0])) &&
(isNil(conf[1]) || isPlainObject(conf[1]))) ||
(isPlainObject(conf) && (isNil(conf.path) || isString(conf.path) || isPlainObject(conf.path)))
);
}
function validateStep({multiple, required}, conf) {
conf = castArray(conf).filter(Boolean);
if (required) {
return Boolean(conf) && conf.length >= 1 && (multiple ? validateMultipleConfig : validateSingleConfig)(conf);
return conf.length >= 1 && (multiple ? validateMultipleStep : validateSingleStep)(conf);
}
return conf.length === 0 || (multiple ? validateMultipleConfig : validateSingleConfig)(conf);
};
return conf.length === 0 || (multiple ? validateMultipleStep : validateSingleStep)(conf);
}
module.exports = {validateConfig};
function loadPlugin({cwd}, path, pluginsPath) {
const basePath = pluginsPath[path]
? dirname(resolveFrom.silent(__dirname, pluginsPath[path]) || resolveFrom(cwd, pluginsPath[path]))
: __dirname;
return isFunction(path) ? path : require(resolveFrom.silent(basePath, path) || resolveFrom(cwd, path));
}
function parseConfig(plugin) {
let path;
let config;
if (isArray(plugin)) {
[path, config] = plugin;
} else if (isPlainObject(plugin) && !isNil(plugin.path)) {
({path, ...config} = plugin);
} else {
path = plugin;
}
return [path, config || {}];
}
module.exports = {validatePlugin, validateStep, loadPlugin, parseConfig};