Skip to content

Commit

Permalink
tools: add no-duplicate-requires rule
Browse files Browse the repository at this point in the history
PR-URL: #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>
  • Loading branch information
devsnek authored and targos committed Jul 14, 2018
1 parent 3096ee5 commit e030dd7
Show file tree
Hide file tree
Showing 8 changed files with 116 additions and 11 deletions.
2 changes: 2 additions & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ module.exports = {
'no-dupe-class-members': 'error',
'no-dupe-keys': 'error',
'no-duplicate-case': 'error',
'no-duplicate-imports': 'error',
'no-empty-character-class': 'error',
'no-ex-assign': 'error',
'no-extra-boolean-cast': 'error',
Expand Down Expand Up @@ -246,6 +247,7 @@ module.exports = {

// Custom rules from eslint-plugin-node-core
'node-core/no-unescaped-regexp-dot': 'error',
'node-core/no-duplicate-requires': 'error',
},
globals: {
Atomics: false,
Expand Down
10 changes: 6 additions & 4 deletions benchmark/streams/creation.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
'use strict';
const common = require('../common.js');
const Duplex = require('stream').Duplex;
const Readable = require('stream').Readable;
const Transform = require('stream').Transform;
const Writable = require('stream').Writable;
const {
Duplex,
Readable,
Transform,
Writable,
} = require('stream');

const bench = common.createBenchmark(main, {
n: [50e6],
Expand Down
4 changes: 2 additions & 2 deletions doc/api/worker_threads.md
Original file line number Diff line number Diff line change
Expand Up @@ -286,7 +286,7 @@ For example:
```js
const assert = require('assert');
const {
Worker, MessageChannel, MessagePort, isMainThread
Worker, MessageChannel, MessagePort, isMainThread, parentPort
} = require('worker_threads');
if (isMainThread) {
const worker = new Worker(__filename);
Expand All @@ -296,7 +296,7 @@ if (isMainThread) {
console.log('received:', value);
});
} else {
require('worker_threads').once('message', (value) => {
parentPort.once('message', (value) => {
assert(value.hereIsYourPort instanceof MessagePort);
value.hereIsYourPort.postMessage('the worker is sending this');
value.hereIsYourPort.close();
Expand Down
8 changes: 6 additions & 2 deletions lib/internal/http2/core.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,12 @@ const { kIncomingMessage } = require('_http_common');
const { kServerResponse } = require('_http_server');
const { StreamWrap } = require('_stream_wrap');

const { defaultTriggerAsyncIdScope } = require('internal/async_hooks');
const { async_id_symbol } = require('internal/async_hooks').symbols;
const {
defaultTriggerAsyncIdScope,
symbols: {
async_id_symbol,
},
} = require('internal/async_hooks');
const { internalBinding } = require('internal/bootstrap/loaders');
const {
codes: {
Expand Down
7 changes: 4 additions & 3 deletions lib/internal/modules/esm/translators.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,14 @@ const fs = require('fs');
const { _makeLong } = require('path');
const { SafeMap } = require('internal/safe_globals');
const { URL } = require('url');
const util = require('util');
const debug = util.debuglog('esm');
const readFileAsync = util.promisify(fs.readFile);
const { debuglog, promisify } = require('util');
const readFileAsync = promisify(fs.readFile);
const readFileSync = fs.readFileSync;
const StringReplace = Function.call.bind(String.prototype.replace);
const JsonParse = JSON.parse;

const debug = debuglog('esm');

const translators = new SafeMap();
module.exports = translators;

Expand Down
1 change: 1 addition & 0 deletions test/.eslintrc.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ rules:
node-core/number-isnan: error
## common module is mandatory in tests
node-core/required-modules: [error, common]
node-core/no-duplicate-requires: off

no-restricted-syntax:
# Config copied from .eslintrc.js
Expand Down
25 changes: 25 additions & 0 deletions test/parallel/test-eslint-duplicate-requires.js
Original file line number Diff line number Diff line change
@@ -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.' }],
},
],
});
70 changes: 70 additions & 0 deletions tools/eslint-rules/no-duplicate-requires.js
Original file line number Diff line number Diff line change
@@ -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;
};

0 comments on commit e030dd7

Please sign in to comment.