268 lines
7.0 KiB
JavaScript
268 lines
7.0 KiB
JavaScript
/**
|
|
* @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',
|
|
});
|
|
}
|
|
},
|
|
};
|
|
},
|
|
};
|