text: /**
   * @fileoverview Rule to enforce grouped require statements for Node.JS
   * @author Raphael Pigulla
   */
  
  "use strict";
  
  //------------------------------------------------------------------------------
  // Rule Definition
  //------------------------------------------------------------------------------
  
  module.exports = {
      meta: {
          type: "suggestion",
  
          docs: {
              description: "disallow `require` calls to be mixed with regular variable declarations",
              category: "Node.js and CommonJS",
              recommended: false,
              url: "https://eslint.org/docs/rules/no-mixed-requires"
          },
  
          schema: [
              {
                  oneOf: [
                      {
                          type: "boolean"
                      },
                      {
                          type: "object",
                          properties: {
                              grouping: {
                                  type: "boolean"
                              },
                              allowCall: {
                                  type: "boolean"
                              }
                          },
                          additionalProperties: false
                      }
                  ]
              }
          ]
      },
  
      create(context) {
  
          const options = context.options[0];
          let grouping = false,
              allowCall = false;
  
          if (typeof options === "object") {
              grouping = options.grouping;
              allowCall = options.allowCall;
          } else {
              grouping = !!options;
          }
  
          /**
           * Returns the list of built-in modules.
           *
           * @returns {string[]} An array of built-in Node.js modules.
           */
          function getBuiltinModules() {
  
              /*
               * This list is generated using:
               * `require("repl")._builtinLibs.concat('repl').sort()`
               * This particular list is as per nodejs v0.12.2 and iojs v0.7.1
               */
              return [
                  "assert", "buffer", "child_process", "cluster", "crypto",
                  "dgram", "dns", "domain", "events", "fs", "http", "https",
                  "net", "os", "path", "punycode", "querystring", "readline",
                  "repl", "smalloc", "stream", "string_decoder", "tls", "tty",
                  "url", "util", "v8", "vm", "zlib"
              ];
          }
  
          const BUILTIN_MODULES = getBuiltinModules();
  
          const DECL_REQUIRE = "require",
              DECL_UNINITIALIZED = "uninitialized",
              DECL_OTHER = "other";
  
          const REQ_CORE = "core",
              REQ_FILE = "file",
              REQ_MODULE = "module",
              REQ_COMPUTED = "computed";
  
          /**
           * Determines the type of a declaration statement.
           * @param {ASTNode} initExpression The init node of the VariableDeclarator.
           * @returns {string} The type of declaration represented by the expression.
           */
          function getDeclarationType(initExpression) {
              if (!initExpression) {
  
                  // "var x;"
                  return DECL_UNINITIALIZED;
              }
  
              if (initExpression.type === "CallExpression" &&
                  initExpression.callee.type === "Identifier" &&
                  initExpression.callee.name === "require"
              ) {
  
                  // "var x = require('util');"
                  return DECL_REQUIRE;
              }
              if (allowCall &&
                  initExpression.type === "CallExpression" &&
                  initExpression.callee.type === "CallExpression"
              ) {
  
                  // "var x = require('diagnose')('sub-module');"
                  return getDeclarationType(initExpression.callee);
              }
              if (initExpression.type === "MemberExpression") {
  
                  // "var x = require('glob').Glob;"
                  return getDeclarationType(initExpression.object);
              }
  
              // "var x = 42;"
              return DECL_OTHER;
          }
  
          /**
           * Determines the type of module that is loaded via require.
           * @param {ASTNode} initExpression The init node of the VariableDeclarator.
           * @returns {string} The module type.
           */
          function inferModuleType(initExpression) {
              if (initExpression.type === "MemberExpression") {
  
                  // "var x = require('glob').Glob;"
                  return inferModuleType(initExpression.object);
              }
              if (initExpression.arguments.length === 0) {
  
                  // "var x = require();"
                  return REQ_COMPUTED;
              }
  
              const arg = initExpression.arguments[0];
  
              if (arg.type !== "Literal" || typeof arg.value !== "string") {
  
                  // "var x = require(42);"
                  return REQ_COMPUTED;
              }
  
              if (BUILTIN_MODULES.indexOf(arg.value) !== -1) {
  
                  // "var fs = require('fs');"
                  return REQ_CORE;
              }
              if (/^\.{0,2}\//u.test(arg.value)) {
  
                  // "var utils = require('./utils');"
                  return REQ_FILE;
              }
  
              // "var async = require('async');"
              return REQ_MODULE;
  
          }
  
          /**
           * Check if the list of variable declarations is mixed, i.e. whether it
           * contains both require and other declarations.
           * @param {ASTNode} declarations The list of VariableDeclarators.
           * @returns {boolean} True if the declarations are mixed, false if not.
           */
          function isMixed(declarations) {
              const contains = {};
  
              declarations.forEach(declaration => {
                  const type = getDeclarationType(declaration.init);
  
                  contains[type] = true;
              });
  
              return !!(
                  contains[DECL_REQUIRE] &&
                  (contains[DECL_UNINITIALIZED] || contains[DECL_OTHER])
              );
          }
  
          /**
           * Check if all require declarations in the given list are of the same
           * type.
           * @param {ASTNode} declarations The list of VariableDeclarators.
           * @returns {boolean} True if the declarations are grouped, false if not.
           */
          function isGrouped(declarations) {
              const found = {};
  
              declarations.forEach(declaration => {
                  if (getDeclarationType(declaration.init) === DECL_REQUIRE) {
                      found[inferModuleType(declaration.init)] = true;
                  }
              });
  
              return Object.keys(found).length <= 1;
          }
  
  
          return {
  
              VariableDeclaration(node) {
  
                  if (isMixed(node.declarations)) {
                      context.report({ node, message: "Do not mix 'require' and other declarations." });
                  } else if (grouping && !isGrouped(node.declarations)) {
                      context.report({ node, message: "Do not mix core, module, file and computed requires." });
                  }
              }
          };
  
      }
  };
  