Skip to content

Commit 88a724c

Browse files
authored
no-new-buffer: Use suggestion for unknown arguments (#1037)
1 parent db5a068 commit 88a724c

File tree

8 files changed

+424
-116
lines changed

8 files changed

+424
-116
lines changed

docs/rules/no-new-buffer.md

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,25 +2,29 @@
22

33
Enforces the use of [Buffer.from](https://nodejs.org/api/buffer.html#buffer_class_method_buffer_from_array) and [Buffer.alloc()](https://nodejs.org/api/buffer.html#buffer_class_method_buffer_alloc_size_fill_encoding) instead of [new Buffer()](https://nodejs.org/api/buffer.html#buffer_new_buffer_array), which has been deprecated since Node.js 4.
44

5-
This rule is fixable.
6-
5+
This rule is partly fixable.
76

87
## Fail
98

109
```js
1110
const buffer = new Buffer('7468697320697320612074c3a97374', 'hex');
11+
```
12+
13+
```
1214
const buffer = new Buffer([0x62, 0x75, 0x66, 0x66, 0x65, 0x72]);
1315
```
1416

1517
```js
1618
const buffer = new Buffer(10);
1719
```
1820

19-
2021
## Pass
2122

2223
```js
2324
const buffer = Buffer.from('7468697320697320612074c3a97374', 'hex');
25+
```
26+
27+
```js
2428
const buffer = Buffer.from([0x62, 0x75, 0x66, 0x66, 0x65, 0x72])
2529
```
2630

readme.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ Configure it in `package.json`.
139139
- [no-lonely-if](docs/rules/no-lonely-if.md) - Disallow `if` statements as the only statement in `if` blocks without `else`. *(fixable)*
140140
- [no-nested-ternary](docs/rules/no-nested-ternary.md) - Disallow nested ternary expressions. *(partly fixable)*
141141
- [no-new-array](docs/rules/no-new-array.md) - Disallow `new Array()`. *(partly fixable)*
142-
- [no-new-buffer](docs/rules/no-new-buffer.md) - Enforce the use of `Buffer.from()` and `Buffer.alloc()` instead of the deprecated `new Buffer()`. *(fixable)*
142+
- [no-new-buffer](docs/rules/no-new-buffer.md) - Enforce the use of `Buffer.from()` and `Buffer.alloc()` instead of the deprecated `new Buffer()`. *(partly fixable)*
143143
- [no-null](docs/rules/no-null.md) - Disallow the use of the `null` literal.
144144
- [no-object-as-default-parameter](docs/rules/no-object-as-default-parameter.md) - Disallow the use of objects as default parameters.
145145
- [no-process-exit](docs/rules/no-process-exit.md) - Disallow `process.exit()`.

rules/new-for-builtins.js

Lines changed: 3 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
const getDocumentationUrl = require('./utils/get-documentation-url');
33
const builtins = require('./utils/builtins');
44
const isShadowed = require('./utils/is-shadowed');
5-
const isNewExpressionWithParentheses = require('./utils/is-new-expression-with-parentheses');
5+
const switchNewExpressionToCallExpression = require('./utils/switch-new-expression-to-call-expression');
66

77
const messages = {
88
enforce: 'Use `new {{name}}()` instead of `{{name}}()`.',
@@ -40,7 +40,7 @@ const create = context => {
4040
}
4141
},
4242
NewExpression: node => {
43-
const {callee, range} = node;
43+
const {callee} = node;
4444
const {name} = callee;
4545

4646
if (disallowNew.has(name) && !isShadowed(context.getScope(), callee)) {
@@ -52,17 +52,7 @@ const create = context => {
5252

5353
if (name !== 'String' && name !== 'Boolean' && name !== 'Number') {
5454
problem.fix = function * (fixer) {
55-
const [start] = range;
56-
let end = start + 3; // `3` = length of `new`
57-
const textAfter = sourceCode.text.slice(end);
58-
const [leadingSpaces] = textAfter.match(/^\s*/);
59-
end += leadingSpaces.length;
60-
61-
yield fixer.removeRange([start, end]);
62-
63-
if (!isNewExpressionWithParentheses(node, sourceCode)) {
64-
yield fixer.insertTextAfter(node, '()');
65-
}
55+
yield * switchNewExpressionToCallExpression(node, sourceCode, fixer);
6656
};
6757
}
6858

rules/no-new-buffer.js

Lines changed: 63 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,81 @@
11
'use strict';
2+
const {getStaticValue} = require('eslint-utils');
23
const getDocumentationUrl = require('./utils/get-documentation-url');
4+
const switchNewExpressionToCallExpression = require('./utils/switch-new-expression-to-call-expression');
35

4-
const MESSAGE_ID = 'no-new-buffer';
6+
const ERROR = 'error';
7+
const ERROR_UNKNOWN = 'error-unknown';
8+
const SUGGESTION = 'suggestion';
59
const messages = {
6-
[MESSAGE_ID]: '`new Buffer()` is deprecated, use `Buffer.{{method}}()` instead.'
10+
[ERROR]: '`new Buffer()` is deprecated, use `Buffer.{{method}}()` instead.',
11+
[ERROR_UNKNOWN]: '`new Buffer()` is deprecated, use `Buffer.alloc()` or `Buffer.from()` instead.',
12+
[SUGGESTION]: 'Switch to `Buffer.{{method}}()`.'
713
};
814

9-
const inferMethod = arguments_ => {
10-
if (arguments_.length > 0) {
11-
const [firstArgument] = arguments_;
15+
const inferMethod = (bufferArguments, scope) => {
16+
if (bufferArguments.length !== 1) {
17+
return 'from';
18+
}
19+
20+
const [firstArgument] = bufferArguments;
21+
if (firstArgument.type === 'SpreadElement') {
22+
return;
23+
}
24+
25+
if (firstArgument.type === 'ArrayExpression' || firstArgument.type === 'TemplateLiteral') {
26+
return 'from';
27+
}
28+
29+
const staticResult = getStaticValue(firstArgument, scope);
30+
if (staticResult) {
31+
const {value} = staticResult;
32+
if (typeof value === 'number') {
33+
return 'alloc';
34+
}
35+
1236
if (
13-
firstArgument.type === 'Literal' &&
14-
typeof firstArgument.value === 'number'
37+
typeof value === 'string' ||
38+
Array.isArray(value)
1539
) {
16-
return 'alloc';
40+
return 'from';
1741
}
1842
}
19-
20-
return 'from';
2143
};
2244

45+
function fix(node, sourceCode, method) {
46+
return function * (fixer) {
47+
yield fixer.insertTextAfter(node.callee, `.${method}`);
48+
yield * switchNewExpressionToCallExpression(node, sourceCode, fixer);
49+
};
50+
}
51+
2352
const create = context => {
53+
const sourceCode = context.getSourceCode();
2454
return {
2555
'NewExpression[callee.name="Buffer"]': node => {
26-
const method = inferMethod(node.arguments);
27-
const range = [
28-
node.range[0],
29-
node.callee.range[1]
30-
];
31-
32-
context.report({
33-
node,
34-
messageId: MESSAGE_ID,
35-
data: {method},
36-
fix: fixer => fixer.replaceTextRange(range, `Buffer.${method}`)
37-
});
56+
const method = inferMethod(node.arguments, context.getScope());
57+
58+
if (method) {
59+
context.report({
60+
node,
61+
messageId: ERROR,
62+
data: {method},
63+
fix: fix(node, sourceCode, method)
64+
});
65+
} else {
66+
context.report({
67+
node,
68+
messageId: ERROR_UNKNOWN,
69+
suggest: [
70+
'from',
71+
'alloc'
72+
].map(method => ({
73+
messageId: SUGGESTION,
74+
data: {method},
75+
fix: fix(node, sourceCode, method)
76+
}))
77+
});
78+
}
3879
}
3980
};
4081
};
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
'use strict';
2+
const isNewExpressionWithParentheses = require('./is-new-expression-with-parentheses');
3+
4+
function * switchNewExpressionToCallExpression(node, sourceCode, fixer) {
5+
const [start] = node.range;
6+
let end = start + 3; // `3` = length of `new`
7+
const textAfter = sourceCode.text.slice(end);
8+
const [leadingSpaces] = textAfter.match(/^\s*/);
9+
end += leadingSpaces.length;
10+
yield fixer.removeRange([start, end]);
11+
12+
if (!isNewExpressionWithParentheses(node, sourceCode)) {
13+
yield fixer.insertTextAfter(node, '()');
14+
}
15+
}
16+
17+
module.exports = switchNewExpressionToCallExpression;

test/no-new-buffer.js

Lines changed: 60 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -1,71 +1,67 @@
11
import {outdent} from 'outdent';
22
import {test} from './utils/test.js';
33

4-
const allocError = {
5-
messageId: 'no-new-buffer',
6-
data: {method: 'alloc'}
7-
};
8-
9-
const fromError = {
10-
messageId: 'no-new-buffer',
11-
data: {method: 'from'}
12-
};
13-
14-
test({
4+
test.snapshot({
155
valid: [
16-
'const buf = Buffer.from(\'buf\')',
17-
'const buf = Buffer.from(\'7468697320697320612074c3a97374\', \'hex\')',
18-
'const buf = Buffer.from([0x62, 0x75, 0x66, 0x66, 0x65, 0x72])',
19-
'const buf = Buffer.alloc(10)'
6+
'const buffer = Buffer',
7+
'const buffer = new NotBuffer(1)',
8+
'const buffer = Buffer.from(\'buf\')',
9+
'const buffer = Buffer.from(\'7468697320697320612074c3a97374\', \'hex\')',
10+
'const buffer = Buffer.from([0x62, 0x75, 0x66, 0x66, 0x65, 0x72])',
11+
'const buffer = Buffer.alloc(10)'
2012
],
2113
invalid: [
22-
{
23-
code: 'const buf = new Buffer()',
24-
errors: [fromError],
25-
output: 'const buf = Buffer.from()'
26-
},
27-
{
28-
code: 'const buf = new Buffer(\'buf\')',
29-
errors: [fromError],
30-
output: 'const buf = Buffer.from(\'buf\')'
31-
},
32-
{
33-
code: 'const buf = new Buffer(\'7468697320697320612074c3a97374\', \'hex\')',
34-
errors: [fromError],
35-
output: 'const buf = Buffer.from(\'7468697320697320612074c3a97374\', \'hex\')'
36-
},
37-
{
38-
code: 'const buf = new Buffer([0x62, 0x75, 0x66, 0x66, 0x65, 0x72])',
39-
errors: [fromError],
40-
output: 'const buf = Buffer.from([0x62, 0x75, 0x66, 0x66, 0x65, 0x72])'
41-
},
42-
{
43-
code: 'const buf = new Buffer(10)',
44-
errors: [allocError],
45-
output: 'const buf = Buffer.alloc(10)'
46-
},
47-
{
48-
code: outdent`
49-
const ab = new ArrayBuffer(10);
50-
const buf = new Buffer(ab, 0, 2);
51-
`,
52-
errors: [fromError],
53-
output: outdent`
54-
const ab = new ArrayBuffer(10);
55-
const buf = Buffer.from(ab, 0, 2);
56-
`
57-
},
58-
{
59-
code: outdent`
60-
const buf1 = new Buffer('buf');
61-
const buf2 = new Buffer(buf1);
62-
`,
63-
errors: [fromError, fromError],
64-
output: outdent`
65-
const buf1 = Buffer.from('buf');
66-
const buf2 = Buffer.from(buf1);
67-
`
68-
}
14+
// `new Buffer(array)`
15+
// https://nodejs.org/api/buffer.html#buffer_new_buffer_array
16+
'const buffer = new Buffer([0x62, 0x75, 0x66, 0x66, 0x65, 0x72])',
17+
'const buffer = new Buffer([0x62, bar])',
18+
outdent`
19+
const array = [0x62];
20+
const buffer = new Buffer(array);
21+
`,
22+
23+
// `new Buffer(arrayBuffer[, byteOffset[, length]])`
24+
// https://nodejs.org/api/buffer.html#buffer_new_buffer_arraybuffer_byteoffset_length
25+
outdent`
26+
const arrayBuffer = new ArrayBuffer(10);
27+
const buffer = new Buffer(arrayBuffer);
28+
`,
29+
outdent`
30+
const arrayBuffer = new ArrayBuffer(10);
31+
const buffer = new Buffer(arrayBuffer, 0, );
32+
`,
33+
outdent`
34+
const arrayBuffer = new ArrayBuffer(10);
35+
const buffer = new Buffer(arrayBuffer, 0, 2);
36+
`,
37+
38+
// `new Buffer(size)`
39+
// https://nodejs.org/api/buffer.html#buffer_new_buffer_size
40+
'const buffer = new Buffer(10);',
41+
outdent`
42+
const size = 10;
43+
const buffer = new Buffer(size);
44+
`,
45+
46+
// `new Buffer(string[, encoding])`
47+
// https://nodejs.org/api/buffer.html#buffer_new_buffer_string_encoding
48+
'const buffer = new Buffer("string");',
49+
'const buffer = new Buffer("7468697320697320612074c3a97374", "hex")',
50+
outdent`
51+
const string = "string";
52+
const buffer = new Buffer(string);
53+
`,
54+
// eslint-disable-next-line no-template-curly-in-string
55+
'const buffer = new Buffer(`${unknown}`)',
56+
57+
// Unknown
58+
'const buffer = new (Buffer)(unknown)',
59+
'const buffer = new Buffer(unknown, 2)',
60+
'const buffer = new Buffer(...unknown)',
61+
62+
// Misc
63+
'const buffer = new /* comment */ Buffer()',
64+
'const buffer = new /* comment */ Buffer'
6965
]
7066
});
7167

@@ -74,13 +70,8 @@ test.typescript({
7470
invalid: [
7571
{
7672
code: 'new Buffer(input, encoding);',
77-
errors: [fromError],
78-
output: 'Buffer.from(input, encoding);'
73+
output: 'Buffer.from(input, encoding);',
74+
errors: 1
7975
}
8076
]
8177
});
82-
83-
test.snapshot([
84-
'const buf = new Buffer()',
85-
'const buf = new Buffer([0x62, 0x75, 0x66, 0x66, 0x65, 0x72])'
86-
]);

0 commit comments

Comments
 (0)