Skip to content

Commit

Permalink
require-array-join-separator: Use simple selector (#2102)
Browse files Browse the repository at this point in the history
  • Loading branch information
fisker committed May 15, 2023
1 parent 8abd753 commit d253cb1
Show file tree
Hide file tree
Showing 3 changed files with 111 additions and 33 deletions.
74 changes: 41 additions & 33 deletions rules/require-array-join-separator.js
@@ -1,45 +1,53 @@
'use strict';
const {matches, methodCallSelector, arrayPrototypeMethodSelector} = require('./selectors/index.js');
const {appendArgument} = require('./fix/index.js');
const {isMethodCall} = require('./ast/index.js');
const {isArrayPrototypeProperty} = require('./utils/index.js');

const MESSAGE_ID = 'require-array-join-separator';
const messages = {
[MESSAGE_ID]: 'Missing the separator argument.',
};

const selector = matches([
// `foo.join()`
methodCallSelector({
method: 'join',
argumentsLength: 0,
includeOptionalMember: true,
}),
// `[].join.call(foo)` and `Array.prototype.join.call(foo)`
[
methodCallSelector({method: 'call', argumentsLength: 1}),
arrayPrototypeMethodSelector({path: 'callee.object', method: 'join'}),
].join(''),
]);

/** @param {import('eslint').Rule.RuleContext} context */
const create = context => {
const {sourceCode} = context;
return {
[selector](node) {
const [penultimateToken, lastToken] = sourceCode.getLastTokens(node, 2);
const isPrototypeMethod = node.arguments.length === 1;
return {
loc: {
start: penultimateToken.loc[isPrototypeMethod ? 'end' : 'start'],
end: lastToken.loc.end,
},
messageId: MESSAGE_ID,
/** @param {import('eslint').Rule.RuleFixer} fixer */
fix: fixer => appendArgument(fixer, node, '\',\'', sourceCode),
};
},
};
};
const create = context => ({
CallExpression(node) {
if (!(
// `foo.join()`
isMethodCall(node, {
method: 'join',
argumentsLength: 0,
optionalCall: false,
})
// `[].join.call(foo)` and `Array.prototype.join.call(foo)`
|| (
isMethodCall(node, {
method: 'call',
argumentsLength: 1,
optionalCall: false,
optionalMember: false,
})
&& isArrayPrototypeProperty(node.callee.object, {
property: 'join',
})
)
)) {
return;
}

const {sourceCode} = context;
const [penultimateToken, lastToken] = sourceCode.getLastTokens(node, 2);
const isPrototypeMethod = node.arguments.length === 1;
return {
loc: {
start: penultimateToken.loc[isPrototypeMethod ? 'end' : 'start'],
end: lastToken.loc.end,
},
messageId: MESSAGE_ID,
/** @param {import('eslint').Rule.RuleFixer} fixer */
fix: fixer => appendArgument(fixer, node, '\',\'', sourceCode),
};
},
});

/** @type {import('eslint').Rule.RuleModule} */
module.exports = {
Expand Down
63 changes: 63 additions & 0 deletions rules/utils/array-or-object-prototype-property.js
@@ -0,0 +1,63 @@
'use strict';
const {isMemberExpression} = require('../ast/index.js');

/**
@param {
{
object?: string,
method?: string,
methods?: string[],
}
} [options]
@returns {string}
*/
function isPrototypeProperty(node, options) {
const {
object,
property,
properties,
} = {
property: '',
properties: [],
...options,
};

if (!isMemberExpression(node, {
property,
properties,
optional: false,
})) {
return;
}

const objectNode = node.object;

return (
// `Object.prototype.method` or `Array.prototype.method`
isMemberExpression(objectNode, {
object,
property: 'prototype',
optional: false,
})
// `[].method`
|| (
object === 'Array'
&& objectNode.type === 'ArrayExpression'
&& objectNode.elements.length === 0
)
// `{}.method`
|| (
object === 'Object'
&& objectNode.type === 'ObjectExpression'
&& objectNode.properties.length === 0
)
);
}

const isArrayPrototypeProperty = (node, options) => isPrototypeProperty(node, {...options, object: 'Array'});
const isObjectPrototypeProperty = (node, options) => isPrototypeProperty(node, {...options, object: 'Object'});

module.exports = {
isArrayPrototypeProperty,
isObjectPrototypeProperty,
};
7 changes: 7 additions & 0 deletions rules/utils/index.js
Expand Up @@ -8,13 +8,20 @@ const {
getParenthesizedText,
} = require('./parentheses.js');

const {
isArrayPrototypeProperty,
isObjectPrototypeProperty,
} = require('./array-or-object-prototype-property.js');

module.exports = {
getParentheses,
getParenthesizedRange,
getParenthesizedText,
getParenthesizedTimes,
isArrayPrototypeProperty,
isNodeValueNotDomNode: require('./is-node-value-not-dom-node.js'),
isNodeValueNotFunction: require('./is-node-value-not-function.js'),
isObjectPrototypeProperty,
isParenthesized,
isValueNotUsable: require('./is-value-not-usable.js'),
needsSemicolon: require('./needs-semicolon.js'),
Expand Down

0 comments on commit d253cb1

Please sign in to comment.