123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206 |
- /**
- * @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
- }
- ]
- };
- /** @type {import('../shared/types').Rule} */
- module.exports = {
- meta: {
- type: "layout",
- docs: {
- description: "Enforce consistent spacing around `*` operators in generator functions",
- 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
- };
- }
- };
|