From 190dd150ee6e1da4be16a9efbbb3e42495837d13 Mon Sep 17 00:00:00 2001 From: YeonJuan Date: Fri, 21 Nov 2025 18:20:26 +0900 Subject: [PATCH 1/8] feat: implement autoBaseIndent --- .../eslint-plugin/lib/rules/indent/indent.js | 55 ++++++++++++++++-- .../eslint-plugin/tests/rules/indent.test.js | 58 +++++++++++++++++++ 2 files changed, 108 insertions(+), 5 deletions(-) diff --git a/packages/eslint-plugin/lib/rules/indent/indent.js b/packages/eslint-plugin/lib/rules/indent/indent.js index 0f8ff3d5..8b9fcb71 100644 --- a/packages/eslint-plugin/lib/rules/indent/indent.js +++ b/packages/eslint-plugin/lib/rules/indent/indent.js @@ -19,6 +19,7 @@ * @property {number} [Option2.Attribute] * @property {Record} [Option2.tagChildrenIndent] * @property {boolean} [Option2.ignoreComment] + * @property {"first"} [Option2.templateIndentBase] */ const { parseTemplateLiteral } = require("../utils/template-literal"); @@ -102,6 +103,10 @@ module.exports = { type: "boolean", default: false, }, + templateIndentBase: { + type: "string", + enum: ["first"], + }, }, additionalProperties: false, }, @@ -116,6 +121,7 @@ module.exports = { const indentLevelOptions = (context.options && context.options[1]) || {}; const lines = sourceCode.getLines(); const ignoreComment = indentLevelOptions.ignoreComment === true; + const autoBaseIndent = indentLevelOptions.templateIndentBase === "first"; const { indentType, indentSize, indentChar } = getIndentOptionInfo(context); /** @@ -161,12 +167,35 @@ module.exports = { return 1; } + /** + * @param {TemplateLiteral} node + * @returns {number} + */ + function getAutoBaseSpaces(node) { + if (!autoBaseIndent) { + return 0; + } + const startLineIndex = node.loc.start.line; + const endLineIndex = node.loc.end.line - 1; + const templLines = lines.slice(startLineIndex, endLineIndex); + for (let i = 0; i < templLines.length; i++) { + const line = templLines[i]; + if (line.trim()) { + return countLeftPadding(line); + } + } + return 0; + } + /** * * @param {TemplateLiteral} node * @returns {number} */ function getTemplateLiteralBaseIndentLevel(node) { + if (autoBaseIndent) { + return 0; + } // @ts-ignore const lineIndex = node.loc.start.line - 1; const line = lines[lineIndex]; @@ -181,8 +210,9 @@ module.exports = { /** * @param {number} baseLevel + * @param {number} baseSpaces */ - function createIndentVisitor(baseLevel) { + function createIndentVisitor(baseLevel, baseSpaces) { const indentLevel = new IndentLevel({ getIncreasingLevel, }); @@ -210,7 +240,14 @@ module.exports = { * @returns {string} */ function getExpectedIndent() { - return indentChar.repeat(indentLevel.value()); + let base = ""; + if (indentType === "space") { + base = " ".repeat(baseSpaces); + } else { + base = indentChar.repeat(baseSpaces); + } + + return base + indentChar.repeat(indentLevel.value()); } /** @@ -254,6 +291,10 @@ module.exports = { } const actualIndent = getActualIndent(node); const expectedIndent = getExpectedIndent(); + console.log( + "expected indent", + `"${expectedIndent}" ${expectedIndent.length}` + ); if (actualIndent.trim().length) { return; @@ -396,24 +437,28 @@ module.exports = { } return { - ...createIndentVisitor(0), + ...createIndentVisitor(0, 0), TaggedTemplateExpression(node) { if (shouldCheckTaggedTemplateExpression(node, context)) { const base = getTemplateLiteralBaseIndentLevel(node.quasi); + const baseSpaces = getAutoBaseSpaces(node.quasi); + console.log("baseSpaces", baseSpaces); parseTemplateLiteral( node.quasi, getSourceCode(context), - createIndentVisitor(base) + createIndentVisitor(base, baseSpaces) ); } }, TemplateLiteral(node) { if (shouldCheckTemplateLiteral(node, context)) { const base = getTemplateLiteralBaseIndentLevel(node); + const baseSpaces = getAutoBaseSpaces(node); + console.log("baseSpaces", baseSpaces); parseTemplateLiteral( node, getSourceCode(context), - createIndentVisitor(base) + createIndentVisitor(base, baseSpaces) ); } }, diff --git a/packages/eslint-plugin/tests/rules/indent.test.js b/packages/eslint-plugin/tests/rules/indent.test.js index ce8f6fab..1a874c3a 100644 --- a/packages/eslint-plugin/tests/rules/indent.test.js +++ b/packages/eslint-plugin/tests/rules/indent.test.js @@ -1566,6 +1566,30 @@ const code = html\` }, ], }, + { + code: `html\` + test + \``, + options: [ + 2, + { + templateIndentBase: "first", + }, + ], + }, + { + code: `html\` +
+ test +
+ \``, + options: [ + 2, + { + templateIndentBase: "first", + }, + ], + }, ], invalid: [ { @@ -1896,5 +1920,39 @@ class Component extends LitElement { options: ["tab", { Attribute: 2, tagChildrenIndent: { span: 2 } }], errors: wrongIndentErrors(1), }, + { + code: ` +const code = html\` +
+ id="\${bar}"> +
\`; + `, + output: ` +const code = html\` +
+ id="\${bar}"> +
\`; + `, + only: true, + options: [4, { templateIndentBase: "first" }], + errors: wrongIndentErrors(1), + }, + { + code: ` +const code = html\` +
+
\`; + `, + output: ` +const code = html\` +
+
\`; + `, + only: true, + options: [4, { templateIndentBase: "first" }], + errors: wrongIndentErrors(1), + }, ], }); From bc43336d1864247adb9fea633256e29230a64b67 Mon Sep 17 00:00:00 2001 From: YeonJuan Date: Fri, 21 Nov 2025 22:40:02 +0900 Subject: [PATCH 2/8] Update indent.test.js --- .../eslint-plugin/tests/rules/indent.test.js | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/packages/eslint-plugin/tests/rules/indent.test.js b/packages/eslint-plugin/tests/rules/indent.test.js index 1a874c3a..f2b914cf 100644 --- a/packages/eslint-plugin/tests/rules/indent.test.js +++ b/packages/eslint-plugin/tests/rules/indent.test.js @@ -1470,6 +1470,23 @@ comment ], errors: wrongIndentErrors(2), }, + { + code: ` +
+
+ `, + output: ` +
+
+ `, + options: [ + 2, + { + templateIndentBase: "first", + }, + ], + errors: wrongIndentErrors(1), + }, ], }; } @@ -1590,6 +1607,18 @@ const code = html\` }, ], }, + { + code: `html\`
+
+
a
+ \``, + options: [ + 2, + { + templateIndentBase: "first", + }, + ], + }, ], invalid: [ { From 4274ab05caec13d2089f04919f4d3c7653ab6453 Mon Sep 17 00:00:00 2001 From: YeonJuan Date: Fri, 21 Nov 2025 23:03:09 +0900 Subject: [PATCH 3/8] update --- docs/rules/indent.md | 4 ++++ packages/eslint-plugin/lib/rules/indent/indent.js | 8 ++------ 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/rules/indent.md b/docs/rules/indent.md index 7e9bde25..8c35129a 100644 --- a/docs/rules/indent.md +++ b/docs/rules/indent.md @@ -131,3 +131,7 @@ This rule has an object option: - `tagChildrenIndent` (default: `{}`): Specifies the indent increment of the child tags of the specified tag. e.g. For example, `"tagChildrenIndent": { "html": 0 }` will set the `` tag children to 0 indent (2 x 0). - `ignoreComment` (default: `false`): When set to `true`, the indentation of HTML comments (including opening ``, and content) will not be checked. This is useful when you want to allow free-form indentation for comments. + +- `templateIndentBase` (default: `"templateTag"`): Controls the indentation base for HTML in template literals. + - `"templateTag"`: Uses the indentation of the template tag (e.g., `html\`...\``) as the base indentation. + - `"first"`: Uses the first element of the template literal as the base indentation (elements on the same line as the template tag are ignored). diff --git a/packages/eslint-plugin/lib/rules/indent/indent.js b/packages/eslint-plugin/lib/rules/indent/indent.js index 8b9fcb71..0d8a6f17 100644 --- a/packages/eslint-plugin/lib/rules/indent/indent.js +++ b/packages/eslint-plugin/lib/rules/indent/indent.js @@ -105,7 +105,8 @@ module.exports = { }, templateIndentBase: { type: "string", - enum: ["first"], + enum: ["first", "templateTag"], + default: "templateTag", }, }, additionalProperties: false, @@ -291,10 +292,6 @@ module.exports = { } const actualIndent = getActualIndent(node); const expectedIndent = getExpectedIndent(); - console.log( - "expected indent", - `"${expectedIndent}" ${expectedIndent.length}` - ); if (actualIndent.trim().length) { return; @@ -454,7 +451,6 @@ module.exports = { if (shouldCheckTemplateLiteral(node, context)) { const base = getTemplateLiteralBaseIndentLevel(node); const baseSpaces = getAutoBaseSpaces(node); - console.log("baseSpaces", baseSpaces); parseTemplateLiteral( node, getSourceCode(context), From f0a4a94883e8d1a2ee8d807672dacf539a711a60 Mon Sep 17 00:00:00 2001 From: YeonJuan Date: Fri, 21 Nov 2025 23:03:29 +0900 Subject: [PATCH 4/8] Update indent.js --- packages/eslint-plugin/lib/rules/indent/indent.js | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/eslint-plugin/lib/rules/indent/indent.js b/packages/eslint-plugin/lib/rules/indent/indent.js index 0d8a6f17..44db351e 100644 --- a/packages/eslint-plugin/lib/rules/indent/indent.js +++ b/packages/eslint-plugin/lib/rules/indent/indent.js @@ -439,7 +439,6 @@ module.exports = { if (shouldCheckTaggedTemplateExpression(node, context)) { const base = getTemplateLiteralBaseIndentLevel(node.quasi); const baseSpaces = getAutoBaseSpaces(node.quasi); - console.log("baseSpaces", baseSpaces); parseTemplateLiteral( node.quasi, getSourceCode(context), From 51ad22b32f780d685e412f6dfd9eb1cb5ae05606 Mon Sep 17 00:00:00 2001 From: YeonJuan Date: Fri, 21 Nov 2025 23:04:07 +0900 Subject: [PATCH 5/8] Update indent.test.js --- packages/eslint-plugin/tests/rules/indent.test.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/eslint-plugin/tests/rules/indent.test.js b/packages/eslint-plugin/tests/rules/indent.test.js index f2b914cf..9008fd9d 100644 --- a/packages/eslint-plugin/tests/rules/indent.test.js +++ b/packages/eslint-plugin/tests/rules/indent.test.js @@ -1962,7 +1962,6 @@ const code = html\` id="\${bar}"> \`; `, - only: true, options: [4, { templateIndentBase: "first" }], errors: wrongIndentErrors(1), }, @@ -1979,7 +1978,6 @@ const code = html\` id="\${bar}"> \`; `, - only: true, options: [4, { templateIndentBase: "first" }], errors: wrongIndentErrors(1), }, From deeac0e6907cd9c34448314e624eb279e80bb08d Mon Sep 17 00:00:00 2001 From: YeonJuan Date: Fri, 21 Nov 2025 23:07:32 +0900 Subject: [PATCH 6/8] Update indent.js --- packages/eslint-plugin/lib/rules/indent/indent.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/eslint-plugin/lib/rules/indent/indent.js b/packages/eslint-plugin/lib/rules/indent/indent.js index 44db351e..a23ec959 100644 --- a/packages/eslint-plugin/lib/rules/indent/indent.js +++ b/packages/eslint-plugin/lib/rules/indent/indent.js @@ -178,9 +178,9 @@ module.exports = { } const startLineIndex = node.loc.start.line; const endLineIndex = node.loc.end.line - 1; - const templLines = lines.slice(startLineIndex, endLineIndex); - for (let i = 0; i < templLines.length; i++) { - const line = templLines[i]; + const templateLines = lines.slice(startLineIndex, endLineIndex); + for (let i = 0; i < templateLines.length; i++) { + const line = templateLines[i]; if (line.trim()) { return countLeftPadding(line); } From e7ba6b4bbe973bd162d2a0430fdeebe63e669630 Mon Sep 17 00:00:00 2001 From: YeonJuan Date: Fri, 21 Nov 2025 23:36:58 +0900 Subject: [PATCH 7/8] fix --- packages/eslint-plugin/lib/rules/indent/indent.js | 2 +- packages/eslint-plugin/tests/rules/indent.test.js | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/packages/eslint-plugin/lib/rules/indent/indent.js b/packages/eslint-plugin/lib/rules/indent/indent.js index a23ec959..ba76801a 100644 --- a/packages/eslint-plugin/lib/rules/indent/indent.js +++ b/packages/eslint-plugin/lib/rules/indent/indent.js @@ -19,7 +19,7 @@ * @property {number} [Option2.Attribute] * @property {Record} [Option2.tagChildrenIndent] * @property {boolean} [Option2.ignoreComment] - * @property {"first"} [Option2.templateIndentBase] + * @property {"first" | "templateTag"} [Option2.templateIndentBase] */ const { parseTemplateLiteral } = require("../utils/template-literal"); diff --git a/packages/eslint-plugin/tests/rules/indent.test.js b/packages/eslint-plugin/tests/rules/indent.test.js index 9008fd9d..04385de5 100644 --- a/packages/eslint-plugin/tests/rules/indent.test.js +++ b/packages/eslint-plugin/tests/rules/indent.test.js @@ -1619,6 +1619,15 @@ const code = html\` }, ], }, + { + code: `html\`\`;`, + options: [ + 2, + { + templateIndentBase: "first", + }, + ], + }, ], invalid: [ { From 45b7d629b928fc711ac2c80d8c061d4054bb2a3d Mon Sep 17 00:00:00 2001 From: YeonJuan Date: Fri, 21 Nov 2025 23:52:34 +0900 Subject: [PATCH 8/8] add tests --- .../eslint-plugin/tests/rules/indent.test.js | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/packages/eslint-plugin/tests/rules/indent.test.js b/packages/eslint-plugin/tests/rules/indent.test.js index 04385de5..cdfb9abb 100644 --- a/packages/eslint-plugin/tests/rules/indent.test.js +++ b/packages/eslint-plugin/tests/rules/indent.test.js @@ -1990,5 +1990,23 @@ const code = html\` options: [4, { templateIndentBase: "first" }], errors: wrongIndentErrors(1), }, + { + code: ` +const code = html\` +\t\t\t
+\t\t\t
+\`; + `, + output: ` +const code = html\` +\t\t\t
+\t\t\t
+\t\t\t\`; + `, + options: ["tab", { templateIndentBase: "first" }], + errors: wrongIndentErrors(2), + }, ], });