/** * @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: null, 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', }); } }, }; }, };