| /** |
| * @fileoverview Rule to enforce grouped require statements for Node.JS |
| * @author Raphael Pigulla |
| * @deprecated in ESLint v7.0.0 |
| */ |
| |
| "use strict"; |
| |
| //------------------------------------------------------------------------------ |
| // Rule Definition |
| //------------------------------------------------------------------------------ |
| |
| /** @type {import('../types').Rule.RuleModule} */ |
| module.exports = { |
| meta: { |
| deprecated: { |
| message: "Node.js rules were moved out of ESLint core.", |
| url: "https://eslint.org/docs/latest/use/migrating-to-7.0.0#deprecate-node-rules", |
| deprecatedSince: "7.0.0", |
| availableUntil: "11.0.0", |
| replacedBy: [ |
| { |
| message: |
| "eslint-plugin-n now maintains deprecated Node.js-related rules.", |
| plugin: { |
| name: "eslint-plugin-n", |
| url: "https://github.com/eslint-community/eslint-plugin-n", |
| }, |
| rule: { |
| name: "no-mixed-requires", |
| url: "https://github.com/eslint-community/eslint-plugin-n/tree/master/docs/rules/no-mixed-requires.md", |
| }, |
| }, |
| ], |
| }, |
| |
| type: "suggestion", |
| |
| docs: { |
| description: |
| "Disallow `require` calls to be mixed with regular variable declarations", |
| recommended: false, |
| url: "https://eslint.org/docs/latest/rules/no-mixed-requires", |
| }, |
| |
| schema: [ |
| { |
| oneOf: [ |
| { |
| type: "boolean", |
| }, |
| { |
| type: "object", |
| properties: { |
| grouping: { |
| type: "boolean", |
| }, |
| allowCall: { |
| type: "boolean", |
| }, |
| }, |
| additionalProperties: false, |
| }, |
| ], |
| }, |
| ], |
| |
| messages: { |
| noMixRequire: "Do not mix 'require' and other declarations.", |
| noMixCoreModuleFileComputed: |
| "Do not mix core, module, file and computed requires.", |
| }, |
| }, |
| |
| 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.includes(arg.value)) { |
| // "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, |
| messageId: "noMixRequire", |
| }); |
| } else if (grouping && !isGrouped(node.declarations)) { |
| context.report({ |
| node, |
| messageId: "noMixCoreModuleFileComputed", |
| }); |
| } |
| }, |
| }; |
| }, |
| }; |