Repository: ember-template-lint/ember-template-lint
Pull request #568 (2018-11-29 19:41:54)

Fix contextual component error message
Currently, the attribute indentation message for a contextual component reads (in part) 
`Incorrect indentation of close curly braces '}}' for the component '{{component}}'`. It should use the correct component name.

Similarly, the block indentation error message for a contextual component reads: `Incorrect indentation for {{#component}} beginning at L13:C3. Expected {{#component}} to be at an indentation of 1 but was found at 3.`

See #403 

Example:

```hbs
{{#foo bar as |foobar|}}
   {{#foobar.baz
   }}
   {{/foobar.baz}}
{{/foo}}
```

* `Incorrect indentation of close curly braces '}}' for the component '{{component}}' beginning at L13:C3. Expected '{{component}}' to be at L12:C16.`
* ```Incorrect indentation for `{{#component}}` beginning at L12:C3. Expected `{{#component}}` to be at an indentation of 1 but was found at 3.```


Patch:
diff --git a/lib/plugins/transform-dot-component-invocation.js b/lib/plugins/transform-dot-component-invocation.js
index aaa15971..e2ac6069 100644
--- a/lib/plugins/transform-dot-component-invocation.js
+++ b/lib/plugins/transform-dot-component-invocation.js
@@ -7,13 +7,13 @@
   of the component.
 
   ```handlebars
-    {{#my-component as |comps|}}
+    {{#my-component as |comp|}}
       {{comp.dropdown isOpen=false}}
     {{/my-component}}
   ```
   with
   ```handlebars
-    {{#my-component as |comps|}}
+    {{#my-component as |comp|}}
       {{component comp.dropdown isOpen=false}}
     {{/my-component}}
   ```
diff --git a/lib/rules/lint-attribute-indentation.js b/lib/rules/lint-attribute-indentation.js
index 0c33d5b7..10aa4c32 100644
--- a/lib/rules/lint-attribute-indentation.js
+++ b/lib/rules/lint-attribute-indentation.js
@@ -371,6 +371,9 @@ module.exports = class AttributeSpacing extends Rule {
     };
 
     let componentName = node.type === 'ElementNode' ? node.tag : node.path.original;
+    if (node._isContextualComponent){
+      componentName = node.params[0].original;
+    }
     if (actualStartLocation.line !== expectedStartLocation.line ||
       actualStartLocation.column !== expectedStartLocation.column) {
       let message = `Incorrect indentation of close curly braces '}}' for the component '{{${componentName}}}' beginning at L${actualStartLocation.line}:C${actualStartLocation.column}. Expected '{{${componentName}}}' to be at L${expectedStartLocation.line}:C${expectedStartLocation.column}.`;
diff --git a/lib/rules/lint-block-indentation.js b/lib/rules/lint-block-indentation.js
index daee5e99..a5689f9a 100644
--- a/lib/rules/lint-block-indentation.js
+++ b/lib/rules/lint-block-indentation.js
@@ -74,6 +74,20 @@ function isControlChar(char) {
   return char === '~' || char === '{' || char === '}';
 }
 
+
+/**
+ * @description Returns the original node path, with special handling for contextual components.
+ */
+function getOriginalPath(node){
+  // Contextual components like `{{foo.bar}}` are interpreted as `{{component foo.bar}}`.
+  // Therefore, we need to do this so error messages report the correct 
+  // component names, e.g., `{{foo.bar}}` instead of `{{component}}`
+  if (node._isContextualComponent) {
+    return node.params[0].original;
+  }
+  return node.path.original;
+}
+
 module.exports = class BlockIndentation extends Rule {
   parseConfig(config) {
     let configType = typeof config;
@@ -137,10 +151,11 @@ module.exports = class BlockIndentation extends Rule {
 
     if (startLine === 1 && startColumn !== 0) {
       let isElementNode = AstNodeInfo.isElementNode(node);
-      let displayName = isElementNode ? node.tag : node.path.original;
-      let startLocation = `L${node.loc.start.line}:C${node.loc.start.column}`;
+      let displayName = isElementNode ? node.tag : getOriginalPath(node);
       let display = isElementNode ? `<${displayName}>` : `{{#${displayName}}}`;
+      let startLocation = `L${node.loc.start.line}:C${node.loc.start.column}`;
 
+      
       let warning = `Incorrect indentation for \`${display}\` beginning at ${startLocation}. ` +
           `Expected \`${display}\` to be at an indentation of 0, but was found at ${startColumn}.`;
 
@@ -159,15 +174,11 @@ module.exports = class BlockIndentation extends Rule {
     }
 
     let isElementNode = AstNodeInfo.isElementNode(node);
-    let displayName = isElementNode ? node.tag : node.path.original;
+    let displayName = isElementNode ? node.tag : getOriginalPath(node);
     let display = isElementNode ? `</${displayName}>` : `{{/${displayName}}}`;
     let startColumn = node.loc.start.column;
     let endColumn   = node.loc.end.column;
 
-    if (node._isContextualComponent) {
-      displayName = node.params[0].original;
-    }
-
     let controlCharCount = this.endingControlCharCount(node);
     let correctedEndColumn = endColumn - displayName.length - controlCharCount;
 
@@ -275,9 +286,9 @@ module.exports = class BlockIndentation extends Rule {
         if (isElementNode) {
           display = `<${child.tag}>`;
         } else if (AstNodeInfo.isBlockStatement(child)){
-          display = `{{#${child.path.original}}}`;
+          display = `{{#${getOriginalPath(child)}}}`;
         } else if (AstNodeInfo.isMustacheStatement(child)) {
-          display = `{{${child.path.original}}}`;
+          display = `{{${getOriginalPath(child)}}}`;
         } else if (AstNodeInfo.isTextNode(child)) {
           display = child.chars;
         } else if (AstNodeInfo.isCommentStatement(child)) {
@@ -285,7 +296,7 @@ module.exports = class BlockIndentation extends Rule {
         } else if (AstNodeInfo.isMustacheCommentStatement(child)) {
           display = `{{!${child.value}}}`;
         } else {
-          display = child.path.original;
+          display = getOriginalPath(child);
         }
 
         let startLocation = `L${child.loc.start.line}:C${child.loc.start.column}`;


Test patch:
diff --git a/test/unit/rules/lint-attribute-indentation-test.js b/test/unit/rules/lint-attribute-indentation-test.js
index e8494cf3..64238a3b 100644
--- a/test/unit/rules/lint-attribute-indentation-test.js
+++ b/test/unit/rules/lint-attribute-indentation-test.js
@@ -1362,5 +1362,21 @@ generateRuleTests({
       },
     ]
   },
+  {
+    template: `{{#foo bar as |foo|}} 
+    {{foo.bar 
+      baz}}{{/foo}}`,
+    results: [
+      {
+        'column': 9,
+        'line': 3,
+        'message': `Incorrect indentation of close curly braces '}}' for the component '{{foo.bar}}' beginning at L3:C9. Expected '{{foo.bar}}' to be at L4:C4.`,
+        'moduleId': 'layout.hbs',
+        'rule': 'attribute-indentation',
+        'severity': 2,
+        'source': '{{foo.bar \n      baz}}'
+      }
+    ]
+  },
   ]
 });
diff --git a/test/unit/rules/lint-block-indentation-test.js b/test/unit/rules/lint-block-indentation-test.js
index 38fc403f..3d86d77d 100644
--- a/test/unit/rules/lint-block-indentation-test.js
+++ b/test/unit/rules/lint-block-indentation-test.js
@@ -649,5 +649,34 @@ generateRuleTests({
         }
       ]
     },
+    {
+      template: [
+        '{{#foo bar as |foobar|}}',
+        '   {{#foobar.baz}}{{/foobar.baz}}',
+        '   {{foobar.baz}}',
+        '{{/foo}}'
+      ].join('\n'),
+
+      results: [
+        {
+          'column': 3,
+          'line': 2,
+          'message': 'Incorrect indentation for `{{#foobar.baz}}` beginning at L2:C3. Expected `{{#foobar.baz}}` to be at an indentation of 2 but was found at 3.',
+          'moduleId': 'layout.hbs',
+          'rule': 'block-indentation',
+          'severity': 2,
+          'source': '{{#foo bar as |foobar|}}\n   {{#foobar.baz}}{{/foobar.baz}}\n   {{foobar.baz}}\n{{/foo}}'
+        },
+        {
+          'column': 3,
+          'line': 3,
+          'message': 'Incorrect indentation for `{{foobar.baz}}` beginning at L3:C3. Expected `{{foobar.baz}}` to be at an indentation of 2 but was found at 3.',
+          'moduleId': 'layout.hbs',
+          'rule': 'block-indentation',
+          'severity': 2,
+          'source': '{{#foo bar as |foobar|}}\n   {{#foobar.baz}}{{/foobar.baz}}\n   {{foobar.baz}}\n{{/foo}}'
+        }
+      ]
+    },
   ]
 });