From 6fd7354b55e74ce0d1240bc26ce9081e21e88c32 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Vincent=20Molini=C3=A9?=
Date: Thu, 16 Dec 2021 17:48:59 +0100
Subject: [PATCH] feat(block-indentation): add autofix to the block-indentation
rule
---
lib/rules/block-indentation.js | 415 ++++++++++++++++------
test/unit/rules/block-indentation-test.js | 127 ++++++-
2 files changed, 430 insertions(+), 112 deletions(-)
diff --git a/lib/rules/block-indentation.js b/lib/rules/block-indentation.js
index 0bfe98e94c..e0a342b5c9 100644
--- a/lib/rules/block-indentation.js
+++ b/lib/rules/block-indentation.js
@@ -21,7 +21,7 @@
+
```
2. Forces children of all blocks to start at a single indentation level deeper.
@@ -52,6 +52,8 @@
const assert = require('assert');
+const { parse, sourceForLoc } = require('ember-template-recast');
+
const AstNodeInfo = require('../helpers/ast-node-info');
const createErrorMessage = require('../helpers/create-error-message');
const Rule = require('./_base');
@@ -81,6 +83,18 @@ function isControlChar(char) {
return char === '~' || char === '{' || char === '}';
}
+function get(node, path) {
+ let value = node;
+
+ if (path) {
+ for (const property of path) {
+ value = value[property];
+ }
+ }
+
+ return value;
+}
+
module.exports = class BlockIndentation extends Rule {
parseConfig(config) {
let configType = typeof config;
@@ -161,17 +175,33 @@ module.exports = class BlockIndentation extends Rule {
visitor() {
this._elementStack = [];
+ this.seen = new Set();
+ this.nodeToSource = new WeakMap();
return {
+ Template(node) {
+ if (this.mode === 'fix') {
+ if (
+ node.body[0] &&
+ node.body[0].type === 'TextNode' &&
+ !node.body[0].chars.replace(/ +/, '')
+ ) {
+ // We need to remove the text node in this case
+ node.body.shift();
+ }
+ }
+ return node;
+ },
+
BlockStatement(node) {
- this.process(node);
+ return this.process(node);
},
ElementNode: {
enter(node) {
this._elementStack.push(node);
- this.process(node);
+ return this.process(node);
},
exit() {
this._elementStack.pop();
@@ -180,56 +210,132 @@ module.exports = class BlockIndentation extends Rule {
};
}
- process(node) {
+ process(node, nestedOptions) {
// Nodes that start and end on the same line cannot have any indentation
// issues (since the column of the start block was already checked in the
// parent's validateBlockChildren())
- if (node.loc.start.line === node.loc.end.line) {
- return;
+ if (node.loc.start.line === node.loc.end.line || this.seen.has(node)) {
+ return nestedOptions ? nestedOptions.sourceToEdit : node;
+ }
+
+ let fixedNode = this.validateBlockStart(node, nestedOptions);
+ fixedNode = this.validateBlockElse(fixedNode, nestedOptions);
+ fixedNode = this.validateBlockEnd(fixedNode, nestedOptions);
+ fixedNode = this.validateBlockChildren(fixedNode, nestedOptions);
+
+ if (this.mode === 'fix') {
+ const { sourceToEdit, path } = nestedOptions || { path: [] };
+ const sourceToEditBeforeFix =
+ sourceToEdit || this.nodeToSource.get(fixedNode) || this.source.join('');
+ let sourceToEditFixed = sourceToEditBeforeFix;
+
+ const childrenWithPath = [];
+
+ if (fixedNode.inverse) {
+ const inverseChildPath = [...path, 'inverse', 'body'];
+ for (const [index, inverseNestedNode] of fixedNode.inverse.body.entries()) {
+ childrenWithPath.push({
+ path: [...inverseChildPath, index],
+ child: inverseNestedNode,
+ });
+ }
+ }
+
+ const childPath =
+ fixedNode.type === 'BlockStatement' ? [...path, 'program', 'body'] : [...path, 'children'];
+ const children =
+ fixedNode.type === 'BlockStatement' ? fixedNode.program.body : fixedNode.children;
+ for (const [index, nestedNode] of children.entries()) {
+ childrenWithPath.push({
+ path: [...childPath, index],
+ child: nestedNode,
+ });
+ }
+
+ for (const { path, child } of childrenWithPath) {
+ if (['BlockStatement', 'ElementNode'].includes(child.type)) {
+ sourceToEditFixed = this.process(child, {
+ sourceToEdit: sourceToEditFixed,
+ path,
+ });
+ if (nestedOptions) {
+ nestedOptions.sourceToEdit = sourceToEditFixed;
+ }
+ }
+ }
+
+ if (sourceToEditBeforeFix !== sourceToEditFixed) {
+ fixedNode = this.fixLine(
+ sourceToEditFixed.split('\n'),
+ 0,
+ fixedNode.loc.start.column,
+ fixedNode.loc.start.column,
+ nestedOptions && nestedOptions.path
+ );
+ }
}
- this.validateBlockStart(node);
- this.validateBlockElse(node);
- this.validateBlockEnd(node);
- this.validateBlockChildren(node);
+ this.seen.add(fixedNode);
+
+ return nestedOptions ? nestedOptions.sourceToEdit : fixedNode;
}
- validateBlockStart(node) {
+ validateBlockStart(node, nestedOptions = {}) {
if (!this.shouldValidateBlockEnd(node)) {
- return;
+ return node;
}
if (this.isWithinIgnoredElement()) {
- return;
+ return node;
}
let startColumn = node.loc.start.column;
let startLine = node.loc.start.line;
if (startLine === 1 && startColumn !== 0) {
- let isElementNode = node && node.type === 'ElementNode';
- let displayName = isElementNode ? node.tag : node.path.original;
- 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}.`;
-
- this.log({
- message: warning,
- node,
- });
+ if (this.mode === 'fix') {
+ const elementSource =
+ nestedOptions.sourceToEdit ||
+ sourceForLoc(this.nodeToSource.get(node) || this.source, {
+ end: node.loc.end,
+ start: { line: node.loc.start.line, column: 0 },
+ });
+ const fixedNode = this.fixLine(
+ elementSource.split('\n'),
+ 0,
+ startColumn,
+ 0,
+ nestedOptions.path
+ );
+ nestedOptions.sourceToEdit = this.nodeToSource.get(fixedNode);
+ return fixedNode;
+ } else {
+ let isElementNode = node && node.type === 'ElementNode';
+ let displayName = isElementNode ? node.tag : node.path.original;
+ 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}.`;
+
+ this.log({
+ message: warning,
+ node,
+ isFixable: true,
+ });
+ }
}
+ return node;
}
- validateBlockEnd(node) {
+ validateBlockEnd(node, nestedOptions = {}) {
if (!this.shouldValidateBlockEnd(node)) {
- return;
+ return node;
}
if (this.isWithinIgnoredElement()) {
- return;
+ return node;
}
let isElementNode = node && node.type === 'ElementNode';
@@ -242,44 +348,73 @@ module.exports = class BlockIndentation extends Rule {
let correctedEndColumn = endColumn - displayName.length - controlCharCount;
if (correctedEndColumn !== startColumn) {
- let startLocation = `L${node.loc.start.line}:C${node.loc.start.column}`;
- let endLocation = `L${node.loc.end.line}:C${node.loc.end.column}`;
-
- let warning =
- `Incorrect indentation for \`${displayName}\` beginning at ${startLocation}` +
- `. Expected \`${display}\` ending at ${endLocation} to be at an indentation of ${startColumn} but ` +
- `was found at ${correctedEndColumn}.`;
-
- this.log({
- message: warning,
- node,
- line: node.loc.end.line,
- column: node.loc.end.column,
- });
+ if (this.mode === 'fix') {
+ const elementSource =
+ nestedOptions.sourceToEdit ||
+ `${' '.repeat(node.loc.start.column)}${sourceForLoc(
+ this.nodeToSource.get(node) || this.source,
+ {
+ end: node.loc.end,
+ start: { line: node.loc.start.line, column: node.loc.start.column },
+ }
+ )}`;
+ const lines = elementSource.split('\n');
+ const fixedNode = this.fixLine(
+ lines,
+ node.loc.end.line - (nestedOptions.sourceToEdit ? 1 : node.loc.start.line),
+ correctedEndColumn,
+ startColumn,
+ nestedOptions.path
+ );
+ nestedOptions.sourceToEdit = this.nodeToSource.get(fixedNode);
+ return fixedNode;
+ } else {
+ let startLocation = `L${node.loc.start.line}:C${node.loc.start.column}`;
+ let endLocation = `L${node.loc.end.line}:C${node.loc.end.column}`;
+
+ let warning =
+ `Incorrect indentation for \`${displayName}\` beginning at ${startLocation}` +
+ `. Expected \`${display}\` ending at ${endLocation} to be at an indentation of ${startColumn} but ` +
+ `was found at ${correctedEndColumn}.`;
+
+ this.log({
+ message: warning,
+ node,
+ line: node.loc.end.line,
+ column: node.loc.end.column,
+ isFixable: true,
+ });
+ }
}
+
+ return node;
}
- validateBlockChildren(node) {
+ validateBlockChildren(node, nestedOptions = {}) {
if (this.isWithinIgnoredElement()) {
- return;
+ return node;
}
let children = AstNodeInfo.childrenFor(node).filter((x) => !x._isElseIfBlock);
if (!AstNodeInfo.hasChildren(node)) {
- return;
+ return node;
}
// HTML elements that start and end on the same line are fine
if (node.loc.start.line === node.loc.end.line) {
- return;
+ return node;
}
let startColumn = node.loc.start.column;
let expectedStartColumn = startColumn + this.config.indentation;
+ let fixedNode = node;
- for (let i = 0; i < children.length; i++) {
+ let i = 0;
+ while (i < children.length) {
let child = children[i];
+ i += 1;
+
if (!child.loc) {
continue;
}
@@ -295,7 +430,7 @@ module.exports = class BlockIndentation extends Rule {
// preceded by another element or statement, or by some text. So walk
// backwards looking for something else on this line.
let hasLeadingContent = false;
- for (let j = i - 1; j >= 0; j--) {
+ for (let j = i - 2; j >= 0; j--) {
let sibling = children[j];
if (sibling.loc && sibling.type !== 'TextNode') {
// Found an element or statement. If it's on this line, then we
@@ -347,7 +482,7 @@ module.exports = class BlockIndentation extends Rule {
if (/^(\r\n|\n)/.test(child.chars)) {
childStartColumn = 0;
let newLineLength = child.chars.length - withoutLeadingNewLines.length;
- let leadingNewLines = child.chars.slice(newLineLength);
+ let leadingNewLines = child.chars.slice(0, newLineLength);
childStartLine += (leadingNewLines.match(/\n/g) || []).length;
}
@@ -361,92 +496,154 @@ module.exports = class BlockIndentation extends Rule {
}
if (expectedStartColumn !== childStartColumn) {
- let isElementNode = child.type === 'ElementNode';
- let display;
+ if (this.mode === 'fix') {
+ const sourceToFix =
+ nestedOptions.sourceToEdit ||
+ sourceForLoc(this.nodeToSource.get(fixedNode) || this.source, {
+ end: fixedNode.loc.end,
+ start: { line: fixedNode.loc.start.line, column: 0 },
+ });
+ const lines = sourceToFix.split('\n');
+
+ fixedNode = this.fixLine(
+ lines,
+ childStartLine - 1,
+ childStartColumn,
+ expectedStartColumn,
+ nestedOptions.path
+ );
+ nestedOptions.sourceToEdit = this.nodeToSource.get(fixedNode);
- if (isElementNode) {
- display = `<${child.tag}>`;
+ children = AstNodeInfo.childrenFor(fixedNode).filter((x) => !x._isElseIfBlock);
} else {
- switch (child.type) {
- case 'BlockStatement': {
- display = `{{#${child.path.original}}}`;
+ const display = this.getDisplayNameForNode(child);
+
+ 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 ` +
+ `was found at ${childStartColumn}.`;
+
+ this.log({
+ message: warning,
+ node,
+ line: childStartLine,
+ column: childStartColumn,
+ isFixable: true,
+ });
+ }
+ }
+ }
- break;
- }
- case 'MustacheStatement': {
- display = `{{${child.path.original}}}`;
+ return fixedNode;
+ }
- break;
- }
- case 'TextNode': {
- display = child.chars.replace(/^\s*/, '');
+ validateBlockElse(node, nestedOptions = {}) {
+ if (node.type !== 'BlockStatement' || !node.inverse) {
+ return node;
+ }
- break;
- }
- case 'CommentStatement': {
- display = ``;
+ if (this.detectNestedElseIfBlock(node)) {
+ let elseBlockStatement = node.inverse.body[0];
- break;
- }
- case 'MustacheCommentStatement': {
- display = `{{!${child.value}}}`;
+ elseBlockStatement._isElseIfBlock = true;
+ }
- break;
- }
- default: {
- display = child.path.original;
- }
- }
- }
+ let inverse = node.inverse;
+ let startColumn = node.loc.start.column;
+ let elseStartColumn = node.program.loc.end.column;
+
+ if (elseStartColumn !== startColumn) {
+ if (this.mode === 'fix') {
+ const sourceToFix =
+ nestedOptions.sourceToEdit ||
+ sourceForLoc(this.nodeToSource.get(node) || this.source, {
+ end: node.loc.end,
+ start: { line: node.loc.start.line, column: 0 },
+ });
+ const lines = sourceToFix.split('\n');
+ const elseLine = node.program.loc.end.line;
+ const fixedNode = this.fixLine(
+ lines,
+ elseLine - 1,
+ elseStartColumn,
+ startColumn,
+ nestedOptions.path
+ );
+ nestedOptions.sourceToEdit = this.nodeToSource.get(fixedNode);
+ return fixedNode;
+ } else {
+ let displayName = node.path.original;
+ let startLocation = `L${node.loc.start.line}:C${node.loc.start.column}`;
+ let elseLocation = `L${inverse.loc.start.line}:C${elseStartColumn}`;
- 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 ` +
- `was found at ${childStartColumn}.`;
+ `Incorrect indentation for inverse block of \`{{#${displayName}}}\` beginning at ${startLocation}` +
+ `. Expected \`{{else}}\` starting at ${elseLocation} to be at an indentation of ${startColumn} but ` +
+ `was found at ${elseStartColumn}.`;
this.log({
message: warning,
node,
- line: childStartLine,
- column: childStartColumn,
+ line: inverse.loc.start.line,
+ column: elseStartColumn,
+ isFixable: true,
});
}
}
+
+ return node;
}
- validateBlockElse(node) {
- if (node.type !== 'BlockStatement' || !node.inverse) {
- return;
+ getDisplayNameForNode(node) {
+ switch (node.type) {
+ case 'ElementNode':
+ return `<${node.tag}>`;
+ case 'BlockStatement': {
+ return `{{#${node.path.original}}}`;
+ }
+ case 'MustacheStatement': {
+ return `{{${node.path.original}}}`;
+ }
+ case 'TextNode': {
+ return node.chars.replace(/^\s*/, '');
+ }
+ case 'CommentStatement': {
+ return ``;
+ }
+ case 'MustacheCommentStatement': {
+ return `{{!${node.value}}}`;
+ }
+ default: {
+ return node.path.original;
+ }
+ }
+ }
+
+ fixLine(lines, lineNumber, actualColumn, expectedColumn, path) {
+ const line = lines[lineNumber];
+ if (!line.startsWith(' '.repeat(actualColumn))) {
+ lines[lineNumber] = `${line.slice(0, Math.max(0, actualColumn)).trimEnd()}\n${' '.repeat(
+ expectedColumn
+ )}${line.slice(Math.max(0, actualColumn))}`;
+ } else {
+ lines[lineNumber] = actualColumn
+ ? line.replace(/^ +/, ' '.repeat(expectedColumn))
+ : `${' '.repeat(expectedColumn)}${line}`;
}
+ let node = parse(lines.join('\n')).body.find((node) => node.type !== 'TextNode');
+ node = get(node, path);
+
if (this.detectNestedElseIfBlock(node)) {
let elseBlockStatement = node.inverse.body[0];
elseBlockStatement._isElseIfBlock = true;
}
- let inverse = node.inverse;
- let startColumn = node.loc.start.column;
- let elseStartColumn = node.program.loc.end.column;
+ this.nodeToSource.set(node, lines.join('\n'));
- if (elseStartColumn !== startColumn) {
- let displayName = node.path.original;
- let startLocation = `L${node.loc.start.line}:C${node.loc.start.column}`;
- let elseLocation = `L${inverse.loc.start.line}:C${elseStartColumn}`;
-
- let warning =
- `Incorrect indentation for inverse block of \`{{#${displayName}}}\` beginning at ${startLocation}` +
- `. Expected \`{{else}}\` starting at ${elseLocation} to be at an indentation of ${startColumn} but ` +
- `was found at ${elseStartColumn}.`;
-
- this.log({
- message: warning,
- node,
- line: inverse.loc.start.line,
- column: elseStartColumn,
- });
- }
+ return node;
}
detectNestedElseIfBlock(node) {
diff --git a/test/unit/rules/block-indentation-test.js b/test/unit/rules/block-indentation-test.js
index 7c1c2b4d7e..5297f6dabc 100644
--- a/test/unit/rules/block-indentation-test.js
+++ b/test/unit/rules/block-indentation-test.js
@@ -233,6 +233,7 @@ generateRuleTests({
{
// start and end must be the same indentation
template: '\n {{#each cats as |dog|}}\n {{/each}}',
+ fixedTemplate: '\n {{#each cats as |dog|}}\n {{/each}}',
verifyResults(results) {
expect(results).toMatchInlineSnapshot(`
@@ -242,6 +243,7 @@ generateRuleTests({
"endColumn": 17,
"endLine": 3,
"filePath": "layout.hbs",
+ "isFixable": true,
"line": 3,
"message": "Incorrect indentation for \`each\` beginning at L2:C2. Expected \`{{/each}}\` ending at L3:C17 to be at an indentation of 2 but was found at 8.",
"rule": "block-indentation",
@@ -255,6 +257,7 @@ generateRuleTests({
},
{
template: '\n
',
+ fixedTemplate: '\n
',
verifyResults(results) {
expect(results).toMatchInlineSnapshot(`
@@ -264,6 +267,7 @@ generateRuleTests({
"endColumn": 8,
"endLine": 2,
"filePath": "layout.hbs",
+ "isFixable": true,
"line": 2,
"message": "Incorrect indentation for \`div\` beginning at L1:C0. Expected \`\` ending at L2:C8 to be at an indentation of 0 but was found at 2.",
"rule": "block-indentation",
@@ -277,6 +281,7 @@ generateRuleTests({
},
{
template: '',
+ fixedTemplate: '',
verifyResults(results) {
expect(results).toMatchInlineSnapshot(`
@@ -286,6 +291,7 @@ generateRuleTests({
"endColumn": 30,
"endLine": 2,
"filePath": "layout.hbs",
+ "isFixable": true,
"line": 2,
"message": "Incorrect indentation for \`div\` beginning at L1:C0. Expected \`\` ending at L2:C30 to be at an indentation of 0 but was found at 24.",
"rule": "block-indentation",
@@ -308,6 +314,7 @@ generateRuleTests({
"endColumn": 6,
"endLine": 3,
"filePath": "layout.hbs",
+ "isFixable": true,
"line": 2,
"message": "Incorrect indentation for \`\` beginning at L2:C0. Expected \`
\` to be at an indentation of 2 but was found at 0.",
"rule": "block-indentation",
@@ -322,6 +329,7 @@ generateRuleTests({
},
{
template: '{{#if}}\n
Stuff goes here
\n{{/if}}',
+ fixedTemplate: '{{#if}}\n Stuff goes here
\n{{/if}}',
verifyResults(results) {
expect(results).toMatchInlineSnapshot(`
@@ -331,6 +339,7 @@ generateRuleTests({
"endColumn": 7,
"endLine": 3,
"filePath": "layout.hbs",
+ "isFixable": true,
"line": 2,
"message": "Incorrect indentation for \`\` beginning at L2:C0. Expected \`
\` to be at an indentation of 2 but was found at 0.",
"rule": "block-indentation",
@@ -351,6 +360,13 @@ generateRuleTests({
' Good night\n' +
' {{/if}}\n' +
'{{/if}}',
+ fixedTemplate:
+ '{{#if isMorning}}\n' +
+ '{{else}}\n' +
+ ' {{#if something}}\n' +
+ ' Good night\n' +
+ ' {{/if}}\n' +
+ '{{/if}}',
verifyResults(results) {
expect(results).toMatchInlineSnapshot(`
@@ -360,6 +376,7 @@ generateRuleTests({
"endColumn": 11,
"endLine": 5,
"filePath": "layout.hbs",
+ "isFixable": true,
"line": 5,
"message": "Incorrect indentation for \`if\` beginning at L3:C2. Expected \`{{/if}}\` ending at L5:C11 to be at an indentation of 2 but was found at 4.",
"rule": "block-indentation",
@@ -375,7 +392,8 @@ generateRuleTests({
{
config: 4,
- template: '' + '
',
+ template: '',
+ fixedTemplate: '',
verifyResults(results) {
expect(results).toMatchInlineSnapshot(`
@@ -385,6 +403,7 @@ generateRuleTests({
"endColumn": 6,
"endLine": 3,
"filePath": "layout.hbs",
+ "isFixable": true,
"line": 2,
"message": "Incorrect indentation for \`\` beginning at L2:C2. Expected \`
\` to be at an indentation of 4 but was found at 2.",
"rule": "block-indentation",
@@ -399,6 +418,7 @@ generateRuleTests({
},
{
template: '
\n' + ' {{foo}}\n' + '{{bar}}\n' + '
',
+ fixedTemplate: '\n' + ' {{foo}}\n' + ' {{bar}}\n' + '
',
verifyResults(results) {
expect(results).toMatchInlineSnapshot(`
@@ -408,6 +428,7 @@ generateRuleTests({
"endColumn": 6,
"endLine": 4,
"filePath": "layout.hbs",
+ "isFixable": true,
"line": 3,
"message": "Incorrect indentation for \`{{bar}}\` beginning at L3:C0. Expected \`{{bar}}\` to be at an indentation of 2 but was found at 0.",
"rule": "block-indentation",
@@ -423,6 +444,7 @@ generateRuleTests({
},
{
template: '\n' + ' Foo:\n' + '{{bar}}\n' + '
',
+ fixedTemplate: '\n' + ' Foo:\n' + ' {{bar}}\n' + '
',
verifyResults(results) {
expect(results).toMatchInlineSnapshot(`
@@ -432,6 +454,7 @@ generateRuleTests({
"endColumn": 6,
"endLine": 4,
"filePath": "layout.hbs",
+ "isFixable": true,
"line": 3,
"message": "Incorrect indentation for \`{{bar}}\` beginning at L3:C0. Expected \`{{bar}}\` to be at an indentation of 2 but was found at 0.",
"rule": "block-indentation",
@@ -450,6 +473,11 @@ generateRuleTests({
// has other content preceding it on its line
template:
'\n' + ' Foo{{#some-thing}}\n' + ' {{/some-thing}}\n' + '
',
+ fixedTemplate:
+ '\n' +
+ ' Foo{{#some-thing}}\n' +
+ ' {{/some-thing}}\n' +
+ '
',
verifyResults(results) {
expect(results).toMatchInlineSnapshot(`
@@ -459,6 +487,7 @@ generateRuleTests({
"endColumn": 17,
"endLine": 3,
"filePath": "layout.hbs",
+ "isFixable": true,
"line": 3,
"message": "Incorrect indentation for \`some-thing\` beginning at L2:C18. Expected \`{{/some-thing}}\` ending at L3:C17 to be at an indentation of 18 but was found at 2.",
"rule": "block-indentation",
@@ -474,6 +503,8 @@ generateRuleTests({
// Start and end of multi-line element must be aligned, even when start
// has other content preceding it on its line
template: '{{#if foo}}\n' + ' {{foo}} \n' + ' Bar\n' + '
\n' + '{{/if}}',
+ fixedTemplate:
+ '{{#if foo}}\n' + ' {{foo}} \n' + ' Bar\n' + '
\n' + '{{/if}}',
verifyResults(results) {
expect(results).toMatchInlineSnapshot(`
@@ -483,6 +514,7 @@ generateRuleTests({
"endColumn": 6,
"endLine": 4,
"filePath": "layout.hbs",
+ "isFixable": true,
"line": 4,
"message": "Incorrect indentation for \`p\` beginning at L2:C10. Expected \`
\` ending at L4:C6 to be at an indentation of 10 but was found at 2.",
"rule": "block-indentation",
@@ -498,6 +530,7 @@ generateRuleTests({
{
template: ['', '', '
'].join('\n'),
+ fixedTemplate: ['', ' ', '
'].join('\n'),
verifyResults(results) {
expect(results).toMatchInlineSnapshot(`
@@ -507,6 +540,7 @@ generateRuleTests({
"endColumn": 6,
"endLine": 3,
"filePath": "layout.hbs",
+ "isFixable": true,
"line": 2,
"message": "Incorrect indentation for \`\` beginning at L2:C0. Expected \`\` to be at an indentation of 2 but was found at 0.",
"rule": "block-indentation",
@@ -521,6 +555,7 @@ generateRuleTests({
},
{
template: ['{{#if foo}}', ' {{else}}', '{{/if}}'].join('\n'),
+ fixedTemplate: ['{{#if foo}}', '{{else}}', '{{/if}}'].join('\n'),
verifyResults(results) {
expect(results).toMatchInlineSnapshot(`
@@ -530,6 +565,7 @@ generateRuleTests({
"endColumn": 7,
"endLine": 3,
"filePath": "layout.hbs",
+ "isFixable": true,
"line": 2,
"message": "Incorrect indentation for inverse block of \`{{#if}}\` beginning at L1:C0. Expected \`{{else}}\` starting at L2:C2 to be at an indentation of 0 but was found at 2.",
"rule": "block-indentation",
@@ -552,6 +588,14 @@ generateRuleTests({
' {{/if~}}',
' {{/if}}',
].join('\n'),
+ fixedTemplate: [
+ '{{#if foo}}',
+ '{{else if bar}}',
+ '{{else}}',
+ ' {{#if baz}}',
+ ' {{/if~}}',
+ '{{/if}}',
+ ].join('\n'),
verifyResults(results) {
expect(results).toMatchInlineSnapshot(`
@@ -561,6 +605,7 @@ generateRuleTests({
"endColumn": 9,
"endLine": 6,
"filePath": "layout.hbs",
+ "isFixable": true,
"line": 6,
"message": "Incorrect indentation for \`if\` beginning at L1:C0. Expected \`{{/if}}\` ending at L6:C9 to be at an indentation of 0 but was found at 2.",
"rule": "block-indentation",
@@ -579,6 +624,7 @@ generateRuleTests({
{
template: ['{{#each foo as |bar|}}', ' {{else}}', '{{/each}}'].join('\n'),
+ fixedTemplate: ['{{#each foo as |bar|}}', '{{else}}', '{{/each}}'].join('\n'),
verifyResults(results) {
expect(results).toMatchInlineSnapshot(`
@@ -588,6 +634,7 @@ generateRuleTests({
"endColumn": 9,
"endLine": 3,
"filePath": "layout.hbs",
+ "isFixable": true,
"line": 2,
"message": "Incorrect indentation for inverse block of \`{{#each}}\` beginning at L1:C0. Expected \`{{else}}\` starting at L2:C2 to be at an indentation of 0 but was found at 2.",
"rule": "block-indentation",
@@ -609,6 +656,13 @@ generateRuleTests({
' {{! comment with incorrect indentation }}',
'',
].join('\n'),
+ fixedTemplate: [
+ '',
+ ' {{#if foo}}',
+ ' {{/if}}',
+ ' {{! comment with incorrect indentation }}',
+ '
',
+ ].join('\n'),
verifyResults(results) {
expect(results).toMatchInlineSnapshot(`
@@ -618,6 +672,7 @@ generateRuleTests({
"endColumn": 6,
"endLine": 5,
"filePath": "layout.hbs",
+ "isFixable": true,
"line": 4,
"message": "Incorrect indentation for \`{{! comment with incorrect indentation }}\` beginning at L4:C4. Expected \`{{! comment with incorrect indentation }}\` to be at an indentation of 2 but was found at 4.",
"rule": "block-indentation",
@@ -643,6 +698,15 @@ generateRuleTests({
' Good night\n' +
'{{/if}}',
].join('\n'),
+ fixedTemplate: [
+ '{{#if isMorning}}\n' +
+ ' Good morning\n' +
+ '{{else if isAfternoon}}\n' +
+ ' Good afternoon\n' +
+ '{{else}}\n' +
+ ' Good night\n' +
+ '{{/if}}',
+ ].join('\n'),
verifyResults(results) {
expect(results).toMatchInlineSnapshot(`
@@ -652,6 +716,7 @@ generateRuleTests({
"endColumn": 7,
"endLine": 6,
"filePath": "layout.hbs",
+ "isFixable": true,
"line": 1,
"message": "Incorrect indentation for \`Good morning
\` beginning at L1:C19. Expected \`Good morning
@@ -678,6 +743,13 @@ generateRuleTests({
' Good afternoon\n' +
'{{/if}}',
].join('\n'),
+ fixedTemplate: [
+ '{{#if isMorning}}\n' +
+ ' Good morning\n' +
+ '{{else if isAfternoon~}}\n' +
+ ' Good afternoon\n' +
+ '{{/if}}',
+ ].join('\n'),
verifyResults(results) {
expect(results).toMatchInlineSnapshot(`
@@ -687,6 +759,7 @@ generateRuleTests({
"endColumn": 0,
"endLine": 5,
"filePath": "layout.hbs",
+ "isFixable": true,
"line": 4,
"message": "Incorrect indentation for \`Good afternoon
\` beginning at L4:C4. Expected \`Good afternoon
@@ -703,6 +776,7 @@ generateRuleTests({
},
{
template: ['', '{{! What a comment }}', ' {{foo-bar}}', '
'].join('\n'),
+ fixedTemplate: ['', ' {{! What a comment }}', ' {{foo-bar}}', '
'].join('\n'),
verifyResults(results) {
expect(results).toMatchInlineSnapshot(`
@@ -712,6 +786,7 @@ generateRuleTests({
"endColumn": 6,
"endLine": 4,
"filePath": "layout.hbs",
+ "isFixable": true,
"line": 2,
"message": "Incorrect indentation for \`{{! What a comment }}\` beginning at L2:C0. Expected \`{{! What a comment }}\` to be at an indentation of 2 but was found at 0.",
"rule": "block-indentation",
@@ -727,6 +802,7 @@ generateRuleTests({
},
{
template: [' {{! bad comment }}', ' {{foo-bar}}', '
'].join('\n'),
+ fixedTemplate: ['', ' {{! bad comment }}', ' {{foo-bar}}', '
'].join('\n'),
verifyResults(results) {
expect(results).toMatchInlineSnapshot(`
@@ -736,6 +812,7 @@ generateRuleTests({
"endColumn": 6,
"endLine": 3,
"filePath": "layout.hbs",
+ "isFixable": true,
"line": 1,
"message": "Incorrect indentation for \`{{! bad comment }}\` beginning at L1:C6. Expected \`{{! bad comment }}\` to be at an indentation of 2 but was found at 6.",
"rule": "block-indentation",
@@ -750,6 +827,13 @@ generateRuleTests({
},
{
template: ['{{#if media.isMobile}}', '{{else}}', '', '', '{{/if}}'].join('\n'),
+ fixedTemplate: [
+ '{{#if media.isMobile}}',
+ '{{else}}',
+ ' ',
+ ' ',
+ '{{/if}}',
+ ].join('\n'),
verifyResults(results) {
expect(results).toMatchInlineSnapshot(`
@@ -759,6 +843,7 @@ generateRuleTests({
"endColumn": 7,
"endLine": 5,
"filePath": "layout.hbs",
+ "isFixable": true,
"line": 3,
"message": "Incorrect indentation for \`\` beginning at L3:C0. Expected \`\` to be at an indentation of 2 but was found at 0.",
"rule": "block-indentation",
@@ -775,6 +860,7 @@ generateRuleTests({
},
{
template: ['\uFEFF {{#if foo}}', '{{/if}}'].join('\n'),
+ fixedTemplate: ['\uFEFF{{#if foo}}', '{{/if}}'].join('\n'),
verifyResults(results) {
expect(results).toMatchInlineSnapshot(`
@@ -784,6 +870,7 @@ generateRuleTests({
"endColumn": 7,
"endLine": 2,
"filePath": "layout.hbs",
+ "isFixable": true,
"line": 1,
"message": "Incorrect indentation for \`{{#if}}\` beginning at L1:C1. Expected \`{{#if}}\` to be at an indentation of 0, but was found at 1.",
"rule": "block-indentation",
@@ -796,6 +883,7 @@ generateRuleTests({
"endColumn": 7,
"endLine": 2,
"filePath": "layout.hbs",
+ "isFixable": true,
"line": 2,
"message": "Incorrect indentation for \`if\` beginning at L1:C1. Expected \`{{/if}}\` ending at L2:C7 to be at an indentation of 1 but was found at 0.",
"rule": "block-indentation",
@@ -809,6 +897,7 @@ generateRuleTests({
},
{
template: ['{{#if foo}}foo{{else}}', ' bar', '{{/if}}'].join('\n'),
+ fixedTemplate: ['{{#if foo}}', ' foo', '{{else}}', ' bar', '{{/if}}'].join('\n'),
verifyResults(results) {
expect(results).toMatchInlineSnapshot(`
@@ -818,6 +907,7 @@ generateRuleTests({
"endColumn": 7,
"endLine": 3,
"filePath": "layout.hbs",
+ "isFixable": true,
"line": 1,
"message": "Incorrect indentation for inverse block of \`{{#if}}\` beginning at L1:C0. Expected \`{{else}}\` starting at L1:C14 to be at an indentation of 0 but was found at 14.",
"rule": "block-indentation",
@@ -831,6 +921,7 @@ generateRuleTests({
"endColumn": 7,
"endLine": 3,
"filePath": "layout.hbs",
+ "isFixable": true,
"line": 1,
"message": "Incorrect indentation for \`foo\` beginning at L1:C11. Expected \`foo\` to be at an indentation of 2 but was found at 11.",
"rule": "block-indentation",
@@ -845,6 +936,7 @@ generateRuleTests({
},
{
template: ['{{#if foo}}', ' foo', '{{else}}', ' bar', '{{/if}}'].join('\n'),
+ fixedTemplate: ['{{#if foo}}', ' foo', '{{else}}', ' bar', '{{/if}}'].join('\n'),
verifyResults(results) {
expect(results).toMatchInlineSnapshot(`
@@ -854,6 +946,7 @@ generateRuleTests({
"endColumn": 7,
"endLine": 5,
"filePath": "layout.hbs",
+ "isFixable": true,
"line": 4,
"message": "Incorrect indentation for \`bar
\` beginning at L4:C4. Expected \`bar
@@ -880,6 +973,15 @@ generateRuleTests({
' {{/if}}',
'{{/if}}',
].join('\n'),
+ fixedTemplate: [
+ '{{#if foo}}',
+ ' foo',
+ '{{else}}',
+ ' {{#if bar}}',
+ ' bar',
+ ' {{/if}}',
+ '{{/if}}',
+ ].join('\n'),
verifyResults(results) {
expect(results).toMatchInlineSnapshot(`
@@ -889,6 +991,7 @@ generateRuleTests({
"endColumn": 7,
"endLine": 7,
"filePath": "layout.hbs",
+ "isFixable": true,
"line": 4,
"message": "Incorrect indentation for \`{{#if}}\` beginning at L4:C4. Expected \`{{#if}}\` to be at an indentation of 2 but was found at 4.",
"rule": "block-indentation",
@@ -907,6 +1010,7 @@ generateRuleTests({
},
{
template: [' {{#foo-bar}}', ' {{/foo-bar}}'].join('\n'),
+ fixedTemplate: ['{{#foo-bar}}', '{{/foo-bar}}'].join('\n'),
verifyResults(results) {
expect(results).toMatchInlineSnapshot(`
@@ -916,6 +1020,7 @@ generateRuleTests({
"endColumn": 17,
"endLine": 2,
"filePath": "layout.hbs",
+ "isFixable": true,
"line": 1,
"message": "Incorrect indentation for \`{{#foo-bar}}\` beginning at L1:C5. Expected \`{{#foo-bar}}\` to be at an indentation of 0, but was found at 5.",
"rule": "block-indentation",
@@ -929,6 +1034,7 @@ generateRuleTests({
},
{
template: [' ', '
'].join('\n'),
+ fixedTemplate: ['', '
'].join('\n'),
verifyResults(results) {
expect(results).toMatchInlineSnapshot(`
@@ -938,6 +1044,7 @@ generateRuleTests({
"endColumn": 8,
"endLine": 2,
"filePath": "layout.hbs",
+ "isFixable": true,
"line": 1,
"message": "Incorrect indentation for \`\` beginning at L1:C2. Expected \`
\` to be at an indentation of 0, but was found at 2.",
"rule": "block-indentation",
@@ -956,6 +1063,12 @@ generateRuleTests({
' {{foobar.baz}}',
'{{/foo}}',
].join('\n'),
+ fixedTemplate: [
+ '{{#foo bar as |foobar|}}',
+ ' {{#foobar.baz}}{{/foobar.baz}}',
+ ' {{foobar.baz}}',
+ '{{/foo}}',
+ ].join('\n'),
verifyResults(results) {
expect(results).toMatchInlineSnapshot(`
@@ -965,6 +1078,7 @@ generateRuleTests({
"endColumn": 8,
"endLine": 4,
"filePath": "layout.hbs",
+ "isFixable": true,
"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.",
"rule": "block-indentation",
@@ -979,6 +1093,7 @@ generateRuleTests({
"endColumn": 8,
"endLine": 4,
"filePath": "layout.hbs",
+ "isFixable": true,
"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.",
"rule": "block-indentation",
@@ -994,6 +1109,7 @@ generateRuleTests({
},
{
template: ['
', '', '
'].join('\n'),
+ fixedTemplate: ['
', ' ', '
'].join('\n'),
verifyResults(results) {
expect(results).toMatchInlineSnapshot(`
Array [
@@ -1002,6 +1118,7 @@ generateRuleTests({
"endColumn": 6,
"endLine": 3,
"filePath": "layout.hbs",
+ "isFixable": true,
"line": 2,
"message": "Incorrect indentation for \`\` beginning at L2:C0. Expected \`\` to be at an indentation of 2 but was found at 0.",
"rule": "block-indentation",
@@ -1016,6 +1133,7 @@ generateRuleTests({
},
{
template: ['
', '{{! Comment }}', '
'].join('\n'),
+ fixedTemplate: ['
', ' {{! Comment }}', '
'].join('\n'),
verifyResults(results) {
expect(results).toMatchInlineSnapshot(`
Array [
@@ -1024,6 +1142,7 @@ generateRuleTests({
"endColumn": 6,
"endLine": 3,
"filePath": "layout.hbs",
+ "isFixable": true,
"line": 2,
"message": "Incorrect indentation for \`{{! Comment }}\` beginning at L2:C0. Expected \`{{! Comment }}\` to be at an indentation of 2 but was found at 0.",
"rule": "block-indentation",
@@ -1038,6 +1157,7 @@ generateRuleTests({
},
{
template: ['
', 'test{{! Comment }}', '
'].join('\n'),
+ fixedTemplate: ['
', ' test{{! Comment }}', '
'].join('\n'),
config: {
ignoreComments: true,
},
@@ -1049,8 +1169,9 @@ generateRuleTests({
"endColumn": 6,
"endLine": 3,
"filePath": "layout.hbs",
- "line": 1,
- "message": "Incorrect indentation for \`test\` beginning at L1:C0. Expected \`test\` to be at an indentation of 2 but was found at 0.",
+ "isFixable": true,
+ "line": 2,
+ "message": "Incorrect indentation for \`test\` beginning at L2:C0. Expected \`test\` to be at an indentation of 2 but was found at 0.",
"rule": "block-indentation",
"severity": 2,
"source": "