Permalink
Cannot retrieve contributors at this time
| /** | |
| * @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." }); | |
| } | |
| } | |
| }; | |
| } | |
| }; |