diff --git a/docs/rules/no-new-buffer.md b/docs/rules/no-new-buffer.md index 10da7f54b6..33c64756df 100644 --- a/docs/rules/no-new-buffer.md +++ b/docs/rules/no-new-buffer.md @@ -2,13 +2,15 @@ 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. -This rule is fixable. - +This rule is partly fixable. ## Fail ```js const buffer = new Buffer('7468697320697320612074c3a97374', 'hex'); +``` + +``` const buffer = new Buffer([0x62, 0x75, 0x66, 0x66, 0x65, 0x72]); ``` @@ -16,11 +18,13 @@ const buffer = new Buffer([0x62, 0x75, 0x66, 0x66, 0x65, 0x72]); const buffer = new Buffer(10); ``` - ## Pass ```js const buffer = Buffer.from('7468697320697320612074c3a97374', 'hex'); +``` + +```js const buffer = Buffer.from([0x62, 0x75, 0x66, 0x66, 0x65, 0x72]) ``` diff --git a/readme.md b/readme.md index d933b45f22..4ee1043694 100644 --- a/readme.md +++ b/readme.md @@ -137,7 +137,7 @@ Configure it in `package.json`. - [no-lonely-if](docs/rules/no-lonely-if.md) - Disallow `if` statements as the only statement in `if` blocks without `else`. *(fixable)* - [no-nested-ternary](docs/rules/no-nested-ternary.md) - Disallow nested ternary expressions. *(partly fixable)* - [no-new-array](docs/rules/no-new-array.md) - Disallow `new Array()`. *(partly fixable)* -- [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)* +- [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)* - [no-null](docs/rules/no-null.md) - Disallow the use of the `null` literal. - [no-object-as-default-parameter](docs/rules/no-object-as-default-parameter.md) - Disallow the use of objects as default parameters. - [no-process-exit](docs/rules/no-process-exit.md) - Disallow `process.exit()`. diff --git a/rules/new-for-builtins.js b/rules/new-for-builtins.js index 8faf1a380f..e91dd78f99 100644 --- a/rules/new-for-builtins.js +++ b/rules/new-for-builtins.js @@ -2,7 +2,7 @@ const getDocumentationUrl = require('./utils/get-documentation-url'); const builtins = require('./utils/builtins'); const isShadowed = require('./utils/is-shadowed'); -const isNewExpressionWithParentheses = require('./utils/is-new-expression-with-parentheses'); +const switchNewExpressionToCallExpression = require('./utils/switch-new-expression-to-call-expression'); const messages = { enforce: 'Use `new {{name}}()` instead of `{{name}}()`.', @@ -40,7 +40,7 @@ const create = context => { } }, NewExpression: node => { - const {callee, range} = node; + const {callee} = node; const {name} = callee; if (disallowNew.has(name) && !isShadowed(context.getScope(), callee)) { @@ -52,17 +52,7 @@ const create = context => { if (name !== 'String' && name !== 'Boolean' && name !== 'Number') { problem.fix = function * (fixer) { - const [start] = range; - let end = start + 3; // `3` = length of `new` - const textAfter = sourceCode.text.slice(end); - const [leadingSpaces] = textAfter.match(/^\s*/); - end += leadingSpaces.length; - - yield fixer.removeRange([start, end]); - - if (!isNewExpressionWithParentheses(node, sourceCode)) { - yield fixer.insertTextAfter(node, '()'); - } + yield * switchNewExpressionToCallExpression(node, sourceCode, fixer); }; } diff --git a/rules/no-new-buffer.js b/rules/no-new-buffer.js index a05c3ffcab..24aaea7394 100644 --- a/rules/no-new-buffer.js +++ b/rules/no-new-buffer.js @@ -1,40 +1,81 @@ 'use strict'; +const {getStaticValue} = require('eslint-utils'); const getDocumentationUrl = require('./utils/get-documentation-url'); +const switchNewExpressionToCallExpression = require('./utils/switch-new-expression-to-call-expression'); -const MESSAGE_ID = 'no-new-buffer'; +const ERROR = 'error'; +const ERROR_UNKNOWN = 'error-unknown'; +const SUGGESTION = 'suggestion'; const messages = { - [MESSAGE_ID]: '`new Buffer()` is deprecated, use `Buffer.{{method}}()` instead.' + [ERROR]: '`new Buffer()` is deprecated, use `Buffer.{{method}}()` instead.', + [ERROR_UNKNOWN]: '`new Buffer()` is deprecated, use `Buffer.alloc()` or `Buffer.from()` instead.', + [SUGGESTION]: 'Switch to `Buffer.{{method}}()`.' }; -const inferMethod = arguments_ => { - if (arguments_.length > 0) { - const [firstArgument] = arguments_; +const inferMethod = (bufferArguments, scope) => { + if (bufferArguments.length !== 1) { + return 'from'; + } + + const [firstArgument] = bufferArguments; + if (firstArgument.type === 'SpreadElement') { + return; + } + + if (firstArgument.type === 'ArrayExpression' || firstArgument.type === 'TemplateLiteral') { + return 'from'; + } + + const staticResult = getStaticValue(firstArgument, scope); + if (staticResult) { + const {value} = staticResult; + if (typeof value === 'number') { + return 'alloc'; + } + if ( - firstArgument.type === 'Literal' && - typeof firstArgument.value === 'number' + typeof value === 'string' || + Array.isArray(value) ) { - return 'alloc'; + return 'from'; } } - - return 'from'; }; +function fix(node, sourceCode, method) { + return function * (fixer) { + yield fixer.insertTextAfter(node.callee, `.${method}`); + yield * switchNewExpressionToCallExpression(node, sourceCode, fixer); + }; +} + const create = context => { + const sourceCode = context.getSourceCode(); return { 'NewExpression[callee.name="Buffer"]': node => { - const method = inferMethod(node.arguments); - const range = [ - node.range[0], - node.callee.range[1] - ]; - - context.report({ - node, - messageId: MESSAGE_ID, - data: {method}, - fix: fixer => fixer.replaceTextRange(range, `Buffer.${method}`) - }); + const method = inferMethod(node.arguments, context.getScope()); + + if (method) { + context.report({ + node, + messageId: ERROR, + data: {method}, + fix: fix(node, sourceCode, method) + }); + } else { + context.report({ + node, + messageId: ERROR_UNKNOWN, + suggest: [ + 'from', + 'alloc' + ].map(method => ({ + messageId: SUGGESTION, + data: {method}, + fix: fix(node, sourceCode, method) + })) + }); + } } }; }; diff --git a/rules/utils/switch-new-expression-to-call-expression.js b/rules/utils/switch-new-expression-to-call-expression.js new file mode 100644 index 0000000000..c7a19e397c --- /dev/null +++ b/rules/utils/switch-new-expression-to-call-expression.js @@ -0,0 +1,17 @@ +'use strict'; +const isNewExpressionWithParentheses = require('./is-new-expression-with-parentheses'); + +function * switchNewExpressionToCallExpression(node, sourceCode, fixer) { + const [start] = node.range; + let end = start + 3; // `3` = length of `new` + const textAfter = sourceCode.text.slice(end); + const [leadingSpaces] = textAfter.match(/^\s*/); + end += leadingSpaces.length; + yield fixer.removeRange([start, end]); + + if (!isNewExpressionWithParentheses(node, sourceCode)) { + yield fixer.insertTextAfter(node, '()'); + } +} + +module.exports = switchNewExpressionToCallExpression; diff --git a/test/no-new-buffer.js b/test/no-new-buffer.js index 822216513f..0a41226fe9 100644 --- a/test/no-new-buffer.js +++ b/test/no-new-buffer.js @@ -1,71 +1,67 @@ import {outdent} from 'outdent'; import {test} from './utils/test.js'; -const allocError = { - messageId: 'no-new-buffer', - data: {method: 'alloc'} -}; - -const fromError = { - messageId: 'no-new-buffer', - data: {method: 'from'} -}; - -test({ +test.snapshot({ valid: [ - 'const buf = Buffer.from(\'buf\')', - 'const buf = Buffer.from(\'7468697320697320612074c3a97374\', \'hex\')', - 'const buf = Buffer.from([0x62, 0x75, 0x66, 0x66, 0x65, 0x72])', - 'const buf = Buffer.alloc(10)' + 'const buffer = Buffer', + 'const buffer = new NotBuffer(1)', + 'const buffer = Buffer.from(\'buf\')', + 'const buffer = Buffer.from(\'7468697320697320612074c3a97374\', \'hex\')', + 'const buffer = Buffer.from([0x62, 0x75, 0x66, 0x66, 0x65, 0x72])', + 'const buffer = Buffer.alloc(10)' ], invalid: [ - { - code: 'const buf = new Buffer()', - errors: [fromError], - output: 'const buf = Buffer.from()' - }, - { - code: 'const buf = new Buffer(\'buf\')', - errors: [fromError], - output: 'const buf = Buffer.from(\'buf\')' - }, - { - code: 'const buf = new Buffer(\'7468697320697320612074c3a97374\', \'hex\')', - errors: [fromError], - output: 'const buf = Buffer.from(\'7468697320697320612074c3a97374\', \'hex\')' - }, - { - code: 'const buf = new Buffer([0x62, 0x75, 0x66, 0x66, 0x65, 0x72])', - errors: [fromError], - output: 'const buf = Buffer.from([0x62, 0x75, 0x66, 0x66, 0x65, 0x72])' - }, - { - code: 'const buf = new Buffer(10)', - errors: [allocError], - output: 'const buf = Buffer.alloc(10)' - }, - { - code: outdent` - const ab = new ArrayBuffer(10); - const buf = new Buffer(ab, 0, 2); - `, - errors: [fromError], - output: outdent` - const ab = new ArrayBuffer(10); - const buf = Buffer.from(ab, 0, 2); - ` - }, - { - code: outdent` - const buf1 = new Buffer('buf'); - const buf2 = new Buffer(buf1); - `, - errors: [fromError, fromError], - output: outdent` - const buf1 = Buffer.from('buf'); - const buf2 = Buffer.from(buf1); - ` - } + // `new Buffer(array)` + // https://nodejs.org/api/buffer.html#buffer_new_buffer_array + 'const buffer = new Buffer([0x62, 0x75, 0x66, 0x66, 0x65, 0x72])', + 'const buffer = new Buffer([0x62, bar])', + outdent` + const array = [0x62]; + const buffer = new Buffer(array); + `, + + // `new Buffer(arrayBuffer[, byteOffset[, length]])` + // https://nodejs.org/api/buffer.html#buffer_new_buffer_arraybuffer_byteoffset_length + outdent` + const arrayBuffer = new ArrayBuffer(10); + const buffer = new Buffer(arrayBuffer); + `, + outdent` + const arrayBuffer = new ArrayBuffer(10); + const buffer = new Buffer(arrayBuffer, 0, ); + `, + outdent` + const arrayBuffer = new ArrayBuffer(10); + const buffer = new Buffer(arrayBuffer, 0, 2); + `, + + // `new Buffer(size)` + // https://nodejs.org/api/buffer.html#buffer_new_buffer_size + 'const buffer = new Buffer(10);', + outdent` + const size = 10; + const buffer = new Buffer(size); + `, + + // `new Buffer(string[, encoding])` + // https://nodejs.org/api/buffer.html#buffer_new_buffer_string_encoding + 'const buffer = new Buffer("string");', + 'const buffer = new Buffer("7468697320697320612074c3a97374", "hex")', + outdent` + const string = "string"; + const buffer = new Buffer(string); + `, + // eslint-disable-next-line no-template-curly-in-string + 'const buffer = new Buffer(`${unknown}`)', + + // Unknown + 'const buffer = new (Buffer)(unknown)', + 'const buffer = new Buffer(unknown, 2)', + 'const buffer = new Buffer(...unknown)', + + // Misc + 'const buffer = new /* comment */ Buffer()', + 'const buffer = new /* comment */ Buffer' ] }); @@ -74,13 +70,8 @@ test.typescript({ invalid: [ { code: 'new Buffer(input, encoding);', - errors: [fromError], - output: 'Buffer.from(input, encoding);' + output: 'Buffer.from(input, encoding);', + errors: 1 } ] }); - -test.snapshot([ - 'const buf = new Buffer()', - 'const buf = new Buffer([0x62, 0x75, 0x66, 0x66, 0x65, 0x72])' -]); diff --git a/test/snapshots/no-new-buffer.js.md b/test/snapshots/no-new-buffer.js.md index 329c957c32..693b9678a8 100644 --- a/test/snapshots/no-new-buffer.js.md +++ b/test/snapshots/no-new-buffer.js.md @@ -5,33 +5,298 @@ The actual snapshot is saved in `no-new-buffer.js.snap`. Generated by [AVA](https://avajs.dev). ## Invalid #1 - 1 | const buf = new Buffer() + 1 | const buffer = new Buffer([0x62, 0x75, 0x66, 0x66, 0x65, 0x72]) > Output `␊ - 1 | const buf = Buffer.from()␊ + 1 | const buffer = Buffer.from([0x62, 0x75, 0x66, 0x66, 0x65, 0x72])␊ ` > Error 1/1 `␊ - > 1 | const buf = new Buffer()␊ - | ^^^^^^^^^^^^ `new Buffer()` is deprecated, use `Buffer.from()` instead.␊ + > 1 | const buffer = new Buffer([0x62, 0x75, 0x66, 0x66, 0x65, 0x72])␊ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `new Buffer()` is deprecated, use `Buffer.from()` instead.␊ ` ## Invalid #2 - 1 | const buf = new Buffer([0x62, 0x75, 0x66, 0x66, 0x65, 0x72]) + 1 | const buffer = new Buffer([0x62, bar]) > Output `␊ - 1 | const buf = Buffer.from([0x62, 0x75, 0x66, 0x66, 0x65, 0x72])␊ + 1 | const buffer = Buffer.from([0x62, bar])␊ ` > Error 1/1 `␊ - > 1 | const buf = new Buffer([0x62, 0x75, 0x66, 0x66, 0x65, 0x72])␊ - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `new Buffer()` is deprecated, use `Buffer.from()` instead.␊ + > 1 | const buffer = new Buffer([0x62, bar])␊ + | ^^^^^^^^^^^^^^^^^^^^^^^ `new Buffer()` is deprecated, use `Buffer.from()` instead.␊ + ` + +## Invalid #3 + 1 | const array = [0x62]; + 2 | const buffer = new Buffer(array); + +> Output + + `␊ + 1 | const array = [0x62];␊ + 2 | const buffer = Buffer.from(array);␊ + ` + +> Error 1/1 + + `␊ + 1 | const array = [0x62];␊ + > 2 | const buffer = new Buffer(array);␊ + | ^^^^^^^^^^^^^^^^^ `new Buffer()` is deprecated, use `Buffer.from()` instead.␊ + ` + +## Invalid #4 + 1 | const arrayBuffer = new ArrayBuffer(10); + 2 | const buffer = new Buffer(arrayBuffer); + +> Error 1/1 + + `␊ + 1 | const arrayBuffer = new ArrayBuffer(10);␊ + > 2 | const buffer = new Buffer(arrayBuffer);␊ + | ^^^^^^^^^^^^^^^^^^^^^^^ `new Buffer()` is deprecated, use `Buffer.alloc()` or `Buffer.from()` instead.␊ + ␊ + --------------------------------------------------------------------------------␊ + Suggestion 1/2: Switch to `Buffer.from()`.␊ + 1 | const arrayBuffer = new ArrayBuffer(10);␊ + 2 | const buffer = Buffer.from(arrayBuffer);␊ + ␊ + --------------------------------------------------------------------------------␊ + Suggestion 2/2: Switch to `Buffer.alloc()`.␊ + 1 | const arrayBuffer = new ArrayBuffer(10);␊ + 2 | const buffer = Buffer.alloc(arrayBuffer);␊ + ` + +## Invalid #5 + 1 | const arrayBuffer = new ArrayBuffer(10); + 2 | const buffer = new Buffer(arrayBuffer, 0, ); + +> Output + + `␊ + 1 | const arrayBuffer = new ArrayBuffer(10);␊ + 2 | const buffer = Buffer.from(arrayBuffer, 0, );␊ + ` + +> Error 1/1 + + `␊ + 1 | const arrayBuffer = new ArrayBuffer(10);␊ + > 2 | const buffer = new Buffer(arrayBuffer, 0, );␊ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `new Buffer()` is deprecated, use `Buffer.from()` instead.␊ + ` + +## Invalid #6 + 1 | const arrayBuffer = new ArrayBuffer(10); + 2 | const buffer = new Buffer(arrayBuffer, 0, 2); + +> Output + + `␊ + 1 | const arrayBuffer = new ArrayBuffer(10);␊ + 2 | const buffer = Buffer.from(arrayBuffer, 0, 2);␊ + ` + +> Error 1/1 + + `␊ + 1 | const arrayBuffer = new ArrayBuffer(10);␊ + > 2 | const buffer = new Buffer(arrayBuffer, 0, 2);␊ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `new Buffer()` is deprecated, use `Buffer.from()` instead.␊ + ` + +## Invalid #7 + 1 | const buffer = new Buffer(10); + +> Output + + `␊ + 1 | const buffer = Buffer.alloc(10);␊ + ` + +> Error 1/1 + + `␊ + > 1 | const buffer = new Buffer(10);␊ + | ^^^^^^^^^^^^^^ `new Buffer()` is deprecated, use `Buffer.alloc()` instead.␊ + ` + +## Invalid #8 + 1 | const size = 10; + 2 | const buffer = new Buffer(size); + +> Output + + `␊ + 1 | const size = 10;␊ + 2 | const buffer = Buffer.alloc(size);␊ + ` + +> Error 1/1 + + `␊ + 1 | const size = 10;␊ + > 2 | const buffer = new Buffer(size);␊ + | ^^^^^^^^^^^^^^^^ `new Buffer()` is deprecated, use `Buffer.alloc()` instead.␊ + ` + +## Invalid #9 + 1 | const buffer = new Buffer("string"); + +> Output + + `␊ + 1 | const buffer = Buffer.from("string");␊ + ` + +> Error 1/1 + + `␊ + > 1 | const buffer = new Buffer("string");␊ + | ^^^^^^^^^^^^^^^^^^^^ `new Buffer()` is deprecated, use `Buffer.from()` instead.␊ + ` + +## Invalid #10 + 1 | const buffer = new Buffer("7468697320697320612074c3a97374", "hex") + +> Output + + `␊ + 1 | const buffer = Buffer.from("7468697320697320612074c3a97374", "hex")␊ + ` + +> Error 1/1 + + `␊ + > 1 | const buffer = new Buffer("7468697320697320612074c3a97374", "hex")␊ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `new Buffer()` is deprecated, use `Buffer.from()` instead.␊ + ` + +## Invalid #11 + 1 | const string = "string"; + 2 | const buffer = new Buffer(string); + +> Output + + `␊ + 1 | const string = "string";␊ + 2 | const buffer = Buffer.from(string);␊ + ` + +> Error 1/1 + + `␊ + 1 | const string = "string";␊ + > 2 | const buffer = new Buffer(string);␊ + | ^^^^^^^^^^^^^^^^^^ `new Buffer()` is deprecated, use `Buffer.from()` instead.␊ + ` + +## Invalid #12 + 1 | const buffer = new Buffer(`${unknown}`) + +> Output + + `␊ + 1 | const buffer = Buffer.from(`${unknown}`)␊ + ` + +> Error 1/1 + + `␊ + > 1 | const buffer = new Buffer(`${unknown}`)␊ + | ^^^^^^^^^^^^^^^^^^^^^^^^ `new Buffer()` is deprecated, use `Buffer.from()` instead.␊ + ` + +## Invalid #13 + 1 | const buffer = new (Buffer)(unknown) + +> Error 1/1 + + `␊ + > 1 | const buffer = new (Buffer)(unknown)␊ + | ^^^^^^^^^^^^^^^^^^^^^ `new Buffer()` is deprecated, use `Buffer.alloc()` or `Buffer.from()` instead.␊ + ␊ + --------------------------------------------------------------------------------␊ + Suggestion 1/2: Switch to `Buffer.from()`.␊ + 1 | const buffer = (Buffer.from)(unknown)␊ + ␊ + --------------------------------------------------------------------------------␊ + Suggestion 2/2: Switch to `Buffer.alloc()`.␊ + 1 | const buffer = (Buffer.alloc)(unknown)␊ + ` + +## Invalid #14 + 1 | const buffer = new Buffer(unknown, 2) + +> Output + + `␊ + 1 | const buffer = Buffer.from(unknown, 2)␊ + ` + +> Error 1/1 + + `␊ + > 1 | const buffer = new Buffer(unknown, 2)␊ + | ^^^^^^^^^^^^^^^^^^^^^^ `new Buffer()` is deprecated, use `Buffer.from()` instead.␊ + ` + +## Invalid #15 + 1 | const buffer = new Buffer(...unknown) + +> Error 1/1 + + `␊ + > 1 | const buffer = new Buffer(...unknown)␊ + | ^^^^^^^^^^^^^^^^^^^^^^ `new Buffer()` is deprecated, use `Buffer.alloc()` or `Buffer.from()` instead.␊ + ␊ + --------------------------------------------------------------------------------␊ + Suggestion 1/2: Switch to `Buffer.from()`.␊ + 1 | const buffer = Buffer.from(...unknown)␊ + ␊ + --------------------------------------------------------------------------------␊ + Suggestion 2/2: Switch to `Buffer.alloc()`.␊ + 1 | const buffer = Buffer.alloc(...unknown)␊ + ` + +## Invalid #16 + 1 | const buffer = new /* comment */ Buffer() + +> Output + + `␊ + 1 | const buffer = /* comment */ Buffer.from()␊ + ` + +> Error 1/1 + + `␊ + > 1 | const buffer = new /* comment */ Buffer()␊ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ `new Buffer()` is deprecated, use `Buffer.from()` instead.␊ + ` + +## Invalid #17 + 1 | const buffer = new /* comment */ Buffer + +> Output + + `␊ + 1 | const buffer = /* comment */ Buffer.from()␊ + ` + +> Error 1/1 + + `␊ + > 1 | const buffer = new /* comment */ Buffer␊ + | ^^^^^^^^^^^^^^^^^^^^^^^^ `new Buffer()` is deprecated, use `Buffer.from()` instead.␊ ` diff --git a/test/snapshots/no-new-buffer.js.snap b/test/snapshots/no-new-buffer.js.snap index e686737633..81653b702f 100644 Binary files a/test/snapshots/no-new-buffer.js.snap and b/test/snapshots/no-new-buffer.js.snap differ