Repository: rwjblue/ember-template-lint
Pull request #316 (2017-11-15 23:04:34)

add quotes rule
Closes #42.

This a MVP of the quotes rule that can be built upon if necessary.


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 87bc073..3f09f62 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,24 @@
 Changelog
 =========
 
+- Add new `quotes` rule. Examples:
+
+Enforce either:
+
+```hbs
+<div class="my-class">test</div>
+{{my-helper "hello there"}}
+```
+
+or:
+
+```hbs
+<div class='my-class'>test</div>
+{{my-helper 'hello there'}}
+```
+
+You can read more about the rule [in the documentation](https://github.com/rwjblue/ember-template-lint/blob/master/docs/rule/quotes.md).
+
 ## 0.8.12
 
 - Ensure packages required by the executable script are dependencies. This fixes issues when using `ember-template-lint` as a globally installed package.
diff --git a/docs/rule/quotes.md b/docs/rule/quotes.md
new file mode 100644
index 0000000..ca0aaa4
--- /dev/null
+++ b/docs/rule/quotes.md
@@ -0,0 +1,21 @@
+#### quotes
+
+Enforce the consistent use of either double or single quotes. Similar to https://eslint.org/docs/rules/quotes.
+
+Enforce either:
+
+```hbs
+<div class="my-class">test</div>
+{{my-helper "hello there"}}
+```
+
+or:
+
+```hbs
+<div class='my-class'>test</div>
+{{my-helper 'hello there'}}
+```
+
+The following values are valid configuration:
+
+  * string -- "double" requires the use of double quotes wherever possible, "single" requires the use of single quotes wherever possible
diff --git a/docs/rules.md b/docs/rules.md
index 1456767..87aef26 100644
--- a/docs/rules.md
+++ b/docs/rules.md
@@ -15,6 +15,7 @@
 * [no-duplicate-attributes](rule/unused-block-params.md)
 * [no-log](rule/no-log.md)
 * [no-trailing-spaces](rule/no-trailing-spaces.md)
+* [quotes](rule/quotes.md)
 * [self-closing-void-elements](rule/self-closing-void-elements.md)
 * [simple-unless](rule/simple-unless.md)
 * [style-concatenation](rule/style-concatenation.md)
diff --git a/lib/rules/index.js b/lib/rules/index.js
index e8bbfa8..dd7be2a 100644
--- a/lib/rules/index.js
+++ b/lib/rules/index.js
@@ -26,5 +26,6 @@ module.exports = {
   'template-length': require('./lint-template-length'),
   'table-groups': require('./lint-table-groups'),
   'eol-last': require('./lint-eol-last'),
-  'no-trailing-spaces': require('./lint-no-trailing-spaces')
+  'no-trailing-spaces': require('./lint-no-trailing-spaces'),
+  'quotes': require('./lint-quotes')
 };
diff --git a/lib/rules/lint-quotes.js b/lib/rules/lint-quotes.js
new file mode 100644
index 0000000..4188d37
--- /dev/null
+++ b/lib/rules/lint-quotes.js
@@ -0,0 +1,68 @@
+'use strict';
+
+const Rule = require('./base');
+const createErrorMessage = require('../helpers/create-error-message');
+
+module.exports = class Quotes extends Rule {
+  parseConfig(config) {
+    let configType = typeof config;
+
+    switch (configType) {
+    case 'boolean':
+      if (!config) {
+        return false;
+      }
+      break;
+    case 'string':
+      if (['double', 'single'].indexOf(config) > -1) {
+        return config;
+      }
+      break;
+    case 'undefined':
+      return false;
+    }
+
+    let errorMessage = createErrorMessage(this.ruleName, [
+      '  * "double" - requires the use of double quotes wherever possible',
+      '  * "single" - requires the use of single quotes wherever possible'
+    ], config);
+
+    throw new Error(errorMessage);
+  }
+
+  visitor() {
+    let badChar;
+    let message;
+    switch (this.config) {
+    case 'double':
+      badChar = '\'';
+      message = 'you must use double quotes in templates';
+      break;
+    case 'single':
+      badChar = '"';
+      message = 'you must use single quotes in templates';
+      break;
+    }
+
+    let checkForQuotes = node => {
+      let source = this.sourceForNode(node);
+      if (source[0] === badChar) {
+        return this.log({
+          message,
+          line: node.loc.start.line,
+          column: node.loc.start.column,
+          source
+        });
+      }
+    };
+
+    return {
+      AttrNode(node) {
+        checkForQuotes(node.value);
+      },
+      StringLiteral(node) {
+        checkForQuotes(node);
+      }
+    };
+  }
+};


Test patch:
diff --git a/test/unit/rules/lint-quotes-test.js b/test/unit/rules/lint-quotes-test.js
new file mode 100644
index 0000000..ddb42f1
--- /dev/null
+++ b/test/unit/rules/lint-quotes-test.js
@@ -0,0 +1,132 @@
+'use strict';
+
+const generateRuleTests = require('../../helpers/rule-test-harness');
+
+generateRuleTests({
+  name: 'quotes',
+
+  good: [
+    {
+      config: 'double',
+      template: `{{component "test"}}`
+    },
+    {
+      config: 'double',
+      template: `{{hello x="test"}}`
+    },
+    {
+      config: 'double',
+      template: `<input type="checkbox">`
+    },
+    {
+      config: 'single',
+      template: `{{component 'test'}}`
+    },
+    {
+      config: 'single',
+      template: `{{hello x='test'}}`
+    },
+    {
+      config: 'single',
+      template: `<input type='checkbox'>`
+    },
+  ],
+
+  bad: [
+    {
+      config: 'double',
+      template: `{{component 'test'}}`,
+
+      result: {
+        moduleId: 'layout.hbs',
+        message: 'you must use double quotes in templates',
+        line: 1,
+        column: 12,
+        source: `'test'`
+      }
+    },
+    {
+      config: 'double',
+      template: `{{hello x='test'}}`,
+
+      result: {
+        moduleId: 'layout.hbs',
+        message: 'you must use double quotes in templates',
+        line: 1,
+        column: 10,
+        source: `'test'`
+      }
+    },
+    {
+      config: 'double',
+      template: `<input type='checkbox'>`,
+
+      result: {
+        moduleId: 'layout.hbs',
+        message: 'you must use double quotes in templates',
+        line: 1,
+        column: 12,
+        source: `'checkbox'`
+      }
+    },
+    {
+      config: 'single',
+      template: `{{component "test"}}`,
+
+      result: {
+        moduleId: 'layout.hbs',
+        message: 'you must use single quotes in templates',
+        line: 1,
+        column: 12,
+        source: `"test"`
+      }
+    },
+    {
+      config: 'single',
+      template: `{{hello x="test"}}`,
+
+      result: {
+        moduleId: 'layout.hbs',
+        message: 'you must use single quotes in templates',
+        line: 1,
+        column: 10,
+        source: `"test"`
+      }
+    },
+    {
+      config: 'single',
+      template: `<input type="checkbox">`,
+
+      result: {
+        moduleId: 'layout.hbs',
+        message: 'you must use single quotes in templates',
+        line: 1,
+        column: 12,
+        source: `"checkbox"`
+      }
+    }
+  ],
+
+  error: [
+    {
+      config: 'sometimes',
+      template: 'test',
+
+      result: {
+        fatal: true,
+        moduleId: 'layout.hbs',
+        message: 'You specified `"sometimes"`'
+      }
+    },
+    {
+      config: true,
+      template: 'test',
+
+      result: {
+        fatal: true,
+        moduleId: 'layout.hbs',
+        message: 'You specified `true`'
+      }
+    }
+  ]
+});