|
|
|
|
|
|
|
|
|
|
|
|
|
"use strict"; |
|
|
|
const astUtils = require("./utils/ast-utils"); |
|
|
|
|
|
|
|
|
|
|
|
|
|
module.exports = { |
|
meta: { |
|
deprecated: true, |
|
replacedBy: [], |
|
type: "layout", |
|
|
|
docs: { |
|
description: "Enforce consistent comma style", |
|
recommended: false, |
|
url: "https://eslint.org/docs/latest/rules/comma-style" |
|
}, |
|
|
|
fixable: "code", |
|
|
|
schema: [ |
|
{ |
|
enum: ["first", "last"] |
|
}, |
|
{ |
|
type: "object", |
|
properties: { |
|
exceptions: { |
|
type: "object", |
|
additionalProperties: { |
|
type: "boolean" |
|
} |
|
} |
|
}, |
|
additionalProperties: false |
|
} |
|
], |
|
|
|
messages: { |
|
unexpectedLineBeforeAndAfterComma: "Bad line breaking before and after ','.", |
|
expectedCommaFirst: "',' should be placed first.", |
|
expectedCommaLast: "',' should be placed last." |
|
} |
|
}, |
|
|
|
create(context) { |
|
const style = context.options[0] || "last", |
|
sourceCode = context.sourceCode; |
|
const exceptions = { |
|
ArrayPattern: true, |
|
ArrowFunctionExpression: true, |
|
CallExpression: true, |
|
FunctionDeclaration: true, |
|
FunctionExpression: true, |
|
ImportDeclaration: true, |
|
ObjectPattern: true, |
|
NewExpression: true |
|
}; |
|
|
|
if (context.options.length === 2 && Object.prototype.hasOwnProperty.call(context.options[1], "exceptions")) { |
|
const keys = Object.keys(context.options[1].exceptions); |
|
|
|
for (let i = 0; i < keys.length; i++) { |
|
exceptions[keys[i]] = context.options[1].exceptions[keys[i]]; |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function getReplacedText(styleType, text) { |
|
switch (styleType) { |
|
case "between": |
|
return `,${text.replace(astUtils.LINEBREAK_MATCHER, "")}`; |
|
|
|
case "first": |
|
return `${text},`; |
|
|
|
case "last": |
|
return `,${text}`; |
|
|
|
default: |
|
return ""; |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function getFixerFunction(styleType, previousItemToken, commaToken, currentItemToken) { |
|
const text = |
|
sourceCode.text.slice(previousItemToken.range[1], commaToken.range[0]) + |
|
sourceCode.text.slice(commaToken.range[1], currentItemToken.range[0]); |
|
const range = [previousItemToken.range[1], currentItemToken.range[0]]; |
|
|
|
return function(fixer) { |
|
return fixer.replaceTextRange(range, getReplacedText(styleType, text)); |
|
}; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function validateCommaItemSpacing(previousItemToken, commaToken, currentItemToken, reportItem) { |
|
|
|
|
|
if (astUtils.isTokenOnSameLine(commaToken, currentItemToken) && |
|
astUtils.isTokenOnSameLine(previousItemToken, commaToken)) { |
|
|
|
|
|
|
|
} else if (!astUtils.isTokenOnSameLine(commaToken, currentItemToken) && |
|
!astUtils.isTokenOnSameLine(previousItemToken, commaToken)) { |
|
|
|
const comment = sourceCode.getCommentsAfter(commaToken)[0]; |
|
const styleType = comment && comment.type === "Block" && astUtils.isTokenOnSameLine(commaToken, comment) |
|
? style |
|
: "between"; |
|
|
|
|
|
context.report({ |
|
node: reportItem, |
|
loc: commaToken.loc, |
|
messageId: "unexpectedLineBeforeAndAfterComma", |
|
fix: getFixerFunction(styleType, previousItemToken, commaToken, currentItemToken) |
|
}); |
|
|
|
} else if (style === "first" && !astUtils.isTokenOnSameLine(commaToken, currentItemToken)) { |
|
|
|
context.report({ |
|
node: reportItem, |
|
loc: commaToken.loc, |
|
messageId: "expectedCommaFirst", |
|
fix: getFixerFunction(style, previousItemToken, commaToken, currentItemToken) |
|
}); |
|
|
|
} else if (style === "last" && astUtils.isTokenOnSameLine(commaToken, currentItemToken)) { |
|
|
|
context.report({ |
|
node: reportItem, |
|
loc: commaToken.loc, |
|
messageId: "expectedCommaLast", |
|
fix: getFixerFunction(style, previousItemToken, commaToken, currentItemToken) |
|
}); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function validateComma(node, property) { |
|
const items = node[property], |
|
arrayLiteral = (node.type === "ArrayExpression" || node.type === "ArrayPattern"); |
|
|
|
if (items.length > 1 || arrayLiteral) { |
|
|
|
|
|
let previousItemToken = sourceCode.getFirstToken(node); |
|
|
|
items.forEach(item => { |
|
const commaToken = item ? sourceCode.getTokenBefore(item) : previousItemToken, |
|
currentItemToken = item ? sourceCode.getFirstToken(item) : sourceCode.getTokenAfter(commaToken), |
|
reportItem = item || currentItemToken; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (astUtils.isCommaToken(commaToken)) { |
|
validateCommaItemSpacing(previousItemToken, commaToken, currentItemToken, reportItem); |
|
} |
|
|
|
if (item) { |
|
const tokenAfterItem = sourceCode.getTokenAfter(item, astUtils.isNotClosingParenToken); |
|
|
|
previousItemToken = tokenAfterItem |
|
? sourceCode.getTokenBefore(tokenAfterItem) |
|
: sourceCode.ast.tokens[sourceCode.ast.tokens.length - 1]; |
|
} else { |
|
previousItemToken = currentItemToken; |
|
} |
|
}); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (arrayLiteral) { |
|
|
|
const lastToken = sourceCode.getLastToken(node), |
|
nextToLastToken = sourceCode.getTokenBefore(lastToken); |
|
|
|
if (astUtils.isCommaToken(nextToLastToken)) { |
|
validateCommaItemSpacing( |
|
sourceCode.getTokenBefore(nextToLastToken), |
|
nextToLastToken, |
|
lastToken, |
|
lastToken |
|
); |
|
} |
|
} |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
const nodes = {}; |
|
|
|
if (!exceptions.VariableDeclaration) { |
|
nodes.VariableDeclaration = function(node) { |
|
validateComma(node, "declarations"); |
|
}; |
|
} |
|
if (!exceptions.ObjectExpression) { |
|
nodes.ObjectExpression = function(node) { |
|
validateComma(node, "properties"); |
|
}; |
|
} |
|
if (!exceptions.ObjectPattern) { |
|
nodes.ObjectPattern = function(node) { |
|
validateComma(node, "properties"); |
|
}; |
|
} |
|
if (!exceptions.ArrayExpression) { |
|
nodes.ArrayExpression = function(node) { |
|
validateComma(node, "elements"); |
|
}; |
|
} |
|
if (!exceptions.ArrayPattern) { |
|
nodes.ArrayPattern = function(node) { |
|
validateComma(node, "elements"); |
|
}; |
|
} |
|
if (!exceptions.FunctionDeclaration) { |
|
nodes.FunctionDeclaration = function(node) { |
|
validateComma(node, "params"); |
|
}; |
|
} |
|
if (!exceptions.FunctionExpression) { |
|
nodes.FunctionExpression = function(node) { |
|
validateComma(node, "params"); |
|
}; |
|
} |
|
if (!exceptions.ArrowFunctionExpression) { |
|
nodes.ArrowFunctionExpression = function(node) { |
|
validateComma(node, "params"); |
|
}; |
|
} |
|
if (!exceptions.CallExpression) { |
|
nodes.CallExpression = function(node) { |
|
validateComma(node, "arguments"); |
|
}; |
|
} |
|
if (!exceptions.ImportDeclaration) { |
|
nodes.ImportDeclaration = function(node) { |
|
validateComma(node, "specifiers"); |
|
}; |
|
} |
|
if (!exceptions.NewExpression) { |
|
nodes.NewExpression = function(node) { |
|
validateComma(node, "arguments"); |
|
}; |
|
} |
|
|
|
return nodes; |
|
} |
|
}; |
|
|