Repository: eslint/eslint
Pull request #11570 (2019-03-30 23:05:45)

Upgrade: karma@^4.0.1, drops Node 6 support, fixes vulnerability
<!--
    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
[ ] 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: Dev dependency upgrade

<!--
    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)**

Upgrade to `karma@^4.0.1`. This fixes some npm-reported security vulnerabilities.

**Is there anything you'd like reviewers to focus on?**

This version of Karma drops support for Node 6, so it would be a breaking change. I am not labeling this "Breaking" because I assume this can be merged after #11557 is merged. (Labeling "do not merge" here instead.)


Patch:
diff --git a/.travis.yml b/.travis.yml
index baf4d28fd..03cd84ead 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,8 +1,6 @@
 language: node_js
 node_js:
-    - "6"
     - "8"
-    - "9"
     - "10"
     - "11"
 
diff --git a/README.md b/README.md
index 172c956ad..f5b23947b 100644
--- a/README.md
+++ b/README.md
@@ -26,7 +26,7 @@ ESLint is a tool for identifying and reporting on patterns found in ECMAScript/J
 
 ## Installation and Usage
 
-Prerequisites: [Node.js](https://nodejs.org/en/) (>=6.14), npm version 3+.
+Prerequisites: [Node.js](https://nodejs.org/) (`^8.10.0`, `^10.13.0`, or `>=11.10.1`), npm version 3+.
 
 There are two ways to install ESLint: globally and locally.
 
@@ -243,18 +243,25 @@ Teddy Katz
 </td></tr></tbody></table>
 
 
+### Reviewers
 
-
-### Committers
-
-The people who review and fix bugs and help triage issues.
+The people who review and implement new features.
 
 <table><tbody><tr><td align="center" valign="top" width="11%">
 <a href="https://github.com/aladdin-add">
 <img src="https://github.com/aladdin-add.png?s=75" width="75" height="75"><br />
 薛定谔的猫
 </a>
-</td><td align="center" valign="top" width="11%">
+</td></tr></tbody></table>
+
+
+
+
+### Committers
+
+The people who review and fix bugs and help triage issues.
+
+<table><tbody><tr><td align="center" valign="top" width="11%">
 <a href="https://github.com/g-plane">
 <img src="https://github.com/g-plane.png?s=75" width="75" height="75"><br />
 Pig Fang
@@ -271,7 +278,7 @@ The following companies, organizations, and individuals support ESLint's ongoing
 <!-- NOTE: This section is autogenerated. Do not manually edit.-->
 <!--sponsorsstart-->
 <h3>Gold Sponsors</h3>
-<p><a href="https://www.airbnb.com/"><img src="https://images.opencollective.com/proxy/images/?src=https%3A%2F%2Fopencollective-production.s3-us-west-1.amazonaws.com%2F098e3bd0-4d57-11e8-9324-0f6cc1f92bf1.png&height=96" alt="Airbnb" height="96"></a> <a href="https://code.facebook.com/projects/"><img src="https://images.opencollective.com/proxy/images/?src=https%3A%2F%2Fres.cloudinary.com%2Fopencollective%2Fimage%2Fupload%2Fv1508519428%2FS9gk78AS_400x400_fulq2l.jpg&height=96" alt="Facebook Open Source" height="96"></a> <a href="https://badoo.com/team?utm_source=eslint"><img src="https://images.opencollective.com/proxy/images/?src=https%3A%2F%2Fopencollective-production.s3-us-west-1.amazonaws.com%2Fbbdb9cc0-3b5d-11e9-9537-ad85092287b8.png&height=96" alt="Badoo" height="96"></a></p><h3>Silver Sponsors</h3>
+<p><a href="https://www.gitkraken.com/"><img src="https://images.opencollective.com/proxy/images/?src=https%3A%2F%2Flogo.clearbit.com%2Fgitkraken.com&height=96" alt="GitKraken" height="96"></a> <a href="https://www.airbnb.com/"><img src="https://images.opencollective.com/proxy/images/?src=https%3A%2F%2Fopencollective-production.s3-us-west-1.amazonaws.com%2F098e3bd0-4d57-11e8-9324-0f6cc1f92bf1.png&height=96" alt="Airbnb" height="96"></a> <a href="https://code.facebook.com/projects/"><img src="https://images.opencollective.com/proxy/images/?src=https%3A%2F%2Fres.cloudinary.com%2Fopencollective%2Fimage%2Fupload%2Fv1508519428%2FS9gk78AS_400x400_fulq2l.jpg&height=96" alt="Facebook Open Source" height="96"></a> <a href="https://badoo.com/team?utm_source=eslint"><img src="https://images.opencollective.com/proxy/images/?src=https%3A%2F%2Fopencollective-production.s3-us-west-1.amazonaws.com%2Fbbdb9cc0-3b5d-11e9-9537-ad85092287b8.png&height=96" alt="Badoo" height="96"></a></p><h3>Silver Sponsors</h3>
 <p><a href="https://www.ampproject.org/"><img src="https://images.opencollective.com/proxy/images/?src=https%3A%2F%2Fopencollective-production.s3-us-west-1.amazonaws.com%2F68ed8b70-ebf2-11e6-9958-cb7e79408c56.png&height=96" alt="AMP Project" height="64"></a></p><h3>Bronze Sponsors</h3>
 <p><a href="http://faithlife.com/ref/about"><img src="https://images.opencollective.com/proxy/images/?src=https%3A%2F%2Flogo.clearbit.com%2Ffaithlife.com&height=96" alt="Faithlife" height="32"></a> <a href="https://jsheroes.io/"><img src="https://images.opencollective.com/proxy/images/?src=https%3A%2F%2Flogo.clearbit.com%2Fjsheroes.io&height=96" alt="JSHeroes " height="32"></a></p>
 <!--sponsorsend-->
diff --git a/conf/eslint-recommended.js b/conf/eslint-recommended.js
index 447a99638..d6368000f 100644
--- a/conf/eslint-recommended.js
+++ b/conf/eslint-recommended.js
@@ -9,7 +9,10 @@
 const builtInRules = require("../lib/built-in-rules-index");
 
 module.exports = {
-    rules: Object.assign({}, ...Object.keys(builtInRules).map(ruleId => ({
-        [ruleId]: builtInRules[ruleId].meta.docs.recommended ? "error" : "off"
-    })))
+    rules: Object.assign(
+        {},
+        ...Object.keys(builtInRules)
+            .filter(ruleId => builtInRules[ruleId].meta.docs.recommended)
+            .map(ruleId => ({ [ruleId]: "error" }))
+    )
 };
diff --git a/docs/rules/function-paren-newline.md b/docs/rules/function-paren-newline.md
index 0c886bf53..099021e0e 100644
--- a/docs/rules/function-paren-newline.md
+++ b/docs/rules/function-paren-newline.md
@@ -13,6 +13,7 @@ This rule has a single option, which can either be a string or an object.
 * `"always"` requires line breaks inside all function parentheses.
 * `"never"` disallows line breaks inside all function parentheses.
 * `"multiline"` (default) requires linebreaks inside function parentheses if any of the parameters/arguments have a line break between them. Otherwise, it disallows linebreaks.
+* `"multiline-arguments"` works like `multiline` but allows linebreaks inside function parentheses if there is only one parameter/argument.
 * `"consistent"` requires consistent usage of linebreaks for each pair of parentheses. It reports an error if one parenthesis in the pair has a linebreak inside it and the other parenthesis does not.
 * `{ "minItems": value }` requires linebreaks inside function parentheses if the number of parameters/arguments is at least `value`. Otherwise, it disallows linebreaks.
 
@@ -225,6 +226,56 @@ foo(
 );
 ```
 
+Examples of **incorrect** code for this rule with the `"multiline-arguments"` option:
+
+```js
+/* eslint function-paren-newline: ["error", "multiline-arguments"] */
+
+function foo(bar,
+  baz
+) {}
+
+var foo = function(bar,
+  baz
+) {};
+
+var foo = (
+  bar,
+  baz) => {};
+
+foo(
+  bar,
+  baz);
+
+foo(
+  bar, qux,
+  baz
+);
+```
+
+Examples of **correct** code for this rule with the consistent `"multiline-arguments"` option:
+
+```js
+/* eslint function-paren-newline: ["error", "multiline-arguments"] */
+
+function foo(
+  bar,
+  baz
+) {}
+
+var foo = function(bar, baz) {};
+
+var foo = (
+  bar
+) => {};
+
+foo(
+  function() {
+    return baz;
+  }
+);
+```
+
 Examples of **incorrect** code for this rule with the `{ "minItems": 3 }` option:
 
 ```js
diff --git a/docs/user-guide/configuring.md b/docs/user-guide/configuring.md
index ec91ed774..a9cbfa643 100644
--- a/docs/user-guide/configuring.md
+++ b/docs/user-guide/configuring.md
@@ -52,10 +52,6 @@ Here's an example `.eslintrc.json` file:
 
 Setting parser options helps ESLint determine what is a parsing error. All language options are `false` by default.
 
-### Deprecated
-
-* `ecmaFeatures.experimentalObjectRestSpread` - enable support for the experimental [object rest/spread properties](https://github.com/tc39/proposal-object-rest-spread). This syntax has been supported in `ecmaVersion: 2018`. This option will be removed in the future.
-
 ## Specifying Parser
 
 By default, ESLint uses [Espree](https://github.com/eslint/espree) as its parser. You can optionally specify that a different parser should be used in your configuration file so long as the parser meets the following requirements:
diff --git a/lib/config/config-ops.js b/lib/config/config-ops.js
index b38cdf7d7..6d89d6f7a 100644
--- a/lib/config/config-ops.js
+++ b/lib/config/config-ops.js
@@ -366,7 +366,7 @@ module.exports = {
             }
         });
 
-        const opts = { matchBase: true };
+        const opts = { dot: true, matchBase: true };
 
         return patternList.some(pattern => minimatch(filePath, pattern, opts)) &&
             !excludedPatternList.some(excludedPattern => minimatch(filePath, excludedPattern, opts));
@@ -377,6 +377,7 @@ module.exports = {
      * @param {(boolean|string|null)} configuredValue The value given for a global in configuration or in
      * a global directive comment
      * @returns {("readable"|"writeable"|"off")} The value normalized as a string
+     * @throws Error if global value is invalid
      */
     normalizeConfigGlobal(configuredValue) {
         switch (configuredValue) {
@@ -396,9 +397,8 @@ module.exports = {
             case "readonly":
                 return "readable";
 
-            // Fallback to minimize compatibility impact
             default:
-                return "writeable";
+                throw new Error(`'${configuredValue}' is not a valid configuration for a global (use 'readonly', 'writable', or 'off')`);
         }
     }
 };
diff --git a/lib/config/config-validator.js b/lib/config/config-validator.js
index 386db7775..4db7cd563 100644
--- a/lib/config/config-validator.js
+++ b/lib/config/config-validator.js
@@ -13,7 +13,8 @@ const path = require("path"),
     ajv = require("../util/ajv"),
     lodash = require("lodash"),
     configSchema = require("../../conf/config-schema.js"),
-    util = require("util");
+    util = require("util"),
+    ConfigOps = require("./config-ops");
 
 const ruleValidators = new WeakMap();
 
@@ -177,6 +178,27 @@ function validateRules(rulesConfig, ruleMapper, source = null) {
     });
 }
 
+/**
+ * Validates a `globals` section of a config file
+ * @param {Object} globalsConfig The `glboals` section
+ * @param {string|null} source The name of the configuration source to report in the event of an error.
+ * @returns {void}
+ */
+function validateGlobals(globalsConfig, source = null) {
+    if (!globalsConfig) {
+        return;
+    }
+
+    Object.entries(globalsConfig)
+        .forEach(([configuredGlobal, configuredValue]) => {
+            try {
+                ConfigOps.normalizeConfigGlobal(configuredValue);
+            } catch (err) {
+                throw new Error(`ESLint configuration of global '${configuredGlobal}' in ${source} is invalid:\n${err.message}`);
+            }
+        });
+}
+
 /**
  * Formats an array of schema validation errors.
  * @param {Array} errors An array of error messages to format.
@@ -238,15 +260,6 @@ function validateConfigSchema(config, source = null) {
     if (Object.hasOwnProperty.call(config, "ecmaFeatures")) {
         emitDeprecationWarning(source, "ESLINT_LEGACY_ECMAFEATURES");
     }
-
-    if (
-        (config.parser || "espree") === "espree" &&
-        config.parserOptions &&
-        config.parserOptions.ecmaFeatures &&
-        config.parserOptions.ecmaFeatures.experimentalObjectRestSpread
-    ) {
-        emitDeprecationWarning(source, "ESLINT_LEGACY_OBJECT_REST_SPREAD");
-    }
 }
 
 /**
@@ -261,6 +274,7 @@ function validate(config, ruleMapper, envContext, source = null) {
     validateConfigSchema(config, source);
     validateRules(config.rules, ruleMapper, source);
     validateEnvironment(config.env, envContext, source);
+    validateGlobals(config.globals, source);
 
     for (const override of config.overrides || []) {
         validateRules(override.rules, ruleMapper, source);
diff --git a/lib/linter.js b/lib/linter.js
index be38b9908..143c869fb 100644
--- a/lib/linter.js
+++ b/lib/linter.js
@@ -73,8 +73,13 @@ const commentParser = new ConfigCommentParser();
 function addDeclaredGlobals(globalScope, configGlobals, commentDirectives) {
     const mergedGlobalsInfo = Object.assign(
         {},
+
+        /*
+         * `ConfigOps.normalizeConfigGlobal` will throw an error if a configured global value is invalid. However, these errors would
+         * typically be caught when validating a config anyway (validity for inline global comments is checked separately).
+         */
         lodash.mapValues(configGlobals, value => ({ sourceComment: null, value: ConfigOps.normalizeConfigGlobal(value) })),
-        lodash.mapValues(commentDirectives.enabledGlobals, ({ comment, value }) => ({ sourceComment: comment, value: ConfigOps.normalizeConfigGlobal(value) }))
+        lodash.mapValues(commentDirectives.enabledGlobals, ({ comment, value }) => ({ sourceComment: comment, value }))
     );
 
     Object.keys(mergedGlobalsInfo)
