Repository: rwjblue/ember-template-lint
Pull request #369 (2018-03-24 19:20:00)

Add `no-partial` rule to allow disabling partial usage.


Patch:
diff --git a/docs/rule/no-partial.md b/docs/rule/no-partial.md
new file mode 100644
index 0000000..590e040
--- /dev/null
+++ b/docs/rule/no-partial.md
@@ -0,0 +1,17 @@
+## no-partial
+
+`{{partial}}` is a legacy hold over from the days in which Ember had little to
+no mechanism for sharing "template snippets". Today we can use "contextual
+components" (and soon "named blocks"), which serves the original need for
+`{{partial`.
+
+In addition to having a better solution for the original problem, there are also a number of issues when using `{{partial`:
+
+* `{{partial` usage is essentially "eval", which means that Ember's template rendering system can make little to no optimizations
+* `{{partial` usage provides the partial template with unrestricted access to the local scope which leads to extreme confusion and "scope creep"
+
+This rule forbids usage of the following:
+
+```hbs
+{{partial "foo"}}
+```
diff --git a/docs/rules.md b/docs/rules.md
index cdc5022..88b208c 100644
--- a/docs/rules.md
+++ b/docs/rules.md
@@ -19,6 +19,7 @@
 * [no-unbound](rule/no-unbound.md)
 * [no-outlet-outside-routes](rule/no-outlet-outside-routes.md)
 * [no-trailing-spaces](rule/no-trailing-spaces.md)
+* [no-partial](rule/no-partial.md)
 * [quotes](rule/quotes.md)
 * [self-closing-void-elements](rule/self-closing-void-elements.md)
 * [simple-unless](rule/simple-unless.md)
diff --git a/lib/rules/index.js b/lib/rules/index.js
index 7194cf9..728ed3f 100644
--- a/lib/rules/index.js
+++ b/lib/rules/index.js
@@ -34,4 +34,5 @@ module.exports = {
   'quotes': require('./lint-quotes'),
   'no-trailing-dot-in-path-expression': require('./lint-trailing-dot-in-path-expression'),
   'no-unnecessary-concat': require('./lint-no-unnecessary-concat'),
+  'no-partial': require('./lint-no-partial'),
 };
diff --git a/lib/rules/lint-no-partial.js b/lib/rules/lint-no-partial.js
new file mode 100644
index 0000000..b7371f4
--- /dev/null
+++ b/lib/rules/lint-no-partial.js
@@ -0,0 +1,27 @@
+'use strict';
+
+const Rule = require('./base');
+const message = 'Unexpected {{partial}} usage.';
+
+module.exports = class NoPartial extends Rule {
+  _checkForPartial(node) {
+    if (node.path.original === 'partial') {
+      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._checkForPartial(node);
+      },
+    };
+  }
+};
+
+module.exports.message = message;


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