From 362713c04aa89092b2b98a77fa94a75b3c933fc6 Mon Sep 17 00:00:00 2001 From: Milos Djermanovic Date: Sat, 28 Mar 2020 01:19:11 +0100 Subject: [PATCH] Update: Improve report location for template-curly-spacing (#12813) --- lib/rules/template-curly-spacing.js | 101 ++++++---- tests/lib/rules/template-curly-spacing.js | 232 +++++++++++++++------- 2 files changed, 216 insertions(+), 117 deletions(-) diff --git a/lib/rules/template-curly-spacing.js b/lib/rules/template-curly-spacing.js index a16c0732df7..26043bc9122 100644 --- a/lib/rules/template-curly-spacing.js +++ b/lib/rules/template-curly-spacing.js @@ -11,13 +11,6 @@ const astUtils = require("./utils/ast-utils"); -//------------------------------------------------------------------------------ -// Helpers -//------------------------------------------------------------------------------ - -const OPEN_PAREN = /\$\{$/u; -const CLOSE_PAREN = /^\}/u; - //------------------------------------------------------------------------------ // Rule Definition //------------------------------------------------------------------------------ @@ -49,7 +42,6 @@ module.exports = { create(context) { const sourceCode = context.getSourceCode(); const always = context.options[0] === "always"; - const prefix = always ? "expected" : "unexpected"; /** * Checks spacing before `}` of a given token. @@ -57,25 +49,39 @@ module.exports = { * @returns {void} */ function checkSpacingBefore(token) { - const prevToken = sourceCode.getTokenBefore(token, { includeComments: true }); + if (!token.value.startsWith("}")) { + return; // starts with a backtick, this is the first template element in the template literal + } + + const prevToken = sourceCode.getTokenBefore(token, { includeComments: true }), + hasSpace = sourceCode.isSpaceBetween(prevToken, token); + + if (!astUtils.isTokenOnSameLine(prevToken, token)) { + return; + } - if (prevToken && - CLOSE_PAREN.test(token.value) && - astUtils.isTokenOnSameLine(prevToken, token) && - sourceCode.isSpaceBetweenTokens(prevToken, token) !== always - ) { + if (always && !hasSpace) { context.report({ - loc: token.loc.start, - messageId: `${prefix}Before`, - fix(fixer) { - if (always) { - return fixer.insertTextBefore(token, " "); + loc: { + start: token.loc.start, + end: { + line: token.loc.start.line, + column: token.loc.start.column + 1 } - return fixer.removeRange([ - prevToken.range[1], - token.range[0] - ]); - } + }, + messageId: "expectedBefore", + fix: fixer => fixer.insertTextBefore(token, " ") + }); + } + + if (!always && hasSpace) { + context.report({ + loc: { + start: prevToken.loc.end, + end: token.loc.start + }, + messageId: "unexpectedBefore", + fix: fixer => fixer.removeRange([prevToken.range[1], token.range[0]]) }); } } @@ -86,28 +92,39 @@ module.exports = { * @returns {void} */ function checkSpacingAfter(token) { - const nextToken = sourceCode.getTokenAfter(token, { includeComments: true }); + if (!token.value.endsWith("${")) { + return; // ends with a backtick, this is the last template element in the template literal + } + + const nextToken = sourceCode.getTokenAfter(token, { includeComments: true }), + hasSpace = sourceCode.isSpaceBetween(token, nextToken); - if (nextToken && - OPEN_PAREN.test(token.value) && - astUtils.isTokenOnSameLine(token, nextToken) && - sourceCode.isSpaceBetweenTokens(token, nextToken) !== always - ) { + if (!astUtils.isTokenOnSameLine(token, nextToken)) { + return; + } + + if (always && !hasSpace) { context.report({ loc: { - line: token.loc.end.line, - column: token.loc.end.column - 2 + start: { + line: token.loc.end.line, + column: token.loc.end.column - 2 + }, + end: token.loc.end }, - messageId: `${prefix}After`, - fix(fixer) { - if (always) { - return fixer.insertTextAfter(token, " "); - } - return fixer.removeRange([ - token.range[1], - nextToken.range[0] - ]); - } + messageId: "expectedAfter", + fix: fixer => fixer.insertTextAfter(token, " ") + }); + } + + if (!always && hasSpace) { + context.report({ + loc: { + start: token.loc.end, + end: nextToken.loc.start + }, + messageId: "unexpectedAfter", + fix: fixer => fixer.removeRange([token.range[1], nextToken.range[0]]) }); } } diff --git a/tests/lib/rules/template-curly-spacing.js b/tests/lib/rules/template-curly-spacing.js index 85fa870566d..ac672a5ec8c 100644 --- a/tests/lib/rules/template-curly-spacing.js +++ b/tests/lib/rules/template-curly-spacing.js @@ -57,10 +57,10 @@ ruleTester.run("template-curly-spacing", rule, { code: "`${ foo } ${ bar }`", output: "`${foo} ${bar}`", errors: [ - { messageId: "unexpectedAfter", column: 2 }, - { messageId: "unexpectedBefore", column: 9 }, - { messageId: "unexpectedAfter", column: 11 }, - { messageId: "unexpectedBefore", column: 18 } + { messageId: "unexpectedAfter", line: 1, column: 4, endLine: 1, endColumn: 5 }, + { messageId: "unexpectedBefore", line: 1, column: 8, endLine: 1, endColumn: 9 }, + { messageId: "unexpectedAfter", line: 1, column: 13, endLine: 1, endColumn: 14 }, + { messageId: "unexpectedBefore", line: 1, column: 17, endLine: 1, endColumn: 18 } ] }, { @@ -68,10 +68,52 @@ ruleTester.run("template-curly-spacing", rule, { output: "`${foo} ${bar}`", options: ["never"], errors: [ - { messageId: "unexpectedAfter", column: 2 }, - { messageId: "unexpectedBefore", column: 9 }, - { messageId: "unexpectedAfter", column: 11 }, - { messageId: "unexpectedBefore", column: 18 } + { messageId: "unexpectedAfter", line: 1, column: 4, endLine: 1, endColumn: 5 }, + { messageId: "unexpectedBefore", line: 1, column: 8, endLine: 1, endColumn: 9 }, + { messageId: "unexpectedAfter", line: 1, column: 13, endLine: 1, endColumn: 14 }, + { messageId: "unexpectedBefore", line: 1, column: 17, endLine: 1, endColumn: 18 } + ] + }, + { + code: "` ${ foo }${ bar }` ", + output: "` ${foo}${bar}` ", + options: ["never"], + errors: [ + { messageId: "unexpectedAfter", line: 1, column: 5, endLine: 1, endColumn: 6 }, + { messageId: "unexpectedBefore", line: 1, column: 9, endLine: 1, endColumn: 10 }, + { messageId: "unexpectedAfter", line: 1, column: 13, endLine: 1, endColumn: 14 }, + { messageId: "unexpectedBefore", line: 1, column: 17, endLine: 1, endColumn: 18 } + ] + }, + { + code: "`${ foo } ${ bar }`", + output: "`${foo} ${bar}`", + options: ["never"], + errors: [ + { messageId: "unexpectedAfter", line: 1, column: 4, endLine: 1, endColumn: 6 }, + { messageId: "unexpectedBefore", line: 1, column: 9, endLine: 1, endColumn: 10 }, + { messageId: "unexpectedAfter", line: 1, column: 14, endLine: 1, endColumn: 15 }, + { messageId: "unexpectedBefore", line: 1, column: 18, endLine: 1, endColumn: 20 } + ] + }, + { + code: "`${foo }${ bar}`", + output: "`${foo}${bar}`", + options: ["never"], + errors: [ + { messageId: "unexpectedBefore", line: 1, column: 7, endLine: 1, endColumn: 10 }, + { messageId: "unexpectedAfter", line: 1, column: 13, endLine: 1, endColumn: 16 } + ] + }, + { + code: "`${ foo \t}${\t\tbar }`", + output: "`${foo}${bar}`", + options: ["never"], + errors: [ + { messageId: "unexpectedAfter", line: 1, column: 4, endLine: 1, endColumn: 7 }, + { messageId: "unexpectedBefore", line: 1, column: 10, endLine: 1, endColumn: 12 }, + { messageId: "unexpectedAfter", line: 1, column: 15, endLine: 1, endColumn: 17 }, + { messageId: "unexpectedBefore", line: 1, column: 20, endLine: 1, endColumn: 23 } ] }, { @@ -79,20 +121,42 @@ ruleTester.run("template-curly-spacing", rule, { output: "`${ foo } ${ bar }`", options: ["always"], errors: [ - { messageId: "expectedAfter", column: 2 }, - { messageId: "expectedBefore", column: 7 }, - { messageId: "expectedAfter", column: 9 }, - { messageId: "expectedBefore", column: 14 } + { messageId: "expectedAfter", line: 1, column: 2, endLine: 1, endColumn: 4 }, + { messageId: "expectedBefore", line: 1, column: 7, endLine: 1, endColumn: 8 }, + { messageId: "expectedAfter", line: 1, column: 9, endLine: 1, endColumn: 11 }, + { messageId: "expectedBefore", line: 1, column: 14, endLine: 1, endColumn: 15 } + ] + }, + { + code: "`${foo}${bar}`", + output: "`${ foo }${ bar }`", + options: ["always"], + errors: [ + { messageId: "expectedAfter", line: 1, column: 2, endLine: 1, endColumn: 4 }, + { messageId: "expectedBefore", line: 1, column: 7, endLine: 1, endColumn: 8 }, + { messageId: "expectedAfter", line: 1, column: 8, endLine: 1, endColumn: 10 }, + { messageId: "expectedBefore", line: 1, column: 13, endLine: 1, endColumn: 14 } + ] + }, + { + code: "`a${foo}b${bar}c`", + output: "`a${ foo }b${ bar }c`", + options: ["always"], + errors: [ + { messageId: "expectedAfter", line: 1, column: 3, endLine: 1, endColumn: 5 }, + { messageId: "expectedBefore", line: 1, column: 8, endLine: 1, endColumn: 9 }, + { messageId: "expectedAfter", line: 1, column: 10, endLine: 1, endColumn: 12 }, + { messageId: "expectedBefore", line: 1, column: 15, endLine: 1, endColumn: 16 } ] }, { code: "tag`${ foo } ${ bar }`", output: "tag`${foo} ${bar}`", errors: [ - { messageId: "unexpectedAfter", column: 5 }, - { messageId: "unexpectedBefore", column: 12 }, - { messageId: "unexpectedAfter", column: 14 }, - { messageId: "unexpectedBefore", column: 21 } + { messageId: "unexpectedAfter", line: 1, column: 7, endLine: 1, endColumn: 8 }, + { messageId: "unexpectedBefore", line: 1, column: 11, endLine: 1, endColumn: 12 }, + { messageId: "unexpectedAfter", line: 1, column: 16, endLine: 1, endColumn: 17 }, + { messageId: "unexpectedBefore", line: 1, column: 20, endLine: 1, endColumn: 21 } ] }, { @@ -100,10 +164,10 @@ ruleTester.run("template-curly-spacing", rule, { output: "tag`${foo} ${bar}`", options: ["never"], errors: [ - { messageId: "unexpectedAfter", column: 5 }, - { messageId: "unexpectedBefore", column: 12 }, - { messageId: "unexpectedAfter", column: 14 }, - { messageId: "unexpectedBefore", column: 21 } + { messageId: "unexpectedAfter", line: 1, column: 7, endLine: 1, endColumn: 8 }, + { messageId: "unexpectedBefore", line: 1, column: 11, endLine: 1, endColumn: 12 }, + { messageId: "unexpectedAfter", line: 1, column: 16, endLine: 1, endColumn: 17 }, + { messageId: "unexpectedBefore", line: 1, column: 20, endLine: 1, endColumn: 21 } ] }, { @@ -111,30 +175,30 @@ ruleTester.run("template-curly-spacing", rule, { output: "tag`${ foo } ${ bar }`", options: ["always"], errors: [ - { messageId: "expectedAfter", column: 5 }, - { messageId: "expectedBefore", column: 10 }, - { messageId: "expectedAfter", column: 12 }, - { messageId: "expectedBefore", column: 17 } + { messageId: "expectedAfter", line: 1, column: 5, endLine: 1, endColumn: 7 }, + { messageId: "expectedBefore", line: 1, column: 10, endLine: 1, endColumn: 11 }, + { messageId: "expectedAfter", line: 1, column: 12, endLine: 1, endColumn: 14 }, + { messageId: "expectedBefore", line: 1, column: 17, endLine: 1, endColumn: 18 } ] }, { code: "`${ /* */foo } ${ bar/* */ }`", output: "`${/* */foo} ${bar/* */}`", errors: [ - { messageId: "unexpectedAfter", column: 2 }, - { messageId: "unexpectedBefore", column: 15 }, - { messageId: "unexpectedAfter", column: 17 }, - { messageId: "unexpectedBefore", column: 30 } + { messageId: "unexpectedAfter", line: 1, column: 4, endLine: 1, endColumn: 5 }, + { messageId: "unexpectedBefore", line: 1, column: 14, endLine: 1, endColumn: 15 }, + { messageId: "unexpectedAfter", line: 1, column: 19, endLine: 1, endColumn: 20 }, + { messageId: "unexpectedBefore", line: 1, column: 29, endLine: 1, endColumn: 30 } ] }, { code: "`${ /*\n */foo } ${ bar/* \n*/ }`", output: "`${/*\n */foo} ${bar/* \n*/}`", errors: [ - { messageId: "unexpectedAfter", line: 1, column: 2 }, - { messageId: "unexpectedBefore", line: 2, column: 9 }, - { messageId: "unexpectedAfter", line: 2, column: 11 }, - { messageId: "unexpectedBefore", line: 3, column: 4 } + { messageId: "unexpectedAfter", line: 1, column: 4, endLine: 1, endColumn: 5 }, + { messageId: "unexpectedBefore", line: 2, column: 8, endLine: 2, endColumn: 9 }, + { messageId: "unexpectedAfter", line: 2, column: 13, endLine: 2, endColumn: 14 }, + { messageId: "unexpectedBefore", line: 3, column: 3, endLine: 3, endColumn: 4 } ] }, { @@ -142,10 +206,10 @@ ruleTester.run("template-curly-spacing", rule, { output: "`${/* */ foo} ${bar /* */}`", options: ["never"], errors: [ - { messageId: "unexpectedAfter", column: 2 }, - { messageId: "unexpectedBefore", column: 16 }, - { messageId: "unexpectedAfter", column: 18 }, - { messageId: "unexpectedBefore", column: 32 } + { messageId: "unexpectedAfter", line: 1, column: 4, endLine: 1, endColumn: 5 }, + { messageId: "unexpectedBefore", line: 1, column: 15, endLine: 1, endColumn: 16 }, + { messageId: "unexpectedAfter", line: 1, column: 20, endLine: 1, endColumn: 21 }, + { messageId: "unexpectedBefore", line: 1, column: 31, endLine: 1, endColumn: 32 } ] }, { @@ -153,10 +217,10 @@ ruleTester.run("template-curly-spacing", rule, { output: "`${/*\n */ foo} ${bar /* \n*/}`", options: ["never"], errors: [ - { messageId: "unexpectedAfter", line: 1, column: 2 }, - { messageId: "unexpectedBefore", line: 2, column: 10 }, - { messageId: "unexpectedAfter", line: 2, column: 12 }, - { messageId: "unexpectedBefore", line: 3, column: 4 } + { messageId: "unexpectedAfter", line: 1, column: 4, endLine: 1, endColumn: 5 }, + { messageId: "unexpectedBefore", line: 2, column: 9, endLine: 2, endColumn: 10 }, + { messageId: "unexpectedAfter", line: 2, column: 14, endLine: 2, endColumn: 15 }, + { messageId: "unexpectedBefore", line: 3, column: 3, endLine: 3, endColumn: 4 } ] }, { @@ -164,10 +228,10 @@ ruleTester.run("template-curly-spacing", rule, { output: "`${ /* */foo } ${ bar/* */ }`", options: ["always"], errors: [ - { messageId: "expectedAfter", column: 2 }, - { messageId: "expectedBefore", column: 13 }, - { messageId: "expectedAfter", column: 15 }, - { messageId: "expectedBefore", column: 26 } + { messageId: "expectedAfter", line: 1, column: 2, endLine: 1, endColumn: 4 }, + { messageId: "expectedBefore", line: 1, column: 13, endLine: 1, endColumn: 14 }, + { messageId: "expectedAfter", line: 1, column: 15, endLine: 1, endColumn: 17 }, + { messageId: "expectedBefore", line: 1, column: 26, endLine: 1, endColumn: 27 } ] }, { @@ -175,30 +239,30 @@ ruleTester.run("template-curly-spacing", rule, { output: "`${ /*\n */foo } ${ bar/* \n*/ }`", options: ["always"], errors: [ - { messageId: "expectedAfter", line: 1, column: 2 }, - { messageId: "expectedBefore", line: 2, column: 8 }, - { messageId: "expectedAfter", line: 2, column: 10 }, - { messageId: "expectedBefore", line: 3, column: 3 } + { messageId: "expectedAfter", line: 1, column: 2, endLine: 1, endColumn: 4 }, + { messageId: "expectedBefore", line: 2, column: 8, endLine: 2, endColumn: 9 }, + { messageId: "expectedAfter", line: 2, column: 10, endLine: 2, endColumn: 12 }, + { messageId: "expectedBefore", line: 3, column: 3, endLine: 3, endColumn: 4 } ] }, { code: "tag`${ /* */foo } ${ bar/* */ }`", output: "tag`${/* */foo} ${bar/* */}`", errors: [ - { messageId: "unexpectedAfter", column: 5 }, - { messageId: "unexpectedBefore", column: 18 }, - { messageId: "unexpectedAfter", column: 20 }, - { messageId: "unexpectedBefore", column: 33 } + { messageId: "unexpectedAfter", line: 1, column: 7, endLine: 1, endColumn: 8 }, + { messageId: "unexpectedBefore", line: 1, column: 17, endLine: 1, endColumn: 18 }, + { messageId: "unexpectedAfter", line: 1, column: 22, endLine: 1, endColumn: 23 }, + { messageId: "unexpectedBefore", line: 1, column: 32, endLine: 1, endColumn: 33 } ] }, { code: "tag`${ /*\n */foo } ${ bar/* \n*/ }`", output: "tag`${/*\n */foo} ${bar/* \n*/}`", errors: [ - { messageId: "unexpectedAfter", line: 1, column: 5 }, - { messageId: "unexpectedBefore", line: 2, column: 9 }, - { messageId: "unexpectedAfter", line: 2, column: 11 }, - { messageId: "unexpectedBefore", line: 3, column: 4 } + { messageId: "unexpectedAfter", line: 1, column: 7, endLine: 1, endColumn: 8 }, + { messageId: "unexpectedBefore", line: 2, column: 8, endLine: 2, endColumn: 9 }, + { messageId: "unexpectedAfter", line: 2, column: 13, endLine: 2, endColumn: 14 }, + { messageId: "unexpectedBefore", line: 3, column: 3, endLine: 3, endColumn: 4 } ] }, { @@ -206,10 +270,10 @@ ruleTester.run("template-curly-spacing", rule, { output: "tag`${/* */foo} ${bar/* */}`", options: ["never"], errors: [ - { messageId: "unexpectedAfter", column: 5 }, - { messageId: "unexpectedBefore", column: 18 }, - { messageId: "unexpectedAfter", column: 20 }, - { messageId: "unexpectedBefore", column: 33 } + { messageId: "unexpectedAfter", line: 1, column: 7, endLine: 1, endColumn: 8 }, + { messageId: "unexpectedBefore", line: 1, column: 17, endLine: 1, endColumn: 18 }, + { messageId: "unexpectedAfter", line: 1, column: 22, endLine: 1, endColumn: 23 }, + { messageId: "unexpectedBefore", line: 1, column: 32, endLine: 1, endColumn: 33 } ] }, { @@ -217,10 +281,10 @@ ruleTester.run("template-curly-spacing", rule, { output: "tag`${/*\n */foo} ${bar/* \n*/}`", options: ["never"], errors: [ - { messageId: "unexpectedAfter", line: 1, column: 5 }, - { messageId: "unexpectedBefore", line: 2, column: 9 }, - { messageId: "unexpectedAfter", line: 2, column: 11 }, - { messageId: "unexpectedBefore", line: 3, column: 4 } + { messageId: "unexpectedAfter", line: 1, column: 7, endLine: 1, endColumn: 8 }, + { messageId: "unexpectedBefore", line: 2, column: 8, endLine: 2, endColumn: 9 }, + { messageId: "unexpectedAfter", line: 2, column: 13, endLine: 2, endColumn: 14 }, + { messageId: "unexpectedBefore", line: 3, column: 3, endLine: 3, endColumn: 4 } ] }, { @@ -228,10 +292,10 @@ ruleTester.run("template-curly-spacing", rule, { output: "tag`${ /* */foo } ${ bar/* */ }`", options: ["always"], errors: [ - { messageId: "expectedAfter", column: 5 }, - { messageId: "expectedBefore", column: 16 }, - { messageId: "expectedAfter", column: 18 }, - { messageId: "expectedBefore", column: 29 } + { messageId: "expectedAfter", line: 1, column: 5, endLine: 1, endColumn: 7 }, + { messageId: "expectedBefore", line: 1, column: 16, endLine: 1, endColumn: 17 }, + { messageId: "expectedAfter", line: 1, column: 18, endLine: 1, endColumn: 20 }, + { messageId: "expectedBefore", line: 1, column: 29, endLine: 1, endColumn: 30 } ] }, { @@ -239,17 +303,17 @@ ruleTester.run("template-curly-spacing", rule, { output: "tag`${ /*\n */foo } ${ bar/* \n*/ }`", options: ["always"], errors: [ - { messageId: "expectedAfter", line: 1, column: 5 }, - { messageId: "expectedBefore", line: 2, column: 8 }, - { messageId: "expectedAfter", line: 2, column: 10 }, - { messageId: "expectedBefore", line: 3, column: 3 } + { messageId: "expectedAfter", line: 1, column: 5, endLine: 1, endColumn: 7 }, + { messageId: "expectedBefore", line: 2, column: 8, endLine: 2, endColumn: 9 }, + { messageId: "expectedAfter", line: 2, column: 10, endLine: 2, endColumn: 12 }, + { messageId: "expectedBefore", line: 3, column: 3, endLine: 3, endColumn: 4 } ] }, { code: "`${ // comment\n foo} ${bar // comment \n}`", output: "`${// comment\n foo} ${bar // comment \n}`", errors: [ - { messageId: "unexpectedAfter", line: 1, column: 2 } + { messageId: "unexpectedAfter", line: 1, column: 4, endLine: 1, endColumn: 5 } ] }, { @@ -257,7 +321,7 @@ ruleTester.run("template-curly-spacing", rule, { output: "`${// comment\n foo} ${bar // comment \n}`", options: ["never"], errors: [ - { messageId: "unexpectedAfter", line: 1, column: 2 } + { messageId: "unexpectedAfter", line: 1, column: 4, endLine: 1, endColumn: 5 } ] }, { @@ -265,7 +329,25 @@ ruleTester.run("template-curly-spacing", rule, { output: "`${ // comment\n foo } ${ bar // comment \n}`", options: ["always"], errors: [ - { messageId: "expectedAfter", line: 1, column: 2 } + { messageId: "expectedAfter", line: 1, column: 2, endLine: 1, endColumn: 4 } + ] + }, + { + code: "`\n${ foo }\n`", + output: "`\n${foo}\n`", + options: ["never"], + errors: [ + { messageId: "unexpectedAfter", line: 2, column: 3, endLine: 2, endColumn: 4 }, + { messageId: "unexpectedBefore", line: 2, column: 7, endLine: 2, endColumn: 8 } + ] + }, + { + code: "`\n${foo}\n`", + output: "`\n${ foo }\n`", + options: ["always"], + errors: [ + { messageId: "expectedAfter", line: 2, column: 1, endLine: 2, endColumn: 3 }, + { messageId: "expectedBefore", line: 2, column: 6, endLine: 2, endColumn: 7 } ] } ]