Skip to content

Commit

Permalink
prefer-spread: Ignore Array#join().concat() and (a + b).concat() (
Browse files Browse the repository at this point in the history
  • Loading branch information
fisker committed Jul 18, 2022
1 parent c32e02e commit ab71971
Show file tree
Hide file tree
Showing 4 changed files with 47 additions and 11 deletions.
35 changes: 24 additions & 11 deletions rules/prefer-spread.js
@@ -1,6 +1,6 @@
'use strict';
const {isParenthesized, getStaticValue, isCommaToken, hasSideEffect} = require('eslint-utils');
const {methodCallSelector, not} = require('./selectors/index.js');
const {methodCallSelector} = require('./selectors/index.js');
const needsSemicolon = require('./utils/needs-semicolon.js');
const {getParenthesizedRange, getParenthesizedText} = require('./utils/parentheses.js');
const shouldAddParenthesesToSpreadElementArgument = require('./utils/should-add-parentheses-to-spread-element-argument.js');
Expand All @@ -11,6 +11,7 @@ const {
removeMethodCall,
} = require('./fix/index.js');
const {isLiteral} = require('./ast/index.js');
const isMethodNamed = require('./utils/is-method-named.js');

const ERROR_ARRAY_FROM = 'array-from';
const ERROR_ARRAY_CONCAT = 'array-concat';
Expand Down Expand Up @@ -44,15 +45,7 @@ const arrayFromCallSelector = [
'[arguments.0.type!="ObjectExpression"]',
].join('');

const arrayConcatCallSelector = [
methodCallSelector('concat'),
not(
[
'Literal',
'TemplateLiteral',
].map(type => `[callee.object.type="${type}"]`),
),
].join('');
const arrayConcatCallSelector = methodCallSelector('concat');

const arraySliceCallSelector = [
methodCallSelector({
Expand Down Expand Up @@ -320,6 +313,26 @@ function isClassName(node) {
return /^[A-Z]./.test(name) && name.toUpperCase() !== name;
}

function isNotArray(node, scope) {
if (
node.type === 'TemplateLiteral'
|| node.type === 'Literal'
|| node.type === 'BinaryExpression'
|| isClassName(node)
// `foo.join()`
|| (isMethodNamed(node, 'join') && node.arguments.length <= 1)
) {
return true;
}

const staticValue = getStaticValue(node, scope);
if (staticValue && !Array.isArray(staticValue.value)) {
return true;
}

return false;
}

/** @param {import('eslint').Rule.RuleContext} context */
const create = context => {
const sourceCode = context.getSourceCode();
Expand All @@ -335,7 +348,7 @@ const create = context => {
[arrayConcatCallSelector](node) {
const {object} = node.callee;

if (isClassName(object)) {
if (isNotArray(object, context.getScope())) {
return;
}

Expand Down
7 changes: 7 additions & 0 deletions test/prefer-spread.mjs
Expand Up @@ -180,6 +180,11 @@ test.snapshot({
'Foo.concat(1)',
'FooBar.concat(1)',
'global.Buffer.concat([])',
// #1809
'["1", "2"].join(",").concat("...")',
'foo.join(",").concat("...")',
'foo.join().concat(bar)',
'(a + b).concat(c)',
],
invalid: [
'[1].concat(2)',
Expand Down Expand Up @@ -288,6 +293,8 @@ test.snapshot({
const baz = [2];
call(foo, ...[bar].concat(baz));
`,
// This not considered `Array#join()` since there are more than one argument
'foo.join(foo, bar).concat("...")',
],
});

Expand Down
16 changes: 16 additions & 0 deletions test/snapshots/prefer-spread.mjs.md
Expand Up @@ -2034,6 +2034,22 @@ Generated by [AVA](https://avajs.dev).
| ^^^^^^ Prefer the spread operator over \`Array#concat(…)\`.␊
`

## Invalid #68
1 | foo.join(foo, bar).concat("...")

> Output
`␊
1 | [...foo.join(foo, bar), "..."]␊
`

> Error 1/1
`␊
> 1 | foo.join(foo, bar).concat("...")␊
| ^^^^^^ Prefer the spread operator over \`Array#concat(…)\`.␊
`

## Invalid #1
1 | array.slice()

Expand Down
Binary file modified test/snapshots/prefer-spread.mjs.snap
Binary file not shown.

0 comments on commit ab71971

Please sign in to comment.