| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889 | '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,	},};
 |