Repository: sveltejs/eslint-plugin-svelte (ts)

Issue:
[bug][svelte/indent] Invalid indentation for children and snippet closing tag
### Before You File a Bug Report Please Confirm You Have Done The Following...

- [X] I have tried restarting my IDE and the issue persists.
- [X] I have updated to the latest version of the packages.

### What version of ESLint are you using?

9.10.0

### What version of `eslint-plugin-svelte` are you using?

2.44.0

### What did you do?

Does not work with any configuration, you can try the following example in the [current docs](https://sveltejs.github.io/eslint-plugin-svelte/rules/indent/).

<details>
<summary>Code example</summary>

Copy the following into the rules docs

```svelte
<script>
  /* eslint svelte/indent: "error" */
  function click() {}
</script>

<!-- ✓ GOOD -->
<button
  type="button"
  on:click={click}
  class="my-button primally"
>
  CLICK ME!
</button>

<!-- ✗ BAD -->
<button
  type="button"
  on:click={click}
  class="my-button primally"
>
{#snippet example()}
<div>Some code</div>
{/snippet}
{#if true}
<div>Some other code</div>
{/if}
</button>
```
</details>

### What did you expect to happen?

To have the indent of the `{/snippet}` and it's content (basically the `div`), be properly indented, as the `if` clause:

<details>
<summary>Expected result</summary>

```svelte
<script>
  /* eslint svelte/indent: "error" */
  function click() {}
</script>

<!-- ✓ GOOD -->
<button
  type="button"
  on:click={click}
  class="my-button primally"
>
  CLICK ME!
</button>

<!-- ✗ BAD -->
<button
  type="button"
  on:click={click}
  class="my-button primally"
>
  {#snippet example()}
    <div>Some code</div>
  {/snippet}
  {#if true}
    <div>Some other code</div>
  {/if}
</button>
```
</details>

### What actually happened?

The `{/snippet}` indent is not correctly updated

<details>
<summary>Results</summary>

```svelte
<script>
  /* eslint svelte/indent: "error" */
  function click() {}
</script>

<!-- ✓ GOOD -->
<button
  type="button"
  on:click={click}
  class="my-button primally"
>
  CLICK ME!
</button>

<!-- ✗ BAD -->
<button
  type="button"
  on:click={click}
  class="my-button primally"
>
  {#snippet example()}
<div>Some code</div>
{/snippet}
  {#if true}
    <div>Some other code</div>
  {/if}
</button>
```
</details>

### Link to **GitHub Repo** with Minimal Reproducible Example

[ESLint playground example](https://eslint-online-playground.netlify.app/#eNq1U0tu2zAQvcqE2SSGJQUFAhSuE7R1iqJoAxfoMsxCkUYuHYoUSMqIIfgUXXTT0/UkHX6c2k6QXTa2OfPem88bD8yaqrhez3TbaYXK5XaF0iGbsKmtjOjcJVcAxQjQSqEcxHQhVE3gCXCGxmjDGYyKPWBWC1veSQSls171FutsVRqbYE2vKie0gkqK6v7kFIYNV9PisSI9jrIM/v75BZ/n8yvIMgpO73rnNOUA3LrDC85SgPmQVpMgdjGEL9IDUi+tJVy7ziIUOiPaUsq154TJZt++zL7C9acjXz+Cduv/ho8fXrP8cGyV6Dp0gA9l20k8OfWbqMXq8oduESpd47TwT66GImEJMRyLBpzpMRT6j9fuJ5oDlmjCctNwbMyiQ3mlVSMW+dKS2YLsNw6WFhqjW7L1fQQVS8vZO65SPrr/XfYLobbI5HcXgllEPMMh86mzxImxbEsNucDBh8CpsSl76eDGT7e0qVWbG6x02yIdXz32qTxP9xpb2uJuOGtk6YodOGe3gTH4DzpAIdFOSJ+NRsUoidAqOBtTe3uByAOQpVr05QLnnb9cIicpgNj/ZG/SRAr2bOhxS8PR6ruyuicNEtaK1h4kOPP3xJn/N7W67iX9DnTOalxdYecHUJVAauax6r5DnkoDo3WJ6gEx/2Ly0LVnobvJ8/wsP8vIJpe/OX/7BHTo6K5e2ARXG7b5B3bRhxo=)

### Additional comments

_No response_

Pull request description:
fix(svelte/indent): ensure proper snippet indent
Fix `svelte/indent` rule for `SvelteSnippetBlock`, by ensuring that the children and closing tags are properly indented.

Closes #863.

Patch:
diff --git a/.changeset/nervous-rings-applaud.md b/.changeset/nervous-rings-applaud.md
new file mode 100644
index 0000000..81db8c4
--- /dev/null
+++ b/.changeset/nervous-rings-applaud.md
@@ -0,0 +1,5 @@
+---
+"eslint-plugin-svelte": patch
+---
+
+fix(svelte/indent): ensure proper snippet indent
diff --git a/packages/eslint-plugin-svelte/src/rules/indent-helpers/svelte.ts b/packages/eslint-plugin-svelte/src/rules/indent-helpers/svelte.ts
index f84e74d..29f9a75 100644
--- a/packages/eslint-plugin-svelte/src/rules/indent-helpers/svelte.ts
+++ b/packages/eslint-plugin-svelte/src/rules/indent-helpers/svelte.ts
@@ -457,15 +457,12 @@ export function defineVisitor(context: IndentContext): NodeListener {
 				offsets.setOffsetToken(token, 1, openToken);
 			}
 
-			const [openCloseTagToken, endAwaitToken, closeCloseTagToken] = sourceCode.getLastTokens(
-				node,
-				{
-					count: 3,
-					includeComments: false
-				}
-			);
+			const [openCloseTagToken, endKeyToken, closeCloseTagToken] = sourceCode.getLastTokens(node, {
+				count: 3,
+				includeComments: false
+			});
 			offsets.setOffsetToken(openCloseTagToken, 0, openToken);
-			offsets.setOffsetToken(endAwaitToken, 1, openCloseTagToken);
+			offsets.setOffsetToken(endKeyToken, 1, openCloseTagToken);
 			offsets.setOffsetToken(closeCloseTagToken, 0, openCloseTagToken);
 		},
 		SvelteSnippetBlock(node: AST.SvelteSnippetBlock) {
@@ -474,8 +471,8 @@ export function defineVisitor(context: IndentContext): NodeListener {
 				includeComments: false
 			});
 			offsets.setOffsetToken(snippetToken, 1, openToken);
-			const id = getFirstAndLastTokens(sourceCode, node.id);
-			offsets.setOffsetToken(id.firstToken, 1, snippetToken);
+			const snippetName = sourceCode.getTokenAfter(snippetToken)!;
+			offsets.setOffsetToken(snippetName, 1, snippetToken);
 
 			const leftParenToken = sourceCode.getTokenBefore(
 				node.params[0] || sourceCode.getLastToken(node),
@@ -492,8 +489,27 @@ export function defineVisitor(context: IndentContext): NodeListener {
 					includeComments: false
 				}
 			)!;
-			offsets.setOffsetToken(leftParenToken, 1, id.firstToken);
+			offsets.setOffsetToken(leftParenToken, 1, snippetName);
 			offsets.setOffsetElementList(node.params, leftParenToken, rightParenToken, 1);
+
+			const closeOpenTagToken = sourceCode.getTokenAfter(rightParenToken)!;
+			offsets.setOffsetToken(closeOpenTagToken, 0, openToken);
+
+			for (const child of node.children) {
+				const token = sourceCode.getFirstToken(child, {
+					includeComments: false,
+					filter: isNotWhitespace
+				});
+				offsets.setOffsetToken(token, 1, openToken);
+			}
+
+			const [openCloseTagToken, endSnippetToken, closeCloseTagToken] = sourceCode.getLastTokens(
+				node,
+				{ count: 3, includeComments: false }
+			);
+			offsets.setOffsetToken(openCloseTagToken, 0, openToken);
+			offsets.setOffsetToken(endSnippetToken, 1, openCloseTagToken);
+			offsets.setOffsetToken(closeCloseTagToken, 0, openCloseTagToken);
 		},
 		// ----------------------------------------------------------------------
 		// COMMENTS


