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

Issue:
block-indentation should ignore <pre>
the `block-indentation` rule, in its current state, can be harmful around HTML tags where whitespace is meaningful (i.e., `<pre>`)

To reproduce
```html
      <pre
        class="bg-grey-lighter border border-grey-light text-grey-darkest text-sm font-mono rounded p-3 mt-2 overflow-scroll">.marquee-lightspeed { -webkit-marquee-speed: fast; }
.marquee-lightspeeder { -webkit-marquee-speed: faster; }</pre>
```
I get this linter feedback
```
15:62  error  Incorrect indentation for `pre` beginning at L14:C6.
Expected `</pre>` ending at L15:C62 to be at an indentation of 6 but was found at 56.
block-indentation
```
The whitespace I'd have to add to make the linter happy in this case would result in an undesirable visual change to the rendered HTML (i.e., an extra blank newline at the bottom of each `<pre>`)

### Suggested solution
ignore `<pre>` for this rule

---
##### Versions
ember-cli-template-lint: 1.0.0-beta.3
ember-template-lint: 1.1.0

Pull request description:
Do not validate indentation within `<pre>`,`<script>`, or `<style>`.
Fixes #702

Patch:
diff --git a/lib/rules/lint-block-indentation.js b/lib/rules/lint-block-indentation.js
index df89e03b..2822aec4 100644
--- a/lib/rules/lint-block-indentation.js
+++ b/lib/rules/lint-block-indentation.js
@@ -71,6 +71,7 @@ const VOID_TAGS = {
   track: true,
   wbr: true,
 };
+const IGNORED_ELEMENTS = new Set(['pre', 'script', 'style']);
 
 function isControlChar(char) {
   return char === '~' || char === '{' || char === '}';
@@ -108,13 +109,22 @@ module.exports = class BlockIndentation extends Rule {
   }
 
   visitor() {
+    this._elementStack = [];
+
     return {
       BlockStatement(node) {
         this.process(node);
       },
 
-      ElementNode(node) {
-        this.process(node);
+      ElementNode: {
+        enter(node) {
+          this._elementStack.push(node);
+
+          this.process(node);
+        },
+        exit() {
+          this._elementStack.pop();
+        },
       },
     };
   }
@@ -138,6 +148,10 @@ module.exports = class BlockIndentation extends Rule {
       return;
     }
 
+    if (this.isWithinIgnoredElement()) {
+      return;
+    }
+
     let startColumn = node.loc.start.column;
     let startLine = node.loc.start.line;
 
@@ -165,6 +179,10 @@ module.exports = class BlockIndentation extends Rule {
       return;
     }
 
+    if (this.isWithinIgnoredElement()) {
+      return;
+    }
+
     let isElementNode = AstNodeInfo.isElementNode(node);
     let displayName = isElementNode ? node.tag : node.path.original;
     let display = isElementNode ? `</${displayName}>` : `{{/${displayName}}}`;
@@ -194,6 +212,10 @@ module.exports = class BlockIndentation extends Rule {
 
   // eslint-disable-next-line complexity
   validateBlockChildren(node) {
+    if (this.isWithinIgnoredElement()) {
+      return;
+    }
+
     let children = AstNodeInfo.childrenFor(node).filter(x => !x._isElseIfBlock);
 
     if (!AstNodeInfo.hasChildren(node)) {
@@ -380,6 +402,10 @@ module.exports = class BlockIndentation extends Rule {
       return false;
     }
 
+    if (this.isWithinIgnoredElement()) {
+      return;
+    }
+
     // do not validate nodes without children (whitespace will count as TextNodes)
     if (AstNodeInfo.isElementNode(node)) {
       return AstNodeInfo.hasChildren(node);
@@ -428,4 +454,8 @@ module.exports = class BlockIndentation extends Rule {
 
     return leadingControlCharCount + closingSlash + trailingControlCharCount;
   }
+
+  isWithinIgnoredElement() {
+    return this._elementStack.some(n => IGNORED_ELEMENTS.has(n.tag));
+  }
 };


Test patch:
diff --git a/test/unit/rules/lint-block-indentation-test.js b/test/unit/rules/lint-block-indentation-test.js
index 6f38aec3..d14abb3f 100644
--- a/test/unit/rules/lint-block-indentation-test.js
+++ b/test/unit/rules/lint-block-indentation-test.js
@@ -105,6 +105,8 @@ generateRuleTests({
       '{{/foo-bar}}',
     ].join('\n'),
     ['{{#if foo}}', '  &nbsp;bar', '{{/if}}'].join('\n'),
+    '<pre>\nsome text</pre>',
+    '<pre>\n{{#foo}}\n {{#baz}}  hi!\n   {{derp}}{{/baz}}{{/foo}}\n </pre>',
   ],
 
   bad: [