Repository: ember-template-lint/ember-template-lint (js)

Issue:
Incorrect indentation on line starting with &nbsp;
Linting a template with a segment like the following incorrectly flags the indentation:

``` handlebars
{{#if foo}}
  &nbsp;bar
{{/if}}
```

with

```
block-indentation: Incorrect indentation for `   bar
` beginning at L2:C0. Expected `   bar
` to be at an indentation of 2 but was found at 3. (thedyrt-ui/templates/components/at-trip):
`{{#if foo}}
  &nbsp;bar
{{/if}}`
```


Pull request:
Changed parser mode to 'codemod'
This allows the linter to lint indentation correctly even if the line starts with `&nbsp;`

Fixes #120

Though some changes were needed to rules that work on TextNodes as they now behave slightly different. The changes I made to the block indentation rule also made it a bit more consistent in my opinion.

Additional test may be needed to cover what ever corners cases there may be, but all current tests should pass.

Patch:
diff --git a/lib/index.js b/lib/index.js
index a16a9100..9882dd0e 100644
--- a/lib/index.js
+++ b/lib/index.js
@@ -145,7 +145,9 @@ class Linter {
     let templateAST;
 
     try {
-      templateAST = preprocess(source);
+      templateAST = preprocess(source, {
+        mode: 'codemod',
+      });
     } catch (error) {
       let message = buildErrorMessage(options.moduleId, error);
       messages.push(message);
diff --git a/lib/rules/lint-block-indentation.js b/lib/rules/lint-block-indentation.js
index cefe9a56..df89e03b 100644
--- a/lib/rules/lint-block-indentation.js
+++ b/lib/rules/lint-block-indentation.js
@@ -252,6 +252,7 @@ module.exports = class BlockIndentation extends Rule {
       }
 
       let childStartColumn = child.loc.start.column;
+      let childStartLine = child.loc.start.line;
 
       // sanitize text node starting column info
       if (AstNodeInfo.isTextNode(child)) {
@@ -268,6 +269,9 @@ module.exports = class BlockIndentation extends Rule {
         // reset the child start column if there's a line break
         if (/^(\r\n|\n)/.test(child.chars)) {
           childStartColumn = 0;
+          let newLineLength = child.chars.length - withoutLeadingNewLines.length;
+          let leadingNewLines = child.chars.substring(newLineLength);
+          childStartLine += (leadingNewLines.match(/\n/g) || []).length;
         }
 
         childStartColumn += firstNonWhitespace;
@@ -290,7 +294,7 @@ module.exports = class BlockIndentation extends Rule {
         } else if (AstNodeInfo.isMustacheStatement(child)) {
           display = `{{${child.path.original}}}`;
         } else if (AstNodeInfo.isTextNode(child)) {
-          display = child.chars;
+          display = child.chars.replace(/^[\r\n\s]*/, '');
         } else if (AstNodeInfo.isCommentStatement(child)) {
           display = `<!--${child.value}-->`;
         } else if (AstNodeInfo.isMustacheCommentStatement(child)) {
@@ -299,7 +303,7 @@ module.exports = class BlockIndentation extends Rule {
           display = child.path.original;
         }
 
-        let startLocation = `L${child.loc.start.line}:C${child.loc.start.column}`;
+        let startLocation = `L${childStartLine}:C${childStartColumn}`;
         let warning =
           `Incorrect indentation for \`${display}\` beginning at ${startLocation}` +
           `. Expected \`${display}\` to be at an indentation of ${expectedStartColumn} but ` +
@@ -307,8 +311,8 @@ module.exports = class BlockIndentation extends Rule {
 
         this.log({
           message: warning,
-          line: child.loc && child.loc.start.line,
-          column: child.loc && child.loc.start.column,
+          line: childStartLine,
+          column: childStartColumn,
           source: this.sourceForNode(node),
         });
       }
diff --git a/lib/rules/lint-linebreak-style.js b/lib/rules/lint-linebreak-style.js
index aa9c50df..b08bb666 100644
--- a/lib/rules/lint-linebreak-style.js
+++ b/lib/rules/lint-linebreak-style.js
@@ -61,9 +61,6 @@ module.exports = class LineBreakStyle extends Rule {
       MustacheStatement(node) {
         this._checkNodeAndLog(node);
       },
-      BlockStatement(node) {
-        this._checkNodeAndLog(node);
-      },
       PartialStatement(node) {
         this._checkNodeAndLog(node);
       },
@@ -73,9 +70,6 @@ module.exports = class LineBreakStyle extends Rule {
       CommentStatement(node) {
         this._checkNodeAndLog(node);
       },
-      ElementNode(node) {
-        this._checkNodeAndLog(node);
-      },
     };
   }
 
diff --git a/lib/rules/lint-no-bare-strings.js b/lib/rules/lint-no-bare-strings.js
index e5f18f76..747bb1a1 100644
--- a/lib/rules/lint-no-bare-strings.js
+++ b/lib/rules/lint-no-bare-strings.js
@@ -38,8 +38,47 @@ const TAG_ATTRIBUTES = {
   img: ['alt'],
 };
 
+// Character entity reference chart: https://dev.w3.org/html5/html-author/charref
 const DEFAULT_CONFIG = {
   whitelist: [
+    '&lpar;', // (
+    '&rpar;', // )
+    '&comma;', // ,
+    '&period;', // .
+    '&amp;', // &
+    '&AMP;', // &
+    '&plus;', // +
+    '&minus;', // -
+    '&equals;', // =
+    '&ast;', // *
+    '&midast;', // *
+    '&sol;', // /
+    '&num;', // #
+    '&percnt;', // %
+    '&excl;', // !
+    '&quest;', // ?
+    '&colon;', // :
+    '&lsqb;', // [
+    '&lbrack;', // [
+    '&rsqb;', // ]
+    '&rbrack;', // ]
+    '&lcub;', // {
+    '&lbrace;', // {
+    '&rcub;', // }
+    '&rbrace;', // }
+    '&lt;', // <
+    '&LT;', // <
+    '&gt;', // >
+    '&GT;', // >
+    '&bull;', // •
+    '&bullet;', // •
+    '&mdash;', // —
+    '&nbsp;', // non-breaking space
+    '&Tab;',
+    '&NewLine;',
+    '&verbar;', // |
+    '&vert;', // |
+    '&VerticalLine;', // |
     '(',
     ')',
     ',',


Test patch:
diff --git a/test/unit/rules/lint-block-indentation-test.js b/test/unit/rules/lint-block-indentation-test.js
index 699f2160..cfe9ad26 100644
--- a/test/unit/rules/lint-block-indentation-test.js
+++ b/test/unit/rules/lint-block-indentation-test.js
@@ -102,6 +102,7 @@ generateRuleTests({
       '  {{/baz.content}}',
       '{{/foo-bar}}',
     ].join('\n'),
+    ['{{#if foo}}', '  &nbsp;bar', '{{/if}}'].join('\n'),
   ],
 
   bad: [
@@ -343,12 +344,12 @@ generateRuleTests({
 
       result: {
         message:
-          'Incorrect indentation for `  Good morning\n` beginning at L1:C17. Expected `  Good morning\n` to be at an indentation of 2 but was found at 19.',
+          'Incorrect indentation for `Good morning\n` beginning at L1:C19. Expected `Good morning\n` to be at an indentation of 2 but was found at 19.',
         moduleId: 'layout.hbs',
         source:
           '{{#if isMorning}}  Good morning\n{{else if isAfternoon}}\n  Good afternoon\n{{else}}\n  Good night\n{{/if}}',
         line: 1,
-        column: 17,
+        column: 19,
       },
     },
 
@@ -456,11 +457,11 @@ generateRuleTests({
       results: [
         {
           message:
-            'Incorrect indentation for `    bar\n` beginning at L4:C0. Expected `    bar\n` to be at an indentation of 2 but was found at 4.',
+            'Incorrect indentation for `bar\n` beginning at L4:C4. Expected `bar\n` to be at an indentation of 2 but was found at 4.',
           moduleId: 'layout.hbs',
           source: '{{#if foo}}\n  foo\n{{else}}\n    bar\n{{/if}}',
           line: 4,
-          column: 0,
+          column: 4,
         },
       ],
     },
diff --git a/test/unit/rules/lint-linebreak-style-test.js b/test/unit/rules/lint-linebreak-style-test.js
index 760af6c1..2c008875 100644
--- a/test/unit/rules/lint-linebreak-style-test.js
+++ b/test/unit/rules/lint-linebreak-style-test.js
@@ -98,6 +98,18 @@ generateRuleTests({
         source: '\r\n',
       },
     },
+    {
+      config: 'unix',
+      template: '<blah arg="\r\n" />',
+
+      result: {
+        moduleId: 'layout.hbs',
+        message: 'Wrong linebreak used. Expected LF but found CRLF',
+        line: 1,
+        column: 11,
+        source: '\r\n',
+      },
+    },
     {
       config: 'windows',
       template: '\n',
diff --git a/test/unit/rules/lint-no-bare-strings-test.js b/test/unit/rules/lint-no-bare-strings-test.js
index 06374722..473ba00a 100644
--- a/test/unit/rules/lint-no-bare-strings-test.js
+++ b/test/unit/rules/lint-no-bare-strings-test.js
@@ -32,6 +32,7 @@ generateRuleTests({
     '{{t "foo"}}',
     '{{t "foo"}}, {{t "bar"}} ({{length}})',
     '(),.&+-=*/#%!?:[]{}',
+    '&lpar;&rpar;&comma;&period;&amp;&nbsp;',
     '{{! template-lint-disable no-bare-strings }}',
     '{{! template-lint-disable }}',