Skip to content

Commit 316f5ca

Browse files
committed
feat(prefer-svelte-reactivity): added rule implementation
1 parent 5f9f913 commit 316f5ca

File tree

7 files changed

+102
-18
lines changed

7 files changed

+102
-18
lines changed

.changeset/rich-colts-nail.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: added the `prefer-svelte-reactivity` rule

README.md

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,8 @@
3030

3131
## Introduction
3232

33-
`eslint-plugin-svelte` is the official [ESLint](https://eslint.org/) plugin for [Svelte](https://svelte.dev/).
34-
It leverages the AST generated by [svelte-eslint-parser](https://github.com/sveltejs/svelte-eslint-parser) to provide custom linting for Svelte.
33+
`eslint-plugin-svelte` is the official [ESLint](https://eslint.org/) plugin for [Svelte](https://svelte.dev/).
34+
It leverages the AST generated by [svelte-eslint-parser](https://github.com/sveltejs/svelte-eslint-parser) to provide custom linting for Svelte.
3535
Note that `eslint-plugin-svelte` and `svelte-eslint-parser` cannot be used alongside [eslint-plugin-svelte3](https://github.com/sveltejs/eslint-plugin-svelte3).
3636

3737
<!--USAGE_SECTION_START-->
@@ -219,8 +219,8 @@ export default [
219219

220220
## Editor Integrations
221221

222-
**Visual Studio Code**
223-
Install [dbaeumer.vscode-eslint](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint).
222+
**Visual Studio Code**
223+
Install [dbaeumer.vscode-eslint](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint).
224224
Configure `.svelte` files in `.vscode/settings.json`:
225225

226226
```json
@@ -247,8 +247,8 @@ This project follows [Semantic Versioning](https://semver.org/). Unlike [ESLint
247247
<!-- prettier-ignore-start -->
248248
<!--RULES_SECTION_START-->
249249

250-
:wrench: Indicates that the rule is fixable, and using `--fix` option on the [command line](https://eslint.org/docs/user-guide/command-line-interface#fixing-problems) can automatically fix some of the reported problems.
251-
:bulb: Indicates that some problems reported by the rule are manually fixable by editor [suggestions](https://eslint.org/docs/developer-guide/working-with-rules#providing-suggestions).
250+
:wrench: Indicates that the rule is fixable, and using `--fix` option on the [command line](https://eslint.org/docs/user-guide/command-line-interface#fixing-problems) can automatically fix some of the reported problems.
251+
:bulb: Indicates that some problems reported by the rule are manually fixable by editor [suggestions](https://eslint.org/docs/developer-guide/working-with-rules#providing-suggestions).
252252
:star: Indicates that the rule is included in the `plugin:svelte/recommended` config.
253253

254254
<!--RULES_TABLE_START-->
@@ -272,7 +272,8 @@ These rules relate to possible syntax or logic errors in Svelte code:
272272
| [svelte/no-shorthand-style-property-overrides](https://sveltejs.github.io/eslint-plugin-svelte/rules/no-shorthand-style-property-overrides/) | disallow shorthand style properties that override related longhand properties | :star: |
273273
| [svelte/no-store-async](https://sveltejs.github.io/eslint-plugin-svelte/rules/no-store-async/) | disallow using async/await inside svelte stores because it causes issues with the auto-unsubscribing features | :star: |
274274
| [svelte/no-unknown-style-directive-property](https://sveltejs.github.io/eslint-plugin-svelte/rules/no-unknown-style-directive-property/) | disallow unknown `style:property` | :star: |
275-
| [svelte/require-store-callbacks-use-set-param](https://sveltejs.github.io/eslint-plugin-svelte/rules/require-store-callbacks-use-set-param/) | store callbacks must use `set` param | :bulb: |
275+
| [svelte/prefer-svelte-reactivity](https://sveltejs.github.io/eslint-plugin-svelte/rules/prefer-svelte-reactivity/) | disallow using built-in classes where a reactive alternative is provided by svelte/reactivity | :star: |
276+
| [svelte/require-store-callbacks-use-set-param](https://sveltejs.github.io/eslint-plugin-svelte/rules/require-store-callbacks-use-set-param/) | store callbacks must use `set` param | |
276277
| [svelte/require-store-reactive-access](https://sveltejs.github.io/eslint-plugin-svelte/rules/require-store-reactive-access/) | disallow to use of the store itself as an operand. Need to use $ prefix or get function. | :star::wrench: |
277278
| [svelte/valid-compile](https://sveltejs.github.io/eslint-plugin-svelte/rules/valid-compile/) | disallow warnings when compiling. | |
278279
| [svelte/valid-style-parse](https://sveltejs.github.io/eslint-plugin-svelte/rules/valid-style-parse/) | require valid style element parsing | |
@@ -400,7 +401,7 @@ These rules relate to this plugin works:
400401

401402
## Contributing
402403

403-
Contributions are welcome! Please open an issue or submit a PR. For more details, see [CONTRIBUTING.md](./CONTRIBUTING.md).
404+
Contributions are welcome! Please open an issue or submit a PR. For more details, see [CONTRIBUTING.md](./CONTRIBUTING.md).
404405
Refer to [svelte-eslint-parser](https://github.com/sveltejs/svelte-eslint-parser) for AST details.
405406

406407
<!--DOCS_IGNORE_END-->

docs/rules.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ sidebarDepth: 0
44

55
# Available Rules
66

7-
:wrench: Indicates that the rule is fixable, and using `--fix` option on the [command line](https://eslint.org/docs/user-guide/command-line-interface#fixing-problems) can automatically fix some of the reported problems.
8-
:bulb: Indicates that some problems reported by the rule are manually fixable by editor [suggestions](https://eslint.org/docs/developer-guide/working-with-rules#providing-suggestions).
7+
:wrench: Indicates that the rule is fixable, and using `--fix` option on the [command line](https://eslint.org/docs/user-guide/command-line-interface#fixing-problems) can automatically fix some of the reported problems.
8+
:bulb: Indicates that some problems reported by the rule are manually fixable by editor [suggestions](https://eslint.org/docs/developer-guide/working-with-rules#providing-suggestions).
99
:star: Indicates that the rule is included in the `plugin:svelte/recommended` config.
1010

1111
<!-- This file is automatically generated in tools/update-docs-rules-index.js, do not change! -->
@@ -29,7 +29,8 @@ These rules relate to possible syntax or logic errors in Svelte code:
2929
| [svelte/no-shorthand-style-property-overrides](./rules/no-shorthand-style-property-overrides.md) | disallow shorthand style properties that override related longhand properties | :star: |
3030
| [svelte/no-store-async](./rules/no-store-async.md) | disallow using async/await inside svelte stores because it causes issues with the auto-unsubscribing features | :star: |
3131
| [svelte/no-unknown-style-directive-property](./rules/no-unknown-style-directive-property.md) | disallow unknown `style:property` | :star: |
32-
| [svelte/require-store-callbacks-use-set-param](./rules/require-store-callbacks-use-set-param.md) | store callbacks must use `set` param | :bulb: |
32+
| [svelte/prefer-svelte-reactivity](./rules/prefer-svelte-reactivity.md) | disallow using built-in classes where a reactive alternative is provided by svelte/reactivity | :star: |
33+
| [svelte/require-store-callbacks-use-set-param](./rules/require-store-callbacks-use-set-param.md) | store callbacks must use `set` param | |
3334
| [svelte/require-store-reactive-access](./rules/require-store-reactive-access.md) | disallow to use of the store itself as an operand. Need to use $ prefix or get function. | :star::wrench: |
3435
| [svelte/valid-compile](./rules/valid-compile.md) | disallow warnings when compiling. | |
3536
| [svelte/valid-style-parse](./rules/valid-style-parse.md) | require valid style element parsing | |

packages/eslint-plugin-svelte/src/configs/flat/recommended.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ const config: Linter.Config[] = [
3737
'svelte/no-unused-svelte-ignore': 'error',
3838
'svelte/no-useless-children-snippet': 'error',
3939
'svelte/no-useless-mustaches': 'error',
40+
'svelte/prefer-svelte-reactivity': 'error',
4041
'svelte/prefer-writable-derived': 'error',
4142
'svelte/require-each-key': 'error',
4243
'svelte/require-event-dispatcher-types': 'error',

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

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -306,6 +306,11 @@ export interface RuleOptions {
306306
* @see https://sveltejs.github.io/eslint-plugin-svelte/rules/prefer-style-directive/
307307
*/
308308
'svelte/prefer-style-directive'?: Linter.RuleEntry<[]>
309+
/**
310+
* disallow using built-in classes where a reactive alternative is provided by svelte/reactivity
311+
* @see https://sveltejs.github.io/eslint-plugin-svelte/rules/prefer-svelte-reactivity/
312+
*/
313+
'svelte/prefer-svelte-reactivity'?: Linter.RuleEntry<[]>
309314
/**
310315
* Prefer using writable $derived instead of $state and $effect
311316
* @see https://sveltejs.github.io/eslint-plugin-svelte/rules/prefer-writable-derived/
@@ -396,9 +401,9 @@ export interface RuleOptions {
396401
/* ======= Declarations ======= */
397402
// ----- svelte/@typescript-eslint/no-unnecessary-condition -----
398403
type SvelteTypescriptEslintNoUnnecessaryCondition = []|[{
399-
404+
400405
allowConstantLoopConditions?: boolean
401-
406+
402407
allowRuleToRunWithoutStrictNullChecksIKnowWhatIAmDoing?: boolean
403408
}]
404409
// ----- svelte/block-lang -----
@@ -421,7 +426,7 @@ type SvelteCommentDirective = []|[{
421426
// ----- svelte/consistent-selector-style -----
422427
type SvelteConsistentSelectorStyle = []|[{
423428
checkGlobal?: boolean
424-
429+
425430
style?: []|[("class" | "id" | "type")]|[("class" | "id" | "type"), ("class" | "id" | "type")]|[("class" | "id" | "type"), ("class" | "id" | "type"), ("class" | "id" | "type")]
426431
}]
427432
// ----- svelte/first-attribute-linebreak -----
@@ -509,11 +514,11 @@ type SvelteNoReactiveReassign = []|[{
509514
}]
510515
// ----- svelte/no-restricted-html-elements -----
511516
type SvelteNoRestrictedHtmlElements = [(string | {
512-
517+
513518
elements?: [string, ...(string)[]]
514519
message?: string
515520
}), ...((string | {
516-
521+
517522
elements?: [string, ...(string)[]]
518523
message?: string
519524
}))[]]
@@ -529,7 +534,7 @@ type SvelteNoTrailingSpaces = []|[{
529534
}]
530535
// ----- svelte/no-unknown-style-directive-property -----
531536
type SvelteNoUnknownStyleDirectiveProperty = []|[{
532-
537+
533538
ignoreProperties?: [string, ...(string)[]]
534539
ignorePrefixed?: boolean
535540
}]
@@ -589,4 +594,4 @@ type SvelteSpacedHtmlComment = []|[("always" | "never")]
589594
// ----- svelte/valid-compile -----
590595
type SvelteValidCompile = []|[{
591596
ignoreWarnings?: boolean
592-
}]
597+
}]
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
import { ReferenceTracker } from '@eslint-community/eslint-utils';
2+
import { createRule } from '../utils/index.js';
3+
4+
export default createRule('prefer-svelte-reactivity', {
5+
meta: {
6+
docs: {
7+
description:
8+
'disallow using built-in classes where a reactive alternative is provided by svelte/reactivity',
9+
category: 'Possible Errors',
10+
recommended: true
11+
},
12+
schema: [],
13+
messages: {
14+
dateUsed: 'Found a usage of the built-in Date class. Use a SvelteDate instead.',
15+
mapUsed: 'Found a usage of the built-in Map class. Use a SvelteMap instead.',
16+
setUsed: 'Found a usage of the built-in Set class. Use a SvelteSet instead.',
17+
urlUsed: 'Found a usage of the built-in URL class. Use a SvelteURL instead.',
18+
urlSearchParamsUsed:
19+
'Found a usage of the built-in URLSearchParams class. Use a SvelteURLSearchParams instead.'
20+
},
21+
type: 'problem', // 'problem', or 'layout',
22+
conditions: [
23+
{
24+
svelteVersions: ['5'],
25+
svelteFileTypes: ['.svelte', '.svelte.[js|ts]']
26+
}
27+
]
28+
},
29+
create(context) {
30+
return {
31+
Program() {
32+
const referenceTracker = new ReferenceTracker(context.sourceCode.scopeManager.globalScope!);
33+
for (const { node, path } of referenceTracker.iterateGlobalReferences({
34+
Date: {
35+
[ReferenceTracker.CONSTRUCT]: true
36+
},
37+
Map: {
38+
[ReferenceTracker.CONSTRUCT]: true
39+
},
40+
Set: {
41+
[ReferenceTracker.CONSTRUCT]: true
42+
},
43+
URL: {
44+
[ReferenceTracker.CALL]: true,
45+
[ReferenceTracker.CONSTRUCT]: true,
46+
[ReferenceTracker.READ]: true
47+
},
48+
URLSearchParams: {
49+
[ReferenceTracker.CALL]: true,
50+
[ReferenceTracker.CONSTRUCT]: true,
51+
[ReferenceTracker.READ]: true
52+
}
53+
})) {
54+
const typeToMessageId: Record<string, string> = {
55+
Date: 'dateUsed',
56+
Map: 'mapUsed',
57+
Set: 'setUsed',
58+
URL: 'urlUsed',
59+
URLSearchParams: 'urlSearchParamsUsed'
60+
};
61+
context.report({
62+
messageId: typeToMessageId[path[0]],
63+
node
64+
});
65+
}
66+
}
67+
};
68+
}
69+
});

packages/eslint-plugin-svelte/src/utils/rules.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ import preferClassDirective from '../rules/prefer-class-directive.js';
6060
import preferConst from '../rules/prefer-const.js';
6161
import preferDestructuredStoreProps from '../rules/prefer-destructured-store-props.js';
6262
import preferStyleDirective from '../rules/prefer-style-directive.js';
63+
import preferSvelteReactivity from '../rules/prefer-svelte-reactivity.js';
6364
import preferWritableDerived from '../rules/prefer-writable-derived.js';
6465
import requireEachKey from '../rules/require-each-key.js';
6566
import requireEventDispatcherTypes from '../rules/require-event-dispatcher-types.js';
@@ -137,6 +138,7 @@ export const rules = [
137138
preferConst,
138139
preferDestructuredStoreProps,
139140
preferStyleDirective,
141+
preferSvelteReactivity,
140142
preferWritableDerived,
141143
requireEachKey,
142144
requireEventDispatcherTypes,

0 commit comments

Comments
 (0)