diff --git a/tools/node_modules/eslint/README.md b/tools/node_modules/eslint/README.md
index 5f5be9c730927b..b75051e6ecd53b 100644
--- a/tools/node_modules/eslint/README.md
+++ b/tools/node_modules/eslint/README.md
@@ -252,7 +252,7 @@ The following companies, organizations, and individuals support ESLint's ongoing
Gold Sponsors
Silver Sponsors
Bronze Sponsors
-
+
## Technology Sponsors
diff --git a/tools/node_modules/eslint/conf/category-list.json b/tools/node_modules/eslint/conf/category-list.json
index 5427667b09e332..6609734950a443 100644
--- a/tools/node_modules/eslint/conf/category-list.json
+++ b/tools/node_modules/eslint/conf/category-list.json
@@ -10,12 +10,12 @@
],
"deprecated": {
"name": "Deprecated",
- "description": "These rules have been deprecated in accordance with the [deprecation policy](/docs/user-guide/rule-deprecation), and replaced by newer rules:",
+ "description": "These rules have been deprecated in accordance with the deprecation policy, and replaced by newer rules:",
"rules": []
},
"removed": {
"name": "Removed",
- "description": "These rules from older versions of ESLint (before the [deprecation policy](/docs/user-guide/rule-deprecation) existed) have been replaced by newer rules:",
+ "description": "These rules from older versions of ESLint (before the deprecation policy existed) have been replaced by newer rules:",
"rules": [
{ "removed": "generator-star", "replacedBy": ["generator-star-spacing"] },
{ "removed": "global-strict", "replacedBy": ["strict"] },
diff --git a/tools/node_modules/eslint/conf/eslint-recommended.js b/tools/node_modules/eslint/conf/eslint-recommended.js
index e915ec449040e9..2137685fb7c63e 100644
--- a/tools/node_modules/eslint/conf/eslint-recommended.js
+++ b/tools/node_modules/eslint/conf/eslint-recommended.js
@@ -26,6 +26,7 @@ module.exports = {
"no-delete-var": "error",
"no-dupe-args": "error",
"no-dupe-class-members": "error",
+ "no-dupe-else-if": "error",
"no-dupe-keys": "error",
"no-duplicate-case": "error",
"no-empty": "error",
@@ -37,6 +38,7 @@ module.exports = {
"no-fallthrough": "error",
"no-func-assign": "error",
"no-global-assign": "error",
+ "no-import-assign": "error",
"no-inner-declarations": "error",
"no-invalid-regexp": "error",
"no-irregular-whitespace": "error",
@@ -49,6 +51,7 @@ module.exports = {
"no-redeclare": "error",
"no-regex-spaces": "error",
"no-self-assign": "error",
+ "no-setter-return": "error",
"no-shadow-restricted-names": "error",
"no-sparse-arrays": "error",
"no-this-before-super": "error",
diff --git a/tools/node_modules/eslint/lib/rule-tester/rule-tester.js b/tools/node_modules/eslint/lib/rule-tester/rule-tester.js
index 2ac26534fcc0ea..5180f87a316f85 100644
--- a/tools/node_modules/eslint/lib/rule-tester/rule-tester.js
+++ b/tools/node_modules/eslint/lib/rule-tester/rule-tester.js
@@ -119,6 +119,32 @@ const RuleTesterParameters = [
"output"
];
+/*
+ * All allowed property names in error objects.
+ */
+const errorObjectParameters = new Set([
+ "message",
+ "messageId",
+ "data",
+ "type",
+ "line",
+ "column",
+ "endLine",
+ "endColumn",
+ "suggestions"
+]);
+const friendlyErrorObjectParameterList = `[${[...errorObjectParameters].map(key => `'${key}'`).join(", ")}]`;
+
+/*
+ * All allowed property names in suggestion objects.
+ */
+const suggestionObjectParameters = new Set([
+ "desc",
+ "messageId",
+ "output"
+]);
+const friendlySuggestionObjectParameterList = `[${[...suggestionObjectParameters].map(key => `'${key}'`).join(", ")}]`;
+
const hasOwnProperty = Function.call.bind(Object.hasOwnProperty);
/**
@@ -573,13 +599,21 @@ class RuleTester {
// Just an error message.
assertMessageMatches(message.message, error);
- } else if (typeof error === "object") {
+ } else if (typeof error === "object" && error !== null) {
/*
* Error object.
* This may have a message, messageId, data, node type, line, and/or
* column.
*/
+
+ Object.keys(error).forEach(propertyName => {
+ assert.ok(
+ errorObjectParameters.has(propertyName),
+ `Invalid error property name '${propertyName}'. Expected one of ${friendlyErrorObjectParameterList}.`
+ );
+ });
+
if (hasOwnProperty(error, "message")) {
assert.ok(!hasOwnProperty(error, "messageId"), "Error should not specify both 'message' and a 'messageId'.");
assert.ok(!hasOwnProperty(error, "data"), "Error should not specify both 'data' and 'message'.");
@@ -654,6 +688,17 @@ class RuleTester {
assert.strictEqual(message.suggestions.length, error.suggestions.length, `Error should have ${error.suggestions.length} suggestions. Instead found ${message.suggestions.length} suggestions`);
error.suggestions.forEach((expectedSuggestion, index) => {
+ assert.ok(
+ typeof expectedSuggestion === "object" && expectedSuggestion !== null,
+ "Test suggestion in 'suggestions' array must be an object."
+ );
+ Object.keys(expectedSuggestion).forEach(propertyName => {
+ assert.ok(
+ suggestionObjectParameters.has(propertyName),
+ `Invalid suggestion property name '${propertyName}'. Expected one of ${friendlySuggestionObjectParameterList}.`
+ );
+ });
+
const actualSuggestion = message.suggestions[index];
/**
diff --git a/tools/node_modules/eslint/lib/rules/accessor-pairs.js b/tools/node_modules/eslint/lib/rules/accessor-pairs.js
index 3a32db6eac7214..02548258ca2e28 100644
--- a/tools/node_modules/eslint/lib/rules/accessor-pairs.js
+++ b/tools/node_modules/eslint/lib/rules/accessor-pairs.js
@@ -171,7 +171,7 @@ module.exports = {
},
enforceForClassMembers: {
type: "boolean",
- default: false
+ default: true
}
},
additionalProperties: false
@@ -190,7 +190,7 @@ module.exports = {
const config = context.options[0] || {};
const checkGetWithoutSet = config.getWithoutSet === true;
const checkSetWithoutGet = config.setWithoutGet !== false;
- const enforceForClassMembers = config.enforceForClassMembers === true;
+ const enforceForClassMembers = config.enforceForClassMembers !== false;
const sourceCode = context.getSourceCode();
/**
diff --git a/tools/node_modules/eslint/lib/rules/arrow-body-style.js b/tools/node_modules/eslint/lib/rules/arrow-body-style.js
index 8d3b400037a6d5..9d5c77d8573d6a 100644
--- a/tools/node_modules/eslint/lib/rules/arrow-body-style.js
+++ b/tools/node_modules/eslint/lib/rules/arrow-body-style.js
@@ -91,7 +91,7 @@ module.exports = {
* @returns {Token} The found closing parenthesis token.
*/
function findClosingParen(token) {
- let node = sourceCode.getNodeByRangeIndex(token.range[1]);
+ let node = sourceCode.getNodeByRangeIndex(token.range[0]);
while (!astUtils.isParenthesised(sourceCode, node)) {
node = node.parent;
@@ -206,24 +206,35 @@ module.exports = {
fix(fixer) {
const fixes = [];
const arrowToken = sourceCode.getTokenBefore(arrowBody, astUtils.isArrowToken);
- const firstBodyToken = sourceCode.getTokenAfter(arrowToken);
- const lastBodyToken = sourceCode.getLastToken(node);
+ const [firstTokenAfterArrow, secondTokenAfterArrow] = sourceCode.getTokensAfter(arrowToken, { count: 2 });
+ const lastToken = sourceCode.getLastToken(node);
const isParenthesisedObjectLiteral =
- astUtils.isOpeningParenToken(firstBodyToken) &&
- astUtils.isOpeningBraceToken(sourceCode.getTokenAfter(firstBodyToken));
-
- // Wrap the value by a block and a return statement.
- fixes.push(
- fixer.insertTextBefore(firstBodyToken, "{return "),
- fixer.insertTextAfter(lastBodyToken, "}")
- );
+ astUtils.isOpeningParenToken(firstTokenAfterArrow) &&
+ astUtils.isOpeningBraceToken(secondTokenAfterArrow);
// If the value is object literal, remove parentheses which were forced by syntax.
if (isParenthesisedObjectLiteral) {
- fixes.push(
- fixer.remove(firstBodyToken),
- fixer.remove(findClosingParen(firstBodyToken))
- );
+ const openingParenToken = firstTokenAfterArrow;
+ const openingBraceToken = secondTokenAfterArrow;
+
+ if (astUtils.isTokenOnSameLine(openingParenToken, openingBraceToken)) {
+ fixes.push(fixer.replaceText(openingParenToken, "{return "));
+ } else {
+
+ // Avoid ASI
+ fixes.push(
+ fixer.replaceText(openingParenToken, "{"),
+ fixer.insertTextBefore(openingBraceToken, "return ")
+ );
+ }
+
+ // Closing paren for the object doesn't have to be lastToken, e.g.: () => ({}).foo()
+ fixes.push(fixer.remove(findClosingParen(openingBraceToken)));
+ fixes.push(fixer.insertTextAfter(lastToken, "}"));
+
+ } else {
+ fixes.push(fixer.insertTextBefore(firstTokenAfterArrow, "{return "));
+ fixes.push(fixer.insertTextAfter(lastToken, "}"));
}
return fixes;
diff --git a/tools/node_modules/eslint/lib/rules/computed-property-spacing.js b/tools/node_modules/eslint/lib/rules/computed-property-spacing.js
index 48bea6a6fdc8f4..53fdb8f4e41f95 100644
--- a/tools/node_modules/eslint/lib/rules/computed-property-spacing.js
+++ b/tools/node_modules/eslint/lib/rules/computed-property-spacing.js
@@ -32,7 +32,7 @@ module.exports = {
properties: {
enforceForClassMembers: {
type: "boolean",
- default: false
+ default: true
}
},
additionalProperties: false
@@ -51,7 +51,7 @@ module.exports = {
create(context) {
const sourceCode = context.getSourceCode();
const propertyNameMustBeSpaced = context.options[0] === "always"; // default is "never"
- const enforceForClassMembers = context.options[1] && context.options[1].enforceForClassMembers;
+ const enforceForClassMembers = !context.options[1] || context.options[1].enforceForClassMembers;
//--------------------------------------------------------------------------
// Helpers
diff --git a/tools/node_modules/eslint/lib/rules/curly.js b/tools/node_modules/eslint/lib/rules/curly.js
index ee2fe4dceb82bf..29f00c0ad0b617 100644
--- a/tools/node_modules/eslint/lib/rules/curly.js
+++ b/tools/node_modules/eslint/lib/rules/curly.js
@@ -141,33 +141,14 @@ module.exports = {
}
/**
- * Checks a given IfStatement node requires braces of the consequent chunk.
- * This returns `true` when below:
- *
- * 1. The given node has the `alternate` node.
- * 2. There is a `IfStatement` which doesn't have `alternate` node in the
- * trailing statement chain of the `consequent` node.
- * @param {ASTNode} node A IfStatement node to check.
- * @returns {boolean} `true` if the node requires braces of the consequent chunk.
+ * Determines whether the given node has an `else` keyword token as the first token after.
+ * @param {ASTNode} node The node to check.
+ * @returns {boolean} `true` if the node is followed by an `else` keyword token.
*/
- function requiresBraceOfConsequent(node) {
- if (node.alternate && node.consequent.type === "BlockStatement") {
- if (node.consequent.body.length >= 2) {
- return true;
- }
-
- for (
- let currentNode = node.consequent.body[0];
- currentNode;
- currentNode = astUtils.getTrailingStatement(currentNode)
- ) {
- if (currentNode.type === "IfStatement" && !currentNode.alternate) {
- return true;
- }
- }
- }
+ function isFollowedByElseKeyword(node) {
+ const nextToken = sourceCode.getTokenAfter(node);
- return false;
+ return Boolean(nextToken) && isElseKeywordToken(nextToken);
}
/**
@@ -224,6 +205,110 @@ module.exports = {
return false;
}
+ /**
+ * Determines whether the code represented by the given node contains an `if` statement
+ * that would become associated with an `else` keyword directly appended to that code.
+ *
+ * Examples where it returns `true`:
+ *
+ * if (a)
+ * foo();
+ *
+ * if (a) {
+ * foo();
+ * }
+ *
+ * if (a)
+ * foo();
+ * else if (b)
+ * bar();
+ *
+ * while (a)
+ * if (b)
+ * if(c)
+ * foo();
+ * else
+ * bar();
+ *
+ * Examples where it returns `false`:
+ *
+ * if (a)
+ * foo();
+ * else
+ * bar();
+ *
+ * while (a) {
+ * if (b)
+ * if(c)
+ * foo();
+ * else
+ * bar();
+ * }
+ *
+ * while (a)
+ * if (b) {
+ * if(c)
+ * foo();
+ * }
+ * else
+ * bar();
+ * @param {ASTNode} node Node representing the code to check.
+ * @returns {boolean} `true` if an `if` statement within the code would become associated with an `else` appended to that code.
+ */
+ function hasUnsafeIf(node) {
+ switch (node.type) {
+ case "IfStatement":
+ if (!node.alternate) {
+ return true;
+ }
+ return hasUnsafeIf(node.alternate);
+ case "ForStatement":
+ case "ForInStatement":
+ case "ForOfStatement":
+ case "LabeledStatement":
+ case "WithStatement":
+ case "WhileStatement":
+ return hasUnsafeIf(node.body);
+ default:
+ return false;
+ }
+ }
+
+ /**
+ * Determines whether the existing curly braces around the single statement are necessary to preserve the semantics of the code.
+ * The braces, which make the given block body, are necessary in either of the following situations:
+ *
+ * 1. The statement is a lexical declaration.
+ * 2. Without the braces, an `if` within the statement would become associated with an `else` after the closing brace:
+ *
+ * if (a) {
+ * if (b)
+ * foo();
+ * }
+ * else
+ * bar();
+ *
+ * if (a)
+ * while (b)
+ * while (c) {
+ * while (d)
+ * if (e)
+ * while(f)
+ * foo();
+ * }
+ * else
+ * bar();
+ * @param {ASTNode} node `BlockStatement` body with exactly one statement directly inside. The statement can have its own nested statements.
+ * @returns {boolean} `true` if the braces are necessary - removing them (replacing the given `BlockStatement` body with its single statement content)
+ * would change the semantics of the code or produce a syntax error.
+ */
+ function areBracesNecessary(node) {
+ const statement = node.body[0];
+
+ return isLexicalDeclaration(statement) ||
+ hasUnsafeIf(statement) && isFollowedByElseKeyword(node);
+ }
+
/**
* Prepares to check the body of a node to see if it's a block statement.
* @param {ASTNode} node The node to report if there's a problem.
@@ -242,30 +327,29 @@ module.exports = {
const hasBlock = (body.type === "BlockStatement");
let expected = null;
- if (node.type === "IfStatement" && node.consequent === body && requiresBraceOfConsequent(node)) {
+ if (hasBlock && (body.body.length !== 1 || areBracesNecessary(body))) {
expected = true;
} else if (multiOnly) {
- if (hasBlock && body.body.length === 1 && !isLexicalDeclaration(body.body[0])) {
- expected = false;
- }
+ expected = false;
} else if (multiLine) {
if (!isCollapsedOneLiner(body)) {
expected = true;
}
+
+ // otherwise, the body is allowed to have braces or not to have braces
+
} else if (multiOrNest) {
- if (hasBlock && body.body.length === 1 && isOneLiner(body.body[0])) {
- const leadingComments = sourceCode.getCommentsBefore(body.body[0]);
- const isLexDef = isLexicalDeclaration(body.body[0]);
-
- if (isLexDef) {
- expected = true;
- } else {
- expected = leadingComments.length > 0;
- }
- } else if (!isOneLiner(body)) {
- expected = true;
+ if (hasBlock) {
+ const statement = body.body[0];
+ const leadingCommentsInBlock = sourceCode.getCommentsBefore(statement);
+
+ expected = !isOneLiner(statement) || leadingCommentsInBlock.length > 0;
+ } else {
+ expected = !isOneLiner(body);
}
} else {
+
+ // default "all"
expected = true;
}
diff --git a/tools/node_modules/eslint/lib/rules/func-names.js b/tools/node_modules/eslint/lib/rules/func-names.js
index 1341d03630823f..ecfedb9e0e99c7 100644
--- a/tools/node_modules/eslint/lib/rules/func-names.js
+++ b/tools/node_modules/eslint/lib/rules/func-names.js
@@ -119,8 +119,7 @@ module.exports = {
(parent.type === "VariableDeclarator" && parent.id.type === "Identifier" && parent.init === node) ||
(parent.type === "Property" && parent.value === node) ||
(parent.type === "AssignmentExpression" && parent.left.type === "Identifier" && parent.right === node) ||
- (parent.type === "ExportDefaultDeclaration" && parent.declaration === node) ||
- (parent.type === "AssignmentPattern" && parent.right === node);
+ (parent.type === "AssignmentPattern" && parent.left.type === "Identifier" && parent.right === node);
}
/**
@@ -151,33 +150,41 @@ module.exports = {
});
}
- return {
- "FunctionExpression:exit"(node) {
+ /**
+ * The listener for function nodes.
+ * @param {ASTNode} node function node
+ * @returns {void}
+ */
+ function handleFunction(node) {
- // Skip recursive functions.
- const nameVar = context.getDeclaredVariables(node)[0];
+ // Skip recursive functions.
+ const nameVar = context.getDeclaredVariables(node)[0];
- if (isFunctionName(nameVar) && nameVar.references.length > 0) {
- return;
- }
+ if (isFunctionName(nameVar) && nameVar.references.length > 0) {
+ return;
+ }
- const hasName = Boolean(node.id && node.id.name);
- const config = getConfigForNode(node);
-
- if (config === "never") {
- if (hasName) {
- reportUnexpectedNamedFunction(node);
- }
- } else if (config === "as-needed") {
- if (!hasName && !hasInferredName(node)) {
- reportUnexpectedUnnamedFunction(node);
- }
- } else {
- if (!hasName && !isObjectOrClassMethod(node)) {
- reportUnexpectedUnnamedFunction(node);
- }
+ const hasName = Boolean(node.id && node.id.name);
+ const config = getConfigForNode(node);
+
+ if (config === "never") {
+ if (hasName && node.type !== "FunctionDeclaration") {
+ reportUnexpectedNamedFunction(node);
+ }
+ } else if (config === "as-needed") {
+ if (!hasName && !hasInferredName(node)) {
+ reportUnexpectedUnnamedFunction(node);
+ }
+ } else {
+ if (!hasName && !isObjectOrClassMethod(node)) {
+ reportUnexpectedUnnamedFunction(node);
}
}
+ }
+
+ return {
+ "FunctionExpression:exit": handleFunction,
+ "ExportDefaultDeclaration > FunctionDeclaration": handleFunction
};
}
};
diff --git a/tools/node_modules/eslint/lib/rules/id-blacklist.js b/tools/node_modules/eslint/lib/rules/id-blacklist.js
index cb6e5e215f6c60..ddff9363aee8a8 100644
--- a/tools/node_modules/eslint/lib/rules/id-blacklist.js
+++ b/tools/node_modules/eslint/lib/rules/id-blacklist.js
@@ -81,6 +81,28 @@ module.exports = {
);
}
+ /**
+ * Checks whether the given node is a renamed identifier node in an ObjectPattern destructuring.
+ *
+ * Examples:
+ * const { a : b } = foo; // node `a` is renamed node.
+ * @param {ASTNode} node `Identifier` node to check.
+ * @returns {boolean} `true` if the node is a renamed node in an ObjectPattern destructuring.
+ */
+ function isRenamedInDestructuring(node) {
+ const parent = node.parent;
+
+ return (
+ (
+ !parent.computed &&
+ parent.type === "Property" &&
+ parent.parent.type === "ObjectPattern" &&
+ parent.value !== node &&
+ parent.key === node
+ )
+ );
+ }
+
/**
* Verifies if we should report an error or not.
* @param {ASTNode} node The node to check
@@ -92,8 +114,8 @@ module.exports = {
return (
parent.type !== "CallExpression" &&
parent.type !== "NewExpression" &&
- parent.parent.type !== "ObjectPattern" &&
!isRenamedImport(node) &&
+ !isRenamedInDestructuring(node) &&
isInvalid(node.name)
);
}
@@ -141,6 +163,24 @@ module.exports = {
if (isInvalid(name)) {
report(node);
}
+
+ // Report the last identifier in an ObjectPattern destructuring.
+ } else if (
+ (
+ effectiveParent.type === "Property" &&
+ effectiveParent.value === node.parent &&
+ effectiveParent.parent.type === "ObjectPattern"
+ ) ||
+ effectiveParent.type === "RestElement" ||
+ effectiveParent.type === "ArrayPattern" ||
+ (
+ effectiveParent.type === "AssignmentPattern" &&
+ effectiveParent.left === node.parent
+ )
+ ) {
+ if (isInvalid(name)) {
+ report(node);
+ }
}
} else if (shouldReport(node)) {
diff --git a/tools/node_modules/eslint/lib/rules/id-length.js b/tools/node_modules/eslint/lib/rules/id-length.js
index c8586ea3481895..a68873ac06289b 100644
--- a/tools/node_modules/eslint/lib/rules/id-length.js
+++ b/tools/node_modules/eslint/lib/rules/id-length.js
@@ -63,6 +63,7 @@ module.exports = {
return obj;
}, {});
+ const reportedNode = new Set();
const SUPPORTED_EXPRESSIONS = {
MemberExpression: properties && function(parent) {
@@ -82,8 +83,15 @@ module.exports = {
VariableDeclarator(parent, node) {
return parent.id === node;
},
- Property: properties && function(parent, node) {
- return parent.key === node;
+ Property(parent, node) {
+
+ if (parent.parent.type === "ObjectPattern") {
+ return (
+ parent.value !== parent.key && parent.value === node ||
+ parent.value === parent.key && parent.key === node && properties
+ );
+ }
+ return properties && !parent.computed && parent.key === node;
},
ImportDefaultSpecifier: true,
RestElement: true,
@@ -92,7 +100,8 @@ module.exports = {
ClassDeclaration: true,
FunctionDeclaration: true,
MethodDefinition: true,
- CatchClause: true
+ CatchClause: true,
+ ArrayPattern: true
};
return {
@@ -109,7 +118,8 @@ module.exports = {
const isValidExpression = SUPPORTED_EXPRESSIONS[parent.type];
- if (isValidExpression && (isValidExpression === true || isValidExpression(parent, node))) {
+ if (isValidExpression && !reportedNode.has(node) && (isValidExpression === true || isValidExpression(parent, node))) {
+ reportedNode.add(node);
context.report({
node,
messageId: isShort ? "tooShort" : "tooLong",
diff --git a/tools/node_modules/eslint/lib/rules/indent-legacy.js b/tools/node_modules/eslint/lib/rules/indent-legacy.js
index f1c024c368475d..50010d3f7acb16 100644
--- a/tools/node_modules/eslint/lib/rules/indent-legacy.js
+++ b/tools/node_modules/eslint/lib/rules/indent-legacy.js
@@ -696,20 +696,6 @@ module.exports = {
return startLine === endLine;
}
- /**
- * Check to see if the first element inside an array is an object and on the same line as the node
- * If the node is not an array then it will return false.
- * @param {ASTNode} node node to check
- * @returns {boolean} success/failure
- */
- function isFirstArrayElementOnSameLine(node) {
- if (node.type === "ArrayExpression" && node.elements[0]) {
- return node.elements[0].loc.start.line === node.loc.start.line && node.elements[0].type === "ObjectExpression";
- }
- return false;
-
- }
-
/**
* Check indent for array block content or object block content
* @param {ASTNode} node node to examine
@@ -776,8 +762,6 @@ module.exports = {
nodeIndent += indentSize;
}
}
- } else if (!parentVarNode && !isFirstArrayElementOnSameLine(parent) && parent.type !== "MemberExpression" && parent.type !== "ExpressionStatement" && parent.type !== "AssignmentExpression" && parent.type !== "Property") {
- nodeIndent += indentSize;
}
checkFirstNodeLineIndent(node, nodeIndent);
diff --git a/tools/node_modules/eslint/lib/rules/no-dupe-else-if.js b/tools/node_modules/eslint/lib/rules/no-dupe-else-if.js
index a165e16607d2f0..cbeb437da1e7ec 100644
--- a/tools/node_modules/eslint/lib/rules/no-dupe-else-if.js
+++ b/tools/node_modules/eslint/lib/rules/no-dupe-else-if.js
@@ -53,7 +53,7 @@ module.exports = {
docs: {
description: "disallow duplicate conditions in if-else-if chains",
category: "Possible Errors",
- recommended: false,
+ recommended: true,
url: "https://eslint.org/docs/rules/no-dupe-else-if"
},
diff --git a/tools/node_modules/eslint/lib/rules/no-eval.js b/tools/node_modules/eslint/lib/rules/no-eval.js
index 9e56fb00b9fc5b..a293b04aa37de2 100644
--- a/tools/node_modules/eslint/lib/rules/no-eval.js
+++ b/tools/node_modules/eslint/lib/rules/no-eval.js
@@ -158,7 +158,7 @@ module.exports = {
context.report({
node: reportNode,
- loc: locationNode.loc.start,
+ loc: locationNode.loc,
messageId: "unexpected"
});
}
diff --git a/tools/node_modules/eslint/lib/rules/no-extra-boolean-cast.js b/tools/node_modules/eslint/lib/rules/no-extra-boolean-cast.js
index 336f601d1652de..8ccd0bce9060b1 100644
--- a/tools/node_modules/eslint/lib/rules/no-extra-boolean-cast.js
+++ b/tools/node_modules/eslint/lib/rules/no-extra-boolean-cast.js
@@ -26,7 +26,16 @@ module.exports = {
url: "https://eslint.org/docs/rules/no-extra-boolean-cast"
},
- schema: [],
+ schema: [{
+ type: "object",
+ properties: {
+ enforceForLogicalOperands: {
+ type: "boolean",
+ default: false
+ }
+ },
+ additionalProperties: false
+ }],
fixable: "code",
messages: {
@@ -47,23 +56,67 @@ module.exports = {
"ForStatement"
];
+ /**
+ * Check if a node is a Boolean function or constructor.
+ * @param {ASTNode} node the node
+ * @returns {boolean} If the node is Boolean function or constructor
+ */
+ function isBooleanFunctionOrConstructorCall(node) {
+
+ // Boolean() and new Boolean()
+ return (node.type === "CallExpression" || node.type === "NewExpression") &&
+ node.callee.type === "Identifier" &&
+ node.callee.name === "Boolean";
+ }
+
+ /**
+ * Checks whether the node is a logical expression and that the option is enabled
+ * @param {ASTNode} node the node
+ * @returns {boolean} if the node is a logical expression and option is enabled
+ */
+ function isLogicalContext(node) {
+ return node.type === "LogicalExpression" &&
+ (node.operator === "||" || node.operator === "&&") &&
+ (context.options.length && context.options[0].enforceForLogicalOperands === true);
+
+ }
+
+
/**
* Check if a node is in a context where its value would be coerced to a boolean at runtime.
* @param {ASTNode} node The node
- * @param {ASTNode} parent Its parent
* @returns {boolean} If it is in a boolean context
*/
- function isInBooleanContext(node, parent) {
+ function isInBooleanContext(node) {
return (
- (BOOLEAN_NODE_TYPES.indexOf(parent.type) !== -1 &&
- node === parent.test) ||
+ (isBooleanFunctionOrConstructorCall(node.parent) &&
+ node === node.parent.arguments[0]) ||
+
+ (BOOLEAN_NODE_TYPES.indexOf(node.parent.type) !== -1 &&
+ node === node.parent.test) ||
// !
- (parent.type === "UnaryExpression" &&
- parent.operator === "!")
+ (node.parent.type === "UnaryExpression" &&
+ node.parent.operator === "!")
+ );
+ }
+
+ /**
+ * Checks whether the node is a context that should report an error
+ * Acts recursively if it is in a logical context
+ * @param {ASTNode} node the node
+ * @returns {boolean} If the node is in one of the flagged contexts
+ */
+ function isInFlaggedContext(node) {
+ return isInBooleanContext(node) ||
+ (isLogicalContext(node.parent) &&
+
+ // For nested logical statements
+ isInFlaggedContext(node.parent)
);
}
+
/**
* Check if a node has comments inside.
* @param {ASTNode} node The node to check.
@@ -75,24 +128,18 @@ module.exports = {
return {
UnaryExpression(node) {
- const ancestors = context.getAncestors(),
- parent = ancestors.pop(),
- grandparent = ancestors.pop();
+ const parent = node.parent;
+
// Exit early if it's guaranteed not to match
if (node.operator !== "!" ||
- parent.type !== "UnaryExpression" ||
- parent.operator !== "!") {
+ parent.type !== "UnaryExpression" ||
+ parent.operator !== "!") {
return;
}
- if (isInBooleanContext(parent, grandparent) ||
- // Boolean() and new Boolean()
- ((grandparent.type === "CallExpression" || grandparent.type === "NewExpression") &&
- grandparent.callee.type === "Identifier" &&
- grandparent.callee.name === "Boolean")
- ) {
+ if (isInFlaggedContext(parent)) {
context.report({
node: parent,
messageId: "unexpectedNegation",
@@ -110,6 +157,10 @@ module.exports = {
prefix = " ";
}
+ if (astUtils.getPrecedence(node.argument) < astUtils.getPrecedence(parent.parent)) {
+ return fixer.replaceText(parent, `(${sourceCode.getText(node.argument)})`);
+ }
+
return fixer.replaceText(parent, prefix + sourceCode.getText(node.argument));
}
});
@@ -122,7 +173,7 @@ module.exports = {
return;
}
- if (isInBooleanContext(node, parent)) {
+ if (isInFlaggedContext(node)) {
context.report({
node,
messageId: "unexpectedCall",
diff --git a/tools/node_modules/eslint/lib/rules/no-import-assign.js b/tools/node_modules/eslint/lib/rules/no-import-assign.js
index 0865cf9a97704d..32e445ff68b3ef 100644
--- a/tools/node_modules/eslint/lib/rules/no-import-assign.js
+++ b/tools/node_modules/eslint/lib/rules/no-import-assign.js
@@ -180,7 +180,7 @@ module.exports = {
docs: {
description: "disallow assigning to imported bindings",
category: "Possible Errors",
- recommended: false,
+ recommended: true,
url: "https://eslint.org/docs/rules/no-import-assign"
},
diff --git a/tools/node_modules/eslint/lib/rules/no-restricted-modules.js b/tools/node_modules/eslint/lib/rules/no-restricted-modules.js
index abbc30a05fb53d..abd8d5cbe29381 100644
--- a/tools/node_modules/eslint/lib/rules/no-restricted-modules.js
+++ b/tools/node_modules/eslint/lib/rules/no-restricted-modules.js
@@ -106,10 +106,19 @@ module.exports = {
* @param {ASTNode} node The node to check.
* @returns {boolean} If the node is a string literal.
*/
- function isString(node) {
+ function isStringLiteral(node) {
return node && node.type === "Literal" && typeof node.value === "string";
}
+ /**
+ * Function to check if a node is a static string template literal.
+ * @param {ASTNode} node The node to check.
+ * @returns {boolean} If the node is a string template literal.
+ */
+ function isStaticTemplateLiteral(node) {
+ return node && node.type === "TemplateLiteral" && node.expressions.length === 0;
+ }
+
/**
* Function to check if a node is a require call.
* @param {ASTNode} node The node to check.
@@ -119,14 +128,31 @@ module.exports = {
return node.callee.type === "Identifier" && node.callee.name === "require";
}
+ /**
+ * Extract string from Literal or TemplateLiteral node
+ * @param {ASTNode} node The node to extract from
+ * @returns {string|null} Extracted string or null if node doesn't represent a string
+ */
+ function getFirstArgumentString(node) {
+ if (isStringLiteral(node)) {
+ return node.value.trim();
+ }
+
+ if (isStaticTemplateLiteral(node)) {
+ return node.quasis[0].value.cooked.trim();
+ }
+
+ return null;
+ }
+
/**
* Report a restricted path.
* @param {node} node representing the restricted path reference
+ * @param {string} name restricted path
* @returns {void}
* @private
*/
- function reportPath(node) {
- const name = node.arguments[0].value.trim();
+ function reportPath(node, name) {
const customMessage = restrictedPathMessages[name];
const messageId = customMessage
? "customMessage"
@@ -156,21 +182,25 @@ module.exports = {
CallExpression(node) {
if (isRequireCall(node)) {
- // node has arguments and first argument is string
- if (node.arguments.length && isString(node.arguments[0])) {
- const name = node.arguments[0].value.trim();
-
- // check if argument value is in restricted modules array
- if (isRestrictedPath(name)) {
- reportPath(node);
- }
-
- if (restrictedPatterns.length > 0 && ig.ignores(name)) {
- context.report({
- node,
- messageId: "patternMessage",
- data: { name }
- });
+ // node has arguments
+ if (node.arguments.length) {
+ const name = getFirstArgumentString(node.arguments[0]);
+
+ // if first argument is a string literal or a static string template literal
+ if (name) {
+
+ // check if argument value is in restricted modules array
+ if (isRestrictedPath(name)) {
+ reportPath(node, name);
+ }
+
+ if (restrictedPatterns.length > 0 && ig.ignores(name)) {
+ context.report({
+ node,
+ messageId: "patternMessage",
+ data: { name }
+ });
+ }
}
}
}
diff --git a/tools/node_modules/eslint/lib/rules/no-setter-return.js b/tools/node_modules/eslint/lib/rules/no-setter-return.js
index e0948696c347f5..a558640c357d92 100644
--- a/tools/node_modules/eslint/lib/rules/no-setter-return.js
+++ b/tools/node_modules/eslint/lib/rules/no-setter-return.js
@@ -145,7 +145,7 @@ module.exports = {
docs: {
description: "disallow returning values from setters",
category: "Possible Errors",
- recommended: false,
+ recommended: true,
url: "https://eslint.org/docs/rules/no-setter-return"
},
diff --git a/tools/node_modules/eslint/lib/rules/no-underscore-dangle.js b/tools/node_modules/eslint/lib/rules/no-underscore-dangle.js
index 1468198ac4e958..cac594e10047e8 100644
--- a/tools/node_modules/eslint/lib/rules/no-underscore-dangle.js
+++ b/tools/node_modules/eslint/lib/rules/no-underscore-dangle.js
@@ -205,7 +205,7 @@ module.exports = {
const identifier = node.key.name;
const isMethod = node.type === "MethodDefinition" || node.type === "Property" && node.method;
- if (typeof identifier !== "undefined" && enforceInMethodNames && isMethod && hasTrailingUnderscore(identifier)) {
+ if (typeof identifier !== "undefined" && enforceInMethodNames && isMethod && hasTrailingUnderscore(identifier) && !isAllowed(identifier)) {
context.report({
node,
messageId: "unexpectedUnderscore",
diff --git a/tools/node_modules/eslint/lib/rules/wrap-iife.js b/tools/node_modules/eslint/lib/rules/wrap-iife.js
index 5e590be13e28b5..896aed63de5222 100644
--- a/tools/node_modules/eslint/lib/rules/wrap-iife.js
+++ b/tools/node_modules/eslint/lib/rules/wrap-iife.js
@@ -10,6 +10,21 @@
//------------------------------------------------------------------------------
const astUtils = require("./utils/ast-utils");
+const eslintUtils = require("eslint-utils");
+
+//----------------------------------------------------------------------
+// Helpers
+//----------------------------------------------------------------------
+
+/**
+ * Check if the given node is callee of a `NewExpression` node
+ * @param {ASTNode} node node to check
+ * @returns {boolean} True if the node is callee of a `NewExpression` node
+ * @private
+ */
+function isCalleeOfNewExpression(node) {
+ return node.parent.type === "NewExpression" && node.parent.callee === node;
+}
//------------------------------------------------------------------------------
// Rule Definition
@@ -58,15 +73,25 @@ module.exports = {
const sourceCode = context.getSourceCode();
/**
- * Check if the node is wrapped in ()
+ * Check if the node is wrapped in any (). All parens count: grouping parens and parens for constructs such as if()
* @param {ASTNode} node node to evaluate
- * @returns {boolean} True if it is wrapped
+ * @returns {boolean} True if it is wrapped in any parens
* @private
*/
- function wrapped(node) {
+ function isWrappedInAnyParens(node) {
return astUtils.isParenthesised(sourceCode, node);
}
+ /**
+ * Check if the node is wrapped in grouping (). Parens for constructs such as if() don't count
+ * @param {ASTNode} node node to evaluate
+ * @returns {boolean} True if it is wrapped in grouping parens
+ * @private
+ */
+ function isWrappedInGroupingParens(node) {
+ return eslintUtils.isParenthesized(1, node, sourceCode);
+ }
+
/**
* Get the function node from an IIFE
* @param {ASTNode} node node to evaluate
@@ -99,10 +124,10 @@ module.exports = {
return;
}
- const callExpressionWrapped = wrapped(node),
- functionExpressionWrapped = wrapped(innerNode);
+ const isCallExpressionWrapped = isWrappedInAnyParens(node),
+ isFunctionExpressionWrapped = isWrappedInAnyParens(innerNode);
- if (!callExpressionWrapped && !functionExpressionWrapped) {
+ if (!isCallExpressionWrapped && !isFunctionExpressionWrapped) {
context.report({
node,
messageId: "wrapInvocation",
@@ -112,27 +137,39 @@ module.exports = {
return fixer.replaceText(nodeToSurround, `(${sourceCode.getText(nodeToSurround)})`);
}
});
- } else if (style === "inside" && !functionExpressionWrapped) {
+ } else if (style === "inside" && !isFunctionExpressionWrapped) {
context.report({
node,
messageId: "wrapExpression",
fix(fixer) {
+ // The outer call expression will always be wrapped at this point.
+
+ if (isWrappedInGroupingParens(node) && !isCalleeOfNewExpression(node)) {
+
+ /*
+ * Parenthesize the function expression and remove unnecessary grouping parens around the call expression.
+ * Replace the range between the end of the function expression and the end of the call expression.
+ * for example, in `(function(foo) {}(bar))`, the range `(bar))` should get replaced with `)(bar)`.
+ */
+
+ const parenAfter = sourceCode.getTokenAfter(node);
+
+ return fixer.replaceTextRange(
+ [innerNode.range[1], parenAfter.range[1]],
+ `)${sourceCode.getText().slice(innerNode.range[1], parenAfter.range[0])}`
+ );
+ }
+
/*
- * The outer call expression will always be wrapped at this point.
- * Replace the range between the end of the function expression and the end of the call expression.
- * for example, in `(function(foo) {}(bar))`, the range `(bar))` should get replaced with `)(bar)`.
- * Replace the parens from the outer expression, and parenthesize the function expression.
+ * Call expression is wrapped in mandatory parens such as if(), or in necessary grouping parens.
+ * These parens cannot be removed, so just parenthesize the function expression.
*/
- const parenAfter = sourceCode.getTokenAfter(node);
- return fixer.replaceTextRange(
- [innerNode.range[1], parenAfter.range[1]],
- `)${sourceCode.getText().slice(innerNode.range[1], parenAfter.range[0])}`
- );
+ return fixer.replaceText(innerNode, `(${sourceCode.getText(innerNode)})`);
}
});
- } else if (style === "outside" && !callExpressionWrapped) {
+ } else if (style === "outside" && !isCallExpressionWrapped) {
context.report({
node,
messageId: "moveInvocation",
diff --git a/tools/node_modules/eslint/package.json b/tools/node_modules/eslint/package.json
index 2a30f80d3bd096..dd7d22ef9a0f85 100644
--- a/tools/node_modules/eslint/package.json
+++ b/tools/node_modules/eslint/package.json
@@ -152,5 +152,5 @@
"test:cli": "mocha",
"webpack": "node Makefile.js webpack"
},
- "version": "7.0.0-alpha.1"
+ "version": "7.0.0-alpha.2"
}
\ No newline at end of file