"use strict"; function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } } var isPlainObject = require("lodash.isplainobject"); /** * Options Manager * * Input Options: Object * Output: Array of plugins enabled with their options * * Handles multiple types of input option keys * * 1. boolean and object values * { mangle: true } // should enable mangler * { mangle: { blacklist: ["foo"] } } // should enabled mangler * // and pass obj to mangle plugin * * 2. group * { unsafe: true } // should enable all plugins under unsafe * { unsafe: { flip: false } } // should disable flip-comparisons plugin * // and other plugins should take their defaults * { unsafe: { simplify: {multipass: true}}} // should pass obj to simplify * // other plugins take defaults * * 3. same option passed on to multiple plugins * { keepFnames: false } // should be passed on to mangle & dce * // without disturbing their own options */ module.exports = { option, proxy, group, generate, resolveOptions, generateResult }; /** * Generate the plugin list from option tree and inputOpts */ function generate(optionTree, inputOpts) { return generateResult(resolveOptions(optionTree, inputOpts)); } /** * Generate plugin list from the resolvedOptionTree * where resolvedOptionTree = for every node, node.resolved = true; */ function generateResult(resolvedOpts) { var options = resolvedOpts.children; var result = []; for (var i = 0; i < options.length; i++) { var _option = options[i]; switch (_option.type) { case "option": if (_option.resolvedValue) { result.push(_option.resolvedValue); } break; case "group": result.push.apply(result, _toConsumableArray(generateResult(_option))); break; } } return result; } /** * Traverses input @param{optionTree} and adds resolvedValue * calculated from @param{inputOpts} for each Node in the tree */ function resolveOptions(optionTree) { var inputOpts = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; var options = optionTree.children; // a queue to resolve proxies at the end after all options groups are resolved var proxiesToResolve = []; for (var i = 0; i < options.length; i++) { var _option2 = options[i]; switch (_option2.type) { case "option": resolveTypeOption(_option2, inputOpts); break; case "group": resolveTypeGroup(_option2, inputOpts); break; case "proxy": if (!hop(inputOpts, _option2.name)) { break; } proxiesToResolve.push(_option2); break; default: throw new TypeError("Option type not supported - " + _option2.type); } } // resolve proxies for (var _i = 0; _i < proxiesToResolve.length; _i++) { var _proxy = proxiesToResolve[_i]; for (var j = 0; j < _proxy.to.length; j++) { var _option3 = _proxy.to[j]; switch (_option3.type) { case "option": resolveTypeProxyToOption(_proxy, _option3, inputOpts); break; case "group": case "proxy": throw new Error(`proxy option cannot proxy to group/proxy. ${_proxy.name} proxied to ${_option3.name}`); default: throw new Error("Unsupported option type ${option.name}"); } } } // return the same tree after modifications return optionTree; } /** * Resolve the type - simple option using the @param{inputOpts} */ function resolveTypeOption(option, inputOpts) { option.resolved = true; // option does NOT exist in inputOpts if (!hop(inputOpts, option.name)) { // default value option.resolvedValue = option.defaultValue ? option.resolvingValue : null; return; } // Object // { mangle: { blacklist: ["foo", "bar"] } } if (isPlainObject(inputOpts[option.name])) { option.resolvedValue = [option.resolvingValue, inputOpts[option.name]]; return; } // any other truthy value, just enables the plugin // { mangle: true } if (inputOpts[option.name]) { option.resolvedValue = option.resolvingValue; return; } // disabled option.resolvedValue = null; } /** * Resolve the group using @param{inputOpts} */ function resolveTypeGroup(option, inputOpts) { option.resolved = true; // option does NOT exist in inputOpts if (!hop(inputOpts, option.name)) { var _newInputOpts = option.children.filter(function (opt) { return opt.type !== "proxy"; }).reduce(function (acc, cur) { var value = void 0; switch (option.defaultValue) { case "all": value = true;break; case "some": value = cur.defaultValue;break; case "none": value = false;break; default: throw new Error(`Unsupported defaultValue - ${option.defaultValue} for option ${option.name}`); } return Object.assign({}, acc, { [cur.name]: value }); }, {}); // recurse resolveOptions(option, _newInputOpts); return; } // has individual options for items in group // { unsafe: { flipComparisons: true } } if (isPlainObject(inputOpts[option.name])) { resolveOptions(option, inputOpts[option.name]); return; } // else // { unsafe: } var newInputOpts = option.children.filter(function (opt) { return opt.type !== "proxy"; }).reduce(function (acc, cur) { return Object.assign({}, acc, { // if the input is truthy, enable all, else disable all [cur.name]: !!inputOpts[option.name] }); }, {}); resolveOptions(option, newInputOpts); } /** * Resolve proxies and update the already resolved Options */ function resolveTypeProxyToOption(proxy, option, inputOpts) { if (!option.resolved) { throw new Error("Proxies cannot be applied before the original option is resolved"); } // option is disabled if (!option.resolvedValue) { return; } // option doesn't contain any option on its own if (option.resolvedValue === option.resolvingValue) { option.resolvedValue = [option.resolvedValue, { [proxy.name]: inputOpts[proxy.name] }]; } // option already has its own set of options to be passed to plugins else if (Array.isArray(option.resolvedValue) && option.resolvedValue.length === 2) { // proxies should not override if (!hop(option.resolvedValue[1], proxy.name)) { option.resolvedValue = [option.resolvingValue, Object.assign({}, option.resolvedValue[1], { [proxy.name]: inputOpts[proxy.name] })]; } } // plugin is invalid else { throw new Error(`Invalid resolved value for option ${option.name}`); } } // create an option of type simple option function option(name, resolvingValue) { var defaultValue = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true; assertName(name); if (!resolvingValue) { // as plugins are truthy values throw new Error("Only truthy resolving values are supported"); } return { type: "option", name, resolvingValue, defaultValue }; } // create an option of type proxy function proxy(name, to) { assertName(name); assertArray(name, "to", to); return { type: "proxy", name, to }; } // create an option of type - group of options function group(name, children) { var defaultValue = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : "some"; assertName(name); assertArray(name, "children", children); return { type: "group", name, children: children.filter(function (x) { return !!x; }), defaultValue }; } function hop(o, key) { return Object.hasOwnProperty.call(o, key); } function assertArray(name, prop, arr) { if (!Array.isArray(arr)) { throw new Error(`Expected ${prop} to be an array in option ${name}`); } } function assertName(name) { if (!name) { throw new Error("Invalid option name " + name); } }