Skip to content

Commit 8788a11

Browse files
author
Kai Cataldo
authoredFeb 18, 2020
Update: add ESLint core Node.js and CommonJS rules (#206)
1 parent b8f9945 commit 8788a11

31 files changed

+3233
-0
lines changed
 

‎docs/rules/callback-return.md

+164
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
# node/callback-return
2+
> require `return` statements after callbacks
3+
4+
The callback pattern is at the heart of most I/O and event-driven programming
5+
in JavaScript.
6+
7+
```js
8+
function doSomething(err, callback) {
9+
if (err) {
10+
return callback(err);
11+
}
12+
callback();
13+
}
14+
```
15+
16+
To prevent calling the callback multiple times it is important to `return` anytime the callback is triggered outside
17+
of the main function body. Neglecting this technique often leads to issues where you do something more than once.
18+
For example, in the case of an HTTP request, you may try to send HTTP headers more than once leading Node.js to `throw`
19+
a `Can't render headers after they are sent to the client.` error.
20+
21+
## 📖 Rule Details
22+
23+
This rule is aimed at ensuring that callbacks used outside of the main function block are always part-of or immediately
24+
preceding a `return` statement. This rule decides what is a callback based on the name of the function being called.
25+
26+
### Options
27+
28+
The rule takes a single option - an array of possible callback names - which may include object methods. The default callback names are `callback`, `cb`, `next`.
29+
30+
#### Default callback names
31+
32+
Examples of **incorrect** code for this rule with the default `["callback", "cb", "next"]` option:
33+
34+
```js
35+
/*eslint callback-return: "error"*/
36+
37+
function foo(err, callback) {
38+
if (err) {
39+
callback(err);
40+
}
41+
callback();
42+
}
43+
```
44+
45+
Examples of **correct** code for this rule with the default `["callback", "cb", "next"]` option:
46+
47+
```js
48+
/*eslint callback-return: "error"*/
49+
50+
function foo(err, callback) {
51+
if (err) {
52+
return callback(err);
53+
}
54+
callback();
55+
}
56+
```
57+
58+
#### Supplied callback names
59+
60+
Examples of **incorrect** code for this rule with the option `["done", "send.error", "send.success"]`:
61+
62+
```js
63+
/*eslint callback-return: ["error", ["done", "send.error", "send.success"]]*/
64+
65+
function foo(err, done) {
66+
if (err) {
67+
done(err);
68+
}
69+
done();
70+
}
71+
72+
function bar(err, send) {
73+
if (err) {
74+
send.error(err);
75+
}
76+
send.success();
77+
}
78+
```
79+
80+
Examples of **correct** code for this rule with the option `["done", "send.error", "send.success"]`:
81+
82+
```js
83+
/*eslint callback-return: ["error", ["done", "send.error", "send.success"]]*/
84+
85+
function foo(err, done) {
86+
if (err) {
87+
return done(err);
88+
}
89+
done();
90+
}
91+
92+
function bar(err, send) {
93+
if (err) {
94+
return send.error(err);
95+
}
96+
send.success();
97+
}
98+
```
99+
100+
### Known Limitations
101+
102+
Because it is difficult to understand the meaning of a program through static analysis, this rule has limitations:
103+
104+
* *false negatives* when this rule reports correct code, but the program calls the callback more than one time (which is incorrect behavior)
105+
* *false positives* when this rule reports incorrect code, but the program calls the callback only one time (which is correct behavior)
106+
107+
#### Passing the callback by reference
108+
109+
The static analysis of this rule does not detect that the program calls the callback if it is an argument of a function (for example, `setTimeout`).
110+
111+
Example of a *false negative* when this rule reports correct code:
112+
113+
```js
114+
/*eslint callback-return: "error"*/
115+
116+
function foo(err, callback) {
117+
if (err) {
118+
setTimeout(callback, 0); // this is bad, but WILL NOT warn
119+
}
120+
callback();
121+
}
122+
```
123+
124+
#### Triggering the callback within a nested function
125+
126+
The static analysis of this rule does not detect that the program calls the callback from within a nested function or an immediately-invoked function expression (IIFE).
127+
128+
Example of a *false negative* when this rule reports correct code:
129+
130+
```js
131+
/*eslint callback-return: "error"*/
132+
133+
function foo(err, callback) {
134+
if (err) {
135+
process.nextTick(function() {
136+
return callback(); // this is bad, but WILL NOT warn
137+
});
138+
}
139+
callback();
140+
}
141+
```
142+
143+
#### If/else statements
144+
145+
The static analysis of this rule does not detect that the program calls the callback only one time in each branch of an `if` statement.
146+
147+
Example of a *false positive* when this rule reports incorrect code:
148+
149+
```js
150+
/*eslint callback-return: "error"*/
151+
152+
function foo(err, callback) {
153+
if (err) {
154+
callback(err); // this is fine, but WILL warn
155+
} else {
156+
callback(); // this is fine, but WILL warn
157+
}
158+
}
159+
```
160+
161+
## 🔎 Implementation
162+
163+
- [Rule source](../../lib/rules/callback-return.js)
164+
- [Test source](../../tests/lib/rules/callback-return.js)

‎docs/rules/global-require.md

+91
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
# node/global-require
2+
> require `require()` calls to be placed at top-level module scope
3+
4+
In Node.js, module dependencies are included using the `require()` function, such as:
5+
6+
```js
7+
var fs = require("fs");
8+
```
9+
10+
While `require()` may be called anywhere in code, some style guides prescribe that it should be called only in the top level of a module to make it easier to identify dependencies. For instance, it's arguably harder to identify dependencies when they are deeply nested inside of functions and other statements:
11+
12+
```js
13+
function foo() {
14+
15+
if (condition) {
16+
var fs = require("fs");
17+
}
18+
}
19+
```
20+
21+
Since `require()` does a synchronous load, it can cause performance problems when used in other locations.
22+
23+
Further, ES6 modules mandate that `import` and `export` statements can only occur in the top level of the module's body.
24+
25+
## 📖 Rule Details
26+
27+
This rule requires all calls to `require()` to be at the top level of the module, similar to ES6 `import` and `export` statements, which also can occur only at the top level.
28+
29+
Examples of **incorrect** code for this rule:
30+
31+
```js
32+
/*eslint global-require: "error"*/
33+
/*eslint-env es6*/
34+
35+
// calling require() inside of a function is not allowed
36+
function readFile(filename, callback) {
37+
var fs = require('fs');
38+
fs.readFile(filename, callback)
39+
}
40+
41+
// conditional requires like this are also not allowed
42+
if (DEBUG) { require('debug'); }
43+
44+
// a require() in a switch statement is also flagged
45+
switch(x) { case '1': require('1'); break; }
46+
47+
// you may not require() inside an arrow function body
48+
var getModule = (name) => require(name);
49+
50+
// you may not require() inside of a function body as well
51+
function getModule(name) { return require(name); }
52+
53+
// you may not require() inside of a try/catch block
54+
try {
55+
require(unsafeModule);
56+
} catch(e) {
57+
console.log(e);
58+
}
59+
```
60+
61+
Examples of **correct** code for this rule:
62+
63+
```js
64+
/*eslint global-require: "error"*/
65+
66+
// all these variations of require() are ok
67+
require('x');
68+
var y = require('y');
69+
var z;
70+
z = require('z').initialize();
71+
72+
// requiring a module and using it in a function is ok
73+
var fs = require('fs');
74+
function readFile(filename, callback) {
75+
fs.readFile(filename, callback)
76+
}
77+
78+
// you can use a ternary to determine which module to require
79+
var logger = DEBUG ? require('dev-logger') : require('logger');
80+
81+
// if you want you can require() at the end of your module
82+
function doSomethingA() {}
83+
function doSomethingB() {}
84+
var x = require("x"),
85+
z = require("z");
86+
```
87+
88+
## 🔎 Implementation
89+
90+
- [Rule source](../../lib/rules/global-require.js)
91+
- [Test source](../../tests/lib/rules/global-require.js)

‎docs/rules/handle-callback-err.md

+78
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
# node/handle-callback-err
2+
> require error handling in callbacks
3+
4+
In Node.js, a common pattern for dealing with asynchronous behavior is called the callback pattern.
5+
This pattern expects an `Error` object or `null` as the first argument of the callback.
6+
Forgetting to handle these errors can lead to some really strange behavior in your application.
7+
8+
```js
9+
function loadData (err, data) {
10+
doSomething(); // forgot to handle error
11+
}
12+
```
13+
14+
## 📖 Rule Details
15+
16+
This rule expects that when you're using the callback pattern in Node.js you'll handle the error.
17+
18+
### Options
19+
20+
The rule takes a single string option: the name of the error parameter. The default is `"err"`.
21+
22+
Examples of **incorrect** code for this rule with the default `"err"` parameter name:
23+
24+
```js
25+
/*eslint handle-callback-err: "error"*/
26+
27+
function loadData (err, data) {
28+
doSomething();
29+
}
30+
31+
```
32+
33+
Examples of **correct** code for this rule with the default `"err"` parameter name:
34+
35+
```js
36+
/*eslint handle-callback-err: "error"*/
37+
38+
function loadData (err, data) {
39+
if (err) {
40+
console.log(err.stack);
41+
}
42+
doSomething();
43+
}
44+
45+
function generateError (err) {
46+
if (err) {}
47+
}
48+
```
49+
50+
Examples of **correct** code for this rule with a sample `"error"` parameter name:
51+
52+
```js
53+
/*eslint handle-callback-err: ["error", "error"]*/
54+
55+
function loadData (error, data) {
56+
if (error) {
57+
console.log(error.stack);
58+
}
59+
doSomething();
60+
}
61+
```
62+
63+
#### Regular Expression
64+
65+
Sometimes (especially in big projects) the name of the error variable is not consistent across the project,
66+
so you need a more flexible configuration to ensure that the rule reports all unhandled errors.
67+
68+
If the configured name of the error variable begins with a `^` it is considered to be a regexp pattern.
69+
70+
* If the option is `"^(err|error|anySpecificError)$"`, the rule reports unhandled errors where the parameter name can be `err`, `error` or `anySpecificError`.
71+
* If the option is `"^.+Error$"`, the rule reports unhandled errors where the parameter name ends with `Error` (for example, `connectionError` or `validationError` will match).
72+
* If the option is `"^.*(e|E)rr"`, the rule reports unhandled errors where the parameter name matches any string that contains `err` or `Err` (for example, `err`, `error`, `anyError`, `some_err` will match).
73+
74+
75+
## 🔎 Implementation
76+
77+
- [Rule source](../../lib/rules/handle-callback-err.js)
78+
- [Test source](../../tests/lib/rules/handle-callback-err.js)

‎docs/rules/no-mixed-requires.md

+121
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
# node/no-mixed-requires
2+
> disallow `require` calls to be mixed with regular variable declarations
3+
4+
In the Node.js community it is often customary to separate initializations with calls to `require` modules from other variable declarations, sometimes also grouping them by the type of module. This rule helps you enforce this convention.
5+
6+
## 📖 Rule Details
7+
8+
When this rule is enabled, each `var` statement must satisfy the following conditions:
9+
10+
* either none or all variable declarations must be require declarations (default)
11+
* all require declarations must be of the same type (grouping)
12+
13+
This rule distinguishes between six kinds of variable declaration types:
14+
15+
* `core`: declaration of a required [core module][1]
16+
* `file`: declaration of a required [file module][2]
17+
* `module`: declaration of a required module from the [node_modules folder][3]
18+
* `computed`: declaration of a required module whose type could not be determined (either because it is computed or because require was called without an argument)
19+
* `uninitialized`: a declaration that is not initialized
20+
* `other`: any other kind of declaration
21+
22+
In this document, the first four types are summed up under the term *require declaration*.
23+
24+
```js
25+
var fs = require('fs'), // "core" \
26+
async = require('async'), // "module" |- these are "require declaration"s
27+
foo = require('./foo'), // "file" |
28+
bar = require(getName()), // "computed" /
29+
baz = 42, // "other"
30+
bam; // "uninitialized"
31+
```
32+
33+
### Options
34+
35+
This rule can have an object literal option whose two properties have `false` values by default.
36+
37+
Configuring this rule with one boolean option `true` is deprecated.
38+
39+
Examples of **incorrect** code for this rule with the default `{ "grouping": false, "allowCall": false }` options:
40+
41+
```js
42+
/*eslint no-mixed-requires: "error"*/
43+
44+
var fs = require('fs'),
45+
i = 0;
46+
47+
var async = require('async'),
48+
debug = require('diagnostics').someFunction('my-module'),
49+
eslint = require('eslint');
50+
```
51+
52+
Examples of **correct** code for this rule with the default `{ "grouping": false, "allowCall": false }` options:
53+
54+
```js
55+
/*eslint no-mixed-requires: "error"*/
56+
57+
// only require declarations (grouping off)
58+
var eventEmitter = require('events').EventEmitter,
59+
myUtils = require('./utils'),
60+
util = require('util'),
61+
bar = require(getBarModuleName());
62+
63+
// only non-require declarations
64+
var foo = 42,
65+
bar = 'baz';
66+
67+
// always valid regardless of grouping because all declarations are of the same type
68+
var foo = require('foo' + VERSION),
69+
bar = require(getBarModuleName()),
70+
baz = require();
71+
```
72+
73+
#### grouping
74+
75+
Examples of **incorrect** code for this rule with the `{ "grouping": true }` option:
76+
77+
```js
78+
/*eslint no-mixed-requires: ["error", { "grouping": true }]*/
79+
80+
// invalid because of mixed types "core" and "module"
81+
var fs = require('fs'),
82+
async = require('async');
83+
84+
// invalid because of mixed types "file" and "unknown"
85+
var foo = require('foo'),
86+
bar = require(getBarModuleName());
87+
```
88+
89+
#### allowCall
90+
91+
Examples of **incorrect** code for this rule with the `{ "allowCall": true }` option:
92+
93+
```js
94+
/*eslint no-mixed-requires: ["error", { "allowCall": true }]*/
95+
96+
var async = require('async'),
97+
debug = require('diagnostics').someFunction('my-module'), /* allowCall doesn't allow calling any function */
98+
eslint = require('eslint');
99+
```
100+
101+
Examples of **correct** code for this rule with the `{ "allowCall": true }` option:
102+
103+
```js
104+
/*eslint no-mixed-requires: ["error", { "allowCall": true }]*/
105+
106+
var async = require('async'),
107+
debug = require('diagnostics')('my-module'),
108+
eslint = require('eslint');
109+
```
110+
111+
### Known Limitations
112+
113+
* The implementation is not aware of any local functions with the name `require` that may shadow Node.js' global `require`.
114+
115+
* Internally, the list of core modules is retrieved via `require("repl")._builtinLibs`. If you use different versions of Node.js for ESLint and your application, the list of core modules for each version may be different.
116+
The above mentioned `_builtinLibs` property became available in 0.8, for earlier versions a hardcoded list of module names is used as a fallback. If your version of Node.js is older than 0.6 that list may be inaccurate.
117+
118+
## 🔎 Implementation
119+
120+
- [Rule source](../../lib/rules/no-mixed-requires.js)
121+
- [Test source](../../tests/lib/rules/no-mixed-requires.js)

‎docs/rules/no-new-require.md

+48
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
# node/no-new-require
2+
> disallow `new` operators with calls to `require`
3+
4+
The `require` function is used to include modules that exist in separate files, such as:
5+
6+
```js
7+
var appHeader = require('app-header');
8+
```
9+
10+
Some modules return a constructor which can potentially lead to code such as:
11+
12+
```js
13+
var appHeader = new require('app-header');
14+
```
15+
16+
Unfortunately, this introduces a high potential for confusion since the code author likely meant to write:
17+
18+
```js
19+
var appHeader = new (require('app-header'));
20+
```
21+
22+
For this reason, it is usually best to disallow this particular expression.
23+
24+
## 📖 Rule Details
25+
26+
This rule aims to eliminate use of the `new require` expression.
27+
28+
Examples of **incorrect** code for this rule:
29+
30+
```js
31+
/*eslint no-new-require: "error"*/
32+
33+
var appHeader = new require('app-header');
34+
```
35+
36+
Examples of **correct** code for this rule:
37+
38+
```js
39+
/*eslint no-new-require: "error"*/
40+
41+
var AppHeader = require('app-header');
42+
var appHeader = new AppHeader();
43+
```
44+
45+
## 🔎 Implementation
46+
47+
- [Rule source](../../lib/rules/no-new-require.js)
48+
- [Test source](../../tests/lib/rules/no-new-require.js)

‎docs/rules/no-path-concat.md

+52
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
# node/no-path-concat
2+
> disallow string concatenation with `__dirname` and `__filename`
3+
4+
In Node.js, the `__dirname` and `__filename` global variables contain the directory path and the file path of the currently executing script file, respectively. Sometimes, developers try to use these variables to create paths to other files, such as:
5+
6+
```js
7+
var fullPath = __dirname + "/foo.js";
8+
```
9+
10+
However, there are a few problems with this. First, you can't be sure what type of system the script is running on. Node.js can be run on any computer, including Windows, which uses a different path separator. It's very easy, therefore, to create an invalid path using string concatenation and assuming Unix-style separators. There's also the possibility of having double separators, or otherwise ending up with an invalid path.
11+
12+
In order to avoid any confusion as to how to create the correct path, Node.js provides the `path` module. This module uses system-specific information to always return the correct value. So you can rewrite the previous example as:
13+
14+
```js
15+
var fullPath = path.join(__dirname, "foo.js");
16+
```
17+
18+
This example doesn't need to include separators as `path.join()` will do it in the most appropriate manner. Alternately, you can use `path.resolve()` to retrieve the fully-qualified path:
19+
20+
```js
21+
var fullPath = path.resolve(__dirname, "foo.js");
22+
```
23+
24+
Both `path.join()` and `path.resolve()` are suitable replacements for string concatenation wherever file or directory paths are being created.
25+
26+
## 📖 Rule Details
27+
28+
This rule aims to prevent string concatenation of directory paths in Node.js
29+
30+
Examples of **incorrect** code for this rule:
31+
32+
```js
33+
/*eslint no-path-concat: "error"*/
34+
35+
var fullPath = __dirname + "/foo.js";
36+
37+
var fullPath = __filename + "/foo.js";
38+
39+
```
40+
41+
Examples of **correct** code for this rule:
42+
43+
```js
44+
/*eslint no-path-concat: "error"*/
45+
46+
var fullPath = dirname + "/foo.js";
47+
```
48+
49+
## 🔎 Implementation
50+
51+
- [Rule source](../../lib/rules/no-path-concat.js)
52+
- [Test source](../../tests/lib/rules/no-path-concat.js)

‎docs/rules/no-process-env.md

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
# node/no-process-env
2+
> disallow the use of `process.env`
3+
4+
The `process.env` object in Node.js is used to store deployment/configuration parameters. Littering it through out a project could lead to maintenance issues as it's another kind of global dependency. As such, it could lead to merge conflicts in a multi-user setup and deployment issues in a multi-server setup. Instead, one of the best practices is to define all those parameters in a single configuration/settings file which could be accessed throughout the project.
5+
6+
## 📖 Rule Details
7+
8+
This rule is aimed at discouraging use of `process.env` to avoid global dependencies. As such, it will warn whenever `process.env` is used.
9+
10+
Examples of **incorrect** code for this rule:
11+
12+
```js
13+
/*eslint no-process-env: "error"*/
14+
15+
if(process.env.NODE_ENV === "development") {
16+
//...
17+
}
18+
```
19+
20+
Examples of **correct** code for this rule:
21+
22+
```js
23+
/*eslint no-process-env: "error"*/
24+
25+
var config = require("./config");
26+
27+
if(config.env === "development") {
28+
//...
29+
}
30+
```
31+
32+
## 🔎 Implementation
33+
34+
- [Rule source](../../lib/rules/no-process-env.js)
35+
- [Test source](../../tests/lib/rules/no-process-env.js)

‎docs/rules/no-process-exit.md

+50
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
# node/no-process-exit
2+
> disallow the use of `process.exit()`
3+
4+
The `process.exit()` method in Node.js is used to immediately stop the Node.js process and exit. This is a dangerous operation because it can occur in any method at any point in time, potentially stopping a Node.js application completely when an error occurs. For example:
5+
6+
```js
7+
if (somethingBadHappened) {
8+
console.error("Something bad happened!");
9+
process.exit(1);
10+
}
11+
```
12+
13+
This code could appear in any module and will stop the entire application when `somethingBadHappened` is truthy. This doesn't give the application any chance to respond to the error. It's usually better to throw an error and allow the application to handle it appropriately:
14+
15+
```js
16+
if (somethingBadHappened) {
17+
throw new Error("Something bad happened!");
18+
}
19+
```
20+
21+
By throwing an error in this way, other parts of the application have an opportunity to handle the error rather than stopping the application altogether. If the error bubbles all the way up to the process without being handled, then the process will exit and a non-zero exit code will returned, so the end result is the same.
22+
23+
If you are using `process.exit()` only for specifying the exit code, you can set [`process.exitCode`](https://nodejs.org/api/process.html#process_process_exitcode) (introduced in Node.js 0.11.8) instead.
24+
25+
## 📖 Rule Details
26+
27+
This rule aims to prevent the use of `process.exit()` in Node.js JavaScript. As such, it warns whenever `process.exit()` is found in code.
28+
29+
Examples of **incorrect** code for this rule:
30+
31+
```js
32+
/*eslint no-process-exit: "error"*/
33+
34+
process.exit(1);
35+
process.exit(0);
36+
```
37+
38+
Examples of **correct** code for this rule:
39+
40+
```js
41+
/*eslint no-process-exit: "error"*/
42+
43+
Process.exit();
44+
var exit = process.exit;
45+
```
46+
47+
## 🔎 Implementation
48+
49+
- [Rule source](../../lib/rules/no-process-exit.js)
50+
- [Test source](../../tests/lib/rules/no-process-exit.js)

‎docs/rules/no-restricted-require.md

+112
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
# node/no-restricted-require
2+
> disallow specified modules when loaded by `require`
3+
4+
A module in Node.js is a simple or complex functionality organized in a JavaScript file which can be reused throughout the Node.js
5+
application. The keyword `require` is used in Node.js/CommonJS to import modules into an application. This way you can have dynamic loading where the loaded module name isn't predefined /static, or where you conditionally load a module only if it's "truly required".
6+
7+
Why would you want to restrict a module?
8+
9+
Disallowing usage of specific Node.js modules can be useful if you want to limit the available methods a developer can use. For example, you can block usage of the `fs` module if you want to disallow file system access.
10+
11+
## 📖 Rule Details
12+
13+
This rule allows you to specify modules that you don’t want to use in your application.
14+
15+
### Options
16+
17+
The rule takes one or more strings as options: the names of restricted modules.
18+
19+
```json
20+
"no-restricted-require": ["error", "foo-module", "bar-module"]
21+
```
22+
23+
It can also take an object with lists of `paths` and gitignore-style `patterns` strings.
24+
25+
```json
26+
"no-restricted-require": ["error", { "paths": ["foo-module", "bar-module"] }]
27+
```
28+
29+
```json
30+
"no-restricted-require": ["error", {
31+
"paths": ["foo-module", "bar-module"],
32+
"patterns": ["foo-module/private/*", "bar-module/*","!baz-module/good"]
33+
}]
34+
```
35+
36+
You may also specify a custom message for any paths you want to restrict as follows:
37+
38+
```json
39+
"no-restricted-require": ["error", {
40+
"name": "foo-module",
41+
"message": "Please use bar-module instead."
42+
}
43+
]
44+
```
45+
46+
or like this:
47+
48+
```json
49+
"no-restricted-require": ["error",{
50+
"paths":[{
51+
"name": "foo-module",
52+
"message": "Please use bar-module instead."
53+
}]
54+
}]
55+
```
56+
57+
The custom message will be appended to the default error message. Please note that you may not specify custom error messages for restricted patterns as a particular module may match more than one pattern.
58+
59+
60+
To restrict the use of all Node.js core modules (via https://github.com/nodejs/node/tree/master/lib):
61+
62+
```json
63+
{
64+
"no-restricted-require": ["error",
65+
"assert","buffer","child_process","cluster","crypto","dgram","dns","domain","events","freelist","fs","http","https","module","net","os","path","punycode","querystring","readline","repl","smalloc","stream","string_decoder","sys","timers","tls","tracing","tty","url","util","vm","zlib"
66+
]
67+
}
68+
```
69+
70+
Examples of **incorrect** code for this rule with sample `"fs", "cluster", "lodash"` restricted modules:
71+
72+
```js
73+
/*eslint no-restricted-require: ["error", "fs", "cluster"]*/
74+
75+
var fs = require('fs');
76+
var cluster = require('cluster');
77+
```
78+
79+
```js
80+
/*eslint no-restricted-require: ["error", {"paths": ["cluster"] }]*/
81+
82+
var cluster = require('cluster');
83+
```
84+
85+
```js
86+
/*eslint no-restricted-require: ["error", { "patterns": ["lodash/*"] }]*/
87+
88+
var pick = require('lodash/pick');
89+
```
90+
91+
Examples of **correct** code for this rule with sample `"fs", "cluster", "lodash"` restricted modules:
92+
93+
```js
94+
/*eslint no-restricted-require: ["error", "fs", "cluster"]*/
95+
96+
var crypto = require('crypto');
97+
```
98+
99+
```js
100+
/*eslint no-restricted-require: ["error", {
101+
"paths": ["fs", "cluster"],
102+
"patterns": ["lodash/*", "!lodash/pick"]
103+
}]*/
104+
105+
var crypto = require('crypto');
106+
var pick = require('lodash/pick');
107+
```
108+
109+
## 🔎 Implementation
110+
111+
- [Rule source](../../lib/rules/no-restricted-require.js)
112+
- [Test source](../../tests/lib/rules/no-restricted-require.js)

‎docs/rules/no-sync.md

+61
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
# node/no-sync
2+
> disallow synchronous methods
3+
4+
In Node.js, most I/O is done through asynchronous methods. However, there are often synchronous versions of the asynchronous methods. For example, `fs.exists()` and `fs.existsSync()`. In some contexts, using synchronous operations is okay (if, as with ESLint, you are writing a command line utility). However, in other contexts the use of synchronous operations is considered a bad practice that should be avoided. For example, if you are running a high-travel web server on Node.js, you should consider carefully if you want to allow any synchronous operations that could lock up the server.
5+
6+
## 📖 Rule Details
7+
8+
This rule is aimed at preventing synchronous methods from being called in Node.js. It looks specifically for the method suffix "`Sync`" (as is the convention with Node.js operations).
9+
10+
### Options
11+
12+
This rule has an optional object option `{ allowAtRootLevel: <boolean> }`, which determines whether synchronous methods should be allowed at the top level of a file, outside of any functions. This option defaults to `false`.
13+
14+
Examples of **incorrect** code for this rule with the default `{ allowAtRootLevel: false }` option:
15+
16+
```js
17+
/*eslint no-sync: "error"*/
18+
19+
fs.existsSync(somePath);
20+
21+
function foo() {
22+
var contents = fs.readFileSync(somePath).toString();
23+
}
24+
```
25+
26+
Examples of **correct** code for this rule with the default `{ allowAtRootLevel: false }` option:
27+
28+
```js
29+
/*eslint no-sync: "error"*/
30+
31+
obj.sync();
32+
33+
async(function() {
34+
// ...
35+
});
36+
```
37+
38+
Examples of **incorrect** code for this rule with the `{ allowAtRootLevel: true }` option
39+
40+
```js
41+
/*eslint no-sync: ["error", { allowAtRootLevel: true }]*/
42+
43+
function foo() {
44+
var contents = fs.readFileSync(somePath).toString();
45+
}
46+
47+
var bar = baz => fs.readFileSync(qux);
48+
```
49+
50+
Examples of **correct** code for this rule with the `{ allowAtRootLevel: true }` option
51+
52+
```js
53+
/*eslint no-sync: ["error", { allowAtRootLevel: true }]*/
54+
55+
fs.readFileSync(somePath).toString();
56+
```
57+
58+
## 🔎 Implementation
59+
60+
- [Rule source](../../lib/rules/no-sync.js)
61+
- [Test source](../../tests/lib/rules/no-sync.js)

‎lib/index.js

+10
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,25 @@ module.exports = {
1010
},
1111
},
1212
rules: {
13+
"callback-return": require("./rules/callback-return"),
1314
"exports-style": require("./rules/exports-style"),
1415
"file-extension-in-import": require("./rules/file-extension-in-import"),
16+
"global-require": require("./rules/global-require"),
17+
"handle-callback-err": require("./rules/handle-callback-err"),
1518
"no-callback-literal": require("./rules/no-callback-literal"),
1619
"no-deprecated-api": require("./rules/no-deprecated-api"),
1720
"no-exports-assign": require("./rules/no-exports-assign"),
1821
"no-extraneous-import": require("./rules/no-extraneous-import"),
1922
"no-extraneous-require": require("./rules/no-extraneous-require"),
2023
"no-missing-import": require("./rules/no-missing-import"),
2124
"no-missing-require": require("./rules/no-missing-require"),
25+
"no-mixed-requires": require("./rules/no-mixed-requires"),
26+
"no-new-require": require("./rules/no-new-require"),
27+
"no-path-concat": require("./rules/no-path-concat"),
28+
"no-process-env": require("./rules/no-process-env"),
29+
"no-process-exit": require("./rules/no-process-exit"),
30+
"no-restricted-require": require("./rules/no-restricted-require"),
31+
"no-sync": require("./rules/no-sync"),
2232
"no-unpublished-bin": require("./rules/no-unpublished-bin"),
2333
"no-unpublished-import": require("./rules/no-unpublished-import"),
2434
"no-unpublished-require": require("./rules/no-unpublished-require"),

‎lib/rules/callback-return.js

+185
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
/**
2+
* @author Jamund Ferguson
3+
* See LICENSE file in root directory for full license.
4+
*/
5+
"use strict"
6+
7+
module.exports = {
8+
meta: {
9+
type: "suggestion",
10+
docs: {
11+
description: "require `return` statements after callbacks",
12+
category: "Stylistic Issues",
13+
recommended: false,
14+
url:
15+
"https://github.com/mysticatea/eslint-plugin-node/blob/v11.0.0/docs/rules/callback-return.md",
16+
},
17+
schema: [
18+
{
19+
type: "array",
20+
items: { type: "string" },
21+
},
22+
],
23+
fixable: null,
24+
messages: {
25+
missingReturn: "Expected return with your callback function.",
26+
},
27+
},
28+
29+
create(context) {
30+
const callbacks = context.options[0] || ["callback", "cb", "next"]
31+
const sourceCode = context.getSourceCode()
32+
33+
/**
34+
* Find the closest parent matching a list of types.
35+
* @param {ASTNode} node The node whose parents we are searching
36+
* @param {Array} types The node types to match
37+
* @returns {ASTNode} The matched node or undefined.
38+
*/
39+
function findClosestParentOfType(node, types) {
40+
if (!node.parent) {
41+
return null
42+
}
43+
if (types.indexOf(node.parent.type) === -1) {
44+
return findClosestParentOfType(node.parent, types)
45+
}
46+
return node.parent
47+
}
48+
49+
/**
50+
* Check to see if a node contains only identifers
51+
* @param {ASTNode} node The node to check
52+
* @returns {boolean} Whether or not the node contains only identifers
53+
*/
54+
function containsOnlyIdentifiers(node) {
55+
if (node.type === "Identifier") {
56+
return true
57+
}
58+
59+
if (node.type === "MemberExpression") {
60+
if (node.object.type === "Identifier") {
61+
return true
62+
}
63+
if (node.object.type === "MemberExpression") {
64+
return containsOnlyIdentifiers(node.object)
65+
}
66+
}
67+
68+
return false
69+
}
70+
71+
/**
72+
* Check to see if a CallExpression is in our callback list.
73+
* @param {ASTNode} node The node to check against our callback names list.
74+
* @returns {boolean} Whether or not this function matches our callback name.
75+
*/
76+
function isCallback(node) {
77+
return (
78+
containsOnlyIdentifiers(node.callee) &&
79+
callbacks.indexOf(sourceCode.getText(node.callee)) > -1
80+
)
81+
}
82+
83+
/**
84+
* Determines whether or not the callback is part of a callback expression.
85+
* @param {ASTNode} node The callback node
86+
* @param {ASTNode} parentNode The expression node
87+
* @returns {boolean} Whether or not this is part of a callback expression
88+
*/
89+
function isCallbackExpression(node, parentNode) {
90+
// ensure the parent node exists and is an expression
91+
if (!parentNode || parentNode.type !== "ExpressionStatement") {
92+
return false
93+
}
94+
95+
// cb()
96+
if (parentNode.expression === node) {
97+
return true
98+
}
99+
100+
// special case for cb && cb() and similar
101+
if (
102+
parentNode.expression.type === "BinaryExpression" ||
103+
parentNode.expression.type === "LogicalExpression"
104+
) {
105+
if (parentNode.expression.right === node) {
106+
return true
107+
}
108+
}
109+
110+
return false
111+
}
112+
113+
return {
114+
CallExpression(node) {
115+
// if we're not a callback we can return
116+
if (!isCallback(node)) {
117+
return
118+
}
119+
120+
// find the closest block, return or loop
121+
const closestBlock =
122+
findClosestParentOfType(node, [
123+
"BlockStatement",
124+
"ReturnStatement",
125+
"ArrowFunctionExpression",
126+
]) || {}
127+
128+
// if our parent is a return we know we're ok
129+
if (closestBlock.type === "ReturnStatement") {
130+
return
131+
}
132+
133+
// arrow functions don't always have blocks and implicitly return
134+
if (closestBlock.type === "ArrowFunctionExpression") {
135+
return
136+
}
137+
138+
// block statements are part of functions and most if statements
139+
if (closestBlock.type === "BlockStatement") {
140+
// find the last item in the block
141+
const lastItem =
142+
closestBlock.body[closestBlock.body.length - 1]
143+
144+
// if the callback is the last thing in a block that might be ok
145+
if (isCallbackExpression(node, lastItem)) {
146+
const parentType = closestBlock.parent.type
147+
148+
// but only if the block is part of a function
149+
if (
150+
parentType === "FunctionExpression" ||
151+
parentType === "FunctionDeclaration" ||
152+
parentType === "ArrowFunctionExpression"
153+
) {
154+
return
155+
}
156+
}
157+
158+
// ending a block with a return is also ok
159+
if (lastItem.type === "ReturnStatement") {
160+
// but only if the callback is immediately before
161+
if (
162+
isCallbackExpression(
163+
node,
164+
closestBlock.body[closestBlock.body.length - 2]
165+
)
166+
) {
167+
return
168+
}
169+
}
170+
}
171+
172+
// as long as you're the child of a function at this point you should be asked to return
173+
if (
174+
findClosestParentOfType(node, [
175+
"FunctionDeclaration",
176+
"FunctionExpression",
177+
"ArrowFunctionExpression",
178+
])
179+
) {
180+
context.report({ node, messageId: "missingReturn" })
181+
}
182+
},
183+
}
184+
},
185+
}

‎lib/rules/global-require.js

+91
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
/**
2+
* @author Jamund Ferguson
3+
* See LICENSE file in root directory for full license.
4+
*/
5+
"use strict"
6+
7+
const ACCEPTABLE_PARENTS = [
8+
"AssignmentExpression",
9+
"VariableDeclarator",
10+
"MemberExpression",
11+
"ExpressionStatement",
12+
"CallExpression",
13+
"ConditionalExpression",
14+
"Program",
15+
"VariableDeclaration",
16+
]
17+
18+
/**
19+
* Finds the eslint-scope reference in the given scope.
20+
* @param {Object} scope The scope to search.
21+
* @param {ASTNode} node The identifier node.
22+
* @returns {Reference|null} Returns the found reference or null if none were found.
23+
*/
24+
function findReference(scope, node) {
25+
const references = scope.references.filter(
26+
reference =>
27+
reference.identifier.range[0] === node.range[0] &&
28+
reference.identifier.range[1] === node.range[1]
29+
)
30+
31+
/* istanbul ignore else: correctly returns null */
32+
if (references.length === 1) {
33+
return references[0]
34+
}
35+
return null
36+
}
37+
38+
/**
39+
* Checks if the given identifier node is shadowed in the given scope.
40+
* @param {Object} scope The current scope.
41+
* @param {ASTNode} node The identifier node to check.
42+
* @returns {boolean} Whether or not the name is shadowed.
43+
*/
44+
function isShadowed(scope, node) {
45+
const reference = findReference(scope, node)
46+
47+
return reference && reference.resolved && reference.resolved.defs.length > 0
48+
}
49+
50+
module.exports = {
51+
meta: {
52+
type: "suggestion",
53+
docs: {
54+
description:
55+
"require `require()` calls to be placed at top-level module scope",
56+
category: "Stylistic Issues",
57+
recommended: false,
58+
url:
59+
"https://github.com/mysticatea/eslint-plugin-node/blob/v11.0.0/docs/rules/global-require.md",
60+
},
61+
fixable: null,
62+
schema: [],
63+
messages: {
64+
unexpected: "Unexpected require().",
65+
},
66+
},
67+
68+
create(context) {
69+
return {
70+
CallExpression(node) {
71+
const currentScope = context.getScope()
72+
73+
if (
74+
node.callee.name === "require" &&
75+
!isShadowed(currentScope, node.callee)
76+
) {
77+
const isGoodRequire = context
78+
.getAncestors()
79+
.every(
80+
parent =>
81+
ACCEPTABLE_PARENTS.indexOf(parent.type) > -1
82+
)
83+
84+
if (!isGoodRequire) {
85+
context.report({ node, messageId: "unexpected" })
86+
}
87+
}
88+
},
89+
}
90+
},
91+
}

‎lib/rules/handle-callback-err.js

+94
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
/**
2+
* @author Jamund Ferguson
3+
* See LICENSE file in root directory for full license.
4+
*/
5+
"use strict"
6+
7+
module.exports = {
8+
meta: {
9+
type: "suggestion",
10+
docs: {
11+
description: "require error handling in callbacks",
12+
category: "Possible Errors",
13+
recommended: false,
14+
url:
15+
"https://github.com/mysticatea/eslint-plugin-node/blob/v11.0.0/docs/rules/handle-callback-err.md",
16+
},
17+
fixable: null,
18+
schema: [
19+
{
20+
type: "string",
21+
},
22+
],
23+
messages: {
24+
expected: "Expected error to be handled.",
25+
},
26+
},
27+
28+
create(context) {
29+
const errorArgument = context.options[0] || "err"
30+
31+
/**
32+
* Checks if the given argument should be interpreted as a regexp pattern.
33+
* @param {string} stringToCheck The string which should be checked.
34+
* @returns {boolean} Whether or not the string should be interpreted as a pattern.
35+
*/
36+
function isPattern(stringToCheck) {
37+
const firstChar = stringToCheck[0]
38+
39+
return firstChar === "^"
40+
}
41+
42+
/**
43+
* Checks if the given name matches the configured error argument.
44+
* @param {string} name The name which should be compared.
45+
* @returns {boolean} Whether or not the given name matches the configured error variable name.
46+
*/
47+
function matchesConfiguredErrorName(name) {
48+
if (isPattern(errorArgument)) {
49+
const regexp = new RegExp(errorArgument, "u")
50+
51+
return regexp.test(name)
52+
}
53+
return name === errorArgument
54+
}
55+
56+
/**
57+
* Get the parameters of a given function scope.
58+
* @param {Object} scope The function scope.
59+
* @returns {Array} All parameters of the given scope.
60+
*/
61+
function getParameters(scope) {
62+
return scope.variables.filter(
63+
variable =>
64+
variable.defs[0] && variable.defs[0].type === "Parameter"
65+
)
66+
}
67+
68+
/**
69+
* Check to see if we're handling the error object properly.
70+
* @param {ASTNode} node The AST node to check.
71+
* @returns {void}
72+
*/
73+
function checkForError(node) {
74+
const scope = context.getScope()
75+
const parameters = getParameters(scope)
76+
const firstParameter = parameters[0]
77+
78+
if (
79+
firstParameter &&
80+
matchesConfiguredErrorName(firstParameter.name)
81+
) {
82+
if (firstParameter.references.length === 0) {
83+
context.report({ node, messageId: "expected" })
84+
}
85+
}
86+
}
87+
88+
return {
89+
FunctionDeclaration: checkForError,
90+
FunctionExpression: checkForError,
91+
ArrowFunctionExpression: checkForError,
92+
}
93+
},
94+
}

‎lib/rules/no-mixed-requires.js

+255
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,255 @@
1+
/**
2+
* @author Raphael Pigulla
3+
* See LICENSE file in root directory for full license.
4+
*/
5+
"use strict"
6+
7+
// This list is generated using:
8+
// `require("module").builtinModules`
9+
//
10+
// This was last updated using Node v13.8.0.
11+
const BUILTIN_MODULES = [
12+
"_http_agent",
13+
"_http_client",
14+
"_http_common",
15+
"_http_incoming",
16+
"_http_outgoing",
17+
"_http_server",
18+
"_stream_duplex",
19+
"_stream_passthrough",
20+
"_stream_readable",
21+
"_stream_transform",
22+
"_stream_wrap",
23+
"_stream_writable",
24+
"_tls_common",
25+
"_tls_wrap",
26+
"assert",
27+
"async_hooks",
28+
"buffer",
29+
"child_process",
30+
"cluster",
31+
"console",
32+
"constants",
33+
"crypto",
34+
"dgram",
35+
"dns",
36+
"domain",
37+
"events",
38+
"fs",
39+
"http",
40+
"http2",
41+
"https",
42+
"inspector",
43+
"module",
44+
"net",
45+
"os",
46+
"path",
47+
"perf_hooks",
48+
"process",
49+
"punycode",
50+
"querystring",
51+
"readline",
52+
"repl",
53+
"stream",
54+
"string_decoder",
55+
"sys",
56+
"timers",
57+
"tls",
58+
"trace_events",
59+
"tty",
60+
"url",
61+
"util",
62+
"v8",
63+
"vm",
64+
"worker_threads",
65+
"zlib",
66+
]
67+
68+
module.exports = {
69+
meta: {
70+
type: "suggestion",
71+
docs: {
72+
description:
73+
"disallow `require` calls to be mixed with regular variable declarations",
74+
category: "Stylistic Issues",
75+
recommended: false,
76+
url:
77+
"https://github.com/mysticatea/eslint-plugin-node/blob/v11.0.0/docs/rules/no-mixed-requires.md",
78+
},
79+
fixable: null,
80+
schema: [
81+
{
82+
oneOf: [
83+
{
84+
type: "boolean",
85+
},
86+
{
87+
type: "object",
88+
properties: {
89+
grouping: {
90+
type: "boolean",
91+
},
92+
allowCall: {
93+
type: "boolean",
94+
},
95+
},
96+
additionalProperties: false,
97+
},
98+
],
99+
},
100+
],
101+
messages: {
102+
noMixRequire: "Do not mix 'require' and other declarations.",
103+
noMixCoreModuleFileComputed:
104+
"Do not mix core, module, file and computed requires.",
105+
},
106+
},
107+
108+
create(context) {
109+
const options = context.options[0]
110+
let grouping = false
111+
let allowCall = false
112+
113+
if (typeof options === "object") {
114+
grouping = options.grouping
115+
allowCall = options.allowCall
116+
} else {
117+
grouping = Boolean(options)
118+
}
119+
120+
const DECL_REQUIRE = "require"
121+
const DECL_UNINITIALIZED = "uninitialized"
122+
const DECL_OTHER = "other"
123+
124+
const REQ_CORE = "core"
125+
const REQ_FILE = "file"
126+
const REQ_MODULE = "module"
127+
const REQ_COMPUTED = "computed"
128+
129+
/**
130+
* Determines the type of a declaration statement.
131+
* @param {ASTNode} initExpression The init node of the VariableDeclarator.
132+
* @returns {string} The type of declaration represented by the expression.
133+
*/
134+
function getDeclarationType(initExpression) {
135+
if (!initExpression) {
136+
// "var x;"
137+
return DECL_UNINITIALIZED
138+
}
139+
140+
if (
141+
initExpression.type === "CallExpression" &&
142+
initExpression.callee.type === "Identifier" &&
143+
initExpression.callee.name === "require"
144+
) {
145+
// "var x = require('util');"
146+
return DECL_REQUIRE
147+
}
148+
if (
149+
allowCall &&
150+
initExpression.type === "CallExpression" &&
151+
initExpression.callee.type === "CallExpression"
152+
) {
153+
// "var x = require('diagnose')('sub-module');"
154+
return getDeclarationType(initExpression.callee)
155+
}
156+
if (initExpression.type === "MemberExpression") {
157+
// "var x = require('glob').Glob;"
158+
return getDeclarationType(initExpression.object)
159+
}
160+
161+
// "var x = 42;"
162+
return DECL_OTHER
163+
}
164+
165+
/**
166+
* Determines the type of module that is loaded via require.
167+
* @param {ASTNode} initExpression The init node of the VariableDeclarator.
168+
* @returns {string} The module type.
169+
*/
170+
function inferModuleType(initExpression) {
171+
if (initExpression.type === "MemberExpression") {
172+
// "var x = require('glob').Glob;"
173+
return inferModuleType(initExpression.object)
174+
}
175+
if (initExpression.arguments.length === 0) {
176+
// "var x = require();"
177+
return REQ_COMPUTED
178+
}
179+
180+
const arg = initExpression.arguments[0]
181+
182+
if (arg.type !== "Literal" || typeof arg.value !== "string") {
183+
// "var x = require(42);"
184+
return REQ_COMPUTED
185+
}
186+
187+
if (BUILTIN_MODULES.indexOf(arg.value) !== -1) {
188+
// "var fs = require('fs');"
189+
return REQ_CORE
190+
}
191+
if (/^\.{0,2}\//u.test(arg.value)) {
192+
// "var utils = require('./utils');"
193+
return REQ_FILE
194+
}
195+
196+
// "var async = require('async');"
197+
return REQ_MODULE
198+
}
199+
200+
/**
201+
* Check if the list of variable declarations is mixed, i.e. whether it
202+
* contains both require and other declarations.
203+
* @param {ASTNode} declarations The list of VariableDeclarators.
204+
* @returns {boolean} True if the declarations are mixed, false if not.
205+
*/
206+
function isMixed(declarations) {
207+
const contains = {}
208+
209+
for (const declaration of declarations) {
210+
const type = getDeclarationType(declaration.init)
211+
212+
contains[type] = true
213+
}
214+
215+
return Boolean(
216+
contains[DECL_REQUIRE] &&
217+
(contains[DECL_UNINITIALIZED] || contains[DECL_OTHER])
218+
)
219+
}
220+
221+
/**
222+
* Check if all require declarations in the given list are of the same
223+
* type.
224+
* @param {ASTNode} declarations The list of VariableDeclarators.
225+
* @returns {boolean} True if the declarations are grouped, false if not.
226+
*/
227+
function isGrouped(declarations) {
228+
const found = {}
229+
230+
for (const declaration of declarations) {
231+
if (getDeclarationType(declaration.init) === DECL_REQUIRE) {
232+
found[inferModuleType(declaration.init)] = true
233+
}
234+
}
235+
236+
return Object.keys(found).length <= 1
237+
}
238+
239+
return {
240+
VariableDeclaration(node) {
241+
if (isMixed(node.declarations)) {
242+
context.report({
243+
node,
244+
messageId: "noMixRequire",
245+
})
246+
} else if (grouping && !isGrouped(node.declarations)) {
247+
context.report({
248+
node,
249+
messageId: "noMixCoreModuleFileComputed",
250+
})
251+
}
252+
},
253+
}
254+
},
255+
}

‎lib/rules/no-new-require.js

+39
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/**
2+
* @author Wil Moore III
3+
* See LICENSE file in root directory for full license.
4+
*/
5+
"use strict"
6+
7+
module.exports = {
8+
meta: {
9+
type: "suggestion",
10+
docs: {
11+
description: "disallow `new` operators with calls to `require`",
12+
category: "Possible Errors",
13+
recommended: false,
14+
url:
15+
"https://github.com/mysticatea/eslint-plugin-node/blob/v11.0.0/docs/rules/no-new-require.md",
16+
},
17+
fixable: null,
18+
schema: [],
19+
messages: {
20+
noNewRequire: "Unexpected use of new with require.",
21+
},
22+
},
23+
24+
create(context) {
25+
return {
26+
NewExpression(node) {
27+
if (
28+
node.callee.type === "Identifier" &&
29+
node.callee.name === "require"
30+
) {
31+
context.report({
32+
node,
33+
messageId: "noNewRequire",
34+
})
35+
}
36+
},
37+
}
38+
},
39+
}

‎lib/rules/no-path-concat.js

+48
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
/**
2+
* @author Nicholas C. Zakas
3+
* See LICENSE file in root directory for full license.
4+
*/
5+
"use strict"
6+
7+
module.exports = {
8+
meta: {
9+
type: "suggestion",
10+
docs: {
11+
description:
12+
"disallow string concatenation with `__dirname` and `__filename`",
13+
category: "Possible Errors",
14+
recommended: false,
15+
url:
16+
"https://github.com/mysticatea/eslint-plugin-node/blob/v11.0.0/docs/rules/no-path-concat.md",
17+
},
18+
fixable: null,
19+
schema: [],
20+
messages: {
21+
usePathFunctions:
22+
"Use path.join() or path.resolve() instead of + to create paths.",
23+
},
24+
},
25+
26+
create(context) {
27+
const MATCHER = /^__(?:dir|file)name$/u
28+
29+
return {
30+
BinaryExpression(node) {
31+
const left = node.left
32+
const right = node.right
33+
34+
if (
35+
node.operator === "+" &&
36+
((left.type === "Identifier" && MATCHER.test(left.name)) ||
37+
(right.type === "Identifier" &&
38+
MATCHER.test(right.name)))
39+
) {
40+
context.report({
41+
node,
42+
messageId: "usePathFunctions",
43+
})
44+
}
45+
},
46+
}
47+
},
48+
}

‎lib/rules/no-process-env.js

+45
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
/**
2+
* @author Vignesh Anand
3+
* See LICENSE file in root directory for full license.
4+
*/
5+
"use strict"
6+
7+
//------------------------------------------------------------------------------
8+
// Rule Definition
9+
//------------------------------------------------------------------------------
10+
11+
module.exports = {
12+
meta: {
13+
type: "suggestion",
14+
docs: {
15+
description: "disallow the use of `process.env`",
16+
category: "Stylistic Issues",
17+
recommended: false,
18+
url:
19+
"https://github.com/mysticatea/eslint-plugin-node/blob/v11.0.0/docs/rules/no-process-env.md",
20+
},
21+
fixable: null,
22+
schema: [],
23+
messages: {
24+
unexpectedProcessEnv: "Unexpected use of process.env.",
25+
},
26+
},
27+
28+
create(context) {
29+
return {
30+
MemberExpression(node) {
31+
const objectName = node.object.name
32+
const propertyName = node.property.name
33+
34+
if (
35+
objectName === "process" &&
36+
!node.computed &&
37+
propertyName &&
38+
propertyName === "env"
39+
) {
40+
context.report({ node, messageId: "unexpectedProcessEnv" })
41+
}
42+
},
43+
}
44+
},
45+
}

‎lib/rules/no-process-exit.js

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/**
2+
* @author Nicholas C. Zakas
3+
* See LICENSE file in root directory for full license.
4+
*/
5+
"use strict"
6+
7+
module.exports = {
8+
meta: {
9+
type: "suggestion",
10+
docs: {
11+
description: "disallow the use of `process.exit()`",
12+
category: "Possible Errors",
13+
recommended: false,
14+
url:
15+
"https://github.com/mysticatea/eslint-plugin-node/blob/v11.0.0/docs/rules/no-process-exit.md",
16+
},
17+
fixable: null,
18+
schema: [],
19+
messages: {
20+
noProcessExit: "Don't use process.exit(); throw an error instead.",
21+
},
22+
},
23+
24+
create(context) {
25+
return {
26+
"CallExpression > MemberExpression.callee[object.name = 'process'][property.name = 'exit']"(
27+
node
28+
) {
29+
context.report({
30+
node: node.parent,
31+
messageId: "noProcessExit",
32+
})
33+
},
34+
}
35+
},
36+
}

‎lib/rules/no-restricted-require.js

+192
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,192 @@
1+
/**
2+
* @author Christian Schulz
3+
* See LICENSE file in root directory for full license.
4+
*/
5+
"use strict"
6+
7+
const ignore = require("ignore")
8+
9+
const arrayOfStrings = {
10+
type: "array",
11+
items: { type: "string" },
12+
uniqueItems: true,
13+
}
14+
15+
const arrayOfStringsOrObjects = {
16+
type: "array",
17+
items: {
18+
anyOf: [
19+
{ type: "string" },
20+
{
21+
type: "object",
22+
properties: {
23+
name: { type: "string" },
24+
message: {
25+
type: "string",
26+
minLength: 1,
27+
},
28+
},
29+
additionalProperties: false,
30+
required: ["name"],
31+
},
32+
],
33+
},
34+
uniqueItems: true,
35+
}
36+
37+
module.exports = {
38+
meta: {
39+
type: "suggestion",
40+
docs: {
41+
description: "disallow specified modules when loaded by `require`",
42+
category: "Stylistic Issues",
43+
recommended: false,
44+
url:
45+
"https://github.com/mysticatea/eslint-plugin-node/blob/v11.0.0/docs/rules/no-restricted-require.md",
46+
},
47+
fixable: null,
48+
schema: {
49+
anyOf: [
50+
arrayOfStringsOrObjects,
51+
{
52+
type: "array",
53+
items: {
54+
type: "object",
55+
properties: {
56+
paths: arrayOfStringsOrObjects,
57+
patterns: arrayOfStrings,
58+
},
59+
additionalProperties: false,
60+
},
61+
additionalItems: false,
62+
},
63+
],
64+
},
65+
messages: {
66+
defaultMessage: "'{{name}}' module is restricted from being used.",
67+
customMessage:
68+
// eslint-disable-next-line @mysticatea/eslint-plugin/report-message-format
69+
"'{{name}}' module is restricted from being used. {{customMessage}}",
70+
patternMessage:
71+
"'{{name}}' module is restricted from being used by a pattern.",
72+
},
73+
},
74+
75+
create(context) {
76+
const options = Array.isArray(context.options) ? context.options : []
77+
const isPathAndPatternsObject =
78+
typeof options[0] === "object" &&
79+
(Object.prototype.hasOwnProperty.call(options[0], "paths") ||
80+
Object.prototype.hasOwnProperty.call(options[0], "patterns"))
81+
82+
const restrictedPaths =
83+
(isPathAndPatternsObject ? options[0].paths : context.options) || []
84+
const restrictedPatterns =
85+
(isPathAndPatternsObject ? options[0].patterns : []) || []
86+
87+
const restrictedPathMessages = restrictedPaths.reduce(
88+
(memo, importName) => {
89+
if (typeof importName === "string") {
90+
memo[importName] = null
91+
} else {
92+
memo[importName.name] = importName.message
93+
}
94+
return memo
95+
},
96+
{}
97+
)
98+
99+
// if no imports are restricted we don"t need to check
100+
if (
101+
Object.keys(restrictedPaths).length === 0 &&
102+
restrictedPatterns.length === 0
103+
) {
104+
return {}
105+
}
106+
107+
const ig = ignore().add(restrictedPatterns)
108+
109+
/**
110+
* Function to check if a node is a string literal.
111+
* @param {ASTNode} node The node to check.
112+
* @returns {boolean} If the node is a string literal.
113+
*/
114+
function isString(node) {
115+
return (
116+
node &&
117+
node.type === "Literal" &&
118+
typeof node.value === "string"
119+
)
120+
}
121+
122+
/**
123+
* Function to check if a node is a require call.
124+
* @param {ASTNode} node The node to check.
125+
* @returns {boolean} If the node is a require call.
126+
*/
127+
function isRequireCall(node) {
128+
return (
129+
node.callee.type === "Identifier" &&
130+
node.callee.name === "require"
131+
)
132+
}
133+
134+
/**
135+
* Report a restricted path.
136+
* @param {node} node representing the restricted path reference
137+
* @returns {void}
138+
* @private
139+
*/
140+
function reportPath(node) {
141+
const name = node.arguments[0].value.trim()
142+
const customMessage = restrictedPathMessages[name]
143+
const messageId = customMessage ? "customMessage" : "defaultMessage"
144+
145+
context.report({
146+
node,
147+
messageId,
148+
data: {
149+
name,
150+
customMessage,
151+
},
152+
})
153+
}
154+
155+
/**
156+
* Check if the given name is a restricted path name
157+
* @param {string} name name of a variable
158+
* @returns {boolean} whether the variable is a restricted path or not
159+
* @private
160+
*/
161+
function isRestrictedPath(name) {
162+
return Object.prototype.hasOwnProperty.call(
163+
restrictedPathMessages,
164+
name
165+
)
166+
}
167+
168+
return {
169+
CallExpression(node) {
170+
if (isRequireCall(node)) {
171+
// node has arguments and first argument is string
172+
if (node.arguments.length && isString(node.arguments[0])) {
173+
const name = node.arguments[0].value.trim()
174+
175+
// check if argument value is in restricted modules array
176+
if (isRestrictedPath(name)) {
177+
reportPath(node)
178+
}
179+
180+
if (restrictedPatterns.length > 0 && ig.ignores(name)) {
181+
context.report({
182+
node,
183+
messageId: "patternMessage",
184+
data: { name },
185+
})
186+
}
187+
}
188+
}
189+
},
190+
}
191+
},
192+
}

‎lib/rules/no-sync.js

+53
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
/**
2+
* @author Matt DuVall<http://mattduvall.com/>
3+
* See LICENSE file in root directory for full license.
4+
*/
5+
"use strict"
6+
7+
module.exports = {
8+
meta: {
9+
type: "suggestion",
10+
docs: {
11+
description: "disallow synchronous methods",
12+
category: "Stylistic Issues",
13+
recommended: false,
14+
url:
15+
"https://github.com/mysticatea/eslint-plugin-node/blob/v11.0.0/docs/rules/no-sync.md",
16+
},
17+
fixable: null,
18+
schema: [
19+
{
20+
type: "object",
21+
properties: {
22+
allowAtRootLevel: {
23+
type: "boolean",
24+
default: false,
25+
},
26+
},
27+
additionalProperties: false,
28+
},
29+
],
30+
messages: {
31+
noSync: "Unexpected sync method: '{{propertyName}}'.",
32+
},
33+
},
34+
35+
create(context) {
36+
const selector =
37+
context.options[0] && context.options[0].allowAtRootLevel
38+
? ":function MemberExpression[property.name=/.*Sync$/]"
39+
: "MemberExpression[property.name=/.*Sync$/]"
40+
41+
return {
42+
[selector](node) {
43+
context.report({
44+
node,
45+
messageId: "noSync",
46+
data: {
47+
propertyName: node.property.name,
48+
},
49+
})
50+
},
51+
}
52+
},
53+
}

‎tests/lib/rules/callback-return.js

+527
Large diffs are not rendered by default.

‎tests/lib/rules/global-require.js

+68
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
/**
2+
* @author Jamund Ferguson
3+
* See LICENSE file in root directory for full license.
4+
*/
5+
"use strict"
6+
7+
const RuleTester = require("eslint").RuleTester
8+
const rule = require("../../../lib/rules/global-require")
9+
10+
const ERROR = { messageId: "unexpected", type: "CallExpression" }
11+
12+
new RuleTester().run("global-require", rule, {
13+
valid: [
14+
"var x = require('y');",
15+
"if (x) { x.require('y'); }",
16+
"var x;\nx = require('y');",
17+
"var x = 1, y = require('y');",
18+
"var x = require('y'), y = require('y'), z = require('z');",
19+
"var x = require('y').foo;",
20+
"require('y').foo();",
21+
"require('y');",
22+
"function x(){}\n\n\nx();\n\n\nif (x > y) {\n\tdoSomething()\n\n}\n\nvar x = require('y').foo;",
23+
"var logger = require(DEBUG ? 'dev-logger' : 'logger');",
24+
"var logger = DEBUG ? require('dev-logger') : require('logger');",
25+
"function localScopedRequire(require) { require('y'); }",
26+
"var someFunc = require('./someFunc'); someFunc(function(require) { return('bananas'); });",
27+
],
28+
invalid: [
29+
// block statements
30+
{
31+
code:
32+
"if (process.env.NODE_ENV === 'DEVELOPMENT') {\n\trequire('debug');\n}",
33+
errors: [ERROR],
34+
},
35+
{
36+
code: "var x; if (y) { x = require('debug'); }",
37+
errors: [ERROR],
38+
},
39+
{
40+
code: "var x; if (y) { x = require('debug').baz; }",
41+
errors: [ERROR],
42+
},
43+
{
44+
code: "function x() { require('y') }",
45+
errors: [ERROR],
46+
},
47+
{
48+
code: "try { require('x'); } catch (e) { console.log(e); }",
49+
errors: [ERROR],
50+
},
51+
52+
// non-block statements
53+
{
54+
code: "var getModule = x => require(x);",
55+
parserOptions: { ecmaVersion: 6 },
56+
errors: [ERROR],
57+
},
58+
{
59+
code: "var x = (x => require(x))('weird')",
60+
parserOptions: { ecmaVersion: 6 },
61+
errors: [ERROR],
62+
},
63+
{
64+
code: "switch(x) { case '1': require('1'); break; }",
65+
errors: [ERROR],
66+
},
67+
],
68+
})
+154
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
/**
2+
* @author Jamund Ferguson
3+
* See LICENSE file in root directory for full license.
4+
*/
5+
"use strict"
6+
7+
const RuleTester = require("eslint").RuleTester
8+
const rule = require("../../../lib/rules/handle-callback-err")
9+
10+
const EXPECTED_DECL_ERROR = {
11+
messageId: "expected",
12+
type: "FunctionDeclaration",
13+
}
14+
const EXPECTED_FUNC_ERROR = {
15+
messageId: "expected",
16+
type: "FunctionExpression",
17+
}
18+
19+
new RuleTester().run("handle-callback-err", rule, {
20+
valid: [
21+
"function test(error) {}",
22+
"function test(err) {console.log(err);}",
23+
"function test(err, data) {if(err){ data = 'ERROR';}}",
24+
"var test = function(err) {console.log(err);};",
25+
"var test = function(err) {if(err){/* do nothing */}};",
26+
"var test = function(err) {if(!err){doSomethingHere();}else{};}",
27+
"var test = function(err, data) {if(!err) { good(); } else { bad(); }}",
28+
"try { } catch(err) {}",
29+
"getData(function(err, data) {if (err) {}getMoreDataWith(data, function(err, moreData) {if (err) {}getEvenMoreDataWith(moreData, function(err, allOfTheThings) {if (err) {}});});});",
30+
"var test = function(err) {if(! err){doSomethingHere();}};",
31+
"function test(err, data) {if (data) {doSomething(function(err) {console.error(err);});} else if (err) {console.log(err);}}",
32+
"function handler(err, data) {if (data) {doSomethingWith(data);} else if (err) {console.log(err);}}",
33+
"function handler(err) {logThisAction(function(err) {if (err) {}}); console.log(err);}",
34+
"function userHandler(err) {process.nextTick(function() {if (err) {}})}",
35+
"function help() { function userHandler(err) {function tester() { err; process.nextTick(function() { err; }); } } }",
36+
"function help(done) { var err = new Error('error'); done(); }",
37+
{ code: "var test = err => err;", parserOptions: { ecmaVersion: 6 } },
38+
{ code: "var test = err => !err;", parserOptions: { ecmaVersion: 6 } },
39+
{
40+
code: "var test = err => err.message;",
41+
parserOptions: { ecmaVersion: 6 },
42+
},
43+
{
44+
code: "var test = function(error) {if(error){/* do nothing */}};",
45+
options: ["error"],
46+
},
47+
{
48+
code: "var test = (error) => {if(error){/* do nothing */}};",
49+
options: ["error"],
50+
parserOptions: { ecmaVersion: 6 },
51+
},
52+
{
53+
code:
54+
"var test = function(error) {if(! error){doSomethingHere();}};",
55+
options: ["error"],
56+
},
57+
{
58+
code: "var test = function(err) { console.log(err); };",
59+
options: ["^(err|error)$"],
60+
},
61+
{
62+
code: "var test = function(error) { console.log(error); };",
63+
options: ["^(err|error)$"],
64+
},
65+
{
66+
code: "var test = function(anyError) { console.log(anyError); };",
67+
options: ["^.+Error$"],
68+
},
69+
{
70+
code: "var test = function(any_error) { console.log(anyError); };",
71+
options: ["^.+Error$"],
72+
},
73+
{
74+
code: "var test = function(any_error) { console.log(any_error); };",
75+
options: ["^.+(e|E)rror$"],
76+
},
77+
],
78+
invalid: [
79+
{ code: "function test(err) {}", errors: [EXPECTED_DECL_ERROR] },
80+
{ code: "function test(err, data) {}", errors: [EXPECTED_DECL_ERROR] },
81+
{
82+
code: "function test(err) {errorLookingWord();}",
83+
errors: [EXPECTED_DECL_ERROR],
84+
},
85+
{
86+
code: "function test(err) {try{} catch(err) {}}",
87+
errors: [EXPECTED_DECL_ERROR],
88+
},
89+
{
90+
code:
91+
"function test(err, callback) { foo(function(err, callback) {}); }",
92+
errors: [EXPECTED_DECL_ERROR, EXPECTED_FUNC_ERROR],
93+
},
94+
{
95+
code: "var test = (err) => {};",
96+
parserOptions: { ecmaVersion: 6 },
97+
errors: [{ messageId: "expected" }],
98+
},
99+
{ code: "var test = function(err) {};", errors: [EXPECTED_FUNC_ERROR] },
100+
{
101+
code: "var test = function test(err, data) {};",
102+
errors: [EXPECTED_FUNC_ERROR],
103+
},
104+
{
105+
code: "var test = function test(err) {/* if(err){} */};",
106+
errors: [EXPECTED_FUNC_ERROR],
107+
},
108+
{
109+
code:
110+
"function test(err) {doSomethingHere(function(err){console.log(err);})}",
111+
errors: [EXPECTED_DECL_ERROR],
112+
},
113+
{
114+
code: "function test(error) {}",
115+
options: ["error"],
116+
errors: [EXPECTED_DECL_ERROR],
117+
},
118+
{
119+
code:
120+
"getData(function(err, data) {getMoreDataWith(data, function(err, moreData) {if (err) {}getEvenMoreDataWith(moreData, function(err, allOfTheThings) {if (err) {}});}); });",
121+
errors: [EXPECTED_FUNC_ERROR],
122+
},
123+
{
124+
code:
125+
"getData(function(err, data) {getMoreDataWith(data, function(err, moreData) {getEvenMoreDataWith(moreData, function(err, allOfTheThings) {if (err) {}});}); });",
126+
errors: [EXPECTED_FUNC_ERROR, EXPECTED_FUNC_ERROR],
127+
},
128+
{
129+
code:
130+
"function userHandler(err) {logThisAction(function(err) {if (err) { console.log(err); } })}",
131+
errors: [EXPECTED_DECL_ERROR],
132+
},
133+
{
134+
code:
135+
"function help() { function userHandler(err) {function tester(err) { err; process.nextTick(function() { err; }); } } }",
136+
errors: [EXPECTED_DECL_ERROR],
137+
},
138+
{
139+
code: "var test = function(anyError) { console.log(otherError); };",
140+
options: ["^.+Error$"],
141+
errors: [EXPECTED_FUNC_ERROR],
142+
},
143+
{
144+
code: "var test = function(anyError) { };",
145+
options: ["^.+Error$"],
146+
errors: [EXPECTED_FUNC_ERROR],
147+
},
148+
{
149+
code: "var test = function(err) { console.log(error); };",
150+
options: ["^(err|error)$"],
151+
errors: [EXPECTED_FUNC_ERROR],
152+
},
153+
],
154+
})

‎tests/lib/rules/no-mixed-requires.js

+168
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
/**
2+
* @author Raphael Pigulla
3+
* See LICENSE file in root directory for full license.
4+
*/
5+
"use strict"
6+
7+
const RuleTester = require("eslint").RuleTester
8+
const rule = require("../../../lib/rules/no-mixed-requires")
9+
10+
new RuleTester().run("no-mixed-requires", rule, {
11+
valid: [
12+
{ code: "var a, b = 42, c = doStuff()", options: [false] },
13+
{
14+
code:
15+
"var a = require(42), b = require(), c = require('y'), d = require(doStuff())",
16+
options: [false],
17+
},
18+
{
19+
code: "var fs = require('fs'), foo = require('foo')",
20+
options: [false],
21+
},
22+
{
23+
code:
24+
"var exec = require('child_process').exec, foo = require('foo')",
25+
options: [false],
26+
},
27+
{
28+
code: "var fs = require('fs'), foo = require('./foo')",
29+
options: [false],
30+
},
31+
{
32+
code: "var foo = require('foo'), foo2 = require('./foo')",
33+
options: [false],
34+
},
35+
{
36+
code:
37+
"var emitter = require('events').EventEmitter, fs = require('fs')",
38+
options: [false],
39+
},
40+
{
41+
code: "var foo = require(42), bar = require(getName())",
42+
options: [false],
43+
},
44+
{
45+
code: "var foo = require(42), bar = require(getName())",
46+
options: [true],
47+
},
48+
{
49+
code: "var fs = require('fs'), foo = require('./foo')",
50+
options: [{ grouping: false }],
51+
},
52+
{
53+
code: "var foo = require('foo'), bar = require(getName())",
54+
options: [false],
55+
},
56+
{ code: "var a;", options: [true] },
57+
{
58+
code:
59+
"var async = require('async'), debug = require('diagnostics')('my-module')",
60+
options: [{ allowCall: true }],
61+
},
62+
],
63+
invalid: [
64+
{
65+
code: "var fs = require('fs'), foo = 42",
66+
options: [false],
67+
errors: [
68+
{
69+
messageId: "noMixRequire",
70+
type: "VariableDeclaration",
71+
},
72+
],
73+
},
74+
{
75+
code: "var fs = require('fs'), foo",
76+
options: [false],
77+
errors: [
78+
{
79+
messageId: "noMixRequire",
80+
type: "VariableDeclaration",
81+
},
82+
],
83+
},
84+
{
85+
code:
86+
"var a = require(42), b = require(), c = require('y'), d = require(doStuff())",
87+
options: [true],
88+
errors: [
89+
{
90+
messageId: "noMixCoreModuleFileComputed",
91+
type: "VariableDeclaration",
92+
},
93+
],
94+
},
95+
{
96+
code: "var fs = require('fs'), foo = require('foo')",
97+
options: [true],
98+
errors: [
99+
{
100+
messageId: "noMixCoreModuleFileComputed",
101+
type: "VariableDeclaration",
102+
},
103+
],
104+
},
105+
{
106+
code: "var fs = require('fs'), foo = require('foo')",
107+
options: [{ grouping: true }],
108+
errors: [
109+
{
110+
messageId: "noMixCoreModuleFileComputed",
111+
type: "VariableDeclaration",
112+
},
113+
],
114+
},
115+
{
116+
code:
117+
"var exec = require('child_process').exec, foo = require('foo')",
118+
options: [true],
119+
errors: [
120+
{
121+
messageId: "noMixCoreModuleFileComputed",
122+
type: "VariableDeclaration",
123+
},
124+
],
125+
},
126+
{
127+
code: "var fs = require('fs'), foo = require('./foo')",
128+
options: [true],
129+
errors: [
130+
{
131+
messageId: "noMixCoreModuleFileComputed",
132+
type: "VariableDeclaration",
133+
},
134+
],
135+
},
136+
{
137+
code: "var foo = require('foo'), foo2 = require('./foo')",
138+
options: [true],
139+
errors: [
140+
{
141+
messageId: "noMixCoreModuleFileComputed",
142+
type: "VariableDeclaration",
143+
},
144+
],
145+
},
146+
{
147+
code: "var foo = require('foo'), bar = require(getName())",
148+
options: [true],
149+
errors: [
150+
{
151+
messageId: "noMixCoreModuleFileComputed",
152+
type: "VariableDeclaration",
153+
},
154+
],
155+
},
156+
{
157+
code:
158+
"var async = require('async'), debug = require('diagnostics').someFun('my-module')",
159+
options: [{ allowCall: true }],
160+
errors: [
161+
{
162+
messageId: "noMixRequire",
163+
type: "VariableDeclaration",
164+
},
165+
],
166+
},
167+
],
168+
})

‎tests/lib/rules/no-new-require.js

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/**
2+
* @author Wil Moore III
3+
* See LICENSE file in root directory for full license.
4+
*/
5+
"use strict"
6+
7+
const RuleTester = require("eslint").RuleTester
8+
const rule = require("../../../lib/rules/no-new-require")
9+
10+
new RuleTester().run("no-new-require", rule, {
11+
valid: [
12+
"var appHeader = require('app-header')",
13+
"var AppHeader = new (require('app-header'))",
14+
"var AppHeader = new (require('headers').appHeader)",
15+
],
16+
invalid: [
17+
{
18+
code: "var appHeader = new require('app-header')",
19+
errors: [
20+
{
21+
messageId: "noNewRequire",
22+
type: "NewExpression",
23+
},
24+
],
25+
},
26+
{
27+
code: "var appHeader = new require('headers').appHeader",
28+
errors: [
29+
{
30+
messageId: "noNewRequire",
31+
type: "NewExpression",
32+
},
33+
],
34+
},
35+
],
36+
})

‎tests/lib/rules/no-path-concat.js

+56
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
/**
2+
* @author Nicholas C. Zakas
3+
* See LICENSE file in root directory for full license.
4+
*/
5+
"use strict"
6+
7+
const RuleTester = require("eslint").RuleTester
8+
const rule = require("../../../lib/rules/no-path-concat")
9+
10+
new RuleTester().run("no-path-concat", rule, {
11+
valid: [
12+
'var fullPath = dirname + "foo.js";',
13+
'var fullPath = __dirname == "foo.js";',
14+
"if (fullPath === __dirname) {}",
15+
"if (__dirname === fullPath) {}",
16+
],
17+
18+
invalid: [
19+
{
20+
code: 'var fullPath = __dirname + "/foo.js";',
21+
errors: [
22+
{
23+
messageId: "usePathFunctions",
24+
type: "BinaryExpression",
25+
},
26+
],
27+
},
28+
{
29+
code: 'var fullPath = __filename + "/foo.js";',
30+
errors: [
31+
{
32+
messageId: "usePathFunctions",
33+
type: "BinaryExpression",
34+
},
35+
],
36+
},
37+
{
38+
code: 'var fullPath = "/foo.js" + __filename;',
39+
errors: [
40+
{
41+
messageId: "usePathFunctions",
42+
type: "BinaryExpression",
43+
},
44+
],
45+
},
46+
{
47+
code: 'var fullPath = "/foo.js" + __dirname;',
48+
errors: [
49+
{
50+
messageId: "usePathFunctions",
51+
type: "BinaryExpression",
52+
},
53+
],
54+
},
55+
],
56+
})

‎tests/lib/rules/no-process-env.js

+47
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
/**
2+
* @author Vignesh Anand
3+
* See LICENSE file in root directory for full license.
4+
*/
5+
"use strict"
6+
7+
const RuleTester = require("eslint").RuleTester
8+
const rule = require("../../../lib/rules/no-process-env")
9+
10+
new RuleTester().run("no-process-env", rule, {
11+
valid: [
12+
"Process.env",
13+
"process[env]",
14+
"process.nextTick",
15+
"process.execArgv",
16+
],
17+
18+
invalid: [
19+
{
20+
code: "process.env",
21+
errors: [
22+
{
23+
messageId: "unexpectedProcessEnv",
24+
type: "MemberExpression",
25+
},
26+
],
27+
},
28+
{
29+
code: "process.env.ENV",
30+
errors: [
31+
{
32+
messageId: "unexpectedProcessEnv",
33+
type: "MemberExpression",
34+
},
35+
],
36+
},
37+
{
38+
code: "f(process.env)",
39+
errors: [
40+
{
41+
messageId: "unexpectedProcessEnv",
42+
type: "MemberExpression",
43+
},
44+
],
45+
},
46+
],
47+
})

‎tests/lib/rules/no-process-exit.js

+42
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/**
2+
* @author Nicholas C. Zakas
3+
* See LICENSE file in root directory for full license.
4+
*/
5+
"use strict"
6+
7+
const RuleTester = require("eslint").RuleTester
8+
const rule = require("../../../lib/rules/no-process-exit")
9+
10+
new RuleTester().run("no-process-exit", rule, {
11+
valid: ["Process.exit()", "var exit = process.exit;", "f(process.exit)"],
12+
13+
invalid: [
14+
{
15+
code: "process.exit(0);",
16+
errors: [
17+
{
18+
messageId: "noProcessExit",
19+
type: "CallExpression",
20+
},
21+
],
22+
},
23+
{
24+
code: "process.exit(1);",
25+
errors: [
26+
{
27+
messageId: "noProcessExit",
28+
type: "CallExpression",
29+
},
30+
],
31+
},
32+
{
33+
code: "f(process.exit(1));",
34+
errors: [
35+
{
36+
messageId: "noProcessExit",
37+
type: "CallExpression",
38+
},
39+
],
40+
},
41+
],
42+
})
+178
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
/**
2+
* @author Christian Schulz
3+
* See LICENSE file in root directory for full license.
4+
*/
5+
"use strict"
6+
7+
const RuleTester = require("eslint").RuleTester
8+
const rule = require("../../../lib/rules/no-restricted-require")
9+
10+
new RuleTester().run("no-restricted-require", rule, {
11+
valid: [
12+
{ code: 'require("fs")', options: ["crypto"] },
13+
{ code: 'require("path")', options: ["crypto", "stream", "os"] },
14+
'require("fs ")',
15+
{ code: "require(2)", options: ["crypto"] },
16+
{ code: "require(foo)", options: ["crypto"] },
17+
{ code: "var foo = bar('crypto');", options: ["crypto"] },
18+
{ code: 'require("foo/bar");', options: ["foo"] },
19+
{
20+
code: 'var withPaths = require("foo/bar");',
21+
options: [{ paths: ["foo", "bar"] }],
22+
},
23+
{
24+
code: 'var withPatterns = require("foo/bar");',
25+
options: [{ patterns: ["foo/c*"] }],
26+
},
27+
{
28+
code: 'var withPatternsAndPaths = require("foo/bar");',
29+
options: [{ paths: ["foo"], patterns: ["foo/c*"] }],
30+
},
31+
{
32+
code: 'var withGitignores = require("foo/bar");',
33+
options: [{ paths: ["foo"], patterns: ["foo/*", "!foo/bar"] }],
34+
},
35+
],
36+
invalid: [
37+
{
38+
code: 'require("fs")',
39+
options: ["fs"],
40+
errors: [
41+
{
42+
messageId: "defaultMessage",
43+
data: { name: "fs" },
44+
type: "CallExpression",
45+
},
46+
],
47+
},
48+
{
49+
code: 'require("os ")',
50+
options: ["fs", "crypto ", "stream", "os"],
51+
errors: [
52+
{
53+
messageId: "defaultMessage",
54+
data: { name: "os" },
55+
type: "CallExpression",
56+
},
57+
],
58+
},
59+
{
60+
code: 'require("foo/bar");',
61+
options: ["foo/bar"],
62+
errors: [
63+
{
64+
messageId: "defaultMessage",
65+
data: { name: "foo/bar" },
66+
type: "CallExpression",
67+
},
68+
],
69+
},
70+
{
71+
code: 'var withPaths = require("foo/bar");',
72+
options: [{ paths: ["foo/bar"] }],
73+
errors: [
74+
{
75+
messageId: "defaultMessage",
76+
data: { name: "foo/bar" },
77+
type: "CallExpression",
78+
},
79+
],
80+
},
81+
{
82+
code: 'var withPatterns = require("foo/bar");',
83+
options: [{ patterns: ["foo/*"] }],
84+
errors: [
85+
{
86+
messageId: "patternMessage",
87+
data: { name: "foo/bar" },
88+
type: "CallExpression",
89+
},
90+
],
91+
},
92+
{
93+
code: 'var withPatternsAndPaths = require("foo/bar");',
94+
options: [{ patterns: ["foo/*"], paths: ["foo"] }],
95+
errors: [
96+
{
97+
messageId: "patternMessage",
98+
data: { name: "foo/bar" },
99+
type: "CallExpression",
100+
},
101+
],
102+
},
103+
{
104+
code: 'var withGitignores = require("foo/bar");',
105+
options: [{ patterns: ["foo/*", "!foo/baz"], paths: ["foo"] }],
106+
errors: [
107+
{
108+
messageId: "patternMessage",
109+
data: { name: "foo/bar" },
110+
type: "CallExpression",
111+
},
112+
],
113+
},
114+
{
115+
code: 'var withGitignores = require("foo");',
116+
options: [
117+
{
118+
name: "foo",
119+
message: "Please use 'bar' module instead.",
120+
},
121+
],
122+
errors: [
123+
{
124+
messageId: "customMessage",
125+
data: {
126+
name: "foo",
127+
customMessage: "Please use 'bar' module instead.",
128+
},
129+
type: "CallExpression",
130+
},
131+
],
132+
},
133+
{
134+
code: 'var withGitignores = require("bar");',
135+
options: [
136+
"foo",
137+
{
138+
name: "bar",
139+
message: "Please use 'baz' module instead.",
140+
},
141+
"baz",
142+
],
143+
errors: [
144+
{
145+
messageId: "customMessage",
146+
data: {
147+
name: "bar",
148+
customMessage: "Please use 'baz' module instead.",
149+
},
150+
type: "CallExpression",
151+
},
152+
],
153+
},
154+
{
155+
code: 'var withGitignores = require("foo");',
156+
options: [
157+
{
158+
paths: [
159+
{
160+
name: "foo",
161+
message: "Please use 'bar' module instead.",
162+
},
163+
],
164+
},
165+
],
166+
errors: [
167+
{
168+
messageId: "customMessage",
169+
data: {
170+
name: "foo",
171+
customMessage: "Please use 'bar' module instead.",
172+
},
173+
type: "CallExpression",
174+
},
175+
],
176+
},
177+
],
178+
})

‎tests/lib/rules/no-sync.js

+97
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
/**
2+
* @author Matt DuVall <http://www.mattduvall.com>
3+
* See LICENSE file in root directory for full license.
4+
*/
5+
"use strict"
6+
7+
const RuleTester = require("eslint").RuleTester
8+
const rule = require("../../../lib/rules/no-sync")
9+
10+
new RuleTester().run("no-sync", rule, {
11+
valid: [
12+
"var foo = fs.foo.foo();",
13+
{
14+
code: "var foo = fs.fooSync;",
15+
options: [{ allowAtRootLevel: true }],
16+
},
17+
{
18+
code: "if (true) {fs.fooSync();}",
19+
options: [{ allowAtRootLevel: true }],
20+
},
21+
],
22+
invalid: [
23+
{
24+
code: "var foo = fs.fooSync();",
25+
errors: [
26+
{
27+
messageId: "noSync",
28+
data: { propertyName: "fooSync" },
29+
type: "MemberExpression",
30+
},
31+
],
32+
},
33+
{
34+
code: "var foo = fs.fooSync();",
35+
options: [{ allowAtRootLevel: false }],
36+
errors: [
37+
{
38+
messageId: "noSync",
39+
data: { propertyName: "fooSync" },
40+
type: "MemberExpression",
41+
},
42+
],
43+
},
44+
{
45+
code: "if (true) {fs.fooSync();}",
46+
errors: [
47+
{
48+
messageId: "noSync",
49+
data: { propertyName: "fooSync" },
50+
type: "MemberExpression",
51+
},
52+
],
53+
},
54+
{
55+
code: "var foo = fs.fooSync;",
56+
errors: [
57+
{
58+
messageId: "noSync",
59+
data: { propertyName: "fooSync" },
60+
type: "MemberExpression",
61+
},
62+
],
63+
},
64+
{
65+
code: "function someFunction() {fs.fooSync();}",
66+
errors: [
67+
{
68+
messageId: "noSync",
69+
data: { propertyName: "fooSync" },
70+
type: "MemberExpression",
71+
},
72+
],
73+
},
74+
{
75+
code: "function someFunction() {fs.fooSync();}",
76+
options: [{ allowAtRootLevel: true }],
77+
errors: [
78+
{
79+
messageId: "noSync",
80+
data: { propertyName: "fooSync" },
81+
type: "MemberExpression",
82+
},
83+
],
84+
},
85+
{
86+
code: "var a = function someFunction() {fs.fooSync();}",
87+
options: [{ allowAtRootLevel: true }],
88+
errors: [
89+
{
90+
messageId: "noSync",
91+
data: { propertyName: "fooSync" },
92+
type: "MemberExpression",
93+
},
94+
],
95+
},
96+
],
97+
})

0 commit comments

Comments
 (0)
Please sign in to comment.