@@ -196,10 +201,33 @@ function getDirectiveComments(filename, ast, ruleMapper) {
                     break;
 
                 case "globals":
-                case "global":
-                    Object.assign(enabledGlobals, commentParser.parseStringConfig(directiveValue, comment));
-                    break;
+                case "global": {
+                    const updatedGlobals = commentParser.parseStringConfig(directiveValue, comment);
+
+                    Object.keys(updatedGlobals).forEach(globalName => {
+                        const { value } = updatedGlobals[globalName];
+                        let normalizedValue;
+
+                        try {
+                            normalizedValue = ConfigOps.normalizeConfigGlobal(value);
+                        } catch (err) {
+                            problems.push({
+                                ruleId: null,
+                                severity: 2,
+                                message: err.message,
+                                line: comment.loc.start.line,
+                                column: comment.loc.start.column + 1,
+                                endLine: comment.loc.end.line,
+                                endColumn: comment.loc.end.column + 1,
+                                nodeType: null
+                            });
+                            return;
+                        }
 
+                        enabledGlobals[globalName] = { comment, value: normalizedValue };
+                    });
+                    break;
+                }
                 case "eslint-disable":
                     disableDirectives.push(...createDisableDirectives("disable", comment.loc.start, directiveValue));
                     break;
@@ -336,16 +364,6 @@ function resolveParserOptions(parserName, providedOptions, enabledEnvironments)
 
     mergedParserOptions.ecmaVersion = normalizeEcmaVersion(mergedParserOptions.ecmaVersion, isModule);
 
-    // TODO: For backward compatibility. Will remove on v6.0.0.
-    if (
-        parserName === DEFAULT_PARSER_NAME &&
-        mergedParserOptions.ecmaFeatures &&
-        mergedParserOptions.ecmaFeatures.experimentalObjectRestSpread &&
-        (!mergedParserOptions.ecmaVersion || mergedParserOptions.ecmaVersion < 9)
-    ) {
-        mergedParserOptions.ecmaVersion = 9;
-    }
-
     return mergedParserOptions;
 }
 
