Repository: babel/babili
Pull request #508 (2017-04-19 16:29:12)

Flatten options
+ Removes Option Manager
+ Flattens options - having a tree of options is complex for the user as well as handling it
+ Now every plugin gets its own option name and it can be toggled or passed its own options

hzoo: Hey @boopathi! It looks like one or more of your builds have failed. I've copied the relevant info below to save you some time.

* [Node v4 - linux](https://travis-ci.org/babel/babili/jobs/223626970)
codecov[bot]: # [Codecov](https://codecov.io/gh/babel/babili/pull/508?src=pr&el=h1) Report
> Merging [#508](https://codecov.io/gh/babel/babili/pull/508?src=pr&el=desc) into [master](https://codecov.io/gh/babel/babili/commit/021eead3820dfc70bd4f2497715d283a777a4c2b?src=pr&el=desc) will **increase** coverage by `0.41%`.
> The diff coverage is `90.47%`.

[![Impacted file tree graph](https://codecov.io/gh/babel/babili/pull/508/graphs/tree.svg?src=pr&token=yjfidKkIaL&height=150&width=650)](https://codecov.io/gh/babel/babili/pull/508?src=pr&el=tree)

```diff
@@            Coverage Diff             @@
##           master     #508      +/-   ##
==========================================
+ Coverage   82.64%   83.06%   +0.41%     
==========================================
  Files          35       34       -1     
  Lines        2633     2521     -112     
  Branches      924      896      -28     
==========================================
- Hits         2176     2094      -82     
+ Misses        279      255      -24     
+ Partials      178      172       -6
```


| [Impacted Files](https://codecov.io/gh/babel/babili/pull/508?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [packages/babel-preset-babili/src/index.js](https://codecov.io/gh/babel/babili/pull/508?src=pr&el=tree#diff-cGFja2FnZXMvYmFiZWwtcHJlc2V0LWJhYmlsaS9zcmMvaW5kZXguanM=) | `92.59% <90.47%> (+15.31%)` | :arrow_up: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/babel/babili/pull/508?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/babel/babili/pull/508?src=pr&el=footer). Last update [021eead...08cb10d](https://codecov.io/gh/babel/babili/pull/508?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).

hzoo: Hey @boopathi! It looks like one or more of your builds have failed. I've copied the relevant info below to save you some time.

* [Node v4 - linux](https://travis-ci.org/babel/babili/jobs/223631108)
vigneshshanmugam: Looks much more clear

Patch:
diff --git a/packages/babel-preset-babili/README.md b/packages/babel-preset-babili/README.md
index cd9fdd1..76d0c72 100644
--- a/packages/babel-preset-babili/README.md
+++ b/packages/babel-preset-babili/README.md
@@ -56,32 +56,50 @@ require("babel-core").transform("code", {
 
 ## Options
 
-All options are **enabled** by default **except** the ones with an explicit mention - `(Default: false)`
-
-Three types of options:
-
-### 1-1 mapping with plugin
-
-+ `false` to disable the plugin
-+ `true` to enable the plugin with default plugin specific options
-+ `{ ...pluginOpts }` to enable the plugin with custom plugin options
-
-The following options have 1-1 mapping with a plugin,
-+ `evaluate` - [babel-plugin-minify-constant-folding](../../packages/babel-plugin-minify-constant-folding)
-+ `deadcode` - [babel-plugin-minify-dead-code-elimination](../../packages/babel-plugin-minify-dead-code-elimination)
-+ `infinity` - [babel-plugin-minify-infinity](../../packages/babel-plugin-minify-infinity)
-+ `mangle` - [babel-plugin-minify-mangle-names](../../packages/babel-plugin-minify-mangle-names)
-+ `numericLiterals` - [babel-plugin-minify-numeric-literals](../../packages/babel-plugin-minify-numeric-literals)
-+ `replace` - [babel-plugin-minify-replace](../../packages/babel-plugin-minify-replace)
-+ `simplify` - [babel-plugin-minify-simplify](../../packages/babel-plugin-minify-simplify)
-+ `builtIns` - [babel-plugin-minify-builtins](../../packages/babel-plugin-minify-builtins)
-+ `mergeVars` - [babel-plugin-transform-merge-sibling-variables](../../packages/babel-plugin-transform-merge-sibling-variables)
-+ `booleans` - [babel-plugin-transform-minify-booleans](../../packages/babel-plugin-transform-minify-booleans)
-+ `regexpConstructors` - [babel-plugin-transform-regexp-constructors](../../packages/babel-plugin-transform-regexp-constructors)
-+ `removeConsole` - `(Default: false)` - [babel-plugin-transform-remove-console](../../packages/babel-plugin-transform-remove-console)
-+ `removeDebugger` - `(Default: false)` - [babel-plugin-transform-remove-debugger](../../packages/babel-plugin-transform-remove-debugger)
-+ `removeUndefined` - [babel-plugin-transform-remove-undefined](../../packages/babel-plugin-transform-remove-undefined)
-+ `undefinedToVoid` - [babel-plugin-transform-undefined-to-void](../../packages/babel-plugin-transform-undefined-to-void)
+Two types of options:
+
+1. 1-1 mapping with plugin
+2. The same option passed to multiple plugins
+
+#### 1-1 mapping with plugin
+
++ `false` - disable plugin
++ `true` - enable plugin
++ `{ ...pluginOpts }` - enable plugin and pass pluginOpts to plugin
+
+OptionName          | Plugin                                                         | DefaultValue
+----------          | ------                                                         | ------------
+booleans            | [transform-minify-booleans][booleans]                          | true
+builtIns            | [minify-builtins][builtIns]                                    | true
+consecutiveAdds     | [transform-inline-consecutive-adds][consecutiveAdds]           | true
+deadcode            | [minify-dead-code-elimination][deadcode]                       | true
+evaluate            | [minify-constant-folding][evaluate]                            | true
+flipComparisons     | [minify-flip-comparisons][flipComparisons]                     | true
+guards              | [minify-guarded-expressions][guards]                           | true
+infinity            | [minify-infinity][infinity]                                    | true
+mangle              | [minify-mangle-names][mangle]                                  | true
+memberExpressions   | [transform-member-expression-literals][memberExpressions]      | true
+mergeVars           | [transform-merge-sibling-variables][mergeVars]                 | true
+numericLiterals     | [minify-numeric-literals][numericLiterals]                     | true
+propertyLiterals    | [transform-property-literals][propertyLiterals]                | true
+regexpConstructors  | [transform-regexp-constructors][regexpConstructors]            | true
+removeConsole       | [transform-remove-console][removeConsole]                      | false
+removeDebugger      | [transform-remove-debugger][removeDebugger]                    | false
+removeUndefined     | [transform-remove-undefined][removeUndefined]                  | true
+replace             | [minify-replace][replace]                                      | true
+simplify            | [minify-simplify][simplify]                                    | true
+simplifyComparisons | [transform-simplify-comparison-operators][simplifyComparisons] | true
+typeConstructors    | [minify-type-constructors][typeConstructors]                   | true
+undefinedToVoid     | [transform-undefined-to-void][undefinedToVoid]                 | true
+
+#### The same option passed to multiple plugins
+
++ When multiple plugins require the same option, it's easier to declare it in one place. These options are passed on to two or more plugins.
+
+OptionName          | Plugins
+----------          | -------
+keepFnName          | Passed to [mangle][mangle] & [deadcode][deadcode]
+keepClassName       | Passed to [mangle][mangle] & [deadcode][deadcode]
 
 **Examples**
 
@@ -98,77 +116,19 @@ The following options have 1-1 mapping with a plugin,
 {
   "presets": [["babili", {
     "mangle": {
-      "blacklist": {
-        "ParserError": true,
-        "NetworkError": false
-      }
+      "blacklist": ["ParserError", "NetworkError"]
     }
   }]]
 }
 ```
 
-### Option groups
-
-+ `false` to disable the entire group
-+ `true` to enable every plugin in the group
-+ `{ pluginKey: <1-1 mapping> }` - enable/disable a particular plugin in a group (or) pass options to that plugin
-
-The following are groups of plugins -
-
-+ `unsafe`
-  + `flipComparisons` - [babel-plugin-minify-flip-comparisons](../../packages/babel-plugin-minify-flip-comparisons)
-  + `simplifyComparisons` - [babel-plugin-transform-simplify-comparison-operators](../../babel-plugin-transform-simplify-comparison-operators)
-  + `guards` - [babel-plugin-minify-guarded-expressions](../../packages/babel-plugin-minify-guarded-expressions)
-  + `typeConstructors` - [babel-plugin-minify-type-constructors](../../packages/babel-plugin-minify-type-constructors)
-+ `properties`
-  + `memberExpressions` - [babel-plugin-transform-member-expression-literals](../../packages/babel-plugin-transform-member-expression-literals)
-  + `propertyLiterals` - [babel-plugin-transform-property-literals](../../packages/babel-plugin-transform-property-literals)
-
-**Examples**
-
-Disables all unsafe plugins:
-
-```json
-{
-  "presets": [["babili", {
-    "unsafe": false
-  }]]
-}
-```
-
-Disables only minify-guarded-expressions, and enable all other unsafe plugins:
-
-```json
-{
-  "presets": [["babili", {
-    "unsafe": {
-      "guards": false
-    }
-  }]]
-}
-```
-
-### Passing same plugin options to multiple plugins
-
-In babili, multiple plugins require the same set of options and it is easier to mention it in one place instead of two.
-
-+ `keepFnName` - This will be passed to `mangle` and `deadcode` and will NOT be overriden if the same option exists under either mangle or deadcode.
-
-**Examples**
-
 ```json
 {
   "presets": [["babili", {
     "keepFnName": true
   }]]
 }
-```
-
-is the same as,
-
-Plugins applied:
-
-```json
+// is the same as
 {
   "presets": [["babili", {
     "mangle": {
@@ -180,3 +140,26 @@ Plugins applied:
   }]]
 }
 ```
+
+[booleans]: ../../packages/babel-plugin-transform-minify-booleans
+[builtIns]: ../../packages/babel-plugin-minify-builtins
+[consecutiveAdds]: ../../packages/babel-plugin-transform-inline-consecutive-adds
+[deadcode]: ../../packages/babel-plugin-minify-dead-code-elimination
+[evaluate]: ../../packages/babel-plugin-minify-constant-folding
+[flipComparisons]: ../../packages/babel-plugin-minify-flip-comparisons
+[guards]: ../../packages/babel-plugin-minify-guarded-expressions
+[infinity]: ../../packages/babel-plugin-minify-infinity
+[mangle]: ../../packages/babel-plugin-minify-mangle-names
+[memberExpressions]: ../../packages/babel-plugin-transform-member-expression-literals
+[mergeVars]: ../../packages/babel-plugin-transform-merge-sibling-variables
+[numericLiterals]: ../../packages/babel-plugin-minify-numeric-literals
+[propertyLiterals]: ../../packages/babel-plugin-transform-property-literals
+[regexpConstructors]: ../../packages/babel-plugin-transform-regexp-constructors
+[removeConsole]: ../../packages/babel-plugin-transform-remove-console
+[removeDebugger]: ../../packages/babel-plugin-transform-remove-debugger
+[removeUndefined]: ../../packages/babel-plugin-transform-remove-undefined
+[replace]: ../../packages/babel-plugin-minify-replace
+[simplify]: ../../packages/babel-plugin-minify-simplify
+[simplifyComparisons]: ../../packages/babel-plugin-transform-simplify-comparison-operators
+[typeConstructors]: ../../packages/babel-plugin-minify-type-constructors
+[undefinedToVoid]: ../../packages/babel-plugin-transform-undefined-to-void
diff --git a/packages/babel-preset-babili/src/index.js b/packages/babel-preset-babili/src/index.js
index 7f8f4bf..0620556 100644
--- a/packages/babel-preset-babili/src/index.js
+++ b/packages/babel-preset-babili/src/index.js
@@ -1,133 +1,98 @@
 const isPlainObject = require("lodash.isplainobject");
-const { group, option, proxy, generate } = require("./options-manager");
 
 // the flat plugin map
 // This is to prevent dynamic requires - require('babel-plugin-' + name);
 // as it suffers during bundling of this code with webpack/browserify
+// sorted by option name
+// prettier-ignore
 const PLUGINS = [
-  ["booleans", require("babel-plugin-transform-minify-booleans"), true],
-  [
-    "consecutiveAdds",
-    require("babel-plugin-transform-inline-consecutive-adds"),
-    true
-  ],
-  ["deadcode", require("babel-plugin-minify-dead-code-elimination"), true],
-  ["evaluate", require("babel-plugin-minify-constant-folding"), true],
-  ["flipComparisons", require("babel-plugin-minify-flip-comparisons"), true],
-  ["guards", require("babel-plugin-minify-guarded-expressions"), true],
-  ["infinity", require("babel-plugin-minify-infinity"), true],
-  ["mangle", require("babel-plugin-minify-mangle-names"), true],
-  [
-    "memberExpressions",
-    require("babel-plugin-transform-member-expression-literals"),
-    true
-  ],
-  [
-    "mergeVars",
-    require("babel-plugin-transform-merge-sibling-variables"),
-    true
-  ],
-  ["numericLiterals", require("babel-plugin-minify-numeric-literals"), true],
-  [
-    "propertyLiterals",
-    require("babel-plugin-transform-property-literals"),
-    true
-  ],
-  [
-    "regexpConstructors",
-    require("babel-plugin-transform-regexp-constructors"),
-    true
-  ],
-  ["removeConsole", require("babel-plugin-transform-remove-console"), false],
-  ["removeDebugger", require("babel-plugin-transform-remove-debugger"), false],
-  ["removeUndefined", require("babel-plugin-transform-remove-undefined"), true],
-  ["replace", require("babel-plugin-minify-replace"), true],
-  ["simplify", require("babel-plugin-minify-simplify"), true],
-  [
-    "simplifyComparisons",
-    require("babel-plugin-transform-simplify-comparison-operators"),
-    true
-  ],
-  ["typeConstructors", require("babel-plugin-minify-type-constructors"), true],
-  [
-    "undefinedToVoid",
-    require("babel-plugin-transform-undefined-to-void"),
-    true
-  ],
-  ["builtIns", require("babel-plugin-minify-builtins"), true]
+  // [optionname,         plugin,                                                          default],
+  ["booleans",            require("babel-plugin-transform-minify-booleans"),               true],
+  ["builtIns",            require("babel-plugin-minify-builtins"),                         true],
+  ["consecutiveAdds",     require("babel-plugin-transform-inline-consecutive-adds"),       true],
+  ["deadcode",            require("babel-plugin-minify-dead-code-elimination"),            true],
+  ["evaluate",            require("babel-plugin-minify-constant-folding"),                 true],
+  ["flipComparisons",     require("babel-plugin-minify-flip-comparisons"),                 true],
+  ["guards",              require("babel-plugin-minify-guarded-expressions"),              true],
+  ["infinity",            require("babel-plugin-minify-infinity"),                         true],
+  ["mangle",              require("babel-plugin-minify-mangle-names"),                     true],
+  ["memberExpressions",   require("babel-plugin-transform-member-expression-literals"),    true],
+  ["mergeVars",           require("babel-plugin-transform-merge-sibling-variables"),       true],
+  ["numericLiterals",     require("babel-plugin-minify-numeric-literals"),                 true],
+  ["propertyLiterals",    require("babel-plugin-transform-property-literals"),             true],
+  ["regexpConstructors",  require("babel-plugin-transform-regexp-constructors"),           true],
+  ["removeConsole",       require("babel-plugin-transform-remove-console"),                false],
+  ["removeDebugger",      require("babel-plugin-transform-remove-debugger"),               false],
+  ["removeUndefined",     require("babel-plugin-transform-remove-undefined"),              true],
+  ["replace",             require("babel-plugin-minify-replace"),                          true],
+  ["simplify",            require("babel-plugin-minify-simplify"),                         true],
+  ["simplifyComparisons", require("babel-plugin-transform-simplify-comparison-operators"), true],
+  ["typeConstructors",    require("babel-plugin-minify-type-constructors"),                true],
+  ["undefinedToVoid",     require("babel-plugin-transform-undefined-to-void"),             true]
 ];
 
+const PROXIES = {
+  keepFnName: ["mangle", "deadcode"],
+  keepClassName: ["mangle", "deadcode"]
+};
+
 module.exports = preset;
 
 function preset(context, _opts = {}) {
   const opts = isPlainObject(_opts) ? _opts : {};
 
-  // to track every plugin is used
-  const usedPlugins = new Set();
-
-  const optionsMap = PLUGINS.map(plugin =>
-    option(plugin[0], plugin[1], plugin[2])
-  ).reduce((acc, cur) => {
-    Object.defineProperty(acc, cur.name, {
-      get() {
-        usedPlugins.add(cur.name);
-        return cur;
-      }
-    });
-    return acc;
-  }, {});
-
-  const optionsTree = group(
-    "options",
-    [
-      optionsMap.evaluate,
-      optionsMap.deadcode,
-
-      group("unsafe", [
-        optionsMap.flipComparisons,
-        optionsMap.simplifyComparisons,
-        optionsMap.guards,
-        optionsMap.typeConstructors
-      ]),
-
-      optionsMap.infinity,
-      optionsMap.mangle,
-      optionsMap.numericLiterals,
-      optionsMap.replace,
-      optionsMap.simplify,
-      optionsMap.builtIns,
-
-      group("properties", [
-        optionsMap.consecutiveAdds,
-        optionsMap.memberExpressions,
-        optionsMap.propertyLiterals
-      ]),
-
-      optionsMap.mergeVars,
-      optionsMap.booleans,
-      optionsMap.undefinedToVoid,
-      optionsMap.regexpConstructors,
-
-      optionsMap.removeConsole,
-      optionsMap.removeDebugger,
-      optionsMap.removeUndefined,
-
-      proxy("keepFnName", [optionsMap.mangle, optionsMap.deadcode]),
+  // validate options
+  const validOptions = [...PLUGINS.map(p => p[0]), ...Object.keys(PROXIES)];
+  for (let name in opts) {
+    if (validOptions.indexOf(name) < 0) {
+      throw new Error(`Invalid option "${name}"`);
+    }
+  }
 
-      proxy("keepClassName", [optionsMap.mangle, optionsMap.deadcode])
-    ],
-    "some"
+  // build a plugins map from the plugin table above
+  const pluginsMap = PLUGINS.reduce(
+    (acc, [name, plugin, defaultValue]) =>
+      Object.assign(acc, {
+        [name]: {
+          plugin,
+          options: null,
+          enabled: defaultValue
+        }
+      }),
+    {}
   );
 
-  // verify all plugins are used
-  if (usedPlugins.size !== PLUGINS.length) {
-    const unusedPlugins = PLUGINS.filter(
-      plugin => !usedPlugins.has(plugin[0])
-    ).map(plugin => plugin[0]);
-    throw new Error("Some imported plugins unused\n" + unusedPlugins);
+  // handle plugins and their options
+  for (const [name] of PLUGINS) {
+    if (isPlainObject(opts[name])) {
+      pluginsMap[name].options = opts[name];
+    } else if (opts[name] !== void 0) {
+      pluginsMap[name].enabled = !!opts[name];
+    }
+  }
+
+  // handle proxies
+  for (let proxyname in PROXIES) {
+    if (opts[proxyname] !== void 0) {
+      for (const to of PROXIES[proxyname]) {
+        if (!pluginsMap[to].options) {
+          pluginsMap[to].options = {};
+        }
+        if (!hop(pluginsMap[to].options, proxyname)) {
+          pluginsMap[to].options[proxyname] = opts[proxyname];
+        }
+      }
+    }
   }
 
-  const plugins = generate(optionsTree, opts);
+  // get the array of plugins
+  const plugins = Object.keys(pluginsMap)
+    .map(name => pluginsMap[name])
+    .filter(plugin => plugin.enabled)
+    .map(
+      plugin =>
+        (plugin.options ? [plugin.plugin, plugin.options] : plugin.plugin)
+    );
 
   return {
     minified: true,
@@ -136,3 +101,7 @@ function preset(context, _opts = {}) {
     passPerPreset: true
   };
 }
+
+function hop(o, key) {
+  return Object.prototype.hasOwnProperty.call(o, key);
+}
diff --git a/packages/babel-preset-babili/src/options-manager.js b/packages/babel-preset-babili/src/options-manager.js
deleted file mode 100644
index ec25274..0000000
--- a/packages/babel-preset-babili/src/options-manager.js
+++ /dev/null
@@ -1,313 +0,0 @@
-const 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) {
-  const options = resolvedOpts.children;
-  const result = [];
-
-  for (let i = 0; i < options.length; i++) {
-    const option = options[i];
-
-    switch (option.type) {
-      case "option":
-        if (option.resolvedValue) {
-          result.push(option.resolvedValue);
-        }
-        break;
-      case "group":
-        result.push(...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, inputOpts = {}) {
-  const options = optionTree.children;
-
-  // a queue to resolve proxies at the end after all options groups are resolved
-  const proxiesToResolve = [];
-
-  for (let i = 0; i < options.length; i++) {
-    const option = options[i];
-    switch (option.type) {
-      case "option":
-        resolveTypeOption(option, inputOpts);
-        break;
-
-      case "group":
-        resolveTypeGroup(option, inputOpts);
-        break;
-
-      case "proxy":
-        if (!hop(inputOpts, option.name)) {
-          break;
-        }
-        proxiesToResolve.push(option);
-        break;
-
-      default:
-        throw new TypeError("Option type not supported - " + option.type);
-    }
-  }
-
-  // resolve proxies
-  for (let i = 0; i < proxiesToResolve.length; i++) {
-    const proxy = proxiesToResolve[i];
-    for (let j = 0; j < proxy.to.length; j++) {
-      const option = proxy.to[j];
-      switch (option.type) {
-        case "option":
-          resolveTypeProxyToOption(proxy, option, inputOpts);
-          break;
-
-        case "group":
-        case "proxy":
-          throw new Error(
-            `proxy option cannot proxy to group/proxy. ${proxy.name} proxied to ${option.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)) {
-    const newInputOpts = option.children
-      .filter(opt => opt.type !== "proxy")
-      .reduce((acc, cur) => {
-        let value;
-        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: <true | false> }
-  const newInputOpts = option.children
-    .filter(opt => opt.type !== "proxy")
-    .reduce(
-      (acc, cur) =>
-        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]
-      }
-    ];
-  } else if (
-    Array.isArray(option.resolvedValue) && option.resolvedValue.length === 2
-  ) {
-    // option already has its own set of options to be passed to plugins
-    // 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]
-        })
-      ];
-    }
-  } else {
-    // plugin is invalid
-    throw new Error(`Invalid resolved value for option ${option.name}`);
-  }
-}
-
-// create an option of type simple option
-function option(name, resolvingValue, defaultValue = 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, defaultValue = "some") {
-  assertName(name);
-  assertArray(name, "children", children);
-  return {
-    type: "group",
-    name,
-    children: children.filter(x => !!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);
-  }
-}


Test patch:
diff --git a/packages/babel-preset-babili/__tests__/__snapshots__/options-tests.js.snap b/packages/babel-preset-babili/__tests__/__snapshots__/options-tests.js.snap
index 3638285..8029ef6 100644
--- a/packages/babel-preset-babili/__tests__/__snapshots__/options-tests.js.snap
+++ b/packages/babel-preset-babili/__tests__/__snapshots__/options-tests.js.snap
@@ -1,62 +1,5 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
-exports[`preset-options should handle groups - remove entire group 1`] = `
-Object {
-  "input": Object {
-    "unsafe": false,
-  },
-  "output": Array [
-    "babel-plugin-minify-constant-folding",
-    "babel-plugin-minify-dead-code-elimination",
-    "babel-plugin-minify-infinity",
-    "babel-plugin-minify-mangle-names",
-    "babel-plugin-minify-numeric-literals",
-    "babel-plugin-minify-replace",
-    "babel-plugin-minify-simplify",
-    "babel-plugin-minify-builtins",
-    "babel-plugin-transform-inline-consecutive-adds",
-    "babel-plugin-transform-member-expression-literals",
-    "babel-plugin-transform-property-literals",
-    "babel-plugin-transform-merge-sibling-variables",
-    "babel-plugin-transform-minify-booleans",
-    "babel-plugin-transform-undefined-to-void",
-    "babel-plugin-transform-regexp-constructors",
-    "babel-plugin-transform-remove-undefined",
-  ],
-}
-`;
-
-exports[`preset-options should handle individual items in a group of options 1`] = `
-Object {
-  "input": Object {
-    "mangle": false,
-    "unsafe": Object {
-      "flipComparisons": false,
-    },
-  },
-  "output": Array [
-    "babel-plugin-minify-constant-folding",
-    "babel-plugin-minify-dead-code-elimination",
-    "babel-plugin-transform-simplify-comparison-operators",
-    "babel-plugin-minify-guarded-expressions",
-    "babel-plugin-minify-type-constructors",
-    "babel-plugin-minify-infinity",
-    "babel-plugin-minify-numeric-literals",
-    "babel-plugin-minify-replace",
-    "babel-plugin-minify-simplify",
-    "babel-plugin-minify-builtins",
-    "babel-plugin-transform-inline-consecutive-adds",
-    "babel-plugin-transform-member-expression-literals",
-    "babel-plugin-transform-property-literals",
-    "babel-plugin-transform-merge-sibling-variables",
-    "babel-plugin-transform-minify-booleans",
-    "babel-plugin-transform-undefined-to-void",
-    "babel-plugin-transform-regexp-constructors",
-    "babel-plugin-transform-remove-undefined",
-  ],
-}
-`;
-
 exports[`preset-options should handle options that are delegated to multiple other options 1`] = `
 Object {
   "input": Object {
@@ -64,7 +7,9 @@ Object {
     "keepFnName": false,
   },
   "output": Array [
-    "babel-plugin-minify-constant-folding",
+    "babel-plugin-transform-minify-booleans",
+    "babel-plugin-minify-builtins",
+    "babel-plugin-transform-inline-consecutive-adds",
     Array [
       "babel-plugin-minify-dead-code-elimination",
       Object {
@@ -72,10 +17,9 @@ Object {
         "keepFnName": false,
       },
     ],
+    "babel-plugin-minify-constant-folding",
     "babel-plugin-minify-flip-comparisons",
-    "babel-plugin-transform-simplify-comparison-operators",
     "babel-plugin-minify-guarded-expressions",
-    "babel-plugin-minify-type-constructors",
     "babel-plugin-minify-infinity",
     Array [
       "babel-plugin-minify-mangle-names",
@@ -84,18 +28,17 @@ Object {
         "keepFnName": false,
       },
     ],
-    "babel-plugin-minify-numeric-literals",
-    "babel-plugin-minify-replace",
-    "babel-plugin-minify-simplify",
-    "babel-plugin-minify-builtins",
-    "babel-plugin-transform-inline-consecutive-adds",
     "babel-plugin-transform-member-expression-literals",
-    "babel-plugin-transform-property-literals",
     "babel-plugin-transform-merge-sibling-variables",
-    "babel-plugin-transform-minify-booleans",
-    "babel-plugin-transform-undefined-to-void",
+    "babel-plugin-minify-numeric-literals",
+    "babel-plugin-transform-property-literals",
     "babel-plugin-transform-regexp-constructors",
     "babel-plugin-transform-remove-undefined",
+    "babel-plugin-minify-replace",
+    "babel-plugin-minify-simplify",
+    "babel-plugin-transform-simplify-comparison-operators",
+    "babel-plugin-minify-type-constructors",
+    "babel-plugin-transform-undefined-to-void",
   ],
 }
 `;
@@ -110,10 +53,14 @@ Object {
         "foo",
         "bar",
       ],
+      "keepClassName": true,
+      "keepFnName": true,
     },
   },
   "output": Array [
-    "babel-plugin-minify-constant-folding",
+    "babel-plugin-transform-minify-booleans",
+    "babel-plugin-minify-builtins",
+    "babel-plugin-transform-inline-consecutive-adds",
     Array [
       "babel-plugin-minify-dead-code-elimination",
       Object {
@@ -121,10 +68,9 @@ Object {
         "keepFnName": true,
       },
     ],
+    "babel-plugin-minify-constant-folding",
     "babel-plugin-minify-flip-comparisons",
-    "babel-plugin-transform-simplify-comparison-operators",
     "babel-plugin-minify-guarded-expressions",
-    "babel-plugin-minify-type-constructors",
     "babel-plugin-minify-infinity",
     Array [
       "babel-plugin-minify-mangle-names",
@@ -137,18 +83,17 @@ Object {
         "keepFnName": true,
       },
     ],
-    "babel-plugin-minify-numeric-literals",
-    "babel-plugin-minify-replace",
-    "babel-plugin-minify-simplify",
-    "babel-plugin-minify-builtins",
-    "babel-plugin-transform-inline-consecutive-adds",
     "babel-plugin-transform-member-expression-literals",
-    "babel-plugin-transform-property-literals",
     "babel-plugin-transform-merge-sibling-variables",
-    "babel-plugin-transform-minify-booleans",
-    "babel-plugin-transform-undefined-to-void",
+    "babel-plugin-minify-numeric-literals",
+    "babel-plugin-transform-property-literals",
     "babel-plugin-transform-regexp-constructors",
     "babel-plugin-transform-remove-undefined",
+    "babel-plugin-minify-replace",
+    "babel-plugin-minify-simplify",
+    "babel-plugin-transform-simplify-comparison-operators",
+    "babel-plugin-minify-type-constructors",
+    "babel-plugin-transform-undefined-to-void",
   ],
 }
 `;
@@ -167,7 +112,9 @@ Object {
     },
   },
   "output": Array [
-    "babel-plugin-minify-constant-folding",
+    "babel-plugin-transform-minify-booleans",
+    "babel-plugin-minify-builtins",
+    "babel-plugin-transform-inline-consecutive-adds",
     Array [
       "babel-plugin-minify-dead-code-elimination",
       Object {
@@ -175,10 +122,9 @@ Object {
         "keepFnName": true,
       },
     ],
+    "babel-plugin-minify-constant-folding",
     "babel-plugin-minify-flip-comparisons",
-    "babel-plugin-transform-simplify-comparison-operators",
     "babel-plugin-minify-guarded-expressions",
-    "babel-plugin-minify-type-constructors",
     "babel-plugin-minify-infinity",
     Array [
       "babel-plugin-minify-mangle-names",
@@ -190,18 +136,17 @@ Object {
         "keepFnName": false,
       },
     ],
-    "babel-plugin-minify-numeric-literals",
-    "babel-plugin-minify-replace",
-    "babel-plugin-minify-simplify",
-    "babel-plugin-minify-builtins",
-    "babel-plugin-transform-inline-consecutive-adds",
     "babel-plugin-transform-member-expression-literals",
-    "babel-plugin-transform-property-literals",
     "babel-plugin-transform-merge-sibling-variables",
-    "babel-plugin-transform-minify-booleans",
-    "babel-plugin-transform-undefined-to-void",
+    "babel-plugin-minify-numeric-literals",
+    "babel-plugin-transform-property-literals",
     "babel-plugin-transform-regexp-constructors",
     "babel-plugin-transform-remove-undefined",
+    "babel-plugin-minify-replace",
+    "babel-plugin-minify-simplify",
+    "babel-plugin-transform-simplify-comparison-operators",
+    "babel-plugin-minify-type-constructors",
+    "babel-plugin-transform-undefined-to-void",
   ],
 }
 `;
