Skip to content

Commit 893166c

Browse files
committed
feat: add ignorePropertyPatterns property and rename ignorePatterns to ignoreTypePatterns in no-unused-props rule
1 parent bea4945 commit 893166c

20 files changed

+170
-26
lines changed

.changeset/crazy-peas-laugh.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'eslint-plugin-svelte': minor
3+
---
4+
5+
feat: add `ignorePropertyPatterns` property and rename `ignorePatterns` to `ignoreTypePatterns` in `no-unused-props` rule

docs/rules/no-unused-props.md

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -159,14 +159,17 @@ Note: Properties of class types are not checked for usage, as they might be used
159159
"svelte/no-unused-props": ["error", {
160160
// Whether to check properties from imported types
161161
"checkImportedTypes": false,
162+
// Patterns to ignore when checking property types
163+
"ignoreTypePatterns": [],
162164
// Patterns to ignore when checking for unused props
163-
"ignorePatterns": []
165+
"ignorePropertyPatterns": ["/^[#$@_~]/"],
164166
}]
165167
}
166168
```
167169

168170
- `checkImportedTypes` ... Controls whether to check properties from imported types. Default is `false`.
169-
- `ignorePatterns` ... Patterns to ignore when checking for unused props. Default is an empty array.
171+
- `ignoreTypePatterns` ... Patterns to ignore when checking property types. Default is `[]`.
172+
- `ignorePropertyPatterns` ... Patterns to ignore when checking for unused props. Default is `/["^[#$@_~]"]/`, which ignores properties starting with special characters often used for internal or framework-specific identifiers.
170173

171174
Examples:
172175

@@ -187,10 +190,28 @@ Examples:
187190
```svelte
188191
<!-- ✓ Good Examples -->
189192
<script lang="ts">
190-
/* eslint svelte/no-unused-props: ["error", { "ignorePatterns": ["^_"] }] */
191-
// Ignore properties starting with underscore
193+
/* eslint svelte/no-unused-props: ["error", { "ignoreTypePatterns": ["/^Internal/"] }] */
194+
// Ignore properties from types matching the pattern
195+
interface InternalConfig {
196+
secretKey: string;
197+
debugMode: boolean;
198+
}
199+
interface Props {
200+
config: InternalConfig; // Properties of InternalConfig won't be checked
201+
value: number;
202+
}
203+
let { config, value }: Props = $props();
204+
console.log(value, config);
205+
</script>
206+
```
207+
208+
```svelte
209+
<!-- ✓ Good Examples -->
210+
<script lang="ts">
211+
/* eslint svelte/no-unused-props: ["error", { "ignorePropertyPatterns": ["/^_/"] }] */
212+
// Ignore properties with names matching the pattern
192213
interface Props {
193-
_internal: string;
214+
_internal: string; // This prop won't be reported even if unused
194215
value: number;
195216
}
196217
let { value }: Props = $props();

