[d565449] | 1 | /**
|
---|
| 2 | * @fileoverview Rule to enforce grouped require statements for Node.JS
|
---|
| 3 | * @author Raphael Pigulla
|
---|
| 4 | * @deprecated in ESLint v7.0.0
|
---|
| 5 | */
|
---|
| 6 |
|
---|
| 7 | "use strict";
|
---|
| 8 |
|
---|
| 9 | //------------------------------------------------------------------------------
|
---|
| 10 | // Rule Definition
|
---|
| 11 | //------------------------------------------------------------------------------
|
---|
| 12 |
|
---|
| 13 | /** @type {import('../shared/types').Rule} */
|
---|
| 14 | module.exports = {
|
---|
| 15 | meta: {
|
---|
| 16 | deprecated: true,
|
---|
| 17 |
|
---|
| 18 | replacedBy: [],
|
---|
| 19 |
|
---|
| 20 | type: "suggestion",
|
---|
| 21 |
|
---|
| 22 | docs: {
|
---|
| 23 | description: "Disallow `require` calls to be mixed with regular variable declarations",
|
---|
| 24 | recommended: false,
|
---|
| 25 | url: "https://eslint.org/docs/latest/rules/no-mixed-requires"
|
---|
| 26 | },
|
---|
| 27 |
|
---|
| 28 | schema: [
|
---|
| 29 | {
|
---|
| 30 | oneOf: [
|
---|
| 31 | {
|
---|
| 32 | type: "boolean"
|
---|
| 33 | },
|
---|
| 34 | {
|
---|
| 35 | type: "object",
|
---|
| 36 | properties: {
|
---|
| 37 | grouping: {
|
---|
| 38 | type: "boolean"
|
---|
| 39 | },
|
---|
| 40 | allowCall: {
|
---|
| 41 | type: "boolean"
|
---|
| 42 | }
|
---|
| 43 | },
|
---|
| 44 | additionalProperties: false
|
---|
| 45 | }
|
---|
| 46 | ]
|
---|
| 47 | }
|
---|
| 48 | ],
|
---|
| 49 |
|
---|
| 50 | messages: {
|
---|
| 51 | noMixRequire: "Do not mix 'require' and other declarations.",
|
---|
| 52 | noMixCoreModuleFileComputed: "Do not mix core, module, file and computed requires."
|
---|
| 53 | }
|
---|
| 54 | },
|
---|
| 55 |
|
---|
| 56 | create(context) {
|
---|
| 57 |
|
---|
| 58 | const options = context.options[0];
|
---|
| 59 | let grouping = false,
|
---|
| 60 | allowCall = false;
|
---|
| 61 |
|
---|
| 62 | if (typeof options === "object") {
|
---|
| 63 | grouping = options.grouping;
|
---|
| 64 | allowCall = options.allowCall;
|
---|
| 65 | } else {
|
---|
| 66 | grouping = !!options;
|
---|
| 67 | }
|
---|
| 68 |
|
---|
| 69 | /**
|
---|
| 70 | * Returns the list of built-in modules.
|
---|
| 71 | * @returns {string[]} An array of built-in Node.js modules.
|
---|
| 72 | */
|
---|
| 73 | function getBuiltinModules() {
|
---|
| 74 |
|
---|
| 75 | /*
|
---|
| 76 | * This list is generated using:
|
---|
| 77 | * `require("repl")._builtinLibs.concat('repl').sort()`
|
---|
| 78 | * This particular list is as per nodejs v0.12.2 and iojs v0.7.1
|
---|
| 79 | */
|
---|
| 80 | return [
|
---|
| 81 | "assert", "buffer", "child_process", "cluster", "crypto",
|
---|
| 82 | "dgram", "dns", "domain", "events", "fs", "http", "https",
|
---|
| 83 | "net", "os", "path", "punycode", "querystring", "readline",
|
---|
| 84 | "repl", "smalloc", "stream", "string_decoder", "tls", "tty",
|
---|
| 85 | "url", "util", "v8", "vm", "zlib"
|
---|
| 86 | ];
|
---|
| 87 | }
|
---|
| 88 |
|
---|
| 89 | const BUILTIN_MODULES = getBuiltinModules();
|
---|
| 90 |
|
---|
| 91 | const DECL_REQUIRE = "require",
|
---|
| 92 | DECL_UNINITIALIZED = "uninitialized",
|
---|
| 93 | DECL_OTHER = "other";
|
---|
| 94 |
|
---|
| 95 | const REQ_CORE = "core",
|
---|
| 96 | REQ_FILE = "file",
|
---|
| 97 | REQ_MODULE = "module",
|
---|
| 98 | REQ_COMPUTED = "computed";
|
---|
| 99 |
|
---|
| 100 | /**
|
---|
| 101 | * Determines the type of a declaration statement.
|
---|
| 102 | * @param {ASTNode} initExpression The init node of the VariableDeclarator.
|
---|
| 103 | * @returns {string} The type of declaration represented by the expression.
|
---|
| 104 | */
|
---|
| 105 | function getDeclarationType(initExpression) {
|
---|
| 106 | if (!initExpression) {
|
---|
| 107 |
|
---|
| 108 | // "var x;"
|
---|
| 109 | return DECL_UNINITIALIZED;
|
---|
| 110 | }
|
---|
| 111 |
|
---|
| 112 | if (initExpression.type === "CallExpression" &&
|
---|
| 113 | initExpression.callee.type === "Identifier" &&
|
---|
| 114 | initExpression.callee.name === "require"
|
---|
| 115 | ) {
|
---|
| 116 |
|
---|
| 117 | // "var x = require('util');"
|
---|
| 118 | return DECL_REQUIRE;
|
---|
| 119 | }
|
---|
| 120 | if (allowCall &&
|
---|
| 121 | initExpression.type === "CallExpression" &&
|
---|
| 122 | initExpression.callee.type === "CallExpression"
|
---|
| 123 | ) {
|
---|
| 124 |
|
---|
| 125 | // "var x = require('diagnose')('sub-module');"
|
---|
| 126 | return getDeclarationType(initExpression.callee);
|
---|
| 127 | }
|
---|
| 128 | if (initExpression.type === "MemberExpression") {
|
---|
| 129 |
|
---|
| 130 | // "var x = require('glob').Glob;"
|
---|
| 131 | return getDeclarationType(initExpression.object);
|
---|
| 132 | }
|
---|
| 133 |
|
---|
| 134 | // "var x = 42;"
|
---|
| 135 | return DECL_OTHER;
|
---|
| 136 | }
|
---|
| 137 |
|
---|
| 138 | /**
|
---|
| 139 | * Determines the type of module that is loaded via require.
|
---|
| 140 | * @param {ASTNode} initExpression The init node of the VariableDeclarator.
|
---|
| 141 | * @returns {string} The module type.
|
---|
| 142 | */
|
---|
| 143 | function inferModuleType(initExpression) {
|
---|
| 144 | if (initExpression.type === "MemberExpression") {
|
---|
| 145 |
|
---|
| 146 | // "var x = require('glob').Glob;"
|
---|
| 147 | return inferModuleType(initExpression.object);
|
---|
| 148 | }
|
---|
| 149 | if (initExpression.arguments.length === 0) {
|
---|
| 150 |
|
---|
| 151 | // "var x = require();"
|
---|
| 152 | return REQ_COMPUTED;
|
---|
| 153 | }
|
---|
| 154 |
|
---|
| 155 | const arg = initExpression.arguments[0];
|
---|
| 156 |
|
---|
| 157 | if (arg.type !== "Literal" || typeof arg.value !== "string") {
|
---|
| 158 |
|
---|
| 159 | // "var x = require(42);"
|
---|
| 160 | return REQ_COMPUTED;
|
---|
| 161 | }
|
---|
| 162 |
|
---|
| 163 | if (BUILTIN_MODULES.includes(arg.value)) {
|
---|
| 164 |
|
---|
| 165 | // "var fs = require('fs');"
|
---|
| 166 | return REQ_CORE;
|
---|
| 167 | }
|
---|
| 168 | if (/^\.{0,2}\//u.test(arg.value)) {
|
---|
| 169 |
|
---|
| 170 | // "var utils = require('./utils');"
|
---|
| 171 | return REQ_FILE;
|
---|
| 172 | }
|
---|
| 173 |
|
---|
| 174 | // "var async = require('async');"
|
---|
| 175 | return REQ_MODULE;
|
---|
| 176 |
|
---|
| 177 | }
|
---|
| 178 |
|
---|
| 179 | /**
|
---|
| 180 | * Check if the list of variable declarations is mixed, i.e. whether it
|
---|
| 181 | * contains both require and other declarations.
|
---|
| 182 | * @param {ASTNode} declarations The list of VariableDeclarators.
|
---|
| 183 | * @returns {boolean} True if the declarations are mixed, false if not.
|
---|
| 184 | */
|
---|
| 185 | function isMixed(declarations) {
|
---|
| 186 | const contains = {};
|
---|
| 187 |
|
---|
| 188 | declarations.forEach(declaration => {
|
---|
| 189 | const type = getDeclarationType(declaration.init);
|
---|
| 190 |
|
---|
| 191 | contains[type] = true;
|
---|
| 192 | });
|
---|
| 193 |
|
---|
| 194 | return !!(
|
---|
| 195 | contains[DECL_REQUIRE] &&
|
---|
| 196 | (contains[DECL_UNINITIALIZED] || contains[DECL_OTHER])
|
---|
| 197 | );
|
---|
| 198 | }
|
---|
| 199 |
|
---|
| 200 | /**
|
---|
| 201 | * Check if all require declarations in the given list are of the same
|
---|
| 202 | * type.
|
---|
| 203 | * @param {ASTNode} declarations The list of VariableDeclarators.
|
---|
| 204 | * @returns {boolean} True if the declarations are grouped, false if not.
|
---|
| 205 | */
|
---|
| 206 | function isGrouped(declarations) {
|
---|
| 207 | const found = {};
|
---|
| 208 |
|
---|
| 209 | declarations.forEach(declaration => {
|
---|
| 210 | if (getDeclarationType(declaration.init) === DECL_REQUIRE) {
|
---|
| 211 | found[inferModuleType(declaration.init)] = true;
|
---|
| 212 | }
|
---|
| 213 | });
|
---|
| 214 |
|
---|
| 215 | return Object.keys(found).length <= 1;
|
---|
| 216 | }
|
---|
| 217 |
|
---|
| 218 |
|
---|
| 219 | return {
|
---|
| 220 |
|
---|
| 221 | VariableDeclaration(node) {
|
---|
| 222 |
|
---|
| 223 | if (isMixed(node.declarations)) {
|
---|
| 224 | context.report({
|
---|
| 225 | node,
|
---|
| 226 | messageId: "noMixRequire"
|
---|
| 227 | });
|
---|
| 228 | } else if (grouping && !isGrouped(node.declarations)) {
|
---|
| 229 | context.report({
|
---|
| 230 | node,
|
---|
| 231 | messageId: "noMixCoreModuleFileComputed"
|
---|
| 232 | });
|
---|
| 233 | }
|
---|
| 234 | }
|
---|
| 235 | };
|
---|
| 236 |
|
---|
| 237 | }
|
---|
| 238 | };
|
---|