Repository: sveltejs/eslint-plugin-svelte
Pull request #1257 (2025-06-26 11:35:01)

feat(no-unused-class-name): Support Regex for `allowedClassNames` option
### Motivation

When I use this rule for project which is using `unocss`, I want to add unocss's [dynamic-rules](https://unocss.dev/config/rules#dynamic-rules) to `allowedClassNames`
ex) `/^m-(\d+)$/`


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

Latest commit: ba742ee0f81ba0c39f66c847854cd7c082becd86

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/tbashiyy/eslint-plugin-svelte/new/feat/support-regex-no-unused-class-names-allowed-class-names?filename=.changeset/seven-sloths-doubt.md&value=---%0A%22eslint-plugin-svelte%22%3A%20patch%0A---%0A%0Afeat%3A%20Support%20Regex%20for%20no-unused-class-names%20%60allowedClassNames%60%0A)


github-actions[bot]: <!-- posted by pkg.pr.new-comment.mjs -->

## Try the Instant Preview in Online Playground

[ESLint Online Playground](https://eslint-online-playground.netlify.app/?overrideDeps=%7B%22eslint-plugin-svelte%22%3A%22https%3A%2F%2Fpkg.pr.new%2Feslint-plugin-svelte%40ba742ee%22%7D#eslint-plugin-svelte%20with%20typescript)

## Install the Instant Preview to Your Local

```
npm i https://pkg.pr.new/eslint-plugin-svelte@ba742ee
```

## Published Instant Preview Packages:

- eslint-plugin-svelte: https://pkg.pr.new/eslint-plugin-svelte@ba742ee

[View Commit](https://github.com/sveltejs/eslint-plugin-svelte/commit/ba742ee0f81ba0c39f66c847854cd7c082becd86)
baseballyama: CI is failed.

Patch:
diff --git a/.changeset/forty-signs-smoke.md b/.changeset/forty-signs-smoke.md
new file mode 100644
index 0000000..f72f3d6
--- /dev/null
+++ b/.changeset/forty-signs-smoke.md
@@ -0,0 +1,5 @@
+---
+'eslint-plugin-svelte': minor
+---
+
+feat(no-unused-class-name): support regex for `allowedClassNames` option
diff --git a/docs/rules/no-unused-class-name.md b/docs/rules/no-unused-class-name.md
index ecfef48..249656c 100644
--- a/docs/rules/no-unused-class-name.md
+++ b/docs/rules/no-unused-class-name.md
@@ -53,7 +53,7 @@ This rule is aimed at reducing unused classes in the HTML template. While `svelt
   "svelte/no-unused-class-name": [
     "error",
     {
-      "allowedClassNames": ["class-name-one", "class-name-two"]
+      "allowedClassNames": ["class-name-one", "class-name-two", "/^regex-.*$/"] // You can also use regex to match class names
     }
   ]
 }
diff --git a/packages/eslint-plugin-svelte/src/rules/no-unused-class-name.ts b/packages/eslint-plugin-svelte/src/rules/no-unused-class-name.ts
index ad03b5c..0906905 100644
--- a/packages/eslint-plugin-svelte/src/rules/no-unused-class-name.ts
+++ b/packages/eslint-plugin-svelte/src/rules/no-unused-class-name.ts
@@ -4,6 +4,7 @@ import type { AnyNode } from 'postcss';
 import type { Node as SelectorNode } from 'postcss-selector-parser';
 import { findClassesInAttribute } from '../utils/ast-utils.js';
 import type { SourceCode } from '../types.js';
+import { toRegExp } from '../utils/regexp.js';
 
 export default createRule('no-unused-class-name', {
 	meta: {
@@ -57,7 +58,12 @@ export default createRule('no-unused-class-name', {
 						? findClassesInPostCSSNode(styleContext.sourceAst, sourceCode.parserServices)
 						: [];
 				for (const className in classesUsedInTemplate) {
-					if (!allowedClassNames.includes(className) && !classesUsedInStyle.includes(className)) {
+					if (
+						!allowedClassNames.some((allowedClassName: string) =>
+							toRegExp(allowedClassName).test(className)
+						) &&
+						!classesUsedInStyle.includes(className)
+					) {
 						context.report({
 							loc: classesUsedInTemplate[className],
 							message: `Unused class "${className}".`


Test patch:
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-class-name/invalid/allowed-class-names/_config.json b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-class-name/invalid/allowed-class-names/_config.json
index 6f1145f..7f72e0d 100644
--- a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-class-name/invalid/allowed-class-names/_config.json
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-class-name/invalid/allowed-class-names/_config.json
@@ -1,3 +1,3 @@
 {
-	"options": [{ "allowedClassNames": ["div-class"] }]
+	"options": [{ "allowedClassNames": ["div-class", "/^p-\\d{1,2}$/"] }]
 }
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-class-name/invalid/allowed-class-names/partially-allowed-class-name01-errors.yaml b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-class-name/invalid/allowed-class-names/partially-allowed-class-name01-errors.yaml
index 8f64dcb..127f72b 100644
--- a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-class-name/invalid/allowed-class-names/partially-allowed-class-name01-errors.yaml
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-class-name/invalid/allowed-class-names/partially-allowed-class-name01-errors.yaml
@@ -2,3 +2,7 @@
   line: 3
   column: 1
   suggestions: null
+- message: Unused class "p-100".
+  line: 5
+  column: 1
+  suggestions: null
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-class-name/invalid/allowed-class-names/partially-allowed-class-name01-input.svelte b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-class-name/invalid/allowed-class-names/partially-allowed-class-name01-input.svelte
index a486966..21e917f 100644
--- a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-class-name/invalid/allowed-class-names/partially-allowed-class-name01-input.svelte
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-class-name/invalid/allowed-class-names/partially-allowed-class-name01-input.svelte
@@ -1,3 +1,5 @@
 <div class="div-class">Hello</div>
 
 <span class="span-class">World!</span>
+
+<span class="p-100">Regex!</span>
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-class-name/valid/allowed-class-names/_config.json b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-class-name/valid/allowed-class-names/_config.json
index 1215054..365e479 100644
--- a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-class-name/valid/allowed-class-names/_config.json
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-class-name/valid/allowed-class-names/_config.json
@@ -1,3 +1,3 @@
 {
-	"options": [{ "allowedClassNames": ["div-class", "span-class"] }]
+	"options": [{ "allowedClassNames": ["div-class", "span-class", "/^p-\\d{1,2}$/"] }]
 }
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-class-name/valid/allowed-class-names/allowed-class-name01-input.svelte b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-class-name/valid/allowed-class-names/allowed-class-name01-input.svelte
index a486966..009527e 100644
--- a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-class-name/valid/allowed-class-names/allowed-class-name01-input.svelte
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-class-name/valid/allowed-class-names/allowed-class-name01-input.svelte
@@ -1,3 +1,5 @@
 <div class="div-class">Hello</div>
 
 <span class="span-class">World!</span>
+
+<span class="p-2">Regex!</span>