Skip to content

Commit 8788a11

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

31 files changed

+3233
-0
lines changed

Diff for: 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)

Diff for: 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)

Diff for: 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)

0 commit comments

Comments
 (0)