Repository: eslint/eslint
Pull request #8611 (2017-05-16 07:44:04)

Chore: Fix lgtm alerts.
**What is the purpose of this pull request? (put an "X" next to item)**

[ ] Documentation update
[ ] Bug fix ([template](https://raw.githubusercontent.com/eslint/eslint/master/templates/bug-report.md))
[ ] New rule ([template](https://raw.githubusercontent.com/eslint/eslint/master/templates/rule-proposal.md))
[ ] Changes an existing rule ([template](https://raw.githubusercontent.com/eslint/eslint/master/templates/rule-change-proposal.md))
[ ] Add autofixing to a rule
[ ] Add a CLI option
[ ] Add something to the core
[ X ] Other, please explain: Code clean-up.

<!--
    If the item you've checked above has a template, please paste the template questions below and answer them. (If this pull request is addressing an issue, you can just paste a link to the issue here instead.)
-->

<!--
    Please ensure your pull request is ready:

    - Read the pull request guide (http://eslint.org/docs/developer-guide/contributing/pull-requests)
    - Include tests for this change
    - Update documentation for this change (if appropriate)
-->

<!--
    The following is required for all pull requests:
-->

**What changes did you make? (Give an overview)**

Fixed a few instances of dead/useless code as identified by [lgtm](https://lgtm.com), see https://lgtm.com/projects/g/eslint/eslint/alerts/ for details.

**Is there anything you'd like reviewers to focus on?**

Nothing in particular.

eslintbot: Thanks for the pull request, @xiemaisi! I took a look to make sure it's ready for merging and found some changes are needed:


* The commit summary needs to begin with a tag (such as `Fix:` or `Update:`). Please check out our [guide](http://eslint.org/docs/developer-guide/contributing/pull-requests#step-2-make-your-changes) for how to properly format your commit summary and [update](http://eslint.org/docs/developer-guide/contributing/pull-requests#updating-the-commit-message) it on this pull request.


Can you please update the pull request to address these?

(More information can be found in our [pull request guide](http://eslint.org/docs/developer-guide/contributing/pull-requests).)
jsf-clabot: [![CLA assistant check](https://cla.js.foundation/pull/badge/not_signed)](https://cla.js.foundation/eslint/eslint?pullRequest=8611) <br/>Thank you for your submission, we really appreciate it. Like many open source projects, we ask that you sign our [Contributor License Agreement](https://cla.js.foundation/eslint/eslint?pullRequest=8611) before we can accept your contribution.<br/>
mention-bot: @xiemaisi, thanks for your PR! By analyzing the history of the files in this pull request, we identified @kaicataldo, @vitorbal and @mysticatea to be potential reviewers.
eslintbot: Thanks for the pull request, @xiemaisi! I took a look to make sure it's ready for merging and found some changes are needed:


* The commit summary needs to begin with a tag (such as `Fix:` or `Update:`). Please check out our [guide](http://eslint.org/docs/developer-guide/contributing/pull-requests#step-2-make-your-changes) for how to properly format your commit summary and [update](http://eslint.org/docs/developer-guide/contributing/pull-requests#updating-the-commit-message) it on this pull request.


Can you please update the pull request to address these?

(More information can be found in our [pull request guide](http://eslint.org/docs/developer-guide/contributing/pull-requests).)
eslintbot: LGTM
eslintbot: LGTM
Aladdin-ADD: lgtm. is there a way to add it to our build process? @xiemaisi 
samlanning: @Aladdin-ADD yep, you can set up Pull Request integration on this page: https://lgtm.com/projects/g/eslint/eslint/ci/

It's already continuously checking the default branch for now, so you can go and check the alerts page that @xiemaisi linked to whenever. We have plans for creating badges you can add to your README file at some point in the near future.

Patch:
diff --git a/docs/rules/indent.md b/docs/rules/indent.md
index 4f32c3367..daa17c8af 100644
--- a/docs/rules/indent.md
+++ b/docs/rules/indent.md
@@ -530,11 +530,10 @@ Examples of **incorrect** code for this rule with the default `4, { "flatTernary
 ```js
 /*eslint indent: ["error", 4, { "flatTernaryExpressions": false }]*/
 
-foo
-    ? bar
-    : baz
-    ? qux
-    : boop;
+var a =
+    foo ? bar :
+    baz ? qux :
+    boop;
 ```
 
 Examples of **correct** code for this rule with the default `4, { "flatTernaryExpressions": false }` option:
@@ -542,11 +541,10 @@ Examples of **correct** code for this rule with the default `4, { "flatTernaryEx
 ```js
 /*eslint indent: ["error", 4, { "flatTernaryExpressions": false }]*/
 
-foo
-    ? bar
-    : baz
-        ? qux
-        : boop;
+var a =
+    foo ? bar :
+        baz ? qux :
+            boop;
 ```
 
 Examples of **incorrect** code for this rule with the `4, { "flatTernaryExpressions": true }` option:
@@ -554,11 +552,10 @@ Examples of **incorrect** code for this rule with the `4, { "flatTernaryExpressi
 ```js
 /*eslint indent: ["error", 4, { "flatTernaryExpressions": true }]*/
 
-foo
-    ? bar
-    : baz
-        ? qux
-        : boop;
+var a =
+    foo ? bar :
+        baz ? qux :
+            boop;
 ```
 
 Examples of **correct** code for this rule with the `4, { "flatTernaryExpressions": true }` option:
@@ -566,11 +563,10 @@ Examples of **correct** code for this rule with the `4, { "flatTernaryExpression
 ```js
 /*eslint indent: ["error", 4, { "flatTernaryExpressions": true }]*/
 
-foo
-    ? bar
-    : baz
-    ? qux
-    : boop;
+var a =
+    foo ? bar :
+    baz ? qux :
+    boop;
 ```
 
 
diff --git a/lib/cli.js b/lib/cli.js
index 640bd81ba..530bfbc42 100644
--- a/lib/cli.js
+++ b/lib/cli.js
@@ -17,7 +17,6 @@
 
 const fs = require("fs"),
     path = require("path"),
-    shell = require("shelljs"),
     options = require("./options"),
     CLIEngine = require("./cli-engine"),
     mkdirp = require("mkdirp"),
@@ -83,7 +82,7 @@ function printResults(engine, results, format, outputFile) {
         if (outputFile) {
             const filePath = path.resolve(process.cwd(), outputFile);
 
-            if (shell.test("-d", filePath)) {
+            if (fs.existsSync(filePath) && fs.statSync(filePath).isDirectory()) {
                 log.error("Cannot write to output file path, it is a directory: %s", outputFile);
                 return false;
             }
diff --git a/lib/code-path-analysis/code-path-state.js b/lib/code-path-analysis/code-path-state.js
index 3faff3ebb..a5adb554f 100644
--- a/lib/code-path-analysis/code-path-state.js
+++ b/lib/code-path-analysis/code-path-state.js
@@ -988,7 +988,7 @@ class CodePathState {
         switch (context.type) {
             case "WhileStatement":
             case "ForStatement":
-                choiceContext = this.popChoiceContext();
+                this.popChoiceContext();
                 makeLooped(
                     this,
                     forkContext.head,
diff --git a/lib/config/config-file.js b/lib/config/config-file.js
index c676413a9..36dd2a16f 100644
--- a/lib/config/config-file.js
+++ b/lib/config/config-file.js
@@ -13,7 +13,6 @@
 
 const fs = require("fs"),
     path = require("path"),
-    shell = require("shelljs"),
     ConfigOps = require("./config-ops"),
     validator = require("./config-validator"),
     Plugins = require("./plugins"),
@@ -596,7 +595,7 @@ module.exports = {
         for (let i = 0, len = CONFIG_FILES.length; i < len; i++) {
             const filename = path.join(directory, CONFIG_FILES[i]);
 
-            if (shell.test("-f", filename)) {
+            if (fs.existsSync(filename) && fs.statSync(filename).isFile()) {
                 return filename;
             }
         }
diff --git a/lib/ignored-paths.js b/lib/ignored-paths.js
index f69e1f61f..0d9152495 100644
--- a/lib/ignored-paths.js
+++ b/lib/ignored-paths.js
@@ -12,7 +12,6 @@
 const fs = require("fs"),
     path = require("path"),
     ignore = require("ignore"),
-    shell = require("shelljs"),
     pathUtil = require("./util/path-util");
 
 const debug = require("debug")("eslint:ignored-paths");
@@ -54,7 +53,7 @@ function findIgnoreFile(cwd) {
 
     const ignoreFilePath = path.resolve(cwd, ESLINT_IGNORE_FILENAME);
 
-    return shell.test("-f", ignoreFilePath) ? ignoreFilePath : "";
+    return fs.existsSync(ignoreFilePath) && fs.statSync(ignoreFilePath).isFile() ? ignoreFilePath : "";
 }
 
 /**
diff --git a/lib/rules/complexity.js b/lib/rules/complexity.js
index 14617bc35..e0313fa78 100644
--- a/lib/rules/complexity.js
+++ b/lib/rules/complexity.js
@@ -122,7 +122,7 @@ module.exports = {
 
             // Avoiding `default`
             if (node.test) {
-                increaseComplexity(node);
+                increaseComplexity();
             }
         }
 
@@ -136,7 +136,7 @@ module.exports = {
 
             // Avoiding &&
             if (node.operator === "||") {
-                increaseComplexity(node);
+                increaseComplexity();
             }
         }
 
diff --git a/lib/rules/indent-legacy.js b/lib/rules/indent-legacy.js
index 27b718e28..f686c18ee 100644
--- a/lib/rules/indent-legacy.js
+++ b/lib/rules/indent-legacy.js
@@ -322,7 +322,6 @@ module.exports = {
          * Get the actual indent of node
          * @param {ASTNode|Token} node Node to examine
          * @param {boolean} [byLastLine=false] get indent of node's last line
-         * @param {boolean} [excludeCommas=false] skip comma on start of line
          * @returns {Object} The node's indent. Contains keys `space` and `tab`, representing the indent of each character. Also
          contains keys `goodChar` and `badChar`, where `goodChar` is the amount of the user's desired indentation character, and
          `badChar` is the amount of the other indentation character.
@@ -623,7 +622,7 @@ module.exports = {
                 calleeNode.parent.type === "ArrayExpression")) {
 
                 // If function is part of array or object, comma can be put at left
-                indent = getNodeIndent(calleeNode, false, false).goodChar;
+                indent = getNodeIndent(calleeNode, false).goodChar;
             } else {
 
                 // If function is standalone, simple calculate indent
diff --git a/lib/rules/indent.js b/lib/rules/indent.js
index a385132de..7d43a1f01 100644
--- a/lib/rules/indent.js
+++ b/lib/rules/indent.js
@@ -128,7 +128,7 @@ class TokenInfo {
             if (!map.has(token.loc.start.line)) {
                 map.set(token.loc.start.line, token);
             }
-            if (!map.has(token.loc.end.line)) {
+            if (!map.has(token.loc.end.line) && sourceCode.text.slice(token.range[1] - token.loc.end.column, token.range[1]).trim()) {
                 map.set(token.loc.end.line, token);
             }
             return map;
@@ -639,12 +639,13 @@ module.exports = {
 
         /**
         * Check indentation for lists of elements (arrays, objects, function params)
-        * @param {Token[]} tokens list of tokens
         * @param {ASTNode[]} elements List of elements that should be offset
+        * @param {Token} startToken The start token of the list that element should be aligned against, e.g. '['
+        * @param {Token} endToken The end token of the list, e.g. ']'
         * @param {number|string} offset The amount that the elements should be offset
         * @returns {void}
         */
-        function addElementListIndent(tokens, elements, offset) {
+        function addElementListIndent(elements, startToken, endToken, offset) {
 
             /**
             * Gets the first token of a given element, including surrounding parentheses.
@@ -654,7 +655,7 @@ module.exports = {
             function getFirstToken(element) {
                 let token = sourceCode.getTokenBefore(element);
 
-                while (astUtils.isOpeningParenToken(token) && token !== tokens[0]) {
+                while (astUtils.isOpeningParenToken(token) && token !== startToken) {
                     token = sourceCode.getTokenBefore(token);
                 }
 
@@ -663,8 +664,12 @@ module.exports = {
 
             // Run through all the tokens in the list, and offset them by one indent level (mainly for comments, other things will end up overridden)
             // FIXME: (not-an-aardvark) This isn't performant at all.
-            offsets.setDesiredOffsets(tokens, tokens[0], offset === "first" ? 1 : offset);
-            offsets.matchIndentOf(tokens[0], tokens[tokens.length - 1]);
+            offsets.setDesiredOffsets(
+                sourceCode.getTokensBetween(startToken, endToken, { includeComments: true }),
+                startToken,
+                offset === "first" ? 1 : offset
+            );
+            offsets.matchIndentOf(startToken, endToken);
 
             // If the preference is "first" but there is no first element (e.g. sparse arrays w/ empty first slot), fall back to 1 level.
             if (offset === "first" && elements.length && !elements[0]) {
@@ -684,7 +689,7 @@ module.exports = {
                     const previousElement = elements[index - 1];
                     const firstTokenOfPreviousElement = previousElement && getFirstToken(previousElement);
 
-                    if (previousElement && previousElement.loc.end.line > tokens[0].loc.end.line) {
+                    if (previousElement && sourceCode.getLastToken(previousElement).loc.start.line > startToken.loc.end.line) {
                         offsets.matchIndentOf(firstTokenOfPreviousElement, getFirstToken(elements[index]));
                     }
                 }
@@ -710,16 +715,14 @@ module.exports = {
                 blockIndentLevel = 1;
             }
 
-            const tokens = getTokensAndComments(node);
-
             /*
              * For blocks that aren't lone statements, ensure that the opening curly brace
              * is aligned with the parent.
              */
             if (!astUtils.STATEMENT_LIST_PARENTS.has(node.parent.type)) {
-                offsets.matchIndentOf(sourceCode.getFirstToken(node.parent), tokens[0]);
+                offsets.matchIndentOf(sourceCode.getFirstToken(node.parent), sourceCode.getFirstToken(node));
             }
-            addElementListIndent(tokens, node.body, blockIndentLevel);
+            addElementListIndent(node.body, sourceCode.getFirstToken(node), sourceCode.getLastToken(node), blockIndentLevel);
         }
 
         /**
@@ -730,7 +733,7 @@ module.exports = {
         function addArrayOrObjectIndent(node) {
             const tokens = getTokensAndComments(node);
 
-            addElementListIndent(tokens, node.elements || node.properties, options[node.type]);
+            addElementListIndent(node.elements || node.properties, tokens[0], tokens[tokens.length - 1], options[node.type]);
         }
 
         /**
@@ -787,12 +790,11 @@ module.exports = {
             const nodeTokens = getTokensAndComments(node);
             const openingParenIndex = lodash.sortedIndexBy(nodeTokens, openingParen, token => token.range[0]);
             const closingParenIndex = lodash.sortedIndexBy(nodeTokens, closingParen, token => token.range[0]);
-            const paramTokens = nodeTokens.slice(openingParenIndex, closingParenIndex + 1);
 
-            parameterParens.add(paramTokens[0]);
-            parameterParens.add(paramTokens[paramTokens.length - 1]);
+            parameterParens.add(nodeTokens[openingParenIndex]);
+            parameterParens.add(nodeTokens[closingParenIndex]);
 
-            addElementListIndent(paramTokens, node.params, paramsIndent);
+            addElementListIndent(node.params, nodeTokens[openingParenIndex], nodeTokens[closingParenIndex], paramsIndent);
         }
 
         /**
@@ -831,14 +833,13 @@ module.exports = {
             } else {
                 openingParen = sourceCode.getLastToken(node, 1);
             }
-            const callExpressionTokens = getTokensAndComments(node);
-            const tokens = callExpressionTokens.slice(lodash.sortedIndexBy(callExpressionTokens, openingParen, token => token.range[0]));
+            const closingParen = sourceCode.getLastToken(node);
 
-            parameterParens.add(tokens[0]);
-            parameterParens.add(tokens[tokens.length - 1]);
+            parameterParens.add(openingParen);
+            parameterParens.add(closingParen);
             offsets.matchIndentOf(sourceCode.getLastToken(node.callee), openingParen);
 
-            addElementListIndent(tokens, node.arguments, options.CallExpression.arguments);
+            addElementListIndent(node.arguments, openingParen, closingParen, options.CallExpression.arguments);
         }
 
         /**
@@ -923,6 +924,23 @@ module.exports = {
             }
         }
 
+        /**
+         * Check whether the given token is the first token of a statement.
+         * @param {Token} token The token to check.
+         * @param {ASTNode} leafNode The expression node that the token belongs directly.
+         * @returns {boolean} `true` if the token is the first token of a statement.
+         */
+        function isFirstTokenOfStatement(token, leafNode) {
+            let node = leafNode;
+
+            while (node.parent && !node.parent.type.endsWith("Statement") && !node.parent.type.endsWith("Declaration")) {
+                node = node.parent;
+            }
+            node = node.parent;
+
+            return !node || node.range[0] === token.range[0];
+        }
+
         return {
             ArrayExpression: addArrayOrObjectIndent,
             ArrayPattern: addArrayOrObjectIndent,
@@ -966,7 +984,15 @@ module.exports = {
             ConditionalExpression(node) {
                 const tokens = getTokensAndComments(node);
 
-                if (!(node.parent.type === "ConditionalExpression" && options.flatTernaryExpressions)) {
+                // `flatTernaryExpressions` option is for the following style:
+                // var a =
+                //     foo > 0 ? bar :
+                //     foo < 0 ? baz :
+                //     /*else*/ qiz ;
+                if (!options.flatTernaryExpressions ||
+                    !astUtils.isTokenOnSameLine(node.test, node.consequent) ||
+                    isFirstTokenOfStatement(tokens[0], node)
+                ) {
                     offsets.setDesiredOffsets(tokens, tokens[0], 1);
                 }
             },
@@ -980,7 +1006,7 @@ module.exports = {
                     const closingCurlyIndex = lodash.sortedIndexBy(tokensInNode, closingCurly, token => token.range[0]);
 
                     // Indent the specifiers in `export {foo, bar, baz}`
-                    addElementListIndent(tokensInNode.slice(1, closingCurlyIndex + 1), node.specifiers, 1);
+                    addElementListIndent(node.specifiers, sourceCode.getFirstToken(node, { skip: 1 }), closingCurly, 1);
 
                     if (node.source) {
 
@@ -1028,9 +1054,8 @@ module.exports = {
                 if (node.specifiers.some(specifier => specifier.type === "ImportSpecifier")) {
                     const openingCurly = sourceCode.getFirstToken(node, astUtils.isOpeningBraceToken);
                     const closingCurly = sourceCode.getLastToken(node, astUtils.isClosingBraceToken);
-                    const specifierTokens = sourceCode.getTokensBetween(openingCurly, closingCurly, 1);
 
-                    addElementListIndent(specifierTokens, node.specifiers.filter(specifier => specifier.type === "ImportSpecifier"), 1);
+                    addElementListIndent(node.specifiers.filter(specifier => specifier.type === "ImportSpecifier"), openingCurly, closingCurly, 1);
                 }
 
                 const fromToken = sourceCode.getLastToken(node, token => token.type === "Identifier" && token.value === "from");
@@ -1044,7 +1069,7 @@ module.exports = {
 
             LogicalExpression: addBinaryOrLogicalExpressionIndent,
 
-            MemberExpression(node) {
+            "MemberExpression, JSXMemberExpression"(node) {
                 const firstNonObjectToken = sourceCode.getFirstTokenBetween(node.object, node.property, astUtils.isNotClosingParenToken);
                 const secondNonObjectToken = sourceCode.getTokenAfter(firstNonObjectToken);
 
@@ -1205,6 +1230,52 @@ module.exports = {
 
             "*:exit": checkForUnknownNode,
 
+            "JSXAttribute[value]"(node) {
+                const equalsToken = sourceCode.getFirstTokenBetween(node.name, node.value, token => token.type === "Punctuator" && token.value === "=");
+                const firstNameToken = sourceCode.getFirstToken(node.name);
+
+                offsets.setDesiredOffset(equalsToken, firstNameToken, 1);
+                offsets.setDesiredOffset(sourceCode.getFirstToken(node.value), firstNameToken, 1);
+            },
+
+            JSXElement(node) {
+                if (node.closingElement) {
+                    addElementListIndent(node.children, sourceCode.getFirstToken(node.openingElement), sourceCode.getFirstToken(node.closingElement), 1);
+                }
+            },
+
+            JSXOpeningElement(node) {
+                const firstToken = sourceCode.getFirstToken(node);
+                let closingToken;
+
+                if (node.selfClosing) {
+                    closingToken = sourceCode.getLastToken(node, { skip: 1 });
+                    offsets.matchIndentOf(closingToken, sourceCode.getLastToken(node));
+                } else {
+                    closingToken = sourceCode.getLastToken(node);
+                }
+                offsets.setDesiredOffsets(getTokensAndComments(node.name), sourceCode.getFirstToken(node));
+                addElementListIndent(node.attributes, firstToken, closingToken, 1);
+            },
+
+            JSXClosingElement(node) {
+                const firstToken = sourceCode.getFirstToken(node);
+
+                offsets.setDesiredOffsets(getTokensAndComments(node.name), firstToken, 1);
+                offsets.matchIndentOf(firstToken, sourceCode.getLastToken(node));
+            },
+
+            JSXExpressionContainer(node) {
+                const openingCurly = sourceCode.getFirstToken(node);
+                const firstExpressionToken = sourceCode.getFirstToken(node.expression);
+
+                if (firstExpressionToken) {
+                    offsets.setDesiredOffset(firstExpressionToken, openingCurly, 1);
+                }
+
+                offsets.matchIndentOf(openingCurly, sourceCode.getLastToken(node));
+            },
+
             "Program:exit"() {
                 addParensIndent(sourceCode.ast.tokens);
 
diff --git a/lib/rules/max-len.js b/lib/rules/max-len.js
index 137d79135..b693c8078 100644
--- a/lib/rules/max-len.js
+++ b/lib/rules/max-len.js
@@ -268,13 +268,13 @@ module.exports = {
                 // we iterate over comments in parallel with the lines
             let commentsIndex = 0;
 
-            const strings = getAllStrings(sourceCode);
+            const strings = getAllStrings();
             const stringsByLine = strings.reduce(groupByLineNumber, {});
 
-            const templateLiterals = getAllTemplateLiterals(sourceCode);
+            const templateLiterals = getAllTemplateLiterals();
             const templateLiteralsByLine = templateLiterals.reduce(groupByLineNumber, {});
 
-            const regExpLiterals = getAllRegExpLiterals(sourceCode);
+            const regExpLiterals = getAllRegExpLiterals();
             const regExpLiteralsByLine = regExpLiterals.reduce(groupByLineNumber, {});
 
             lines.forEach((line, i) => {
diff --git a/lib/rules/no-inner-declarations.js b/lib/rules/no-inner-declarations.js
index 7923972ab..e7d1b004e 100644
--- a/lib/rules/no-inner-declarations.js
+++ b/lib/rules/no-inner-declarations.js
@@ -58,7 +58,7 @@ module.exports = {
          * @returns {void}
          */
         function check(node) {
-            const body = nearestBody(node),
+            const body = nearestBody(),
                 valid = ((body.type === "Program" && body.distance === 1) ||
                     body.distance === 2);
 
diff --git a/lib/rules/no-irregular-whitespace.js b/lib/rules/no-irregular-whitespace.js
index 6747dd5c8..cfbdfd1a5 100644
--- a/lib/rules/no-irregular-whitespace.js
+++ b/lib/rules/no-irregular-whitespace.js
@@ -16,8 +16,8 @@ const astUtils = require("../ast-utils");
 // Constants
 //------------------------------------------------------------------------------
 
-const ALL_IRREGULARS = /[\f\v\u0085\u00A0\ufeff\u00a0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u200b\u202f\u205f\u3000\u2028\u2029]/;
-const IRREGULAR_WHITESPACE = /[\f\v\u0085\u00A0\ufeff\u00a0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u200b\u202f\u205f\u3000]+/mg;
+const ALL_IRREGULARS = /[\f\v\u0085\ufeff\u00a0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u200b\u202f\u205f\u3000\u2028\u2029]/;
+const IRREGULAR_WHITESPACE = /[\f\v\u0085\ufeff\u00a0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u200b\u202f\u205f\u3000]+/mg;
 const IRREGULAR_LINE_TERMINATORS = /[\u2028\u2029]/mg;
 const LINE_BREAK = astUtils.createGlobalLinebreakMatcher();
 
diff --git a/lib/rules/no-lone-blocks.js b/lib/rules/no-lone-blocks.js
index 95a5b334c..652812fba 100644
--- a/lib/rules/no-lone-blocks.js
+++ b/lib/rules/no-lone-blocks.js
@@ -94,13 +94,13 @@ module.exports = {
 
             ruleDef.VariableDeclaration = function(node) {
                 if (node.kind === "let" || node.kind === "const") {
-                    markLoneBlock(node);
+                    markLoneBlock();
                 }
             };
 
-            ruleDef.FunctionDeclaration = function(node) {
+            ruleDef.FunctionDeclaration = function() {
                 if (context.getScope().isStrict) {
-                    markLoneBlock(node);
+                    markLoneBlock();
                 }
             };
 
diff --git a/lib/rules/no-this-before-super.js b/lib/rules/no-this-before-super.js
index c8d5dc469..2a686ac72 100644
--- a/lib/rules/no-this-before-super.js
+++ b/lib/rules/no-this-before-super.js
@@ -89,7 +89,7 @@ module.exports = {
          */
         function isBeforeCallOfSuper() {
             return (
-                isInConstructorOfDerivedClass(funcInfo) &&
+                isInConstructorOfDerivedClass() &&
                 !funcInfo.codePath.currentSegments.every(isCalled)
             );
         }
@@ -206,7 +206,7 @@ module.exports = {
              * @returns {void}
              */
             onCodePathSegmentStart(segment) {
-                if (!isInConstructorOfDerivedClass(funcInfo)) {
+                if (!isInConstructorOfDerivedClass()) {
                     return;
                 }
 
@@ -230,7 +230,7 @@ module.exports = {
              * @returns {void}
              */
             onCodePathSegmentLoop(fromSegment, toSegment) {
-                if (!isInConstructorOfDerivedClass(funcInfo)) {
+                if (!isInConstructorOfDerivedClass()) {
                     return;
                 }
 
diff --git a/lib/rules/no-use-before-define.js b/lib/rules/no-use-before-define.js
index 6d01e3e10..bdff23934 100644
--- a/lib/rules/no-use-before-define.js
+++ b/lib/rules/no-use-before-define.js
@@ -250,7 +250,7 @@ module.exports = {
 
             ruleDefinition["ArrowFunctionExpression:exit"] = function(node) {
                 if (node.body.type !== "BlockStatement") {
-                    findVariables(node);
+                    findVariables();
                 }
             };
         } else {
diff --git a/lib/rules/switch-colon-spacing.js b/lib/rules/switch-colon-spacing.js
index 84820956e..400e85099 100644
--- a/lib/rules/switch-colon-spacing.js
+++ b/lib/rules/switch-colon-spacing.js
@@ -105,7 +105,7 @@ module.exports = {
 
         return {
             SwitchCase(node) {
-                const colonToken = getColonToken(node, sourceCode);
+                const colonToken = getColonToken(node);
                 const beforeToken = sourceCode.getTokenBefore(colonToken);
                 const afterToken = sourceCode.getTokenAfter(colonToken);
 
diff --git a/lib/util/glob-util.js b/lib/util/glob-util.js
index 4c21fc551..6a1f150a5 100644
--- a/lib/util/glob-util.js
+++ b/lib/util/glob-util.js
@@ -11,7 +11,6 @@
 const fs = require("fs"),
     path = require("path"),
     GlobSync = require("./glob"),
-    shell = require("shelljs"),
 
     pathUtil = require("./path-util"),
     IgnoredPaths = require("../ignored-paths");
@@ -64,7 +63,7 @@ function processPath(options) {
         let newPath = pathname;
         const resolvedPath = path.resolve(cwd, pathname);
 
-        if (shell.test("-d", resolvedPath)) {
+        if (fs.existsSync(resolvedPath) && fs.statSync(resolvedPath).isDirectory()) {
             newPath = pathname.replace(/[/\\]$/, "") + suffix;
         }
 
@@ -151,10 +150,10 @@ function listFilesToProcess(globPatterns, options) {
     globPatterns.forEach(pattern => {
         const file = path.resolve(cwd, pattern);
 
-        if (shell.test("-f", file)) {
+        if (fs.existsSync(file) && fs.statSync(file).isFile()) {
             const ignoredPaths = new IgnoredPaths(options);
 
-            addFile(fs.realpathSync(file), !shell.test("-d", file), ignoredPaths);
+            addFile(fs.realpathSync(file), true, ignoredPaths);
         } else {
 
             // regex to find .hidden or /.hidden patterns, but not ./relative or ../relative
diff --git a/lib/util/npm-util.js b/lib/util/npm-util.js
index ef1c0c629..4859fabc9 100644
--- a/lib/util/npm-util.js
+++ b/lib/util/npm-util.js
@@ -10,8 +10,8 @@
 //------------------------------------------------------------------------------
 
 const fs = require("fs"),
+    childProcess = require("child_process"),
     path = require("path"),
-    shell = require("shelljs"),
     log = require("../logging");
 
 //------------------------------------------------------------------------------
@@ -29,13 +29,13 @@ function findPackageJson(startDir) {
     let dir = path.resolve(startDir || process.cwd());
 
     do {
-        const pkgfile = path.join(dir, "package.json");
+        const pkgFile = path.join(dir, "package.json");
 
-        if (!shell.test("-f", pkgfile)) {
+        if (!fs.existsSync(pkgFile) || !fs.statSync(pkgFile).isFile()) {
             dir = path.join(dir, "..");
             continue;
         }
-        return pkgfile;
+        return pkgFile;
     } while (dir !== path.resolve(dir, ".."));
     return null;
 }
@@ -53,7 +53,7 @@ function installSyncSaveDev(packages) {
     if (Array.isArray(packages)) {
         packages = packages.join(" ");
     }
-    shell.exec(`npm i --save-dev ${packages}`, { stdio: "inherit" });
+    childProcess.execSync(`npm i --save-dev ${packages}`, { stdio: "inherit", encoding: "utf8" });
 }
 
 /**
diff --git a/package.json b/package.json
index 7d0cab381..dee84c99b 100644
--- a/package.json
+++ b/package.json
@@ -63,7 +63,6 @@
     "pluralize": "^4.0.0",
     "progress": "^1.1.8",
     "require-uncached": "^1.0.3",
-    "shelljs": "^0.7.7",
     "strip-json-comments": "~2.0.1",
     "table": "^4.0.1",
     "text-table": "~0.2.0",
@@ -102,6 +101,7 @@
     "phantomjs-prebuilt": "^2.1.14",
     "proxyquire": "^1.7.11",
     "semver": "^5.3.0",
+    "shelljs": "^0.7.7",
     "shelljs-nodecli": "~0.1.1",
     "sinon": "^2.0.0",
     "temp": "^0.8.3",


Test patch:
diff --git a/tests/lib/rules/indent.js b/tests/lib/rules/indent.js
index b41ee8ab9..c675c0f96 100644
--- a/tests/lib/rules/indent.js
+++ b/tests/lib/rules/indent.js
@@ -67,7 +67,7 @@ function unIndent(strings) {
     return lines.map(line => line.slice(minLineIndent)).join("\n");
 }
 
-const ruleTester = new RuleTester();
+const ruleTester = new RuleTester({ parserOptions: { ecmaVersion: 6, ecmaFeatures: { jsx: true } } });
 
 ruleTester.run("indent", rule, {
     valid: [
@@ -124,8 +124,7 @@ ruleTester.run("indent", rule, {
                     );
                 }
             `,
-            options: [4],
-            parserOptions: { ecmaVersion: 6 }
+            options: [4]
         },
         {
             code: unIndent`
@@ -147,8 +146,7 @@ ruleTester.run("indent", rule, {
                         return 100 * x;
                     });
             `,
-            options: [4],
-            parserOptions: { ecmaVersion: 6 }
+            options: [4]
         },
         {
             code: unIndent`
@@ -257,8 +255,7 @@ ruleTester.run("indent", rule, {
                   expect(true).toBe(true);
                 });
             `,
-            options: [2],
-            parserOptions: { ecmaVersion: 6 }
+            options: [2]
         },
         {
             code: unIndent`
@@ -326,8 +323,7 @@ ruleTester.run("indent", rule, {
                   console.log('hi');
                   return true;};
             `,
-            options: [2, { VariableDeclarator: 1, SwitchCase: 1 }],
-            parserOptions: { ecmaVersion: 6 }
+            options: [2, { VariableDeclarator: 1, SwitchCase: 1 }]
         },
         {
             code: unIndent`
@@ -766,16 +762,14 @@ ruleTester.run("indent", rule, {
                 let geometry,
                     rotate;
             `,
-            options: [2, { VariableDeclarator: 2 }],
-            parserOptions: { ecmaVersion: 6 }
+            options: [2, { VariableDeclarator: 2 }]
         },
         {
             code: unIndent`
                 const geometry = 2,
                     rotate = 3;
             `,
-            options: [2, { VariableDeclarator: 2 }],
-            parserOptions: { ecmaVersion: 6 }
+            options: [2, { VariableDeclarator: 2 }]
         },
         {
             code: unIndent`
@@ -816,8 +810,7 @@ ruleTester.run("indent", rule, {
                     index;
                 });
             `,
-            options: [4],
-            parserOptions: { ecmaVersion: 6 }
+            options: [4]
         },
         {
             code: unIndent`
@@ -826,8 +819,7 @@ ruleTester.run("indent", rule, {
                     return index;
                 });
             `,
-            options: [4],
-            parserOptions: { ecmaVersion: 6 }
+            options: [4]
         },
         {
             code: unIndent`
@@ -835,8 +827,7 @@ ruleTester.run("indent", rule, {
                     index;
                 });
             `,
-            options: [4],
-            parserOptions: { ecmaVersion: 6 }
+            options: [4]
         },
         {
             code: unIndent`
@@ -844,8 +835,7 @@ ruleTester.run("indent", rule, {
                     return index;
                 });
             `,
-            options: [4],
-            parserOptions: { ecmaVersion: 6 }
+            options: [4]
         },
         {
             code: unIndent`
@@ -854,8 +844,7 @@ ruleTester.run("indent", rule, {
                         baz
                     ]);
             `,
-            options: [4, { MemberExpression: 1 }],
-            parserOptions: { ecmaVersion: 6 }
+            options: [4, { MemberExpression: 1 }]
         },
         {
             code: unIndent`
@@ -1103,8 +1092,7 @@ ruleTester.run("indent", rule, {
                   }
                 );
             `,
-            options: [2, { VariableDeclarator: 3 }],
-            parserOptions: { ecmaVersion: 6 }
+            options: [2, { VariableDeclarator: 3 }]
 
         },
         {
@@ -1116,8 +1104,7 @@ ruleTester.run("indent", rule, {
                 let light = true,
                     shadow = false;
             `,
-            options: [2, { VariableDeclarator: { const: 3, let: 2 } }],
-            parserOptions: { ecmaVersion: 6 }
+            options: [2, { VariableDeclarator: { const: 3, let: 2 } }]
         },
         {
             code: unIndent`
@@ -1143,8 +1130,7 @@ ruleTester.run("indent", rule, {
                       b: 2
                     };
             `,
-            options: [2, { VariableDeclarator: { var: 2, const: 3 }, SwitchCase: 1 }],
-            parserOptions: { ecmaVersion: 6 }
+            options: [2, { VariableDeclarator: { var: 2, const: 3 }, SwitchCase: 1 }]
         },
         {
             code: unIndent`
@@ -1255,7 +1241,6 @@ ruleTester.run("indent", rule, {
                     a = 5,
                     b = 4
             `,
-            parserOptions: { ecmaVersion: 6 },
             options: [2, { VariableDeclarator: { var: 2, let: 2, const: 3 } }]
         },
         {
@@ -1269,7 +1254,6 @@ ruleTester.run("indent", rule, {
 
                 if (YO) console.log(TE)
             `,
-            parserOptions: { ecmaVersion: 6 },
             options: [2, { VariableDeclarator: { var: 2, let: 2, const: 3 } }]
         },
         {
@@ -1326,8 +1310,7 @@ ruleTester.run("indent", rule, {
                         console.log(argument);
                     },
                     someOtherValue = 'someOtherValue';
-            `,
-            parserOptions: { ecmaVersion: 6 }
+            `
         },
         {
             code: unIndent`
@@ -1350,8 +1333,7 @@ ruleTester.run("indent", rule, {
                       get b(){}
                     };
             `,
-            options: [2, { VariableDeclarator: 2, SwitchCase: 1 }],
-            parserOptions: { ecmaVersion: 6 }
+            options: [2, { VariableDeclarator: 2, SwitchCase: 1 }]
         },
         {
             code: unIndent`
@@ -1364,8 +1346,7 @@ ruleTester.run("indent", rule, {
                     },
                     c = 3;
             `,
-            options: [2, { VariableDeclarator: 2, SwitchCase: 1 }],
-            parserOptions: { ecmaVersion: 6 }
+            options: [2, { VariableDeclarator: 2, SwitchCase: 1 }]
         },
         {
             code: unIndent`
@@ -1375,8 +1356,7 @@ ruleTester.run("indent", rule, {
                     get b(){}
                 }
             `,
-            options: [4, { VariableDeclarator: 1, SwitchCase: 1 }],
-            parserOptions: { ecmaVersion: 6 }
+            options: [4, { VariableDeclarator: 1, SwitchCase: 1 }]
         },
         {
             code: unIndent`
@@ -1386,8 +1366,7 @@ ruleTester.run("indent", rule, {
                     get b(){}
                 }
             `,
-            options: [4, { VariableDeclarator: 1, SwitchCase: 1 }],
-            parserOptions: { ecmaVersion: 6 }
+            options: [4, { VariableDeclarator: 1, SwitchCase: 1 }]
         },
         {
             code: unIndent`
@@ -1448,7 +1427,6 @@ ruleTester.run("indent", rule, {
                     });
                 };
             `,
-            parserOptions: { ecmaVersion: 6 },
             options: [4, { MemberExpression: 0 }]
         },
         {
@@ -1462,7 +1440,6 @@ ruleTester.run("indent", rule, {
                         });
                 };
             `,
-            parserOptions: { ecmaVersion: 6 },
             options: [4]
         },
         {
@@ -1509,7 +1486,6 @@ ruleTester.run("indent", rule, {
                   }
                 };
             `,
-            parserOptions: { ecmaVersion: 6 },
             options: [2]
         },
         {
@@ -1531,7 +1507,6 @@ ruleTester.run("indent", rule, {
                   baz() {}
                 }
             `,
-            parserOptions: { ecmaVersion: 6 },
             options: [2]
         },
         {
@@ -1541,7 +1516,6 @@ ruleTester.run("indent", rule, {
                   baz() {}
                 }
             `,
-            parserOptions: { ecmaVersion: 6 },
             options: [2]
         },
         {
@@ -1553,7 +1527,6 @@ ruleTester.run("indent", rule, {
                   baz() {}
                 }
             `,
-            parserOptions: { ecmaVersion: 6 },
             options: [2]
         },
         {
@@ -1562,8 +1535,7 @@ ruleTester.run("indent", rule, {
                   files[name] = foo;
                 });
             `,
-            options: [2, { outerIIFEBody: 0 }],
-            parserOptions: { ecmaVersion: 6 }
+            options: [2, { outerIIFEBody: 0 }]
         },
         {
             code: unIndent`
@@ -1705,7 +1677,6 @@ ruleTester.run("indent", rule, {
                 }
                 })();
             `,
-            parserOptions: { ecmaVersion: 6 },
             options: [2, { outerIIFEBody: 0 }]
         },
         {
@@ -1723,7 +1694,6 @@ ruleTester.run("indent", rule, {
                 }
                 })();
             `,
-            parserOptions: { ecmaVersion: 6 },
             options: [2, { outerIIFEBody: 0 }]
         },
         {
@@ -2043,8 +2013,7 @@ ruleTester.run("indent", rule, {
                   foobar: baz = foobar
                 } = qux;
             `,
-            options: [2],
-            parserOptions: { ecmaVersion: 6 }
+            options: [2]
         },
         {
             code: unIndent`
@@ -2055,8 +2024,7 @@ ruleTester.run("indent", rule, {
                   foobar = baz
                 ] = qux;
             `,
-            options: [2],
-            parserOptions: { ecmaVersion: 6 }
+            options: [2]
         },
         {
 
@@ -2073,16 +2041,14 @@ ruleTester.run("indent", rule, {
                 for (const foo of bar)
                   baz();
             `,
-            options: [2],
-            parserOptions: { ecmaVersion: 6 }
+            options: [2]
         },
         {
             code: unIndent`
                 var x = () =>
                   5;
             `,
-            options: [2],
-            parserOptions: { ecmaVersion: 6 }
+            options: [2]
         },
         {
 
@@ -2534,8 +2500,7 @@ ruleTester.run("indent", rule, {
                   );
                 }
             `,
-            options: [2, { ObjectExpression: 1 }],
-            parserOptions: { ecmaVersion: 6 }
+            options: [2, { ObjectExpression: 1 }]
         },
         {
             code: unIndent`
@@ -2548,8 +2513,7 @@ ruleTester.run("indent", rule, {
                   );
                 }
             `,
-            options: [2, { ObjectExpression: "first" }],
-            parserOptions: { ecmaVersion: 6 }
+            options: [2, { ObjectExpression: "first" }]
         },
 
         // https://github.com/eslint/eslint/issues/7733
@@ -2632,8 +2596,7 @@ ruleTester.run("indent", rule, {
                 \`foo\${
                   bar}\`
             `,
-            options: [2],
-            parserOptions: { ecmaVersion: 6 }
+            options: [2]
         },
         {
             code: unIndent`
@@ -2641,8 +2604,7 @@ ruleTester.run("indent", rule, {
                   \`bar\${
                     baz}\`}\`
             `,
-            options: [2],
-            parserOptions: { ecmaVersion: 6 }
+            options: [2]
         },
         {
             code: unIndent`
@@ -2652,8 +2614,7 @@ ruleTester.run("indent", rule, {
                   }\`
                 }\`
             `,
-            options: [2],
-            parserOptions: { ecmaVersion: 6 }
+            options: [2]
         },
         {
             code: unIndent`
@@ -2663,8 +2624,7 @@ ruleTester.run("indent", rule, {
                   )
                 }\`
             `,
-            options: [2],
-            parserOptions: { ecmaVersion: 6 }
+            options: [2]
         },
         {
 
@@ -2674,8 +2634,7 @@ ruleTester.run("indent", rule, {
                         qux}foo\${
                         bar}baz\`
                 }
-            `,
-            parserOptions: { ecmaVersion: 6 }
+            `
         },
         {
 
@@ -2711,8 +2670,7 @@ ruleTester.run("indent", rule, {
                     const template = \`this indentation is not checked
                 because it's part of a template literal.\`;
                 }
-            `,
-            parserOptions: { ecmaVersion: 6 }
+            `
         },
         {
             code: unIndent`
@@ -2721,8 +2679,7 @@ ruleTester.run("indent", rule, {
                         node.type
                     } node is checked.\`;
                 }
-            `,
-            parserOptions: { ecmaVersion: 6 }
+            `
         },
         {
 
@@ -2942,10 +2899,10 @@ ruleTester.run("indent", rule, {
                 foo
                     ? bar
                     : baz
-                    ? qux
-                    : foobar
-                    ? boop
-                    : beep
+                        ? qux
+                        : foobar
+                            ? boop
+                            : beep
             `,
             options: [4, { flatTernaryExpressions: true }]
         },
@@ -2954,10 +2911,94 @@ ruleTester.run("indent", rule, {
                 foo ?
                     bar :
                     baz ?
-                    qux :
-                    foobar ?
-                    boop :
-                    beep
+                        qux :
+                        foobar ?
+                            boop :
+                            beep
+            `,
+            options: [4, { flatTernaryExpressions: true }]
+        },
+        {
+            code: unIndent`
+                var a =
+                    foo ? bar :
+                    baz ? qux :
+                    foobar ? boop :
+                    /*else*/ beep
+            `,
+            options: [4, { flatTernaryExpressions: true }]
+        },
+        {
+            code: unIndent`
+                var a = foo
+                    ? bar
+                    : baz
+            `,
+            options: [4, { flatTernaryExpressions: true }]
+        },
+        {
+            code: unIndent`
+                var a =
+                    foo
+                        ? bar
+                        : baz
+            `,
+            options: [4, { flatTernaryExpressions: true }]
+        },
+        {
+            code: unIndent`
+                a =
+                    foo ? bar :
+                    baz ? qux :
+                    foobar ? boop :
+                    /*else*/ beep
+            `,
+            options: [4, { flatTernaryExpressions: true }]
+        },
+        {
+            code: unIndent`
+                a = foo
+                    ? bar
+                    : baz
+            `,
+            options: [4, { flatTernaryExpressions: true }]
+        },
+        {
+            code: unIndent`
+                a =
+                    foo
+                        ? bar
+                        : baz
+            `,
+            options: [4, { flatTernaryExpressions: true }]
+        },
+        {
+            code: unIndent`
+                foo(
+                    foo ? bar :
+                    baz ? qux :
+                    foobar ? boop :
+                    /*else*/ beep
+                )
+            `,
+            options: [4, { flatTernaryExpressions: true }]
+        },
+        {
+            code: unIndent`
+                foo(
+                    foo
+                        ? bar
+                        : baz
+                )
+            `,
+            options: [4, { flatTernaryExpressions: true }]
+        },
+        {
+            code: unIndent`
+                foo(foo
+                    ? bar
+                    : baz
+                )
             `,
             options: [4, { flatTernaryExpressions: true }]
         },
@@ -3039,8 +3080,7 @@ ruleTester.run("indent", rule, {
                   }
                 );
             `,
-            options: [2, { ObjectExpression: "first" }],
-            parserOptions: { ecmaVersion: 6 }
+            options: [2, { ObjectExpression: "first" }]
         },
         {
             code: unIndent`
@@ -3050,8 +3090,7 @@ ruleTester.run("indent", rule, {
                     baz;
                 })
             `,
-            options: [4, { CallExpression: { arguments: "first" } }],
-            parserOptions: { ecmaVersion: 6 }
+            options: [4, { CallExpression: { arguments: "first" } }]
         },
         {
             code: unIndent`
@@ -3103,8 +3142,7 @@ ruleTester.run("indent", rule, {
             `
         },
         {
-            code: "x => {}",
-            parserOptions: { ecmaVersion: 6 }
+            code: "x => {}"
         },
         {
             code: unIndent`
@@ -3126,8 +3164,7 @@ ruleTester.run("indent", rule, {
                 ) => b => {
                     c
                 }
-            `,
-            parserOptions: { ecmaVersion: 6 }
+            `
         },
         {
             code: unIndent`
@@ -3136,8 +3173,7 @@ ruleTester.run("indent", rule, {
                 ) => b => c => d => {
                     e
                 }
-            `,
-            parserOptions: { ecmaVersion: 6 }
+            `
         },
         {
             code: unIndent`
@@ -3149,8 +3185,7 @@ ruleTester.run("indent", rule, {
                     ) => {
                         c
                     }
-            `,
-            parserOptions: { ecmaVersion: 6 }
+            `
         },
         {
             code: unIndent`
@@ -3181,15 +3216,13 @@ ruleTester.run("indent", rule, {
             code: unIndent`
                 () =>
                     ({})
-            `,
-            parserOptions: { ecmaVersion: 6 }
+            `
         },
         {
             code: unIndent`
                 () =>
                     (({}))
-            `,
-            parserOptions: { ecmaVersion: 6 }
+            `
         },
         {
             code: unIndent`
@@ -3197,8 +3230,7 @@ ruleTester.run("indent", rule, {
                     () =>
                         ({})
                 )
-            `,
-            parserOptions: { ecmaVersion: 6 }
+            `
         },
         {
             code: unIndent`
@@ -3214,8 +3246,7 @@ ruleTester.run("indent", rule, {
                 {
                     baz();
                 }
-            `,
-            parserOptions: { ecmaVersion: 6 }
+            `
         },
         {
             code: unIndent`
@@ -3231,8 +3262,7 @@ ruleTester.run("indent", rule, {
                         baz();
                     }
                 }
-            `,
-            parserOptions: { ecmaVersion: 6 }
+            `
         },
         {
             code: unIndent`
@@ -3249,8 +3279,7 @@ ruleTester.run("indent", rule, {
                         baz();
                     }
                 }
-            `,
-            parserOptions: { ecmaVersion: 6 }
+            `
         },
         {
             code: unIndent`
@@ -3268,8 +3297,7 @@ ruleTester.run("indent", rule, {
                         }
                     }
                 )
-            `,
-            parserOptions: { ecmaVersion: 6 }
+            `
         },
         {
             code: unIndent`
@@ -3476,102 +3504,865 @@ ruleTester.run("indent", rule, {
         },
         {
             code: unIndent`
-              type httpMethod = 'GET'
-                | 'POST'
-                | 'PUT';
+                type httpMethod = 'GET'
+                  | 'POST'
+                  | 'PUT';
             `,
             options: [2, { VariableDeclarator: 0 }],
             parser: parser("unknown-nodes/variable-declarator-type-indent-two-spaces")
         },
         {
             code: unIndent`
-              type httpMethod = 'GET'
-              | 'POST'
-              | 'PUT';
+                type httpMethod = 'GET'
+                | 'POST'
+                | 'PUT';
             `,
             options: [2, { VariableDeclarator: 1 }],
             parser: parser("unknown-nodes/variable-declarator-type-no-indent")
-        }
-    ],
+        },
+        {
+            code: unIndent`
+                foo(\`foo
+                        \`, {
+                        ok: true
+                    },
+                    {
+                        ok: false
+                    }
+                )
+            `
+        },
+        {
+            code: unIndent`
+                foo(tag\`foo
+                        \`, {
+                        ok: true
+                    },
+                    {
+                        ok: false
+                    }
+                )
+            `
+        },
 
+        //----------------------------------------------------------------------
+        // JSX tests
+        // https://github.com/eslint/eslint/issues/8425
+        // Some of the following tests are adapted from the the tests in eslint-plugin-react.
+        // License: https://github.com/yannickcr/eslint-plugin-react/blob/7ca9841f22d599f447a27ef5b2a97def9229d6c8/LICENSE
+        //----------------------------------------------------------------------
 
-    invalid: [
+        {
+            code: "<Foo a=\"b\" c=\"d\"/>;"
+        },
         {
             code: unIndent`
-                var a = b;
-                if (a) {
-                b();
-                }
-            `,
-            options: [2],
-            errors: expectedErrors([[3, 2, 0, "Identifier"]]),
-            output: unIndent`
-                var a = b;
-                if (a) {
-                  b();
-                }
+                <Foo
+                    a="b"
+                    c="d"
+                />;
             `
         },
+        {
+            code: "var foo = <Bar a=\"b\" c=\"d\"/>;"
+        },
         {
             code: unIndent`
-                require('http').request({hostname: 'localhost',
-                                  port: 80}, function(res) {
-                    res.end();
-                  });
-            `,
-            output: unIndent`
-                require('http').request({hostname: 'localhost',
-                  port: 80}, function(res) {
-                  res.end();
-                });
-            `,
-            options: [2],
-            errors: expectedErrors([[2, 2, 18, "Identifier"], [3, 2, 4, "Identifier"], [4, 0, 2, "Punctuator"]])
+                var foo = <Bar
+                    a="b"
+                    c="d"
+                />;
+            `
         },
         {
             code: unIndent`
-                if (array.some(function(){
-                  return true;
-                })) {
-                a++; // ->
-                  b++;
-                    c++; // <-
-                }
-            `,
-            output: unIndent`
-                if (array.some(function(){
-                  return true;
-                })) {
-                  a++; // ->
-                  b++;
-                  c++; // <-
-                }
-            `,
-            options: [2],
-            errors: expectedErrors([[4, 2, 0, "Identifier"], [6, 2, 4, "Identifier"]])
+                var foo = (<Bar
+                    a="b"
+                    c="d"
+                />);
+            `
         },
         {
             code: unIndent`
-                if (a){
-                \tb=c;
-                \t\tc=d;
-                e=f;
-                }
-            `,
-            output: unIndent`
-                if (a){
-                \tb=c;
-                \tc=d;
-                \te=f;
-                }
-            `,
-            options: ["tab"],
-            errors: expectedErrors("tab", [[3, 1, 2, "Identifier"], [4, 1, 0, "Identifier"]])
+                var foo = (
+                    <Bar
+                        a="b"
+                        c="d"
+                    />
+                );
+            `
         },
         {
             code: unIndent`
-                if (a){
-                    b=c;
+                <
+                    Foo
+                    a="b"
+                    c="d"
+                />;
+            `
+        },
+        {
+            code: unIndent`
+                <Foo
+                    a="b"
+                    c="d"/>;
+            `
+        },
+        {
+            code: unIndent`
+                <
+                    Foo
+                    a="b"
+                    c="d"/>;
+            `
+        },
+        {
+            code: "<a href=\"foo\">bar</a>;"
+
+        },
+        {
+            code: unIndent`
+                <a href="foo">
+                    bar
+                </a>;
+            `
+        },
+        {
+            code: unIndent`
+                <a
+                    href="foo"
+                >
+                    bar
+                </a>;
+            `
+        },
+        {
+            code: unIndent`
+                <a
+                    href="foo">
+                    bar
+                </a>;
+            `
+        },
+        {
+            code: unIndent`
+                <
+                    a
+                    href="foo">
+                    bar
+                </a>;
+            `
+        },
+        {
+            code: unIndent`
+                <a
+                    href="foo">
+                    bar
+                </
+                    a>;
+            `
+        },
+        {
+            code: unIndent`
+                <a
+                    href="foo">
+                    bar
+                </a
+                >;
+            `
+        },
+        {
+            code: unIndent`
+                var foo = <a href="bar">
+                    baz
+                </a>;
+            `
+        },
+        {
+            code: unIndent`
+                var foo = <a
+                    href="bar"
+                >
+                    baz
+                </a>;
+            `
+        },
+        {
+            code: unIndent`
+                var foo = <a
+                    href="bar">
+                    baz
+                </a>;
+            `
+        },
+        {
+            code: unIndent`
+                var foo = <
+                    a
+                    href="bar">
+                    baz
+                </a>;
+            `
+        },
+        {
+            code: unIndent`
+                var foo = <a
+                    href="bar">
+                    baz
+                </
+                    a>;
+            `
+        },
+        {
+            code: unIndent`
+                var foo = <a
+                    href="bar">
+                    baz
+                </a
+                >
+            `
+        },
+        {
+            code: unIndent`
+                var foo = (<a
+                    href="bar">
+                    baz
+                </a>);
+            `
+        },
+        {
+            code: unIndent`
+                var foo = (
+                    <a href="bar">baz</a>
+                );
+            `
+        },
+        {
+            code: unIndent`
+                var foo = (
+                    <a href="bar">
+                        baz
+                    </a>
+                );
+            `
+        },
+        {
+            code: unIndent`
+                var foo = (
+                    <a
+                        href="bar">
+                        baz
+                    </a>
+                );
+            `
+        },
+        {
+            code: "var foo = <a href=\"bar\">baz</a>;"
+        },
+        {
+            code: unIndent`
+                <a>
+                    {
+                    }
+                </a>
+            `
+        },
+        {
+            code: unIndent`
+                <a>
+                    {
+                        foo
+                    }
+                </a>
+            `
+        },
+        {
+            code: unIndent`
+                function foo() {
+                    return (
+                        <a>
+                            {
+                                b.forEach(() => {
+                                    // comment
+                                    a = c
+                                        .d()
+                                        .e();
+                                })
+                            }
+                        </a>
+                    );
+                }
+            `
+        },
+        {
+            code: "<App></App>"
+        },
+        {
+            code: unIndent`
+                <App>
+                </App>
+            `
+        },
+        {
+            code: unIndent`
+                <App>
+                  <Foo />
+                </App>
+            `,
+            options: [2]
+        },
+        {
+            code: unIndent`
+                <App>
+                <Foo />
+                </App>
+            `,
+            options: [0]
+        },
+        {
+            code: unIndent`
+                <App>
+                \t<Foo />
+                </App>
+            `,
+            options: ["tab"]
+        },
+        {
+            code: unIndent`
+                function App() {
+                  return <App>
+                    <Foo />
+                  </App>;
+                }
+            `,
+            options: [2]
+        },
+        {
+            code: unIndent`
+                function App() {
+                  return (<App>
+                    <Foo />
+                  </App>);
+                }
+            `,
+            options: [2]
+        },
+        {
+            code: unIndent`
+                function App() {
+                  return (
+                    <App>
+                      <Foo />
+                    </App>
+                  );
+                }
+            `,
+            options: [2]
+        },
+        {
+            code: unIndent`
+                it(
+                  (
+                    <div>
+                      <span />
+                    </div>
+                  )
+                )
+            `,
+            options: [2]
+        },
+        {
+            code: unIndent`
+                it(
+                  (<div>
+                    <span />
+                    <span />
+                    <span />
+                  </div>)
+                )
+            `,
+            options: [2]
+        },
+        {
+            code: unIndent`
+                (
+                  <div>
+                    <span />
+                  </div>
+                )
+            `,
+            options: [2]
+        },
+        {
+            code: unIndent`
+                {
+                  head.title &&
+                  <h1>
+                    {head.title}
+                  </h1>
+                }
+            `,
+            options: [2]
+        },
+        {
+            code: unIndent`
+                {
+                  head.title &&
+                    <h1>
+                      {head.title}
+                    </h1>
+                }
+            `,
+            options: [2]
+        },
+        {
+            code: unIndent`
+                {
+                  head.title && (
+                    <h1>
+                      {head.title}
+                    </h1>)
+                }
+            `,
+            options: [2]
+        },
+        {
+            code: unIndent`
+                {
+                  head.title && (
+                    <h1>
+                      {head.title}
+                    </h1>
+                  )
+                }
+            `,
+            options: [2]
+        },
+        {
+            code: unIndent`
+                [
+                  <div />,
+                  <div />
+                ]
+            `,
+            options: [2]
+        },
+        {
+            code: unIndent`
+                <div>
+                    {
+                        [
+                            <Foo />,
+                            <Bar />
+                        ]
+                    }
+                </div>
+            `
+        },
+        {
+            code: unIndent`
+                <div>
+                    {foo &&
+                        [
+                            <Foo />,
+                            <Bar />
+                        ]
+                    }
+                </div>
+            `
+        },
+        {
+
+            // Literals indentation is not touched
+            code: unIndent`
+                <div>
+                bar <div>
+                   bar
+                   bar {foo}
+                bar </div>
+                </div>
+            `
+        },
+        {
+
+            // Multiline ternary
+            // (colon at the end of the first expression)
+            code: unIndent`
+                foo ?
+                    <Foo /> :
+                    <Bar />
+            `
+        },
+        {
+
+            // Multiline ternary
+            // (colon at the start of the second expression)
+            code: unIndent`
+                foo ?
+                    <Foo />
+                    : <Bar />
+            `
+        },
+        {
+
+            // Multiline ternary
+            // (colon on its own line)
+            code: unIndent`
+                foo ?
+                    <Foo />
+                    :
+                    <Bar />
+            `
+        },
+        {
+
+            // Multiline ternary
+            // (multiline JSX, colon on its own line)
+            code: unIndent`
+                {!foo ?
+                    <Foo
+                        onClick={this.onClick}
+                    />
+                    :
+                    <Bar
+                        onClick={this.onClick}
+                    />
+                }
+            `
+        },
+        {
+            code: unIndent`
+                <span>
+                  {condition ?
+                    <Thing
+                      foo={\`bar\`}
+                    /> :
+                    <Thing/>
+                  }
+                </span>
+            `,
+            options: [2]
+        },
+        {
+            code: unIndent`
+                <span>
+                  {condition ?
+                    <Thing
+                      foo={"bar"}
+                    /> :
+                    <Thing/>
+                  }
+                </span>
+            `,
+            options: [2]
+        },
+        {
+            code: unIndent`
+                function foo() {
+                  <span>
+                    {condition ?
+                      <Thing
+                        foo={super}
+                      /> :
+                      <Thing/>
+                    }
+                  </span>
+                }
+            `,
+            options: [2]
+        },
+        {
+            code: unIndent`
+              <App foo
+              />
+            `
+        },
+        {
+            code: unIndent`
+              <App
+                foo
+              />
+            `,
+            options: [2]
+        },
+        {
+            code: unIndent`
+              <App
+              foo
+              />
+            `,
+            options: [0]
+        },
+        {
+            code: unIndent`
+              <App
+              \tfoo
+              />
+            `,
+            options: ["tab"]
+        },
+        {
+            code: unIndent`
+                <App
+                    foo
+                />
+            `
+        },
+        {
+            code: unIndent`
+                <App
+                    foo
+                ></App>
+            `
+        },
+        {
+            code: unIndent`
+                <App
+                  foo={function() {
+                    console.log('bar');
+                  }}
+                />
+            `,
+            options: [2]
+        },
+        {
+            code: unIndent`
+                <App foo={function() {
+                  console.log('bar');
+                }}
+                />
+            `,
+            options: [2]
+        },
+        {
+            code: unIndent`
+                var x = function() {
+                  return <App
+                    foo={function() {
+                      console.log('bar');
+                    }}
+                  />
+                }
+            `,
+            options: [2]
+        },
+        {
+            code: unIndent`
+                var x = <App
+                  foo={function() {
+                    console.log('bar');
+                  }}
+                />
+            `,
+            options: [2]
+        },
+        {
+            code: unIndent`
+                <Provider
+                  store
+                >
+                  <App
+                    foo={function() {
+                      console.log('bar');
+                    }}
+                  />
+                </Provider>
+            `,
+            options: [2]
+        },
+        {
+            code: unIndent`
+                <Provider
+                  store
+                >
+                  {baz && <App
+                    foo={function() {
+                      console.log('bar');
+                    }}
+                  />}
+                </Provider>
+            `,
+            options: [2]
+        },
+        {
+            code: unIndent`
+                <App
+                \tfoo
+                />
+            `,
+            options: ["tab"]
+        },
+        {
+            code: unIndent`
+                <App
+                \tfoo
+                ></App>
+            `,
+            options: ["tab"]
+        },
+        {
+            code: unIndent`
+                <App foo={function() {
+                \tconsole.log('bar');
+                }}
+                />
+            `,
+            options: ["tab"]
+        },
+        {
+            code: unIndent`
+                var x = <App
+                \tfoo={function() {
+                \t\tconsole.log('bar');
+                \t}}
+                />
+            `,
+            options: ["tab"]
+        },
+        {
+            code: unIndent`
+                <App
+                    foo />
+            `
+        },
+        {
+            code: unIndent`
+                <div>
+                   unrelated{
+                        foo
+                    }
+                </div>
+            `
+        },
+        {
+            code: unIndent`
+                <div>unrelated{
+                    foo
+                }
+                </div>
+            `
+        },
+        {
+            code: unIndent`
+                <
+                    foo
+                        .bar
+                        .baz
+                >
+                    foo
+                </
+                    foo.
+                        bar.
+                        baz
+                >
+            `
+        },
+        {
+            code: unIndent`
+                <
+                    input
+                    type=
+                        "number"
+                />
+            `
+        },
+        {
+            code: unIndent`
+                <
+                    input
+                    type=
+                        {'number'}
+                />
+            `
+        },
+        {
+            code: unIndent`
+                <
+                    input
+                    type
+                        ="number"
+                />
+            `
+        }
+    ],
+
+    invalid: [
+        {
+            code: unIndent`
+                var a = b;
+                if (a) {
+                b();
+                }
+            `,
+            options: [2],
+            errors: expectedErrors([[3, 2, 0, "Identifier"]]),
+            output: unIndent`
+                var a = b;
+                if (a) {
+                  b();
+                }
+            `
+        },
+        {
+            code: unIndent`
+                require('http').request({hostname: 'localhost',
+                                  port: 80}, function(res) {
+                    res.end();
+                  });
+            `,
+            output: unIndent`
+                require('http').request({hostname: 'localhost',
+                  port: 80}, function(res) {
+                  res.end();
+                });
+            `,
+            options: [2],
+            errors: expectedErrors([[2, 2, 18, "Identifier"], [3, 2, 4, "Identifier"], [4, 0, 2, "Punctuator"]])
+        },
+        {
+            code: unIndent`
+                if (array.some(function(){
+                  return true;
+                })) {
+                a++; // ->
+                  b++;
+                    c++; // <-
+                }
+            `,
+            output: unIndent`
+                if (array.some(function(){
+                  return true;
+                })) {
+                  a++; // ->
+                  b++;
+                  c++; // <-
+                }
+            `,
+            options: [2],
+            errors: expectedErrors([[4, 2, 0, "Identifier"], [6, 2, 4, "Identifier"]])
+        },
+        {
+            code: unIndent`
+                if (a){
+                \tb=c;
+                \t\tc=d;
+                e=f;
+                }
+            `,
+            output: unIndent`
+                if (a){
+                \tb=c;
+                \tc=d;
+                \te=f;
+                }
+            `,
+            options: ["tab"],
+            errors: expectedErrors("tab", [[3, 1, 2, "Identifier"], [4, 1, 0, "Identifier"]])
+        },
+        {
+            code: unIndent`
+                if (a){
+                    b=c;
                       c=d;
                  e=f;
                 }
@@ -3949,7 +4740,6 @@ ruleTester.run("indent", rule, {
                 }
             `,
             options: [4, { MemberExpression: 2 }],
-            parserOptions: { ecmaVersion: 6 },
             errors: expectedErrors(
                 [3, 12, 13, "Punctuator"]
             )
@@ -3972,7 +4762,6 @@ ruleTester.run("indent", rule, {
                 };
             `,
             options: [2, { MemberExpression: 1 }],
-            parserOptions: { ecmaVersion: 6 },
             errors: expectedErrors([3, 4, 6, "Punctuator"])
         },
         {
@@ -4185,7 +4974,6 @@ ruleTester.run("indent", rule, {
                 });
             `,
             options: [4],
-            parserOptions: { ecmaVersion: 6 },
             errors: expectedErrors([
                 [3, 4, 8, "Identifier"],
                 [4, 0, 4, "Punctuator"]
@@ -4205,7 +4993,6 @@ ruleTester.run("indent", rule, {
                 });
             `,
             options: [4],
-            parserOptions: { ecmaVersion: 6 },
             errors: expectedErrors([
                 [2, 4, 0, "Identifier"],
                 [3, 4, 2, "Keyword"]
@@ -4223,7 +5010,6 @@ ruleTester.run("indent", rule, {
                 });
             `,
             options: [4],
-            parserOptions: { ecmaVersion: 6 },
             errors: expectedErrors([
                 [2, 4, 2, "Keyword"]
             ])
@@ -4242,7 +5028,6 @@ ruleTester.run("indent", rule, {
                     ]);
             `,
             options: [4, { MemberExpression: 1 }],
-            parserOptions: { ecmaVersion: 6 },
             errors: expectedErrors([[3, 8, 4, "Identifier"], [4, 4, 0, "Punctuator"]])
         },
         {
@@ -4325,7 +5110,6 @@ ruleTester.run("indent", rule, {
                 ];
             `,
             options: [4],
-            parserOptions: { ecmaVersion: 6 },
             errors: expectedErrors([
                 [2, 4, 9, "String"],
                 [3, 4, 9, "String"],
@@ -4469,7 +5253,6 @@ ruleTester.run("indent", rule, {
                     rotate;
             `,
             options: [2, { VariableDeclarator: 2 }],
-            parserOptions: { ecmaVersion: 6 },
             errors: expectedErrors([
                 [2, 4, 2, "Identifier"]
             ])
@@ -4544,7 +5327,6 @@ ruleTester.run("indent", rule, {
                 ]
             `,
             options: [2, { VariableDeclarator: { let: 2 }, SwitchCase: 1 }],
-            parserOptions: { ecmaVersion: 6 },
             errors: expectedErrors([
                 [2, 2, 4, "Identifier"],
                 [3, 2, 4, "Identifier"]
@@ -4591,7 +5373,6 @@ ruleTester.run("indent", rule, {
                   d = 4;
             `,
             options: [2, { VariableDeclarator: { var: 2 } }],
-            parserOptions: { ecmaVersion: 6 },
             errors: expectedErrors([
                 [6, 4, 6, "Identifier"],
                 [7, 2, 4, "Punctuator"],
@@ -4707,7 +5488,6 @@ ruleTester.run("indent", rule, {
                 }
             `,
             options: [4, { VariableDeclarator: 1, SwitchCase: 1 }],
-            parserOptions: { ecmaVersion: 6 },
             errors: expectedErrors([[2, 4, 2, "Identifier"]])
         },
         {
@@ -4726,7 +5506,6 @@ ruleTester.run("indent", rule, {
                 };
             `,
             options: [4, { VariableDeclarator: 1, SwitchCase: 1 }],
-            parserOptions: { ecmaVersion: 6 },
             errors: expectedErrors([[2, 4, 2, "Identifier"], [4, 4, 2, "Identifier"]])
         },
         {
@@ -4747,7 +5526,6 @@ ruleTester.run("indent", rule, {
                     };
             `,
             options: [2, { VariableDeclarator: 2, SwitchCase: 1 }],
-            parserOptions: { ecmaVersion: 6 },
             errors: expectedErrors([[3, 6, 4, "Identifier"]])
         },
         {
@@ -5821,7 +6599,6 @@ ruleTester.run("indent", rule, {
                 } = qux;
             `,
             options: [2],
-            parserOptions: { ecmaVersion: 6 },
             errors: expectedErrors([[2, 2, 0, "Identifier"], [4, 2, 4, "Identifier"], [5, 2, 6, "Identifier"], [6, 0, 2, "Punctuator"]])
         },
         {
@@ -6000,7 +6777,6 @@ ruleTester.run("indent", rule, {
                 ] = qux;
             `,
             options: [2],
-            parserOptions: { ecmaVersion: 6 },
             errors: expectedErrors([[2, 2, 0, "Identifier"], [4, 2, 4, "Identifier"], [5, 2, 6, "Identifier"], [6, 0, 2, "Punctuator"]])
         },
         {
@@ -6083,7 +6859,6 @@ ruleTester.run("indent", rule, {
                   baz();
             `,
             options: [2],
-            parserOptions: { ecmaVersion: 6 },
             errors: expectedErrors([2, 2, 4, "Identifier"])
         },
         {
@@ -6096,7 +6871,6 @@ ruleTester.run("indent", rule, {
                   5;
             `,
             options: [2],
-            parserOptions: { ecmaVersion: 6 },
             errors: expectedErrors([2, 2, 4, "Numeric"])
         },
         {
@@ -6127,7 +6901,6 @@ ruleTester.run("indent", rule, {
                   bar}\`
             `,
             options: [2],
-            parserOptions: { ecmaVersion: 6 },
             errors: expectedErrors([2, 2, 0, "Identifier"])
         },
         {
@@ -6142,7 +6915,6 @@ ruleTester.run("indent", rule, {
                     baz}\`}\`
             `,
             options: [2],
-            parserOptions: { ecmaVersion: 6 },
             errors: expectedErrors([[2, 2, 4, "Template"], [3, 4, 0, "Identifier"]])
         },
         {
@@ -6161,7 +6933,6 @@ ruleTester.run("indent", rule, {
                 }\`
             `,
             options: [2],
-            parserOptions: { ecmaVersion: 6 },
             errors: expectedErrors([[2, 2, 4, "Template"], [3, 4, 2, "Identifier"], [4, 2, 4, "Template"], [5, 0, 2, "Template"]])
         },
         {
@@ -6180,7 +6951,6 @@ ruleTester.run("indent", rule, {
                 }\`
             `,
             options: [2],
-            parserOptions: { ecmaVersion: 6 },
             errors: expectedErrors([[2, 2, 0, "Punctuator"], [3, 4, 2, "Identifier"], [4, 2, 0, "Punctuator"]])
         },
         {
@@ -6198,7 +6968,6 @@ ruleTester.run("indent", rule, {
                         bar}baz\`
                 }
             `,
-            parserOptions: { ecmaVersion: 6 },
             errors: expectedErrors([[3, 8, 0, "Identifier"], [4, 8, 2, "Identifier"]])
         },
         {
@@ -6218,8 +6987,7 @@ ruleTester.run("indent", rule, {
                 } node is checked.\`;
                 }
             `,
-            errors: expectedErrors([[4, 4, 8, "Identifier"], [5, 0, 4, "Template"]]),
-            parserOptions: { ecmaVersion: 6 }
+            errors: expectedErrors([[4, 4, 8, "Identifier"], [5, 0, 4, "Template"]])
         },
         {
             code: unIndent`
@@ -6238,8 +7006,7 @@ ruleTester.run("indent", rule, {
                         so the spaces before this line aren't removed.\`;
                 }
             `,
-            errors: expectedErrors([4, 4, 12, "Identifier"]),
-            parserOptions: { ecmaVersion: 6 }
+            errors: expectedErrors([4, 4, 12, "Identifier"])
         },
         {
 
@@ -6487,56 +7254,118 @@ ruleTester.run("indent", rule, {
         },
         {
             code: unIndent`
-                foo
-                    ? bar
-                    : baz
-                        ? qux
-                        : foobar
-                            ? boop
+                foo ? bar
+                    : baz ? qux
+                        : foobar ? boop
                             : beep
             `,
             output: unIndent`
-                foo
+                foo ? bar
+                    : baz ? qux
+                    : foobar ? boop
+                    : beep
+            `,
+            options: [4, { flatTernaryExpressions: true }],
+            errors: expectedErrors([
+                [3, 4, 8, "Punctuator"],
+                [4, 4, 12, "Punctuator"]
+            ])
+        },
+        {
+            code: unIndent`
+                foo ? bar :
+                    baz ? qux :
+                        foobar ? boop :
+                            beep
+            `,
+            output: unIndent`
+                foo ? bar :
+                    baz ? qux :
+                    foobar ? boop :
+                    beep
+            `,
+            options: [4, { flatTernaryExpressions: true }],
+            errors: expectedErrors([
+                [3, 4, 8, "Identifier"],
+                [4, 4, 12, "Identifier"]
+            ])
+        },
+        {
+            code: unIndent`
+                var a =
+                    foo ? bar :
+                      baz ? qux :
+                  foobar ? boop :
+                    /*else*/ beep
+            `,
+            output: unIndent`
+                var a =
+                    foo ? bar :
+                    baz ? qux :
+                    foobar ? boop :
+                    /*else*/ beep
+            `,
+            options: [4, { flatTernaryExpressions: true }],
+            errors: expectedErrors([
+                [3, 4, 6, "Identifier"],
+                [4, 4, 2, "Identifier"]
+            ])
+        },
+        {
+            code: unIndent`
+                var a =
+                    foo
                     ? bar
                     : baz
-                    ? qux
-                    : foobar
-                    ? boop
-                    : beep
+            `,
+            output: unIndent`
+                var a =
+                    foo
+                        ? bar
+                        : baz
             `,
             options: [4, { flatTernaryExpressions: true }],
             errors: expectedErrors([
-                [4, 4, 8, "Punctuator"],
-                [5, 4, 8, "Punctuator"],
-                [6, 4, 12, "Punctuator"],
-                [7, 4, 12, "Punctuator"]
+                [3, 8, 4, "Punctuator"],
+                [4, 8, 4, "Punctuator"]
+            ])
+        },
+        {
+            code: unIndent`
+                foo ? bar
+                    : baz ? qux
+                    : foobar ? boop
+                    : beep
+            `,
+            output: unIndent`
+                foo ? bar
+                    : baz ? qux
+                        : foobar ? boop
+                            : beep
+            `,
+            options: [4, { flatTernaryExpressions: false }],
+            errors: expectedErrors([
+                [3, 8, 4, "Punctuator"],
+                [4, 12, 4, "Punctuator"]
             ])
         },
         {
             code: unIndent`
-                foo ?
-                    bar :
-                    baz ?
-                        qux :
-                        foobar ?
-                            boop :
-                            beep
+                foo ? bar :
+                    baz ? qux :
+                    foobar ? boop :
+                    beep
             `,
             output: unIndent`
-                foo ?
-                    bar :
-                    baz ?
-                    qux :
-                    foobar ?
-                    boop :
-                    beep
+                foo ? bar :
+                    baz ? qux :
+                        foobar ? boop :
+                            beep
             `,
-            options: [4, { flatTernaryExpressions: true }],
+            options: [4, { flatTernaryExpressions: false }],
             errors: expectedErrors([
-                [4, 4, 8, "Identifier"],
-                [5, 4, 8, "Identifier"],
-                [6, 4, 12, "Identifier"],
-                [7, 4, 12, "Identifier"]
+                [3, 8, 4, "Identifier"],
+                [4, 12, 4, "Identifier"]
             ])
         },
         {
@@ -6755,7 +7584,6 @@ ruleTester.run("indent", rule, {
                     c
                 }
             `,
-            parserOptions: { ecmaVersion: 6 },
             errors: expectedErrors([[4, 4, 8, "Identifier"], [5, 0, 4, "Punctuator"]])
         },
         {
@@ -6773,7 +7601,6 @@ ruleTester.run("indent", rule, {
                     e
                 }
             `,
-            parserOptions: { ecmaVersion: 6 },
             errors: expectedErrors([[4, 4, 8, "Identifier"], [5, 0, 4, "Punctuator"]])
         },
         {
@@ -6939,6 +7766,462 @@ ruleTester.run("indent", rule, {
                 [3, 8, 4, "Keyword"],
                 [7, 24, 20, "Identifier"]
             ])
+        },
+
+        //----------------------------------------------------------------------
+        // JSX tests
+        // Some of the following tests are adapted from the the tests in eslint-plugin-react.
+        // License: https://github.com/yannickcr/eslint-plugin-react/blob/7ca9841f22d599f447a27ef5b2a97def9229d6c8/LICENSE
+        //----------------------------------------------------------------------
+
+        {
+            code: unIndent`
+                <App>
+                  <Foo />
+                </App>
+            `,
+            output: unIndent`
+                <App>
+                    <Foo />
+                </App>
+            `,
+            errors: expectedErrors([2, 4, 2, "Punctuator"])
+        },
+        {
+            code: unIndent`
+                <App>
+                    <Foo />
+                </App>
+            `,
+            output: unIndent`
+                <App>
+                  <Foo />
+                </App>
+            `,
+            options: [2],
+            errors: expectedErrors([2, 2, 4, "Punctuator"])
+        },
+        {
+            code: unIndent`
+                <App>
+                    <Foo />
+                </App>
+            `,
+            output: unIndent`
+                <App>
+                \t<Foo />
+                </App>
+            `,
+            options: ["tab"],
+            errors: expectedErrors([2, "1 tab", "4 spaces", "Punctuator"])
+        },
+        {
+            code: unIndent`
+                function App() {
+                  return <App>
+                    <Foo />
+                         </App>;
+                }
+            `,
+            output: unIndent`
+                function App() {
+                  return <App>
+                    <Foo />
+                  </App>;
+                }
+            `,
+            options: [2],
+            errors: expectedErrors([4, 2, 9, "Punctuator"])
+        },
+        {
+            code: unIndent`
+                function App() {
+                  return (<App>
+                    <Foo />
+                    </App>);
+                }
+            `,
+            output: unIndent`
+                function App() {
+                  return (<App>
+                    <Foo />
+                  </App>);
+                }
+            `,
+            options: [2],
+            errors: expectedErrors([4, 2, 4, "Punctuator"])
+        },
+        {
+            code: unIndent`
+                function App() {
+                  return (
+                <App>
+                  <Foo />
+                </App>
+                  );
+                }
+            `,
+            output: unIndent`
+                function App() {
+                  return (
+                    <App>
+                      <Foo />
+                    </App>
+                  );
+                }
+            `,
+            options: [2],
+            errors: expectedErrors([[3, 4, 0, "Punctuator"], [4, 6, 2, "Punctuator"], [5, 4, 0, "Punctuator"]])
+        },
+        {
+            code: unIndent`
+                <App>
+                 {test}
+                </App>
+            `,
+            output: unIndent`
+                <App>
+                    {test}
+                </App>
+            `,
+            errors: expectedErrors([2, 4, 1, "Punctuator"])
+        },
+        {
+            code: unIndent`
+                <App>
+                    {options.map((option, index) => (
+                        <option key={index} value={option.key}>
+                           {option.name}
+                        </option>
+                    ))}
+                </App>
+            `,
+            output: unIndent`
+                <App>
+                    {options.map((option, index) => (
+                        <option key={index} value={option.key}>
+                            {option.name}
+                        </option>
+                    ))}
+                </App>
+            `,
+            errors: expectedErrors([4, 12, 11, "Punctuator"])
+        },
+        {
+            code: unIndent`
+                [
+                  <div />,
+                    <div />
+                ]
+            `,
+            output: unIndent`
+                [
+                  <div />,
+                  <div />
+                ]
+            `,
+            options: [2],
+            errors: expectedErrors([3, 2, 4, "Punctuator"])
+        },
+        {
+            code: unIndent`
+                <App>
+
+                 <Foo />
+
+                </App>
+            `,
+            output: unIndent`
+                <App>
+
+                \t<Foo />
+
+                </App>
+            `,
+            options: ["tab"],
+            errors: expectedErrors([3, "1 tab", "1 space", "Punctuator"])
+        },
+        {
+
+            // Multiline ternary
+            // (colon at the end of the first expression)
+            code: unIndent`
+                foo ?
+                    <Foo /> :
+                <Bar />
+            `,
+            output: unIndent`
+                foo ?
+                    <Foo /> :
+                    <Bar />
+            `,
+            errors: expectedErrors([3, 4, 0, "Punctuator"])
+        },
+        {
+
+            // Multiline ternary
+            // (colon on its own line)
+            code: unIndent`
+                foo ?
+                    <Foo />
+                :
+                <Bar />
+            `,
+            output: unIndent`
+                foo ?
+                    <Foo />
+                    :
+                    <Bar />
+            `,
+            errors: expectedErrors([[3, 4, 0, "Punctuator"], [4, 4, 0, "Punctuator"]])
+        },
+        {
+
+            // Multiline ternary
+            // (colon at the end of the first expression, parenthesized first expression)
+            code: unIndent`
+                foo ? (
+                    <Foo />
+                ) :
+                <Bar />
+            `,
+            output: unIndent`
+                foo ? (
+                    <Foo />
+                ) :
+                    <Bar />
+            `,
+            errors: expectedErrors([4, 4, 0, "Punctuator"])
+        },
+        {
+            code: unIndent`
+                <App
+                  foo
+                />
+            `,
+            output: unIndent`
+                <App
+                    foo
+                />
+            `,
+            errors: expectedErrors([2, 4, 2, "JSXIdentifier"])
+        },
+        {
+            code: unIndent`
+                <App
+                  foo
+                  />
+            `,
+            output: unIndent`
+                <App
+                  foo
+                />
+            `,
+            options: [2],
+            errors: expectedErrors([3, 0, 2, "Punctuator"])
+        },
+        {
+            code: unIndent`
+                <App
+                  foo
+                  ></App>
+            `,
+            output: unIndent`
+                <App
+                  foo
+                ></App>
+            `,
+            options: [2],
+            errors: expectedErrors([3, 0, 2, "Punctuator"])
+        },
+        {
+            code: unIndent`
+                const Button = function(props) {
+                  return (
+                    <Button
+                      size={size}
+                      onClick={onClick}
+                                                    >
+                      Button Text
+                    </Button>
+                  );
+                };
+            `,
+            output: unIndent`
+                const Button = function(props) {
+                  return (
+                    <Button
+                      size={size}
+                      onClick={onClick}
+                    >
+                      Button Text
+                    </Button>
+                  );
+                };
+            `,
+            options: [2],
+            errors: expectedErrors([6, 4, 36, "Punctuator"])
+        },
+        {
+            code: unIndent`
+                var x = function() {
+                  return <App
+                    foo
+                         />
+                }
+            `,
+            output: unIndent`
+                var x = function() {
+                  return <App
+                    foo
+                  />
+                }
+            `,
+            options: [2],
+            errors: expectedErrors([4, 2, 9, "Punctuator"])
+        },
+        {
+            code: unIndent`
+                var x = <App
+                  foo
+                        />
+            `,
+            output: unIndent`
+                var x = <App
+                  foo
+                />
+            `,
+            options: [2],
+            errors: expectedErrors([3, 0, 8, "Punctuator"])
+        },
+        {
+            code: unIndent`
+                var x = (
+                  <Something
+                    />
+                )
+            `,
+            output: unIndent`
+                var x = (
+                  <Something
+                  />
+                )
+            `,
+            options: [2],
+            errors: expectedErrors([3, 2, 4, "Punctuator"])
+        },
+        {
+            code: unIndent`
+                <App
+                \tfoo
+                \t/>
+            `,
+            output: unIndent`
+                <App
+                \tfoo
+                />
+            `,
+            options: ["tab"],
+            errors: expectedErrors("tab", [3, 0, 1, "Punctuator"])
+        },
+        {
+            code: unIndent`
+                <App
+                \tfoo
+                \t></App>
+            `,
+            output: unIndent`
+                <App
+                \tfoo
+                ></App>
+            `,
+            options: ["tab"],
+            errors: expectedErrors("tab", [3, 0, 1, "Punctuator"])
+        },
+        {
+            code: unIndent`
+                <
+                    foo
+                    .bar
+                    .baz
+                >
+                    foo
+                </
+                    foo.
+                    bar.
+                    baz
+                >
+            `,
+            output: unIndent`
+                <
+                    foo
+                        .bar
+                        .baz
+                >
+                    foo
+                </
+                    foo.
+                        bar.
+                        baz
+                >
+            `,
+            errors: expectedErrors([
+                [3, 8, 4, "Punctuator"],
+                [4, 8, 4, "Punctuator"],
+                [9, 8, 4, "JSXIdentifier"],
+                [10, 8, 4, "JSXIdentifier"]
+            ])
+        },
+        {
+            code: unIndent`
+                <
+                    input
+                    type=
+                    "number"
+                />
+            `,
+            output: unIndent`
+                <
+                    input
+                    type=
+                        "number"
+                />
+            `,
+            errors: expectedErrors([4, 8, 4, "JSXText"])
+        },
+        {
+            code: unIndent`
+                <
+                    input
+                    type=
+                    {'number'}
+                />
+            `,
+            output: unIndent`
+                <
+                    input
+                    type=
+                        {'number'}
+                />
+            `,
+            errors: expectedErrors([4, 8, 4, "Punctuator"])
+        },
+        {
+            code: unIndent`
+                <
+                    input
+                    type
+                    ="number"
+                />
+            `,
+            output: unIndent`
+                <
+                    input
+                    type
+                        ="number"
+                />
+            `,
+            errors: expectedErrors([4, 8, 4, "Punctuator"])
         }
     ]
 });
diff --git a/tests/lib/util/npm-util.js b/tests/lib/util/npm-util.js
index 42ef8a357..48a41aa50 100644
--- a/tests/lib/util/npm-util.js
+++ b/tests/lib/util/npm-util.js
@@ -9,8 +9,7 @@
 //------------------------------------------------------------------------------
 
 const assert = require("chai").assert,
-    fs = require("fs"),
-    shell = require("shelljs"),
+    childProcess = require("child_process"),
     sinon = require("sinon"),
     npmUtil = require("../../../lib/util/npm-util"),
     log = require("../../../lib/logging"),
@@ -30,6 +29,7 @@ describe("npmUtil", () => {
 
     afterEach(() => {
         sandbox.verifyAndRestore();
+        mockFs.restore();
     });
 
     describe("checkDevDeps()", () => {
@@ -61,11 +61,9 @@ describe("npmUtil", () => {
         });
 
         it("should handle missing devDependencies key", () => {
-            sandbox.stub(shell, "test").returns(true);
-            sandbox.stub(fs, "readFileSync").returns(JSON.stringify({
-                private: true,
-                dependencies: {}
-            }));
+            mockFs({
+                "package.json": JSON.stringify({ private: true, dependencies: {} })
+            });
 
             const fn = npmUtil.checkDevDeps.bind(null, ["some-package"]);
 
@@ -75,8 +73,9 @@ describe("npmUtil", () => {
         it("should throw with message when parsing invalid package.json", () => {
             const logInfo = sandbox.stub(log, "info");
 
-            sandbox.stub(shell, "test").returns(true);
-            sandbox.stub(fs, "readFileSync").returns("{ \"not: \"valid json\" }");
+            mockFs({
+                "package.json": "{ \"not: \"valid json\" }"
+            });
 
             const fn = npmUtil.checkDevDeps.bind(null, ["some-package"]);
 
@@ -93,6 +92,10 @@ describe("npmUtil", () => {
             installStatus = npmUtil.checkDeps(["debug", "mocha", "notarealpackage", "jshint"]);
         });
 
+        afterEach(() => {
+            mockFs.restore();
+        });
+
         it("should find a direct dependency of the project", () => {
             assert.isTrue(installStatus.debug);
         });
@@ -121,34 +124,27 @@ describe("npmUtil", () => {
         });
 
         it("should handle missing dependencies key", () => {
-            sandbox.stub(shell, "test").returns(true);
-            sandbox.stub(fs, "readFileSync").returns(JSON.stringify({
-                private: true,
-                devDependencies: {}
-            }));
+            mockFs({
+                "package.json": JSON.stringify({ private: true, devDependencies: {} })
+            });
 
             const fn = npmUtil.checkDeps.bind(null, ["some-package"]);
 
             assert.doesNotThrow(fn);
-
-            shell.test.restore();
-            fs.readFileSync.restore();
         });
 
         it("should throw with message when parsing invalid package.json", () => {
             const logInfo = sandbox.stub(log, "info");
 
-            sandbox.stub(shell, "test").returns(true);
-            sandbox.stub(fs, "readFileSync").returns("{ \"not: \"valid json\" }");
+            mockFs({
+                "package.json": "{ \"not: \"valid json\" }"
+            });
 
             const fn = npmUtil.checkDevDeps.bind(null, ["some-package"]);
 
             assert.throws(fn, "SyntaxError: Unexpected token v");
             assert(logInfo.calledOnce);
             assert.equal(logInfo.firstCall.args[0], "Could not read package.json file. Please check that the file contains valid JSON.");
-
-            shell.test.restore();
-            fs.readFileSync.restore();
             logInfo.restore();
         });
     });
@@ -174,7 +170,7 @@ describe("npmUtil", () => {
 
     describe("installSyncSaveDev()", () => {
         it("should invoke npm to install a single desired package", () => {
-            const stub = sandbox.stub(shell, "exec");
+            const stub = sandbox.stub(childProcess, "execSync");
 
             npmUtil.installSyncSaveDev("desired-package");
             assert(stub.calledOnce);
@@ -183,7 +179,7 @@ describe("npmUtil", () => {
         });
 
         it("should accept an array of packages to install", () => {
-            const stub = sandbox.stub(shell, "exec");
+            const stub = sandbox.stub(childProcess, "execSync");
 
             npmUtil.installSyncSaveDev(["first-package", "second-package"]);
             assert(stub.calledOnce);