You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
135 lines
3.2 KiB
135 lines
3.2 KiB
'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}`;
|
|
};
|
|
|
|
const 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));
|
|
}
|
|
|
|
return [fixer.replaceText(node, replacement)];
|
|
};
|
|
|
|
const getTemplateLiteralFix = (fixer, node, identifierName) => {
|
|
const fix = [
|
|
fixer.insertTextAfter(node, '`'),
|
|
fixer.insertTextBefore(node, '`')
|
|
];
|
|
|
|
node.quasis.forEach(templateElement => {
|
|
if (identifierName === 'getElementById') {
|
|
fix.push(
|
|
fixer.replaceText(
|
|
templateElement,
|
|
getReplacementForId(templateElement.value.cooked)
|
|
)
|
|
);
|
|
}
|
|
|
|
if (identifierName === 'getElementsByClassName') {
|
|
fix.push(
|
|
fixer.replaceText(
|
|
templateElement,
|
|
getReplacementForClass(templateElement.value.cooked)
|
|
)
|
|
);
|
|
}
|
|
});
|
|
|
|
return fix;
|
|
};
|
|
|
|
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 fixer => [
|
|
...getArgumentFix(fixer, nodeToBeFixed, identifierName),
|
|
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'
|
|
}
|
|
};
|