Test patch:
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/indent/invalid/snippets01-errors.yaml b/packages/eslint-plugin-svelte/tests/fixtures/rules/indent/invalid/snippets01-errors.yaml
index 0e010e0..ac83cef 100644
--- a/packages/eslint-plugin-svelte/tests/fixtures/rules/indent/invalid/snippets01-errors.yaml
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/indent/invalid/snippets01-errors.yaml
@@ -38,3 +38,63 @@
   line: 15
   column: 1
   suggestions: null
+- message: Expected indentation of 2 spaces but found 0 spaces.
+  line: 18
+  column: 1
+  suggestions: null
+- message: Expected indentation of 4 spaces but found 0 spaces.
+  line: 19
+  column: 1
+  suggestions: null
+- message: Expected indentation of 2 spaces but found 0 spaces.
+  line: 20
+  column: 1
+  suggestions: null
+- message: Expected indentation of 2 spaces but found 0 spaces.
+  line: 22
+  column: 1
+  suggestions: null
+- message: Expected indentation of 4 spaces but found 2 spaces.
+  line: 23
+  column: 1
+  suggestions: null
+- message: Expected indentation of 2 spaces but found 0 spaces.
+  line: 24
+  column: 1
+  suggestions: null
+- message: Expected indentation of 4 spaces but found 0 spaces.
+  line: 25
+  column: 1
+  suggestions: null
+- message: Expected indentation of 2 spaces but found 0 spaces.
+  line: 26
+  column: 1
+  suggestions: null
+- message: Expected indentation of 4 spaces but found 0 spaces.
+  line: 27
+  column: 1
+  suggestions: null
+- message: Expected indentation of 2 spaces but found 0 spaces.
+  line: 28
+  column: 1
+  suggestions: null
+- message: Expected indentation of 2 spaces but found 0 spaces.
+  line: 31
+  column: 1
+  suggestions: null
+- message: Expected indentation of 4 spaces but found 0 spaces.
+  line: 32
+  column: 1
+  suggestions: null
+- message: Expected indentation of 4 spaces but found 0 spaces.
+  line: 34
+  column: 1
+  suggestions: null
+- message: Expected indentation of 2 spaces but found 0 spaces.
+  line: 36
+  column: 1
+  suggestions: null
+- message: Expected indentation of 2 spaces but found 0 spaces.
+  line: 38
+  column: 1
+  suggestions: null
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/indent/invalid/snippets01-input.svelte b/packages/eslint-plugin-svelte/tests/fixtures/rules/indent/invalid/snippets01-input.svelte
index e86807d..6bec73e 100644
--- a/packages/eslint-plugin-svelte/tests/fixtures/rules/indent/invalid/snippets01-input.svelte
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/indent/invalid/snippets01-input.svelte
@@ -14,3 +14,26 @@ a
 }
 )
 }
+<div>
+{#snippet example()}
+<div></div>
+{/snippet}
+
+{
+  #snippet example_2()
+}
+<div></div>
+{
+/snippet
+}
+</div>
+{
+#snippet example_3
+(
+
+)
+}
+<div></div>
+{
+/snippet
+}
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/indent/invalid/snippets01-output.svelte b/packages/eslint-plugin-svelte/tests/fixtures/rules/indent/invalid/snippets01-output.svelte
index e07895e..221a81d 100644
--- a/packages/eslint-plugin-svelte/tests/fixtures/rules/indent/invalid/snippets01-output.svelte
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/indent/invalid/snippets01-output.svelte
@@ -14,3 +14,26 @@
     }
   )
 }
+<div>
+  {#snippet example()}
+    <div></div>
+  {/snippet}
+
+  {
+    #snippet example_2()
+  }
+    <div></div>
+  {
+    /snippet
+  }
+</div>
+{
+  #snippet example_3
+    (
+
+    )
+}
+  <div></div>
+{
+  /snippet
+}