diff --git a/lib/rules/camelcase.js b/lib/rules/camelcase.js
index 4647cc17e..cdc16fab9 100644
--- a/lib/rules/camelcase.js
+++ b/lib/rules/camelcase.js
@@ -90,7 +90,7 @@ module.exports = {
          */
         function isAllowed(name) {
             return allow.findIndex(
-                entry => name === entry || name.match(new RegExp(entry)) // eslint-disable-line require-unicode-regexp
+                entry => name === entry || name.match(new RegExp(entry, "u"))
             ) !== -1;
         }
 
diff --git a/lib/rules/capitalized-comments.js b/lib/rules/capitalized-comments.js
index 137be9e93..036616b33 100644
--- a/lib/rules/capitalized-comments.js
+++ b/lib/rules/capitalized-comments.js
@@ -91,7 +91,7 @@ function createRegExpForIgnorePatterns(normalizedOptions) {
         const ignorePatternStr = normalizedOptions[key].ignorePattern;
 
         if (ignorePatternStr) {
-            const regExp = RegExp(`^\\s*(?:${ignorePatternStr})`); // eslint-disable-line require-unicode-regexp
+            const regExp = RegExp(`^\\s*(?:${ignorePatternStr})`, "u");
 
             normalizedOptions[key].ignorePatternRegExp = regExp;
         }
diff --git a/lib/rules/default-case.js b/lib/rules/default-case.js
index c8d0af392..821e0d72b 100644
--- a/lib/rules/default-case.js
+++ b/lib/rules/default-case.js
@@ -39,7 +39,7 @@ module.exports = {
     create(context) {
         const options = context.options[0] || {};
         const commentPattern = options.commentPattern
-            ? new RegExp(options.commentPattern) // eslint-disable-line require-unicode-regexp
+            ? new RegExp(options.commentPattern, "u")
             : DEFAULT_COMMENT_PATTERN;
 
         const sourceCode = context.getSourceCode();
diff --git a/lib/rules/dot-notation.js b/lib/rules/dot-notation.js
index e25ab12b7..883c9090f 100644
--- a/lib/rules/dot-notation.js
+++ b/lib/rules/dot-notation.js
@@ -61,7 +61,7 @@ module.exports = {
         let allowPattern;
 
         if (options.allowPattern) {
-            allowPattern = new RegExp(options.allowPattern); // eslint-disable-line require-unicode-regexp
+            allowPattern = new RegExp(options.allowPattern, "u");
         }
 
         /**
diff --git a/lib/rules/function-paren-newline.js b/lib/rules/function-paren-newline.js
index 37256484f..c4775fb19 100644
--- a/lib/rules/function-paren-newline.js
+++ b/lib/rules/function-paren-newline.js
@@ -31,7 +31,7 @@ module.exports = {
             {
                 oneOf: [
                     {
-                        enum: ["always", "never", "consistent", "multiline"]
+                        enum: ["always", "never", "consistent", "multiline", "multiline-arguments"]
                     },
                     {
                         type: "object",
@@ -50,6 +50,7 @@ module.exports = {
         messages: {
             expectedBefore: "Expected newline before ')'.",
             expectedAfter: "Expected newline after '('.",
+            expectedBetween: "Expected newline between arguments/params.",
             unexpectedBefore: "Unexpected newline before '('.",
             unexpectedAfter: "Unexpected newline after ')'."
         }
@@ -59,6 +60,7 @@ module.exports = {
         const sourceCode = context.getSourceCode();
         const rawOption = context.options[0] || "multiline";
         const multilineOption = rawOption === "multiline";
+        const multilineArgumentsOption = rawOption === "multiline-arguments";
         const consistentOption = rawOption === "consistent";
         let minItems;
 
@@ -83,7 +85,10 @@ module.exports = {
          * @returns {boolean} `true` if there should be newlines inside the function parens
          */
         function shouldHaveNewlines(elements, hasLeftNewline) {
-            if (multilineOption) {
+            if (multilineArgumentsOption && elements.length === 1) {
+                return hasLeftNewline;
+            }
+            if (multilineOption || multilineArgumentsOption) {
                 return elements.some((element, index) => index !== elements.length - 1 && element.loc.end.line !== elements[index + 1].loc.start.line);
             }
             if (consistentOption) {
@@ -93,7 +98,7 @@ module.exports = {
         }
 
         /**
-         * Validates a list of arguments or parameters
+         * Validates parens
          * @param {Object} parens An object with keys `leftParen` for the left paren token, and `rightParen` for the right paren token
          * @param {ASTNode[]} elements The arguments or parameters in the list
          * @returns {void}
@@ -148,6 +153,33 @@ module.exports = {
             }
         }
 
+        /**
+         * Validates a list of arguments or parameters
+         * @param {Object} parens An object with keys `leftParen` for the left paren token, and `rightParen` for the right paren token
+         * @param {ASTNode[]} elements The arguments or parameters in the list
+         * @returns {void}
+         */
+        function validateArguments(parens, elements) {
+            const leftParen = parens.leftParen;
+            const tokenAfterLeftParen = sourceCode.getTokenAfter(leftParen);
+            const hasLeftNewline = !astUtils.isTokenOnSameLine(leftParen, tokenAfterLeftParen);
+            const needsNewlines = shouldHaveNewlines(elements, hasLeftNewline);
+
+            for (let i = 0; i <= elements.length - 2; i++) {
+                const currentElement = elements[i];
+                const nextElement = elements[i + 1];
+                const hasNewLine = currentElement.loc.end.line !== nextElement.loc.start.line;
+
+                if (!hasNewLine && needsNewlines) {
+                    context.report({
+                        node: currentElement,
+                        messageId: "expectedBetween",
+                        fix: fixer => fixer.insertTextBefore(nextElement, "\n")
+                    });
+                }
+            }
+        }
+
         /**
          * Gets the left paren and right paren tokens of a node.
          * @param {ASTNode} node The node with parens
@@ -215,6 +247,10 @@ module.exports = {
 
             if (parens) {
                 validateParens(parens, astUtils.isFunction(node) ? node.params : node.arguments);
+
+                if (multilineArgumentsOption) {
+                    validateArguments(parens, astUtils.isFunction(node) ? node.params : node.arguments);
+                }
             }
         }
 
diff --git a/lib/rules/handle-callback-err.js b/lib/rules/handle-callback-err.js
index 4d08e4fec..640946699 100644
--- a/lib/rules/handle-callback-err.js
+++ b/lib/rules/handle-callback-err.js
@@ -52,7 +52,7 @@ module.exports = {
          */
         function matchesConfiguredErrorName(name) {
             if (isPattern(errorArgument)) {
-                const regexp = new RegExp(errorArgument); // eslint-disable-line require-unicode-regexp
+                const regexp = new RegExp(errorArgument, "u");
 
                 return regexp.test(name);
             }
diff --git a/lib/rules/id-match.js b/lib/rules/id-match.js
index 48c108767..b97a497fd 100644
--- a/lib/rules/id-match.js
+++ b/lib/rules/id-match.js
@@ -53,7 +53,7 @@ module.exports = {
         // Options
         //--------------------------------------------------------------------------
         const pattern = context.options[0] || "^.+$",
-            regexp = new RegExp(pattern); // eslint-disable-line require-unicode-regexp
+            regexp = new RegExp(pattern, "u");
 
         const options = context.options[1] || {},
             properties = !!options.properties,
diff --git a/lib/rules/line-comment-position.js b/lib/rules/line-comment-position.js
index 0023a1f11..9a91d4302 100644
--- a/lib/rules/line-comment-position.js
+++ b/lib/rules/line-comment-position.js
@@ -78,7 +78,7 @@ module.exports = {
 
         const defaultIgnoreRegExp = astUtils.COMMENTS_IGNORE_PATTERN;
         const fallThroughRegExp = /^\s*falls?\s?through/u;
-        const customIgnoreRegExp = new RegExp(ignorePattern); // eslint-disable-line require-unicode-regexp
+        const customIgnoreRegExp = new RegExp(ignorePattern, "u");
         const sourceCode = context.getSourceCode();
 
         //--------------------------------------------------------------------------
diff --git a/lib/rules/lines-around-comment.js b/lib/rules/lines-around-comment.js
index f2f9e3f9d..9d6ce4616 100644
--- a/lib/rules/lines-around-comment.js
+++ b/lib/rules/lines-around-comment.js
@@ -130,7 +130,7 @@ module.exports = {
         const options = Object.assign({}, context.options[0]);
         const ignorePattern = options.ignorePattern;
         const defaultIgnoreRegExp = astUtils.COMMENTS_IGNORE_PATTERN;
-        const customIgnoreRegExp = new RegExp(ignorePattern); // eslint-disable-line require-unicode-regexp
+        const customIgnoreRegExp = new RegExp(ignorePattern, "u");
         const applyDefaultIgnorePatterns = options.applyDefaultIgnorePatterns !== false;
 
         options.beforeBlockComment = typeof options.beforeBlockComment !== "undefined" ? options.beforeBlockComment : true;
diff --git a/lib/rules/max-len.js b/lib/rules/max-len.js
index 6e7dd6f1a..438e38b0a 100644
--- a/lib/rules/max-len.js
+++ b/lib/rules/max-len.js
@@ -153,7 +153,7 @@ module.exports = {
         let ignorePattern = options.ignorePattern || null;
 
         if (ignorePattern) {
-            ignorePattern = new RegExp(ignorePattern); // eslint-disable-line require-unicode-regexp
+            ignorePattern = new RegExp(ignorePattern, "u");
         }
 
         //--------------------------------------------------------------------------
diff --git a/lib/rules/new-cap.js b/lib/rules/new-cap.js
index 9e88981ce..dcae238d9 100644
--- a/lib/rules/new-cap.js
+++ b/lib/rules/new-cap.js
@@ -136,10 +136,10 @@ module.exports = {
         const skipProperties = config.properties === false;
 
         const newIsCapExceptions = checkArray(config, "newIsCapExceptions", []).reduce(invert, {});
-        const newIsCapExceptionPattern = config.newIsCapExceptionPattern ? new RegExp(config.newIsCapExceptionPattern) : null; // eslint-disable-line require-unicode-regexp
+        const newIsCapExceptionPattern = config.newIsCapExceptionPattern ? new RegExp(config.newIsCapExceptionPattern, "u") : null;
 
         const capIsNewExceptions = calculateCapIsNewExceptions(config);
-        const capIsNewExceptionPattern = config.capIsNewExceptionPattern ? new RegExp(config.capIsNewExceptionPattern) : null; // eslint-disable-line require-unicode-regexp
+        const capIsNewExceptionPattern = config.capIsNewExceptionPattern ? new RegExp(config.capIsNewExceptionPattern, "u") : null;
 
         const listeners = {};
 
diff --git a/lib/rules/no-fallthrough.js b/lib/rules/no-fallthrough.js
index 241e07258..c6a71b6c8 100644
--- a/lib/rules/no-fallthrough.js
+++ b/lib/rules/no-fallthrough.js
@@ -95,7 +95,7 @@ module.exports = {
         let fallthroughCommentPattern = null;
 
         if (options.commentPattern) {
-            fallthroughCommentPattern = new RegExp(options.commentPattern); // eslint-disable-line require-unicode-regexp
+            fallthroughCommentPattern = new RegExp(options.commentPattern, "u");
         } else {
             fallthroughCommentPattern = DEFAULT_FALLTHROUGH_COMMENT;
         }
diff --git a/lib/rules/no-unused-vars.js b/lib/rules/no-unused-vars.js
index d4c4c6355..1482a0238 100644
--- a/lib/rules/no-unused-vars.js
+++ b/lib/rules/no-unused-vars.js
@@ -88,15 +88,15 @@ module.exports = {
                 config.caughtErrors = firstOption.caughtErrors || config.caughtErrors;
 
                 if (firstOption.varsIgnorePattern) {
-                    config.varsIgnorePattern = new RegExp(firstOption.varsIgnorePattern); // eslint-disable-line require-unicode-regexp
+                    config.varsIgnorePattern = new RegExp(firstOption.varsIgnorePattern, "u");
                 }
 
                 if (firstOption.argsIgnorePattern) {
-                    config.argsIgnorePattern = new RegExp(firstOption.argsIgnorePattern); // eslint-disable-line require-unicode-regexp
+                    config.argsIgnorePattern = new RegExp(firstOption.argsIgnorePattern, "u");
                 }
 
                 if (firstOption.caughtErrorsIgnorePattern) {
-                    config.caughtErrorsIgnorePattern = new RegExp(firstOption.caughtErrorsIgnorePattern); // eslint-disable-line require-unicode-regexp
+                    config.caughtErrorsIgnorePattern = new RegExp(firstOption.caughtErrorsIgnorePattern, "u");
                 }
             }
         }
diff --git a/lib/rules/no-warning-comments.js b/lib/rules/no-warning-comments.js
index 1d1b55460..0882f6126 100644
--- a/lib/rules/no-warning-comments.js
+++ b/lib/rules/no-warning-comments.js
@@ -5,6 +5,7 @@
 
 "use strict";
 
+const { escapeRegExp } = require("lodash");
 const astUtils = require("../util/ast-utils");
 
 //------------------------------------------------------------------------------
@@ -58,7 +59,7 @@ module.exports = {
          * @returns {RegExp} The term converted to a RegExp
          */
         function convertToRegExp(term) {
-            const escaped = term.replace(/[-/\\$^*+?.()|[\]{}]/gu, "\\$&");
+            const escaped = escapeRegExp(term);
             const wordBoundary = "\\b";
             const eitherOrWordBoundary = `|${wordBoundary}`;
             let prefix;
@@ -95,7 +96,7 @@ module.exports = {
                  * ^\s*TERM\b.  This checks the word boundary
                  * at the beginning of the comment.
                  */
-                return new RegExp(prefix + escaped + suffix, "i"); // eslint-disable-line require-unicode-regexp
+                return new RegExp(prefix + escaped + suffix, "iu");
             }
 
             /*
@@ -103,7 +104,7 @@ module.exports = {
              * \bTERM\b|\bTERM\b, this checks the entire comment
              * for the term.
              */
-            return new RegExp(prefix + escaped + suffix + eitherOrWordBoundary + term + wordBoundary, "i"); // eslint-disable-line require-unicode-regexp
+            return new RegExp(prefix + escaped + suffix + eitherOrWordBoundary + term + wordBoundary, "iu");
         }
 
         const warningRegExps = warningTerms.map(convertToRegExp);
diff --git a/lib/rules/spaced-comment.js b/lib/rules/spaced-comment.js
index 6e469ef94..e6b6e1b65 100644
--- a/lib/rules/spaced-comment.js
+++ b/lib/rules/spaced-comment.js
@@ -126,7 +126,7 @@ function createAlwaysStylePattern(markers, exceptions) {
     pattern += "?"; // or nothing.
     pattern += createExceptionsPattern(exceptions);
 
-    return new RegExp(pattern); // eslint-disable-line require-unicode-regexp
+    return new RegExp(pattern, "u");
 }
 
 /**
@@ -142,7 +142,7 @@ function createAlwaysStylePattern(markers, exceptions) {
 function createNeverStylePattern(markers) {
     const pattern = `^(${markers.map(escape).join("|")})?[ \t]+`;
 
-    return new RegExp(pattern); // eslint-disable-line require-unicode-regexp
+    return new RegExp(pattern, "u");
 }
 
 //------------------------------------------------------------------------------
@@ -250,9 +250,9 @@ module.exports = {
             // Create RegExp object for valid patterns.
             rule[type] = {
                 beginRegex: requireSpace ? createAlwaysStylePattern(markers, exceptions) : createNeverStylePattern(markers),
-                endRegex: balanced && requireSpace ? new RegExp(`${createExceptionsPattern(exceptions)}$`) : new RegExp(endNeverPattern), // eslint-disable-line require-unicode-regexp
+                endRegex: balanced && requireSpace ? new RegExp(`${createExceptionsPattern(exceptions)}$`, "u") : new RegExp(endNeverPattern, "u"),
                 hasExceptions: exceptions.length > 0,
-                markers: new RegExp(`^(${markers.map(escape).join("|")})`) // eslint-disable-line require-unicode-regexp
+                markers: new RegExp(`^(${markers.map(escape).join("|")})`, "u")
             };
 
             return rule;
diff --git a/lib/rules/valid-jsdoc.js b/lib/rules/valid-jsdoc.js
index 3250d9233..9ec6938e3 100644
--- a/lib/rules/valid-jsdoc.js
+++ b/lib/rules/valid-jsdoc.js
@@ -482,7 +482,7 @@ module.exports = {
                 }
 
                 if (options.matchDescription) {
-                    const regex = new RegExp(options.matchDescription); // eslint-disable-line require-unicode-regexp
+                    const regex = new RegExp(options.matchDescription, "u");
 
                     if (!regex.test(jsdoc.description)) {
                         context.report({ node: jsdocNode, messageId: "unsatisfiedDesc" });
diff --git a/package.json b/package.json
index c2c315d69..8f95910bb 100644
--- a/package.json
+++ b/package.json
@@ -93,7 +93,7 @@
     "eslump": "^2.0.0",
     "esprima": "^4.0.1",
     "jsdoc": "^3.5.5",
-    "karma": "^3.1.4",
+    "karma": "^4.0.1",
     "karma-chrome-launcher": "^2.2.0",
     "karma-mocha": "^1.3.0",
     "karma-mocha-reporter": "^2.2.3",
@@ -123,6 +123,6 @@
   ],
   "license": "MIT",
   "engines": {
-    "node": "^6.14.0 || ^8.10.0 || >=9.10.0"
+    "node": "^8.10.0 || ^10.13.0 || >=11.10.1"
   }
 }


Test patch:
diff --git a/tests/conf/eslint-recommended.js b/tests/conf/eslint-recommended.js
new file mode 100644
index 000000000..89d9f4447
--- /dev/null
+++ b/tests/conf/eslint-recommended.js
@@ -0,0 +1,24 @@
+/**
+ * @fileoverview Tests for eslint:recommended.
+ * @author Kevin Partington
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const assert = require("chai").assert;
+const eslintRecommended = require("../../conf/eslint-recommended");
+const rules = eslintRecommended.rules;
+
+describe("eslint-recommended", () => {
+    it("should configure recommended rules as error", () => {
+        assert.strictEqual(rules["no-undef"], "error");
+    });
+
+    it("should not configure non-recommended rules", () => {
+        assert.notProperty(rules, "camelcase");
+    });
+});
diff --git a/tests/fixtures/cli-engine/overrides-with-dot/.eslintrc.yml b/tests/fixtures/cli-engine/overrides-with-dot/.eslintrc.yml
new file mode 100644
index 000000000..5acdae3fd
--- /dev/null
+++ b/tests/fixtures/cli-engine/overrides-with-dot/.eslintrc.yml
@@ -0,0 +1,6 @@
+root: true
+
+overrides:
+  - files: "*.js"
+    rules:
+      no-unused-vars: error
diff --git a/tests/fixtures/cli-engine/overrides-with-dot/.test-target.js b/tests/fixtures/cli-engine/overrides-with-dot/.test-target.js
new file mode 100644
index 000000000..ac09549ac
--- /dev/null
+++ b/tests/fixtures/cli-engine/overrides-with-dot/.test-target.js
@@ -0,0 +1,1 @@
+var a = 0
diff --git a/tests/fixtures/config-file/experimental-object-rest-spread/another-parser/.eslintrc.yml b/tests/fixtures/config-file/experimental-object-rest-spread/another-parser/.eslintrc.yml
deleted file mode 100644
index c64b2c309..000000000
--- a/tests/fixtures/config-file/experimental-object-rest-spread/another-parser/.eslintrc.yml
+++ /dev/null
@@ -1,6 +0,0 @@
-root: true
-
-parser: ./parser.js
-parserOptions:
-  ecmaFeatures:
-    experimentalObjectRestSpread: true
diff --git a/tests/fixtures/config-file/experimental-object-rest-spread/another-parser/parser.js b/tests/fixtures/config-file/experimental-object-rest-spread/another-parser/parser.js
deleted file mode 100644
index f193589ef..000000000
--- a/tests/fixtures/config-file/experimental-object-rest-spread/another-parser/parser.js
+++ /dev/null
@@ -1,1 +0,0 @@
-module.exports.parse = () => ({ type: "Program", body: [] })
diff --git a/tests/fixtures/config-file/experimental-object-rest-spread/basic/.eslintrc.yml b/tests/fixtures/config-file/experimental-object-rest-spread/basic/.eslintrc.yml
deleted file mode 100644
index a5c9e4ebf..000000000
--- a/tests/fixtures/config-file/experimental-object-rest-spread/basic/.eslintrc.yml
+++ /dev/null
@@ -1,5 +0,0 @@
-root: true
-
-parserOptions:
-  ecmaFeatures:
-    experimentalObjectRestSpread: true
diff --git a/tests/fixtures/config-file/experimental-object-rest-spread/extends/.eslintrc.yml b/tests/fixtures/config-file/experimental-object-rest-spread/extends/.eslintrc.yml
deleted file mode 100644
index 2daef0dea..000000000
--- a/tests/fixtures/config-file/experimental-object-rest-spread/extends/.eslintrc.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-root: true
-
-extends:
-  - ./common.yml
diff --git a/tests/fixtures/config-file/experimental-object-rest-spread/extends/common.yml b/tests/fixtures/config-file/experimental-object-rest-spread/extends/common.yml
deleted file mode 100644
index 99975a7bd..000000000
--- a/tests/fixtures/config-file/experimental-object-rest-spread/extends/common.yml
+++ /dev/null
@@ -1,3 +0,0 @@
-parserOptions:
-  ecmaFeatures:
-    experimentalObjectRestSpread: true
diff --git a/tests/fixtures/config-file/experimental-object-rest-spread/subdir/.eslintrc.yml b/tests/fixtures/config-file/experimental-object-rest-spread/subdir/.eslintrc.yml
deleted file mode 100644
index a5c9e4ebf..000000000
--- a/tests/fixtures/config-file/experimental-object-rest-spread/subdir/.eslintrc.yml
+++ /dev/null
@@ -1,5 +0,0 @@
-root: true
-
-parserOptions:
-  ecmaFeatures:
-    experimentalObjectRestSpread: true
diff --git a/tests/fixtures/config-file/experimental-object-rest-spread/subdir/lib/.eslintrc.yml b/tests/fixtures/config-file/experimental-object-rest-spread/subdir/lib/.eslintrc.yml
deleted file mode 100644
index 78dc43786..000000000
--- a/tests/fixtures/config-file/experimental-object-rest-spread/subdir/lib/.eslintrc.yml
+++ /dev/null
@@ -1,2 +0,0 @@
-rules:
-  no-undef: off
diff --git a/tests/lib/cli-engine.js b/tests/lib/cli-engine.js
index ee582c83a..ff5e041d3 100644
--- a/tests/lib/cli-engine.js
+++ b/tests/lib/cli-engine.js
@@ -1532,7 +1532,7 @@ describe("CLIEngine", () => {
 
             const report = engine.executeOnFiles(["lib/cli*.js"]);
 
-            assert.deepStrictEqual(
+            assert.sameDeepMembers(
                 report.usedDeprecatedRules,
                 [
                     { ruleId: "indent-legacy", replacedBy: ["indent"] },
@@ -2870,6 +2870,23 @@ describe("CLIEngine", () => {
                 }, "No files matching 'non-exist.js' were found.");
             });
         });
+
+        describe("overrides", () => {
+            beforeEach(() => {
+                engine = new CLIEngine({
+                    cwd: getFixturePath("cli-engine/overrides-with-dot"),
+                    ignore: false
+                });
+            });
+
+            it("should recognize dotfiles", () => {
+                const ret = engine.executeOnFiles([".test-target.js"]);
+
+                assert.strictEqual(ret.results.length, 1);
+                assert.strictEqual(ret.results[0].messages.length, 1);
+                assert.strictEqual(ret.results[0].messages[0].ruleId, "no-unused-vars");
+            });
+        });
     });
 
     describe("getConfigForFile", () => {
diff --git a/tests/lib/config.js b/tests/lib/config.js
index 5381ce1eb..6eaa2c2f1 100644
--- a/tests/lib/config.js
+++ b/tests/lib/config.js
@@ -1397,74 +1397,6 @@ describe("Config", () => {
                         `The 'ecmaFeatures' config file property is deprecated, and has no effect. (found in "tests${path.sep}fixtures${path.sep}config-file${path.sep}ecma-features${path.sep}.eslintrc.yml")`
                     );
                 }));
-
-            it("should emit a deprecation warning if 'parserOptions.ecmaFeatures.experimentalObjectRestSpread' is given.", () => Promise.resolve()
-                .then(() => {
-                    const cwd = path.resolve(__dirname, "../fixtures/config-file/experimental-object-rest-spread/basic/");
-                    const config = new Config({ cwd }, linter);
-
-                    config.getConfig("test.js");
-
-                    // Wait for "warning" event.
-                    return nextTick();
-                })
-                .then(() => {
-                    assert.notStrictEqual(warning, null);
-                    assert.strictEqual(
-                        warning.message,
-                        `The 'parserOptions.ecmaFeatures.experimentalObjectRestSpread' option is deprecated. Use 'parserOptions.ecmaVersion' instead. (found in "tests${path.sep}fixtures${path.sep}config-file${path.sep}experimental-object-rest-spread${path.sep}basic${path.sep}.eslintrc.yml")`
-                    );
-                }));
-
-            it("should emit a deprecation warning if 'parserOptions.ecmaFeatures.experimentalObjectRestSpread' is given in a parent config.", () => Promise.resolve()
-                .then(() => {
-                    const cwd = path.resolve(__dirname, "../fixtures/config-file/experimental-object-rest-spread/subdir/");
-                    const config = new Config({ cwd }, linter);
-
-                    config.getConfig("lib/test.js");
-
-                    // Wait for "warning" event.
-                    return nextTick();
-                })
-                .then(() => {
-                    assert.notStrictEqual(warning, null);
-                    assert.strictEqual(
-                        warning.message,
-                        `The 'parserOptions.ecmaFeatures.experimentalObjectRestSpread' option is deprecated. Use 'parserOptions.ecmaVersion' instead. (found in "tests${path.sep}fixtures${path.sep}config-file${path.sep}experimental-object-rest-spread${path.sep}subdir${path.sep}.eslintrc.yml")`
-                    );
-                }));
-
-            it("should emit a deprecation warning if 'parserOptions.ecmaFeatures.experimentalObjectRestSpread' is given in a shareable config.", () => Promise.resolve()
-                .then(() => {
-                    const cwd = path.resolve(__dirname, "../fixtures/config-file/experimental-object-rest-spread/extends/");
-                    const config = new Config({ cwd }, linter);
-
-                    config.getConfig("test.js");
-
-                    // Wait for "warning" event.
-                    return nextTick();
-                })
-                .then(() => {
-                    assert.notStrictEqual(warning, null);
-                    assert.strictEqual(
-                        warning.message,
-                        `The 'parserOptions.ecmaFeatures.experimentalObjectRestSpread' option is deprecated. Use 'parserOptions.ecmaVersion' instead. (found in "tests${path.sep}fixtures${path.sep}config-file${path.sep}experimental-object-rest-spread${path.sep}extends${path.sep}common.yml")`
-                    );
-                }));
-
-            it("should NOT emit a deprecation warning even if 'parserOptions.ecmaFeatures.experimentalObjectRestSpread' is given, if parser is not espree.", () => Promise.resolve()
-                .then(() => {
-                    const cwd = path.resolve(__dirname, "../fixtures/config-file/experimental-object-rest-spread/another-parser/");
-                    const config = new Config({ cwd }, linter);
-
-                    config.getConfig("test.js");
-
-                    // Wait for "warning" event.
-                    return nextTick();
-                })
-                .then(() => {
-                    assert.strictEqual(warning, null);
-                }));
         });
     });
 
diff --git a/tests/lib/config/config-ops.js b/tests/lib/config/config-ops.js
index 8d5ad5fa5..ffa65660d 100644
--- a/tests/lib/config/config-ops.js
+++ b/tests/lib/config/config-ops.js
@@ -911,12 +911,18 @@ describe("ConfigOps", () => {
             ["writable", "writeable"],
             ["readable", "readable"],
             ["readonly", "readable"],
-            ["writable", "writeable"],
-            ["something else", "writeable"]
+            ["writable", "writeable"]
         ].forEach(([input, output]) => {
             it(util.inspect(input), () => {
                 assert.strictEqual(ConfigOps.normalizeConfigGlobal(input), output);
             });
         });
+
+        it("throws on other inputs", () => {
+            assert.throws(
+                () => ConfigOps.normalizeConfigGlobal("something else"),
+                /^'something else' is not a valid configuration for a global \(use 'readonly', 'writable', or 'off'\)$/u
+            );
+        });
     });
 });
diff --git a/tests/lib/config/config-validator.js b/tests/lib/config/config-validator.js
index 9d4c39baf..da4d79f32 100644
--- a/tests/lib/config/config-validator.js
+++ b/tests/lib/config/config-validator.js
@@ -115,7 +115,7 @@ describe("Validator", () => {
             validator.validate(
                 {
                     root: true,
-                    globals: { globalFoo: "bar" },
+                    globals: { globalFoo: "readonly" },
                     parser: "parserFoo",
                     env: { browser: true },
                     plugins: ["pluginFoo", "pluginBar"],
@@ -325,6 +325,15 @@ describe("Validator", () => {
             });
         });
 
+        describe("globals", () => {
+            it("should disallow globals set to invalid values", () => {
+                assert.throws(
+                    () => validator.validate({ globals: { foo: "AAAAA" } }, () => {}, linter.environments, "tests"),
+                    "ESLint configuration of global 'foo' in tests is invalid:\n'AAAAA' is not a valid configuration for a global (use 'readonly', 'writable', or 'off')"
+                );
+            });
+        });
+
         describe("overrides", () => {
             it("should not throw with an empty overrides array", () => {
                 validator.validate({ overrides: [] }, ruleMapper, linter.environments, "tests");
diff --git a/tests/lib/linter.js b/tests/lib/linter.js
index e31b61d95..a02ea3913 100644
--- a/tests/lib/linter.js
+++ b/tests/lib/linter.js
@@ -3490,6 +3490,34 @@ describe("Linter", () => {
             assert(ok);
         });
 
+        it("should report a linting error when a global is set to an invalid value", () => {
+            const results = linter.verify("/* global foo: AAAAA, bar: readonly */\nfoo;\nbar;", { rules: { "no-undef": "error" } });
+
+            assert.deepStrictEqual(results, [
+                {
+                    ruleId: null,
+                    severity: 2,
+                    message: "'AAAAA' is not a valid configuration for a global (use 'readonly', 'writable', or 'off')",
+                    line: 1,
+                    column: 1,
+                    endLine: 1,
+                    endColumn: 39,
+                    nodeType: null
+                },
+                {
+                    ruleId: "no-undef",
+                    messageId: "undef",
+                    severity: 2,
+                    message: "'foo' is not defined.",
+                    line: 2,
+                    column: 1,
+                    endLine: 2,
+                    endColumn: 4,
+                    nodeType: "Identifier"
+                }
+            ]);
+        });
+
         it("should not crash when we reuse the SourceCode object", () => {
             linter.verify("function render() { return <div className='test'>{hello}</div> }", { parserOptions: { ecmaVersion: 6, ecmaFeatures: { jsx: true } } });
             linter.verify(linter.getSourceCode(), { parserOptions: { ecmaVersion: 6, ecmaFeatures: { jsx: true } } });
@@ -3545,15 +3573,6 @@ describe("Linter", () => {
             linter.verify("x", { rules: { "foo-bar-baz": "error" } });
             assert(spy.calledOnce);
         });
-
-        it("should parse ES2018 code for backward compatibility if 'parserOptions.ecmaFeatures.experimentalObjectRestSpread' is given.", () => {
-            const messages = linter.verify(
-                "async function* f() { let {a, ...rest} = { a, ...obj }; }",
-                { parserOptions: { ecmaFeatures: { experimentalObjectRestSpread: true } } }
-            );
-
-            assert(messages.length === 0);
-        });
     });
 
     describe("context.getScope()", () => {
diff --git a/tests/lib/rules/function-paren-newline.js b/tests/lib/rules/function-paren-newline.js
index 5a062a1bf..522182734 100644
--- a/tests/lib/rules/function-paren-newline.js
+++ b/tests/lib/rules/function-paren-newline.js
@@ -20,6 +20,7 @@ const LEFT_MISSING_ERROR = { messageId: "expectedAfter", type: "Punctuator" };
 const LEFT_UNEXPECTED_ERROR = { messageId: "unexpectedAfter", type: "Punctuator" };
 const RIGHT_MISSING_ERROR = { messageId: "expectedBefore", type: "Punctuator" };
 const RIGHT_UNEXPECTED_ERROR = { messageId: "unexpectedBefore", type: "Punctuator" };
+const EXPECTED_BETWEEN = { messageId: "expectedBetween", type: "Identifier" };
 
 const ruleTester = new RuleTester({ parserOptions: { ecmaVersion: 6 } });
 
@@ -82,12 +83,182 @@ ruleTester.run("function-paren-newline", rule, {
               return value;
             })
         `,
-
-        // always option
         {
             code: "function baz(foo, bar) {}",
             options: ["multiline"]
         },
+
+        // multiline-arguments
+        {
+            code: "function baz(foo, bar) {}",
+            options: ["multiline-arguments"]
+        },
+        {
+            code: "function baz(foo) {}",
+            options: ["multiline-arguments"]
+        },
+        {
+            code: "(function(foo, bar) {});",
+            options: ["multiline-arguments"]
+        },
+        {
+            code: "(function(foo) {});",
+            options: ["multiline-arguments"]
+        },
+        {
+            code: "(function baz(foo, bar) {});",
+            options: ["multiline-arguments"]
+        },
+        {
+            code: "(function baz(foo) {});",
+            options: ["multiline-arguments"]
+        },
+        {
+            code: "(foo, bar) => {};",
+            options: ["multiline-arguments"]
+        },
+        {
+            code: "foo => {};",
+            options: ["multiline-arguments"]
+        },
+        {
+            code: "baz(foo, bar);",
+            options: ["multiline-arguments"]
+        },
+        {
+            code: "baz(foo);",
+            options: ["multiline-arguments"]
+        },
+        {
+            code: "function baz() {}",
+            options: ["multiline-arguments"]
+        },
+        {
+            code: `
+                function baz(
+                    foo,
+                    bar
+                ) {}
+            `,
+            options: ["multiline-arguments"]
+        },
+        {
+            code: `
+                function baz(
+                    foo
+                ) {}
+            `,
+            options: ["multiline-arguments"]
+        },
+        {
+            code: `
+                (function(
+                    foo,
+                    bar
+                ) {});
+            `,
+            options: ["multiline-arguments"]
+        },
+        {
+            code: `
+                (function(
+                    foo
+                ) {});
+            `,
+            options: ["multiline-arguments"]
+        },
+        {
+            code: `
+                (function baz(
+                    foo,
+                    bar
+                ) {});
+            `,
+            options: ["multiline-arguments"]
+        },
+        {
+            code: `
+                (function baz(
+                    foo
+                ) {});
+            `,
+            options: ["multiline-arguments"]
+        },
+        {
+            code: `
+                (
+                    foo,
+                    bar
+                ) => {};
+            `,
+            options: ["multiline-arguments"]
+        },
+        {
+            code: `
+                (
+                    foo
+                ) => {};
+            `,
+            options: ["multiline-arguments"]
+        },
+        {
+            code: `
+                baz(
+                    foo,
+                    bar
+                );
+            `,
+            options: ["multiline-arguments"]
+        },
+        {
+            code: `
+                baz(
+                    foo
+                );
+            `,
+            options: ["multiline-arguments"]
+        },
+        {
+            code: `
+                baz(\`foo
+                    bar\`)
+            `,
+            options: ["multiline-arguments"]
+        },
+        {
+            code: "new Foo(bar, baz)",
+            options: ["multiline-arguments"]
+        },
+        {
+            code: "new Foo(bar)",
+            options: ["multiline-arguments"]
+        },
+        {
+            code: "new Foo",
+            options: ["multiline-arguments"]
+        },
+        {
+            code: "new (Foo)",
+            options: ["multiline-arguments"]
+        },
+
+        {
+            code: `
+                (foo)
+                (bar)
+            `,
+            options: ["multiline-arguments"]
+        },
+        {
+            code: `
+                foo.map(value => {
+                  return value;
+                })
+            `,
+            options: ["multiline-arguments"]
+        },
+
+        // always option
         {
             code: `
                 function baz(
@@ -344,6 +515,193 @@ ruleTester.run("function-paren-newline", rule, {
             errors: [RIGHT_UNEXPECTED_ERROR]
         },
 
+        // multiline-arguments
+        {
+            code: `
+                function baz(foo,
+                    bar
+                ) {}
+            `,
+            output: `
+                function baz(\nfoo,
+                    bar
+                ) {}
+            `,
+            options: ["multiline-arguments"],
+            errors: [LEFT_MISSING_ERROR]
+        },
+        {
+            code: `
+                (function(
+                    foo,
+                    bar) {})
+            `,
+            output: `
+                (function(
+                    foo,
+                    bar\n) {})
+            `,
+            options: ["multiline-arguments"],
+            errors: [RIGHT_MISSING_ERROR]
+        },
+        {
+            code: `
+                (function baz(foo,
+                    bar) {})
+            `,
+            output: `
+                (function baz(\nfoo,
+                    bar\n) {})
+            `,
+            options: ["multiline-arguments"],
+            errors: [LEFT_MISSING_ERROR, RIGHT_MISSING_ERROR]
+        },
+        {
+            code: `
+                baz(
+                    foo, bar);
+            `,
+            output: `
+                baz(foo, bar);
+            `,
+            options: ["multiline-arguments"],
+            errors: [LEFT_UNEXPECTED_ERROR]
+        },
+        {
+            code: `
+                (foo, bar
+                ) => {};
+            `,
+            output: `
+                (foo, bar) => {};
+            `,
+            options: ["multiline-arguments"],
+            errors: [RIGHT_UNEXPECTED_ERROR]
+        },
+        {
+            code: `
+                function baz(
+                    foo, bar
+                ) {}
+            `,
+            output: `
+                function baz(foo, bar) {}
+            `,
+            options: ["multiline-arguments"],
+            errors: [LEFT_UNEXPECTED_ERROR, RIGHT_UNEXPECTED_ERROR]
+        },
+        {
+            code: `
+                function baz(
+                ) {}
+            `,
+            output: `
+                function baz() {}
+            `,
+            options: ["multiline-arguments"],
+            errors: [LEFT_UNEXPECTED_ERROR, RIGHT_UNEXPECTED_ERROR]
+        },
+        {
+            code: `
+                new Foo(bar,
+                    baz);
+            `,
+            output: `
+                new Foo(\nbar,
+                    baz\n);
+            `,
+            options: ["multiline-arguments"],
+            errors: [LEFT_MISSING_ERROR, RIGHT_MISSING_ERROR]
+        },
+        {
+            code: `
+                function baz(/* not fixed due to comment */
+                foo) {}
+            `,
+            output: `
+                function baz(/* not fixed due to comment */
+                foo\n) {}
+            `,
+            options: ["multiline-arguments"],
+            errors: [RIGHT_MISSING_ERROR]
+        },
+        {
+            code: `
+                function baz(foo
+                /* not fixed due to comment */) {}
+            `,
+            output: null,
+            options: ["multiline-arguments"],
+            errors: [RIGHT_UNEXPECTED_ERROR]
+        },
+        {
+            code: `
+                function baz(
+                    qwe,
+                    foo, bar
+                ) {}
+            `,
+            output: `
+                function baz(
+                    qwe,
+                    foo, \nbar
+                ) {}
+            `,
+            options: ["multiline-arguments"],
+            errors: [EXPECTED_BETWEEN]
+        },
+        {
+            code: `
+                function baz(
+                    qwe, foo,
+                    bar
+                ) {}
+            `,
+            output: `
+                function baz(
+                    qwe, \nfoo,
+                    bar
+                ) {}
+            `,
+            options: ["multiline-arguments"],
+            errors: [EXPECTED_BETWEEN]
+        },
+        {
+            code: `
+                function baz(qwe, foo,
+                    bar) {}
+            `,
+            output: `
+                function baz(\nqwe, \nfoo,
+                    bar\n) {}
+            `,
+            options: ["multiline-arguments"],
+            errors: [LEFT_MISSING_ERROR, EXPECTED_BETWEEN, RIGHT_MISSING_ERROR]
+        },
+        {
+            code: `
+                baz(
+                    foo);
+            `,
+            output: `
+                baz(
+                    foo\n);
+            `,
+            options: ["multiline-arguments"],
+            errors: [RIGHT_MISSING_ERROR]
+        },
+        {
+            code: `
+                baz(foo
+                    );
+            `,
+            output: `
+                baz(foo);
+            `,
+            options: ["multiline-arguments"],
+            errors: [RIGHT_UNEXPECTED_ERROR]
+        },
+
         // always option
         {
             code: `
diff --git a/tests/lib/rules/max-len.js b/tests/lib/rules/max-len.js
index a7080f477..41b0efe9b 100644
--- a/tests/lib/rules/max-len.js
+++ b/tests/lib/rules/max-len.js
@@ -178,6 +178,12 @@ ruleTester.run("max-len", rule, {
         {
             code: "'🙂😀😆😎😊😜😉👍'",
             options: [10]
+        },
+
+        // Astral symbols in pattern (only matched by unicode regexes)
+        {
+            code: "var longNameLongName = '𝌆𝌆'",
+            options: [5, { ignorePattern: "𝌆{2}" }]
         }
     ],
 
diff --git a/tests/lib/rules/no-unused-vars.js b/tests/lib/rules/no-unused-vars.js
index 3012dec63..ba8e57dcc 100644
--- a/tests/lib/rules/no-unused-vars.js
+++ b/tests/lib/rules/no-unused-vars.js
@@ -330,33 +330,33 @@ ruleTester.run("no-unused-vars", rule, {
         {
             code: "var _a; var b;",
             options: [{ vars: "all", varsIgnorePattern: "^_" }],
-            errors: [{ message: "'b' is defined but never used. Allowed unused vars must match /^_/.", line: 1, column: 13 }]
+            errors: [{ message: "'b' is defined but never used. Allowed unused vars must match /^_/u.", line: 1, column: 13 }]
         },
         {
             code: "var a; function foo() { var _b; var c_; } foo();",
             options: [{ vars: "local", varsIgnorePattern: "^_" }],
-            errors: [{ message: "'c_' is defined but never used. Allowed unused vars must match /^_/.", line: 1, column: 37 }]
+            errors: [{ message: "'c_' is defined but never used. Allowed unused vars must match /^_/u.", line: 1, column: 37 }]
         },
         {
             code: "function foo(a, _b) { } foo();",
             options: [{ args: "all", argsIgnorePattern: "^_" }],
-            errors: [{ message: "'a' is defined but never used. Allowed unused args must match /^_/.", line: 1, column: 14 }]
+            errors: [{ message: "'a' is defined but never used. Allowed unused args must match /^_/u.", line: 1, column: 14 }]
         },
         {
             code: "function foo(a, _b, c) { return a; } foo();",
             options: [{ args: "after-used", argsIgnorePattern: "^_" }],
-            errors: [{ message: "'c' is defined but never used. Allowed unused args must match /^_/.", line: 1, column: 21 }]
+            errors: [{ message: "'c' is defined but never used. Allowed unused args must match /^_/u.", line: 1, column: 21 }]
         },
         {
             code: "function foo(_a) { } foo();",
             options: [{ args: "all", argsIgnorePattern: "[iI]gnored" }],
-            errors: [{ message: "'_a' is defined but never used. Allowed unused args must match /[iI]gnored/.", line: 1, column: 14 }]
+            errors: [{ message: "'_a' is defined but never used. Allowed unused args must match /[iI]gnored/u.", line: 1, column: 14 }]
         },
         {
             code: "var [ firstItemIgnored, secondItem ] = items;",
             options: [{ vars: "all", varsIgnorePattern: "[iI]gnored" }],
             parserOptions: { ecmaVersion: 6 },
-            errors: [{ message: "'secondItem' is assigned a value but never used. Allowed unused vars must match /[iI]gnored/.", line: 1, column: 25 }]
+            errors: [{ message: "'secondItem' is assigned a value but never used. Allowed unused vars must match /[iI]gnored/u.", line: 1, column: 25 }]
         },
 
         // for-in loops (see #2342)
@@ -524,14 +524,14 @@ ruleTester.run("no-unused-vars", rule, {
         {
             code: "try{}catch(err){};",
             options: [{ caughtErrors: "all", caughtErrorsIgnorePattern: "^ignore" }],
-            errors: [{ message: "'err' is defined but never used. Allowed unused args must match /^ignore/." }]
+            errors: [{ message: "'err' is defined but never used. Allowed unused args must match /^ignore/u." }]
         },
 
         // multiple try catch with one success
         {
             code: "try{}catch(ignoreErr){}try{}catch(err){};",
             options: [{ caughtErrors: "all", caughtErrorsIgnorePattern: "^ignore" }],
-            errors: [{ message: "'err' is defined but never used. Allowed unused args must match /^ignore/." }]
+            errors: [{ message: "'err' is defined but never used. Allowed unused args must match /^ignore/u." }]
         },
 
         // multiple try catch both fail
@@ -539,8 +539,8 @@ ruleTester.run("no-unused-vars", rule, {
             code: "try{}catch(error){}try{}catch(err){};",
             options: [{ caughtErrors: "all", caughtErrorsIgnorePattern: "^ignore" }],
             errors: [
-                { message: "'error' is defined but never used. Allowed unused args must match /^ignore/." },
-                { message: "'err' is defined but never used. Allowed unused args must match /^ignore/." }
+                { message: "'error' is defined but never used. Allowed unused args must match /^ignore/u." },
+                { message: "'err' is defined but never used. Allowed unused args must match /^ignore/u." }
             ]
         },
 
@@ -613,10 +613,10 @@ ruleTester.run("no-unused-vars", rule, {
             options: [{ argsIgnorePattern: "c" }],
             errors: [
                 {
-                    message: "'a' is defined but never used. Allowed unused args must match /c/."
+                    message: "'a' is defined but never used. Allowed unused args must match /c/u."
                 },
                 {
-                    message: "'b' is defined but never used. Allowed unused args must match /c/."
+                    message: "'b' is defined but never used. Allowed unused args must match /c/u."
                 }
             ]
         },
@@ -626,10 +626,10 @@ ruleTester.run("no-unused-vars", rule, {
             parserOptions: { ecmaVersion: 6 },
             errors: [
                 {
-                    message: "'a' is defined but never used. Allowed unused args must match /[cd]/."
+                    message: "'a' is defined but never used. Allowed unused args must match /[cd]/u."
                 },
                 {
-                    message: "'b' is defined but never used. Allowed unused args must match /[cd]/."
+                    message: "'b' is defined but never used. Allowed unused args must match /[cd]/u."
                 }
             ]
         },
@@ -639,13 +639,13 @@ ruleTester.run("no-unused-vars", rule, {
             parserOptions: { ecmaVersion: 6 },
             errors: [
                 {
-                    message: "'a' is defined but never used. Allowed unused args must match /c/."
+                    message: "'a' is defined but never used. Allowed unused args must match /c/u."
                 },
                 {
-                    message: "'b' is defined but never used. Allowed unused args must match /c/."
+                    message: "'b' is defined but never used. Allowed unused args must match /c/u."
                 },
                 {
-                    message: "'d' is defined but never used. Allowed unused args must match /c/."
+                    message: "'d' is defined but never used. Allowed unused args must match /c/u."
                 }
             ]
         },
@@ -655,13 +655,13 @@ ruleTester.run("no-unused-vars", rule, {
             parserOptions: { ecmaVersion: 6 },
             errors: [
                 {
-                    message: "'a' is defined but never used. Allowed unused args must match /d/."
+                    message: "'a' is defined but never used. Allowed unused args must match /d/u."
                 },
                 {
-                    message: "'b' is defined but never used. Allowed unused args must match /d/."
+                    message: "'b' is defined but never used. Allowed unused args must match /d/u."
                 },
                 {
-                    message: "'c' is defined but never used. Allowed unused args must match /d/."
+                    message: "'c' is defined but never used. Allowed unused args must match /d/u."
                 }
             ]
         },