From 2492bacc0eca65f27f20650f0b822f6627c9858d Mon Sep 17 00:00:00 2001 From: Vitor Balocco Date: Mon, 25 Apr 2016 23:31:34 +0200 Subject: [PATCH] Chore: Add metadata to existing rules - Batch 6 (refs #5417) Chore: Add metadata to existing rules - Batch 6 of 7 (refs #5417) --- lib/rules/no-useless-call.js | 46 +- lib/rules/no-useless-concat.js | 64 +-- lib/rules/no-useless-constructor.js | 62 ++- lib/rules/no-useless-escape.js | 108 +++-- lib/rules/no-var.js | 28 +- lib/rules/no-void.js | 34 +- lib/rules/no-warning-comments.js | 196 ++++---- lib/rules/no-whitespace-before-property.js | 133 ++--- lib/rules/no-with.js | 26 +- lib/rules/object-curly-spacing.js | 460 +++++++++--------- lib/rules/object-shorthand.js | 198 ++++---- lib/rules/one-var-declaration-per-line.js | 110 +++-- lib/rules/one-var.js | 536 +++++++++++---------- lib/rules/operator-assignment.js | 92 ++-- lib/rules/operator-linebreak.js | 226 ++++----- lib/rules/padded-blocks.js | 382 ++++++++------- lib/rules/prefer-arrow-callback.js | 212 ++++---- lib/rules/prefer-const.js | 162 ++++--- lib/rules/prefer-reflect.js | 172 +++---- lib/rules/prefer-rest-params.js | 68 +-- 20 files changed, 1759 insertions(+), 1556 deletions(-) diff --git a/lib/rules/no-useless-call.js b/lib/rules/no-useless-call.js index 808e39624ce..eb14f0baf94 100644 --- a/lib/rules/no-useless-call.js +++ b/lib/rules/no-useless-call.js @@ -71,25 +71,35 @@ function isValidThisArg(expectedThis, thisArg, context) { // Rule Definition //------------------------------------------------------------------------------ -module.exports = function(context) { - return { - CallExpression: function(node) { - if (!isCallOrNonVariadicApply(node)) { - return; - } +module.exports = { + meta: { + docs: { + description: "disallow unnecessary calls to `.call()` and `.apply()`", + category: "Best Practices", + recommended: false + }, + + schema: [] + }, + + create: function(context) { + return { + CallExpression: function(node) { + if (!isCallOrNonVariadicApply(node)) { + return; + } - var applied = node.callee.object; - var expectedThis = (applied.type === "MemberExpression") ? applied.object : null; - var thisArg = node.arguments[0]; + var applied = node.callee.object; + var expectedThis = (applied.type === "MemberExpression") ? applied.object : null; + var thisArg = node.arguments[0]; - if (isValidThisArg(expectedThis, thisArg, context)) { - context.report( - node, - "unnecessary '.{{name}}()'.", - {name: node.callee.property.name}); + if (isValidThisArg(expectedThis, thisArg, context)) { + context.report( + node, + "unnecessary '.{{name}}()'.", + {name: node.callee.property.name}); + } } - } - }; + }; + } }; - -module.exports.schema = []; diff --git a/lib/rules/no-useless-concat.js b/lib/rules/no-useless-concat.js index 104a688315c..ce9589d4880 100644 --- a/lib/rules/no-useless-concat.js +++ b/lib/rules/no-useless-concat.js @@ -55,38 +55,48 @@ function getRight(node) { // Rule Definition //------------------------------------------------------------------------------ -module.exports = function(context) { - return { - BinaryExpression: function(node) { +module.exports = { + meta: { + docs: { + description: "disallow unnecessary concatenation of literals or template literals", + category: "Best Practices", + recommended: false + }, + + schema: [] + }, + + create: function(context) { + return { + BinaryExpression: function(node) { + + // check if not concatenation + if (node.operator !== "+") { + return; + } - // check if not concatenation - if (node.operator !== "+") { - return; - } + // account for the `foo + "a" + "b"` case + var left = getLeft(node); + var right = getRight(node); - // account for the `foo + "a" + "b"` case - var left = getLeft(node); - var right = getRight(node); + if (astUtils.isStringLiteral(left) && + astUtils.isStringLiteral(right) && + astUtils.isTokenOnSameLine(left, right) + ) { - if (astUtils.isStringLiteral(left) && - astUtils.isStringLiteral(right) && - astUtils.isTokenOnSameLine(left, right) - ) { + // move warning location to operator + var operatorToken = context.getTokenAfter(left); - // move warning location to operator - var operatorToken = context.getTokenAfter(left); + while (operatorToken.value !== "+") { + operatorToken = context.getTokenAfter(operatorToken); + } - while (operatorToken.value !== "+") { - operatorToken = context.getTokenAfter(operatorToken); + context.report( + node, + operatorToken.loc.start, + "Unexpected string concatenation of literals."); } - - context.report( - node, - operatorToken.loc.start, - "Unexpected string concatenation of literals."); } - } - }; + }; + } }; - -module.exports.schema = []; diff --git a/lib/rules/no-useless-constructor.js b/lib/rules/no-useless-constructor.js index a2f1dd82575..b91ab4bcae5 100644 --- a/lib/rules/no-useless-constructor.js +++ b/lib/rules/no-useless-constructor.js @@ -140,33 +140,43 @@ function isRedundantSuperCall(body, ctorParams) { // Rule Definition //------------------------------------------------------------------------------ -module.exports = function(context) { - - /** - * Checks whether a node is a redundant constructor - * @param {ASTNode} node - node to check - * @returns {void} - */ - function checkForConstructor(node) { - if (node.kind !== "constructor") { - return; +module.exports = { + meta: { + docs: { + description: "disallow unnecessary constructors", + category: "ECMAScript 6", + recommended: false + }, + + schema: [] + }, + + create: function(context) { + + /** + * Checks whether a node is a redundant constructor + * @param {ASTNode} node - node to check + * @returns {void} + */ + function checkForConstructor(node) { + if (node.kind !== "constructor") { + return; + } + + var body = node.value.body.body; + var ctorParams = node.value.params; + var superClass = node.parent.parent.superClass; + + if (superClass ? isRedundantSuperCall(body, ctorParams) : (body.length === 0)) { + context.report({ + node: node, + message: "Useless constructor." + }); + } } - var body = node.value.body.body; - var ctorParams = node.value.params; - var superClass = node.parent.parent.superClass; - - if (superClass ? isRedundantSuperCall(body, ctorParams) : (body.length === 0)) { - context.report({ - node: node, - message: "Useless constructor." - }); - } + return { + MethodDefinition: checkForConstructor + }; } - - return { - MethodDefinition: checkForConstructor - }; }; - -module.exports.schema = []; diff --git a/lib/rules/no-useless-escape.js b/lib/rules/no-useless-escape.js index 7a4a26287f2..036831fc394 100644 --- a/lib/rules/no-useless-escape.js +++ b/lib/rules/no-useless-escape.js @@ -57,64 +57,74 @@ var VALID_REGEX_ESCAPES = [ "u" ]; -module.exports = function(context) { +module.exports = { + meta: { + docs: { + description: "disallow unnecessary escape characters", + category: "Best Practices", + recommended: false + }, - /** - * Checks if the escape character in given slice is unnecessary. - * - * @private - * @param {string[]} escapes - list of valid escapes - * @param {ASTNode} node - node to validate. - * @param {string} elm - string slice to validate. - * @returns {void} - */ - function validate(escapes, node, elm) { - var escapeNotFound = escapes.indexOf(elm[0][1]) === -1; - var isQuoteEscape = elm[0][1] === node.raw[0]; + schema: [] + }, - if (escapeNotFound && !isQuoteEscape) { - context.report({ - node: node, - loc: { - line: node.loc.start.line, - column: node.loc.start.column + elm.index - }, - message: "Unnecessary escape character: " + elm[0] - }); + create: function(context) { + + /** + * Checks if the escape character in given slice is unnecessary. + * + * @private + * @param {string[]} escapes - list of valid escapes + * @param {ASTNode} node - node to validate. + * @param {string} elm - string slice to validate. + * @returns {void} + */ + function validate(escapes, node, elm) { + var escapeNotFound = escapes.indexOf(elm[0][1]) === -1; + var isQuoteEscape = elm[0][1] === node.raw[0]; + + if (escapeNotFound && !isQuoteEscape) { + context.report({ + node: node, + loc: { + line: node.loc.start.line, + column: node.loc.start.column + elm.index + }, + message: "Unnecessary escape character: " + elm[0] + }); + } } - } - /** - * Checks if a node has an escape. - * - * @param {ASTNode} node - node to check. - * @returns {void} - */ - function check(node) { - var nodeEscapes, match; - var pattern = /\\[^\d]/g; + /** + * Checks if a node has an escape. + * + * @param {ASTNode} node - node to check. + * @returns {void} + */ + function check(node) { + var nodeEscapes, match; + var pattern = /\\[^\d]/g; + + if (typeof node.value === "string") { - if (typeof node.value === "string") { + // JSXAttribute doesn't have any escape sequence: https://facebook.github.io/jsx/ + if (node.parent.type === "JSXAttribute") { + return; + } - // JSXAttribute doesn't have any escape sequence: https://facebook.github.io/jsx/ - if (node.parent.type === "JSXAttribute") { + nodeEscapes = VALID_STRING_ESCAPES; + } else if (node.regex) { + nodeEscapes = VALID_REGEX_ESCAPES; + } else { return; } - nodeEscapes = VALID_STRING_ESCAPES; - } else if (node.regex) { - nodeEscapes = VALID_REGEX_ESCAPES; - } else { - return; - } - - while ((match = pattern.exec(node.raw))) { - validate(nodeEscapes, node, match); + while ((match = pattern.exec(node.raw))) { + validate(nodeEscapes, node, match); + } } + return { + Literal: check + }; } - return { - Literal: check - }; }; - -module.exports.schema = []; diff --git a/lib/rules/no-var.js b/lib/rules/no-var.js index caa11351b55..2e9948a330e 100644 --- a/lib/rules/no-var.js +++ b/lib/rules/no-var.js @@ -9,17 +9,27 @@ // Rule Definition //------------------------------------------------------------------------------ -module.exports = function(context) { +module.exports = { + meta: { + docs: { + description: "require `let` or `const` instead of `var`", + category: "ECMAScript 6", + recommended: false + }, - return { - VariableDeclaration: function(node) { - if (node.kind === "var") { - context.report(node, "Unexpected var, use let or const instead."); + schema: [] + }, + + create: function(context) { + + return { + VariableDeclaration: function(node) { + if (node.kind === "var") { + context.report(node, "Unexpected var, use let or const instead."); + } } - } - }; + }; + } }; - -module.exports.schema = []; diff --git a/lib/rules/no-void.js b/lib/rules/no-void.js index 9f872fa4cc1..4adae20b37b 100644 --- a/lib/rules/no-void.js +++ b/lib/rules/no-void.js @@ -8,20 +8,30 @@ // Rule Definition //------------------------------------------------------------------------------ -module.exports = function(context) { +module.exports = { + meta: { + docs: { + description: "disallow `void` operators", + category: "Best Practices", + recommended: false + }, - //-------------------------------------------------------------------------- - // Public - //-------------------------------------------------------------------------- + schema: [] + }, - return { - UnaryExpression: function(node) { - if (node.operator === "void") { - context.report(node, "Expected 'undefined' and instead saw 'void'."); + create: function(context) { + + //-------------------------------------------------------------------------- + // Public + //-------------------------------------------------------------------------- + + return { + UnaryExpression: function(node) { + if (node.operator === "void") { + context.report(node, "Expected 'undefined' and instead saw 'void'."); + } } - } - }; + }; + } }; - -module.exports.schema = []; diff --git a/lib/rules/no-warning-comments.js b/lib/rules/no-warning-comments.js index 5d4859fcc26..dadf231f5c3 100644 --- a/lib/rules/no-warning-comments.js +++ b/lib/rules/no-warning-comments.js @@ -11,110 +11,120 @@ var astUtils = require("../ast-utils"); // Rule Definition //------------------------------------------------------------------------------ -module.exports = function(context) { - - var configuration = context.options[0] || {}, - warningTerms = configuration.terms || ["todo", "fixme", "xxx"], - location = configuration.location || "start", - selfConfigRegEx = /\bno-warning-comments\b/, - warningRegExps; - - /** - * Convert a warning term into a RegExp which will match a comment containing that whole word in the specified - * location ("start" or "anywhere"). If the term starts or ends with non word characters, then the match will not - * require word boundaries on that side. - * - * @param {String} term A term to convert to a RegExp - * @returns {RegExp} The term converted to a RegExp - */ - function convertToRegExp(term) { - var escaped = term.replace(/[-\/\\$\^*+?.()|\[\]{}]/g, "\\$&"), - suffix, - prefix; - - /* - * If the term ends in a word character (a-z0-9_), ensure a word - * boundary at the end, so that substrings do not get falsely - * matched. eg "todo" in a string such as "mastodon". - * If the term ends in a non-word character, then \b won't match on - * the boundary to the next non-word character, which would likely - * be a space. For example `/\bFIX!\b/.test('FIX! blah') === false`. - * In these cases, use no bounding match. Same applies for the - * prefix, handled below. - */ - suffix = /\w$/.test(term) ? "\\b" : ""; +module.exports = { + meta: { + docs: { + description: "disallow specified warning terms in comments", + category: "Best Practices", + recommended: false + }, - if (location === "start") { + schema: [ + { + type: "object", + properties: { + terms: { + type: "array", + items: { + type: "string" + } + }, + location: { + enum: ["start", "anywhere"] + } + }, + additionalProperties: false + } + ] + }, + + create: function(context) { + + var configuration = context.options[0] || {}, + warningTerms = configuration.terms || ["todo", "fixme", "xxx"], + location = configuration.location || "start", + selfConfigRegEx = /\bno-warning-comments\b/, + warningRegExps; + + /** + * Convert a warning term into a RegExp which will match a comment containing that whole word in the specified + * location ("start" or "anywhere"). If the term starts or ends with non word characters, then the match will not + * require word boundaries on that side. + * + * @param {String} term A term to convert to a RegExp + * @returns {RegExp} The term converted to a RegExp + */ + function convertToRegExp(term) { + var escaped = term.replace(/[-\/\\$\^*+?.()|\[\]{}]/g, "\\$&"), + suffix, + prefix; /* - * When matching at the start, ignore leading whitespace, and - * there's no need to worry about word boundaries. + * If the term ends in a word character (a-z0-9_), ensure a word + * boundary at the end, so that substrings do not get falsely + * matched. eg "todo" in a string such as "mastodon". + * If the term ends in a non-word character, then \b won't match on + * the boundary to the next non-word character, which would likely + * be a space. For example `/\bFIX!\b/.test('FIX! blah') === false`. + * In these cases, use no bounding match. Same applies for the + * prefix, handled below. */ - prefix = "^\\s*"; - } else if (/^\w/.test(term)) { - prefix = "\\b"; - } else { - prefix = ""; - } + suffix = /\w$/.test(term) ? "\\b" : ""; + + if (location === "start") { + + /* + * When matching at the start, ignore leading whitespace, and + * there's no need to worry about word boundaries. + */ + prefix = "^\\s*"; + } else if (/^\w/.test(term)) { + prefix = "\\b"; + } else { + prefix = ""; + } - return new RegExp(prefix + escaped + suffix, "i"); - } + return new RegExp(prefix + escaped + suffix, "i"); + } - /** - * Checks the specified comment for matches of the configured warning terms and returns the matches. - * @param {String} comment The comment which is checked. - * @returns {Array} All matched warning terms for this comment. - */ - function commentContainsWarningTerm(comment) { - var matches = []; - - warningRegExps.forEach(function(regex, index) { - if (regex.test(comment)) { - matches.push(warningTerms[index]); - } - }); + /** + * Checks the specified comment for matches of the configured warning terms and returns the matches. + * @param {String} comment The comment which is checked. + * @returns {Array} All matched warning terms for this comment. + */ + function commentContainsWarningTerm(comment) { + var matches = []; - return matches; - } + warningRegExps.forEach(function(regex, index) { + if (regex.test(comment)) { + matches.push(warningTerms[index]); + } + }); - /** - * Checks the specified node for matching warning comments and reports them. - * @param {ASTNode} node The AST node being checked. - * @returns {void} undefined. - */ - function checkComment(node) { - if (astUtils.isDirectiveComment(node) && selfConfigRegEx.test(node.value)) { - return; + return matches; } - var matches = commentContainsWarningTerm(node.value); + /** + * Checks the specified node for matching warning comments and reports them. + * @param {ASTNode} node The AST node being checked. + * @returns {void} undefined. + */ + function checkComment(node) { + if (astUtils.isDirectiveComment(node) && selfConfigRegEx.test(node.value)) { + return; + } - matches.forEach(function(matchedTerm) { - context.report(node, "Unexpected '" + matchedTerm + "' comment."); - }); - } + var matches = commentContainsWarningTerm(node.value); - warningRegExps = warningTerms.map(convertToRegExp); - return { - BlockComment: checkComment, - LineComment: checkComment - }; -}; + matches.forEach(function(matchedTerm) { + context.report(node, "Unexpected '" + matchedTerm + "' comment."); + }); + } -module.exports.schema = [ - { - type: "object", - properties: { - terms: { - type: "array", - items: { - type: "string" - } - }, - location: { - enum: ["start", "anywhere"] - } - }, - additionalProperties: false + warningRegExps = warningTerms.map(convertToRegExp); + return { + BlockComment: checkComment, + LineComment: checkComment + }; } -]; +}; diff --git a/lib/rules/no-whitespace-before-property.js b/lib/rules/no-whitespace-before-property.js index bc0f2d84624..347c63d934f 100644 --- a/lib/rules/no-whitespace-before-property.js +++ b/lib/rules/no-whitespace-before-property.js @@ -10,77 +10,88 @@ var astUtils = require("../ast-utils"); // Rule Definition //------------------------------------------------------------------------------ -module.exports = function(context) { - var sourceCode = context.getSourceCode(); +module.exports = { + meta: { + docs: { + description: "disallow whitespace before properties", + category: "Stylistic Issues", + recommended: false + }, - //-------------------------------------------------------------------------- - // Helpers - //-------------------------------------------------------------------------- + fixable: "whitespace", + schema: [] + }, - /** - * Finds opening bracket token of node's computed property - * @param {ASTNode} node - the node to check - * @returns {Token} opening bracket token of node's computed property - * @private - */ - function findOpeningBracket(node) { - var token = sourceCode.getTokenBefore(node.property); + create: function(context) { + var sourceCode = context.getSourceCode(); - while (token.value !== "[") { - token = sourceCode.getTokenBefore(token); - } - return token; - } + //-------------------------------------------------------------------------- + // Helpers + //-------------------------------------------------------------------------- - /** - * Reports whitespace before property token - * @param {ASTNode} node - the node to report in the event of an error - * @param {Token} leftToken - the left token - * @param {Token} rightToken - the right token - * @returns {void} - * @private - */ - function reportError(node, leftToken, rightToken) { - var replacementText = node.computed ? "" : "."; + /** + * Finds opening bracket token of node's computed property + * @param {ASTNode} node - the node to check + * @returns {Token} opening bracket token of node's computed property + * @private + */ + function findOpeningBracket(node) { + var token = sourceCode.getTokenBefore(node.property); - context.report({ - node: node, - message: "Unexpected whitespace before property {{propName}}.", - data: { - propName: sourceCode.getText(node.property) - }, - fix: function(fixer) { - return fixer.replaceTextRange([leftToken.range[1], rightToken.range[0]], replacementText); + while (token.value !== "[") { + token = sourceCode.getTokenBefore(token); } - }); - } + return token; + } - //-------------------------------------------------------------------------- - // Public - //-------------------------------------------------------------------------- + /** + * Reports whitespace before property token + * @param {ASTNode} node - the node to report in the event of an error + * @param {Token} leftToken - the left token + * @param {Token} rightToken - the right token + * @returns {void} + * @private + */ + function reportError(node, leftToken, rightToken) { + var replacementText = node.computed ? "" : "."; - return { - MemberExpression: function(node) { - var rightToken; - var leftToken; + context.report({ + node: node, + message: "Unexpected whitespace before property {{propName}}.", + data: { + propName: sourceCode.getText(node.property) + }, + fix: function(fixer) { + return fixer.replaceTextRange([leftToken.range[1], rightToken.range[0]], replacementText); + } + }); + } - if (!astUtils.isTokenOnSameLine(node.object, node.property)) { - return; - } + //-------------------------------------------------------------------------- + // Public + //-------------------------------------------------------------------------- - if (node.computed) { - rightToken = findOpeningBracket(node); - leftToken = sourceCode.getTokenBefore(rightToken); - } else { - rightToken = sourceCode.getFirstToken(node.property); - leftToken = sourceCode.getTokenBefore(rightToken, 1); - } + return { + MemberExpression: function(node) { + var rightToken; + var leftToken; + + if (!astUtils.isTokenOnSameLine(node.object, node.property)) { + return; + } + + if (node.computed) { + rightToken = findOpeningBracket(node); + leftToken = sourceCode.getTokenBefore(rightToken); + } else { + rightToken = sourceCode.getFirstToken(node.property); + leftToken = sourceCode.getTokenBefore(rightToken, 1); + } - if (sourceCode.isSpaceBetweenTokens(leftToken, rightToken)) { - reportError(node, leftToken, rightToken); + if (sourceCode.isSpaceBetweenTokens(leftToken, rightToken)) { + reportError(node, leftToken, rightToken); + } } - } - }; + }; + } }; - -module.exports.schema = []; diff --git a/lib/rules/no-with.js b/lib/rules/no-with.js index 894f2278f1f..df7528fdfa8 100644 --- a/lib/rules/no-with.js +++ b/lib/rules/no-with.js @@ -9,14 +9,24 @@ // Rule Definition //------------------------------------------------------------------------------ -module.exports = function(context) { +module.exports = { + meta: { + docs: { + description: "disallow `with` statements", + category: "Best Practices", + recommended: false + }, - return { - WithStatement: function(node) { - context.report(node, "Unexpected use of 'with' statement."); - } - }; + schema: [] + }, -}; + create: function(context) { + + return { + WithStatement: function(node) { + context.report(node, "Unexpected use of 'with' statement."); + } + }; -module.exports.schema = []; + } +}; diff --git a/lib/rules/object-curly-spacing.js b/lib/rules/object-curly-spacing.js index bd14372d4c7..11224bbdd13 100644 --- a/lib/rules/object-curly-spacing.js +++ b/lib/rules/object-curly-spacing.js @@ -10,265 +10,277 @@ var astUtils = require("../ast-utils"); // Rule Definition //------------------------------------------------------------------------------ -module.exports = function(context) { - var spaced = context.options[0] === "always", - sourceCode = context.getSourceCode(); - - /** - * Determines whether an option is set, relative to the spacing option. - * If spaced is "always", then check whether option is set to false. - * If spaced is "never", then check whether option is set to true. - * @param {Object} option - The option to exclude. - * @returns {boolean} Whether or not the property is excluded. - */ - function isOptionSet(option) { - return context.options[1] ? context.options[1][option] === !spaced : false; - } +module.exports = { + meta: { + docs: { + description: "enforce consistent spacing inside braces", + category: "Stylistic Issues", + recommended: false + }, - var options = { - spaced: spaced, - arraysInObjectsException: isOptionSet("arraysInObjects"), - objectsInObjectsException: isOptionSet("objectsInObjects") - }; - - //-------------------------------------------------------------------------- - // Helpers - //-------------------------------------------------------------------------- - - /** - * Reports that there shouldn't be a space after the first token - * @param {ASTNode} node - The node to report in the event of an error. - * @param {Token} token - The token to use for the report. - * @returns {void} - */ - function reportNoBeginningSpace(node, token) { - context.report({ - node: node, - loc: token.loc.start, - message: "There should be no space after '" + token.value + "'", - fix: function(fixer) { - var nextToken = context.getSourceCode().getTokenAfter(token); - - return fixer.removeRange([token.range[1], nextToken.range[0]]); - } - }); - } + fixable: "whitespace", - /** - * Reports that there shouldn't be a space before the last token - * @param {ASTNode} node - The node to report in the event of an error. - * @param {Token} token - The token to use for the report. - * @returns {void} - */ - function reportNoEndingSpace(node, token) { - context.report({ - node: node, - loc: token.loc.start, - message: "There should be no space before '" + token.value + "'", - fix: function(fixer) { - var previousToken = context.getSourceCode().getTokenBefore(token); - - return fixer.removeRange([previousToken.range[1], token.range[0]]); - } - }); - } - - /** - * Reports that there should be a space after the first token - * @param {ASTNode} node - The node to report in the event of an error. - * @param {Token} token - The token to use for the report. - * @returns {void} - */ - function reportRequiredBeginningSpace(node, token) { - context.report({ - node: node, - loc: token.loc.start, - message: "A space is required after '" + token.value + "'", - fix: function(fixer) { - return fixer.insertTextAfter(token, " "); + schema: [ + { + enum: ["always", "never"] + }, + { + type: "object", + properties: { + arraysInObjects: { + type: "boolean" + }, + objectsInObjects: { + type: "boolean" + } + }, + additionalProperties: false } - }); - } + ] + }, - /** - * Reports that there should be a space before the last token - * @param {ASTNode} node - The node to report in the event of an error. - * @param {Token} token - The token to use for the report. - * @returns {void} - */ - function reportRequiredEndingSpace(node, token) { - context.report({ - node: node, - loc: token.loc.start, - message: "A space is required before '" + token.value + "'", - fix: function(fixer) { - return fixer.insertTextBefore(token, " "); - } - }); - } + create: function(context) { + var spaced = context.options[0] === "always", + sourceCode = context.getSourceCode(); + + /** + * Determines whether an option is set, relative to the spacing option. + * If spaced is "always", then check whether option is set to false. + * If spaced is "never", then check whether option is set to true. + * @param {Object} option - The option to exclude. + * @returns {boolean} Whether or not the property is excluded. + */ + function isOptionSet(option) { + return context.options[1] ? context.options[1][option] === !spaced : false; + } - /** - * Determines if spacing in curly braces is valid. - * @param {ASTNode} node The AST node to check. - * @param {Token} first The first token to check (should be the opening brace) - * @param {Token} second The second token to check (should be first after the opening brace) - * @param {Token} penultimate The penultimate token to check (should be last before closing brace) - * @param {Token} last The last token to check (should be closing brace) - * @returns {void} - */ - function validateBraceSpacing(node, first, second, penultimate, last) { - var shouldCheckPenultimate, - penultimateType, - closingCurlyBraceMustBeSpaced, - firstSpaced, - lastSpaced; - - if (astUtils.isTokenOnSameLine(first, second)) { - firstSpaced = sourceCode.isSpaceBetweenTokens(first, second); - if (options.spaced && !firstSpaced) { - reportRequiredBeginningSpace(node, first); - } - if (!options.spaced && firstSpaced) { - reportNoBeginningSpace(node, first); - } + var options = { + spaced: spaced, + arraysInObjectsException: isOptionSet("arraysInObjects"), + objectsInObjectsException: isOptionSet("objectsInObjects") + }; + + //-------------------------------------------------------------------------- + // Helpers + //-------------------------------------------------------------------------- + + /** + * Reports that there shouldn't be a space after the first token + * @param {ASTNode} node - The node to report in the event of an error. + * @param {Token} token - The token to use for the report. + * @returns {void} + */ + function reportNoBeginningSpace(node, token) { + context.report({ + node: node, + loc: token.loc.start, + message: "There should be no space after '" + token.value + "'", + fix: function(fixer) { + var nextToken = context.getSourceCode().getTokenAfter(token); + + return fixer.removeRange([token.range[1], nextToken.range[0]]); + } + }); } - if (astUtils.isTokenOnSameLine(penultimate, last)) { - shouldCheckPenultimate = ( - options.arraysInObjectsException && penultimate.value === "]" || - options.objectsInObjectsException && penultimate.value === "}" - ); - penultimateType = shouldCheckPenultimate && sourceCode.getNodeByRangeIndex(penultimate.start).type; + /** + * Reports that there shouldn't be a space before the last token + * @param {ASTNode} node - The node to report in the event of an error. + * @param {Token} token - The token to use for the report. + * @returns {void} + */ + function reportNoEndingSpace(node, token) { + context.report({ + node: node, + loc: token.loc.start, + message: "There should be no space before '" + token.value + "'", + fix: function(fixer) { + var previousToken = context.getSourceCode().getTokenBefore(token); + + return fixer.removeRange([previousToken.range[1], token.range[0]]); + } + }); + } - closingCurlyBraceMustBeSpaced = ( - options.arraysInObjectsException && penultimateType === "ArrayExpression" || - options.objectsInObjectsException && penultimateType === "ObjectExpression" - ) ? !options.spaced : options.spaced; + /** + * Reports that there should be a space after the first token + * @param {ASTNode} node - The node to report in the event of an error. + * @param {Token} token - The token to use for the report. + * @returns {void} + */ + function reportRequiredBeginningSpace(node, token) { + context.report({ + node: node, + loc: token.loc.start, + message: "A space is required after '" + token.value + "'", + fix: function(fixer) { + return fixer.insertTextAfter(token, " "); + } + }); + } - lastSpaced = sourceCode.isSpaceBetweenTokens(penultimate, last); + /** + * Reports that there should be a space before the last token + * @param {ASTNode} node - The node to report in the event of an error. + * @param {Token} token - The token to use for the report. + * @returns {void} + */ + function reportRequiredEndingSpace(node, token) { + context.report({ + node: node, + loc: token.loc.start, + message: "A space is required before '" + token.value + "'", + fix: function(fixer) { + return fixer.insertTextBefore(token, " "); + } + }); + } - if (closingCurlyBraceMustBeSpaced && !lastSpaced) { - reportRequiredEndingSpace(node, last); - } - if (!closingCurlyBraceMustBeSpaced && lastSpaced) { - reportNoEndingSpace(node, last); + /** + * Determines if spacing in curly braces is valid. + * @param {ASTNode} node The AST node to check. + * @param {Token} first The first token to check (should be the opening brace) + * @param {Token} second The second token to check (should be first after the opening brace) + * @param {Token} penultimate The penultimate token to check (should be last before closing brace) + * @param {Token} last The last token to check (should be closing brace) + * @returns {void} + */ + function validateBraceSpacing(node, first, second, penultimate, last) { + var shouldCheckPenultimate, + penultimateType, + closingCurlyBraceMustBeSpaced, + firstSpaced, + lastSpaced; + + if (astUtils.isTokenOnSameLine(first, second)) { + firstSpaced = sourceCode.isSpaceBetweenTokens(first, second); + if (options.spaced && !firstSpaced) { + reportRequiredBeginningSpace(node, first); + } + if (!options.spaced && firstSpaced) { + reportNoBeginningSpace(node, first); + } } - } - } - /** - * Reports a given object node if spacing in curly braces is invalid. - * @param {ASTNode} node - An ObjectExpression or ObjectPattern node to check. - * @returns {void} - */ - function checkForObject(node) { - if (node.properties.length === 0) { - return; + if (astUtils.isTokenOnSameLine(penultimate, last)) { + shouldCheckPenultimate = ( + options.arraysInObjectsException && penultimate.value === "]" || + options.objectsInObjectsException && penultimate.value === "}" + ); + penultimateType = shouldCheckPenultimate && sourceCode.getNodeByRangeIndex(penultimate.start).type; + + closingCurlyBraceMustBeSpaced = ( + options.arraysInObjectsException && penultimateType === "ArrayExpression" || + options.objectsInObjectsException && penultimateType === "ObjectExpression" + ) ? !options.spaced : options.spaced; + + lastSpaced = sourceCode.isSpaceBetweenTokens(penultimate, last); + + if (closingCurlyBraceMustBeSpaced && !lastSpaced) { + reportRequiredEndingSpace(node, last); + } + if (!closingCurlyBraceMustBeSpaced && lastSpaced) { + reportNoEndingSpace(node, last); + } + } } - var first = sourceCode.getFirstToken(node), - last = sourceCode.getLastToken(node), - second = sourceCode.getTokenAfter(first), - penultimate = sourceCode.getTokenBefore(last); + /** + * Reports a given object node if spacing in curly braces is invalid. + * @param {ASTNode} node - An ObjectExpression or ObjectPattern node to check. + * @returns {void} + */ + function checkForObject(node) { + if (node.properties.length === 0) { + return; + } - validateBraceSpacing(node, first, second, penultimate, last); - } + var first = sourceCode.getFirstToken(node), + last = sourceCode.getLastToken(node), + second = sourceCode.getTokenAfter(first), + penultimate = sourceCode.getTokenBefore(last); - /** - * Reports a given import node if spacing in curly braces is invalid. - * @param {ASTNode} node - An ImportDeclaration node to check. - * @returns {void} - */ - function checkForImport(node) { - if (node.specifiers.length === 0) { - return; + validateBraceSpacing(node, first, second, penultimate, last); } - var firstSpecifier = node.specifiers[0], - lastSpecifier = node.specifiers[node.specifiers.length - 1]; + /** + * Reports a given import node if spacing in curly braces is invalid. + * @param {ASTNode} node - An ImportDeclaration node to check. + * @returns {void} + */ + function checkForImport(node) { + if (node.specifiers.length === 0) { + return; + } - if (lastSpecifier.type !== "ImportSpecifier") { - return; - } - if (firstSpecifier.type !== "ImportSpecifier") { - firstSpecifier = node.specifiers[1]; - } + var firstSpecifier = node.specifiers[0], + lastSpecifier = node.specifiers[node.specifiers.length - 1]; - var first = sourceCode.getTokenBefore(firstSpecifier), - last = sourceCode.getTokenAfter(lastSpecifier); + if (lastSpecifier.type !== "ImportSpecifier") { + return; + } + if (firstSpecifier.type !== "ImportSpecifier") { + firstSpecifier = node.specifiers[1]; + } - // to support a trailing comma. - if (last.value === ",") { - last = sourceCode.getTokenAfter(last); - } + var first = sourceCode.getTokenBefore(firstSpecifier), + last = sourceCode.getTokenAfter(lastSpecifier); - var second = sourceCode.getTokenAfter(first), - penultimate = sourceCode.getTokenBefore(last); + // to support a trailing comma. + if (last.value === ",") { + last = sourceCode.getTokenAfter(last); + } - validateBraceSpacing(node, first, second, penultimate, last); - } + var second = sourceCode.getTokenAfter(first), + penultimate = sourceCode.getTokenBefore(last); - /** - * Reports a given export node if spacing in curly braces is invalid. - * @param {ASTNode} node - An ExportNamedDeclaration node to check. - * @returns {void} - */ - function checkForExport(node) { - if (node.specifiers.length === 0) { - return; + validateBraceSpacing(node, first, second, penultimate, last); } - var firstSpecifier = node.specifiers[0], - lastSpecifier = node.specifiers[node.specifiers.length - 1], - first = sourceCode.getTokenBefore(firstSpecifier), - last = sourceCode.getTokenAfter(lastSpecifier); + /** + * Reports a given export node if spacing in curly braces is invalid. + * @param {ASTNode} node - An ExportNamedDeclaration node to check. + * @returns {void} + */ + function checkForExport(node) { + if (node.specifiers.length === 0) { + return; + } - // to support a trailing comma. - if (last.value === ",") { - last = sourceCode.getTokenAfter(last); - } + var firstSpecifier = node.specifiers[0], + lastSpecifier = node.specifiers[node.specifiers.length - 1], + first = sourceCode.getTokenBefore(firstSpecifier), + last = sourceCode.getTokenAfter(lastSpecifier); - var second = sourceCode.getTokenAfter(first), - penultimate = sourceCode.getTokenBefore(last); + // to support a trailing comma. + if (last.value === ",") { + last = sourceCode.getTokenAfter(last); + } - validateBraceSpacing(node, first, second, penultimate, last); - } + var second = sourceCode.getTokenAfter(first), + penultimate = sourceCode.getTokenBefore(last); - //-------------------------------------------------------------------------- - // Public - //-------------------------------------------------------------------------- + validateBraceSpacing(node, first, second, penultimate, last); + } - return { + //-------------------------------------------------------------------------- + // Public + //-------------------------------------------------------------------------- - // var {x} = y; - ObjectPattern: checkForObject, + return { - // var y = {x: 'y'} - ObjectExpression: checkForObject, + // var {x} = y; + ObjectPattern: checkForObject, - // import {y} from 'x'; - ImportDeclaration: checkForImport, + // var y = {x: 'y'} + ObjectExpression: checkForObject, - // export {name} from 'yo'; - ExportNamedDeclaration: checkForExport - }; + // import {y} from 'x'; + ImportDeclaration: checkForImport, -}; + // export {name} from 'yo'; + ExportNamedDeclaration: checkForExport + }; -module.exports.schema = [ - { - enum: ["always", "never"] - }, - { - type: "object", - properties: { - arraysInObjects: { - type: "boolean" - }, - objectsInObjects: { - type: "boolean" - } - }, - additionalProperties: false } -]; +}; diff --git a/lib/rules/object-shorthand.js b/lib/rules/object-shorthand.js index d9f5a0ed0d3..bf0778c99db 100644 --- a/lib/rules/object-shorthand.js +++ b/lib/rules/object-shorthand.js @@ -15,112 +15,122 @@ var OPTIONS = { //------------------------------------------------------------------------------ // Rule Definition //------------------------------------------------------------------------------ -module.exports = function(context) { - var APPLY = context.options[0] || OPTIONS.always; - var APPLY_TO_METHODS = APPLY === OPTIONS.methods || APPLY === OPTIONS.always; - var APPLY_TO_PROPS = APPLY === OPTIONS.properties || APPLY === OPTIONS.always; - var APPLY_NEVER = APPLY === OPTIONS.never; - - var PARAMS = context.options[1] || {}; - var IGNORE_CONSTRUCTORS = PARAMS.ignoreConstructors; - - //-------------------------------------------------------------------------- - // Helpers - //-------------------------------------------------------------------------- - - /** - * Determines if the first character of the name is a capital letter. - * @param {string} name The name of the node to evaluate. - * @returns {boolean} True if the first character of the property name is a capital letter, false if not. - * @private - */ - function isConstructor(name) { - var firstChar = name.charAt(0); - - return firstChar === firstChar.toUpperCase(); - } - - //-------------------------------------------------------------------------- - // Public - //-------------------------------------------------------------------------- +module.exports = { + meta: { + docs: { + description: "require or disallow method and property shorthand syntax for object literals", + category: "ECMAScript 6", + recommended: false + }, - return { - Property: function(node) { - var isConciseProperty = node.method || node.shorthand, - type; + schema: { + anyOf: [ + { + type: "array", + items: [ + { + enum: ["always", "methods", "properties", "never"] + } + ], + minItems: 0, + maxItems: 1 + }, + { + type: "array", + items: [ + { + enum: ["always", "methods"] + }, + { + type: "object", + properties: { + ignoreConstructors: { + type: "boolean" + } + }, + additionalProperties: false + } + ], + minItems: 0, + maxItems: 2 + } + ] + } + }, + + create: function(context) { + var APPLY = context.options[0] || OPTIONS.always; + var APPLY_TO_METHODS = APPLY === OPTIONS.methods || APPLY === OPTIONS.always; + var APPLY_TO_PROPS = APPLY === OPTIONS.properties || APPLY === OPTIONS.always; + var APPLY_NEVER = APPLY === OPTIONS.never; + + var PARAMS = context.options[1] || {}; + var IGNORE_CONSTRUCTORS = PARAMS.ignoreConstructors; + + //-------------------------------------------------------------------------- + // Helpers + //-------------------------------------------------------------------------- + + /** + * Determines if the first character of the name is a capital letter. + * @param {string} name The name of the node to evaluate. + * @returns {boolean} True if the first character of the property name is a capital letter, false if not. + * @private + */ + function isConstructor(name) { + var firstChar = name.charAt(0); + + return firstChar === firstChar.toUpperCase(); + } - // Ignore destructuring assignment - if (node.parent.type === "ObjectPattern") { - return; - } + //-------------------------------------------------------------------------- + // Public + //-------------------------------------------------------------------------- - // if we're "never" and concise we should warn now - if (APPLY_NEVER && isConciseProperty) { - type = node.method ? "method" : "property"; - context.report(node, "Expected longform " + type + " syntax."); - } + return { + Property: function(node) { + var isConciseProperty = node.method || node.shorthand, + type; - // at this point if we're concise or if we're "never" we can leave - if (APPLY_NEVER || isConciseProperty) { - return; - } + // Ignore destructuring assignment + if (node.parent.type === "ObjectPattern") { + return; + } - // getters, setters and computed properties are ignored - if (node.kind === "get" || node.kind === "set" || node.computed) { - return; - } + // if we're "never" and concise we should warn now + if (APPLY_NEVER && isConciseProperty) { + type = node.method ? "method" : "property"; + context.report(node, "Expected longform " + type + " syntax."); + } - if (node.value.type === "FunctionExpression" && !node.value.id && APPLY_TO_METHODS) { - if (IGNORE_CONSTRUCTORS && isConstructor(node.key.name)) { + // at this point if we're concise or if we're "never" we can leave + if (APPLY_NEVER || isConciseProperty) { return; } - // {x: function(){}} should be written as {x() {}} - context.report(node, "Expected method shorthand."); - } else if (node.value.type === "Identifier" && node.key.name === node.value.name && APPLY_TO_PROPS) { + // getters, setters and computed properties are ignored + if (node.kind === "get" || node.kind === "set" || node.computed) { + return; + } - // {x: x} should be written as {x} - context.report(node, "Expected property shorthand."); - } else if (node.value.type === "Identifier" && node.key.type === "Literal" && node.key.value === node.value.name && APPLY_TO_PROPS) { + if (node.value.type === "FunctionExpression" && !node.value.id && APPLY_TO_METHODS) { + if (IGNORE_CONSTRUCTORS && isConstructor(node.key.name)) { + return; + } - // {"x": x} should be written as {x} - context.report(node, "Expected property shorthand."); - } - } - }; + // {x: function(){}} should be written as {x() {}} + context.report(node, "Expected method shorthand."); + } else if (node.value.type === "Identifier" && node.key.name === node.value.name && APPLY_TO_PROPS) { -}; + // {x: x} should be written as {x} + context.report(node, "Expected property shorthand."); + } else if (node.value.type === "Identifier" && node.key.type === "Literal" && node.key.value === node.value.name && APPLY_TO_PROPS) { -module.exports.schema = { - anyOf: [ - { - type: "array", - items: [ - { - enum: ["always", "methods", "properties", "never"] + // {"x": x} should be written as {x} + context.report(node, "Expected property shorthand."); } - ], - minItems: 0, - maxItems: 1 - }, - { - type: "array", - items: [ - { - enum: ["always", "methods"] - }, - { - type: "object", - properties: { - ignoreConstructors: { - type: "boolean" - } - }, - additionalProperties: false - } - ], - minItems: 0, - maxItems: 2 - } - ] + } + }; + + } }; diff --git a/lib/rules/one-var-declaration-per-line.js b/lib/rules/one-var-declaration-per-line.js index 7949d48bc5a..f4cdb84b187 100644 --- a/lib/rules/one-var-declaration-per-line.js +++ b/lib/rules/one-var-declaration-per-line.js @@ -8,66 +8,76 @@ // Rule Definition //------------------------------------------------------------------------------ -module.exports = function(context) { +module.exports = { + meta: { + docs: { + description: "require or disallow newlines around `var` declarations", + category: "Stylistic Issues", + recommended: false + }, - var ERROR_MESSAGE = "Expected variable declaration to be on a new line."; - var always = context.options[0] === "always"; + schema: [ + { + enum: ["always", "initializations"] + } + ] + }, - //-------------------------------------------------------------------------- - // Helpers - //-------------------------------------------------------------------------- + create: function(context) { + var ERROR_MESSAGE = "Expected variable declaration to be on a new line."; + var always = context.options[0] === "always"; - /** - * Determine if provided keyword is a variant of for specifiers - * @private - * @param {string} keyword - keyword to test - * @returns {boolean} True if `keyword` is a variant of for specifier - */ - function isForTypeSpecifier(keyword) { - return keyword === "ForStatement" || keyword === "ForInStatement" || keyword === "ForOfStatement"; - } + //-------------------------------------------------------------------------- + // Helpers + //-------------------------------------------------------------------------- - /** - * Checks newlines around variable declarations. - * @private - * @param {ASTNode} node - `VariableDeclaration` node to test - * @returns {void} - */ - function checkForNewLine(node) { - if (isForTypeSpecifier(node.parent.type)) { - return; - } - var declarations = node.declarations; - var prev; + /** + * Determine if provided keyword is a variant of for specifiers + * @private + * @param {string} keyword - keyword to test + * @returns {boolean} True if `keyword` is a variant of for specifier + */ + function isForTypeSpecifier(keyword) { + return keyword === "ForStatement" || keyword === "ForInStatement" || keyword === "ForOfStatement"; + } - declarations.forEach(function(current) { - if (prev && prev.loc.end.line === current.loc.start.line) { - if (always || prev.init || current.init) { - context.report({ - node: node, - message: ERROR_MESSAGE, - loc: current.loc.start - }); - } + /** + * Checks newlines around variable declarations. + * @private + * @param {ASTNode} node - `VariableDeclaration` node to test + * @returns {void} + */ + function checkForNewLine(node) { + if (isForTypeSpecifier(node.parent.type)) { + return; } - prev = current; - }); - } - //-------------------------------------------------------------------------- - // Public - //-------------------------------------------------------------------------- + var declarations = node.declarations; + var prev; - return { - VariableDeclaration: checkForNewLine - }; + declarations.forEach(function(current) { + if (prev && prev.loc.end.line === current.loc.start.line) { + if (always || prev.init || current.init) { + context.report({ + node: node, + message: ERROR_MESSAGE, + loc: current.loc.start + }); + } + } + prev = current; + }); + } -}; + //-------------------------------------------------------------------------- + // Public + //-------------------------------------------------------------------------- + + return { + VariableDeclaration: checkForNewLine + }; -module.exports.schema = [ - { - enum: ["always", "initializations"] } -]; +}; diff --git a/lib/rules/one-var.js b/lib/rules/one-var.js index 783b1de6926..805cec3654d 100644 --- a/lib/rules/one-var.js +++ b/lib/rules/one-var.js @@ -9,311 +9,321 @@ // Rule Definition //------------------------------------------------------------------------------ -module.exports = function(context) { +module.exports = { + meta: { + docs: { + description: "enforce variables to be declared either together or separately in functions", + category: "Stylistic Issues", + recommended: false + }, - var MODE_ALWAYS = "always", - MODE_NEVER = "never"; + schema: [ + { + oneOf: [ + { + enum: ["always", "never"] + }, + { + type: "object", + properties: { + var: { + enum: ["always", "never"] + }, + let: { + enum: ["always", "never"] + }, + const: { + enum: ["always", "never"] + } + }, + additionalProperties: false + }, + { + type: "object", + properties: { + initialized: { + enum: ["always", "never"] + }, + uninitialized: { + enum: ["always", "never"] + } + }, + additionalProperties: false + } + ] + } + ] + }, - var mode = context.options[0] || MODE_ALWAYS; + create: function(context) { - var options = { - }; + var MODE_ALWAYS = "always", + MODE_NEVER = "never"; - if (typeof mode === "string") { // simple options configuration with just a string - options.var = { uninitialized: mode, initialized: mode}; - options.let = { uninitialized: mode, initialized: mode}; - options.const = { uninitialized: mode, initialized: mode}; - } else if (typeof mode === "object") { // options configuration is an object - if (mode.hasOwnProperty("var") && typeof mode.var === "string") { - options.var = { uninitialized: mode.var, initialized: mode.var}; - } - if (mode.hasOwnProperty("let") && typeof mode.let === "string") { - options.let = { uninitialized: mode.let, initialized: mode.let}; - } - if (mode.hasOwnProperty("const") && typeof mode.const === "string") { - options.const = { uninitialized: mode.const, initialized: mode.const}; - } - if (mode.hasOwnProperty("uninitialized")) { - if (!options.var) { - options.var = {}; - } - if (!options.let) { - options.let = {}; + var mode = context.options[0] || MODE_ALWAYS; + + var options = { + }; + + if (typeof mode === "string") { // simple options configuration with just a string + options.var = { uninitialized: mode, initialized: mode}; + options.let = { uninitialized: mode, initialized: mode}; + options.const = { uninitialized: mode, initialized: mode}; + } else if (typeof mode === "object") { // options configuration is an object + if (mode.hasOwnProperty("var") && typeof mode.var === "string") { + options.var = { uninitialized: mode.var, initialized: mode.var}; } - if (!options.const) { - options.const = {}; + if (mode.hasOwnProperty("let") && typeof mode.let === "string") { + options.let = { uninitialized: mode.let, initialized: mode.let}; } - options.var.uninitialized = mode.uninitialized; - options.let.uninitialized = mode.uninitialized; - options.const.uninitialized = mode.uninitialized; - } - if (mode.hasOwnProperty("initialized")) { - if (!options.var) { - options.var = {}; + if (mode.hasOwnProperty("const") && typeof mode.const === "string") { + options.const = { uninitialized: mode.const, initialized: mode.const}; } - if (!options.let) { - options.let = {}; + if (mode.hasOwnProperty("uninitialized")) { + if (!options.var) { + options.var = {}; + } + if (!options.let) { + options.let = {}; + } + if (!options.const) { + options.const = {}; + } + options.var.uninitialized = mode.uninitialized; + options.let.uninitialized = mode.uninitialized; + options.const.uninitialized = mode.uninitialized; } - if (!options.const) { - options.const = {}; + if (mode.hasOwnProperty("initialized")) { + if (!options.var) { + options.var = {}; + } + if (!options.let) { + options.let = {}; + } + if (!options.const) { + options.const = {}; + } + options.var.initialized = mode.initialized; + options.let.initialized = mode.initialized; + options.const.initialized = mode.initialized; } - options.var.initialized = mode.initialized; - options.let.initialized = mode.initialized; - options.const.initialized = mode.initialized; } - } - //-------------------------------------------------------------------------- - // Helpers - //-------------------------------------------------------------------------- - - var functionStack = []; - var blockStack = []; - - /** - * Increments the blockStack counter. - * @returns {void} - * @private - */ - function startBlock() { - blockStack.push({ - let: {initialized: false, uninitialized: false}, - const: {initialized: false, uninitialized: false} - }); - } + //-------------------------------------------------------------------------- + // Helpers + //-------------------------------------------------------------------------- + + var functionStack = []; + var blockStack = []; + + /** + * Increments the blockStack counter. + * @returns {void} + * @private + */ + function startBlock() { + blockStack.push({ + let: {initialized: false, uninitialized: false}, + const: {initialized: false, uninitialized: false} + }); + } - /** - * Increments the functionStack counter. - * @returns {void} - * @private - */ - function startFunction() { - functionStack.push({initialized: false, uninitialized: false}); - startBlock(); - } + /** + * Increments the functionStack counter. + * @returns {void} + * @private + */ + function startFunction() { + functionStack.push({initialized: false, uninitialized: false}); + startBlock(); + } - /** - * Decrements the blockStack counter. - * @returns {void} - * @private - */ - function endBlock() { - blockStack.pop(); - } + /** + * Decrements the blockStack counter. + * @returns {void} + * @private + */ + function endBlock() { + blockStack.pop(); + } - /** - * Decrements the functionStack counter. - * @returns {void} - * @private - */ - function endFunction() { - functionStack.pop(); - endBlock(); - } + /** + * Decrements the functionStack counter. + * @returns {void} + * @private + */ + function endFunction() { + functionStack.pop(); + endBlock(); + } - /** - * Records whether initialized or uninitialized variables are defined in current scope. - * @param {string} statementType node.kind, one of: "var", "let", or "const" - * @param {ASTNode[]} declarations List of declarations - * @param {Object} currentScope The scope being investigated - * @returns {void} - * @private - */ - function recordTypes(statementType, declarations, currentScope) { - for (var i = 0; i < declarations.length; i++) { - if (declarations[i].init === null) { - if (options[statementType] && options[statementType].uninitialized === MODE_ALWAYS) { - currentScope.uninitialized = true; - } - } else { - if (options[statementType] && options[statementType].initialized === MODE_ALWAYS) { - currentScope.initialized = true; + /** + * Records whether initialized or uninitialized variables are defined in current scope. + * @param {string} statementType node.kind, one of: "var", "let", or "const" + * @param {ASTNode[]} declarations List of declarations + * @param {Object} currentScope The scope being investigated + * @returns {void} + * @private + */ + function recordTypes(statementType, declarations, currentScope) { + for (var i = 0; i < declarations.length; i++) { + if (declarations[i].init === null) { + if (options[statementType] && options[statementType].uninitialized === MODE_ALWAYS) { + currentScope.uninitialized = true; + } + } else { + if (options[statementType] && options[statementType].initialized === MODE_ALWAYS) { + currentScope.initialized = true; + } } } } - } - /** - * Determines the current scope (function or block) - * @param {string} statementType node.kind, one of: "var", "let", or "const" - * @returns {Object} The scope associated with statementType - */ - function getCurrentScope(statementType) { - var currentScope; - - if (statementType === "var") { - currentScope = functionStack[functionStack.length - 1]; - } else if (statementType === "let") { - currentScope = blockStack[blockStack.length - 1].let; - } else if (statementType === "const") { - currentScope = blockStack[blockStack.length - 1].const; + /** + * Determines the current scope (function or block) + * @param {string} statementType node.kind, one of: "var", "let", or "const" + * @returns {Object} The scope associated with statementType + */ + function getCurrentScope(statementType) { + var currentScope; + + if (statementType === "var") { + currentScope = functionStack[functionStack.length - 1]; + } else if (statementType === "let") { + currentScope = blockStack[blockStack.length - 1].let; + } else if (statementType === "const") { + currentScope = blockStack[blockStack.length - 1].const; + } + return currentScope; } - return currentScope; - } - /** - * Counts the number of initialized and uninitialized declarations in a list of declarations - * @param {ASTNode[]} declarations List of declarations - * @returns {Object} Counts of 'uninitialized' and 'initialized' declarations - * @private - */ - function countDeclarations(declarations) { - var counts = { uninitialized: 0, initialized: 0 }; - - for (var i = 0; i < declarations.length; i++) { - if (declarations[i].init === null) { - counts.uninitialized++; - } else { - counts.initialized++; + /** + * Counts the number of initialized and uninitialized declarations in a list of declarations + * @param {ASTNode[]} declarations List of declarations + * @returns {Object} Counts of 'uninitialized' and 'initialized' declarations + * @private + */ + function countDeclarations(declarations) { + var counts = { uninitialized: 0, initialized: 0 }; + + for (var i = 0; i < declarations.length; i++) { + if (declarations[i].init === null) { + counts.uninitialized++; + } else { + counts.initialized++; + } } + return counts; } - return counts; - } - /** - * Determines if there is more than one var statement in the current scope. - * @param {string} statementType node.kind, one of: "var", "let", or "const" - * @param {ASTNode[]} declarations List of declarations - * @returns {boolean} Returns true if it is the first var declaration, false if not. - * @private - */ - function hasOnlyOneStatement(statementType, declarations) { - - var declarationCounts = countDeclarations(declarations); - var currentOptions = options[statementType] || {}; - var currentScope = getCurrentScope(statementType); - - if (currentOptions.uninitialized === MODE_ALWAYS && currentOptions.initialized === MODE_ALWAYS) { - if (currentScope.uninitialized || currentScope.initialized) { - return false; + /** + * Determines if there is more than one var statement in the current scope. + * @param {string} statementType node.kind, one of: "var", "let", or "const" + * @param {ASTNode[]} declarations List of declarations + * @returns {boolean} Returns true if it is the first var declaration, false if not. + * @private + */ + function hasOnlyOneStatement(statementType, declarations) { + + var declarationCounts = countDeclarations(declarations); + var currentOptions = options[statementType] || {}; + var currentScope = getCurrentScope(statementType); + + if (currentOptions.uninitialized === MODE_ALWAYS && currentOptions.initialized === MODE_ALWAYS) { + if (currentScope.uninitialized || currentScope.initialized) { + return false; + } } - } - if (declarationCounts.uninitialized > 0) { - if (currentOptions.uninitialized === MODE_ALWAYS && currentScope.uninitialized) { - return false; + if (declarationCounts.uninitialized > 0) { + if (currentOptions.uninitialized === MODE_ALWAYS && currentScope.uninitialized) { + return false; + } } - } - if (declarationCounts.initialized > 0) { - if (currentOptions.initialized === MODE_ALWAYS && currentScope.initialized) { - return false; + if (declarationCounts.initialized > 0) { + if (currentOptions.initialized === MODE_ALWAYS && currentScope.initialized) { + return false; + } } + recordTypes(statementType, declarations, currentScope); + return true; } - recordTypes(statementType, declarations, currentScope); - return true; - } - //-------------------------------------------------------------------------- - // Public API - //-------------------------------------------------------------------------- - - return { - Program: startFunction, - FunctionDeclaration: startFunction, - FunctionExpression: startFunction, - ArrowFunctionExpression: startFunction, - BlockStatement: startBlock, - ForStatement: startBlock, - ForInStatement: startBlock, - ForOfStatement: startBlock, - SwitchStatement: startBlock, - - VariableDeclaration: function(node) { - var parent = node.parent, - type, declarations, declarationCounts; - - type = node.kind; - if (!options[type]) { - return; - } - - declarations = node.declarations; - declarationCounts = countDeclarations(declarations); + //-------------------------------------------------------------------------- + // Public API + //-------------------------------------------------------------------------- + + return { + Program: startFunction, + FunctionDeclaration: startFunction, + FunctionExpression: startFunction, + ArrowFunctionExpression: startFunction, + BlockStatement: startBlock, + ForStatement: startBlock, + ForInStatement: startBlock, + ForOfStatement: startBlock, + SwitchStatement: startBlock, + + VariableDeclaration: function(node) { + var parent = node.parent, + type, declarations, declarationCounts; + + type = node.kind; + if (!options[type]) { + return; + } - // always - if (!hasOnlyOneStatement(type, declarations)) { - if (options[type].initialized === MODE_ALWAYS && options[type].uninitialized === MODE_ALWAYS) { - context.report(node, "Combine this with the previous '" + type + "' statement."); - } else { - if (options[type].initialized === MODE_ALWAYS) { - context.report(node, "Combine this with the previous '" + type + "' statement with initialized variables."); - } - if (options[type].uninitialized === MODE_ALWAYS) { - context.report(node, "Combine this with the previous '" + type + "' statement with uninitialized variables."); + declarations = node.declarations; + declarationCounts = countDeclarations(declarations); + + // always + if (!hasOnlyOneStatement(type, declarations)) { + if (options[type].initialized === MODE_ALWAYS && options[type].uninitialized === MODE_ALWAYS) { + context.report(node, "Combine this with the previous '" + type + "' statement."); + } else { + if (options[type].initialized === MODE_ALWAYS) { + context.report(node, "Combine this with the previous '" + type + "' statement with initialized variables."); + } + if (options[type].uninitialized === MODE_ALWAYS) { + context.report(node, "Combine this with the previous '" + type + "' statement with uninitialized variables."); + } } } - } - // never - if (parent.type !== "ForStatement" || parent.init !== node) { - var totalDeclarations = declarationCounts.uninitialized + declarationCounts.initialized; + // never + if (parent.type !== "ForStatement" || parent.init !== node) { + var totalDeclarations = declarationCounts.uninitialized + declarationCounts.initialized; - if (totalDeclarations > 1) { + if (totalDeclarations > 1) { - if (options[type].initialized === MODE_NEVER && options[type].uninitialized === MODE_NEVER) { + if (options[type].initialized === MODE_NEVER && options[type].uninitialized === MODE_NEVER) { - // both initialized and uninitialized - context.report(node, "Split '" + type + "' declarations into multiple statements."); - } else if (options[type].initialized === MODE_NEVER && declarationCounts.initialized > 0) { + // both initialized and uninitialized + context.report(node, "Split '" + type + "' declarations into multiple statements."); + } else if (options[type].initialized === MODE_NEVER && declarationCounts.initialized > 0) { - // initialized - context.report(node, "Split initialized '" + type + "' declarations into multiple statements."); - } else if (options[type].uninitialized === MODE_NEVER && declarationCounts.uninitialized > 0) { + // initialized + context.report(node, "Split initialized '" + type + "' declarations into multiple statements."); + } else if (options[type].uninitialized === MODE_NEVER && declarationCounts.uninitialized > 0) { - // uninitialized - context.report(node, "Split uninitialized '" + type + "' declarations into multiple statements."); + // uninitialized + context.report(node, "Split uninitialized '" + type + "' declarations into multiple statements."); + } } } - } - }, - - "ForStatement:exit": endBlock, - "ForOfStatement:exit": endBlock, - "ForInStatement:exit": endBlock, - "SwitchStatement:exit": endBlock, - "BlockStatement:exit": endBlock, - "Program:exit": endFunction, - "FunctionDeclaration:exit": endFunction, - "FunctionExpression:exit": endFunction, - "ArrowFunctionExpression:exit": endFunction - }; + }, -}; + "ForStatement:exit": endBlock, + "ForOfStatement:exit": endBlock, + "ForInStatement:exit": endBlock, + "SwitchStatement:exit": endBlock, + "BlockStatement:exit": endBlock, + "Program:exit": endFunction, + "FunctionDeclaration:exit": endFunction, + "FunctionExpression:exit": endFunction, + "ArrowFunctionExpression:exit": endFunction + }; -module.exports.schema = [ - { - oneOf: [ - { - enum: ["always", "never"] - }, - { - type: "object", - properties: { - var: { - enum: ["always", "never"] - }, - let: { - enum: ["always", "never"] - }, - const: { - enum: ["always", "never"] - } - }, - additionalProperties: false - }, - { - type: "object", - properties: { - initialized: { - enum: ["always", "never"] - }, - uninitialized: { - enum: ["always", "never"] - } - }, - additionalProperties: false - } - ] } -]; +}; diff --git a/lib/rules/operator-assignment.js b/lib/rules/operator-assignment.js index 823de602c18..7e60c9efd40 100644 --- a/lib/rules/operator-assignment.js +++ b/lib/rules/operator-assignment.js @@ -70,54 +70,64 @@ function same(a, b) { } } -module.exports = function(context) { - - /** - * Ensures that an assignment uses the shorthand form where possible. - * @param {ASTNode} node An AssignmentExpression node. - * @returns {void} - */ - function verify(node) { - var expr, left, operator; - - if (node.operator !== "=" || node.right.type !== "BinaryExpression") { - return; - } +module.exports = { + meta: { + docs: { + description: "require or disallow assignment operator shorthand where possible", + category: "Stylistic Issues", + recommended: false + }, + + schema: [ + { + enum: ["always", "never"] + } + ] + }, + + create: function(context) { - left = node.left; - expr = node.right; - operator = expr.operator; + /** + * Ensures that an assignment uses the shorthand form where possible. + * @param {ASTNode} node An AssignmentExpression node. + * @returns {void} + */ + function verify(node) { + var expr, left, operator; - if (isCommutativeOperatorWithShorthand(operator)) { - if (same(left, expr.left) || same(left, expr.right)) { - context.report(node, "Assignment can be replaced with operator assignment."); + if (node.operator !== "=" || node.right.type !== "BinaryExpression") { + return; } - } else if (isNonCommutativeOperatorWithShorthand(operator)) { - if (same(left, expr.left)) { - context.report(node, "Assignment can be replaced with operator assignment."); + + left = node.left; + expr = node.right; + operator = expr.operator; + + if (isCommutativeOperatorWithShorthand(operator)) { + if (same(left, expr.left) || same(left, expr.right)) { + context.report(node, "Assignment can be replaced with operator assignment."); + } + } else if (isNonCommutativeOperatorWithShorthand(operator)) { + if (same(left, expr.left)) { + context.report(node, "Assignment can be replaced with operator assignment."); + } } } - } - /** - * Warns if an assignment expression uses operator assignment shorthand. - * @param {ASTNode} node An AssignmentExpression node. - * @returns {void} - */ - function prohibit(node) { - if (node.operator !== "=") { - context.report(node, "Unexpected operator assignment shorthand."); + /** + * Warns if an assignment expression uses operator assignment shorthand. + * @param {ASTNode} node An AssignmentExpression node. + * @returns {void} + */ + function prohibit(node) { + if (node.operator !== "=") { + context.report(node, "Unexpected operator assignment shorthand."); + } } - } - return { - AssignmentExpression: context.options[0] !== "never" ? verify : prohibit - }; + return { + AssignmentExpression: context.options[0] !== "never" ? verify : prohibit + }; -}; - -module.exports.schema = [ - { - enum: ["always", "never"] } -]; +}; diff --git a/lib/rules/operator-linebreak.js b/lib/rules/operator-linebreak.js index 2ed1e216e7c..85f90b908fe 100644 --- a/lib/rules/operator-linebreak.js +++ b/lib/rules/operator-linebreak.js @@ -12,136 +12,146 @@ var lodash = require("lodash"), // Rule Definition //------------------------------------------------------------------------------ -module.exports = function(context) { +module.exports = { + meta: { + docs: { + description: "enforce consistent linebreak style for operators", + category: "Stylistic Issues", + recommended: false + }, - var usedDefaultGlobal = !context.options[0]; - var globalStyle = context.options[0] || "after"; - var options = context.options[1] || {}; - var styleOverrides = options.overrides ? lodash.assign({}, options.overrides) : {}; + schema: [ + { + enum: ["after", "before", "none", null] + }, + { + type: "object", + properties: { + overrides: { + type: "object", + properties: { + anyOf: { + type: "string", + enum: ["after", "before", "none", "ignore"] + } + } + } + }, + additionalProperties: false + } + ] + }, - if (usedDefaultGlobal && !styleOverrides["?"]) { - styleOverrides["?"] = "before"; - } + create: function(context) { - if (usedDefaultGlobal && !styleOverrides[":"]) { - styleOverrides[":"] = "before"; - } + var usedDefaultGlobal = !context.options[0]; + var globalStyle = context.options[0] || "after"; + var options = context.options[1] || {}; + var styleOverrides = options.overrides ? lodash.assign({}, options.overrides) : {}; - //-------------------------------------------------------------------------- - // Helpers - //-------------------------------------------------------------------------- - - /** - * Checks the operator placement - * @param {ASTNode} node The node to check - * @param {ASTNode} leftSide The node that comes before the operator in `node` - * @private - * @returns {void} - */ - function validateNode(node, leftSide) { - var leftToken = context.getLastToken(leftSide); - var operatorToken = context.getTokenAfter(leftToken); - - // When the left part of a binary expression is a single expression wrapped in - // parentheses (ex: `(a) + b`), leftToken will be the last token of the expression - // and operatorToken will be the closing parenthesis. - // The leftToken should be the last closing parenthesis, and the operatorToken - // should be the token right after that. - while (operatorToken.value === ")") { - leftToken = operatorToken; - operatorToken = context.getTokenAfter(operatorToken); + if (usedDefaultGlobal && !styleOverrides["?"]) { + styleOverrides["?"] = "before"; } - var rightToken = context.getTokenAfter(operatorToken); - var operator = operatorToken.value; - var operatorStyleOverride = styleOverrides[operator]; - var style = operatorStyleOverride || globalStyle; + if (usedDefaultGlobal && !styleOverrides[":"]) { + styleOverrides[":"] = "before"; + } - // if single line - if (astUtils.isTokenOnSameLine(leftToken, operatorToken) && - astUtils.isTokenOnSameLine(operatorToken, rightToken)) { + //-------------------------------------------------------------------------- + // Helpers + //-------------------------------------------------------------------------- + + /** + * Checks the operator placement + * @param {ASTNode} node The node to check + * @param {ASTNode} leftSide The node that comes before the operator in `node` + * @private + * @returns {void} + */ + function validateNode(node, leftSide) { + var leftToken = context.getLastToken(leftSide); + var operatorToken = context.getTokenAfter(leftToken); + + // When the left part of a binary expression is a single expression wrapped in + // parentheses (ex: `(a) + b`), leftToken will be the last token of the expression + // and operatorToken will be the closing parenthesis. + // The leftToken should be the last closing parenthesis, and the operatorToken + // should be the token right after that. + while (operatorToken.value === ")") { + leftToken = operatorToken; + operatorToken = context.getTokenAfter(operatorToken); + } - return; + var rightToken = context.getTokenAfter(operatorToken); + var operator = operatorToken.value; + var operatorStyleOverride = styleOverrides[operator]; + var style = operatorStyleOverride || globalStyle; - } else if (operatorStyleOverride !== "ignore" && !astUtils.isTokenOnSameLine(leftToken, operatorToken) && - !astUtils.isTokenOnSameLine(operatorToken, rightToken)) { + // if single line + if (astUtils.isTokenOnSameLine(leftToken, operatorToken) && + astUtils.isTokenOnSameLine(operatorToken, rightToken)) { - // lone operator - context.report(node, { - line: operatorToken.loc.end.line, - column: operatorToken.loc.end.column - }, "Bad line breaking before and after '" + operator + "'."); + return; - } else if (style === "before" && astUtils.isTokenOnSameLine(leftToken, operatorToken)) { + } else if (operatorStyleOverride !== "ignore" && !astUtils.isTokenOnSameLine(leftToken, operatorToken) && + !astUtils.isTokenOnSameLine(operatorToken, rightToken)) { - context.report(node, { - line: operatorToken.loc.end.line, - column: operatorToken.loc.end.column - }, "'" + operator + "' should be placed at the beginning of the line."); + // lone operator + context.report(node, { + line: operatorToken.loc.end.line, + column: operatorToken.loc.end.column + }, "Bad line breaking before and after '" + operator + "'."); - } else if (style === "after" && astUtils.isTokenOnSameLine(operatorToken, rightToken)) { + } else if (style === "before" && astUtils.isTokenOnSameLine(leftToken, operatorToken)) { - context.report(node, { - line: operatorToken.loc.end.line, - column: operatorToken.loc.end.column - }, "'" + operator + "' should be placed at the end of the line."); + context.report(node, { + line: operatorToken.loc.end.line, + column: operatorToken.loc.end.column + }, "'" + operator + "' should be placed at the beginning of the line."); - } else if (style === "none") { + } else if (style === "after" && astUtils.isTokenOnSameLine(operatorToken, rightToken)) { - context.report(node, { - line: operatorToken.loc.end.line, - column: operatorToken.loc.end.column - }, "There should be no line break before or after '" + operator + "'"); + context.report(node, { + line: operatorToken.loc.end.line, + column: operatorToken.loc.end.column + }, "'" + operator + "' should be placed at the end of the line."); - } - } + } else if (style === "none") { - /** - * Validates a binary expression using `validateNode` - * @param {BinaryExpression|LogicalExpression|AssignmentExpression} node node to be validated - * @returns {void} - */ - function validateBinaryExpression(node) { - validateNode(node, node.left); - } + context.report(node, { + line: operatorToken.loc.end.line, + column: operatorToken.loc.end.column + }, "There should be no line break before or after '" + operator + "'"); - //-------------------------------------------------------------------------- - // Public - //-------------------------------------------------------------------------- - - return { - BinaryExpression: validateBinaryExpression, - LogicalExpression: validateBinaryExpression, - AssignmentExpression: validateBinaryExpression, - VariableDeclarator: function(node) { - if (node.init) { - validateNode(node, node.id); } - }, - ConditionalExpression: function(node) { - validateNode(node, node.test); - validateNode(node, node.consequent); } - }; -}; -module.exports.schema = [ - { - enum: ["after", "before", "none", null] - }, - { - type: "object", - properties: { - overrides: { - type: "object", - properties: { - anyOf: { - type: "string", - enum: ["after", "before", "none", "ignore"] - } + /** + * Validates a binary expression using `validateNode` + * @param {BinaryExpression|LogicalExpression|AssignmentExpression} node node to be validated + * @returns {void} + */ + function validateBinaryExpression(node) { + validateNode(node, node.left); + } + + //-------------------------------------------------------------------------- + // Public + //-------------------------------------------------------------------------- + + return { + BinaryExpression: validateBinaryExpression, + LogicalExpression: validateBinaryExpression, + AssignmentExpression: validateBinaryExpression, + VariableDeclarator: function(node) { + if (node.init) { + validateNode(node, node.id); } + }, + ConditionalExpression: function(node) { + validateNode(node, node.test); + validateNode(node, node.consequent); } - }, - additionalProperties: false + }; } -]; +}; diff --git a/lib/rules/padded-blocks.js b/lib/rules/padded-blocks.js index 9e1da0deda7..39a2b07c8d8 100644 --- a/lib/rules/padded-blocks.js +++ b/lib/rules/padded-blocks.js @@ -9,209 +9,219 @@ // Rule Definition //------------------------------------------------------------------------------ -module.exports = function(context) { - var options = {}; - var config = context.options[0] || "always"; - - if (typeof config === "string") { - options.blocks = config === "always"; - } else { - if (config.hasOwnProperty("blocks")) { - options.blocks = config.blocks === "always"; - } - if (config.hasOwnProperty("switches")) { - options.switches = config.switches === "always"; - } - if (config.hasOwnProperty("classes")) { - options.classes = config.classes === "always"; +module.exports = { + meta: { + docs: { + description: "require or disallow padding within blocks", + category: "Stylistic Issues", + recommended: false + }, + + schema: [ + { + oneOf: [ + { + enum: ["always", "never"] + }, + { + type: "object", + properties: { + blocks: { + enum: ["always", "never"] + }, + switches: { + enum: ["always", "never"] + }, + classes: { + enum: ["always", "never"] + } + }, + additionalProperties: false, + minProperties: 1 + } + ] + } + ] + }, + + create: function(context) { + var options = {}; + var config = context.options[0] || "always"; + + if (typeof config === "string") { + options.blocks = config === "always"; + } else { + if (config.hasOwnProperty("blocks")) { + options.blocks = config.blocks === "always"; + } + if (config.hasOwnProperty("switches")) { + options.switches = config.switches === "always"; + } + if (config.hasOwnProperty("classes")) { + options.classes = config.classes === "always"; + } } - } - var ALWAYS_MESSAGE = "Block must be padded by blank lines.", - NEVER_MESSAGE = "Block must not be padded by blank lines."; + var ALWAYS_MESSAGE = "Block must be padded by blank lines.", + NEVER_MESSAGE = "Block must not be padded by blank lines."; - var sourceCode = context.getSourceCode(); + var sourceCode = context.getSourceCode(); - /** - * Gets the open brace token from a given node. - * @param {ASTNode} node - A BlockStatement or SwitchStatement node from which to get the open brace. - * @returns {Token} The token of the open brace. - */ - function getOpenBrace(node) { - if (node.type === "SwitchStatement") { - return sourceCode.getTokenBefore(node.cases[0]); + /** + * Gets the open brace token from a given node. + * @param {ASTNode} node - A BlockStatement or SwitchStatement node from which to get the open brace. + * @returns {Token} The token of the open brace. + */ + function getOpenBrace(node) { + if (node.type === "SwitchStatement") { + return sourceCode.getTokenBefore(node.cases[0]); + } + return sourceCode.getFirstToken(node); } - return sourceCode.getFirstToken(node); - } - /** - * Checks if the given parameter is a comment node - * @param {ASTNode|Token} node An AST node or token - * @returns {boolean} True if node is a comment - */ - function isComment(node) { - return node.type === "Line" || node.type === "Block"; - } - - /** - * Checks if the given token has a blank line after it. - * @param {Token} token The token to check. - * @returns {boolean} Whether or not the token is followed by a blank line. - */ - function isTokenTopPadded(token) { - var tokenStartLine = token.loc.start.line, - expectedFirstLine = tokenStartLine + 2, - first, - firstLine; - - first = token; - do { - first = sourceCode.getTokenOrCommentAfter(first); - } while (isComment(first) && first.loc.start.line === tokenStartLine); - - firstLine = first.loc.start.line; - return expectedFirstLine <= firstLine; - } + /** + * Checks if the given parameter is a comment node + * @param {ASTNode|Token} node An AST node or token + * @returns {boolean} True if node is a comment + */ + function isComment(node) { + return node.type === "Line" || node.type === "Block"; + } - /** - * Checks if the given token is preceeded by a blank line. - * @param {Token} token The token to check - * @returns {boolean} Whether or not the token is preceeded by a blank line - */ - function isTokenBottomPadded(token) { - var blockEnd = token.loc.end.line, - expectedLastLine = blockEnd - 2, - last, - lastLine; - - last = token; - do { - last = sourceCode.getTokenOrCommentBefore(last); - } while (isComment(last) && last.loc.end.line === blockEnd); - - lastLine = last.loc.end.line; - return lastLine <= expectedLastLine; - } + /** + * Checks if the given token has a blank line after it. + * @param {Token} token The token to check. + * @returns {boolean} Whether or not the token is followed by a blank line. + */ + function isTokenTopPadded(token) { + var tokenStartLine = token.loc.start.line, + expectedFirstLine = tokenStartLine + 2, + first, + firstLine; + + first = token; + do { + first = sourceCode.getTokenOrCommentAfter(first); + } while (isComment(first) && first.loc.start.line === tokenStartLine); + + firstLine = first.loc.start.line; + return expectedFirstLine <= firstLine; + } - /** - * Checks if a node should be padded, according to the rule config. - * @param {ASTNode} node The AST node to check. - * @returns {boolean} True if the node should be padded, false otherwise. - */ - function requirePaddingFor(node) { - switch (node.type) { - case "BlockStatement": - return options.blocks; - case "SwitchStatement": - return options.switches; - case "ClassBody": - return options.classes; - - /* istanbul ignore next */ - default: - throw new Error("unreachable"); + /** + * Checks if the given token is preceeded by a blank line. + * @param {Token} token The token to check + * @returns {boolean} Whether or not the token is preceeded by a blank line + */ + function isTokenBottomPadded(token) { + var blockEnd = token.loc.end.line, + expectedLastLine = blockEnd - 2, + last, + lastLine; + + last = token; + do { + last = sourceCode.getTokenOrCommentBefore(last); + } while (isComment(last) && last.loc.end.line === blockEnd); + + lastLine = last.loc.end.line; + return lastLine <= expectedLastLine; } - } - /** - * Checks the given BlockStatement node to be padded if the block is not empty. - * @param {ASTNode} node The AST node of a BlockStatement. - * @returns {void} undefined. - */ - function checkPadding(node) { - var openBrace = getOpenBrace(node), - closeBrace = sourceCode.getLastToken(node), - blockHasTopPadding = isTokenTopPadded(openBrace), - blockHasBottomPadding = isTokenBottomPadded(closeBrace); - - if (requirePaddingFor(node)) { - if (!blockHasTopPadding) { - context.report({ - node: node, - loc: { line: openBrace.loc.start.line, column: openBrace.loc.start.column }, - message: ALWAYS_MESSAGE - }); - } - if (!blockHasBottomPadding) { - context.report({ - node: node, - loc: {line: closeBrace.loc.end.line, column: closeBrace.loc.end.column - 1 }, - message: ALWAYS_MESSAGE - }); - } - } else { - if (blockHasTopPadding) { - context.report({ - node: node, - loc: { line: openBrace.loc.start.line, column: openBrace.loc.start.column }, - message: NEVER_MESSAGE - }); + /** + * Checks if a node should be padded, according to the rule config. + * @param {ASTNode} node The AST node to check. + * @returns {boolean} True if the node should be padded, false otherwise. + */ + function requirePaddingFor(node) { + switch (node.type) { + case "BlockStatement": + return options.blocks; + case "SwitchStatement": + return options.switches; + case "ClassBody": + return options.classes; + + /* istanbul ignore next */ + default: + throw new Error("unreachable"); } + } - if (blockHasBottomPadding) { - context.report({ - node: node, - loc: {line: closeBrace.loc.end.line, column: closeBrace.loc.end.column - 1 }, - message: NEVER_MESSAGE - }); + /** + * Checks the given BlockStatement node to be padded if the block is not empty. + * @param {ASTNode} node The AST node of a BlockStatement. + * @returns {void} undefined. + */ + function checkPadding(node) { + var openBrace = getOpenBrace(node), + closeBrace = sourceCode.getLastToken(node), + blockHasTopPadding = isTokenTopPadded(openBrace), + blockHasBottomPadding = isTokenBottomPadded(closeBrace); + + if (requirePaddingFor(node)) { + if (!blockHasTopPadding) { + context.report({ + node: node, + loc: { line: openBrace.loc.start.line, column: openBrace.loc.start.column }, + message: ALWAYS_MESSAGE + }); + } + if (!blockHasBottomPadding) { + context.report({ + node: node, + loc: {line: closeBrace.loc.end.line, column: closeBrace.loc.end.column - 1 }, + message: ALWAYS_MESSAGE + }); + } + } else { + if (blockHasTopPadding) { + context.report({ + node: node, + loc: { line: openBrace.loc.start.line, column: openBrace.loc.start.column }, + message: NEVER_MESSAGE + }); + } + + if (blockHasBottomPadding) { + context.report({ + node: node, + loc: {line: closeBrace.loc.end.line, column: closeBrace.loc.end.column - 1 }, + message: NEVER_MESSAGE + }); + } } } - } - var rule = {}; + var rule = {}; - if (options.hasOwnProperty("switches")) { - rule.SwitchStatement = function(node) { - if (node.cases.length === 0) { - return; - } - checkPadding(node); - }; - } - - if (options.hasOwnProperty("blocks")) { - rule.BlockStatement = function(node) { - if (node.body.length === 0) { - return; - } - checkPadding(node); - }; - } + if (options.hasOwnProperty("switches")) { + rule.SwitchStatement = function(node) { + if (node.cases.length === 0) { + return; + } + checkPadding(node); + }; + } - if (options.hasOwnProperty("classes")) { - rule.ClassBody = function(node) { - if (node.body.length === 0) { - return; - } - checkPadding(node); - }; - } + if (options.hasOwnProperty("blocks")) { + rule.BlockStatement = function(node) { + if (node.body.length === 0) { + return; + } + checkPadding(node); + }; + } - return rule; -}; + if (options.hasOwnProperty("classes")) { + rule.ClassBody = function(node) { + if (node.body.length === 0) { + return; + } + checkPadding(node); + }; + } -module.exports.schema = [ - { - oneOf: [ - { - enum: ["always", "never"] - }, - { - type: "object", - properties: { - blocks: { - enum: ["always", "never"] - }, - switches: { - enum: ["always", "never"] - }, - classes: { - enum: ["always", "never"] - } - }, - additionalProperties: false, - minProperties: 1 - } - ] + return rule; } -]; +}; diff --git a/lib/rules/prefer-arrow-callback.js b/lib/rules/prefer-arrow-callback.js index e37df4c9eac..4c4c2e6f2b1 100644 --- a/lib/rules/prefer-arrow-callback.js +++ b/lib/rules/prefer-arrow-callback.js @@ -119,126 +119,136 @@ function getCallbackInfo(node) { // Rule Definition //------------------------------------------------------------------------------ -module.exports = function(context) { - var options = context.options[0] || {}; - - var allowUnboundThis = options.allowUnboundThis !== false; // default to true - var allowNamedFunctions = options.allowNamedFunctions; - - /* - * {Array<{this: boolean, super: boolean, meta: boolean}>} - * - this - A flag which shows there are one or more ThisExpression. - * - super - A flag which shows there are one or more Super. - * - meta - A flag which shows there are one or more MethProperty. - */ - var stack = []; - - /** - * Pushes new function scope with all `false` flags. - * @returns {void} - */ - function enterScope() { - stack.push({this: false, super: false, meta: false}); - } +module.exports = { + meta: { + docs: { + description: "require arrow functions as callbacks", + category: "ECMAScript 6", + recommended: false + }, - /** - * Pops a function scope from the stack. - * @returns {{this: boolean, super: boolean, meta: boolean}} The information of the last scope. - */ - function exitScope() { - return stack.pop(); - } + schema: [ + { + type: "object", + properties: { + allowNamedFunctions: { + type: "boolean" + }, + allowUnboundThis: { + type: "boolean" + } + }, + additionalProperties: false + } + ] + }, + + create: function(context) { + var options = context.options[0] || {}; + + var allowUnboundThis = options.allowUnboundThis !== false; // default to true + var allowNamedFunctions = options.allowNamedFunctions; + + /* + * {Array<{this: boolean, super: boolean, meta: boolean}>} + * - this - A flag which shows there are one or more ThisExpression. + * - super - A flag which shows there are one or more Super. + * - meta - A flag which shows there are one or more MethProperty. + */ + var stack = []; + + /** + * Pushes new function scope with all `false` flags. + * @returns {void} + */ + function enterScope() { + stack.push({this: false, super: false, meta: false}); + } - return { + /** + * Pops a function scope from the stack. + * @returns {{this: boolean, super: boolean, meta: boolean}} The information of the last scope. + */ + function exitScope() { + return stack.pop(); + } - // Reset internal state. - Program: function() { - stack = []; - }, + return { - // If there are below, it cannot replace with arrow functions merely. - ThisExpression: function() { - var info = stack[stack.length - 1]; + // Reset internal state. + Program: function() { + stack = []; + }, - if (info) { - info.this = true; - } - }, + // If there are below, it cannot replace with arrow functions merely. + ThisExpression: function() { + var info = stack[stack.length - 1]; - Super: function() { - var info = stack[stack.length - 1]; + if (info) { + info.this = true; + } + }, - if (info) { - info.super = true; - } - }, + Super: function() { + var info = stack[stack.length - 1]; - MetaProperty: function(node) { - var info = stack[stack.length - 1]; + if (info) { + info.super = true; + } + }, - if (info && checkMetaProperty(node, "new", "target")) { - info.meta = true; - } - }, + MetaProperty: function(node) { + var info = stack[stack.length - 1]; - // To skip nested scopes. - FunctionDeclaration: enterScope, - "FunctionDeclaration:exit": exitScope, + if (info && checkMetaProperty(node, "new", "target")) { + info.meta = true; + } + }, - // Main. - FunctionExpression: enterScope, - "FunctionExpression:exit": function(node) { - var scopeInfo = exitScope(); + // To skip nested scopes. + FunctionDeclaration: enterScope, + "FunctionDeclaration:exit": exitScope, - // Skip named function expressions - if (allowNamedFunctions && node.id && node.id.name) { - return; - } + // Main. + FunctionExpression: enterScope, + "FunctionExpression:exit": function(node) { + var scopeInfo = exitScope(); - // Skip generators. - if (node.generator) { - return; - } + // Skip named function expressions + if (allowNamedFunctions && node.id && node.id.name) { + return; + } - // Skip recursive functions. - var nameVar = context.getDeclaredVariables(node)[0]; + // Skip generators. + if (node.generator) { + return; + } - if (isFunctionName(nameVar) && nameVar.references.length > 0) { - return; - } + // Skip recursive functions. + var nameVar = context.getDeclaredVariables(node)[0]; - // Skip if it's using arguments. - var variable = getVariableOfArguments(context.getScope()); + if (isFunctionName(nameVar) && nameVar.references.length > 0) { + return; + } - if (variable && variable.references.length > 0) { - return; - } + // Skip if it's using arguments. + var variable = getVariableOfArguments(context.getScope()); - // Reports if it's a callback which can replace with arrows. - var callbackInfo = getCallbackInfo(node); + if (variable && variable.references.length > 0) { + return; + } - if (callbackInfo.isCallback && - (!allowUnboundThis || !scopeInfo.this || callbackInfo.isLexicalThis) && - !scopeInfo.super && - !scopeInfo.meta - ) { - context.report(node, "Unexpected function expression."); - } - } - }; -}; + // Reports if it's a callback which can replace with arrows. + var callbackInfo = getCallbackInfo(node); -module.exports.schema = [ - { - type: "object", - properties: { - allowNamedFunctions: { - type: "boolean" - }, - allowUnboundThis: { - type: "boolean" + if (callbackInfo.isCallback && + (!allowUnboundThis || !scopeInfo.this || callbackInfo.isLexicalThis) && + !scopeInfo.super && + !scopeInfo.meta + ) { + context.report(node, "Unexpected function expression."); + } } - }, - additionalProperties: false + }; } -]; +}; diff --git a/lib/rules/prefer-const.js b/lib/rules/prefer-const.js index 380b0a880d1..668453520fc 100644 --- a/lib/rules/prefer-const.js +++ b/lib/rules/prefer-const.js @@ -178,90 +178,100 @@ function groupByDestructuring(variables) { // Rule Definition //------------------------------------------------------------------------------ -module.exports = function(context) { - var options = context.options[0] || {}; - var checkingMixedDestructuring = options.destructuring !== "all"; - var variables = null; - - /** - * Reports a given reference. - * - * @param {escope.Reference} reference - A reference to report. - * @returns {void} - */ - function report(reference) { - var id = reference.identifier; - - context.report({ - node: id, - message: "'{{name}}' is never reassigned, use 'const' instead.", - data: id - }); - } +module.exports = { + meta: { + docs: { + description: "require `const` declarations for variables that are never reassigned after declared", + category: "ECMAScript 6", + recommended: false + }, - /** - * Reports a given variable if the variable should be declared as const. - * - * @param {escope.Variable} variable - A variable to report. - * @returns {void} - */ - function checkVariable(variable) { - var writer = getWriteReferenceIfShouldBeConst(variable); + schema: [ + { + type: "object", + properties: { + destructuring: {enum: ["any", "all"]} + }, + additionalProperties: false + } + ] + }, + + create: function(context) { + var options = context.options[0] || {}; + var checkingMixedDestructuring = options.destructuring !== "all"; + var variables = null; + + /** + * Reports a given reference. + * + * @param {escope.Reference} reference - A reference to report. + * @returns {void} + */ + function report(reference) { + var id = reference.identifier; - if (writer) { - report(writer); + context.report({ + node: id, + message: "'{{name}}' is never reassigned, use 'const' instead.", + data: id + }); } - } - /** - * Reports given references if all of the reference should be declared as - * const. - * - * The argument 'writers' is an array of references. - * This reference is the result of - * 'getWriteReferenceIfShouldBeConst(variable)', so it's nullable. - * In simple declaration or assignment cases, the length of the array is 1. - * In destructuring cases, the length of the array can be 2 or more. - * - * @param {(escope.Reference|null)[]} writers - References which are grouped - * by destructuring to report. - * @returns {void} - */ - function checkGroup(writers) { - if (writers.every(Boolean)) { - writers.forEach(report); + /** + * Reports a given variable if the variable should be declared as const. + * + * @param {escope.Variable} variable - A variable to report. + * @returns {void} + */ + function checkVariable(variable) { + var writer = getWriteReferenceIfShouldBeConst(variable); + + if (writer) { + report(writer); + } } - } - return { - Program: function() { - variables = []; - }, - - "Program:exit": function() { - if (checkingMixedDestructuring) { - variables.forEach(checkVariable); - } else { - groupByDestructuring(variables).forEach(checkGroup); + /** + * Reports given references if all of the reference should be declared as + * const. + * + * The argument 'writers' is an array of references. + * This reference is the result of + * 'getWriteReferenceIfShouldBeConst(variable)', so it's nullable. + * In simple declaration or assignment cases, the length of the array is 1. + * In destructuring cases, the length of the array can be 2 or more. + * + * @param {(escope.Reference|null)[]} writers - References which are grouped + * by destructuring to report. + * @returns {void} + */ + function checkGroup(writers) { + if (writers.every(Boolean)) { + writers.forEach(report); } + } - variables = null; - }, + return { + Program: function() { + variables = []; + }, - VariableDeclaration: function(node) { - if (node.kind === "let" && !isInitOfForStatement(node)) { - pushAll(variables, context.getDeclaredVariables(node)); - } - } - }; -}; + "Program:exit": function() { + if (checkingMixedDestructuring) { + variables.forEach(checkVariable); + } else { + groupByDestructuring(variables).forEach(checkGroup); + } -module.exports.schema = [ - { - type: "object", - properties: { - destructuring: {enum: ["any", "all"]} - }, - additionalProperties: false + variables = null; + }, + + VariableDeclaration: function(node) { + if (node.kind === "let" && !isInitOfForStatement(node)) { + pushAll(variables, context.getDeclaredVariables(node)); + } + } + }; } -]; +}; diff --git a/lib/rules/prefer-reflect.js b/lib/rules/prefer-reflect.js index 9f48d69e3d2..38bb093ba6f 100644 --- a/lib/rules/prefer-reflect.js +++ b/lib/rules/prefer-reflect.js @@ -8,94 +8,104 @@ // Rule Definition //------------------------------------------------------------------------------ -module.exports = function(context) { - var existingNames = { - apply: "Function.prototype.apply", - call: "Function.prototype.call", - defineProperty: "Object.defineProperty", - getOwnPropertyDescriptor: "Object.getOwnPropertyDescriptor", - getPrototypeOf: "Object.getPrototypeOf", - setPrototypeOf: "Object.setPrototypeOf", - isExtensible: "Object.isExtensible", - getOwnPropertyNames: "Object.getOwnPropertyNames", - preventExtensions: "Object.preventExtensions" - }; +module.exports = { + meta: { + docs: { + description: "require `Reflect` methods where applicable", + category: "ECMAScript 6", + recommended: false + }, - var reflectSubsitutes = { - apply: "Reflect.apply", - call: "Reflect.apply", - defineProperty: "Reflect.defineProperty", - getOwnPropertyDescriptor: "Reflect.getOwnPropertyDescriptor", - getPrototypeOf: "Reflect.getPrototypeOf", - setPrototypeOf: "Reflect.setPrototypeOf", - isExtensible: "Reflect.isExtensible", - getOwnPropertyNames: "Reflect.getOwnPropertyNames", - preventExtensions: "Reflect.preventExtensions" - }; + schema: [ + { + type: "object", + properties: { + exceptions: { + type: "array", + items: { + enum: [ + "apply", + "call", + "delete", + "defineProperty", + "getOwnPropertyDescriptor", + "getPrototypeOf", + "setPrototypeOf", + "isExtensible", + "getOwnPropertyNames", + "preventExtensions" + ] + }, + uniqueItems: true + } + }, + additionalProperties: false + } + ] + }, - var exceptions = (context.options[0] || {}).exceptions || []; + create: function(context) { + var existingNames = { + apply: "Function.prototype.apply", + call: "Function.prototype.call", + defineProperty: "Object.defineProperty", + getOwnPropertyDescriptor: "Object.getOwnPropertyDescriptor", + getPrototypeOf: "Object.getPrototypeOf", + setPrototypeOf: "Object.setPrototypeOf", + isExtensible: "Object.isExtensible", + getOwnPropertyNames: "Object.getOwnPropertyNames", + preventExtensions: "Object.preventExtensions" + }; - /** - * Reports the Reflect violation based on the `existing` and `substitute` - * @param {Object} node The node that violates the rule. - * @param {string} existing The existing method name that has been used. - * @param {string} substitute The Reflect substitute that should be used. - * @returns {void} - */ - function report(node, existing, substitute) { - context.report(node, "Avoid using {{existing}}, instead use {{substitute}}", { - existing: existing, - substitute: substitute - }); - } + var reflectSubsitutes = { + apply: "Reflect.apply", + call: "Reflect.apply", + defineProperty: "Reflect.defineProperty", + getOwnPropertyDescriptor: "Reflect.getOwnPropertyDescriptor", + getPrototypeOf: "Reflect.getPrototypeOf", + setPrototypeOf: "Reflect.setPrototypeOf", + isExtensible: "Reflect.isExtensible", + getOwnPropertyNames: "Reflect.getOwnPropertyNames", + preventExtensions: "Reflect.preventExtensions" + }; - return { - CallExpression: function(node) { - var methodName = (node.callee.property || {}).name; - var isReflectCall = (node.callee.object || {}).name === "Reflect"; - var hasReflectSubsitute = reflectSubsitutes.hasOwnProperty(methodName); - var userConfiguredException = exceptions.indexOf(methodName) !== -1; - - if (hasReflectSubsitute && !isReflectCall && !userConfiguredException) { - report(node, existingNames[methodName], reflectSubsitutes[methodName]); - } - }, - UnaryExpression: function(node) { - var isDeleteOperator = node.operator === "delete"; - var targetsIdentifier = node.argument.type === "Identifier"; - var userConfiguredException = exceptions.indexOf("delete") !== -1; + var exceptions = (context.options[0] || {}).exceptions || []; - if (isDeleteOperator && !targetsIdentifier && !userConfiguredException) { - report(node, "the delete keyword", "Reflect.deleteProperty"); - } + /** + * Reports the Reflect violation based on the `existing` and `substitute` + * @param {Object} node The node that violates the rule. + * @param {string} existing The existing method name that has been used. + * @param {string} substitute The Reflect substitute that should be used. + * @returns {void} + */ + function report(node, existing, substitute) { + context.report(node, "Avoid using {{existing}}, instead use {{substitute}}", { + existing: existing, + substitute: substitute + }); } - }; -}; + return { + CallExpression: function(node) { + var methodName = (node.callee.property || {}).name; + var isReflectCall = (node.callee.object || {}).name === "Reflect"; + var hasReflectSubsitute = reflectSubsitutes.hasOwnProperty(methodName); + var userConfiguredException = exceptions.indexOf(methodName) !== -1; -module.exports.schema = [ - { - type: "object", - properties: { - exceptions: { - type: "array", - items: { - enum: [ - "apply", - "call", - "delete", - "defineProperty", - "getOwnPropertyDescriptor", - "getPrototypeOf", - "setPrototypeOf", - "isExtensible", - "getOwnPropertyNames", - "preventExtensions" - ] - }, - uniqueItems: true + if (hasReflectSubsitute && !isReflectCall && !userConfiguredException) { + report(node, existingNames[methodName], reflectSubsitutes[methodName]); + } + }, + UnaryExpression: function(node) { + var isDeleteOperator = node.operator === "delete"; + var targetsIdentifier = node.argument.type === "Identifier"; + var userConfiguredException = exceptions.indexOf("delete") !== -1; + + if (isDeleteOperator && !targetsIdentifier && !userConfiguredException) { + report(node, "the delete keyword", "Reflect.deleteProperty"); + } } - }, - additionalProperties: false + }; + } -]; +}; diff --git a/lib/rules/prefer-rest-params.js b/lib/rules/prefer-rest-params.js index 70692949736..0ce1d8a1220 100644 --- a/lib/rules/prefer-rest-params.js +++ b/lib/rules/prefer-rest-params.js @@ -36,38 +36,48 @@ function getVariableOfArguments(scope) { // Rule Definition //------------------------------------------------------------------------------ -module.exports = function(context) { +module.exports = { + meta: { + docs: { + description: "require rest parameters instead of `arguments`", + category: "ECMAScript 6", + recommended: false + }, - /** - * Reports a given reference. - * - * @param {escope.Reference} reference - A reference to report. - * @returns {void} - */ - function report(reference) { - context.report({ - node: reference.identifier, - message: "Use the rest parameters instead of 'arguments'." - }); - } + schema: [] + }, - /** - * Reports references of the implicit `arguments` variable if exist. - * - * @returns {void} - */ - function checkForArguments() { - var argumentsVar = getVariableOfArguments(context.getScope()); + create: function(context) { - if (argumentsVar) { - argumentsVar.references.forEach(report); + /** + * Reports a given reference. + * + * @param {escope.Reference} reference - A reference to report. + * @returns {void} + */ + function report(reference) { + context.report({ + node: reference.identifier, + message: "Use the rest parameters instead of 'arguments'." + }); } - } - return { - FunctionDeclaration: checkForArguments, - FunctionExpression: checkForArguments - }; -}; + /** + * Reports references of the implicit `arguments` variable if exist. + * + * @returns {void} + */ + function checkForArguments() { + var argumentsVar = getVariableOfArguments(context.getScope()); -module.exports.schema = []; + if (argumentsVar) { + argumentsVar.references.forEach(report); + } + } + + return { + FunctionDeclaration: checkForArguments, + FunctionExpression: checkForArguments + }; + } +};