tools: add no-duplicate-requires rule

PR-URL: https://github.com/nodejs/node/pull/21712
Reviewed-By: Richard Lau <riclau@uk.ibm.com>
Reviewed-By: Tiancheng "Timothy" Gu <timothygu99@gmail.com>
Reviewed-By: Weijia Wang <starkwang@126.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Jon Moss <me@jonathanmoss.me>
This commit is contained in:
Gus Caplan 2018-07-07 23:32:23 -05:00
parent 1849a2b2e9
commit 8cae9b2ff8
No known key found for this signature in database
GPG Key ID: F00BD11880E82F0E
8 changed files with 116 additions and 11 deletions

View File

@ -98,6 +98,7 @@ module.exports = {
'no-dupe-class-members': 'error', 'no-dupe-class-members': 'error',
'no-dupe-keys': 'error', 'no-dupe-keys': 'error',
'no-duplicate-case': 'error', 'no-duplicate-case': 'error',
'no-duplicate-imports': 'error',
'no-empty-character-class': 'error', 'no-empty-character-class': 'error',
'no-ex-assign': 'error', 'no-ex-assign': 'error',
'no-extra-boolean-cast': 'error', 'no-extra-boolean-cast': 'error',
@ -246,6 +247,7 @@ module.exports = {
// Custom rules from eslint-plugin-node-core // Custom rules from eslint-plugin-node-core
'node-core/no-unescaped-regexp-dot': 'error', 'node-core/no-unescaped-regexp-dot': 'error',
'node-core/no-duplicate-requires': 'error',
}, },
globals: { globals: {
Atomics: false, Atomics: false,

View File

@ -1,9 +1,11 @@
'use strict'; 'use strict';
const common = require('../common.js'); const common = require('../common.js');
const Duplex = require('stream').Duplex; const {
const Readable = require('stream').Readable; Duplex,
const Transform = require('stream').Transform; Readable,
const Writable = require('stream').Writable; Transform,
Writable,
} = require('stream');
const bench = common.createBenchmark(main, { const bench = common.createBenchmark(main, {
n: [50e6], n: [50e6],

View File

@ -286,7 +286,7 @@ For example:
```js ```js
const assert = require('assert'); const assert = require('assert');
const { const {
Worker, MessageChannel, MessagePort, isMainThread Worker, MessageChannel, MessagePort, isMainThread, parentPort
} = require('worker_threads'); } = require('worker_threads');
if (isMainThread) { if (isMainThread) {
const worker = new Worker(__filename); const worker = new Worker(__filename);
@ -296,7 +296,7 @@ if (isMainThread) {
console.log('received:', value); console.log('received:', value);
}); });
} else { } else {
require('worker_threads').once('message', (value) => { parentPort.once('message', (value) => {
assert(value.hereIsYourPort instanceof MessagePort); assert(value.hereIsYourPort instanceof MessagePort);
value.hereIsYourPort.postMessage('the worker is sending this'); value.hereIsYourPort.postMessage('the worker is sending this');
value.hereIsYourPort.close(); value.hereIsYourPort.close();

View File

@ -24,8 +24,12 @@ const { kIncomingMessage } = require('_http_common');
const { kServerResponse } = require('_http_server'); const { kServerResponse } = require('_http_server');
const { StreamWrap } = require('_stream_wrap'); const { StreamWrap } = require('_stream_wrap');
const { defaultTriggerAsyncIdScope } = require('internal/async_hooks'); const {
const { async_id_symbol } = require('internal/async_hooks').symbols; defaultTriggerAsyncIdScope,
symbols: {
async_id_symbol,
},
} = require('internal/async_hooks');
const { internalBinding } = require('internal/bootstrap/loaders'); const { internalBinding } = require('internal/bootstrap/loaders');
const { const {
codes: { codes: {

View File

@ -14,13 +14,14 @@ const fs = require('fs');
const { _makeLong } = require('path'); const { _makeLong } = require('path');
const { SafeMap } = require('internal/safe_globals'); const { SafeMap } = require('internal/safe_globals');
const { URL } = require('url'); const { URL } = require('url');
const util = require('util'); const { debuglog, promisify } = require('util');
const debug = util.debuglog('esm'); const readFileAsync = promisify(fs.readFile);
const readFileAsync = util.promisify(fs.readFile);
const readFileSync = fs.readFileSync; const readFileSync = fs.readFileSync;
const StringReplace = Function.call.bind(String.prototype.replace); const StringReplace = Function.call.bind(String.prototype.replace);
const JsonParse = JSON.parse; const JsonParse = JSON.parse;
const debug = debuglog('esm');
const translators = new SafeMap(); const translators = new SafeMap();
module.exports = translators; module.exports = translators;

View File

@ -18,6 +18,7 @@ rules:
node-core/number-isnan: error node-core/number-isnan: error
## common module is mandatory in tests ## common module is mandatory in tests
node-core/required-modules: [error, common] node-core/required-modules: [error, common]
node-core/no-duplicate-requires: off
no-restricted-syntax: no-restricted-syntax:
# Config copied from .eslintrc.js # Config copied from .eslintrc.js

View File

@ -0,0 +1,25 @@
'use strict';
const common = require('../common');
common.skipIfEslintMissing();
const { RuleTester } = require('../../tools/node_modules/eslint');
const rule = require('../../tools/eslint-rules/no-duplicate-requires');
new RuleTester().run('no-duplicate-requires', rule, {
valid: [
{
code: 'require("a"); require("b"); (function() { require("a"); });',
},
{
code: 'require(a); require(a);',
},
],
invalid: [
{
code: 'require("a"); require("a");',
errors: [{ message: '\'a\' require is duplicated.' }],
},
],
});

View File

@ -0,0 +1,70 @@
/**
* @fileoverview Ensure modules are not required twice at top level of a module
* @author devsnek
*/
'use strict';
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
function isString(node) {
return node && node.type === 'Literal' && typeof node.value === 'string';
}
function isRequireCall(node) {
return node.callee.type === 'Identifier' && node.callee.name === 'require';
}
function isTopLevel(node) {
do {
if (node.type === 'FunctionDeclaration' ||
node.type === 'FunctionExpression' ||
node.type === 'ArrowFunctionExpression' ||
node.type === 'ClassBody' ||
node.type === 'MethodDefinition') {
return false;
}
} while (node = node.parent);
return true;
}
module.exports = (context) => {
if (context.parserOptions.sourceType === 'module') {
return {};
}
function getRequiredModuleNameFromCall(node) {
// node has arguments and first argument is string
if (node.arguments.length && isString(node.arguments[0])) {
return node.arguments[0].value.trim();
}
return undefined;
}
const required = new Set();
const rules = {
CallExpression: (node) => {
if (isRequireCall(node) && isTopLevel(node)) {
const moduleName = getRequiredModuleNameFromCall(node);
if (moduleName === undefined) {
return;
}
if (required.has(moduleName)) {
context.report(
node,
'\'{{moduleName}}\' require is duplicated.',
{ moduleName }
);
} else {
required.add(moduleName);
}
}
},
};
return rules;
};