eslint-plugin-ember
Version:
ESLint plugin for Ember.js apps
104 lines (97 loc) • 2.83 kB
JavaScript
function isInsideForm(node) {
let current = node.parent;
while (current) {
if (current.type === 'GlimmerElementNode' && current.tag === 'form') {
// <form method="dialog"> closes the dialog on submit without navigating,
// so click handlers on submit buttons are intentional and correct.
// See https://github.com/ember-template-lint/ember-template-lint/issues/2989
const methodAttr = current.attributes?.find((a) => a.name === 'method');
if (
methodAttr &&
methodAttr.value?.type === 'GlimmerTextNode' &&
methodAttr.value.chars.toLowerCase() === 'dialog'
) {
return false;
}
return true;
}
current = current.parent;
}
return false;
}
function isSubmitButton(node) {
for (const attr of node.attributes || []) {
if (
attr.name === 'type' &&
attr.value?.type === 'GlimmerTextNode' &&
attr.value.chars !== 'submit'
) {
return false;
}
}
return true;
}
function hasClickHandlingModifier(node) {
for (const mod of node.modifiers || []) {
if (mod.path?.original === 'action') {
// {{action ...}} defaults to click event
const onPair = mod.hash?.pairs?.find((p) => p.key === 'on');
if (!onPair) {
return true;
}
const eventValue = onPair.value?.value ?? onPair.value?.chars;
if (eventValue === 'click') {
return true;
}
}
if (mod.path?.original === 'on') {
// {{on "event" handler}}
if (mod.params?.length > 0 && mod.params[0].value === 'click') {
return true;
}
}
}
return false;
}
/** @type {import('eslint').Rule.RuleModule} */
module.exports = {
meta: {
type: 'problem',
docs: {
description: 'disallow action attribute on submit buttons',
category: 'Best Practices',
url: 'https://github.com/ember-cli/eslint-plugin-ember/tree/master/docs/rules/template-no-action-on-submit-button.md',
templateMode: 'both',
},
fixable: null,
schema: [],
messages: {
noActionOnSubmitButton:
'In a `<form>`, a `<button>` with `type="submit"` should have no click action',
},
originallyFrom: {
name: 'ember-template-lint',
rule: 'lib/rules/no-action-on-submit-button.js',
docs: 'docs/rule/no-action-on-submit-button.md',
tests: 'test/unit/rules/no-action-on-submit-button-test.js',
},
},
create(context) {
return {
GlimmerElementNode(node) {
if (node.tag !== 'button') {
return;
}
if (!isInsideForm(node)) {
return;
}
if (isSubmitButton(node) && hasClickHandlingModifier(node)) {
context.report({
node,
messageId: 'noActionOnSubmitButton',
});
}
},
};
},
};