180 lines
4.0 KiB
JavaScript
180 lines
4.0 KiB
JavaScript
'use strict';
|
|
const getDocumentationUrl = require('./utils/get-documentation-url');
|
|
const isMethodNamed = require('./utils/is-method-named');
|
|
|
|
const MESSAGE_ID_FLATMAP = 'flat-map';
|
|
const MESSAGE_ID_SPREAD = 'spread';
|
|
|
|
const SELECTOR_SPREAD = [
|
|
// [].concat(...bar.map((i) => i))
|
|
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
'CallExpression',
|
|
|
|
// [].concat(...bar.map((i) => i))
|
|
// ^^
|
|
'[callee.object.type="ArrayExpression"]',
|
|
'[callee.object.elements.length=0]',
|
|
|
|
// [].concat(...bar.map((i) => i))
|
|
// ^^^^^^
|
|
'[callee.type="MemberExpression"]',
|
|
'[callee.computed=false]',
|
|
'[callee.property.type="Identifier"]',
|
|
'[callee.property.name="concat"]',
|
|
|
|
// [].concat(...bar.map((i) => i))
|
|
// ^^^^^^^^^^^^^^^^^^^^
|
|
'[arguments.0.type="SpreadElement"]',
|
|
|
|
// [].concat(...bar.map((i) => i))
|
|
// ^^^^^^^^^^^^^^^^^
|
|
'[arguments.0.argument.type="CallExpression"]',
|
|
|
|
// [].concat(...bar.map((i) => i))
|
|
// ^^^^^^^
|
|
'[arguments.0.argument.callee.type="MemberExpression"]',
|
|
'[arguments.0.argument.callee.computed=false]',
|
|
|
|
// [].concat(...bar.map((i) => i))
|
|
// ^^^
|
|
'[arguments.0.argument.callee.property.type="Identifier"]',
|
|
'[arguments.0.argument.callee.property.name="map"]'
|
|
].join('');
|
|
|
|
const reportFlatMap = (context, nodeFlat, nodeMap) => {
|
|
const source = context.getSourceCode();
|
|
|
|
// Node covers:
|
|
// map(…).flat();
|
|
// ^^^^
|
|
// (map(…)).flat();
|
|
// ^^^^
|
|
const flatIdentifer = nodeFlat.callee.property;
|
|
|
|
// Location will be:
|
|
// map(…).flat();
|
|
// ^
|
|
// (map(…)).flat();
|
|
// ^
|
|
const dot = source.getTokenBefore(flatIdentifer);
|
|
|
|
// Location will be:
|
|
// map(…).flat();
|
|
// ^
|
|
// (map(…)).flat();
|
|
// ^
|
|
const maybeSemicolon = source.getTokenAfter(nodeFlat);
|
|
const hasSemicolon = Boolean(maybeSemicolon) && maybeSemicolon.value === ';';
|
|
|
|
// Location will be:
|
|
// (map(…)).flat();
|
|
// ^
|
|
const tokenBetween = source.getLastTokenBetween(nodeMap, dot);
|
|
|
|
// Location will be:
|
|
// map(…).flat();
|
|
// ^
|
|
// (map(…)).flat();
|
|
// ^
|
|
const beforeSemicolon = tokenBetween || nodeMap;
|
|
|
|
// Location will be:
|
|
// map(…).flat();
|
|
// ^
|
|
// (map(…)).flat();
|
|
// ^
|
|
const fixEnd = nodeFlat.range[1];
|
|
|
|
// Location will be:
|
|
// map(…).flat();
|
|
// ^
|
|
// (map(…)).flat();
|
|
// ^
|
|
const fixStart = dot.range[0];
|
|
|
|
const mapProperty = nodeMap.callee.property;
|
|
|
|
context.report({
|
|
node: nodeFlat,
|
|
messageId: MESSAGE_ID_FLATMAP,
|
|
fix: fixer => {
|
|
const fixings = [
|
|
// Removes:
|
|
// map(…).flat();
|
|
// ^^^^^^^
|
|
// (map(…)).flat();
|
|
// ^^^^^^^
|
|
fixer.removeRange([fixStart, fixEnd]),
|
|
|
|
// Renames:
|
|
// map(…).flat();
|
|
// ^^^
|
|
// (map(…)).flat();
|
|
// ^^^
|
|
fixer.replaceText(mapProperty, 'flatMap')
|
|
];
|
|
|
|
if (hasSemicolon) {
|
|
// Moves semicolon to:
|
|
// map(…).flat();
|
|
// ^
|
|
// (map(…)).flat();
|
|
// ^
|
|
fixings.push(fixer.insertTextAfter(beforeSemicolon, ';'));
|
|
fixings.push(fixer.remove(maybeSemicolon));
|
|
}
|
|
|
|
return fixings;
|
|
}
|
|
});
|
|
};
|
|
|
|
const create = context => ({
|
|
CallExpression: node => {
|
|
if (!isMethodNamed(node, 'flat')) {
|
|
return;
|
|
}
|
|
|
|
if (node.arguments.length > 1) {
|
|
return;
|
|
}
|
|
|
|
if (
|
|
node.arguments.length === 1 &&
|
|
node.arguments[0].type === 'Literal' &&
|
|
node.arguments[0].value !== 1
|
|
) {
|
|
return;
|
|
}
|
|
|
|
const parent = node.callee.object;
|
|
|
|
if (!isMethodNamed(parent, 'map')) {
|
|
return;
|
|
}
|
|
|
|
reportFlatMap(context, node, parent);
|
|
},
|
|
[SELECTOR_SPREAD]: node => {
|
|
context.report({
|
|
node,
|
|
messageId: MESSAGE_ID_SPREAD
|
|
});
|
|
}
|
|
});
|
|
|
|
module.exports = {
|
|
create,
|
|
meta: {
|
|
type: 'suggestion',
|
|
docs: {
|
|
url: getDocumentationUrl(__filename)
|
|
},
|
|
fixable: 'code',
|
|
messages: {
|
|
[MESSAGE_ID_FLATMAP]: 'Prefer `.flatMap(…)` over `.map(…).flat()`.',
|
|
[MESSAGE_ID_SPREAD]: 'Prefer `.flatMap(…)` over `[].concat(...foo.map(…))`.'
|
|
}
|
|
}
|
|
};
|