/
prefer-modern-dom-apis.js
121 lines (102 loc) · 3.06 KB
/
prefer-modern-dom-apis.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
'use strict';
const getDocsUrl = require('./utils/get-docs-url');
const checkForReplaceChildOrInsertBefore = (context, node) => {
const getArgumentName = args => {
if (args.type === 'Identifier') {
return args.name;
}
return null;
};
const forbiddenIdentifierNames = new Map([
['replaceChild', 'replaceWith'],
['insertBefore', 'before']
]);
const identifierName = node.callee.property.name;
// Only handle methods that exist in forbiddenIdentifierNames
if (forbiddenIdentifierNames.has(identifierName)) {
const args = node.arguments;
const newChildNodeArg = getArgumentName(args[0]);
const oldChildNodeArg = getArgumentName(args[1]);
// Return early in case that one of the provided arguments is not a node
if (!newChildNodeArg || !oldChildNodeArg) {
return;
}
const parentNode = node.callee.object.name;
const preferredSelector = forbiddenIdentifierNames.get(identifierName);
return context.report({
node,
message: `Prefer \`${oldChildNodeArg}.${preferredSelector}(${newChildNodeArg})\` over \`${parentNode}.${identifierName}(${newChildNodeArg}, ${oldChildNodeArg})\`.`,
fix: fixer =>
fixer.replaceText(
node,
`${oldChildNodeArg}.${preferredSelector}(${newChildNodeArg})`
)
});
}
};
const checkForInsertAdjacentTextOrInsertAdjacentElement = (context, node) => {
// Handle both Identifier and Literal because the preferred selectors support nodes and DOMString
const getArgumentName = args => {
if (args.type === 'Identifier') {
return args.name;
}
if (args.type === 'Literal') {
return args.raw;
}
return null;
};
const positionReplacers = new Map([
['beforebegin', 'before'],
['afterbegin', 'prepend'],
['beforeend', 'append'],
['afterend', 'after']
]);
const identifierName = node.callee.property.name;
if (
identifierName === 'insertAdjacentText' ||
identifierName === 'insertAdjacentElement'
) {
const args = node.arguments;
const positionArg = getArgumentName(args[0]);
const positionAsValue = args[0].value;
// Return early when specified position value of 1st argument is not a recognised value
if (!positionReplacers.has(positionAsValue)) {
return;
}
const referenceNode = node.callee.object.name;
const preferredSelector = positionReplacers.get(positionAsValue);
const insertedTextArg = getArgumentName(args[1]);
return context.report({
node,
message: `Prefer \`${referenceNode}.${preferredSelector}(${insertedTextArg})\` over \`${referenceNode}.${identifierName}(${positionArg}, ${insertedTextArg})\`.`,
fix: fixer =>
fixer.replaceText(
node,
`${referenceNode}.${preferredSelector}(${insertedTextArg})`
)
});
}
};
const create = context => {
return {
CallExpression(node) {
if (
node.callee.type === 'MemberExpression' &&
node.arguments.length === 2
) {
checkForReplaceChildOrInsertBefore(context, node);
checkForInsertAdjacentTextOrInsertAdjacentElement(context, node);
}
}
};
};
module.exports = {
create,
meta: {
type: 'suggestion',
docs: {
url: getDocsUrl(__filename)
},
fixable: 'code'
}
};