You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
206 lines
6.9 KiB
206 lines
6.9 KiB
/** |
|
* @fileoverview Rule to check the spacing around the * in generator functions. |
|
* @author Jamund Ferguson |
|
*/ |
|
|
|
"use strict"; |
|
|
|
//------------------------------------------------------------------------------ |
|
// Rule Definition |
|
//------------------------------------------------------------------------------ |
|
|
|
const OVERRIDE_SCHEMA = { |
|
oneOf: [ |
|
{ |
|
enum: ["before", "after", "both", "neither"] |
|
}, |
|
{ |
|
type: "object", |
|
properties: { |
|
before: { type: "boolean" }, |
|
after: { type: "boolean" } |
|
}, |
|
additionalProperties: false |
|
} |
|
] |
|
}; |
|
|
|
module.exports = { |
|
meta: { |
|
type: "layout", |
|
|
|
docs: { |
|
description: "enforce consistent spacing around `*` operators in generator functions", |
|
category: "ECMAScript 6", |
|
recommended: false, |
|
url: "https://eslint.org/docs/rules/generator-star-spacing" |
|
}, |
|
|
|
fixable: "whitespace", |
|
|
|
schema: [ |
|
{ |
|
oneOf: [ |
|
{ |
|
enum: ["before", "after", "both", "neither"] |
|
}, |
|
{ |
|
type: "object", |
|
properties: { |
|
before: { type: "boolean" }, |
|
after: { type: "boolean" }, |
|
named: OVERRIDE_SCHEMA, |
|
anonymous: OVERRIDE_SCHEMA, |
|
method: OVERRIDE_SCHEMA |
|
}, |
|
additionalProperties: false |
|
} |
|
] |
|
} |
|
], |
|
|
|
messages: { |
|
missingBefore: "Missing space before *.", |
|
missingAfter: "Missing space after *.", |
|
unexpectedBefore: "Unexpected space before *.", |
|
unexpectedAfter: "Unexpected space after *." |
|
} |
|
}, |
|
|
|
create(context) { |
|
|
|
const optionDefinitions = { |
|
before: { before: true, after: false }, |
|
after: { before: false, after: true }, |
|
both: { before: true, after: true }, |
|
neither: { before: false, after: false } |
|
}; |
|
|
|
/** |
|
* Returns resolved option definitions based on an option and defaults |
|
* @param {any} option The option object or string value |
|
* @param {Object} defaults The defaults to use if options are not present |
|
* @returns {Object} the resolved object definition |
|
*/ |
|
function optionToDefinition(option, defaults) { |
|
if (!option) { |
|
return defaults; |
|
} |
|
|
|
return typeof option === "string" |
|
? optionDefinitions[option] |
|
: Object.assign({}, defaults, option); |
|
} |
|
|
|
const modes = (function(option) { |
|
const defaults = optionToDefinition(option, optionDefinitions.before); |
|
|
|
return { |
|
named: optionToDefinition(option.named, defaults), |
|
anonymous: optionToDefinition(option.anonymous, defaults), |
|
method: optionToDefinition(option.method, defaults) |
|
}; |
|
}(context.options[0] || {})); |
|
|
|
const sourceCode = context.getSourceCode(); |
|
|
|
/** |
|
* Checks if the given token is a star token or not. |
|
* @param {Token} token The token to check. |
|
* @returns {boolean} `true` if the token is a star token. |
|
*/ |
|
function isStarToken(token) { |
|
return token.value === "*" && token.type === "Punctuator"; |
|
} |
|
|
|
/** |
|
* Gets the generator star token of the given function node. |
|
* @param {ASTNode} node The function node to get. |
|
* @returns {Token} Found star token. |
|
*/ |
|
function getStarToken(node) { |
|
return sourceCode.getFirstToken( |
|
(node.parent.method || node.parent.type === "MethodDefinition") ? node.parent : node, |
|
isStarToken |
|
); |
|
} |
|
|
|
/** |
|
* capitalize a given string. |
|
* @param {string} str the given string. |
|
* @returns {string} the capitalized string. |
|
*/ |
|
function capitalize(str) { |
|
return str[0].toUpperCase() + str.slice(1); |
|
} |
|
|
|
/** |
|
* Checks the spacing between two tokens before or after the star token. |
|
* @param {string} kind Either "named", "anonymous", or "method" |
|
* @param {string} side Either "before" or "after". |
|
* @param {Token} leftToken `function` keyword token if side is "before", or |
|
* star token if side is "after". |
|
* @param {Token} rightToken Star token if side is "before", or identifier |
|
* token if side is "after". |
|
* @returns {void} |
|
*/ |
|
function checkSpacing(kind, side, leftToken, rightToken) { |
|
if (!!(rightToken.range[0] - leftToken.range[1]) !== modes[kind][side]) { |
|
const after = leftToken.value === "*"; |
|
const spaceRequired = modes[kind][side]; |
|
const node = after ? leftToken : rightToken; |
|
const messageId = `${spaceRequired ? "missing" : "unexpected"}${capitalize(side)}`; |
|
|
|
context.report({ |
|
node, |
|
messageId, |
|
fix(fixer) { |
|
if (spaceRequired) { |
|
if (after) { |
|
return fixer.insertTextAfter(node, " "); |
|
} |
|
return fixer.insertTextBefore(node, " "); |
|
} |
|
return fixer.removeRange([leftToken.range[1], rightToken.range[0]]); |
|
} |
|
}); |
|
} |
|
} |
|
|
|
/** |
|
* Enforces the spacing around the star if node is a generator function. |
|
* @param {ASTNode} node A function expression or declaration node. |
|
* @returns {void} |
|
*/ |
|
function checkFunction(node) { |
|
if (!node.generator) { |
|
return; |
|
} |
|
|
|
const starToken = getStarToken(node); |
|
const prevToken = sourceCode.getTokenBefore(starToken); |
|
const nextToken = sourceCode.getTokenAfter(starToken); |
|
|
|
let kind = "named"; |
|
|
|
if (node.parent.type === "MethodDefinition" || (node.parent.type === "Property" && node.parent.method)) { |
|
kind = "method"; |
|
} else if (!node.id) { |
|
kind = "anonymous"; |
|
} |
|
|
|
// Only check before when preceded by `function`|`static` keyword |
|
if (!(kind === "method" && starToken === sourceCode.getFirstToken(node.parent))) { |
|
checkSpacing(kind, "before", prevToken, starToken); |
|
} |
|
|
|
checkSpacing(kind, "after", starToken, nextToken); |
|
} |
|
|
|
return { |
|
FunctionDeclaration: checkFunction, |
|
FunctionExpression: checkFunction |
|
}; |
|
|
|
} |
|
};
|
|
|