Repository: eslint/eslint
Pull request #11097 (2018-11-17 21:52:13)

Fix: Amend keyword-spacing to validate `default` keywords
<!--
    ESLint adheres to the [JS Foundation Code of Conduct](https://js.foundation/community/code-of-conduct).
-->

**What is the purpose of this pull request? (put an "X" next to item)**

[ ] Documentation update
[X] 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
[ ] Other, please explain:

<!--
    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 (https://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)**
I updated the keyword-spacing rule definition to properly validate `default` keywords. At present, it mistakenly validates the `export` token rather than the accompanying `default`. I changed the handler to share `checkSpacingForModuleDeclaration` and amended that handler to include a special case for nodes with default export statements. This fixes: #11096 




jsf-clabot: [![CLA assistant check](https://cla.js.foundation/pull/badge/not_signed)](https://cla.js.foundation/eslint/eslint?pullRequest=11097) <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=11097) before we can accept your contribution.<br/>

Patch:
diff --git a/.github/ISSUE_TEMPLATE/NEW_RULE.md b/.github/ISSUE_TEMPLATE/NEW_RULE.md
index 5a50001b0..247302d7b 100644
--- a/.github/ISSUE_TEMPLATE/NEW_RULE.md
+++ b/.github/ISSUE_TEMPLATE/NEW_RULE.md
@@ -1,5 +1,5 @@
 ---
-/name: "\U0001F680 New rule proposal"
+name: "\U0001F680 New rule proposal"
 about: Propose a new rule to be added to ESLint
 
 ---
diff --git a/Makefile.js b/Makefile.js
index e468d0a16..eb2c586a7 100644
--- a/Makefile.js
+++ b/Makefile.js
@@ -350,7 +350,7 @@ function publishRelease() {
 /**
  * Splits a command result to separate lines.
  * @param {string} result The command result string.
- * @returns {array} The separated lines.
+ * @returns {Array} The separated lines.
  */
 function splitCommandResultToLines(result) {
     return result.trim().split("\n");
@@ -416,7 +416,7 @@ function getFirstVersionOfDeletion(filePath) {
 
 /**
  * Lints Markdown files.
- * @param {array} files Array of file names to lint.
+ * @param {Array} files Array of file names to lint.
  * @returns {Object} exec-style exit code object.
  * @private
  */
diff --git a/README.md b/README.md
index 572cd8a07..65ee43400 100644
--- a/README.md
+++ b/README.md
@@ -38,7 +38,7 @@ If you want to include ESLint as part of your project's build system, we recomme
 $ npm install eslint --save-dev
 ```
 
-You should then setup a configuration file:
+You should then set up a configuration file:
 
 ```
 $ ./node_modules/.bin/eslint --init
@@ -60,7 +60,7 @@ If you want to make ESLint available to tools that run across all of your projec
 $ npm install -g eslint
 ```
 
-You should then setup a configuration file:
+You should then set up a configuration file:
 
 ```
 $ eslint --init
diff --git a/docs/rules/id-match.md b/docs/rules/id-match.md
index 55f5e3253..0d9deebd7 100644
--- a/docs/rules/id-match.md
+++ b/docs/rules/id-match.md
@@ -23,20 +23,6 @@ For example, to enforce a camelcase naming convention:
 }
 ```
 
-Examples of **correct** code for this rule with the `"^[a-z]+([A-Z][a-z]+)*$"` option:
-
-```js
-/*eslint id-match: ["error", "^[a-z]+([A-Z][a-z]+)*$"]*/
-
-var myFavoriteColor   = "#112C85";
-var foo = bar.baz_boom;
-var foo = { qux: bar.baz_boom };
-do_something();
-var obj = {
-    my_pref: 1
-};
-```
-
 Examples of **incorrect** code for this rule with the `"^[a-z]+([A-Z][a-z]+)*$"` option:
 
 ```js
@@ -54,6 +40,20 @@ obj.do_something = function() {
 };
 ```
 
+Examples of **correct** code for this rule with the `"^[a-z]+([A-Z][a-z]+)*$"` option:
+
+```js
+/*eslint id-match: ["error", "^[a-z]+([A-Z][a-z]+)*$"]*/
+
+var myFavoriteColor   = "#112C85";
+var foo = bar.baz_boom;
+var foo = { qux: bar.baz_boom };
+do_something();
+var obj = {
+    my_pref: 1
+};
+```
+
 This rule has an object option:
 
 * `"properties": true` requires object properties to match the specified regular expression
diff --git a/docs/rules/no-console.md b/docs/rules/no-console.md
index 1c36a85f9..bc163b937 100644
--- a/docs/rules/no-console.md
+++ b/docs/rules/no-console.md
@@ -78,10 +78,11 @@ However, you might not want to manually add `eslint-disable-next-line` or `eslin
 ```json
 {
     "rules": {
+        "no-console": "off",
         "no-restricted-syntax": [
             "error",
             {
-                "selector": "CallExpression[callee.object.name='console'][callee.property.name=/^(log|warn|error|info|trace)$/]",
+                "selector": "CallExpression[callee.object.name='console'][callee.property.name!=/^(log|warn|error|info|trace)$/]",
                 "message": "Unexpected property on console object was called"
             }
         ]
diff --git a/lib/cli-engine.js b/lib/cli-engine.js
index 652d68b59..ce9fe289b 100644
--- a/lib/cli-engine.js
+++ b/lib/cli-engine.js
@@ -20,7 +20,7 @@ const fs = require("fs"),
     defaultOptions = require("../conf/default-cli-options"),
     Linter = require("./linter"),
     lodash = require("lodash"),
-    IgnoredPaths = require("./ignored-paths"),
+    IgnoredPaths = require("./util/ignored-paths"),
     Config = require("./config"),
     ConfigOps = require("./config/config-ops"),
     LintResultCache = require("./util/lint-result-cache"),
diff --git a/lib/config.js b/lib/config.js
index 8ea9aeb6f..abcb38b50 100644
--- a/lib/config.js
+++ b/lib/config.js
@@ -15,8 +15,7 @@ const path = require("path"),
     ConfigFile = require("./config/config-file"),
     ConfigCache = require("./config/config-cache"),
     Plugins = require("./config/plugins"),
-    FileFinder = require("./util/file-finder"),
-    isResolvable = require("is-resolvable");
+    FileFinder = require("./util/file-finder");
 
 const debug = require("debug")("eslint:config");
 
@@ -41,6 +40,20 @@ function hasRules(options) {
     return options.rules && Object.keys(options.rules).length > 0;
 }
 
+/**
+ * Determines if a module is can be resolved.
+ * @param {string} moduleId The ID (name) of the module
+ * @returns {boolean} True if it is resolvable; False otherwise.
+ */
+function isResolvable(moduleId) {
+    try {
+        require.resolve(moduleId);
+        return true;
+    } catch (err) {
+        return false;
+    }
+}
+
 //------------------------------------------------------------------------------
 // API
 //------------------------------------------------------------------------------
diff --git a/lib/config/autoconfig.js b/lib/config/autoconfig.js
index 8536fdc55..f2c707be8 100644
--- a/lib/config/autoconfig.js
+++ b/lib/config/autoconfig.js
@@ -102,7 +102,6 @@ class Registry {
      *
      * The length of the returned array will be <= MAX_CONFIG_COMBINATIONS.
      *
-     * @param   {Object}   registry The autoconfig registry
      * @returns {Object[]}          "rules" configurations to use for linting
      */
     buildRuleSets() {
diff --git a/lib/config/config-rule.js b/lib/config/config-rule.js
index 27d29446d..e4221406d 100644
--- a/lib/config/config-rule.js
+++ b/lib/config/config-rule.js
@@ -37,9 +37,9 @@ function explodeArray(xs) {
  * For example:
  * combineArrays([a, [b, c]], [x, y]); // -> [[a, x], [a, y], [b, c, x], [b, c, y]]
  *
- * @param   {array} arr1 The first array to combine.
- * @param   {array} arr2 The second array to combine.
- * @returns {array}      A mixture of the elements of the first and second arrays.
+ * @param   {Array} arr1 The first array to combine.
+ * @param   {Array} arr2 The second array to combine.
+ * @returns {Array}      A mixture of the elements of the first and second arrays.
  */
 function combineArrays(arr1, arr2) {
     const res = [];
@@ -268,7 +268,7 @@ class RuleConfigSet {
 /**
  * Generate valid rule configurations based on a schema object
  * @param   {Object} schema  A rule's schema object
- * @returns {array[]}        Valid rule configurations
+ * @returns {Array[]}        Valid rule configurations
  */
 function generateConfigsFromSchema(schema) {
     const configSet = new RuleConfigSet();
diff --git a/lib/config/config-validator.js b/lib/config/config-validator.js
index 6b151f46a..0c63b68f7 100644
--- a/lib/config/config-validator.js
+++ b/lib/config/config-validator.js
@@ -83,7 +83,7 @@ function validateRuleSeverity(options) {
 /**
  * Validates the non-severity options passed to a rule, based on its schema.
  * @param {{create: Function}} rule The rule to validate
- * @param {array} localOptions The options for the rule, excluding severity
+ * @param {Array} localOptions The options for the rule, excluding severity
  * @returns {void}
  */
 function validateRuleSchema(rule, localOptions) {
@@ -111,7 +111,7 @@ function validateRuleSchema(rule, localOptions) {
  * Validates a rule's options against its schema.
  * @param {{create: Function}|null} rule The rule that the config is being validated for
  * @param {string} ruleId The rule's unique name.
- * @param {array|number} options The given options for the rule.
+ * @param {Array|number} options The given options for the rule.
  * @param {string|null} source The name of the configuration source to report in any errors. If null or undefined,
  * no source is prepended to the message.
  * @returns {void}
diff --git a/lib/linter.js b/lib/linter.js
index edcf74106..d6d635f9f 100644
--- a/lib/linter.js
+++ b/lib/linter.js
@@ -11,7 +11,6 @@
 
 const eslintScope = require("eslint-scope"),
     evk = require("eslint-visitor-keys"),
-    levn = require("levn"),
     lodash = require("lodash"),
     CodePathAnalyzer = require("./code-path-analysis/code-path-analyzer"),
     ConfigOps = require("./config/config-ops"),
@@ -22,9 +21,10 @@ const eslintScope = require("eslint-scope"),
     NodeEventGenerator = require("./util/node-event-generator"),
     SourceCode = require("./util/source-code"),
     Traverser = require("./util/traverser"),
-    createReportTranslator = require("./report-translator"),
+    createReportTranslator = require("./util/report-translator"),
     Rules = require("./rules"),
     timing = require("./util/timing"),
+    ConfigCommentParser = require("./util/config-comment-parser"),
     astUtils = require("./util/ast-utils"),
     pkg = require("../package.json"),
     SourceCodeFixer = require("./util/source-code-fixer");
@@ -32,6 +32,7 @@ const eslintScope = require("eslint-scope"),
 const debug = require("debug")("eslint:linter");
 const MAX_AUTOFIX_PASSES = 10;
 const DEFAULT_PARSER_NAME = "espree";
+const commentParser = new ConfigCommentParser();
 
 //------------------------------------------------------------------------------
 // Typedefs
@@ -59,112 +60,6 @@ const DEFAULT_PARSER_NAME = "espree";
 // Helpers
 //------------------------------------------------------------------------------
 
-/**
- * Parses a list of "name:boolean_value" or/and "name" options divided by comma or
- * whitespace.
- * @param {string} string The string to parse.
- * @param {Comment} comment The comment node which has the string.
- * @returns {Object} Result map object of names and boolean values
- */
-function parseBooleanConfig(string, comment) {
-    const items = {};
-
-    // Collapse whitespace around `:` and `,` to make parsing easier
-    const trimmedString = string.replace(/\s*([:,])\s*/g, "$1");
-
-    trimmedString.split(/\s|,+/).forEach(name => {
-        if (!name) {
-            return;
-        }
-
-        // value defaults to "false" (if not provided), e.g: "foo" => ["foo", "false"]
-        const [key, value = "false"] = name.split(":");
-
-        items[key] = {
-            value: value === "true",
-            comment
-        };
-    });
-    return items;
-}
-
-/**
- * Parses a JSON-like config.
- * @param {string} string The string to parse.
- * @param {Object} location Start line and column of comments for potential error message.
- * @returns {({success: true, config: Object}|{success: false, error: Problem})} Result map object
- */
-function parseJsonConfig(string, location) {
-    let items = {};
-
-    // Parses a JSON-like comment by the same way as parsing CLI option.
-    try {
-        items = levn.parse("Object", string) || {};
-
-        // Some tests say that it should ignore invalid comments such as `/*eslint no-alert:abc*/`.
-        // Also, commaless notations have invalid severity:
-        //     "no-alert: 2 no-console: 2" --> {"no-alert": "2 no-console: 2"}
-        // Should ignore that case as well.
-        if (ConfigOps.isEverySeverityValid(items)) {
-            return {
-                success: true,
-                config: items
-            };
-        }
-    } catch (ex) {
-
-        // ignore to parse the string by a fallback.
-    }
-
-    /*
-     * Optionator cannot parse commaless notations.
-     * But we are supporting that. So this is a fallback for that.
-     */
-    items = {};
-    const normalizedString = string.replace(/([a-zA-Z0-9\-/]+):/g, "\"$1\":").replace(/(]|[0-9])\s+(?=")/, "$1,");
-
-    try {
-        items = JSON.parse(`{${normalizedString}}`);
-    } catch (ex) {
-        return {
-            success: false,
-            error: {
-                ruleId: null,
-                fatal: true,
-                severity: 2,
-                message: `Failed to parse JSON from '${normalizedString}': ${ex.message}`,
-                line: location.start.line,
-                column: location.start.column + 1
-            }
-        };
-
-    }
-
-    return {
-        success: true,
-        config: items
-    };
-}
-
-/**
- * Parses a config of values separated by comma.
- * @param {string} string The string to parse.
- * @returns {Object} Result map of values and true values
- */
-function parseListConfig(string) {
-    const items = {};
-
-    // Collapse whitespace around ,
-    string.replace(/\s*,\s*/g, ",").split(/,+/).forEach(name => {
-        const trimmedName = name.trim();
-
-        if (trimmedName) {
-            items[trimmedName] = true;
-        }
-    });
-    return items;
-}
-
 /**
  * Ensures that variables representing built-in properties of the Global Object,
  * and any globals declared by special block comments, are present in the global
@@ -243,7 +138,7 @@ function addDeclaredGlobals(globalScope, configGlobals, commentDirectives) {
  * @returns {DisableDirective[]} Directives from the comment
  */
 function createDisableDirectives(type, loc, value) {
-    const ruleIds = Object.keys(parseListConfig(value));
+    const ruleIds = Object.keys(commentParser.parseListConfig(value));
     const directiveRules = ruleIds.length ? ruleIds : [null];
 
     return directiveRules.map(ruleId => ({ type, line: loc.line, column: loc.column + 1, ruleId }));
@@ -296,12 +191,12 @@ function getDirectiveComments(filename, ast, ruleMapper) {
         } else if (comment.type === "Block") {
             switch (match[1]) {
                 case "exported":
-                    Object.assign(exportedVariables, parseBooleanConfig(directiveValue, comment));
+                    Object.assign(exportedVariables, commentParser.parseBooleanConfig(directiveValue, comment));
                     break;
 
                 case "globals":
                 case "global":
-                    Object.assign(enabledGlobals, parseBooleanConfig(directiveValue, comment));
+                    Object.assign(enabledGlobals, commentParser.parseBooleanConfig(directiveValue, comment));
                     break;
 
                 case "eslint-disable":
@@ -313,7 +208,7 @@ function getDirectiveComments(filename, ast, ruleMapper) {
                     break;
 
                 case "eslint": {
-                    const parseResult = parseJsonConfig(directiveValue, comment.loc);
+                    const parseResult = commentParser.parseJsonConfig(directiveValue, comment.loc);
 
                     if (parseResult.success) {
                         Object.keys(parseResult.config).forEach(name => {
@@ -393,7 +288,7 @@ function findEslintEnv(text) {
     eslintEnvPattern.lastIndex = 0;
 
     while ((match = eslintEnvPattern.exec(text))) {
-        retv = Object.assign(retv || {}, parseListConfig(match[1]));
+        retv = Object.assign(retv || {}, commentParser.parseListConfig(match[1]));
     }
 
     return retv;
@@ -1027,8 +922,6 @@ module.exports = class Linter {
      * @param {(string|Object)} [filenameOrOptions] The optional filename of the file being checked.
      *      If this is not set, the filename will default to '<input>' in the rule context. If
      *      an object, then it has "filename", "saveState", and "allowInlineConfig" properties.
-     * @param {boolean} [saveState] Indicates if the state from the last run should be saved.
-     *      Mostly useful for testing purposes.
      * @param {boolean} [filenameOrOptions.allowInlineConfig] Allow/disallow inline comments' ability to change config once it is set. Defaults to true if not supplied.
      *      Useful if you want to validate JS without comments overriding rules.
      * @param {function(string): string[]} [filenameOrOptions.preprocess] preprocessor for source text. If provided,
diff --git a/lib/rules/array-element-newline.js b/lib/rules/array-element-newline.js
index 440290917..dadb26fdd 100644
--- a/lib/rules/array-element-newline.js
+++ b/lib/rules/array-element-newline.js
@@ -173,7 +173,6 @@ module.exports = {
          * Reports a given node if it violated this rule.
          *
          * @param {ASTNode} node - A node to check. This is an ObjectExpression node or an ObjectPattern node.
-         * @param {{multiline: boolean, minItems: number}} options - An option object.
          * @returns {void}
          */
         function check(node) {
diff --git a/lib/rules/camelcase.js b/lib/rules/camelcase.js
index 8aeb4b5bd..6fb8760f1 100644
--- a/lib/rules/camelcase.js
+++ b/lib/rules/camelcase.js
@@ -132,9 +132,10 @@ module.exports = {
 
                 /*
                  * Leading and trailing underscores are commonly used to flag
-                 * private/protected identifiers, strip them
+                 * private/protected identifiers, strip them before checking if underscored
                  */
-                const name = node.name.replace(/^_+|_+$/g, ""),
+                const name = node.name,
+                    nameIsUnderscored = isUnderscored(name.replace(/^_+|_+$/g, "")),
                     effectiveParent = (node.parent.type === "MemberExpression") ? node.parent.parent : node.parent;
 
                 // First, we ignore the node if it match the ignore list
@@ -151,11 +152,11 @@ module.exports = {
                     }
 
                     // Always report underscored object names
-                    if (node.parent.object.type === "Identifier" && node.parent.object.name === node.name && isUnderscored(name)) {
+                    if (node.parent.object.type === "Identifier" && node.parent.object.name === node.name && nameIsUnderscored) {
                         report(node);
 
                     // Report AssignmentExpressions only if they are the left side of the assignment
-                    } else if (effectiveParent.type === "AssignmentExpression" && isUnderscored(name) && (effectiveParent.right.type !== "MemberExpression" || effectiveParent.left.type === "MemberExpression" && effectiveParent.left.property.name === node.name)) {
+                    } else if (effectiveParent.type === "AssignmentExpression" && nameIsUnderscored && (effectiveParent.right.type !== "MemberExpression" || effectiveParent.left.type === "MemberExpression" && effectiveParent.left.property.name === node.name)) {
                         report(node);
                     }
 
@@ -167,7 +168,7 @@ module.exports = {
                 } else if (node.parent.type === "Property" || node.parent.type === "AssignmentPattern") {
 
                     if (node.parent.parent && node.parent.parent.type === "ObjectPattern") {
-                        if (node.parent.shorthand && node.parent.value.left && isUnderscored(name)) {
+                        if (node.parent.shorthand && node.parent.value.left && nameIsUnderscored) {
 
                             report(node);
                         }
@@ -179,7 +180,7 @@ module.exports = {
                             return;
                         }
 
-                        const valueIsUnderscored = node.parent.value.name && isUnderscored(name);
+                        const valueIsUnderscored = node.parent.value.name && nameIsUnderscored;
 
                         // ignore destructuring if the option is set, unless a new identifier is created
                         if (valueIsUnderscored && !(assignmentKeyEqualsValue && ignoreDestructuring)) {
@@ -193,7 +194,7 @@ module.exports = {
                     }
 
                     // don't check right hand side of AssignmentExpression to prevent duplicate warnings
-                    if (isUnderscored(name) && !ALLOWED_PARENT_TYPES.has(effectiveParent.type) && !(node.parent.right === node)) {
+                    if (nameIsUnderscored && !ALLOWED_PARENT_TYPES.has(effectiveParent.type) && !(node.parent.right === node)) {
                         report(node);
                     }
 
@@ -201,12 +202,12 @@ module.exports = {
                 } else if (["ImportSpecifier", "ImportNamespaceSpecifier", "ImportDefaultSpecifier"].indexOf(node.parent.type) >= 0) {
 
                     // Report only if the local imported identifier is underscored
-                    if (node.parent.local && node.parent.local.name === node.name && isUnderscored(name)) {
+                    if (node.parent.local && node.parent.local.name === node.name && nameIsUnderscored) {
                         report(node);
                     }
 
                 // Report anything that is underscored that isn't a CallExpression
-                } else if (isUnderscored(name) && !ALLOWED_PARENT_TYPES.has(effectiveParent.type)) {
+                } else if (nameIsUnderscored && !ALLOWED_PARENT_TYPES.has(effectiveParent.type)) {
                     report(node);
                 }
             }
diff --git a/lib/rules/eqeqeq.js b/lib/rules/eqeqeq.js
index 715c5ce7c..3e8a392cf 100644
--- a/lib/rules/eqeqeq.js
+++ b/lib/rules/eqeqeq.js
@@ -119,7 +119,6 @@ module.exports = {
         /**
          * Gets the location (line and column) of the binary expression's operator
          * @param {ASTNode} node The binary expression node to check
-         * @param {string} operator The operator to find
          * @returns {Object} { line, column } location of operator
          * @private
          */
diff --git a/lib/rules/handle-callback-err.js b/lib/rules/handle-callback-err.js
index c62016d58..5845aff4b 100644
--- a/lib/rules/handle-callback-err.js
+++ b/lib/rules/handle-callback-err.js
@@ -59,7 +59,7 @@ module.exports = {
         /**
          * Get the parameters of a given function scope.
          * @param {Object} scope The function scope.
-         * @returns {array} All parameters of the given scope.
+         * @returns {Array} All parameters of the given scope.
          */
         function getParameters(scope) {
             return scope.variables.filter(variable => variable.defs[0] && variable.defs[0].type === "Parameter");
diff --git a/lib/rules/implicit-arrow-linebreak.js b/lib/rules/implicit-arrow-linebreak.js
index cd729f8ad..a883b2264 100644
--- a/lib/rules/implicit-arrow-linebreak.js
+++ b/lib/rules/implicit-arrow-linebreak.js
@@ -44,7 +44,6 @@ module.exports = {
         /**
          * Validates the location of an arrow function body
          * @param {ASTNode} node The arrow function body
-         * @param {string} keywordName The applicable keyword name for the arrow function body
          * @returns {void}
          */
         function validateExpression(node) {
diff --git a/lib/rules/indent-legacy.js b/lib/rules/indent-legacy.js
index 16687b521..cfb807e34 100644
--- a/lib/rules/indent-legacy.js
+++ b/lib/rules/indent-legacy.js
@@ -300,7 +300,6 @@ module.exports = {
          * @param {int} gottenTabs Indentation tab count in the actual node/code
          * @param {Object=} loc Error line and column location
          * @param {boolean} isLastNodeCheck Is the error for last node check
-         * @param {int} lastNodeCheckEndOffset Number of charecters to skip from the end
          * @returns {void}
          */
         function report(node, needed, gottenSpaces, gottenTabs, loc, isLastNodeCheck) {
@@ -365,7 +364,6 @@ module.exports = {
          * Check indent for node
          * @param {ASTNode} node Node to check
          * @param {int} neededIndent needed indent
-         * @param {boolean} [excludeCommas=false] skip comma on start of line
          * @returns {void}
          */
         function checkNodeIndent(node, neededIndent) {
@@ -413,7 +411,6 @@ module.exports = {
          * Check indent for nodes list
          * @param {ASTNode[]} nodes list of node objects
          * @param {int} indent needed indent
-         * @param {boolean} [excludeCommas=false] skip comma on start of line
          * @returns {void}
          */
         function checkNodesIndent(nodes, indent) {
diff --git a/lib/rules/indent.js b/lib/rules/indent.js
index dc9fbaf90..0b87412c8 100644
--- a/lib/rules/indent.js
+++ b/lib/rules/indent.js
@@ -1229,9 +1229,13 @@ module.exports = {
                 }
 
                 const fromToken = sourceCode.getLastToken(node, token => token.type === "Identifier" && token.value === "from");
+                const sourceToken = sourceCode.getLastToken(node, token => token.type === "String");
+                const semiToken = sourceCode.getLastToken(node, token => token.type === "Punctuator" && token.value === ";");
 
                 if (fromToken) {
-                    offsets.setDesiredOffsets([fromToken.range[0], node.range[1]], sourceCode.getFirstToken(node), 1);
+                    const end = semiToken && semiToken.range[1] === sourceToken.range[1] ? node.range[1] : sourceToken.range[1];
+
+                    offsets.setDesiredOffsets([fromToken.range[0], end], sourceCode.getFirstToken(node), 1);
                 }
             },
 
diff --git a/lib/rules/keyword-spacing.js b/lib/rules/keyword-spacing.js
index 66ce2cb34..790d7b031 100644
--- a/lib/rules/keyword-spacing.js
+++ b/lib/rules/keyword-spacing.js
@@ -453,6 +453,10 @@ module.exports = {
             checkSpacingBefore(firstToken, PREV_TOKEN_M);
             checkSpacingAfter(firstToken, NEXT_TOKEN_M);
 
+            if (node.type === "ExportDefaultDeclaration") {
+                checkSpacingAround(sourceCode.getTokenAfter(firstToken));
+            }
+
             if (node.source) {
                 const fromToken = sourceCode.getTokenBefore(node.source);
 
@@ -554,7 +558,7 @@ module.exports = {
             // Statements - Declarations
             ClassDeclaration: checkSpacingForClass,
             ExportNamedDeclaration: checkSpacingForModuleDeclaration,
-            ExportDefaultDeclaration: checkSpacingAroundFirstToken,
+            ExportDefaultDeclaration: checkSpacingForModuleDeclaration,
             ExportAllDeclaration: checkSpacingForModuleDeclaration,
             FunctionDeclaration: checkSpacingForFunction,
             ImportDeclaration: checkSpacingForModuleDeclaration,
diff --git a/lib/rules/newline-before-return.js b/lib/rules/newline-before-return.js
index 2743bf7a8..6a0ec8d5e 100644
--- a/lib/rules/newline-before-return.js
+++ b/lib/rules/newline-before-return.js
@@ -36,7 +36,7 @@ module.exports = {
         /**
          * Tests whether node is preceded by supplied tokens
          * @param {ASTNode} node - node to check
-         * @param {array} testTokens - array of tokens to test against
+         * @param {Array} testTokens - array of tokens to test against
          * @returns {boolean} Whether or not the node is preceded by one of the supplied tokens
          * @private
          */
diff --git a/lib/rules/no-constant-condition.js b/lib/rules/no-constant-condition.js
index 88984c36e..8d7934d8a 100644
--- a/lib/rules/no-constant-condition.js
+++ b/lib/rules/no-constant-condition.js
@@ -173,7 +173,6 @@ module.exports = {
 
         /**
          * Reports when the set still contains stored constant conditions
-         * @param {ASTNode} node The AST node to check.
          * @returns {void}
          * @private
          */
diff --git a/lib/rules/no-else-return.js b/lib/rules/no-else-return.js
index eebdec76e..5ba02de29 100644
--- a/lib/rules/no-else-return.js
+++ b/lib/rules/no-else-return.js
@@ -183,7 +183,6 @@ module.exports = {
          * code paths.
          *
          * @param {Node} node The consequent or body node
-         * @param {Node} alternate The alternate node
          * @returns {boolean} `true` if it is a Return/If node that always returns.
          */
         function checkForReturnOrIf(node) {
diff --git a/lib/rules/no-implied-eval.js b/lib/rules/no-implied-eval.js
index d31b5dfee..afa24ab8e 100644
--- a/lib/rules/no-implied-eval.js
+++ b/lib/rules/no-implied-eval.js
@@ -38,7 +38,7 @@ module.exports = {
 
         /**
          * Get the last element of an array, without modifying arr, like pop(), but non-destructive.
-         * @param {array} arr What to inspect
+         * @param {Array} arr What to inspect
          * @returns {*} The last element of arr
          * @private
          */
diff --git a/lib/rules/no-restricted-imports.js b/lib/rules/no-restricted-imports.js
index b8fcca1aa..b8917ee9c 100644
--- a/lib/rules/no-restricted-imports.js
+++ b/lib/rules/no-restricted-imports.js
@@ -195,7 +195,7 @@ module.exports = {
         /**
          * Check if the given importNames are restricted given a list of restrictedImportNames.
          * @param {Set.<string>} importNames - Set of import names that are being imported
-         * @param {[string]} restrictedImportNames - array of import names that are restricted for this import
+         * @param {string[]} restrictedImportNames - array of import names that are restricted for this import
          * @returns {boolean} whether the objectName is restricted
          * @private
          */
diff --git a/lib/rules/no-this-before-super.js b/lib/rules/no-this-before-super.js
index 93fb094e9..bca379bf9 100644
--- a/lib/rules/no-this-before-super.js
+++ b/lib/rules/no-this-before-super.js
@@ -171,7 +171,6 @@ module.exports = {
              * invalid node.
              *
              * @param {CodePath} codePath - A code path which was ended.
-             * @param {ASTNode} node - The current node.
              * @returns {void}
              */
             onCodePathEnd(codePath) {
diff --git a/lib/rules/object-curly-newline.js b/lib/rules/object-curly-newline.js
index c460ea56b..d1a6bc20f 100644
--- a/lib/rules/object-curly-newline.js
+++ b/lib/rules/object-curly-newline.js
@@ -172,7 +172,6 @@ module.exports = {
         /**
          * Reports a given node if it violated this rule.
          * @param {ASTNode} node - A node to check. This is an ObjectExpression, ObjectPattern, ImportDeclaration or ExportNamedDeclaration node.
-         * @param {{multiline: boolean, minProperties: number, consistent: boolean}} options - An option object.
          * @returns {void}
          */
         function check(node) {
diff --git a/lib/rules/one-var.js b/lib/rules/one-var.js
index 44f05fb70..0e9dff416 100644
--- a/lib/rules/one-var.js
+++ b/lib/rules/one-var.js
@@ -310,7 +310,6 @@ module.exports = {
         /**
          * Fixer to split a VariableDeclaration into individual declarations
          * @param   {VariableDeclaration}   declaration The `VariableDeclaration` to split
-         * @param   {?Function}             filter      Function to filter the declarations
          * @returns {Function}                          The fixer function
          */
         function splitDeclarations(declaration) {
diff --git a/lib/rules/space-in-parens.js b/lib/rules/space-in-parens.js
index 88f4f0b50..306866263 100644
--- a/lib/rules/space-in-parens.js
+++ b/lib/rules/space-in-parens.js
@@ -61,7 +61,6 @@ module.exports = {
 
         /**
          * Produces an object with the opener and closer exception values
-         * @param {Object} opts The exception options
          * @returns {Object} `openers` and `closers` exception values
          * @private
          */
diff --git a/lib/util/config-comment-parser.js b/lib/util/config-comment-parser.js
new file mode 100644
index 000000000..88504caf1
--- /dev/null
+++ b/lib/util/config-comment-parser.js
@@ -0,0 +1,144 @@
+/**
+ * @fileoverview Config Comment Parser
+ * @author Nicholas C. Zakas
+ */
+
+/* eslint-disable class-methods-use-this*/
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const levn = require("levn"),
+    ConfigOps = require("../config/config-ops");
+
+const debug = require("debug")("eslint:config-comment-parser");
+
+//------------------------------------------------------------------------------
+// Public Interface
+//------------------------------------------------------------------------------
+
+/**
+ * Object to parse ESLint configuration comments inside JavaScript files.
+ * @name ConfigCommentParser
+ */
+module.exports = class ConfigCommentParser {
+
+    /**
+     * Parses a list of "name:boolean_value" or/and "name" options divided by comma or
+     * whitespace. Used for "global" and "exported" comments.
+     * @param {string} string The string to parse.
+     * @param {Comment} comment The comment node which has the string.
+     * @returns {Object} Result map object of names and boolean values
+     */
+    parseBooleanConfig(string, comment) {
+        debug("Parsing Boolean config");
+
+        const items = {};
+
+        // Collapse whitespace around `:` and `,` to make parsing easier
+        const trimmedString = string.replace(/\s*([:,])\s*/g, "$1");
+
+        trimmedString.split(/\s|,+/).forEach(name => {
+            if (!name) {
+                return;
+            }
+
+            // value defaults to "false" (if not provided), e.g: "foo" => ["foo", "false"]
+            const [key, value = "false"] = name.split(":");
+
+            items[key] = {
+                value: value === "true",
+                comment
+            };
+        });
+        return items;
+    }
+
+    /**
+     * Parses a JSON-like config.
+     * @param {string} string The string to parse.
+     * @param {Object} location Start line and column of comments for potential error message.
+     * @returns {({success: true, config: Object}|{success: false, error: Problem})} Result map object
+     */
+    parseJsonConfig(string, location) {
+        debug("Parsing JSON config");
+
+        let items = {};
+
+        // Parses a JSON-like comment by the same way as parsing CLI option.
+        try {
+            items = levn.parse("Object", string) || {};
+
+            // Some tests say that it should ignore invalid comments such as `/*eslint no-alert:abc*/`.
+            // Also, commaless notations have invalid severity:
+            //     "no-alert: 2 no-console: 2" --> {"no-alert": "2 no-console: 2"}
+            // Should ignore that case as well.
+            if (ConfigOps.isEverySeverityValid(items)) {
+                return {
+                    success: true,
+                    config: items
+                };
+            }
+        } catch (ex) {
+
+            debug("Levn parsing failed; falling back to manual parsing.");
+
+            // ignore to parse the string by a fallback.
+        }
+
+        /*
+         * Optionator cannot parse commaless notations.
+         * But we are supporting that. So this is a fallback for that.
+         */
+        items = {};
+        const normalizedString = string.replace(/([a-zA-Z0-9\-/]+):/g, "\"$1\":").replace(/(]|[0-9])\s+(?=")/, "$1,");
+
+        try {
+            items = JSON.parse(`{${normalizedString}}`);
+        } catch (ex) {
+            debug("Manual parsing failed.");
+
+            return {
+                success: false,
+                error: {
+                    ruleId: null,
+                    fatal: true,
+                    severity: 2,
+                    message: `Failed to parse JSON from '${normalizedString}': ${ex.message}`,
+                    line: location.start.line,
+                    column: location.start.column + 1
+                }
+            };
+
+        }
+
+        return {
+            success: true,
+            config: items
+        };
+    }
+
+    /**
+     * Parses a config of values separated by comma.
+     * @param {string} string The string to parse.
+     * @returns {Object} Result map of values and true values
+     */
+    parseListConfig(string) {
+        debug("Parsing list config");
+
+        const items = {};
+
+        // Collapse whitespace around commas
+        string.replace(/\s*,\s*/g, ",").split(/,+/).forEach(name => {
+            const trimmedName = name.trim();
+
+            if (trimmedName) {
+                items[trimmedName] = true;
+            }
+        });
+        return items;
+    }
+
+};
diff --git a/lib/util/glob-utils.js b/lib/util/glob-utils.js
index a2be0f042..fd4cfa008 100644
--- a/lib/util/glob-utils.js
+++ b/lib/util/glob-utils.js
@@ -14,7 +14,7 @@ const lodash = require("lodash"),
     GlobSync = require("./glob"),
 
     pathUtils = require("./path-utils"),
-    IgnoredPaths = require("../ignored-paths");
+    IgnoredPaths = require("./ignored-paths");
 
 const debug = require("debug")("eslint:glob-utils");
 
diff --git a/lib/ignored-paths.js b/lib/util/ignored-paths.js
similarity index 98%
rename from lib/ignored-paths.js
rename to lib/util/ignored-paths.js
index 8520090af..de4a79cde 100644
--- a/lib/ignored-paths.js
+++ b/lib/util/ignored-paths.js
@@ -12,7 +12,7 @@
 const fs = require("fs"),
     path = require("path"),
     ignore = require("ignore"),
-    pathUtils = require("./util/path-utils");
+    pathUtils = require("./path-utils");
 
 const debug = require("debug")("eslint:ignored-paths");
 
@@ -296,7 +296,7 @@ class IgnoredPaths {
     /**
      * read ignore filepath
      * @param {string} filePath, file to add to ig
-     * @returns {array} raw ignore rules
+     * @returns {Array} raw ignore rules
      */
     readIgnoreFile(filePath) {
         if (typeof this.cache[filePath] === "undefined") {
@@ -307,8 +307,8 @@ class IgnoredPaths {
 
     /**
      * add ignore file to node-ignore instance
-     * @param {Object} ig, instance of node-ignore
-     * @param {string} filePath, file to add to ig
+     * @param {Object} ig instance of node-ignore
+     * @param {string} filePath file to add to ig
      * @returns {void}
      */
     addIgnoreFile(ig, filePath) {
diff --git a/lib/report-translator.js b/lib/util/report-translator.js
similarity index 99%
rename from lib/report-translator.js
rename to lib/util/report-translator.js
index f82830586..3dfdca0e4 100644
--- a/lib/report-translator.js
+++ b/lib/util/report-translator.js
@@ -10,8 +10,8 @@
 //------------------------------------------------------------------------------
 
 const assert = require("assert");
-const ruleFixer = require("./util/rule-fixer");
-const interpolate = require("./util/interpolate");
+const ruleFixer = require("./rule-fixer");
+const interpolate = require("./interpolate");
 
 //------------------------------------------------------------------------------
 // Typedefs
diff --git a/lib/util/source-code.js b/lib/util/source-code.js
index dd45f6112..765fde725 100644
--- a/lib/util/source-code.js
+++ b/lib/util/source-code.js
@@ -87,7 +87,7 @@ class SourceCode extends TokenStore {
      * @param {string|Object} textOrConfig - The source code text or config object.
      * @param {string} textOrConfig.text - The source code text.
      * @param {ASTNode} textOrConfig.ast - The Program node of the AST representing the code. This AST should be created from the text that BOM was stripped.
-     * @param {Object|null} textOrConfig.parserServices - The parser srevices.
+     * @param {Object|null} textOrConfig.parserServices - The parser services.
      * @param {ScopeManager|null} textOrConfig.scopeManager - The scope of this source code.
      * @param {Object|null} textOrConfig.visitorKeys - The visitor keys to traverse AST.
      * @param {ASTNode} [astIfNoConfig] - The Program node of the AST representing the code. This AST should be created from the text that BOM was stripped.
diff --git a/package.json b/package.json
index c64318d0b..2cbe9f0b3 100644
--- a/package.json
+++ b/package.json
@@ -54,7 +54,6 @@
     "ignore": "^4.0.6",
     "imurmurhash": "^0.1.4",
     "inquirer": "^6.1.0",
-    "is-resolvable": "^1.1.0",
     "js-yaml": "^3.12.0",
     "json-stable-stringify-without-jsonify": "^1.0.1",
     "levn": "^0.3.0",
diff --git a/packages/eslint-config-eslint/default.yml b/packages/eslint-config-eslint/default.yml
index 30444d640..42ce7b1ec 100644
--- a/packages/eslint-config-eslint/default.yml
+++ b/packages/eslint-config-eslint/default.yml
@@ -181,6 +181,7 @@ rules:
             "String": "string",
             "Number": "number",
             "Boolean": "boolean",
+            "array": "Array",
             "object": "Object",
             "function": "Function"
         }


Test patch:
diff --git a/tests/lib/rules/camelcase.js b/tests/lib/rules/camelcase.js
index 3d2b7516c..d1ce3b3fd 100644
--- a/tests/lib/rules/camelcase.js
+++ b/tests/lib/rules/camelcase.js
@@ -193,6 +193,10 @@ ruleTester.run("camelcase", rule, {
         {
             code: "user_id = 0;",
             options: [{ allow: ["_id$"] }]
+        },
+        {
+            code: "__option_foo__ = 0;",
+            options: [{ allow: ["__option_foo__"] }]
         }
     ],
     invalid: [
diff --git a/tests/lib/rules/indent-legacy.js b/tests/lib/rules/indent-legacy.js
index 17a1f62a4..3e3a21684 100644
--- a/tests/lib/rules/indent-legacy.js
+++ b/tests/lib/rules/indent-legacy.js
@@ -24,7 +24,7 @@ const fixedFixture = fs.readFileSync(path.join(__dirname, "../../fixtures/rules/
 /**
  * Create error message object for failure cases with a single 'found' indentation type
  * @param {string} providedIndentType indent type of string or tab
- * @param {array} providedErrors error info
+ * @param {Array} providedErrors error info
  * @returns {Object} returns the error messages collection
  * @private
  */
diff --git a/tests/lib/rules/indent.js b/tests/lib/rules/indent.js
index b25e25949..8f20a8ea8 100644
--- a/tests/lib/rules/indent.js
+++ b/tests/lib/rules/indent.js
@@ -25,7 +25,7 @@ const parser = require("../../fixtures/fixture-parser");
 /**
  * Create error message object for failure cases with a single 'found' indentation type
  * @param {string} providedIndentType indent type of string or tab
- * @param {array} providedErrors error info
+ * @param {Array} providedErrors error info
  * @returns {Object} returns the error messages collection
  * @private
  */
@@ -271,6 +271,17 @@ ruleTester.run("indent", rule, {
             `,
             options: [4]
         },
+        {
+
+            // https://github.com/eslint/eslint/issues/11802
+            code: unIndent`
+                import foo from "foo"
+
+                ;(() => {})()
+            `,
+            options: [4],
+            parserOptions: { sourceType: "module" }
+        },
         {
             code: unIndent`
                 function test() {
diff --git a/tests/lib/rules/keyword-spacing.js b/tests/lib/rules/keyword-spacing.js
index 33a9d5068..cebd16367 100644
--- a/tests/lib/rules/keyword-spacing.js
+++ b/tests/lib/rules/keyword-spacing.js
@@ -2023,6 +2023,12 @@ ruleTester.run("keyword-spacing", rule, {
             parserOptions: { sourceType: "module" },
             errors: expectedBefore("export")
         },
+        {
+            code: "export default{a}",
+            output: "export default {a}",
+            parserOptions: { sourceType: "module" },
+            errors: expectedAfter("default")
+        },
         {
             code: "{}export* from \"a\"",
             output: "{} export * from \"a\"",
diff --git a/tests/lib/rules/no-empty-function.js b/tests/lib/rules/no-empty-function.js
index 416a47da6..21f1bdae0 100644
--- a/tests/lib/rules/no-empty-function.js
+++ b/tests/lib/rules/no-empty-function.js
@@ -31,9 +31,9 @@ const ALLOW_OPTIONS = Object.freeze([
  * Folds test items to `{valid: [], invalid: []}`.
  * One item would be converted to 4 valid patterns and 8 invalid patterns.
  *
- * @param {{valid: object[], invalid: object[]}} patterns - The result.
+ * @param {{valid: Object[], invalid: Object[]}} patterns - The result.
  * @param {{code: string, message: string, allow: string}} item - A test item.
- * @returns {{valid: object[], invalid: object[]}} The result.
+ * @returns {{valid: Object[], invalid: Object[]}} The result.
  */
 function toValidInvalid(patterns, item) {
 
diff --git a/tests/lib/util/config-comment-parser.js b/tests/lib/util/config-comment-parser.js
new file mode 100644
index 000000000..6dbe9381a
--- /dev/null
+++ b/tests/lib/util/config-comment-parser.js
@@ -0,0 +1,229 @@
+/**
+ * @fileoverview Tests for ConfigCommentParser object.
+ * @author Nicholas C. Zakas
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const assert = require("chai").assert,
+    ConfigCommentParser = require("../../../lib/util/config-comment-parser");
+
+//------------------------------------------------------------------------------
+// Tests
+//------------------------------------------------------------------------------
+
+describe("ConfigCommentParser", () => {
+
+    let commentParser;
+    const location = {
+        start: {
+            line: 1,
+            column: 0
+        }
+    };
+
+    beforeEach(() => {
+        commentParser = new ConfigCommentParser();
+    });
+
+    describe("parseJsonConfig", () => {
+
+        it("should parse JSON config with one item", () => {
+            const code = "no-alert:0";
+            const result = commentParser.parseJsonConfig(code, location);
+
+
+            assert.deepStrictEqual(result, {
+                success: true,
+                config: {
+                    "no-alert": 0
+                }
+            });
+        });
+
+        it("should parse JSON config with two items", () => {
+            const code = "no-alert:0 semi: 2";
+            const result = commentParser.parseJsonConfig(code, location);
+
+
+            assert.deepStrictEqual(result, {
+                success: true,
+                config: {
+                    "no-alert": 0,
+                    semi: 2
+                }
+            });
+        });
+
+        it("should parse JSON config with two comma-separated items", () => {
+            const code = "no-alert:0,semi: 2";
+            const result = commentParser.parseJsonConfig(code, location);
+
+
+            assert.deepStrictEqual(result, {
+                success: true,
+                config: {
+                    "no-alert": 0,
+                    semi: 2
+                }
+            });
+        });
+
+        it("should parse JSON config with two items and a string severity", () => {
+            const code = "no-alert:off,semi: 2";
+            const result = commentParser.parseJsonConfig(code, location);
+
+
+            assert.deepStrictEqual(result, {
+                success: true,
+                config: {
+                    "no-alert": "off",
+                    semi: 2
+                }
+            });
+        });
+
+        it("should parse JSON config with two items and options", () => {
+            const code = "no-alert:off, semi: [2, always]";
+            const result = commentParser.parseJsonConfig(code, location);
+
+
+            assert.deepStrictEqual(result, {
+                success: true,
+                config: {
+                    "no-alert": "off",
+                    semi: [2, "always"]
+                }
+            });
+        });
+
+        it("should parse JSON config with two items and options from plugins", () => {
+            const code = "plugin/no-alert:off, plugin/semi: [2, always]";
+            const result = commentParser.parseJsonConfig(code, location);
+
+            assert.deepStrictEqual(result, {
+                success: true,
+                config: {
+                    "plugin/no-alert": "off",
+                    "plugin/semi": [2, "always"]
+                }
+            });
+        });
+
+
+    });
+
+    describe("parseBooleanConfig", () => {
+
+        const comment = {};
+
+        it("should parse Boolean config with one item", () => {
+            const code = "a: true";
+            const result = commentParser.parseBooleanConfig(code, comment);
+
+            assert.deepStrictEqual(result, {
+                a: {
+                    value: true,
+                    comment
+                }
+            });
+        });
+
+        it("should parse Boolean config with one item and no value", () => {
+            const code = "a";
+            const result = commentParser.parseBooleanConfig(code, comment);
+
+            assert.deepStrictEqual(result, {
+                a: {
+                    value: false,
+                    comment
+                }
+            });
+        });
+
+        it("should parse Boolean config with two items", () => {
+            const code = "a: true b:false";
+            const result = commentParser.parseBooleanConfig(code, comment);
+
+            assert.deepStrictEqual(result, {
+                a: {
+                    value: true,
+                    comment
+                },
+                b: {
+                    value: false,
+                    comment
+                }
+            });
+        });
+
+        it("should parse Boolean config with two comma-separated items", () => {
+            const code = "a: true, b:false";
+            const result = commentParser.parseBooleanConfig(code, comment);
+
+            assert.deepStrictEqual(result, {
+                a: {
+                    value: true,
+                    comment
+                },
+                b: {
+                    value: false,
+                    comment
+                }
+            });
+        });
+
+        it("should parse Boolean config with two comma-separated items and no values", () => {
+            const code = "a , b";
+            const result = commentParser.parseBooleanConfig(code, comment);
+
+            assert.deepStrictEqual(result, {
+                a: {
+                    value: false,
+                    comment
+                },
+                b: {
+                    value: false,
+                    comment
+                }
+            });
+        });
+    });
+
+    describe("parseListConfig", () => {
+
+        it("should parse list config with one item", () => {
+            const code = "a";
+            const result = commentParser.parseListConfig(code);
+
+            assert.deepStrictEqual(result, {
+                a: true
+            });
+        });
+
+        it("should parse list config with two items", () => {
+            const code = "a, b";
+            const result = commentParser.parseListConfig(code);
+
+            assert.deepStrictEqual(result, {
+                a: true,
+                b: true
+            });
+        });
+
+        it("should parse list config with two items and exta whitespace", () => {
+            const code = "  a , b  ";
+            const result = commentParser.parseListConfig(code);
+
+            assert.deepStrictEqual(result, {
+                a: true,
+                b: true
+            });
+        });
+    });
+
+});
diff --git a/tests/lib/ignored-paths.js b/tests/lib/util/ignored-paths.js
similarity index 99%
rename from tests/lib/ignored-paths.js
rename to tests/lib/util/ignored-paths.js
index b03314e61..acf007b18 100644
--- a/tests/lib/ignored-paths.js
+++ b/tests/lib/util/ignored-paths.js
@@ -12,7 +12,7 @@
 const assert = require("chai").assert,
     path = require("path"),
     os = require("os"),
-    IgnoredPaths = require("../../lib/ignored-paths.js"),
+    IgnoredPaths = require("../../../lib/util/ignored-paths.js"),
     sinon = require("sinon"),
     fs = require("fs"),
     includes = require("lodash").includes;
@@ -29,7 +29,7 @@ let fixtureDir;
 
 /**
  * get raw rules from IgnorePaths instance
- * @param {IgnoredPaths} ignoredPaths, instance of IgnoredPaths
+ * @param {IgnoredPaths} ignoredPaths instance of IgnoredPaths
  * @returns {string[]} raw ignore rules
  */
 function getIgnoreRules(ignoredPaths) {
@@ -78,7 +78,7 @@ function getIgnorePatterns(ignoredPaths) {
 
 /**
  * count the number of default patterns applied to IgnoredPaths instance
- * @param {IgnoredPaths} ignoredPaths, instance of IgnoredPaths
+ * @param {IgnoredPaths} ignoredPaths instance of IgnoredPaths
  * @returns {integer} count of default patterns
  */
 function countDefaultPatterns(ignoredPaths) {
diff --git a/tests/lib/util/npm-utils.js b/tests/lib/util/npm-utils.js
index 1b07f3715..5c8e4cc84 100644
--- a/tests/lib/util/npm-utils.js
+++ b/tests/lib/util/npm-utils.js
@@ -162,8 +162,15 @@ describe("npmUtils", () => {
         });
 
         it("should return false if package.json does not exist", () => {
-            mockFs({});
-            assert.strictEqual(npmUtils.checkPackageJson(), false);
+
+            /*
+             * Checking local file system directly, which does not
+             * contains package.json. This is necessary because mock-fs
+             * doesn't work in Node.js 11: it throws an error when
+             * a file is missing rather than returning false.
+             * TODO: Find a replacement for mock-fs that works in Node 11.
+             */
+            assert.strictEqual(npmUtils.checkPackageJson("/"), false);
         });
     });
 
diff --git a/tests/lib/report-translator.js b/tests/lib/util/report-translator.js
similarity index 99%
rename from tests/lib/report-translator.js
rename to tests/lib/util/report-translator.js
index e009e4e82..cd600934d 100644
--- a/tests/lib/report-translator.js
+++ b/tests/lib/util/report-translator.js
@@ -9,9 +9,9 @@
 //------------------------------------------------------------------------------
 
 const assert = require("chai").assert;
-const SourceCode = require("../../lib/util/source-code");
+const SourceCode = require("../../../lib/util/source-code");
 const espree = require("espree");
-const createReportTranslator = require("../../lib/report-translator");
+const createReportTranslator = require("../../../lib/util/report-translator");
 
 //------------------------------------------------------------------------------
 // Tests