90 lines
2.4 KiB
JavaScript
90 lines
2.4 KiB
JavaScript
|
'use strict';
|
||
|
const {isIdentifierName} = require('@babel/helper-validator-identifier');
|
||
|
const escapeString = require('./utils/escape-string.js');
|
||
|
const {methodCallSelector, matches} = require('./selectors/index.js');
|
||
|
|
||
|
const MESSAGE_ID = 'prefer-dom-node-dataset';
|
||
|
const messages = {
|
||
|
[MESSAGE_ID]: 'Prefer `.dataset` over `{{method}}(…)`.',
|
||
|
};
|
||
|
|
||
|
const selector = [
|
||
|
matches([
|
||
|
methodCallSelector({method: 'setAttribute', argumentsLength: 2}),
|
||
|
methodCallSelector({methods: ['getAttribute', 'removeAttribute', 'hasAttribute'], argumentsLength: 1}),
|
||
|
]),
|
||
|
'[arguments.0.type="Literal"]',
|
||
|
].join('');
|
||
|
|
||
|
const dashToCamelCase = string => string.replace(/-[a-z]/g, s => s[1].toUpperCase());
|
||
|
|
||
|
/** @param {import('eslint').Rule.RuleContext} context */
|
||
|
const create = context => ({
|
||
|
[selector](node) {
|
||
|
const [nameNode] = node.arguments;
|
||
|
let attributeName = nameNode.value;
|
||
|
|
||
|
if (typeof attributeName !== 'string') {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
attributeName = attributeName.toLowerCase();
|
||
|
|
||
|
if (!attributeName.startsWith('data-')) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
const method = node.callee.property.name;
|
||
|
const name = dashToCamelCase(attributeName.slice(5));
|
||
|
|
||
|
const sourceCode = context.getSourceCode();
|
||
|
let text = '';
|
||
|
const datasetText = `${sourceCode.getText(node.callee.object)}.dataset`;
|
||
|
switch (method) {
|
||
|
case 'setAttribute':
|
||
|
case 'getAttribute':
|
||
|
case 'removeAttribute': {
|
||
|
text = isIdentifierName(name) ? `.${name}` : `[${escapeString(name, nameNode.raw.charAt(0))}]`;
|
||
|
text = `${datasetText}${text}`;
|
||
|
if (method === 'setAttribute') {
|
||
|
text += ` = ${sourceCode.getText(node.arguments[1])}`;
|
||
|
} else if (method === 'removeAttribute') {
|
||
|
text = `delete ${text}`;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
For non-exists attribute, `element.getAttribute('data-foo')` returns `null`,
|
||
|
but `element.dataset.foo` returns `undefined`, switch to suggestions if necessary
|
||
|
*/
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case 'hasAttribute': {
|
||
|
text = `Object.hasOwn(${datasetText}, ${escapeString(name, nameNode.raw.charAt(0))})`;
|
||
|
break;
|
||
|
}
|
||
|
// No default
|
||
|
}
|
||
|
|
||
|
return {
|
||
|
node,
|
||
|
messageId: MESSAGE_ID,
|
||
|
data: {method},
|
||
|
fix: fixer => fixer.replaceText(node, text),
|
||
|
};
|
||
|
},
|
||
|
});
|
||
|
|
||
|
/** @type {import('eslint').Rule.RuleModule} */
|
||
|
module.exports = {
|
||
|
create,
|
||
|
meta: {
|
||
|
type: 'suggestion',
|
||
|
docs: {
|
||
|
description: 'Prefer using `.dataset` on DOM elements over calling attribute methods.',
|
||
|
},
|
||
|
fixable: 'code',
|
||
|
messages,
|
||
|
},
|
||
|
};
|