text: /**
   * @fileoverview Rule to validate spacing before function paren.
   * @author Mathias Schreck <https://github.com/lo1tuma>
   */
  "use strict";
  
  //------------------------------------------------------------------------------
  // Requirements
  //------------------------------------------------------------------------------
  
  const astUtils = require("./utils/ast-utils");
  
  //------------------------------------------------------------------------------
  // Rule Definition
  //------------------------------------------------------------------------------
  
  module.exports = {
      meta: {
          type: "layout",
  
          docs: {
              description: "enforce consistent spacing before `function` definition opening parenthesis",
              category: "Stylistic Issues",
              recommended: false,
              url: "https://eslint.org/docs/rules/space-before-function-paren"
          },
  
          fixable: "whitespace",
  
          schema: [
              {
                  oneOf: [
                      {
                          enum: ["always", "never"]
                      },
                      {
                          type: "object",
                          properties: {
                              anonymous: {
                                  enum: ["always", "never", "ignore"]
                              },
                              named: {
                                  enum: ["always", "never", "ignore"]
                              },
                              asyncArrow: {
                                  enum: ["always", "never", "ignore"]
                              }
                          },
                          additionalProperties: false
                      }
                  ]
              }
          ]
      },
  
      create(context) {
          const sourceCode = context.getSourceCode();
          const baseConfig = typeof context.options[0] === "string" ? context.options[0] : "always";
          const overrideConfig = typeof context.options[0] === "object" ? context.options[0] : {};
  
          /**
           * Determines whether a function has a name.
           * @param {ASTNode} node The function node.
           * @returns {boolean} Whether the function has a name.
           */
          function isNamedFunction(node) {
              if (node.id) {
                  return true;
              }
  
              const parent = node.parent;
  
              return parent.type === "MethodDefinition" ||
                  (parent.type === "Property" &&
                      (
                          parent.kind === "get" ||
                          parent.kind === "set" ||
                          parent.method
                      )
                  );
          }
  
          /**
           * Gets the config for a given function
           * @param {ASTNode} node The function node
           * @returns {string} "always", "never", or "ignore"
           */
          function getConfigForFunction(node) {
              if (node.type === "ArrowFunctionExpression") {
  
                  // Always ignore non-async functions and arrow functions without parens, e.g. async foo => bar
                  if (node.async && astUtils.isOpeningParenToken(sourceCode.getFirstToken(node, { skip: 1 }))) {
                      return overrideConfig.asyncArrow || baseConfig;
                  }
              } else if (isNamedFunction(node)) {
                  return overrideConfig.named || baseConfig;
  
              // `generator-star-spacing` should warn anonymous generators. E.g. `function* () {}`
              } else if (!node.generator) {
                  return overrideConfig.anonymous || baseConfig;
              }
  
              return "ignore";
          }
  
          /**
           * Checks the parens of a function node
           * @param {ASTNode} node A function node
           * @returns {void}
           */
          function checkFunction(node) {
              const functionConfig = getConfigForFunction(node);
  
              if (functionConfig === "ignore") {
                  return;
              }
  
              const rightToken = sourceCode.getFirstToken(node, astUtils.isOpeningParenToken);
              const leftToken = sourceCode.getTokenBefore(rightToken);
              const hasSpacing = sourceCode.isSpaceBetweenTokens(leftToken, rightToken);
  
              if (hasSpacing && functionConfig === "never") {
                  context.report({
                      node,
                      loc: leftToken.loc.end,
                      message: "Unexpected space before function parentheses.",
                      fix(fixer) {
                          const comments = sourceCode.getCommentsBefore(rightToken);
  
                          // Don't fix anything if there's a single line comment between the left and the right token
                          if (comments.some(comment => comment.type === "Line")) {
                              return null;
                          }
                          return fixer.replaceTextRange(
                              [leftToken.range[1], rightToken.range[0]],
                              comments.reduce((text, comment) => text + sourceCode.getText(comment), "")
                          );
                      }
                  });
              } else if (!hasSpacing && functionConfig === "always") {
                  context.report({
                      node,
                      loc: leftToken.loc.end,
                      message: "Missing space before function parentheses.",
                      fix: fixer => fixer.insertTextAfter(leftToken, " ")
                  });
              }
          }
  
          return {
              ArrowFunctionExpression: checkFunction,
              FunctionDeclaration: checkFunction,
              FunctionExpression: checkFunction
          };
      }
  };
  