-
-
Notifications
You must be signed in to change notification settings - Fork 353
/
prefer-query-selector.js
126 lines (104 loc) · 3.14 KB
/
prefer-query-selector.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
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
'use strict';
const getDocumentationUrl = require('./utils/get-documentation-url');
const forbiddenIdentifierNames = new Map([
['getElementById', 'querySelector'],
['getElementsByClassName', 'querySelectorAll'],
['getElementsByTagName', 'querySelectorAll']
]);
const getReplacementForId = value => `#${value}`;
const getReplacementForClass = value => value.match(/\S+/g).map(className => `.${className}`).join('');
const getQuotedReplacement = (node, value) => {
const leftQuote = node.raw.charAt(0);
const rightQuote = node.raw.charAt(node.raw.length - 1);
return `${leftQuote}${value}${rightQuote}`;
};
function * getLiteralFix(fixer, node, identifierName) {
let replacement = node.raw;
if (identifierName === 'getElementById') {
replacement = getQuotedReplacement(node, getReplacementForId(node.value));
}
if (identifierName === 'getElementsByClassName') {
replacement = getQuotedReplacement(node, getReplacementForClass(node.value));
}
yield fixer.replaceText(node, replacement);
}
function * getTemplateLiteralFix(fixer, node, identifierName) {
yield fixer.insertTextAfter(node, '`');
yield fixer.insertTextBefore(node, '`');
for (const templateElement of node.quasis) {
if (identifierName === 'getElementById') {
yield fixer.replaceText(
templateElement,
getReplacementForId(templateElement.value.cooked)
);
}
if (identifierName === 'getElementsByClassName') {
yield fixer.replaceText(
templateElement,
getReplacementForClass(templateElement.value.cooked)
);
}
}
}
const canBeFixed = node => {
if (node.type === 'Literal') {
return node.value === null || Boolean(node.value.trim());
}
if (node.type === 'TemplateLiteral') {
return (
node.expressions.length === 0 &&
node.quasis.some(templateElement => templateElement.value.cooked.trim())
);
}
return false;
};
const hasValue = node => {
if (node.type === 'Literal') {
return node.value;
}
return true;
};
const fix = (node, identifierName, preferedSelector) => {
const nodeToBeFixed = node.arguments[0];
if (identifierName === 'getElementsByTagName' || !hasValue(nodeToBeFixed)) {
return fixer => fixer.replaceText(node.callee.property, preferedSelector);
}
const getArgumentFix = nodeToBeFixed.type === 'Literal' ? getLiteralFix : getTemplateLiteralFix;
return function * (fixer) {
yield * getArgumentFix(fixer, nodeToBeFixed, identifierName);
yield fixer.replaceText(node.callee.property, preferedSelector);
};
};
const create = context => {
return {
CallExpression(node) {
const {callee: {property, type}} = node;
if (!property || type !== 'MemberExpression') {
return;
}
const identifierName = property.name;
const preferedSelector = forbiddenIdentifierNames.get(identifierName);
if (!preferedSelector) {
return;
}
const report = {
node,
message: `Prefer \`.${preferedSelector}()\` over \`.${identifierName}()\`.`
};
if (canBeFixed(node.arguments[0])) {
report.fix = fix(node, identifierName, preferedSelector);
}
context.report(report);
}
};
};
module.exports = {
create,
meta: {
type: 'suggestion',
docs: {
url: getDocumentationUrl(__filename)
},
fixable: 'code'
}
};