Repository: sveltejs/eslint-plugin-svelte
Pull request #1164 (2025-03-28 17:51:41)

feat: add fixer for `no-at-debug-tags`
Adds a simple fixer which removes the debug node.

changeset-bot[bot]: ###  ⚠️  No Changeset found

Latest commit: c5324eb34e85d68d28b1fe75683aa47e1ba9dfc3

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. **If these changes should result in a version bump, you need to add a changeset.**

<details><summary>This PR includes no changesets</summary>

  When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

</details>

[Click here to learn what changesets are, and how to add one](https://github.com/changesets/changesets/blob/main/docs/adding-a-changeset.md).

[Click here if you're a maintainer who wants to add a changeset to this PR](https://github.com/43081j/eslint-plugin-svelte/new/fixer-debug-tags?filename=.changeset/swift-birds-occur.md&value=---%0A%22eslint-plugin-svelte%22%3A%20patch%0A---%0A%0Afeat%3A%20add%20fixer%20for%20%60no-at-debug-tags%60%0A)



Patch:
diff --git a/.changeset/weak-crews-shake.md b/.changeset/weak-crews-shake.md
new file mode 100644
index 0000000..bb42095
--- /dev/null
+++ b/.changeset/weak-crews-shake.md
@@ -0,0 +1,5 @@
+---
+'eslint-plugin-svelte': minor
+---
+
+Added a suggestion for `no-at-debug-tags` rule which removes the tags
diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 0000000..d4479be
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,7 @@
+root = true
+
+[*]
+end_of_line = lf
+indent_size = 2
+indent_style = tab
+trim_trailing_whitespace = true
diff --git a/.github/workflows/GHPages.yml b/.github/workflows/GHPages.yml
index d5a3e28..ac8f0c3 100644
--- a/.github/workflows/GHPages.yml
+++ b/.github/workflows/GHPages.yml
@@ -26,7 +26,7 @@ jobs:
     steps:
       - name: Checkout
         uses: actions/checkout@v4
-      - uses: pnpm/action-setup@v2
+      - uses: pnpm/action-setup@v4
       - uses: actions/setup-node@v4
       - name: Install And Build
         run: |
diff --git a/.github/workflows/NodeCI.yml b/.github/workflows/NodeCI.yml
index 7ac120e..3b442aa 100644
--- a/.github/workflows/NodeCI.yml
+++ b/.github/workflows/NodeCI.yml
@@ -14,7 +14,7 @@ jobs:
     runs-on: ubuntu-latest
     steps:
       - uses: actions/checkout@v4
-      - uses: pnpm/action-setup@v2
+      - uses: pnpm/action-setup@v4
       - uses: actions/setup-node@v4
       - name: Install Packages
         run: pnpm install
@@ -36,7 +36,7 @@ jobs:
     runs-on: ubuntu-latest
     steps:
       - uses: actions/checkout@v4
-      - uses: pnpm/action-setup@v2
+      - uses: pnpm/action-setup@v4
       - uses: actions/setup-node@v4
       - name: Install Packages
         run: pnpm install
@@ -57,7 +57,7 @@ jobs:
     steps:
       - name: Checkout
         uses: actions/checkout@v4
-      - uses: pnpm/action-setup@v2
+      - uses: pnpm/action-setup@v4
       - name: Setup Node.js ${{ matrix.node }}
         uses: actions/setup-node@v4
         with:
@@ -90,7 +90,7 @@ jobs:
     steps:
       - name: Checkout
         uses: actions/checkout@v4
-      - uses: pnpm/action-setup@v2
+      - uses: pnpm/action-setup@v4
       - name: Setup Node.js ${{ matrix.node }}
         uses: actions/setup-node@v4
         with:
@@ -120,7 +120,7 @@ jobs:
     steps:
       - name: Checkout
         uses: actions/checkout@v4
-      - uses: pnpm/action-setup@v2
+      - uses: pnpm/action-setup@v4
       - name: Setup Node.js ${{ matrix.node }}
         uses: actions/setup-node@v4
         with:
@@ -143,7 +143,7 @@ jobs:
     runs-on: ubuntu-latest
     steps:
       - uses: actions/checkout@v4
-      - uses: pnpm/action-setup@v2
+      - uses: pnpm/action-setup@v4
       - uses: actions/setup-node@v4
       - name: Install Packages
         run: pnpm install
diff --git a/.github/workflows/Release.yml b/.github/workflows/Release.yml
index 7e1949f..0625bea 100644
--- a/.github/workflows/Release.yml
+++ b/.github/workflows/Release.yml
@@ -22,7 +22,7 @@ jobs:
           fetch-depth: 0
 
       - name: Setup pnpm
-        uses: pnpm/action-setup@v2
+        uses: pnpm/action-setup@v4
 
       - name: Setup Node.js
         uses: actions/setup-node@v4
diff --git a/.github/workflows/pkg.pr.new.yml b/.github/workflows/pkg.pr.new.yml
index cdb7776..6f2fa51 100644
--- a/.github/workflows/pkg.pr.new.yml
+++ b/.github/workflows/pkg.pr.new.yml
@@ -13,7 +13,7 @@ jobs:
 
     steps:
       - uses: actions/checkout@v4
-      - uses: pnpm/action-setup@v2
+      - uses: pnpm/action-setup@v4
       - uses: actions/setup-node@v4
       - name: Install Packages
         run: pnpm install
diff --git a/README.md b/README.md
index db731dd..59a2ec4 100644
--- a/README.md
+++ b/README.md
@@ -294,7 +294,7 @@ These rules relate to better ways of doing things to help you avoid problems:
 |:--------|:------------|:---|
 | [svelte/block-lang](https://sveltejs.github.io/eslint-plugin-svelte/rules/block-lang/) | disallows the use of languages other than those specified in the configuration for the lang attribute of `<script>` and `<style>` blocks. | :bulb: |
 | [svelte/button-has-type](https://sveltejs.github.io/eslint-plugin-svelte/rules/button-has-type/) | disallow usage of button without an explicit type attribute |  |
-| [svelte/no-at-debug-tags](https://sveltejs.github.io/eslint-plugin-svelte/rules/no-at-debug-tags/) | disallow the use of `{@debug}` | :star: |
+| [svelte/no-at-debug-tags](https://sveltejs.github.io/eslint-plugin-svelte/rules/no-at-debug-tags/) | disallow the use of `{@debug}` | :star::bulb: |
 | [svelte/no-ignored-unsubscribe](https://sveltejs.github.io/eslint-plugin-svelte/rules/no-ignored-unsubscribe/) | disallow ignoring the unsubscribe method returned by the `subscribe()` on Svelte stores. |  |
 | [svelte/no-immutable-reactive-statements](https://sveltejs.github.io/eslint-plugin-svelte/rules/no-immutable-reactive-statements/) | disallow reactive statements that don't reference reactive values. | :star: |
 | [svelte/no-inline-styles](https://sveltejs.github.io/eslint-plugin-svelte/rules/no-inline-styles/) | disallow attributes and directives that produce inline styles |  |
diff --git a/docs/rules.md b/docs/rules.md
index 58d896b..f7d28ab 100644
--- a/docs/rules.md
+++ b/docs/rules.md
@@ -51,7 +51,7 @@ These rules relate to better ways of doing things to help you avoid problems:
 | :--------------------------------------------------------------------------------------- | :---------------------------------------------------------------------------------------------------------------------------------------- | :------------- |
 | [svelte/block-lang](./rules/block-lang.md)                                               | disallows the use of languages other than those specified in the configuration for the lang attribute of `<script>` and `<style>` blocks. | :bulb:         |
 | [svelte/button-has-type](./rules/button-has-type.md)                                     | disallow usage of button without an explicit type attribute                                                                               |                |
-| [svelte/no-at-debug-tags](./rules/no-at-debug-tags.md)                                   | disallow the use of `{@debug}`                                                                                                            | :star:         |
+| [svelte/no-at-debug-tags](./rules/no-at-debug-tags.md)                                   | disallow the use of `{@debug}`                                                                                                            | :star::bulb:   |
 | [svelte/no-ignored-unsubscribe](./rules/no-ignored-unsubscribe.md)                       | disallow ignoring the unsubscribe method returned by the `subscribe()` on Svelte stores.                                                  |                |
 | [svelte/no-immutable-reactive-statements](./rules/no-immutable-reactive-statements.md)   | disallow reactive statements that don't reference reactive values.                                                                        | :star:         |
 | [svelte/no-inline-styles](./rules/no-inline-styles.md)                                   | disallow attributes and directives that produce inline styles                                                                             |                |
diff --git a/docs/rules/no-at-debug-tags.md b/docs/rules/no-at-debug-tags.md
index 5167bef..4655cd8 100644
--- a/docs/rules/no-at-debug-tags.md
+++ b/docs/rules/no-at-debug-tags.md
@@ -11,6 +11,7 @@ since: 'v0.0.1'
 > disallow the use of `{@debug}`
 
 - :gear: This rule is included in `"plugin:svelte/recommended"`.
+- :bulb: Some problems reported by this rule are manually fixable by editor [suggestions](https://eslint.org/docs/developer-guide/working-with-rules#providing-suggestions).
 
 ## :book: Rule Details
 
diff --git a/packages/eslint-plugin-svelte/src/rules/no-at-debug-tags.ts b/packages/eslint-plugin-svelte/src/rules/no-at-debug-tags.ts
index 13e3de6..f7876de 100644
--- a/packages/eslint-plugin-svelte/src/rules/no-at-debug-tags.ts
+++ b/packages/eslint-plugin-svelte/src/rules/no-at-debug-tags.ts
@@ -8,9 +8,11 @@ export default createRule('no-at-debug-tags', {
 			recommended: true,
 			default: 'warn'
 		},
+		hasSuggestions: true,
 		schema: [],
 		messages: {
-			unexpected: 'Unexpected `{@debug}`.'
+			unexpected: 'Unexpected `{@debug}`.',
+			suggestRemove: 'Remove `{@debug}` from the source'
 		},
 		type: 'problem'
 	},
@@ -19,7 +21,13 @@ export default createRule('no-at-debug-tags', {
 			SvelteDebugTag(node) {
 				context.report({
 					node,
-					messageId: 'unexpected'
+					messageId: 'unexpected',
+					suggest: [
+						{
+							messageId: 'suggestRemove',
+							fix: (fixer) => fixer.remove(node)
+						}
+					]
 				});
 			}
 		};
diff --git a/packages/eslint-plugin-svelte/src/rules/no-unused-props.ts b/packages/eslint-plugin-svelte/src/rules/no-unused-props.ts
index 2ea58fd..5a3dc7d 100644
--- a/packages/eslint-plugin-svelte/src/rules/no-unused-props.ts
+++ b/packages/eslint-plugin-svelte/src/rules/no-unused-props.ts
@@ -6,7 +6,7 @@ import { findVariable } from '../utils/ast-utils.js';
 import { toRegExp } from '../utils/regexp.js';
 import { getFilename } from '../utils/compat.js';
 
-type PropertyPath = string[];
+type PropertyPathArray = string[];
 
 let isRemovedWarningShown = false;
 
@@ -124,8 +124,8 @@ export default createRule('no-unused-props', {
 		/**
 		 * Extracts property paths from member expressions.
 		 */
-		function getPropertyPath(node: TSESTree.Identifier): PropertyPath {
-			const paths: PropertyPath = [];
+		function getPropertyPath(node: TSESTree.Identifier): PropertyPathArray {
+			const paths: PropertyPathArray = [];
 			let currentNode: TSESTree.Node = node;
 			let parentNode: TSESTree.Node | null = currentNode.parent ?? null;
 
@@ -150,11 +150,11 @@ export default createRule('no-unused-props', {
 		/**
 		 * Finds all property access paths for a given variable.
 		 */
-		function getUsedNestedPropertyNames(node: TSESTree.Identifier): PropertyPath[] {
+		function getUsedNestedPropertyPathsArray(node: TSESTree.Identifier): PropertyPathArray[] {
 			const variable = findVariable(context, node);
 			if (!variable) return [];
 
-			const paths: PropertyPath[] = [];
+			const pathsArray: PropertyPathArray[] = [];
 			for (const reference of variable.references) {
 				if (
 					'identifier' in reference &&
@@ -163,10 +163,10 @@ export default createRule('no-unused-props', {
 						reference.identifier.range[1] !== node.range[1])
 				) {
 					const referencePath = getPropertyPath(reference.identifier);
-					paths.push(referencePath);
+					pathsArray.push(referencePath);
 				}
 			}
-			return paths;
+			return pathsArray;
 		}
 
 		/**
@@ -183,7 +183,7 @@ export default createRule('no-unused-props', {
 			return sourceFile.fileName.includes('node_modules/typescript/lib/');
 		}
 
-		function getUsedPropertiesFromPattern(pattern: TSESTree.ObjectPattern): Set<string> {
+		function getUsedPropertyNamesFromPattern(pattern: TSESTree.ObjectPattern): Set<string> {
 			const usedProps = new Set<string>();
 			for (const prop of pattern.properties) {
 				if (prop.type === 'Property' && prop.key.type === 'Identifier') {
@@ -219,41 +219,49 @@ export default createRule('no-unused-props', {
 		/**
 		 * Recursively checks for unused properties in a type.
 		 */
-		function checkUnusedProperties(
-			type: ts.Type,
-			usedPaths: PropertyPath[],
-			usedProps: Set<string>,
-			reportNode: TSESTree.Node,
-			parentPath: string[],
-			checkedTypes: Set<string>,
-			reportedProps: Set<string>
-		) {
+		function checkUnusedProperties({
+			propsType,
+			usedPropertyPaths,
+			declaredPropertyNames,
+			reportNode,
+			parentPath,
+			checkedPropsTypes,
+			reportedPropertyPaths
+		}: {
+			propsType: ts.Type;
+			usedPropertyPaths: string[];
+			declaredPropertyNames: Set<string>;
+			reportNode: TSESTree.Node;
+			parentPath: string[];
+			checkedPropsTypes: Set<string>;
+			reportedPropertyPaths: Set<string>;
+		}) {
 			// Skip checking if the type itself is a class
-			if (isClassType(type)) return;
+			if (isClassType(propsType)) return;
 
-			const typeStr = typeChecker.typeToString(type);
-			if (checkedTypes.has(typeStr)) return;
-			checkedTypes.add(typeStr);
-			if (shouldIgnoreType(type)) return;
+			const typeStr = typeChecker.typeToString(propsType);
+			if (checkedPropsTypes.has(typeStr)) return;
+			checkedPropsTypes.add(typeStr);
+			if (shouldIgnoreType(propsType)) return;
 
-			const properties = typeChecker.getPropertiesOfType(type);
-			const baseTypes = type.getBaseTypes();
+			const properties = typeChecker.getPropertiesOfType(propsType);
+			const propsBaseTypes = propsType.getBaseTypes();
 
-			if (!properties.length && (!baseTypes || baseTypes.length === 0)) {
+			if (!properties.length && (!propsBaseTypes || propsBaseTypes.length === 0)) {
 				return;
 			}
 
-			if (baseTypes) {
-				for (const baseType of baseTypes) {
-					checkUnusedProperties(
-						baseType,
-						usedPaths,
-						usedProps,
+			if (propsBaseTypes) {
+				for (const propsBaseType of propsBaseTypes) {
+					checkUnusedProperties({
+						propsType: propsBaseType,
+						usedPropertyPaths,
+						declaredPropertyNames,
 						reportNode,
 						parentPath,
-						checkedTypes,
-						reportedProps
-					);
+						checkedPropsTypes,
+						reportedPropertyPaths
+					});
 				}
 			}
 
@@ -267,13 +275,12 @@ export default createRule('no-unused-props', {
 				const currentPath = [...parentPath, propName];
 				const currentPathStr = [...parentPath, propName].join('.');
 
-				if (reportedProps.has(currentPathStr)) continue;
+				if (reportedPropertyPaths.has(currentPathStr)) continue;
 
 				const propType = typeChecker.getTypeOfSymbol(prop);
 
-				const joinedUsedPaths = usedPaths.map((path) => path.join('.'));
-				const isUsedThisInPath = joinedUsedPaths.includes(currentPathStr);
-				const isUsedInPath = joinedUsedPaths.some((path) => {
+				const isUsedThisInPath = usedPropertyPaths.includes(currentPathStr);
+				const isUsedInPath = usedPropertyPaths.some((path) => {
 					return path.startsWith(`${currentPathStr}.`);
 				});
 
@@ -281,10 +288,10 @@ export default createRule('no-unused-props', {
 					continue;
 				}
 
-				const isUsedInProps = usedProps.has(propName);
+				const isUsedInProps = declaredPropertyNames.has(propName);
 
 				if (!isUsedInPath && !isUsedInProps) {
-					reportedProps.add(currentPathStr);
+					reportedPropertyPaths.add(currentPathStr);
 					context.report({
 						node: reportNode,
 						messageId: parentPath.length ? 'unusedNestedProp' : 'unusedProp',
@@ -296,30 +303,30 @@ export default createRule('no-unused-props', {
 					continue;
 				}
 
-				const isUsedNested = joinedUsedPaths.some((path) => {
+				const isUsedNested = usedPropertyPaths.some((path) => {
 					return path.startsWith(`${currentPathStr}.`);
 				});
 
 				if (isUsedNested || isUsedInProps) {
-					checkUnusedProperties(
-						propType,
-						usedPaths,
-						usedProps,
+					checkUnusedProperties({
+						propsType: propType,
+						usedPropertyPaths,
+						declaredPropertyNames,
 						reportNode,
-						currentPath,
-						checkedTypes,
-						reportedProps
-					);
+						parentPath: currentPath,
+						checkedPropsTypes,
+						reportedPropertyPaths
+					});
 				}
 			}
 
 			// Check for unused index signatures only at the root level
 			if (parentPath.length === 0) {
-				const indexType = type.getStringIndexType();
-				const numberIndexType = type.getNumberIndexType();
+				const indexType = propsType.getStringIndexType();
+				const numberIndexType = propsType.getNumberIndexType();
 				const hasIndexSignature = Boolean(indexType) || Boolean(numberIndexType);
 
-				if (hasIndexSignature && !hasRestElement(usedProps)) {
+				if (hasIndexSignature && !hasRestElement(declaredPropertyNames)) {
 					context.report({
 						node: reportNode,
 						messageId: 'unusedIndexSignature'
@@ -336,8 +343,8 @@ export default createRule('no-unused-props', {
 			return usedProps.size === 0;
 		}
 
-		function normalizeUsedPaths(paths: PropertyPath[]): PropertyPath[] {
-			const normalized: PropertyPath[] = [];
+		function normalizeUsedPaths(paths: PropertyPathArray[]): PropertyPathArray[] {
+			const normalized: PropertyPathArray[] = [];
 			for (const path of paths.sort((a, b) => a.length - b.length)) {
 				if (path.length === 0) continue;
 				if (normalized.some((p) => p.every((part, idx) => part === path[idx]))) {
@@ -362,13 +369,13 @@ export default createRule('no-unused-props', {
 				const tsNode = tools.service.esTreeNodeToTSNodeMap.get(node) as ts.VariableDeclaration;
 				if (!tsNode || !tsNode.type) return;
 
-				const propType = typeChecker.getTypeFromTypeNode(tsNode.type);
-				let usedPaths: PropertyPath[] = [];
-				let usedProps = new Set<string>();
+				const propsType = typeChecker.getTypeFromTypeNode(tsNode.type);
+				let usedPropertyPathsArray: PropertyPathArray[] = [];
+				let declaredPropertyNames = new Set<string>();
 
 				if (node.id.type === 'ObjectPattern') {
-					usedProps = getUsedPropertiesFromPattern(node.id);
-					if (usedProps.size === 0) return;
+					declaredPropertyNames = getUsedPropertyNamesFromPattern(node.id);
+					if (declaredPropertyNames.size === 0) return;
 					const identifiers: TSESTree.Identifier[] = [];
 					for (const p of node.id.properties) {
 						if (p.type !== 'Property') {
@@ -381,22 +388,24 @@ export default createRule('no-unused-props', {
 						}
 					}
 					for (const identifier of identifiers) {
-						const paths = getUsedNestedPropertyNames(identifier);
-						usedPaths.push(...paths.map((path) => [identifier.name, ...path]));
+						const paths = getUsedNestedPropertyPathsArray(identifier);
+						usedPropertyPathsArray.push(...paths.map((path) => [identifier.name, ...path]));
 					}
 				} else if (node.id.type === 'Identifier') {
-					usedPaths = getUsedNestedPropertyNames(node.id);
+					usedPropertyPathsArray = getUsedNestedPropertyPathsArray(node.id);
 				}
 
-				checkUnusedProperties(
-					propType,
-					normalizeUsedPaths(usedPaths),
-					usedProps,
-					node.id,
-					[],
-					new Set<string>(),
-					new Set<string>()
-				);
+				checkUnusedProperties({
+					propsType,
+					usedPropertyPaths: normalizeUsedPaths(usedPropertyPathsArray).map((pathArray) => {
+						return pathArray.join('.');
+					}),
+					declaredPropertyNames,
+					reportNode: node.id,
+					parentPath: [],
+					checkedPropsTypes: new Set<string>(),
+					reportedPropertyPaths: new Set<string>()
+				});
 			}
 		};
 	}


Test patch:
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-at-debug-tags/invalid/debug01-errors.yaml b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-at-debug-tags/invalid/debug01-errors.yaml
index 7583d5b..6327cee 100644
--- a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-at-debug-tags/invalid/debug01-errors.yaml
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-at-debug-tags/invalid/debug01-errors.yaml
@@ -1,4 +1,20 @@
 - message: Unexpected `{@debug}`.
   line: 11
   column: 1
-  suggestions: null
+  suggestions:
+    - desc: Remove `{@debug}` from the source
+      messageId: suggestRemove
+      output: |
+        <script>
+        	let user = {
+        		firstname: 'Ada',
+        		lastname: 'Lovelace'
+        	};
+        </script>
+
+        <input bind:value={user.firstname} />
+        <input bind:value={user.lastname} />
+
+
+
+        <h1>Hello {user.firstname}!</h1>
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-at-debug-tags/invalid/debug02-errors.yaml b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-at-debug-tags/invalid/debug02-errors.yaml
index 7583d5b..6327cee 100644
--- a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-at-debug-tags/invalid/debug02-errors.yaml
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-at-debug-tags/invalid/debug02-errors.yaml
@@ -1,4 +1,20 @@
 - message: Unexpected `{@debug}`.
   line: 11
   column: 1
-  suggestions: null
+  suggestions:
+    - desc: Remove `{@debug}` from the source
+      messageId: suggestRemove
+      output: |
+        <script>
+        	let user = {
+        		firstname: 'Ada',
+        		lastname: 'Lovelace'
+        	};
+        </script>
+
+        <input bind:value={user.firstname} />
+        <input bind:value={user.lastname} />
+
+
+
+        <h1>Hello {user.firstname}!</h1>
diff --git a/packages/eslint-plugin-svelte/tests/utils/utils.ts b/packages/eslint-plugin-svelte/tests/utils/utils.ts
index dcf466a..2177447 100644
--- a/packages/eslint-plugin-svelte/tests/utils/utils.ts
+++ b/packages/eslint-plugin-svelte/tests/utils/utils.ts
@@ -126,15 +126,19 @@ export function loadTestCases(
 		.map((inputFile) => {
 			const config = getConfig(ruleName, inputFile);
 			const errorFile = inputFile.replace(/(input|\+.+)\.[a-z]+$/u, 'errors.yaml');
-			const outputFile = inputFile.replace(/(input|\+.+)\.[a-z]+$/u, 'output.svelte');
+			const outputExt = path.extname(inputFile);
+			const outputFile = inputFile.replace(
+				/(input|\+.+)\.[a-z]+$/u,
+				`output.${outputExt.slice(1)}`
+			);
 
 			if (!fs.existsSync(errorFile)) {
-				writeFixtures(ruleName, inputFile);
+				writeFixtures(ruleName, inputFile, outputFile);
 			} else if (
 				// eslint-disable-next-line no-process-env -- tool
 				process.env.UPDATE_FIXTURES
 			) {
-				writeFixtures(ruleName, inputFile, { force: true });
+				writeFixtures(ruleName, inputFile, outputFile, { force: true });
 			}
 			const errors = fs.readFileSync(errorFile, 'utf8');
 			config.errors = parseYaml(errors);
@@ -151,7 +155,7 @@ export function loadTestCases(
 				try {
 					output = fs.readFileSync(outputFile, 'utf8');
 				} catch (_e) {
-					writeFixtures(ruleName, inputFile);
+					writeFixtures(ruleName, inputFile, outputFile);
 					output = fs.readFileSync(outputFile, 'utf8');
 				}
 				config.output = output === config.code ? null : output;
@@ -213,10 +217,14 @@ function applySuggestion(code: string, suggestion: LinterType.LintSuggestion) {
 	return `${code.slice(0, fix.range[0])}${fix.text}${code.slice(fix.range[1])}`;
 }
 
-function writeFixtures(ruleName: string, inputFile: string, { force }: { force?: boolean } = {}) {
+function writeFixtures(
+	ruleName: string,
+	inputFile: string,
+	outputFile: string,
+	{ force }: { force?: boolean } = {}
+) {
 	const linter = new Linter();
 	const errorFile = inputFile.replace(/(input|\+.+)\.[a-z]+$/u, 'errors.yaml');
-	const outputFile = inputFile.replace(/(input|\+.+)\.[a-z]+$/u, 'output.svelte');
 
 	const config = getConfig(ruleName, inputFile);