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 @@

{{t "greeting"}}

-
+ ``` 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: '
\n

Stuff goes here

', + fixedTemplate: '
\n

Stuff goes here

\n
', 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: '' + '

\n' + '

Hi!

\n' + '
', + template: '
\n' + '

Hi!

\n' + '
', + fixedTemplate: '
\n' + '

Hi!

\n' + '
', 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": "