/
no-useless-undefined.js
103 lines (90 loc) · 2.81 KB
/
no-useless-undefined.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
'use strict';
const {isCommaToken} = require('eslint-utils');
const getDocumentationUrl = require('./utils/get-documentation-url');
const messageId = 'no-useless-undefined';
const getSelector = (parent, property) =>
`${parent} > Identifier.${property}[name="undefined"]`;
// `return undefined`
const returnSelector = getSelector('ReturnStatement', 'argument');
// `yield undefined`
const yieldSelector = getSelector('YieldExpression', 'argument');
// `() => undefined`
const arrowFunctionSelector = getSelector('ArrowFunctionExpression', 'body');
// `let foo = undefined` / `var foo = undefined`
const variableInitSelector = getSelector(
[
'VariableDeclaration',
'[kind!="const"]',
'>',
'VariableDeclarator'
].join(''),
'init'
);
// `const {foo = undefined} = {}`
const assignmentPatternSelector = getSelector('AssignmentPattern', 'right');
// `foo(bar, undefined)`
// TODO: Use this selector and remove `ifLastArgument` function
// esquery throws when use `:last-child` with `typescript-eslint`,
// maybe because ESLint hasn't support OptionalCallExpression
// const lastArgumentSelector = getSelector('CallExpression', 'arguments:last-child');
const lastArgumentSelector = getSelector('CallExpression', 'arguments');
const ifLastArgument = listener => node => {
const argumentNodes = node.parent.arguments;
if (argumentNodes[argumentNodes.length - 1] === node) {
listener(node);
}
};
const removeNode = (node, fixer) => fixer.remove(node);
const create = context => {
const listener = fix => node => {
context.report({
node,
messageId,
fix: fixer => fix(node, fixer)
});
};
return {
[returnSelector]: listener(removeNode),
[yieldSelector]: listener(removeNode),
[arrowFunctionSelector]: listener(
(node, fixer) => fixer.replaceText(node, '{}')
),
[variableInitSelector]: listener(
(node, fixer) => fixer.removeRange([node.parent.id.range[1], node.range[1]])
),
[assignmentPatternSelector]: listener(
(node, fixer) => fixer.removeRange([node.parent.left.range[1], node.range[1]])
),
[lastArgumentSelector]: ifLastArgument(listener(
(node, fixer) => {
const argumentNodes = node.parent.arguments;
const previousArgument = argumentNodes[argumentNodes.length - 2];
let [start, end] = node.range;
if (previousArgument) {
start = previousArgument.range[1];
} else {
// If it's the only argument, and there is trailing comma, we need remove it.
const tokenAfter = context.getTokenAfter(node);
if (isCommaToken(tokenAfter)) {
end = tokenAfter.range[1];
}
}
return fixer.removeRange([start, end]);
}
))
};
};
module.exports = {
create,
meta: {
type: 'suggestion',
docs: {
url: getDocumentationUrl(__filename)
},
messages: {
// [TBD]: better message
[messageId]: '`undefined` is useless.'
},
fixable: 'code'
}
};