Repository: rwjblue/ember-template-lint
Pull request #360 (2018-03-20 22:37:35)

[FEAT] no-unbound


Patch:
diff --git a/docs/rule/no-unbound.md b/docs/rule/no-unbound.md
new file mode 100644
index 0000000..8ed9eda
--- /dev/null
+++ b/docs/rule/no-unbound.md
@@ -0,0 +1,16 @@
+## no-unbound
+
+`{{unbound}}` is a legacy hold over from the days in which Ember's template engine was less performant. It's use today
+is vestigial, and it no longer offers performance benefits.
+
+This rule forbids usage of the following:
+
+```hbs
+{{unbound aVar}}
+```
+
+And
+
+```hbs
+{{a-thing foo=(unbound aVar)}}
+```
diff --git a/docs/rules.md b/docs/rules.md
index dcd69d7..74d90fa 100644
--- a/docs/rules.md
+++ b/docs/rules.md
@@ -14,6 +14,7 @@
 * [no-debugger](rule/no-debugger.md)
 * [no-duplicate-attributes](rule/no-duplicate-attributes.md)
 * [no-log](rule/no-log.md)
+* [no-unbound](rule/no-unbound.md)
 * [no-trailing-spaces](rule/no-trailing-spaces.md)
 * [quotes](rule/quotes.md)
 * [self-closing-void-elements](rule/self-closing-void-elements.md)
diff --git a/lib/config/recommended.js b/lib/config/recommended.js
index 5bcaf6f..fda336e 100644
--- a/lib/config/recommended.js
+++ b/lib/config/recommended.js
@@ -5,6 +5,7 @@ module.exports = {
     'block-indentation': 2,
     'no-debugger': true,
     'no-log': true,
+    'no-unbound': true,
     'html-comments': true,
     'nested-interactive': true,
     'self-closing-void-elements': true,
diff --git a/lib/rules/index.js b/lib/rules/index.js
index 83cbb30..da12a57 100644
--- a/lib/rules/index.js
+++ b/lib/rules/index.js
@@ -5,6 +5,7 @@ module.exports = {
   'block-indentation': require('./lint-block-indentation'),
   'no-debugger': require('./lint-no-debugger'),
   'no-log': require('./lint-no-log'),
+  'no-unbound': require('./lint-no-unbound'),
   'html-comments': require('./lint-html-comments'),
   'img-alt-attributes': require('./lint-img-alt-attributes'),
   'inline-styles': require('./lint-inline-styles'),
diff --git a/lib/rules/lint-no-unbound.js b/lib/rules/lint-no-unbound.js
new file mode 100644
index 0000000..7d1692e
--- /dev/null
+++ b/lib/rules/lint-no-unbound.js
@@ -0,0 +1,35 @@
+'use strict';
+
+const Rule = require('./base');
+const message = 'Unexpected {{unbound}} usage.';
+
+module.exports = class NoUnbound extends Rule {
+  _checkForUnbound(node) {
+    if (node.path.original === 'unbound') {
+      this.log({
+        message,
+        line: node.loc && node.loc.start.line,
+        column: node.loc && node.loc.start.column,
+        source: this.sourceForNode(node)
+      });
+    }
+  }
+
+  visitor() {
+    return {
+      MustacheStatement(node) {
+        this._checkForUnbound(node);
+      },
+
+      BlockStatement(node) {
+        this._checkForUnbound(node);
+      },
+
+      SubExpression(node) {
+        this._checkForUnbound(node);
+      }
+    };
+  }
+};
+
+module.exports.message = message;


Test patch:
diff --git a/test/unit/rules/lint-no-unbound-test.js b/test/unit/rules/lint-no-unbound-test.js
new file mode 100644
index 0000000..203dd6a
--- /dev/null
+++ b/test/unit/rules/lint-no-unbound-test.js
@@ -0,0 +1,40 @@
+'use strict';
+
+const generateRuleTests = require('../../helpers/rule-test-harness');
+const message = require('../../../lib/rules/lint-no-unbound').message;
+
+generateRuleTests({
+  name: 'no-unbound',
+
+  config: true,
+
+  good: [
+    '{{foo}}',
+    '{{button}}'
+  ],
+
+  bad: [
+    {
+      template: '{{unbound foo}}',
+
+      result: {
+        message,
+        moduleId: 'layout.hbs',
+        source: '{{unbound foo}}',
+        line: 1,
+        column: 0
+      }
+    },
+    {
+      template: '{{my-thing foo=(unbound foo)}}',
+
+      result: {
+        message,
+        moduleId: 'layout.hbs',
+        source: '(unbound foo)',
+        line: 1,
+        column: 15
+      }
+    },
+  ]
+});