mirror of https://github.com/lukechilds/node.git
Browse Source
Update ESLint to 3.0.0. This includes an enhancement to `no-unused-vars` such that it finds a few instances in our code base that it did not find previously (fixed in previous commits readying this for landing). PR-URL: https://github.com/nodejs/node/pull/7601 Reviewed-By: Michaël Zasso <mic.besace@gmail.com> Reviewed-By: Roman Reiss <me@silverwind.io>v4.x
Rich Trott
9 years ago
committed by
Myles Borins
546 changed files with 9689 additions and 10543 deletions
File diff suppressed because it is too large
@ -0,0 +1,29 @@ |
|||
/** |
|||
* @fileoverview Config to enable all rules. |
|||
* @author Robert Fletcher |
|||
*/ |
|||
|
|||
"use strict"; |
|||
|
|||
//------------------------------------------------------------------------------
|
|||
// Requirements
|
|||
//------------------------------------------------------------------------------
|
|||
|
|||
var fs = require("fs"), |
|||
path = require("path"); |
|||
|
|||
//------------------------------------------------------------------------------
|
|||
// Helpers
|
|||
//------------------------------------------------------------------------------
|
|||
|
|||
var ruleFiles = fs.readdirSync(path.resolve(__dirname, "../lib/rules")); |
|||
var enabledRules = ruleFiles.reduce(function(result, filename) { |
|||
result[path.basename(filename, ".js")] = "error"; |
|||
return result; |
|||
}, {}); |
|||
|
|||
//------------------------------------------------------------------------------
|
|||
// Public Interface
|
|||
//------------------------------------------------------------------------------
|
|||
|
|||
module.exports = { rules: enabledRules }; |
@ -0,0 +1,148 @@ |
|||
/** |
|||
* @fileoverview enforce a maximum file length |
|||
* @author Alberto Rodríguez |
|||
*/ |
|||
"use strict"; |
|||
|
|||
//------------------------------------------------------------------------------
|
|||
// Requirements
|
|||
//------------------------------------------------------------------------------
|
|||
|
|||
var lodash = require("lodash"); |
|||
var astUtils = require("../ast-utils"); |
|||
|
|||
//------------------------------------------------------------------------------
|
|||
// Rule Definition
|
|||
//------------------------------------------------------------------------------
|
|||
|
|||
module.exports = { |
|||
meta: { |
|||
docs: { |
|||
description: "enforce a maximum number of lines per file", |
|||
category: "Stylistic Issues", |
|||
recommended: false |
|||
}, |
|||
|
|||
schema: [ |
|||
{ |
|||
oneOf: [ |
|||
{ |
|||
type: "integer", |
|||
minimum: 0 |
|||
}, |
|||
{ |
|||
type: "object", |
|||
properties: { |
|||
max: { |
|||
type: "integer", |
|||
minimum: 0 |
|||
}, |
|||
skipComments: { |
|||
type: "boolean" |
|||
}, |
|||
skipBlankLines: { |
|||
type: "boolean" |
|||
} |
|||
}, |
|||
additionalProperties: false |
|||
} |
|||
] |
|||
} |
|||
] |
|||
}, |
|||
|
|||
create: function(context) { |
|||
var option = context.options[0], |
|||
max = 300; |
|||
|
|||
if (typeof option === "object" && option.hasOwnProperty("max") && typeof option.max === "number") { |
|||
max = option.max; |
|||
} |
|||
|
|||
if (typeof option === "number") { |
|||
max = option; |
|||
} |
|||
|
|||
var skipComments = option && option.skipComments; |
|||
var skipBlankLines = option && option.skipBlankLines; |
|||
|
|||
var sourceCode = context.getSourceCode(); |
|||
|
|||
/** |
|||
* Returns whether or not a token is a comment node type |
|||
* @param {Token} token The token to check |
|||
* @returns {boolean} True if the token is a comment node |
|||
*/ |
|||
function isCommentNodeType(token) { |
|||
return token && (token.type === "Block" || token.type === "Line"); |
|||
} |
|||
|
|||
/** |
|||
* Returns the line numbers of a comment that don't have any code on the same line |
|||
* @param {Node} comment The comment node to check |
|||
* @returns {int[]} The line numbers |
|||
*/ |
|||
function getLinesWithoutCode(comment) { |
|||
var start = comment.loc.start.line; |
|||
var end = comment.loc.end.line; |
|||
|
|||
var token; |
|||
|
|||
token = comment; |
|||
do { |
|||
token = sourceCode.getTokenOrCommentBefore(token); |
|||
} while (isCommentNodeType(token)); |
|||
|
|||
if (token && astUtils.isTokenOnSameLine(token, comment)) { |
|||
start += 1; |
|||
} |
|||
|
|||
token = comment; |
|||
do { |
|||
token = sourceCode.getTokenOrCommentAfter(token); |
|||
} while (isCommentNodeType(token)); |
|||
|
|||
if (token && astUtils.isTokenOnSameLine(comment, token)) { |
|||
end -= 1; |
|||
} |
|||
|
|||
if (start <= end) { |
|||
return lodash.range(start, end + 1); |
|||
} |
|||
return []; |
|||
} |
|||
|
|||
return { |
|||
"Program:exit": function() { |
|||
var lines = sourceCode.lines.map(function(text, i) { |
|||
return { lineNumber: i + 1, text: text }; |
|||
}); |
|||
|
|||
if (skipBlankLines) { |
|||
lines = lines.filter(function(l) { |
|||
return l.text.trim() !== ""; |
|||
}); |
|||
} |
|||
|
|||
if (skipComments) { |
|||
var comments = sourceCode.getAllComments(); |
|||
|
|||
var commentLines = lodash.flatten(comments.map(function(comment) { |
|||
return getLinesWithoutCode(comment); |
|||
})); |
|||
|
|||
lines = lines.filter(function(l) { |
|||
return !lodash.includes(commentLines, l.lineNumber); |
|||
}); |
|||
} |
|||
|
|||
if (lines.length > max) { |
|||
context.report({ |
|||
loc: { line: 1, column: 0 }, |
|||
message: "File must be at most " + max + " lines long" |
|||
}); |
|||
} |
|||
} |
|||
}; |
|||
} |
|||
}; |
@ -0,0 +1,212 @@ |
|||
/** |
|||
* @fileoverview Rule to disallow mixed binary operators. |
|||
* @author Toru Nagashima |
|||
*/ |
|||
|
|||
"use strict"; |
|||
|
|||
//------------------------------------------------------------------------------
|
|||
// Requirements
|
|||
//------------------------------------------------------------------------------
|
|||
|
|||
var astUtils = require("../ast-utils.js"); |
|||
|
|||
//------------------------------------------------------------------------------
|
|||
// Helpers
|
|||
//------------------------------------------------------------------------------
|
|||
|
|||
var ARITHMETIC_OPERATORS = ["+", "-", "*", "/", "%", "**"]; |
|||
var BITWISE_OPERATORS = ["&", "|", "^", "~", "<<", ">>", ">>>"]; |
|||
var COMPARISON_OPERATORS = ["==", "!=", "===", "!==", ">", ">=", "<", "<="]; |
|||
var LOGICAL_OPERATORS = ["&&", "||"]; |
|||
var RELATIONAL_OPERATORS = ["in", "instanceof"]; |
|||
var ALL_OPERATORS = [].concat( |
|||
ARITHMETIC_OPERATORS, |
|||
BITWISE_OPERATORS, |
|||
COMPARISON_OPERATORS, |
|||
LOGICAL_OPERATORS, |
|||
RELATIONAL_OPERATORS |
|||
); |
|||
var DEFAULT_GROUPS = [ |
|||
ARITHMETIC_OPERATORS, |
|||
BITWISE_OPERATORS, |
|||
COMPARISON_OPERATORS, |
|||
LOGICAL_OPERATORS, |
|||
RELATIONAL_OPERATORS |
|||
]; |
|||
var TARGET_NODE_TYPE = /^(?:Binary|Logical)Expression$/; |
|||
|
|||
/** |
|||
* Normalizes options. |
|||
* |
|||
* @param {object|undefined} options - A options object to normalize. |
|||
* @returns {object} Normalized option object. |
|||
*/ |
|||
function normalizeOptions(options) { |
|||
var hasGroups = (options && options.groups && options.groups.length > 0); |
|||
var groups = hasGroups ? options.groups : DEFAULT_GROUPS; |
|||
var allowSamePrecedence = (options && options.allowSamePrecedence) !== false; |
|||
|
|||
return { |
|||
groups: groups, |
|||
allowSamePrecedence: allowSamePrecedence |
|||
}; |
|||
} |
|||
|
|||
/** |
|||
* Checks whether any group which includes both given operator exists or not. |
|||
* |
|||
* @param {Array.<string[]>} groups - A list of groups to check. |
|||
* @param {string} left - An operator. |
|||
* @param {string} right - Another operator. |
|||
* @returns {boolean} `true` if such group existed. |
|||
*/ |
|||
function includesBothInAGroup(groups, left, right) { |
|||
return groups.some(function(group) { |
|||
return group.indexOf(left) !== -1 && group.indexOf(right) !== -1; |
|||
}); |
|||
} |
|||
|
|||
//------------------------------------------------------------------------------
|
|||
// Rule Definition
|
|||
//------------------------------------------------------------------------------
|
|||
|
|||
module.exports = { |
|||
meta: { |
|||
docs: { |
|||
description: "disallow mixed binary operators", |
|||
category: "Stylistic Issues", |
|||
recommended: false |
|||
}, |
|||
schema: [ |
|||
{ |
|||
type: "object", |
|||
properties: { |
|||
groups: { |
|||
type: "array", |
|||
items: { |
|||
type: "array", |
|||
items: {enum: ALL_OPERATORS}, |
|||
minItems: 2, |
|||
uniqueItems: true |
|||
}, |
|||
uniqueItems: true |
|||
}, |
|||
allowSamePrecedence: { |
|||
type: "boolean" |
|||
} |
|||
}, |
|||
additionalProperties: false |
|||
} |
|||
] |
|||
}, |
|||
|
|||
create: function(context) { |
|||
var sourceCode = context.getSourceCode(); |
|||
var options = normalizeOptions(context.options[0]); |
|||
|
|||
/** |
|||
* Checks whether a given node should be ignored by options or not. |
|||
* |
|||
* @param {ASTNode} node - A node to check. This is a BinaryExpression |
|||
* node or a LogicalExpression node. This parent node is one of |
|||
* them, too. |
|||
* @returns {boolean} `true` if the node should be ignored. |
|||
*/ |
|||
function shouldIgnore(node) { |
|||
var a = node; |
|||
var b = node.parent; |
|||
|
|||
return ( |
|||
!includesBothInAGroup(options.groups, a.operator, b.operator) || |
|||
( |
|||
options.allowSamePrecedence && |
|||
astUtils.getPrecedence(a) === astUtils.getPrecedence(b) |
|||
) |
|||
); |
|||
} |
|||
|
|||
/** |
|||
* Checks whether the operator of a given node is mixed with parent |
|||
* node's operator or not. |
|||
* |
|||
* @param {ASTNode} node - A node to check. This is a BinaryExpression |
|||
* node or a LogicalExpression node. This parent node is one of |
|||
* them, too. |
|||
* @returns {boolean} `true` if the node was mixed. |
|||
*/ |
|||
function isMixedWithParent(node) { |
|||
return ( |
|||
node.operator !== node.parent.operator && |
|||
!astUtils.isParenthesised(sourceCode, node) |
|||
); |
|||
} |
|||
|
|||
/** |
|||
* Gets the operator token of a given node. |
|||
* |
|||
* @param {ASTNode} node - A node to check. This is a BinaryExpression |
|||
* node or a LogicalExpression node. |
|||
* @returns {Token} The operator token of the node. |
|||
*/ |
|||
function getOperatorToken(node) { |
|||
var token = sourceCode.getTokenAfter(node.left); |
|||
|
|||
while (token.value === ")") { |
|||
token = sourceCode.getTokenAfter(token); |
|||
} |
|||
|
|||
return token; |
|||
} |
|||
|
|||
/** |
|||
* Reports both the operator of a given node and the operator of the |
|||
* parent node. |
|||
* |
|||
* @param {ASTNode} node - A node to check. This is a BinaryExpression |
|||
* node or a LogicalExpression node. This parent node is one of |
|||
* them, too. |
|||
* @returns {void} |
|||
*/ |
|||
function reportBothOperators(node) { |
|||
var parent = node.parent; |
|||
var left = (parent.left === node) ? node : parent; |
|||
var right = (parent.left !== node) ? node : parent; |
|||
var message = |
|||
"Unexpected mix of '" + left.operator + "' and '" + |
|||
right.operator + "'."; |
|||
|
|||
context.report({ |
|||
node: left, |
|||
loc: getOperatorToken(left).loc.start, |
|||
message: message |
|||
}); |
|||
context.report({ |
|||
node: right, |
|||
loc: getOperatorToken(right).loc.start, |
|||
message: message |
|||
}); |
|||
} |
|||
|
|||
/** |
|||
* Checks between the operator of this node and the operator of the |
|||
* parent node. |
|||
* |
|||
* @param {ASTNode} node - A node to check. |
|||
* @returns {void} |
|||
*/ |
|||
function check(node) { |
|||
if (TARGET_NODE_TYPE.test(node.parent.type) && |
|||
isMixedWithParent(node) && |
|||
!shouldIgnore(node) |
|||
) { |
|||
reportBothOperators(node); |
|||
} |
|||
} |
|||
|
|||
return { |
|||
BinaryExpression: check, |
|||
LogicalExpression: check |
|||
}; |
|||
} |
|||
}; |
@ -0,0 +1,52 @@ |
|||
/** |
|||
* @fileoverview Rule to disallow use of Object.prototype builtins on objects |
|||
* @author Andrew Levine |
|||
*/ |
|||
"use strict"; |
|||
|
|||
//------------------------------------------------------------------------------
|
|||
// Rule Definition
|
|||
//------------------------------------------------------------------------------
|
|||
|
|||
module.exports = { |
|||
meta: { |
|||
docs: { |
|||
description: "disallow calling some `Object.prototype` methods directly on objects", |
|||
category: "Possible Errors", |
|||
recommended: false |
|||
} |
|||
}, |
|||
|
|||
create: function(context) { |
|||
var DISALLOWED_PROPS = [ |
|||
"hasOwnProperty", |
|||
"isPrototypeOf", |
|||
"propertyIsEnumerable" |
|||
]; |
|||
|
|||
/** |
|||
* Reports if a disallowed property is used in a CallExpression |
|||
* @param {ASTNode} node The CallExpression node. |
|||
* @returns {void} |
|||
*/ |
|||
function disallowBuiltIns(node) { |
|||
if (node.callee.type !== "MemberExpression" || node.callee.computed) { |
|||
return; |
|||
} |
|||
var propName = node.callee.property.name; |
|||
|
|||
if (DISALLOWED_PROPS.indexOf(propName) > -1) { |
|||
context.report({ |
|||
message: "Do not access Object.prototype method '{{prop}}' from target object.", |
|||
loc: node.callee.property.loc.start, |
|||
data: {prop: propName}, |
|||
node: node |
|||
}); |
|||
} |
|||
} |
|||
|
|||
return { |
|||
CallExpression: disallowBuiltIns |
|||
}; |
|||
} |
|||
}; |
@ -0,0 +1,150 @@ |
|||
/** |
|||
* @fileoverview Disallow renaming import, export, and destructured assignments to the same name. |
|||
* @author Kai Cataldo |
|||
*/ |
|||
|
|||
"use strict"; |
|||
|
|||
//------------------------------------------------------------------------------
|
|||
// Rule Definition
|
|||
//------------------------------------------------------------------------------
|
|||
|
|||
module.exports = { |
|||
meta: { |
|||
docs: { |
|||
description: "disallow renaming import, export, and destructured assignments to the same name", |
|||
category: "ECMAScript 6", |
|||
recommended: false |
|||
}, |
|||
fixable: "code", |
|||
schema: [ |
|||
{ |
|||
type: "object", |
|||
properties: { |
|||
ignoreDestructuring: { type: "boolean" }, |
|||
ignoreImport: { type: "boolean" }, |
|||
ignoreExport: { type: "boolean" } |
|||
}, |
|||
additionalProperties: false |
|||
} |
|||
] |
|||
}, |
|||
|
|||
create: function(context) { |
|||
var options = context.options[0] || {}, |
|||
ignoreDestructuring = options.ignoreDestructuring === true, |
|||
ignoreImport = options.ignoreImport === true, |
|||
ignoreExport = options.ignoreExport === true; |
|||
|
|||
//--------------------------------------------------------------------------
|
|||
// Helpers
|
|||
//--------------------------------------------------------------------------
|
|||
|
|||
/** |
|||
* Reports error for unnecessarily renamed assignments |
|||
* @param {ASTNode} node - node to report |
|||
* @param {ASTNode} initial - node with initial name value |
|||
* @param {ASTNode} result - node with new name value |
|||
* @param {string} type - the type of the offending node |
|||
* @returns {void} |
|||
*/ |
|||
function reportError(node, initial, result, type) { |
|||
var name = initial.type === "Identifier" ? initial.name : initial.value; |
|||
|
|||
return context.report({ |
|||
node: node, |
|||
message: "{{type}} {{name}} unnecessarily renamed.", |
|||
data: { |
|||
name: name, |
|||
type: type |
|||
}, |
|||
fix: function(fixer) { |
|||
return fixer.replaceTextRange([ |
|||
initial.range[0], |
|||
result.range[1] |
|||
], name); |
|||
} |
|||
}); |
|||
} |
|||
|
|||
/** |
|||
* Checks whether a destructured assignment is unnecessarily renamed |
|||
* @param {ASTNode} node - node to check |
|||
* @returns {void} |
|||
*/ |
|||
function checkDestructured(node) { |
|||
var properties, |
|||
i; |
|||
|
|||
if (ignoreDestructuring) { |
|||
return; |
|||
} |
|||
|
|||
properties = node.properties; |
|||
|
|||
for (i = 0; i < properties.length; i++) { |
|||
if (properties[i].shorthand) { |
|||
continue; |
|||
} |
|||
|
|||
/** |
|||
* If an ObjectPattern property is computed, we have no idea |
|||
* if a rename is useless or not. If an ObjectPattern property |
|||
* lacks a key, it is likely an ExperimentalRestProperty and |
|||
* so there is no "renaming" occurring here. |
|||
*/ |
|||
if (properties[i].computed || !properties[i].key) { |
|||
continue; |
|||
} |
|||
|
|||
if (properties[i].key.type === "Identifier" && properties[i].key.name === properties[i].value.name || |
|||
properties[i].key.type === "Literal" && properties[i].key.value === properties[i].value.name) { |
|||
reportError(properties[i], properties[i].key, properties[i].value, "Destructuring assignment"); |
|||
} |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* Checks whether an import is unnecessarily renamed |
|||
* @param {ASTNode} node - node to check |
|||
* @returns {void} |
|||
*/ |
|||
function checkImport(node) { |
|||
if (ignoreImport) { |
|||
return; |
|||
} |
|||
|
|||
if (node.imported.name === node.local.name && |
|||
node.imported.range[0] !== node.local.range[0]) { |
|||
reportError(node, node.imported, node.local, "Import"); |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* Checks whether an export is unnecessarily renamed |
|||
* @param {ASTNode} node - node to check |
|||
* @returns {void} |
|||
*/ |
|||
function checkExport(node) { |
|||
if (ignoreExport) { |
|||
return; |
|||
} |
|||
|
|||
if (node.local.name === node.exported.name && |
|||
node.local.range[0] !== node.exported.range[0]) { |
|||
reportError(node, node.local, node.exported, "Export"); |
|||
} |
|||
|
|||
} |
|||
|
|||
//--------------------------------------------------------------------------
|
|||
// Public
|
|||
//--------------------------------------------------------------------------
|
|||
|
|||
return { |
|||
ObjectPattern: checkDestructured, |
|||
ImportSpecifier: checkImport, |
|||
ExportSpecifier: checkExport |
|||
}; |
|||
} |
|||
}; |
@ -0,0 +1,209 @@ |
|||
/** |
|||
* @fileoverview Rule to require or disallow line breaks inside braces. |
|||
* @author Toru Nagashima |
|||
*/ |
|||
|
|||
"use strict"; |
|||
|
|||
//------------------------------------------------------------------------------
|
|||
// Requirements
|
|||
//------------------------------------------------------------------------------
|
|||
|
|||
var astUtils = require("../ast-utils"); |
|||
|
|||
//------------------------------------------------------------------------------
|
|||
// Helpers
|
|||
//------------------------------------------------------------------------------
|
|||
|
|||
// Schema objects.
|
|||
var OPTION_VALUE = { |
|||
oneOf: [ |
|||
{ |
|||
enum: ["always", "never"] |
|||
}, |
|||
{ |
|||
type: "object", |
|||
properties: { |
|||
multiline: { |
|||
type: "boolean" |
|||
}, |
|||
minProperties: { |
|||
type: "integer", |
|||
minimum: 0 |
|||
} |
|||
}, |
|||
additionalProperties: false, |
|||
minProperties: 1 |
|||
} |
|||
] |
|||
}; |
|||
|
|||
/** |
|||
* Normalizes a given option value. |
|||
* |
|||
* @param {string|object|undefined} value - An option value to parse. |
|||
* @returns {{multiline: boolean, minProperties: number}} Normalized option object. |
|||
*/ |
|||
function normalizeOptionValue(value) { |
|||
var multiline = false; |
|||
var minProperties = Number.POSITIVE_INFINITY; |
|||
|
|||
if (value) { |
|||
if (value === "always") { |
|||
minProperties = 0; |
|||
} else if (value === "never") { |
|||
minProperties = Number.POSITIVE_INFINITY; |
|||
} else { |
|||
multiline = Boolean(value.multiline); |
|||
minProperties = value.minProperties || Number.POSITIVE_INFINITY; |
|||
} |
|||
} else { |
|||
multiline = true; |
|||
} |
|||
|
|||
return {multiline: multiline, minProperties: minProperties}; |
|||
} |
|||
|
|||
/** |
|||
* Normalizes a given option value. |
|||
* |
|||
* @param {string|object|undefined} options - An option value to parse. |
|||
* @returns {{ObjectExpression: {multiline: boolean, minProperties: number}, ObjectPattern: {multiline: boolean, minProperties: number}}} Normalized option object. |
|||
*/ |
|||
function normalizeOptions(options) { |
|||
if (options && (options.ObjectExpression || options.ObjectPattern)) { |
|||
return { |
|||
ObjectExpression: normalizeOptionValue(options.ObjectExpression), |
|||
ObjectPattern: normalizeOptionValue(options.ObjectPattern) |
|||
}; |
|||
} |
|||
|
|||
var value = normalizeOptionValue(options); |
|||
|
|||
return {ObjectExpression: value, ObjectPattern: value}; |
|||
} |
|||
|
|||
//------------------------------------------------------------------------------
|
|||
// Rule Definition
|
|||
//------------------------------------------------------------------------------
|
|||
|
|||
module.exports = { |
|||
meta: { |
|||
docs: { |
|||
description: "enforce consistent line breaks inside braces", |
|||
category: "Stylistic Issues", |
|||
recommended: false |
|||
}, |
|||
fixable: "whitespace", |
|||
schema: [ |
|||
{ |
|||
oneOf: [ |
|||
OPTION_VALUE, |
|||
{ |
|||
type: "object", |
|||
properties: { |
|||
ObjectExpression: OPTION_VALUE, |
|||
ObjectPattern: OPTION_VALUE |
|||
}, |
|||
additionalProperties: false, |
|||
minProperties: 1 |
|||
} |
|||
] |
|||
} |
|||
] |
|||
}, |
|||
|
|||
create: function(context) { |
|||
var sourceCode = context.getSourceCode(); |
|||
var normalizedOptions = normalizeOptions(context.options[0]); |
|||
|
|||
/** |
|||
* Reports a given node if it violated this rule. |
|||
* |
|||
* @param {ASTNode} node - A node to check. This is an ObjectExpression node or an ObjectPattern node. |
|||
* @param {{multiline: boolean, minProperties: number}} options - An option object. |
|||
* @returns {void} |
|||
*/ |
|||
function check(node) { |
|||
var options = normalizedOptions[node.type]; |
|||
var openBrace = sourceCode.getFirstToken(node); |
|||
var closeBrace = sourceCode.getLastToken(node); |
|||
var first = sourceCode.getTokenOrCommentAfter(openBrace); |
|||
var last = sourceCode.getTokenOrCommentBefore(closeBrace); |
|||
var needsLinebreaks = ( |
|||
node.properties.length >= options.minProperties || |
|||
( |
|||
options.multiline && |
|||
node.properties.length > 0 && |
|||
first.loc.start.line !== last.loc.end.line |
|||
) |
|||
); |
|||
|
|||
/* |
|||
* Use tokens or comments to check multiline or not. |
|||
* But use only tokens to check whether line breaks are needed. |
|||
* This allows: |
|||
* var obj = { // eslint-disable-line foo
|
|||
* a: 1 |
|||
* } |
|||
*/ |
|||
first = sourceCode.getTokenAfter(openBrace); |
|||
last = sourceCode.getTokenBefore(closeBrace); |
|||
|
|||
if (needsLinebreaks) { |
|||
if (astUtils.isTokenOnSameLine(openBrace, first)) { |
|||
context.report({ |
|||
message: "Expected a line break after this open brace.", |
|||
node: node, |
|||
loc: openBrace.loc.start, |
|||
fix: function(fixer) { |
|||
return fixer.insertTextAfter(openBrace, "\n"); |
|||
} |
|||
}); |
|||
} |
|||
if (astUtils.isTokenOnSameLine(last, closeBrace)) { |
|||
context.report({ |
|||
message: "Expected a line break before this close brace.", |
|||
node: node, |
|||
loc: closeBrace.loc.start, |
|||
fix: function(fixer) { |
|||
return fixer.insertTextBefore(closeBrace, "\n"); |
|||
} |
|||
}); |
|||
} |
|||
} else { |
|||
if (!astUtils.isTokenOnSameLine(openBrace, first)) { |
|||
context.report({ |
|||
message: "Unexpected a line break after this open brace.", |
|||
node: node, |
|||
loc: openBrace.loc.start, |
|||
fix: function(fixer) { |
|||
return fixer.removeRange([ |
|||
openBrace.range[1], |
|||
first.range[0] |
|||
]); |
|||
} |
|||
}); |
|||
} |
|||
if (!astUtils.isTokenOnSameLine(last, closeBrace)) { |
|||
context.report({ |
|||
message: "Unexpected a line break before this close brace.", |
|||
node: node, |
|||
loc: closeBrace.loc.start, |
|||
fix: function(fixer) { |
|||
return fixer.removeRange([ |
|||
last.range[1], |
|||
closeBrace.range[0] |
|||
]); |
|||
} |
|||
}); |
|||
} |
|||
} |
|||
} |
|||
|
|||
return { |
|||
ObjectExpression: check, |
|||
ObjectPattern: check |
|||
}; |
|||
} |
|||
}; |
@ -0,0 +1,73 @@ |
|||
/** |
|||
* @fileoverview Rule to enforce placing object properties on separate lines. |
|||
* @author Vitor Balocco |
|||
*/ |
|||
|
|||
"use strict"; |
|||
|
|||
//------------------------------------------------------------------------------
|
|||
// Rule Definition
|
|||
//------------------------------------------------------------------------------
|
|||
|
|||
module.exports = { |
|||
meta: { |
|||
docs: { |
|||
description: "enforce placing object properties on separate lines", |
|||
category: "Stylistic Issues", |
|||
recommended: false |
|||
}, |
|||
|
|||
schema: [ |
|||
{ |
|||
type: "object", |
|||
properties: { |
|||
allowMultiplePropertiesPerLine: { |
|||
type: "boolean" |
|||
} |
|||
}, |
|||
additionalProperties: false |
|||
} |
|||
] |
|||
}, |
|||
|
|||
create: function(context) { |
|||
var allowSameLine = context.options[0] && Boolean(context.options[0].allowMultiplePropertiesPerLine); |
|||
var errorMessage = allowSameLine ? |
|||
"Object properties must go on a new line if they aren't all on the same line" : |
|||
"Object properties must go on a new line"; |
|||
|
|||
var sourceCode = context.getSourceCode(); |
|||
|
|||
return { |
|||
ObjectExpression: function(node) { |
|||
var lastTokenOfPreviousProperty, firstTokenOfCurrentProperty; |
|||
|
|||
if (allowSameLine) { |
|||
if (node.properties.length > 1) { |
|||
var firstTokenOfFirstProperty = sourceCode.getFirstToken(node.properties[0]); |
|||
var lastTokenOfLastProperty = sourceCode.getLastToken(node.properties[node.properties.length - 1]); |
|||
|
|||
if (firstTokenOfFirstProperty.loc.end.line === lastTokenOfLastProperty.loc.start.line) { |
|||
|
|||
// All keys and values are on the same line
|
|||
return; |
|||
} |
|||
} |
|||
} |
|||
|
|||
for (var i = 1; i < node.properties.length; i++) { |
|||
lastTokenOfPreviousProperty = sourceCode.getLastToken(node.properties[i - 1]); |
|||
firstTokenOfCurrentProperty = sourceCode.getFirstToken(node.properties[i]); |
|||
|
|||
if (lastTokenOfPreviousProperty.loc.end.line === firstTokenOfCurrentProperty.loc.start.line) { |
|||
context.report({ |
|||
node: node, |
|||
loc: firstTokenOfCurrentProperty.loc.start, |
|||
message: errorMessage |
|||
}); |
|||
} |
|||
} |
|||
} |
|||
}; |
|||
} |
|||
}; |
@ -0,0 +1,107 @@ |
|||
/** |
|||
* @fileoverview Enforce spacing between rest and spread operators and their expressions. |
|||
* @author Kai Cataldo |
|||
*/ |
|||
|
|||
"use strict"; |
|||
|
|||
//------------------------------------------------------------------------------
|
|||
// Rule Definition
|
|||
//------------------------------------------------------------------------------
|
|||
|
|||
module.exports = { |
|||
meta: { |
|||
docs: { |
|||
description: "enforce spacing between rest and spread operators and their expressions", |
|||
category: "ECMAScript 6", |
|||
recommended: false |
|||
}, |
|||
fixable: "whitespace", |
|||
schema: [ |
|||
{ |
|||
enum: ["always", "never"] |
|||
} |
|||
] |
|||
}, |
|||
|
|||
create: function(context) { |
|||
var sourceCode = context.getSourceCode(), |
|||
alwaysSpace = context.options[0] === "always"; |
|||
|
|||
//--------------------------------------------------------------------------
|
|||
// Helpers
|
|||
//--------------------------------------------------------------------------
|
|||
|
|||
/** |
|||
* Checks whitespace between rest/spread operators and their expressions |
|||
* @param {ASTNode} node - The node to check |
|||
* @returns {void} |
|||
*/ |
|||
function checkWhiteSpace(node) { |
|||
var operator = sourceCode.getFirstToken(node), |
|||
nextToken = sourceCode.getTokenAfter(operator), |
|||
hasWhitespace = sourceCode.isSpaceBetweenTokens(operator, nextToken), |
|||
type; |
|||
|
|||
switch (node.type) { |
|||
case "SpreadElement": |
|||
type = "spread"; |
|||
break; |
|||
case "RestElement": |
|||
type = "rest"; |
|||
break; |
|||
case "ExperimentalSpreadProperty": |
|||
type = "spread property"; |
|||
break; |
|||
case "ExperimentalRestProperty": |
|||
type = "rest property"; |
|||
break; |
|||
default: |
|||
return; |
|||
} |
|||
|
|||
if (alwaysSpace && !hasWhitespace) { |
|||
context.report({ |
|||
node: node, |
|||
loc: { |
|||
line: operator.loc.end.line, |
|||
column: operator.loc.end.column |
|||
}, |
|||
message: "Expected whitespace after {{type}} operator", |
|||
data: { |
|||
type: type |
|||
}, |
|||
fix: function(fixer) { |
|||
return fixer.replaceTextRange([operator.range[1], nextToken.range[0]], " "); |
|||
} |
|||
}); |
|||
} else if (!alwaysSpace && hasWhitespace) { |
|||
context.report({ |
|||
node: node, |
|||
loc: { |
|||
line: operator.loc.end.line, |
|||
column: operator.loc.end.column |
|||
}, |
|||
message: "Unexpected whitespace after {{type}} operator", |
|||
data: { |
|||
type: type |
|||
}, |
|||
fix: function(fixer) { |
|||
return fixer.removeRange([operator.range[1], nextToken.range[0]]); |
|||
} |
|||
}); |
|||
} |
|||
} |
|||
|
|||
//--------------------------------------------------------------------------
|
|||
// Public
|
|||
//--------------------------------------------------------------------------
|
|||
|
|||
return { |
|||
SpreadElement: checkWhiteSpace, |
|||
RestElement: checkWhiteSpace, |
|||
ExperimentalSpreadProperty: checkWhiteSpace, |
|||
ExperimentalRestProperty: checkWhiteSpace |
|||
}; |
|||
} |
|||
}; |
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue