Repository: brenolf/polyjuice
Pull request #16 (2015-10-20 19:48:19)

Add ESLint conversion support


Patch:
diff --git a/README.md b/README.md
index 88588ce..578b324 100644
--- a/README.md
+++ b/README.md
@@ -10,9 +10,9 @@
 [![Coverage Status](https://coveralls.io/repos/brenolf/polyjuice/badge.svg?branch=master&service=github)](https://coveralls.io/github/brenolf/polyjuice?branch=master)
 [![Code Climate](https://codeclimate.com/github/brenolf/polyjuice/badges/gpa.svg)](https://codeclimate.com/github/brenolf/polyjuice)
 [![npm version](https://badge.fury.io/js/polyjuice.svg)](http://badge.fury.io/js/polyjuice)
-> Converts `.jshintrc` and `.jscs` files into `.eslintrc`
+> Converts `.jshintrc` and `.jscsrc` files into `.eslintrc` and vice-versa
 
-Polyjuice is a utility that converts [JSHint](http://jshint.com/) and [JSCS](http://jscs.info/) files into [ESLint](http://eslint.org/) files.
+Polyjuice is a utility that converts [JSHint](http://jshint.com/) and [JSCS](http://jscs.info/) files into [ESLint](http://eslint.org/) files and the other way around.
 
 ## Install
 `$ npm install --save-dev polyjuice`
@@ -21,22 +21,39 @@ Polyjuice is a utility that converts [JSHint](http://jshint.com/) and [JSCS](htt
 ```js
 var polyjuice = require('polyjuice')
 
-var eslint_json = {
+var to_eslint = {
   jshint: polyjuice.from.jshint('./.jshintrc'),
-  jscs  : polyjuice.from.jscs('./.jscs'),
-  both  : polyjuice.to.eslint('./.jshintrc', './.jscs')
+  jscs  : polyjuice.from.jscs('./.jscsrc'),
+  both  : polyjuice.to.eslint('./.jshintrc', './.jscsrc')
+}
+
+var to_jscs_and_jshint = {
+  jshint: polyjuice.to.jshint('./.eslintrc'),
+  jscs  : polyjuice.to.jscs('./.eslintrc'),
+  both  : polyjuice.from.eslint('./.eslintrc')
 }
 ```
 
+**Note**: When using `polyjuice.from.eslint` an object with keys `jscs` and `jshint` is returned.
+
 ## CLI
-If installed globally, you can use polyjuice as a command line utility. You must provide the files to be assessed by using the options `--jshint` and `--jscs`, for instance:
+
+If installed globally, you can use polyjuice as a command line utility. You can check which rules have been discarded by adding `--discarded` or `-d` to your command.
+
+### To ESLint
+
+You must provide the files to be assessed by using the options `--jshint` and `--jscs`, for instance:
 
 `$ polyjuice --jshint .jshintrc --jscs .jscsrc > .eslintrc`
 
-You can also check which rules have been discarded by adding `--discarded` or `-d` to your command.
+### To JSHint and JSCS
+
+You must provide the file to be assessed by using the option `--eslint` and also which output you want by using either `--to-jshint` or `--to-jscs`, for instance:
+
+`$ polyjuice --eslint .eslintrc --to-jshint > .jshintrc`
 
 ## Contributing
-Contributions are always welcome! If you want to have a better understanding about the conversion files, I encourage you to take a look at [the conversion page](/doc/DICTIONARIES.md). You can also check the rules discarded when [parsing JSCS](/doc/JSCS.md) and when [parsing JSHint](/doc/JSHINT.md).
+Contributions are always welcome! If you want to have a better understanding about the conversion files, I encourage you to take a look at [the conversion page](/doc/DICTIONARIES.md). You can also check the rules discarded when [parsing JSCS](/doc/JSCS.md), [parsing JSHint](/doc/JSHINT.md) and [parsing ESLint](/doc/ESLINT.md).
 
 ## License
 MIT
diff --git a/doc/DICTIONARIES.md b/doc/DICTIONARIES.md
index 3b66273..503047d 100644
--- a/doc/DICTIONARIES.md
+++ b/doc/DICTIONARIES.md
@@ -1,7 +1,7 @@
 # Dictionaries
 Polyjuice uses JavaScript objects to find a proper translation for a given rule. The object has the rule name to be translated as key and variable options as its value.
 
-Basically, there is a truth function that will be used to test the value of the rule to be translated,  and then its output is going to be matched against a truth table of default values of the destination tool.
+Basically, there is a truth function that will be used to test the value of the rule to be translated, and then its output is going to be matched against a truth table of default values of the destination tool.
 
 The truth function is currently default for all languages, and its implementation is as follows.
 
@@ -68,10 +68,22 @@ This will work just as above, applying it to every string in the array.
 }
 ```
 
-For objects there are 4 possible keys to be used: `name`, `truthy`, `falsy` and `test`. The attribute `name` is **mandatory** it is the name in the target language. The attribute `test`, if given, should be always a function and works much like using a function directly, but having their context set to an object with a single key `value`, whose value is the current value of the  attribute being set (the one represented by `name`).
+For objects there are 4 possible keys to be used: `name`, `truthy`, `falsy`, `eval` and `test`. The attribute `name` is **mandatory** it is the name in the target language. The attribute `test`, if given, should be always a function and works much like using a function directly, but having their context set to an object with a single key `value`, whose value is the current value of the  attribute being set (the one represented by `name`). The attribute `eval`, if given, should be a function that returns an object with the keys do be added, just like using it directly as a function. It is useful when there are more things you take into account (like ESLint conversion).
 
-Since Polyjuice has a FCFS policy for transpiling, the context provided for `test` plays an important rule on it. For instance, there could be a scenario where multiple options can be applied to the same rule and they are non-overlapping. Thus, you could find the context's values useful in order to preserve the previously applied rules.
+Since Polyjuice has a FCFS policy for transpiling, the context provided for both `test` and `eval` plays an important rule on it. For instance, there could be a scenario where multiple options can be applied to the same rule and they are non-overlapping. Thus, you could find the context's values useful in order to preserve the previously applied rules.
 
 The attributes `truthy` and `falsy`, if functions, are shorthands for `test` because they will only be called after applying the truth function and deciding whether or not the value is to be considered true/false. However, `truthy` and `falsy` can also have the content to be applied directly (no need to be functions as `test`).
 
-Note that `test` is always prioritized over `truthy` and `falsy`. Also mind the interpreter's FCFS policy when implementing a translation.
+Bear in mind that the function priority is `eval > test > truthy/falsy`. Also mind the interpreter's FCFS policy when implementing a translation.
+
+#### ESLint attributes
+
+When converting from ESLint, the object has to contain the `target` library it's converting to. ESLint conversions returns an object whose keys are the targets read when transpiling a file. For instance:
+
+```js
+'rule-in-eslint': {
+  target: 'jshint',
+  name: 'rule-in-jshint',
+  truthy: true
+}
+```
diff --git a/doc/ESLINT.md b/doc/ESLINT.md
index fefcd6c..fe7e3c9 100644
--- a/doc/ESLINT.md
+++ b/doc/ESLINT.md
@@ -1,1 +1,102 @@
 # ESLint discarded rules
+
+* `id-match` works precisely only when the rule is on the form `^(x|y|...|z)$`
+* `no-constant-condition`
+* `no-control-regex`
+* `no-dupe-args`
+* `no-dupe-keys`
+* `no-duplicate-case`
+* `no-empty-character-class`
+* `no-ex-assign`
+* `no-extra-boolean-cast`
+* `no-extra-semi`
+* `no-func-assign`
+* `no-inner-declarations`
+* `no-invalid-regexp`
+* `no-negated-in-lhs`
+* `no-obj-calls`
+* `no-regex-spaces`
+* `no-unexpected-multiline`
+* `no-unreachable`
+* `use-isnan`
+* `accessor-pairs`
+* `consistent-return`
+* `default-case`
+* `dot-location`
+* `no-alert`
+* `no-div-regex`
+* `no-else-return`
+* `no-empty-label`
+* `no-empty-pattern`
+* `no-extra-bind`
+* `no-fallthrough`
+* `no-floating-decimal`
+* `no-implied-eval`
+* `no-labels`
+* `no-lone-blocks`
+* `no-magic-numbers`
+* `no-native-reassign`
+* `no-octal-escape`
+* `no-octal`
+* `no-param-reassign`
+* `no-process-env`
+* `no-redeclare`
+* `no-return-assign`
+* `no-self-compare`
+* `no-throw-literal`
+* `no-useless-call`
+* `no-useless-concat`
+* `no-void`
+* `radix`
+* `vars-on-top`
+* `init-declarations`
+* `no-catch-shadow`
+* `no-delete-var`
+* `no-label-var`
+* `no-shadow-restricted-names`
+* `no-undef-init`
+* `no-undefined`
+* `callback-return`
+* `global-require`
+* `handle-callback-err`
+* `no-mixed-requires`
+* `no-new-require`
+* `no-path-concat`
+* `no-process-exit`
+* `no-restricted-modules`
+* `no-sync`
+* `block-spacing`
+* `computed-property-spacing`
+* `id-length`
+* `jsx-quotes`
+* `max-nested-callbacks`
+* `new-parens`
+* `no-array-constructor`
+* `no-continue`
+* `no-inline-comments`
+* `no-lonely-if`
+* `no-negated-condition`
+* `no-nested-ternary`
+* `no-new-object`
+* `no-restricted-syntax`
+* `no-ternary`
+* `no-unneeded-ternary`
+* `object-curly-spacing`
+* `operator-assignment`
+* `require-jsdoc`
+* `sort-vars`
+* `space-before-keywords`
+* `spaced-comment`
+* `wrap-regex`
+* `arrow-spacing`
+* `constructor-super`
+* `generator-star-spacing`
+* `no-class-assign`
+* `no-const-assign`
+* `no-dupe-class-members`
+* `no-this-before-super`
+* `object-shorthand`
+* `prefer-arrow-callback`
+* `prefer-const`
+* `prefer-reflect`
+* `prefer-template`
diff --git a/lib/assets/jshint_jscs_truth_tbl.json b/lib/assets/jshint_jscs_truth_tbl.json
new file mode 100644
index 0000000..955baa5
--- /dev/null
+++ b/lib/assets/jshint_jscs_truth_tbl.json
@@ -0,0 +1,4 @@
+{
+  "true": true,
+  "false": false
+}
diff --git a/lib/bin.js b/lib/bin.js
index 6a6f0c6..7e09319 100644
--- a/lib/bin.js
+++ b/lib/bin.js
@@ -15,6 +15,18 @@ var yargs = require('yargs')
     describe: 'the jshint config file',
     type: 'string'
   })
+  .option('eslint', {
+    describe: 'the eslint config file',
+    type: 'string'
+  })
+  .option('to-jscs', {
+    describe: 'whether the output should be jscs',
+    type: 'boolean'
+  })
+  .option('to-jshint', {
+    describe: 'whether the output should be jshint',
+    type: 'boolean'
+  })
   .option('discarded', {
     alias: 'd',
     describe: 'show discarded rules when transpiling'
@@ -38,6 +50,10 @@ if (argv.jshint && argv.jscs) {
   output = polyjuice.from.jshint(argv.jshint, discarded)
 } else if (argv.jscs) {
   output = polyjuice.from.jscs(argv.jscs, discarded)
+} else if (argv.eslint) {
+  var method = argv['to-jscs'] ? 'jscs' : 'jshint'
+
+  output = polyjuice.to[method](argv.eslint, discarded)
 } else {
   output = null
   yargs.showHelp()
diff --git a/lib/dictionaries/eslint.js b/lib/dictionaries/eslint.js
index 418367b..4727df5 100644
--- a/lib/dictionaries/eslint.js
+++ b/lib/dictionaries/eslint.js
@@ -1,8 +1,7 @@
 module.exports = {
   'comma-dangle': {
     target: 'jscs',
-    name: '',
-    test: function (value) {
+    eval: function (value) {
       if (value === 2 || value[1] === 'never') {
         return { disallowTrailingComma: true }
       }
@@ -13,1115 +12,843 @@ module.exports = {
 
   'no-cond-assign': {
     target: 'jshint',
-    name: 'boss',
-    truthy: true
+    name: 'boss'
   },
 
   'no-console': {
     target: 'jscs',
     name: 'predef',
     truthy: function () {
-      this.value = this.value || []
-
-      this.value.push('-console')
-    }
-  },
+      var list = this.value || []
 
-  'no-constant-condition': {
-    target: 'jscs',
-    name: '',
-    truthy: []
-  },
+      list.push('-console')
 
-  'no-control-regex': {
-    target: 'jscs',
-    name: '',
-    truthy: []
+      return list
+    }
   },
 
   'no-debugger': {
-    target: 'jscs',
-    name: '',
-    truthy: []
-  },
-
-  'no-dupe-args': {
-    target: 'jscs',
-    name: '',
-    truthy: []
-  },
-
-  'no-dupe-keys': {
-    target: 'jscs',
-    name: '',
-    truthy: []
-  },
-
-  'no-duplicate-case': {
-    target: 'jscs',
-    name: '',
-    truthy: []
-  },
-
-  'no-empty-character-class': {
-    target: 'jscs',
-    name: '',
-    truthy: []
+    target: 'jshint',
+    name: 'debug'
   },
 
   'no-empty': {
     target: 'jscs',
-    name: '',
-    truthy: []
-  },
-
-  'no-ex-assign': {
-    target: 'jscs',
-    name: '',
-    truthy: []
-  },
-
-  'no-extra-boolean-cast': {
-    target: 'jscs',
-    name: '',
-    truthy: []
+    name: 'disallowEmptyBlocks'
   },
 
   'no-extra-parens': {
-    target: 'jscs',
-    name: '',
-    truthy: []
-  },
-
-  'no-extra-semi': {
-    target: 'jscs',
-    name: '',
-    truthy: []
-  },
-
-  'no-func-assign': {
-    target: 'jscs',
-    name: '',
-    truthy: []
-  },
-
-  'no-inner-declarations': {
-    target: 'jscs',
-    name: '',
-    truthy: []
-  },
-
-  'no-invalid-regexp': {
-    target: 'jscs',
-    name: '',
-    truthy: []
+    target: 'jshint',
+    name: 'singleGroups',
+    truthy: false,
+    falsy: true
   },
 
   'no-irregular-whitespace': {
-    target: 'jscs',
-    name: '',
-    truthy: []
-  },
-
-  'no-negated-in-lhs': {
-    target: 'jscs',
-    name: '',
-    truthy: []
-  },
-
-  'no-obj-calls': {
-    target: 'jscs',
-    name: '',
-    truthy: []
-  },
-
-  'no-regex-spaces': {
-    target: 'jscs',
-    name: '',
-    truthy: []
+    target: 'jshint',
+    name: 'nonbsp'
   },
 
   'no-sparse-arrays': {
-    target: 'jscs',
-    name: '',
-    truthy: []
-  },
-
-  'no-unexpected-multiline': {
-    target: 'jscs',
-    name: '',
-    truthy: []
-  },
-
-  'no-unreachable': {
-    target: 'jscs',
-    name: '',
-    truthy: []
-  },
-
-  'use-isnan': {
-    target: 'jscs',
-    name: '',
-    truthy: []
+    target: 'jshint',
+    name: 'elision'
   },
 
   'valid-jsdoc': {
     target: 'jscs',
-    name: '',
-    truthy: []
+    name: 'jsDoc'
   },
 
   'valid-typeof': {
-    target: 'jscs',
-    name: '',
-    truthy: []
-  },
-
-  'accessor-pairs': {
-    target: 'jscs',
-    name: '',
-    truthy: []
+    target: 'jshint',
+    name: 'notypeof'
   },
 
   'block-scoped-var': {
-    target: 'jscs',
-    name: '',
-    truthy: []
+    target: 'jshint',
+    name: 'funcscope'
   },
 
   'complexity': {
-    target: 'jscs',
-    name: '',
-    truthy: []
-  },
-
-  'consistent-return': {
-    target: 'jscs',
-    name: '',
-    truthy: []
+    target: 'jshint',
+    name: 'maxcomplexity',
+    truthy: function (value) {
+      return value[1]
+    }
   },
 
   'curly': {
     target: 'jscs',
-    name: '',
-    truthy: []
-  },
-
-  'default-case': {
-    target: 'jscs',
-    name: '',
-    truthy: []
-  },
+    eval: function (value) {
+      if (value[1] === 'multi-line') {
+        return { disallowCurlyBraces: true }
+      }
 
-  'dot-location': {
-    target: 'jscs',
-    name: '',
-    truthy: []
+      return { requireCurlyBraces: true }
+    }
   },
 
   'dot-notation': {
     target: 'jscs',
-    name: '',
-    truthy: []
-  },
+    name: 'requireDotNotation',
+    truthy: function (value) {
+      if (value[1] && value[1].allowPattern === '^[a-z]+(_[a-z]+)+$') {
+        var allExcept = ['snake_case']
 
-  'eqeqeq': {
-    target: 'jscs',
-    name: '',
-    truthy: []
-  },
+        if (value[1].allowKeywords) {
+          allExcept.push('keywords')
+        }
 
-  'guard-for-in': {
-    target: 'jscs',
-    name: '',
-    truthy: []
-  },
-
-  'no-alert': {
-    target: 'jscs',
-    name: '',
-    truthy: []
-  },
-
-  'no-caller': {
-    target: 'jscs',
-    name: '',
-    truthy: []
-  },
+        return { allExcept: allExcept }
+      }
 
-  'no-div-regex': {
-    target: 'jscs',
-    name: '',
-    truthy: []
+      return true
+    }
   },
 
-  'no-else-return': {
-    target: 'jscs',
-    name: '',
-    truthy: []
+  'eqeqeq': {
+    target: 'jshint',
+    name: 'eqeqeq'
   },
 
-  'no-empty-label': {
-    target: 'jscs',
-    name: '',
-    truthy: []
+  'guard-for-in': {
+    target: 'jshint',
+    name: 'forin'
   },
 
-  'no-empty-pattern': {
-    target: 'jscs',
-    name: '',
-    truthy: []
+  'no-caller': {
+    target: 'jshint',
+    name: 'noarg'
   },
 
   'no-eq-null': {
-    target: 'jscs',
-    name: '',
-    truthy: []
+    target: 'jshint',
+    name: 'eqnull'
   },
 
   'no-eval': {
-    target: 'jscs',
-    name: '',
-    truthy: []
+    target: 'jshint',
+    name: 'evil'
   },
 
   'no-extend-native': {
-    target: 'jscs',
-    name: '',
-    truthy: []
+    target: 'jshint',
+    name: 'freeze'
   },
 
-  'no-extra-bind': {
+  'no-implicit-coercion': {
     target: 'jscs',
-    name: '',
-    truthy: []
-  },
+    name: 'disallowImplicitTypeConversion',
+    truthy: function (value) {
+      var list = []
 
-  'no-fallthrough': {
-    target: 'jscs',
-    name: '',
-    truthy: []
-  },
+      value = value[1]
 
-  'no-floating-decimal': {
-    target: 'jscs',
-    name: '',
-    truthy: []
-  },
+      var counterpart = {
+        'boolean': 'boolean',
+        'string': 'string',
+        'number': 'numeric'
+      }
 
-  'no-implicit-coercion': {
-    target: 'jscs',
-    name: '',
-    truthy: []
-  },
+      for (var type in value) {
+        if (type in counterpart) {
+          list.push(counterpart[type])
+        }
+      }
 
-  'no-implied-eval': {
-    target: 'jscs',
-    name: '',
-    truthy: []
+      return list
+    }
   },
 
   'no-invalid-this': {
-    target: 'jscs',
-    name: '',
-    truthy: []
+    target: 'jshint',
+    name: 'validthis'
   },
 
   'no-iterator': {
-    target: 'jscs',
-    name: '',
-    truthy: []
-  },
-
-  'no-labels': {
-    target: 'jscs',
-    name: '',
-    truthy: []
-  },
-
-  'no-lone-blocks': {
-    target: 'jscs',
-    name: '',
-    truthy: []
+    target: 'jshint',
+    name: 'iterator'
   },
 
   'no-loop-func': {
-    target: 'jscs',
-    name: '',
-    truthy: []
-  },
-
-  'no-magic-numbers': {
-    target: 'jscs',
-    name: '',
-    truthy: []
+    target: 'jshint',
+    name: 'loopfunc'
   },
 
   'no-multi-spaces': {
     target: 'jscs',
-    name: '',
-    truthy: []
+    name: 'disallowMultipleSpaces'
   },
 
   'no-multi-str': {
     target: 'jscs',
-    name: '',
-    truthy: []
-  },
-
-  'no-native-reassign': {
-    target: 'jscs',
-    name: '',
-    truthy: []
+    name: 'disallowMultipleLineStrings'
   },
 
   'no-new-func': {
-    target: 'jscs',
-    name: '',
-    truthy: []
+    target: 'jshint',
+    name: 'supernew'
   },
 
   'no-new-wrappers': {
-    target: 'jscs',
-    name: '',
-    truthy: []
+    target: 'jshint',
+    name: 'supernew'
   },
 
   'no-new': {
-    target: 'jscs',
-    name: '',
-    truthy: []
-  },
-
-  'no-octal-escape': {
-    target: 'jscs',
-    name: '',
-    truthy: []
-  },
-
-  'no-octal': {
-    target: 'jscs',
-    name: '',
-    truthy: []
-  },
-
-  'no-param-reassign': {
-    target: 'jscs',
-    name: '',
-    truthy: []
-  },
-
-  'no-process-env': {
-    target: 'jscs',
-    name: '',
-    truthy: []
+    target: 'jshint',
+    name: 'nonew'
   },
 
   'no-proto': {
-    target: 'jscs',
-    name: '',
-    truthy: []
-  },
-
-  'no-redeclare': {
-    target: 'jscs',
-    name: '',
-    truthy: []
-  },
-
-  'no-return-assign': {
-    target: 'jscs',
-    name: '',
-    truthy: []
+    target: 'jshint',
+    name: 'proto'
   },
 
   'no-script-url': {
-    target: 'jscs',
-    name: '',
-    truthy: []
-  },
-
-  'no-self-compare': {
-    target: 'jscs',
-    name: '',
-    truthy: []
+    target: 'jshint',
+    name: 'scripturl'
   },
 
   'no-sequences': {
-    target: 'jscs',
-    name: '',
-    truthy: []
-  },
-
-  'no-throw-literal': {
-    target: 'jscs',
-    name: '',
-    truthy: []
+    target: 'jshint',
+    name: 'nocomma'
   },
 
   'no-unused-expressions': {
-    target: 'jscs',
-    name: '',
-    truthy: []
-  },
-
-  'no-useless-call': {
-    target: 'jscs',
-    name: '',
-    truthy: []
-  },
-
-  'no-useless-concat': {
-    target: 'jscs',
-    name: '',
-    truthy: []
-  },
-
-  'no-void': {
-    target: 'jscs',
-    name: '',
-    truthy: []
+    target: 'jshint',
+    name: 'expr'
   },
 
   'no-warning-comments': {
     target: 'jscs',
-    name: '',
-    truthy: []
+    name: 'disallowKeywordsInComments',
+    truthy: function (value) {
+      return (value[1] && value[1].terms) ? value[1].terms : []
+    }
   },
 
   'no-with': {
     target: 'jscs',
-    name: '',
-    truthy: []
-  },
+    name: 'disallowKeywords',
+    truthy: function () {
+      var list = this.value || []
 
-  'radix': {
-    target: 'jscs',
-    name: '',
-    truthy: []
-  },
+      list.push('with')
 
-  'vars-on-top': {
-    target: 'jscs',
-    name: '',
-    truthy: []
+      return list
+    }
   },
 
   'wrap-iife': {
     target: 'jscs',
-    name: '',
-    truthy: []
+    name: 'requireParenthesesAroundIIFE'
   },
 
   'yoda': {
     target: 'jscs',
-    name: '',
-    truthy: []
-  },
+    eval: function (value) {
+      var verb = (value[1] === 'never') ? 'disallow' : 'require'
 
-  'strict': {
-    target: 'jscs',
-    name: '',
-    truthy: []
-  },
+      var obj = {}
 
-  'init-declarations': {
-    target: 'jscs',
-    name: '',
-    truthy: []
-  },
+      obj[verb + 'YodaConditions'] = true
 
-  'no-catch-shadow': {
-    target: 'jscs',
-    name: '',
-    truthy: []
-  },
-
-  'no-delete-var': {
-    target: 'jscs',
-    name: '',
-    truthy: []
+      return obj
+    }
   },
 
-  'no-label-var': {
-    target: 'jscs',
-    name: '',
-    truthy: []
-  },
+  'strict': {
+    target: 'jshint',
+    eval: function (value) {
+      if (value === 0) {
+        return { globalstrict: true }
+      }
 
-  'no-shadow-restricted-names': {
-    target: 'jscs',
-    name: '',
-    truthy: []
+      return { strict: true }
+    }
   },
 
   'no-shadow': {
-    target: 'jscs',
-    name: '',
-    truthy: []
-  },
+    target: 'jshint',
+    eval: function (value) {
+      var result = 'outer'
 
-  'no-undef-init': {
-    target: 'jscs',
-    name: '',
-    truthy: []
-  },
+      if (value === 2) {
+        result = false
+      }
 
-  'no-undef': {
-    target: 'jscs',
-    name: '',
-    truthy: []
+      if (value === 0) {
+        result = true
+      }
+
+      return { shadow: result }
+    }
   },
 
-  'no-undefined': {
-    target: 'jscs',
-    name: '',
-    truthy: []
+  'no-undef': {
+    target: 'jshint',
+    name: 'undef'
   },
 
   'no-unused-vars': {
-    target: 'jscs',
-    name: '',
-    truthy: []
+    target: 'jshint',
+    name: 'unused'
   },
 
   'no-use-before-define': {
-    target: 'jscs',
-    name: '',
-    truthy: []
-  },
-
-  'callback-return': {
-    target: 'jscs',
-    name: '',
-    truthy: []
-  },
-
-  'global-require': {
-    target: 'jscs',
-    name: '',
-    truthy: []
-  },
-
-  'handle-callback-err': {
-    target: 'jscs',
-    name: '',
-    truthy: []
+    target: 'jshint',
+    name: 'latedef'
   },
 
-  'no-mixed-requires': {
+  'array-bracket-spacing': {
     target: 'jscs',
-    name: '',
-    truthy: []
-  },
+    eval: function (value) {
+      var result = true
+      var verb = 'require'
+      var obj = {}
 
-  'no-new-require': {
-    target: 'jscs',
-    name: '',
-    truthy: []
-  },
+      if (value[1] === 'never') {
+        verb = 'disallow'
+      }
 
-  'no-path-concat': {
-    target: 'jscs',
-    name: '',
-    truthy: []
-  },
+      if (value[2] && (value[2].objectsInArrays || value[2].arraysInArrays)) {
+        result = {
+          allExcept: value[2].objectsInArrays ? ['{', '}'] : []
+        }
 
-  'no-process-exit': {
-    target: 'jscs',
-    name: '',
-    truthy: []
-  },
+        result.allExcept =
+          result.allExcept.concat(value[2].arraysInArrays ? ['[', ']'] : [])
+      }
 
-  'no-restricted-modules': {
-    target: 'jscs',
-    name: '',
-    truthy: []
-  },
+      obj[verb + 'SpacesInsideArrayBrackets'] = result
+      obj[verb + 'SpacesInsideObjectBrackets'] = result
 
-  'no-sync': {
-    target: 'jscs',
-    name: '',
-    truthy: []
+      return obj
+    }
   },
 
-  'array-bracket-spacing': {
+  'brace-style': {
     target: 'jscs',
-    name: '',
-    truthy: []
-  },
+    eval: function (value) {
+      var spacedKeywords = ['do', 'for', 'if', 'else', 'switch', 'case', 'try',
+      'catch', 'finally', 'void', 'while', 'with', 'return', 'typeof',
+      'function']
 
-  'block-spacing': {
-    target: 'jscs',
-    name: '',
-    truthy: []
-  },
+      if (value[0] > 0 && value[1] === 'stroustrup'
+          && value[2] && value[2].allowSingleLine) {
+        return { requireKeywordsOnNewLine: spacedKeywords }
+      }
 
-  'brace-style': {
-    target: 'jscs',
-    name: '',
-    truthy: []
+      return { disallowKeywordsOnNewLine: spacedKeywords }
+    }
   },
 
   'camelcase': {
     target: 'jscs',
-    name: '',
-    truthy: []
+    name: 'requireCamelCaseOrUpperCaseIdentifiers',
+    truthy: function (value) {
+      return value[1].properties === 'never'
+    }
   },
 
   'comma-spacing': {
     target: 'jscs',
-    name: '',
-    truthy: []
+    eval: function (value) {
+      if (value[0] > 0 && value[1] && !value[1].before) {
+        return { requireSpaceBeforeComma: true }
+      }
+
+      return { disallowSpaceBeforeComma: true }
+    }
   },
 
   'comma-style': {
     target: 'jscs',
-    name: '',
-    truthy: []
-  },
+    eval: function (value) {
+      if (value[0] > 0 && value[1] === 'first') {
+        return { disallowCommaBeforeLineBreak: true }
+      }
 
-  'computed-property-spacing': {
-    target: 'jscs',
-    name: '',
-    truthy: []
+      return { requireCommaBeforeLineBreak: true }
+    }
   },
 
   'consistent-this': {
     target: 'jscs',
-    name: '',
-    truthy: []
+    name: 'safeContextKeyword',
+    truthy: function (value) {
+      return value[1]
+    }
   },
 
   'eol-last': {
     target: 'jscs',
-    name: '',
-    truthy: []
+    name: 'requireLineFeedAtFileEnd'
   },
 
   'func-names': {
     target: 'jscs',
-    name: '',
-    truthy: []
-  },
+    eval: function (value) {
+      if (value[0] === 0 || value === 0) {
+        return {}
+      }
 
-  'func-style': {
-    target: 'jscs',
-    name: '',
-    truthy: []
+      return {
+        requireNamedUnassignedFunctions: true,
+        disallowAnonymousFunctions: true
+      }
+    }
   },
 
-  'id-length': {
+  'func-style': {
     target: 'jscs',
-    name: '',
-    truthy: []
+    name: 'disallowFunctionDeclarations',
+    truthy: true
   },
 
   'id-match': {
     target: 'jscs',
-    name: '',
-    truthy: []
+    name: 'disallowIdentifierNames',
+    truthy: function (value) {
+      return value[1].slice(2, -2).split('|')
+    }
   },
 
   'indent': {
     target: 'jscs',
-    name: '',
-    truthy: []
-  },
+    name: 'validateIndentation',
+    truthy: function (value) {
+      if (value === 2) {
+        return 4
+      }
 
-  'jsx-quotes': {
-    target: 'jscs',
-    name: '',
-    truthy: []
+      return value[1] === 'tab' ? '\t' : value[1]
+    }
   },
 
   'key-spacing': {
     target: 'jscs',
-    name: '',
-    truthy: []
-  },
-
-  'linebreak-style': {
-    target: 'jscs',
-    name: '',
-    truthy: []
-  },
+    eval: function (value) {
+      if (value[0] === 0) {
+        return {}
+      }
 
-  'lines-around-comment': {
-    target: 'jscs',
-    name: '',
-    truthy: []
-  },
+      if (value[1].align === 'colon') {
+        return {
+          requireAlignedObjectValues: true,
+          disallowSpaceAfterObjectKeys: {
+            allExcept: ['aligned']
+          }
+        }
+      }
 
-  'max-nested-callbacks': {
-    target: 'jscs',
-    name: '',
-    truthy: []
-  },
+      var obj = {}
 
-  'new-cap': {
-    target: 'jscs',
-    name: '',
-    truthy: []
-  },
+      if (value[1].afterColon === true) {
+        obj.requireSpaceBeforeObjectValues = true
+      } else if (value[1].afterColon === false) {
+        obj.disallowSpaceBeforeObjectValues = true
+      }
 
-  'new-parens': {
-    target: 'jscs',
-    name: '',
-    truthy: []
-  },
+      if (value[1].beforeColon === true) {
+        obj.requireSpaceAfterObjectKeys = true
+      } else if (value[1].beforeColon === false) {
+        obj.disallowSpaceAfterObjectKeys = true
+      }
 
-  'newline-after-var': {
-    target: 'jscs',
-    name: '',
-    truthy: []
+      return obj
+    }
   },
 
-  'no-array-constructor': {
+  'linebreak-style': {
     target: 'jscs',
-    name: '',
-    truthy: []
+    name: 'validateLineBreaks',
+    truthy: function (value) {
+      return value[1] === 'windows' ? 'CLRF' : 'LF'
+    }
   },
 
-  'no-continue': {
+  'lines-around-comment': {
     target: 'jscs',
-    name: '',
-    truthy: []
-  },
+    eval: function (value) {
+      if (value[0] > 0 || value > 0) {
+        return {
+          requirePaddingNewLinesBeforeLineComments: true,
+          requireSpaceAfterLineComment: true
+        }
+      }
 
-  'no-inline-comments': {
-    target: 'jscs',
-    name: '',
-    truthy: []
+      return {}
+    }
   },
 
-  'no-lonely-if': {
+  'new-cap': {
     target: 'jscs',
-    name: '',
-    truthy: []
-  },
+    name: 'requireCapitalizedConstructors',
+    truthy: function (value) {
+      if (value[1]) {
+        var a = value[1].newIsCapExceptions || []
+        var b = value[1].capIsNewExceptions || []
 
-  'no-mixed-spaces-and-tabs': {
-    target: 'jscs',
-    name: '',
-    truthy: []
-  },
+        return { allExcept: a.concat(b) }
+      }
 
-  'no-multiple-empty-lines': {
-    target: 'jscs',
-    name: '',
-    truthy: []
+      return true
+    }
   },
 
-  'no-negated-condition': {
+  'newline-after-var': {
     target: 'jscs',
-    name: '',
-    truthy: []
-  },
+    eval: function (value) {
+      if (value > 0 || (value[0] > 0 && value[1] === 'always')) {
+        return {
+          requireLineBreakAfterVariableAssignment: true,
+          requirePaddingNewLineAfterVariableDeclaration: true
+        }
+      }
 
-  'no-nested-ternary': {
-    target: 'jscs',
-    name: '',
-    truthy: []
+      return {}
+    }
   },
 
-  'no-new-object': {
+  'no-mixed-spaces-and-tabs': {
     target: 'jscs',
-    name: '',
-    truthy: []
+    name: 'disallowMixedSpacesAndTabs'
   },
 
-  'no-restricted-syntax': {
+  'no-multiple-empty-lines': {
     target: 'jscs',
-    name: '',
-    truthy: []
+    name: 'disallowMultipleLineBreaks'
   },
 
   'no-spaced-func': {
     target: 'jscs',
-    name: '',
-    truthy: []
-  },
-
-  'no-ternary': {
-    target: 'jscs',
-    name: '',
-    truthy: []
+    name: 'disallowSpacesInCallExpression'
   },
 
   'no-trailing-spaces': {
     target: 'jscs',
-    name: '',
-    truthy: []
-  },
-
-  'no-underscore-dangle': {
-    target: 'jscs',
-    name: '',
-    truthy: []
-  },
+    name: 'disallowTrailingWhitespace',
+    truthy: function (value) {
+      if (value[1] && value[1].skipBlankLines) {
+        return 'ignoreEmptyLines'
+      }
 
-  'no-unneeded-ternary': {
-    target: 'jscs',
-    name: '',
-    truthy: []
+      return true
+    }
   },
 
-  'object-curly-spacing': {
+  'no-underscore-dangle': {
     target: 'jscs',
-    name: '',
-    truthy: []
+    name: 'disallowDanglingUnderscores'
   },
 
   'one-var': {
     target: 'jscs',
-    name: '',
-    truthy: []
-  },
+    eval: function (value) {
+      if (value[1] && value[1] === 'never') {
+        return {
+          disallowMultipleVarDecl: true,
+          requireMultipleVarDecl: true
+        }
+      }
 
-  'operator-assignment': {
-    target: 'jscs',
-    name: '',
-    truthy: []
+      if (value[0] > 0 || value > 0) {
+        return { disallowMultipleVarDecl: true }
+      }
+
+      return {}
+    }
   },
 
   'operator-linebreak': {
     target: 'jscs',
-    name: '',
-    truthy: []
+    eval: function (value) {
+      if (value[1] && value[1] === 'before') {
+        return { disallowOperatorBeforeLineBreak: true }
+      } else if (value[1] && value[1] === 'after') {
+        return { requireOperatorBeforeLineBreak: true }
+      }
+
+      return {}
+    }
   },
 
   'padded-blocks': {
     target: 'jscs',
-    name: '',
-    truthy: []
+    eval: function (value) {
+      if (value[1] && value[1] === 'never') {
+        return { disallowPaddingNewlinesInBlocks: true }
+      } else if (value[1] && value[1] === 'always') {
+        return { requirePaddingNewlinesInBlocks: true }
+      }
+
+      return {}
+    }
   },
 
   'quote-props': {
     target: 'jscs',
-    name: '',
-    truthy: []
+    eval: function (value) {
+      if (value[2]) {
+        return { disallowQuotedKeysInObjects: 'allButReserved' }
+      }
+
+      if (value[1] === 'as-needed') {
+        return { disallowQuotedKeysInObjects: true }
+      }
+
+      if (value[0] > 0 || value > 0) {
+        return { requireQuotedKeysInObjects: true }
+      }
+
+      return {}
+    }
   },
 
   'quotes': {
     target: 'jscs',
-    name: '',
-    truthy: []
-  },
+    eval: function (value) {
+      if (value[1] === 'backtick') {
+        return { requireTemplateStrings: true }
+      }
 
-  'require-jsdoc': {
-    target: 'jscs',
-    name: '',
-    truthy: []
+      var mark = value[1] === 'double' ? '"' : '\''
+
+      if (value[2] === 'avoid-escape') {
+        return {
+          validateQuoteMarks: {
+            mark: mark,
+            escape: true
+          }
+        }
+      }
+
+      if (value[0] === 0 || value === 0) {
+        return {}
+      }
+
+      return { validateQuoteMarks: mark }
+    }
   },
 
   'semi-spacing': {
     target: 'jscs',
-    name: '',
-    truthy: []
+    name: 'disallowSpaceBeforeSemicolon',
+    truthy: true
   },
 
   'semi': {
     target: 'jscs',
-    name: '',
-    truthy: []
-  },
+    eval: function (value) {
+      if (value[0] === 0 || value === 0) {
+        return {}
+      }
 
-  'sort-vars': {
-    target: 'jscs',
-    name: '',
-    truthy: []
+      if (value[1] === 'always') {
+        return { requireSemicolons: true }
+      }
+
+      return { disallowSemicolons: true }
+    }
   },
 
   'space-after-keywords': {
     target: 'jscs',
-    name: '',
-    truthy: []
+    eval: function (value) {
+      if (value[0] === 0 || value === 0) {
+        return {}
+      }
+
+      var keywords = ['do', 'for', 'if', 'else', 'switch', 'try', 'catch',
+        'finally', 'void', 'while', 'with', 'typeof', 'function']
+
+      var list = (this.value || []).concat(keywords)
+
+      if (value[1] === 'always') {
+        return { requireSpaceAfterKeywords: list }
+      }
+
+      return { disallowSpaceAfterKeywords: list }
+    }
   },
 
   'space-before-blocks': {
     target: 'jscs',
-    name: '',
-    truthy: []
-  },
+    eval: function (value) {
+      if (value[0] === 0 || value === 0) {
+        return {}
+      }
 
-  'space-before-function-paren': {
-    target: 'jscs',
-    name: '',
-    truthy: []
-  },
+      if (value[1] === 'always') {
+        return { requireSpaceBeforeBlockStatements: true }
+      }
 
-  'space-before-keywords': {
-    target: 'jscs',
-    name: '',
-    truthy: []
+      return { disallowSpaceBeforeBlockStatements: true }
+    }
   },
 
-  'space-in-parens': {
+  'space-before-function-paren': {
     target: 'jscs',
-    name: '',
-    truthy: []
-  },
+    eval: function (value) {
+      if (value[0] === 0 || value === 0) {
+        return {}
+      }
 
-  'space-infix-ops': {
-    target: 'jscs',
-    name: '',
-    truthy: []
-  },
+      if (value[1] === 'always') {
+        return {
+          requireSpacesInFunctionDeclaration: true,
+          requireSpacesInFunctionExpression: true
+        }
+      }
 
-  'space-return-throw-case': {
-    target: 'jscs',
-    name: '',
-    truthy: []
+      return {
+        disallowSpacesInFunctionDeclaration: true,
+        disallowSpacesInFunctionExpression: true,
+        disallowSpacesInFunction: true,
+        disallowSpacesInNamedFunctionExpression: true
+      }
+    }
   },
 
-  'space-unary-ops': {
+  'space-in-parens': {
     target: 'jscs',
-    name: '',
-    truthy: []
-  },
+    eval: function (value) {
+      if (value[0] === 0 || value === 0) {
+        return {}
+      }
 
-  'spaced-comment': {
-    target: 'jscs',
-    name: '',
-    truthy: []
-  },
+      var obj = {}
 
-  'wrap-regex': {
-    target: 'jscs',
-    name: '',
-    truthy: []
-  },
+      var list = null
 
-  'arrow-parens': {
-    target: 'jscs',
-    name: '',
-    truthy: []
-  },
+      if (value[2]) {
+        list = value[2].exceptions.join('').split('')
+      }
 
-  'arrow-spacing': {
-    target: 'jscs',
-    name: '',
-    truthy: []
-  },
+      if (value[1] === 'never') {
+        obj.disallowSpacesInsideParentheses = true
+
+        if (list) {
+          obj.disallowSpacesInsideParentheses = {
+            only: list
+          }
+        }
+      } else {
+        obj.requireSpacesInsideParentheses = 'all'
+
+        if (list) {
+          obj.requireSpacesInsideParentheses = {
+            all: true,
+            except: list
+          }
+        }
+      }
 
-  'constructor-super': {
-    target: 'jscs',
-    name: '',
-    truthy: []
+      return obj
+    }
   },
 
-  'generator-star-spacing': {
+  'space-infix-ops': {
     target: 'jscs',
-    name: '',
-    truthy: []
+    name: 'requireSpaceAfterBinaryOperators',
+    truthy: true
   },
 
-  'no-class-assign': {
+  'space-return-throw-case': {
     target: 'jscs',
-    name: '',
-    truthy: []
-  },
+    eval: function (value) {
+      if (value[0] === 0 || value === 0) {
+        return {}
+      }
 
-  'no-const-assign': {
-    target: 'jscs',
-    name: '',
-    truthy: []
-  },
+      var list = (this.value || []).concat(['return', 'throw', 'case'])
 
-  'no-dupe-class-members': {
-    target: 'jscs',
-    name: '',
-    truthy: []
-  },
+      if (value[1] === 'always') {
+        return { requireSpaceAfterKeywords: list }
+      }
 
-  'no-this-before-super': {
-    target: 'jscs',
-    name: '',
-    truthy: []
+      return { disallowSpaceAfterKeywords: list }
+    }
   },
 
-  'no-var': {
+  'space-unary-ops': {
     target: 'jscs',
-    name: '',
-    truthy: []
-  },
+    eval: function (value) {
+      if (value[0] === 0 || value === 0) {
+        return { disallowSpaceAfterPrefixUnaryOperators: true }
+      }
 
-  'object-shorthand': {
-    target: 'jscs',
-    name: '',
-    truthy: []
+      return { requireSpaceAfterPrefixUnaryOperators: true }
+    }
   },
 
-  'prefer-arrow-callback': {
+  'arrow-parens': {
     target: 'jscs',
-    name: '',
-    truthy: []
-  },
+    eval: function (value) {
+      if (value[0] === 0 || value === 0) {
+        return { disallowParenthesesAroundArrowParam: true }
+      }
 
-  'prefer-const': {
-    target: 'jscs',
-    name: '',
-    truthy: []
+      return { requireParenthesesAroundArrowParam: true }
+    }
   },
 
-  'prefer-reflect': {
-    target: 'jscs',
-    name: '',
-    truthy: []
+  'no-var': {
+    target: 'jshint',
+    name: 'varstmt'
   },
 
   'prefer-spread': {
     target: 'jscs',
-    name: '',
-    truthy: []
-  },
-
-  'prefer-template': {
-    target: 'jscs',
-    name: '',
-    truthy: []
+    name: 'requireSpread'
   },
 
   'require-yield': {
-    target: 'jscs',
-    name: '',
-    truthy: []
+    target: 'jshint',
+    name: 'noyield'
   },
 
   'max-depth': {
-    target: 'jscs',
-    name: '',
-    truthy: []
+    target: 'jshint',
+    name: 'maxdepth',
+    truthy: function (value) {
+      return value[1]
+    }
   },
 
   'max-len': {
     target: 'jscs',
-    name: '',
-    truthy: []
+    name: 'maximumLineLength',
+    truthy: function (value) {
+      return value[1]
+    }
   },
 
   'max-params': {
-    target: 'jscs',
-    name: '',
-    truthy: []
+    target: 'jshint',
+    name: 'maxparams',
+    truthy: function (value) {
+      return value[1]
+    }
   },
 
   'max-statements': {
-    target: 'jscs',
-    name: '',
-    truthy: []
+    target: 'jshint',
+    name: 'maxstatements',
+    truthy: function (value) {
+      return value[1]
+    }
   },
 
   'no-bitwise': {
-    target: 'jscs',
-    name: '',
-    truthy: []
+    target: 'jshint',
+    name: 'bitwise'
   },
 
   'no-plusplus': {
-    target: 'jscs',
-    name: '',
-    truthy: []
+    target: 'jshint',
+    name: 'plusplus'
   }
 }
diff --git a/lib/dictionaries/jscs.js b/lib/dictionaries/jscs.js
index d58a375..dd0f4f5 100644
--- a/lib/dictionaries/jscs.js
+++ b/lib/dictionaries/jscs.js
@@ -103,7 +103,11 @@ module.exports = {
     truthy: [2, 'never']
   },
 
-  'disallowParenthesesAroundArrowParam': 'arrow-parens',
+  'disallowParenthesesAroundArrowParam': {
+    name: 'arrow-parens',
+    truthy: 0,
+    falsy: [2, 'always']
+  },
 
   'disallowQuotedKeysInObjects': {
     name: 'quote-props',
@@ -227,7 +231,7 @@ module.exports = {
   'disallowSpacesInsideArrayBrackets': {
     name: 'array-bracket-spacing',
     truthy: function (value) {
-      var rule = this.value || [2, 'always', {}]
+      var rule = this.value || [2, 'never', {}]
 
       rule[2].singleValue = true
 
@@ -242,7 +246,7 @@ module.exports = {
   'disallowSpacesInsideObjectBrackets': {
     name: 'array-bracket-spacing',
     truthy: function (value) {
-      var rule = this.value || [2, 'always', {}]
+      var rule = this.value || [2, 'never', {}]
 
       rule[2].singleValue = true
 
@@ -487,7 +491,11 @@ module.exports = {
     }
   },
 
-  'disallowSpaceAfterPrefixUnaryOperators': 'space-unary-ops',
+  'disallowSpaceAfterPrefixUnaryOperators': {
+    name: 'space-unary-ops',
+    truthy: 0,
+    falsy: 2
+  },
 
   'requireSpaceAfterPrefixUnaryOperators': 'space-unary-ops',
 
diff --git a/lib/polyjuice.js b/lib/polyjuice.js
index d7f220f..c18520a 100644
--- a/lib/polyjuice.js
+++ b/lib/polyjuice.js
@@ -8,10 +8,12 @@
 
 var jshint = require('./polyjuice/jshint')
 var jscs = require('./polyjuice/jscs')
+var eslint = require('./polyjuice/eslint')
 
 module.exports.from = {
   jshint: jshint,
-  jscs: jscs
+  jscs: jscs,
+  eslint: eslint
 }
 
 module.exports.to = {
@@ -24,5 +26,17 @@ module.exports.to = {
     }
 
     return jshint_json
+  },
+
+  jscs: function (eslint_src, log) {
+    var results = eslint(eslint_src, log)
+
+    return results.jscs || {}
+  },
+
+  jshint: function (eslint_src, log) {
+    var results = eslint(eslint_src, log)
+
+    return results.jshint || {}
   }
 }
diff --git a/lib/polyjuice/eslint.js b/lib/polyjuice/eslint.js
new file mode 100644
index 0000000..72f81d5
--- /dev/null
+++ b/lib/polyjuice/eslint.js
@@ -0,0 +1,22 @@
+/*
+ * Polyjuice
+ * Copyright(c) 2015 Breno Lima de Freitas <brenolimadefreitas@gmail.com>
+ * MIT Licensed
+ */
+
+'use strict'
+
+var fs = require('fs')
+var stripJsonComments = require('strip-json-comments')
+var dictionary = require('../dictionaries/eslint')
+var interpreter = require('./interpreter')
+var truth_tbl = require('../assets/jshint_jscs_truth_tbl.json')
+
+module.exports = function (filename, log, encoding) {
+  encoding = encoding || 'utf8'
+
+  var json = stripJsonComments(fs.readFileSync(filename, encoding))
+  var data = JSON.parse(json)
+
+  return interpreter(dictionary, data.rules, truth_tbl, log)
+}
diff --git a/lib/polyjuice/interpreter.js b/lib/polyjuice/interpreter.js
index 9d20e88..9aa2c89 100644
--- a/lib/polyjuice/interpreter.js
+++ b/lib/polyjuice/interpreter.js
@@ -10,7 +10,7 @@ var Reader = require('./reader')
 
 module.exports = function (dictionary, json_data, truth_tbl, discarded) {
   var output = {}
-  
+
   discarded = discarded || []
 
   var fn = function (input) {
@@ -26,12 +26,22 @@ module.exports = function (dictionary, json_data, truth_tbl, discarded) {
   for (var name in json_data) {
     var tuple = reader.get(name, json_data[name])
 
+    var target = reader.target(name)
+
     if (Object.keys(tuple).length === 0) {
       discarded.push(name)
     }
 
+    var reference = output
+
+    if (target) {
+      output[target] = output[target] || {}
+
+      reference = output[target]
+    }
+
     for (var newRule in tuple) {
-      output[newRule] = tuple[newRule]
+      reference[newRule] = tuple[newRule]
     }
   }
 
diff --git a/lib/polyjuice/jscs.js b/lib/polyjuice/jscs.js
index 57ee99c..e776c0e 100644
--- a/lib/polyjuice/jscs.js
+++ b/lib/polyjuice/jscs.js
@@ -8,7 +8,6 @@
 
 var fs = require('fs')
 var stripJsonComments = require('strip-json-comments')
-var stripJsonComments = require('strip-json-comments')
 var dictionary = require('../dictionaries/jscs')
 var interpreter = require('./interpreter')
 var truth_tbl = require('../assets/eslint_truth_tbl.json')
diff --git a/lib/polyjuice/reader.js b/lib/polyjuice/reader.js
index 0b047d9..993a9de 100644
--- a/lib/polyjuice/reader.js
+++ b/lib/polyjuice/reader.js
@@ -12,6 +12,12 @@ module.exports = function (dictionary, truth_fn, truth_tbl, current) {
 
   var that = this
 
+  this.target = function (key) {
+    var read = that.dictionary[key]
+
+    return read ? read.target : null
+  }
+
   this.get = function (key, value) {
     var read = that.dictionary[key]
 
@@ -19,8 +25,8 @@ module.exports = function (dictionary, truth_fn, truth_tbl, current) {
       return {}
     }
 
-    if (typeof read === 'function') {
-      return read(value)
+    if (typeof read === 'function' || typeof read.eval === 'function') {
+      return (read.eval || read)(value)
     }
 
     if (typeof read === 'string') {
@@ -40,7 +46,7 @@ module.exports = function (dictionary, truth_fn, truth_tbl, current) {
       return tmp
     }
 
-    if (!read.name) {
+    if (!read.name && !read.eval) {
       throw new Error('Missing rule name')
     }
 
diff --git a/package.json b/package.json
index 35b6273..f1c8215 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
 {
   "name": "polyjuice",
-  "version": "1.1.2",
-  "description": "A utility to convert JSHint files into ESLint files",
+  "version": "1.1.3",
+  "description": "A utility to converts JSHint and JSCS files into ESLint and vice-versa",
   "main": "./index",
   "scripts": {
     "test": "npm run lint && npm run mocha",


Test patch:
diff --git a/test/dictionaries/eslint.test.js b/test/dictionaries/eslint.test.js
new file mode 100644
index 0000000..e160b23
--- /dev/null
+++ b/test/dictionaries/eslint.test.js
@@ -0,0 +1,575 @@
+var eslint = require('../../lib/dictionaries/eslint')
+
+var getFn = caller(eslint)
+
+describe('eslint', function () {
+  it('converts comma-dangle correctly', function () {
+    var fn = getFn('comma-dangle')
+
+    expect(fn(2)).to.eql(
+      { disallowTrailingComma: true }
+    )
+
+    expect(fn([2, 'never'])).to.eql(
+      { disallowTrailingComma: true }
+    )
+
+    expect(fn([2, 'always'])).to.eql(
+      { requireTrailingComma: true }
+    )
+  })
+
+  it('converts no-console correctly', function () {
+    var fn = getFn('no-console')
+    var fnContext = getFn('no-console', ['x'])
+
+    expect(fn()).to.eql(['-console'])
+    expect(fnContext()).to.eql(['x', '-console'])
+  })
+
+  it('converts complexity correctly', function () {
+    var fn = getFn('complexity')
+
+    expect(fn([2, 10])).to.eql(10)
+  })
+
+  it('converts curly correctly', function () {
+    var fn = getFn('curly')
+
+    expect(fn([2, 'multi-line'])).to.eql({ disallowCurlyBraces: true })
+    expect(fn([2, 'all'])).to.eql({ requireCurlyBraces: true })
+  })
+
+  it('converts dot-notation correctly', function () {
+    var fn = getFn('dot-notation')
+
+    expect(fn(2)).to.eql(true)
+    expect(fn([2, { 'allowPattern': '^[a-z]+(_[a-z]+)+$' }])).to.eql({
+      allExcept: ['snake_case']
+    })
+    expect(fn([2, {
+      'allowKeywords': true,
+      'allowPattern': '^[a-z]+(_[a-z]+)+$'
+    }])).to.eql({
+      allExcept: ['snake_case', 'keywords']
+    })
+  })
+
+  it('converts no-implicit-coercion correctly', function () {
+    var fn = getFn('no-implicit-coercion')
+
+    expect(fn([2, {'boolean': true, 'number': true, 'string': true}])).to.eql(
+      ['boolean', 'numeric', 'string']
+    )
+  })
+
+  it('converts no-warning-comments correctly', function () {
+    var fn = getFn('no-warning-comments')
+
+    expect(fn(
+      [2, { 'terms': ['todo', 'fixme', 'any'], 'location': 'anywhere' }]
+    )).to.eql(['todo', 'fixme', 'any'])
+
+    expect(fn(
+      [1, { 'location': 'anywhere' }]
+    )).to.eql([])
+  })
+
+  it('converts no-with correctly', function () {
+    var fn = getFn('no-with')
+    var fnContext = getFn('no-with', ['x'])
+
+    expect(fn()).to.eql(['with'])
+    expect(fnContext()).to.eql(['x', 'with'])
+  })
+
+  it('converts yoda correctly', function () {
+    var fn = getFn('yoda')
+
+    expect(fn([2, 'always'])).to.eql({
+      requireYodaConditions: true
+    })
+
+    expect(fn([2, 'never'])).to.eql({
+      disallowYodaConditions: true
+    })
+  })
+
+  it('converts strict correctly', function () {
+    var fn = getFn('strict')
+
+    expect(fn([2, 'function'])).to.eql({
+      strict: true
+    })
+
+    expect(fn(0)).to.eql({
+      globalstrict: true
+    })
+  })
+
+  it('converts no-shadow correctly', function () {
+    var fn = getFn('no-shadow')
+
+    expect(fn(2)).to.eql({ shadow: false })
+    expect(fn(0)).to.eql({ shadow: true })
+    expect(fn([2, {}])).to.eql({ shadow: 'outer' })
+  })
+
+  it('converts array-bracket-spacing correctly', function () {
+    var fn = getFn('array-bracket-spacing')
+
+    expect(fn([2, 'never'])).to.eql({
+      disallowSpacesInsideArrayBrackets: true,
+      disallowSpacesInsideObjectBrackets: true
+    })
+
+    expect(fn([2, 'always'])).to.eql({
+      requireSpacesInsideArrayBrackets: true,
+      requireSpacesInsideObjectBrackets: true
+    })
+
+    var result = fn([2, 'never', {
+      'singleValue': true,
+      'objectsInArrays': true,
+      'arraysInArrays': true
+    }])
+
+    expect(result.disallowSpacesInsideArrayBrackets.allExcept).to.eql(
+      ['{', '}', '[', ']']
+    )
+
+    expect(result.disallowSpacesInsideObjectBrackets.allExcept).to.eql(
+      ['{', '}', '[', ']']
+    )
+  })
+
+  it('converts brace-style correctly', function () {
+    var fn = getFn('brace-style')
+
+    var spacedKeywords = ['do', 'for', 'if', 'else', 'switch', 'case', 'try',
+    'catch', 'finally', 'void', 'while', 'with', 'return', 'typeof',
+    'function']
+
+    expect(fn([2, 'stroustrup', { 'allowSingleLine': true }])).to.eql({
+      requireKeywordsOnNewLine: spacedKeywords
+    })
+
+    expect(fn(2)).to.eql({
+      disallowKeywordsOnNewLine: spacedKeywords
+    })
+  })
+
+  it('converts camelcase correctly', function () {
+    var fn = getFn('camelcase')
+
+    expect(fn([2, {properties: 'always'}])).to.eql(false)
+    expect(fn([2, {properties: 'never'}])).to.eql(true)
+  })
+
+  it('converts comma-spacing correctly', function () {
+    var fn = getFn('comma-spacing')
+
+    expect(fn([2, {'before': false}])).to.eql({
+      requireSpaceBeforeComma: true
+    })
+
+    expect(fn([2, {'before': true}])).to.eql({
+      disallowSpaceBeforeComma: true
+    })
+  })
+
+  it('converts comma-style correctly', function () {
+    var fn = getFn('comma-style')
+
+    expect(fn([1, 'first'])).to.eql({
+      disallowCommaBeforeLineBreak: true
+    })
+
+    expect(fn([1, 'all'])).to.eql({
+      requireCommaBeforeLineBreak: true
+    })
+  })
+
+  it('converts consistent-this correctly', function () {
+    var fn = getFn('consistent-this')
+
+    expect(fn([2, 'x'])).to.eql('x')
+    expect(fn([2, ['x', 'y']])).to.eql(['x', 'y'])
+  })
+
+  it('converts func-names correctly', function () {
+    var fn = getFn('func-names')
+
+    expect(fn(0)).to.eql({})
+
+    expect(fn(2)).to.eql({
+      requireNamedUnassignedFunctions: true,
+      disallowAnonymousFunctions: true
+    })
+  })
+
+  it('converts id-match correctly', function () {
+    var fn = getFn('id-match')
+
+    expect(fn([2, '^(a|b|c)$', {}])).to.eql(['a', 'b', 'c'])
+    expect(fn([2, '^[a-z]+([A-Z][a-z]+)*$', {}])).to.eql(['a-z]+([A-Z][a-z]+)'])
+  })
+
+  it('converts indent correctly', function () {
+    var fn = getFn('indent')
+
+    expect(fn(2)).to.eql(4)
+    expect(fn([2, 'tab'])).to.eql('\t')
+    expect(fn([2, 2])).to.eql(2)
+  })
+
+  it('converts key-spacing correctly', function () {
+    var fn = getFn('key-spacing')
+
+    expect(fn([2, { 'align': 'colon' }])).to.eql({
+      requireAlignedObjectValues: true,
+      disallowSpaceAfterObjectKeys: {
+        allExcept: ['aligned']
+      }
+    })
+
+    expect(fn(
+      [2, { 'align': 'colon', 'beforeColon': true, 'afterColon': false }]
+    )).to.eql({
+      requireAlignedObjectValues: true,
+      disallowSpaceAfterObjectKeys: {
+        allExcept: ['aligned']
+      }
+    })
+
+    expect(fn([2, {'beforeColon': true, 'afterColon': true}])).to.eql({
+      requireSpaceAfterObjectKeys: true,
+      requireSpaceBeforeObjectValues: true
+    })
+
+    expect(fn([2, {'beforeColon': false, 'afterColon': false}])).to.eql({
+      disallowSpaceBeforeObjectValues: true,
+      disallowSpaceAfterObjectKeys: true
+    })
+
+    expect(fn([2, {'beforeColon': true, 'afterColon': false}])).to.eql({
+      requireSpaceAfterObjectKeys: true,
+      disallowSpaceBeforeObjectValues: true
+    })
+  })
+
+  it('converts linebreak-style correctly', function () {
+    var fn = getFn('linebreak-style')
+
+    expect(fn([2, 'windows'])).to.eql('CLRF')
+    expect(fn([2, 'unix'])).to.eql('LF')
+  })
+
+  it('converts lines-around-comment correctly', function () {
+    var fn = getFn('lines-around-comment')
+
+    var result = {
+      requirePaddingNewLinesBeforeLineComments: true,
+      requireSpaceAfterLineComment: true
+    }
+
+    expect(fn(2)).to.eql(result)
+    expect(fn([2, 'x'])).to.eql(result)
+    expect(fn([0])).to.eql({})
+  })
+
+  it('converts new-cap correctly', function () {
+    var fn = getFn('new-cap')
+
+    expect(fn(2)).to.eql(true)
+
+    expect(fn([2, {'capIsNewExceptions': ['Person']}])).to.eql({
+      allExcept: ['Person']
+    })
+
+    expect(fn([2, {'capIsNewExceptions': ['foo.Person']}])).to.eql({
+      allExcept: ['foo.Person']
+    })
+  })
+
+  it('converts newline-after-var correctly', function () {
+    var fn = getFn('newline-after-var')
+
+    expect(fn(0)).to.eql({})
+
+    expect(fn([1, 'always'])).to.eql({
+      requireLineBreakAfterVariableAssignment: true,
+      requirePaddingNewLineAfterVariableDeclaration: true
+    })
+
+    expect(fn(2)).to.eql({
+      requireLineBreakAfterVariableAssignment: true,
+      requirePaddingNewLineAfterVariableDeclaration: true
+    })
+  })
+
+  it('converts no-trailing-spaces correctly', function () {
+    var fn = getFn('no-trailing-spaces')
+
+    expect(fn([2])).to.eql(true)
+
+    expect(fn([2, { 'skipBlankLines': true }])).to.eql('ignoreEmptyLines')
+  })
+
+  it('converts one-var correctly', function () {
+    var fn = getFn('one-var')
+
+    expect(fn([2, 'never'])).to.eql({
+      disallowMultipleVarDecl: true,
+      requireMultipleVarDecl: true
+    })
+
+    expect(fn([1, 'never'])).to.eql({
+      disallowMultipleVarDecl: true,
+      requireMultipleVarDecl: true
+    })
+
+    expect(fn([2])).to.eql({ disallowMultipleVarDecl: true })
+
+    expect(fn(2)).to.eql({ disallowMultipleVarDecl: true })
+
+    expect(fn(0)).to.eql({})
+  })
+
+  it('converts operator-linebreak correctly', function () {
+    var fn = getFn('operator-linebreak')
+
+    expect(fn([2, 'before'])).to.eql(
+      { disallowOperatorBeforeLineBreak: true }
+    )
+
+    expect(fn([2, 'after'])).to.eql(
+      { requireOperatorBeforeLineBreak: true }
+    )
+
+    expect(fn(0)).to.eql({})
+  })
+
+  it('converts padded-blocks correctly', function () {
+    var fn = getFn('padded-blocks')
+
+    expect(fn([2, 'never'])).to.eql(
+      { disallowPaddingNewlinesInBlocks: true }
+    )
+
+    expect(fn([2, 'always'])).to.eql(
+      { requirePaddingNewlinesInBlocks: true }
+    )
+
+    expect(fn(0)).to.eql({})
+  })
+
+  it('converts quote-props correctly', function () {
+    var fn = getFn('quote-props')
+
+    expect(fn([2, 'as-needed'])).to.eql({
+      disallowQuotedKeysInObjects: true
+    })
+
+    expect(fn([2, 'always'])).to.eql({
+      requireQuotedKeysInObjects: true
+    })
+
+    expect(fn([2, 'as-needed', { 'keywords': true }])).to.eql({
+      disallowQuotedKeysInObjects: 'allButReserved'
+    })
+
+    expect(fn(2)).to.eql({
+      requireQuotedKeysInObjects: true
+    })
+
+    expect(fn(0)).to.eql({})
+  })
+
+  it('converts quotes correctly', function () {
+    var fn = getFn('quotes')
+
+    expect(fn([2, 'backtick'])).to.eql({
+      requireTemplateStrings: true
+    })
+
+    expect(fn([2, 'double'])).to.eql({
+      validateQuoteMarks: '"'
+    })
+
+    expect(fn([2, 'single'])).to.eql({
+      validateQuoteMarks: '\''
+    })
+
+    expect(fn([2, 'single', 'avoid-escape'])).to.eql({
+      validateQuoteMarks: {
+        mark: '\'',
+        escape: true
+      }
+    })
+
+    expect(fn(2)).to.eql({
+      validateQuoteMarks: '\''
+    })
+
+    expect(fn(0)).to.eql({})
+  })
+
+  it('converts semi correctly', function () {
+    var fn = getFn('semi')
+
+    expect(fn([2, 'always'])).to.eql({ requireSemicolons: true })
+
+    expect(fn([2, 'never'])).to.eql({ disallowSemicolons: true })
+
+    expect(fn([0])).to.eql({})
+  })
+
+  it('converts space-after-keywords correctly', function () {
+    var fn = getFn('space-after-keywords')
+    var fnContext = getFn('space-after-keywords', ['a', 'b'])
+
+    var keywords = ['do', 'for', 'if', 'else', 'switch', 'try', 'catch',
+      'finally', 'void', 'while', 'with', 'typeof', 'function']
+
+    expect(fn(0)).to.eql({})
+
+    expect(fn([2, 'always'])).to.eql({
+      requireSpaceAfterKeywords: keywords
+    })
+
+    expect(fn([2, 'never'])).to.eql({
+      disallowSpaceAfterKeywords: keywords
+    })
+
+    expect(fnContext([2, 'always'])).to.eql({
+      requireSpaceAfterKeywords: ['a', 'b'].concat(keywords)
+    })
+
+    expect(fnContext([2, 'never'])).to.eql({
+      disallowSpaceAfterKeywords: ['a', 'b'].concat(keywords)
+    })
+  })
+
+  it('converts space-before-blocks correctly', function () {
+    var fn = getFn('space-before-blocks')
+
+    expect(fn([2, 'always'])).to.eql({
+      requireSpaceBeforeBlockStatements: true
+    })
+
+    expect(fn([2, 'never'])).to.eql({
+      disallowSpaceBeforeBlockStatements: true
+    })
+
+    expect(fn([0])).to.eql({})
+  })
+
+  it('converts space-before-function-paren correctly', function () {
+    var fn = getFn('space-before-function-paren')
+
+    expect(fn([2, 'always'])).to.eql({
+      requireSpacesInFunctionDeclaration: true,
+      requireSpacesInFunctionExpression: true
+    })
+
+    expect(fn([2, 'never'])).to.eql({
+      disallowSpacesInFunctionDeclaration: true,
+      disallowSpacesInFunctionExpression: true,
+      disallowSpacesInFunction: true,
+      disallowSpacesInNamedFunctionExpression: true
+    })
+
+    expect(fn([0])).to.eql({})
+  })
+
+  it('converts space-in-parens correctly', function () {
+    var fn = getFn('space-in-parens')
+
+    expect(fn([2, 'always'])).to.eql({
+      requireSpacesInsideParentheses: 'all'
+    })
+
+    expect(fn([2, 'never'])).to.eql({
+      disallowSpacesInsideParentheses: true
+    })
+
+    expect(fn(0)).to.eql({})
+
+    expect(fn([2, 'never', { exceptions: ['[]'] }])).to.eql({
+      disallowSpacesInsideParentheses: { only: [ '[', ']' ] }
+    })
+
+    expect(fn([2, 'always', { exceptions: ['{}'] }])).to.eql({
+      requireSpacesInsideParentheses: {
+        all: true,
+        except: [ '{', '}' ]
+      }
+    })
+  })
+
+  it('converts space-return-throw-case correctly', function () {
+    var fn = getFn('space-return-throw-case')
+    var fnContext = getFn('space-return-throw-case', ['a', 'b'])
+
+    var keywords = ['return', 'throw', 'case']
+
+    expect(fn(0)).to.eql({})
+
+    expect(fn([2, 'always'])).to.eql({
+      requireSpaceAfterKeywords: keywords
+    })
+
+    expect(fn([2, 'never'])).to.eql({
+      disallowSpaceAfterKeywords: keywords
+    })
+
+    expect(fnContext([2, 'always'])).to.eql({
+      requireSpaceAfterKeywords: ['a', 'b'].concat(keywords)
+    })
+
+    expect(fnContext([2, 'never'])).to.eql({
+      disallowSpaceAfterKeywords: ['a', 'b'].concat(keywords)
+    })
+  })
+
+  it('converts space-unary-ops correctly', function () {
+    var fn = getFn('space-unary-ops')
+
+    expect(fn([2])).to.eql({ requireSpaceAfterPrefixUnaryOperators: true })
+
+    expect(fn([0])).to.eql({ disallowSpaceAfterPrefixUnaryOperators: true })
+  })
+
+  it('converts arrow-parens correctly', function () {
+    var fn = getFn('arrow-parens')
+
+    expect(fn([2])).to.eql({ requireParenthesesAroundArrowParam: true })
+
+    expect(fn([0])).to.eql({ disallowParenthesesAroundArrowParam: true })
+  })
+
+  it('converts max-depth correctly', function () {
+    var fn = getFn('max-depth')
+
+    expect(fn([2, 42])).to.eql(42)
+  })
+
+  it('converts max-len correctly', function () {
+    var fn = getFn('max-len')
+
+    expect(fn([2, 42])).to.eql(42)
+  })
+
+  it('converts max-params correctly', function () {
+    var fn = getFn('max-params')
+
+    expect(fn([2, 42])).to.eql(42)
+  })
+
+  it('converts max-statements correctly', function () {
+    var fn = getFn('max-statements')
+
+    expect(fn([2, 42])).to.eql(42)
+  })
+})
diff --git a/test/dictionaries/jscs.test.js b/test/dictionaries/jscs.test.js
index 8b53c72..dfaa4df 100644
--- a/test/dictionaries/jscs.test.js
+++ b/test/dictionaries/jscs.test.js
@@ -157,9 +157,9 @@ describe('jscs', function () {
       [2, 'never', {}]
     )
 
-    expect(fn(true)).to.eql([2, 'always', {'singleValue': true}])
+    expect(fn(true)).to.eql([2, 'never', {'singleValue': true}])
 
-    expect(fn('nested')).to.eql([2, 'always', {
+    expect(fn('nested')).to.eql([2, 'never', {
       'singleValue': true,
       'arraysInArrays': true
     }])
@@ -174,9 +174,9 @@ describe('jscs', function () {
       [2, 'never', {}]
     )
 
-    expect(fn(true)).to.eql([2, 'always', {'singleValue': true}])
+    expect(fn(true)).to.eql([2, 'never', {'singleValue': true}])
 
-    expect(fn('nested')).to.eql([2, 'always', {
+    expect(fn('nested')).to.eql([2, 'never', {
       'singleValue': true,
       'objectsInArrays': true
     }])
diff --git a/test/mocha.init.js b/test/mocha.init.js
index 8bd75cc..c4bbfa0 100644
--- a/test/mocha.init.js
+++ b/test/mocha.init.js
@@ -6,10 +6,12 @@ global.expect = chai.expect
 
 global.caller = function (lib) {
   var result = function (name, scope) {
-    var fn = (typeof lib[name] === 'function') ? lib[name] : lib[name].truthy
+    var fn = typeof lib[name] === 'function'
+             ? lib[name]
+             : (lib[name].truthy || lib[name].test || lib[name].eval)
 
-    if (scope !== undefined) {
-      return caller(lib)(name).bind({ value: scope })
+    if (scope) {
+      return fn.bind({ value: scope })
     }
 
     return fn