Repository: ember-template-lint/ember-template-lint
Pull request #449 (2018-07-01 08:50:37)

feat: Adds rule making href a required attribute for link tags.
Adds a linting rule to make `href` required for links.  Did not add it to recommended because I wasn't sure what the process is for that.


Patch:
diff --git a/docs/rule/link-href-attributes.md b/docs/rule/link-href-attributes.md
new file mode 100644
index 00000000..1034ce79
--- /dev/null
+++ b/docs/rule/link-href-attributes.md
@@ -0,0 +1,22 @@
+## link-href-attributes
+
+It's common to treat anchor tags as a button: `<a {{action 'handleClick'}}>Submit</a>`.
+
+However, this is typically considered bad practice as an anchor tag without an `href` is unfocusable which break accessability.
+
+This rule forbids the following:
+
+```hbs
+<a>I'm a fake link</a>
+<a {{action 'handleClick'}}>I'm a fake link</a>
+```
+
+Instead, you should write the template as:
+
+```hbs
+<a href="https://alink.com">I'm a real link</a>
+```
+
+The following values are valid configuration:
+
+  * boolean -- `true` for enabled / `false` for disabled
diff --git a/lib/rules/index.js b/lib/rules/index.js
index e53018cc..4a52cf41 100644
--- a/lib/rules/index.js
+++ b/lib/rules/index.js
@@ -13,6 +13,7 @@ module.exports = {
   'img-alt-attributes': require('./lint-img-alt-attributes'),
   'no-inline-styles': require('./lint-no-inline-styles'),
   'link-rel-noopener': require('./lint-link-rel-noopener'),
+  'link-href-attributes': require('./lint-link-href-attributes'),
   'no-triple-curlies': require('./lint-no-triple-curlies'),
   'self-closing-void-elements': require('./lint-self-closing-void-elements'),
   'no-nested-interactive': require('./lint-no-nested-interactive'),
diff --git a/lib/rules/lint-link-href-attributes.js b/lib/rules/lint-link-href-attributes.js
new file mode 100644
index 00000000..84726c2b
--- /dev/null
+++ b/lib/rules/lint-link-href-attributes.js
@@ -0,0 +1,36 @@
+'use strict';
+
+const AstNodeInfo = require('../helpers/ast-node-info');
+const Rule = require('./base');
+
+/**
+ Disallow usage of `<a>` without an `href` attribute.
+
+ Good:
+
+ ```
+ <a href="http://localhost">
+ ```
+
+ Bad:
+
+ ```
+ <a>
+ ```
+*/
+module.exports = class LinkHrefAttributes extends Rule {
+  visitor() {
+    return {
+      ElementNode(node) {
+        if (AstNodeInfo.isLinkElement(node) && !AstNodeInfo.hasAttribute(node, 'href')) {
+          this.log({
+            message: 'a tags must have an href attribute',
+            line: node.loc && node.loc.start.line,
+            column: node.loc && node.loc.start.column,
+            source: this.sourceForNode(node)
+          });
+        }
+      }
+    };
+  }
+};


Test patch:
diff --git a/test/unit/rules/lint-link-href-attributes-test.js b/test/unit/rules/lint-link-href-attributes-test.js
new file mode 100644
index 00000000..ac9d8698
--- /dev/null
+++ b/test/unit/rules/lint-link-href-attributes-test.js
@@ -0,0 +1,30 @@
+'use strict';
+
+const generateRuleTests = require('../../helpers/rule-test-harness');
+
+generateRuleTests({
+  name: 'link-href-attributes',
+
+  config: true,
+
+  good: [
+    '<a href=""></a>', /* empty string is really valid! */
+    '<a href="#"></a>',
+    '<a href="javascript:;"></a>',
+    '<a href="http://localhost"></a>',
+    '<a href={{link}}></a>'
+  ],
+
+  bad: [
+    {
+      template: '<a></a>',
+      result: {
+        message: 'a tags must have an href attribute',
+        moduleId: 'layout.hbs',
+        source: '<a></a>',
+        line: 1,
+        column: 0
+      }
+    }
+  ]
+});