packages/eslint-plugin-svelte/src/rule-types.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -535,7 +535,8 @@ type SvelteNoUnusedClassName = []|[{
535535
// ----- svelte/no-unused-props -----
536536
type SvelteNoUnusedProps = []|[{
537537
checkImportedTypes?: boolean
538-
ignorePatterns?: string[]
538+
ignoreTypePatterns?: string[]
539+
ignorePropertyPatterns?: string[]
539540
}]
540541
// ----- svelte/no-useless-mustaches -----
541542
type SvelteNoUselessMustaches = []|[{

packages/eslint-plugin-svelte/src/rules/no-unused-props.ts

Lines changed: 52 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ import { getFilename } from '../utils/compat.js';
88

99
type PropertyPath = string[];
1010

11+
let isDeprecationWarningShown = false;
12+
1113
export default createRule('no-unused-props', {
1214
meta: {
1315
docs: {
@@ -23,12 +25,21 @@ export default createRule('no-unused-props', {
2325
type: 'boolean',
2426
default: false
2527
},
26-
ignorePatterns: {
28+
ignoreTypePatterns: {
2729
type: 'array',
2830
items: {
2931
type: 'string'
3032
},
3133
default: []
34+
},
35+
ignorePropertyPatterns: {
36+
type: 'array',
37+
items: {
38+
type: 'string'
39+
},
40+
// For example, valibot generates symbol like `__@BrandSymbol@1167`.
41+
// So, we ignore properties starting with special characters often used for internal or framework-specific identifiers.
42+
default: ['^[#$@_~]']
3243
}
3344
},
3445
additionalProperties: false
@@ -61,6 +72,17 @@ export default createRule('no-unused-props', {
6172
}
6273

6374
const options = context.options[0] ?? {};
75+
76+
// TODO: Remove in v4
77+
// MEMO: `ignorePatterns` was a property that only existed from v3.2.0 to v3.2.1.
78+
// From v3.3.0, it was replaced with `ignorePropertyPatterns` and `ignoreTypePatterns`.
79+
if (options.ignorePatterns != null && !isDeprecationWarningShown) {
80+
console.warn(
81+
'eslint-plugin-svelte: The `ignorePatterns` option in the `no-unused-props` rule has been deprecated. Please use `ignorePropertyPatterns` and `ignoreTypePatterns` instead.'
82+
);
83+
isDeprecationWarningShown = true;
84+
}
85+
6486
const checkImportedTypes = options.checkImportedTypes ?? false;
6587
const ignorePatterns = (options.ignorePatterns ?? []).map((p: string | RegExp) => {
6688
if (typeof p === 'string') {
@@ -69,15 +91,39 @@ export default createRule('no-unused-props', {
6991
return p;
7092
});
7193

72-
function shouldIgnore(name: string): boolean {
73-
return ignorePatterns.some((pattern: RegExp) => pattern.test(name));
94+
const ignoreTypePatterns = [
95+
...ignorePatterns,
96+
...(options.ignoreTypePatterns ?? []).map((p: string | RegExp) => {
97+
if (typeof p === 'string') {
98+
return toRegExp(p);
99+
}
100+
return p;
101+
})
102+
];
103+
104+
const ignorePropertyPatterns = [
105+
...ignorePatterns,
106+
...(options.ignorePropertyPatterns ?? [/^[#$@_~]/]).map((p: string | RegExp) => {
107+
if (typeof p === 'string') {
108+
return toRegExp(p);
109+
}
110+
return p;
111+
})
112+
];
113+
114+
function shouldIgnoreProperty(name: string): boolean {
115+
return ignorePropertyPatterns.some((pattern: RegExp) => pattern.test(name));
74116
}
75117

76118
function shouldIgnoreType(type: ts.Type): boolean {
119+
function isMatched(name: string): boolean {
120+
return ignoreTypePatterns.some((pattern: RegExp) => pattern.test(name));
121+
}
122+
77123
const typeStr = typeChecker.typeToString(type);
78124
const symbol = type.getSymbol();
79125
const symbolName = symbol?.getName();
80-
return shouldIgnore(typeStr) || (symbolName ? shouldIgnore(symbolName) : false);
126+
return isMatched(typeStr) || (symbolName ? isMatched(symbolName) : false);
81127
}
82128

83129
function isExternalType(type: ts.Type): boolean {
@@ -227,6 +273,8 @@ export default createRule('no-unused-props', {
227273
if (isBuiltInProperty(prop)) continue;
228274

229275
const propName = prop.getName();
276+
if (shouldIgnoreProperty(propName)) continue;
277+
230278
const currentPath = [...parentPath, propName];
231279
const currentPathStr = [...parentPath, propName].join('.');
232280

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"options": [
3+
{
4+
"ignorePropertyPatterns": ["^foo$"]
5+
}
6+
]
7+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
- message: "'foo' is an unused Props property."
2+
line: 8
3+
column: 8
4+
suggestions: null
5+
- message: "'_foo' is an unused Props property."
6+
line: 8
7+
column: 8
8+
suggestions: null
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<script lang="ts">
2+
interface Props {
3+
foo: string;
4+
_foo: string;
5+
bar: string;
6+
}
7+
8+
const { bar }: Props = $props();
9+
</script>

packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/invalid/ignored-pattern-partial-config.json

Lines changed: 0 additions & 7 deletions
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"options": [
3+
{
4+
"ignoreTypePatterns": [".*DTO$"]
5+
}
6+
]
7+
}
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
11
- message: "'name' is an unused Props property."
22
line: 6
33
column: 6
4-
endLine: 6
5-
endColumn: 20
64
suggestions: null
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"options": [
3+
{
4+
"ignorePropertyPatterns": ["/^foo$/"]
5+
}
6+
]
7+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<script lang="ts">
2+
interface Props {
3+
foo: string;
4+
bar: string;
5+
}
6+
7+
const { bar }: Props = $props();
8+
</script>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<script lang="ts">
2+
interface Props {
3+
_internal: string;
4+
$store: boolean;
5+
'@decorator': string;
6+
'#private': number;
7+
'~tilde': boolean;
8+
normalUsed: string;
9+
}
10+
11+
const { normalUsed }: Props = $props();
12+
console.log(normalUsed);
13+
</script>

packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/ignored-conditional-type-config.json

Lines changed: 0 additions & 7 deletions
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"options": [
3+
{
4+
"ignoreTypePatterns": ["/^Conditional/"]
5+
}
6+
]
7+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"options": [
3+
{
4+
"ignoreTypePatterns": ["/^Internal/"]
5+
}
6+
]
7+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<script lang="ts">
2+
interface InternalConfig {
3+
secretKey: string;
4+
debugMode: boolean;
5+
}
6+
interface Props {
7+
config: InternalConfig;
8+
value: number;
9+
}
10+
let { config, value }: Props = $props();
11+
console.log(value, config);
12+
</script>

0 commit comments

Comments
 (0)