@@ -213,24 +158,24 @@ Object {
     "mangle": false,
   },
   "output": Array [
+    "babel-plugin-transform-minify-booleans",
+    "babel-plugin-minify-builtins",
+    "babel-plugin-transform-inline-consecutive-adds",
     "babel-plugin-minify-constant-folding",
     "babel-plugin-minify-flip-comparisons",
-    "babel-plugin-transform-simplify-comparison-operators",
     "babel-plugin-minify-guarded-expressions",
-    "babel-plugin-minify-type-constructors",
     "babel-plugin-minify-infinity",
-    "babel-plugin-minify-numeric-literals",
-    "babel-plugin-minify-replace",
-    "babel-plugin-minify-simplify",
-    "babel-plugin-minify-builtins",
-    "babel-plugin-transform-inline-consecutive-adds",
     "babel-plugin-transform-member-expression-literals",
-    "babel-plugin-transform-property-literals",
     "babel-plugin-transform-merge-sibling-variables",
-    "babel-plugin-transform-minify-booleans",
-    "babel-plugin-transform-undefined-to-void",
+    "babel-plugin-minify-numeric-literals",
+    "babel-plugin-transform-property-literals",
     "babel-plugin-transform-regexp-constructors",
     "babel-plugin-transform-remove-undefined",
+    "babel-plugin-minify-replace",
+    "babel-plugin-minify-simplify",
+    "babel-plugin-transform-simplify-comparison-operators",
+    "babel-plugin-minify-type-constructors",
+    "babel-plugin-transform-undefined-to-void",
   ],
 }
 `;
@@ -246,12 +191,13 @@ Object {
     },
   },
   "output": Array [
-    "babel-plugin-minify-constant-folding",
+    "babel-plugin-transform-minify-booleans",
+    "babel-plugin-minify-builtins",
+    "babel-plugin-transform-inline-consecutive-adds",
     "babel-plugin-minify-dead-code-elimination",
+    "babel-plugin-minify-constant-folding",
     "babel-plugin-minify-flip-comparisons",
-    "babel-plugin-transform-simplify-comparison-operators",
     "babel-plugin-minify-guarded-expressions",
-    "babel-plugin-minify-type-constructors",
     "babel-plugin-minify-infinity",
     Array [
       "babel-plugin-minify-mangle-names",
@@ -262,93 +208,92 @@ Object {
         ],
       },
     ],
-    "babel-plugin-minify-numeric-literals",
-    "babel-plugin-minify-replace",
-    "babel-plugin-minify-simplify",
-    "babel-plugin-minify-builtins",
-    "babel-plugin-transform-inline-consecutive-adds",
     "babel-plugin-transform-member-expression-literals",
-    "babel-plugin-transform-property-literals",
     "babel-plugin-transform-merge-sibling-variables",
-    "babel-plugin-transform-minify-booleans",
-    "babel-plugin-transform-undefined-to-void",
+    "babel-plugin-minify-numeric-literals",
+    "babel-plugin-transform-property-literals",
     "babel-plugin-transform-regexp-constructors",
     "babel-plugin-transform-remove-undefined",
+    "babel-plugin-minify-replace",
+    "babel-plugin-minify-simplify",
+    "babel-plugin-transform-simplify-comparison-operators",
+    "babel-plugin-minify-type-constructors",
+    "babel-plugin-transform-undefined-to-void",
   ],
 }
 `;
 
 exports[`preset-options should return defaults with no options 1`] = `
 Array [
-  "babel-plugin-minify-constant-folding",
+  "babel-plugin-transform-minify-booleans",
+  "babel-plugin-minify-builtins",
+  "babel-plugin-transform-inline-consecutive-adds",
   "babel-plugin-minify-dead-code-elimination",
+  "babel-plugin-minify-constant-folding",
   "babel-plugin-minify-flip-comparisons",
-  "babel-plugin-transform-simplify-comparison-operators",
   "babel-plugin-minify-guarded-expressions",
-  "babel-plugin-minify-type-constructors",
   "babel-plugin-minify-infinity",
   "babel-plugin-minify-mangle-names",
-  "babel-plugin-minify-numeric-literals",
-  "babel-plugin-minify-replace",
-  "babel-plugin-minify-simplify",
-  "babel-plugin-minify-builtins",
-  "babel-plugin-transform-inline-consecutive-adds",
   "babel-plugin-transform-member-expression-literals",
-  "babel-plugin-transform-property-literals",
   "babel-plugin-transform-merge-sibling-variables",
-  "babel-plugin-transform-minify-booleans",
-  "babel-plugin-transform-undefined-to-void",
+  "babel-plugin-minify-numeric-literals",
+  "babel-plugin-transform-property-literals",
   "babel-plugin-transform-regexp-constructors",
   "babel-plugin-transform-remove-undefined",
+  "babel-plugin-minify-replace",
+  "babel-plugin-minify-simplify",
+  "babel-plugin-transform-simplify-comparison-operators",
+  "babel-plugin-minify-type-constructors",
+  "babel-plugin-transform-undefined-to-void",
 ]
 `;
 
 exports[`preset-options should return defaults with no options 2`] = `
 Array [
-  "babel-plugin-minify-constant-folding",
+  "babel-plugin-transform-minify-booleans",
+  "babel-plugin-minify-builtins",
+  "babel-plugin-transform-inline-consecutive-adds",
   "babel-plugin-minify-dead-code-elimination",
+  "babel-plugin-minify-constant-folding",
   "babel-plugin-minify-flip-comparisons",
-  "babel-plugin-transform-simplify-comparison-operators",
   "babel-plugin-minify-guarded-expressions",
-  "babel-plugin-minify-type-constructors",
   "babel-plugin-minify-infinity",
   "babel-plugin-minify-mangle-names",
-  "babel-plugin-minify-numeric-literals",
-  "babel-plugin-minify-replace",
-  "babel-plugin-minify-simplify",
-  "babel-plugin-minify-builtins",
-  "babel-plugin-transform-inline-consecutive-adds",
   "babel-plugin-transform-member-expression-literals",
-  "babel-plugin-transform-property-literals",
   "babel-plugin-transform-merge-sibling-variables",
-  "babel-plugin-transform-minify-booleans",
-  "babel-plugin-transform-undefined-to-void",
+  "babel-plugin-minify-numeric-literals",
+  "babel-plugin-transform-property-literals",
   "babel-plugin-transform-regexp-constructors",
   "babel-plugin-transform-remove-undefined",
+  "babel-plugin-minify-replace",
+  "babel-plugin-minify-simplify",
+  "babel-plugin-transform-simplify-comparison-operators",
+  "babel-plugin-minify-type-constructors",
+  "babel-plugin-transform-undefined-to-void",
 ]
 `;
 
 exports[`preset-options should return defaults with no options 3`] = `
 Array [
-  "babel-plugin-minify-constant-folding",
+  "babel-plugin-transform-minify-booleans",
+  "babel-plugin-minify-builtins",
+  "babel-plugin-transform-inline-consecutive-adds",
   "babel-plugin-minify-dead-code-elimination",
+  "babel-plugin-minify-constant-folding",
   "babel-plugin-minify-flip-comparisons",
-  "babel-plugin-transform-simplify-comparison-operators",
   "babel-plugin-minify-guarded-expressions",
-  "babel-plugin-minify-type-constructors",
   "babel-plugin-minify-infinity",
   "babel-plugin-minify-mangle-names",
-  "babel-plugin-minify-numeric-literals",
-  "babel-plugin-minify-replace",
-  "babel-plugin-minify-simplify",
-  "babel-plugin-minify-builtins",
-  "babel-plugin-transform-inline-consecutive-adds",
   "babel-plugin-transform-member-expression-literals",
-  "babel-plugin-transform-property-literals",
   "babel-plugin-transform-merge-sibling-variables",
-  "babel-plugin-transform-minify-booleans",
-  "babel-plugin-transform-undefined-to-void",
+  "babel-plugin-minify-numeric-literals",
+  "babel-plugin-transform-property-literals",
   "babel-plugin-transform-regexp-constructors",
   "babel-plugin-transform-remove-undefined",
+  "babel-plugin-minify-replace",
+  "babel-plugin-minify-simplify",
+  "babel-plugin-transform-simplify-comparison-operators",
+  "babel-plugin-minify-type-constructors",
+  "babel-plugin-transform-undefined-to-void",
 ]
 `;
diff --git a/packages/babel-preset-babili/__tests__/options-tests.js b/packages/babel-preset-babili/__tests__/options-tests.js
index d299ff2..416ce28 100644
--- a/packages/babel-preset-babili/__tests__/options-tests.js
+++ b/packages/babel-preset-babili/__tests__/options-tests.js
@@ -69,21 +69,6 @@ describe("preset-options", () => {
     });
   });
 
-  it("should handle groups - remove entire group", () => {
-    testOpts({
-      unsafe: false
-    });
-  });
-
-  it("should handle individual items in a group of options", () => {
-    testOpts({
-      unsafe: {
-        flipComparisons: false
-      },
-      mangle: false
-    });
-  });
-
   it("should handle options that are delegated to multiple other options", () => {
     testOpts({
       keepFnName: false,