UNPKG

eslint-plugin-ember

Version:
146 lines (134 loc) 3.67 kB
// Comprehensive Ember event handler names // Note: mouseMove, mouseEnter, and mouseLeave are intentionally excluded — // they are native DOM events that do not have corresponding Ember // classic-event aliases on components. const EMBER_EVENTS = new Set([ 'touchStart', 'touchMove', 'touchEnd', 'touchCancel', 'keyDown', 'keyUp', 'keyPress', 'mouseDown', 'mouseUp', 'contextMenu', 'click', 'doubleClick', 'focusIn', 'focusOut', 'submit', 'change', 'input', 'dragStart', 'drag', 'dragEnter', 'dragLeave', 'dragOver', 'dragEnd', 'drop', ]); function isEventName(name) { return EMBER_EVENTS.has(name); } /** @type {import('eslint').Rule.RuleModule} */ module.exports = { meta: { type: 'suggestion', docs: { description: 'disallow passing event handlers directly as component arguments', category: 'Best Practices', url: 'https://github.com/ember-cli/eslint-plugin-ember/tree/master/docs/rules/template-no-passed-in-event-handlers.md', templateMode: 'both', }, fixable: null, schema: [ { type: 'object', properties: { ignore: { type: 'object', additionalProperties: { type: 'array', items: { type: 'string' }, }, }, }, additionalProperties: false, }, ], messages: { unexpected: 'Event handler "@{{name}}" should not be passed as a component argument. Use the `on` modifier instead.', }, originallyFrom: { name: 'ember-template-lint', rule: 'lib/rules/no-passed-in-event-handlers.js', docs: 'docs/rule/no-passed-in-event-handlers.md', tests: 'test/unit/rules/no-passed-in-event-handlers-test.js', }, }, create(context) { const options = context.options[0] || {}; const ignoreConfig = options.ignore || {}; return { GlimmerElementNode(node) { // Only check component invocations (PascalCase) if (!/^[A-Z]/.test(node.tag)) { return; } // Skip built-in Input/Textarea if (node.tag === 'Input' || node.tag === 'Textarea') { return; } if (!node.attributes) { return; } const ignoredAttrs = ignoreConfig[node.tag] || []; for (const attr of node.attributes) { if (!attr.name || !attr.name.startsWith('@')) { continue; } const argName = attr.name.slice(1); if (ignoredAttrs.includes(argName)) { continue; } if (isEventName(argName)) { context.report({ node: attr, messageId: 'unexpected', data: { name: argName }, }); } } }, GlimmerMustacheStatement(node) { const path = node.path; if (!path || path.type !== 'GlimmerPathExpression') { return; } // Skip built-in input/textarea if (path.original === 'input' || path.original === 'textarea') { return; } // Check hash pairs for event handler names if (!node.hash || !node.hash.pairs) { return; } const ignoredAttrs = ignoreConfig[path.original] || []; for (const pair of node.hash.pairs) { if (ignoredAttrs.includes(pair.key)) { continue; } if (isEventName(pair.key)) { context.report({ node: pair, messageId: 'unexpected', data: { name: pair.key }, }); } } }, }; }, };