123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131 |
- 'use strict';
- const {methodCallSelector, notDomNodeSelector} = require('./selectors/index.js');
- const {isStringLiteral, isNullLiteral} = require('./ast/index.js');
- const MESSAGE_ID = 'prefer-query-selector';
- const messages = {
- [MESSAGE_ID]: 'Prefer `.{{replacement}}()` over `.{{method}}()`.',
- };
- const selector = [
- methodCallSelector({
- methods: ['getElementById', 'getElementsByClassName', 'getElementsByTagName'],
- argumentsLength: 1,
- }),
- notDomNodeSelector('callee.object'),
- ].join('');
- const disallowedIdentifierNames = 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 =>
- isNullLiteral(node)
- || (isStringLiteral(node) && Boolean(node.value.trim()))
- || (
- node.type === 'TemplateLiteral'
- && node.expressions.length === 0
- && node.quasis.some(templateElement => templateElement.value.cooked.trim())
- );
- const hasValue = node => {
- if (node.type === 'Literal') {
- return node.value;
- }
- return true;
- };
- const fix = (node, identifierName, preferredSelector) => {
- const nodeToBeFixed = node.arguments[0];
- if (identifierName === 'getElementsByTagName' || !hasValue(nodeToBeFixed)) {
- return fixer => fixer.replaceText(node.callee.property, preferredSelector);
- }
- const getArgumentFix = nodeToBeFixed.type === 'Literal' ? getLiteralFix : getTemplateLiteralFix;
- return function * (fixer) {
- yield * getArgumentFix(fixer, nodeToBeFixed, identifierName);
- yield fixer.replaceText(node.callee.property, preferredSelector);
- };
- };
- /** @param {import('eslint').Rule.RuleContext} context */
- const create = () => ({
- [selector](node) {
- const method = node.callee.property.name;
- const preferredSelector = disallowedIdentifierNames.get(method);
- const problem = {
- node: node.callee.property,
- messageId: MESSAGE_ID,
- data: {
- replacement: preferredSelector,
- method,
- },
- };
- if (canBeFixed(node.arguments[0])) {
- problem.fix = fix(node, method, preferredSelector);
- }
- return problem;
- },
- });
- /** @type {import('eslint').Rule.RuleModule} */
- module.exports = {
- create,
- meta: {
- type: 'suggestion',
- docs: {
- description: 'Prefer `.querySelector()` over `.getElementById()`, `.querySelectorAll()` over `.getElementsByClassName()` and `.getElementsByTagName()`.',
- },
- fixable: 'code',
- messages,
- },
- };
|