eslint-plugin-svelte 3.17.1 → 3.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -301,6 +301,7 @@ These rules relate to possible syntax or logic errors in Svelte code:
301
301
  | [svelte/no-dupe-on-directives](https://sveltejs.github.io/eslint-plugin-svelte/rules/no-dupe-on-directives/) | disallow duplicate `on:` directives | :star: |
302
302
  | [svelte/no-dupe-style-properties](https://sveltejs.github.io/eslint-plugin-svelte/rules/no-dupe-style-properties/) | disallow duplicate style properties | :star: |
303
303
  | [svelte/no-dupe-use-directives](https://sveltejs.github.io/eslint-plugin-svelte/rules/no-dupe-use-directives/) | disallow duplicate `use:` directives | :star: |
304
+ | [svelte/no-nested-style-tag](https://sveltejs.github.io/eslint-plugin-svelte/rules/no-nested-style-tag/) | disallow `<style>` elements nested inside other elements or blocks | |
304
305
  | [svelte/no-not-function-handler](https://sveltejs.github.io/eslint-plugin-svelte/rules/no-not-function-handler/) | disallow use of not function in event handler | :star: |
305
306
  | [svelte/no-object-in-text-mustaches](https://sveltejs.github.io/eslint-plugin-svelte/rules/no-object-in-text-mustaches/) | disallow objects in text mustache interpolation | :star: |
306
307
  | [svelte/no-raw-special-elements](https://sveltejs.github.io/eslint-plugin-svelte/rules/no-raw-special-elements/) | Checks for invalid raw HTML elements | :star::wrench: |
@@ -348,6 +349,7 @@ These rules relate to better ways of doing things to help you avoid problems:
348
349
  | [svelte/no-useless-children-snippet](https://sveltejs.github.io/eslint-plugin-svelte/rules/no-useless-children-snippet/) | disallow explicit children snippet where it's not needed | :star: |
349
350
  | [svelte/no-useless-mustaches](https://sveltejs.github.io/eslint-plugin-svelte/rules/no-useless-mustaches/) | disallow unnecessary mustache interpolations | :star::wrench: |
350
351
  | [svelte/prefer-const](https://sveltejs.github.io/eslint-plugin-svelte/rules/prefer-const/) | Require `const` declarations for variables that are never reassigned after declared | :wrench: |
352
+ | [svelte/prefer-derived-over-derived-by](https://sveltejs.github.io/eslint-plugin-svelte/rules/prefer-derived-over-derived-by/) | disallow unnecessary `$derived.by()` when `$derived()` is sufficient | :wrench: |
351
353
  | [svelte/prefer-destructured-store-props](https://sveltejs.github.io/eslint-plugin-svelte/rules/prefer-destructured-store-props/) | destructure values from object stores for better change tracking & fewer redraws | :bulb: |
352
354
  | [svelte/prefer-writable-derived](https://sveltejs.github.io/eslint-plugin-svelte/rules/prefer-writable-derived/) | Prefer using writable $derived instead of $state and $effect | :star::bulb: |
353
355
  | [svelte/require-each-key](https://sveltejs.github.io/eslint-plugin-svelte/rules/require-each-key/) | require keyed `{#each}` block | :star: |
package/lib/main.d.ts CHANGED
@@ -14,7 +14,7 @@ export declare const configs: {
14
14
  export declare const rules: Record<string, Rule.RuleModule>;
15
15
  export declare const meta: {
16
16
  name: "eslint-plugin-svelte";
17
- version: "3.17.1";
17
+ version: "3.19.0";
18
18
  };
19
19
  export declare const processors: {
20
20
  '.svelte': typeof processor;
package/lib/meta.d.ts CHANGED
@@ -1,2 +1,2 @@
1
1
  export declare const name: "eslint-plugin-svelte";
2
- export declare const version: "3.17.1";
2
+ export declare const version: "3.19.0";
package/lib/meta.js CHANGED
@@ -2,4 +2,4 @@
2
2
  // This file has been automatically generated,
3
3
  // in order to update its content execute "pnpm run update"
4
4
  export const name = 'eslint-plugin-svelte';
5
- export const version = '3.17.1';
5
+ export const version = '3.19.0';
@@ -193,6 +193,11 @@ export interface RuleOptions {
193
193
  * @see https://sveltejs.github.io/eslint-plugin-svelte/rules/no-navigation-without-resolve/
194
194
  */
195
195
  'svelte/no-navigation-without-resolve'?: Linter.RuleEntry<SvelteNoNavigationWithoutResolve>;
196
+ /**
197
+ * disallow `<style>` elements nested inside other elements or blocks
198
+ * @see https://sveltejs.github.io/eslint-plugin-svelte/rules/no-nested-style-tag/
199
+ */
200
+ 'svelte/no-nested-style-tag'?: Linter.RuleEntry<[]>;
196
201
  /**
197
202
  * disallow use of not function in event handler
198
203
  * @see https://sveltejs.github.io/eslint-plugin-svelte/rules/no-not-function-handler/
@@ -308,6 +313,11 @@ export interface RuleOptions {
308
313
  * @see https://sveltejs.github.io/eslint-plugin-svelte/rules/prefer-const/
309
314
  */
310
315
  'svelte/prefer-const'?: Linter.RuleEntry<SveltePreferConst>;
316
+ /**
317
+ * disallow unnecessary `$derived.by()` when `$derived()` is sufficient
318
+ * @see https://sveltejs.github.io/eslint-plugin-svelte/rules/prefer-derived-over-derived-by/
319
+ */
320
+ 'svelte/prefer-derived-over-derived-by'?: Linter.RuleEntry<[]>;
311
321
  /**
312
322
  * destructure values from object stores for better change tracking & fewer redraws
313
323
  * @see https://sveltejs.github.io/eslint-plugin-svelte/rules/prefer-destructured-store-props/
@@ -172,6 +172,13 @@ export function defineVisitor(context) {
172
172
  offsets.setOffsetToken(declarationToken, 1, openToken);
173
173
  offsets.setOffsetToken(closeToken, 0, openToken);
174
174
  },
175
+ SvelteDeclarationTag(node) {
176
+ const openToken = sourceCode.getFirstToken(node);
177
+ const declarationToken = sourceCode.getFirstToken(node.declaration);
178
+ const closeToken = sourceCode.getLastToken(node);
179
+ offsets.setOffsetToken(declarationToken, 1, openToken);
180
+ offsets.setOffsetToken(closeToken, 0, openToken);
181
+ },
175
182
  SvelteRenderTag(node) {
176
183
  const openToken = sourceCode.getFirstToken(node);
177
184
  const renderToken = sourceCode.getTokenAfter(openToken);
@@ -151,6 +151,10 @@ export default createRule('mustache-spacing', {
151
151
  const mustacheTokens = getMustacheTokens(node, sourceCode);
152
152
  verifyBraces(mustacheTokens.openToken, mustacheTokens.closeToken, options.tags.openingBrace, options.tags.closingBrace, true);
153
153
  },
154
+ SvelteDeclarationTag(node) {
155
+ const mustacheTokens = getMustacheTokens(node, sourceCode);
156
+ verifyBraces(mustacheTokens.openToken, mustacheTokens.closeToken, options.tags.openingBrace, options.tags.closingBrace, true);
157
+ },
154
158
  SvelteRenderTag(node) {
155
159
  const mustacheTokens = getMustacheTokens(node, sourceCode);
156
160
  verifyBraces(mustacheTokens.openToken, mustacheTokens.closeToken, options.tags.openingBrace, options.tags.closingBrace, true);
@@ -2,7 +2,7 @@ import { createRule } from '../utils/index.js';
2
2
  import { ReferenceTracker } from '@eslint-community/eslint-utils';
3
3
  import { FindVariableContext } from '../utils/ast-utils.js';
4
4
  import { findVariable } from '../utils/ast-utils.js';
5
- import { getTypeScriptTools } from '../utils/ts-utils/index.js';
5
+ import { getTypeScriptTools, isNullType, isUndefinedType } from '../utils/ts-utils/index.js';
6
6
  export default createRule('no-navigation-without-resolve', {
7
7
  meta: {
8
8
  docs: {
@@ -209,7 +209,7 @@ function isValueAllowed(ctx, value, resolveReferences, tsTools, config) {
209
209
  if (variable !== null &&
210
210
  variable.identifiers.length > 0 &&
211
211
  variable.identifiers[0].parent.type === 'VariableDeclarator') {
212
- if (expressionIsResolvedPathname(variable.identifiers[0], tsTools)) {
212
+ if (expressionIsAllowedType(variable.identifiers[0], config.allowNullish, tsTools)) {
213
213
  return true;
214
214
  }
215
215
  if (variable.identifiers[0].parent.init !== null) {
@@ -225,14 +225,14 @@ function isValueAllowed(ctx, value, resolveReferences, tsTools, config) {
225
225
  (config.allowEmpty && expressionIsEmpty(value)) ||
226
226
  (config.allowFragment && expressionStartsWith(ctx, value, '#')) ||
227
227
  (config.allowNullish && expressionIsNullish(value)) ||
228
- expressionIsResolvedPathname(value, tsTools) ||
228
+ expressionIsAllowedType(value, config.allowNullish, tsTools) ||
229
229
  expressionIsResolveCall(ctx, value, resolveReferences)) {
230
230
  return true;
231
231
  }
232
232
  return false;
233
233
  }
234
234
  // Helper functions
235
- function expressionIsResolvedPathname(value, tsTools) {
235
+ function expressionIsAllowedType(value, allowNullish, tsTools) {
236
236
  if (tsTools === null) {
237
237
  return false;
238
238
  }
@@ -241,7 +241,13 @@ function expressionIsResolvedPathname(value, tsTools) {
241
241
  if (tsNode === undefined) {
242
242
  return false;
243
243
  }
244
- const nodeType = checker.getTypeAtLocation(tsNode);
244
+ let nodeType = checker.getTypeAtLocation(tsNode);
245
+ if (allowNullish === true) {
246
+ if (isNullType(nodeType, tsTools.ts) || isUndefinedType(nodeType, tsTools.ts)) {
247
+ return true;
248
+ }
249
+ nodeType = checker.getNonNullableType(nodeType);
250
+ }
245
251
  const appTypesModule = checker.getAmbientModules().find((m) => m.name === '"$app/types"');
246
252
  if (!appTypesModule) {
247
253
  return false;
@@ -0,0 +1,2 @@
1
+ declare const _default: import("../types.js").RuleModule;
2
+ export default _default;
@@ -0,0 +1,25 @@
1
+ import { createRule } from '../utils/index.js';
2
+ export default createRule('no-nested-style-tag', {
3
+ meta: {
4
+ docs: {
5
+ description: 'disallow `<style>` elements nested inside other elements or blocks',
6
+ category: 'Possible Errors',
7
+ recommended: false
8
+ },
9
+ schema: [],
10
+ messages: {
11
+ nestedStyle: 'Nested `<style>` elements are not scoped and may lead to unintended styles being applied.'
12
+ },
13
+ type: 'problem'
14
+ },
15
+ create(context) {
16
+ return {
17
+ SvelteElement(node) {
18
+ if (node.kind !== 'html' || node.name.type !== 'SvelteName' || node.name.name !== 'style') {
19
+ return;
20
+ }
21
+ context.report({ node, messageId: 'nestedStyle' });
22
+ }
23
+ };
24
+ }
25
+ });
@@ -0,0 +1,2 @@
1
+ declare const _default: import("../types.js").RuleModule;
2
+ export default _default;
@@ -0,0 +1,68 @@
1
+ import { createRule } from '../utils/index.js';
2
+ export default createRule('prefer-derived-over-derived-by', {
3
+ meta: {
4
+ docs: {
5
+ description: 'disallow unnecessary `$derived.by()` when `$derived()` is sufficient',
6
+ category: 'Best Practices',
7
+ recommended: false
8
+ },
9
+ fixable: 'code',
10
+ schema: [],
11
+ messages: {
12
+ unnecessary: 'Unnecessary use of `$derived.by()`. Use `$derived()` directly for simple expressions.'
13
+ },
14
+ type: 'suggestion',
15
+ conditions: [
16
+ {
17
+ svelteVersions: ['5'],
18
+ runes: [true, 'undetermined']
19
+ }
20
+ ]
21
+ },
22
+ create(context) {
23
+ return {
24
+ CallExpression(node) {
25
+ const { callee } = node;
26
+ if (callee.type !== 'MemberExpression' ||
27
+ callee.computed ||
28
+ callee.object.type !== 'Identifier' ||
29
+ callee.object.name !== '$derived' ||
30
+ callee.property.type !== 'Identifier' ||
31
+ callee.property.name !== 'by') {
32
+ return;
33
+ }
34
+ if (node.arguments.length !== 1) {
35
+ return;
36
+ }
37
+ const arg = node.arguments[0];
38
+ if (arg.type !== 'ArrowFunctionExpression' && arg.type !== 'FunctionExpression') {
39
+ return;
40
+ }
41
+ if (arg.params.length !== 0 || arg.async || arg.generator) {
42
+ return;
43
+ }
44
+ let expressionNode = null;
45
+ if (arg.type === 'ArrowFunctionExpression' && arg.body.type !== 'BlockStatement') {
46
+ expressionNode = arg.body;
47
+ }
48
+ else if (arg.body.type === 'BlockStatement' &&
49
+ arg.body.body.length === 1 &&
50
+ arg.body.body[0].type === 'ReturnStatement' &&
51
+ arg.body.body[0].argument !== null) {
52
+ expressionNode = arg.body.body[0].argument;
53
+ }
54
+ if (expressionNode === null) {
55
+ return;
56
+ }
57
+ context.report({
58
+ node,
59
+ messageId: 'unnecessary',
60
+ fix(fixer) {
61
+ const expressionText = context.sourceCode.getText(expressionNode);
62
+ return fixer.replaceText(node, `$derived(${expressionText})`);
63
+ }
64
+ });
65
+ }
66
+ };
67
+ }
68
+ });
@@ -369,6 +369,8 @@ export type ASTNodeListener = {
369
369
  'SvelteDebugTag:exit'?: (node: AST.SvelteDebugTag & ASTNodeWithParent) => void;
370
370
  SvelteConstTag?: (node: AST.SvelteConstTag & ASTNodeWithParent) => void;
371
371
  'SvelteConstTag:exit'?: (node: AST.SvelteConstTag & ASTNodeWithParent) => void;
372
+ SvelteDeclarationTag?: (node: AST.SvelteDeclarationTag & ASTNodeWithParent) => void;
373
+ 'SvelteDeclarationTag:exit'?: (node: AST.SvelteDeclarationTag & ASTNodeWithParent) => void;
372
374
  SvelteRenderTag?: (node: AST.SvelteRenderTag & ASTNodeWithParent) => void;
373
375
  'SvelteRenderTag:exit'?: (node: AST.SvelteRenderTag & ASTNodeWithParent) => void;
374
376
  SvelteIfBlock?: (node: AST.SvelteIfBlock & ASTNodeWithParent) => void;
@@ -757,6 +759,8 @@ export type SvelteNodeListener = {
757
759
  'SvelteDebugTag:exit'?: (node: AST.SvelteDebugTag & ASTNodeWithParent) => void;
758
760
  SvelteConstTag?: (node: AST.SvelteConstTag & ASTNodeWithParent) => void;
759
761
  'SvelteConstTag:exit'?: (node: AST.SvelteConstTag & ASTNodeWithParent) => void;
762
+ SvelteDeclarationTag?: (node: AST.SvelteDeclarationTag & ASTNodeWithParent) => void;
763
+ 'SvelteDeclarationTag:exit'?: (node: AST.SvelteDeclarationTag & ASTNodeWithParent) => void;
760
764
  SvelteRenderTag?: (node: AST.SvelteRenderTag & ASTNodeWithParent) => void;
761
765
  'SvelteRenderTag:exit'?: (node: AST.SvelteRenderTag & ASTNodeWithParent) => void;
762
766
  SvelteIfBlock?: (node: AST.SvelteIfBlock & ASTNodeWithParent) => void;
@@ -91,11 +91,11 @@ export type QuoteAndRange = {
91
91
  };
92
92
  /** Get the quote and range from given attribute values */
93
93
  export declare function getAttributeValueQuoteAndRange(attr: SvAST.SvelteAttribute | SvAST.SvelteDirective | SvAST.SvelteStyleDirective | SvAST.SvelteSpecialDirective, sourceCode: SourceCode): QuoteAndRange | null;
94
- export declare function getMustacheTokens(node: SvAST.SvelteMustacheTag | SvAST.SvelteShorthandAttribute | SvAST.SvelteSpreadAttribute | SvAST.SvelteDebugTag | SvAST.SvelteRenderTag, sourceCode: SourceCode): {
94
+ export declare function getMustacheTokens(node: SvAST.SvelteMustacheTag | SvAST.SvelteShorthandAttribute | SvAST.SvelteSpreadAttribute | SvAST.SvelteDebugTag | SvAST.SvelteDeclarationTag | SvAST.SvelteRenderTag, sourceCode: SourceCode): {
95
95
  openToken: SvAST.Token;
96
96
  closeToken: SvAST.Token;
97
97
  };
98
- export declare function getMustacheTokens(node: SvAST.SvelteDirective | SvAST.SvelteSpecialDirective | SvAST.SvelteMustacheTag | SvAST.SvelteShorthandAttribute | SvAST.SvelteSpreadAttribute | SvAST.SvelteDebugTag | SvAST.SvelteRenderTag, sourceCode: SourceCode): {
98
+ export declare function getMustacheTokens(node: SvAST.SvelteDirective | SvAST.SvelteSpecialDirective | SvAST.SvelteMustacheTag | SvAST.SvelteShorthandAttribute | SvAST.SvelteSpreadAttribute | SvAST.SvelteDebugTag | SvAST.SvelteDeclarationTag | SvAST.SvelteRenderTag, sourceCode: SourceCode): {
99
99
  openToken: SvAST.Token;
100
100
  closeToken: SvAST.Token;
101
101
  } | null;
@@ -333,6 +333,7 @@ function isWrappedInBraces(node) {
333
333
  node.type === 'SvelteShorthandAttribute' ||
334
334
  node.type === 'SvelteSpreadAttribute' ||
335
335
  node.type === 'SvelteDebugTag' ||
336
+ node.type === 'SvelteDeclarationTag' ||
336
337
  node.type === 'SvelteRenderTag');
337
338
  }
338
339
  /** Get attribute key text */
@@ -35,6 +35,7 @@ import noInnerDeclarations from '../rules/no-inner-declarations.js';
35
35
  import noInspect from '../rules/no-inspect.js';
36
36
  import noNavigationWithoutBase from '../rules/no-navigation-without-base.js';
37
37
  import noNavigationWithoutResolve from '../rules/no-navigation-without-resolve.js';
38
+ import noNestedStyleTag from '../rules/no-nested-style-tag.js';
38
39
  import noNotFunctionHandler from '../rules/no-not-function-handler.js';
39
40
  import noObjectInTextMustaches from '../rules/no-object-in-text-mustaches.js';
40
41
  import noRawSpecialElements from '../rules/no-raw-special-elements.js';
@@ -58,6 +59,7 @@ import noUselessChildrenSnippet from '../rules/no-useless-children-snippet.js';
58
59
  import noUselessMustaches from '../rules/no-useless-mustaches.js';
59
60
  import preferClassDirective from '../rules/prefer-class-directive.js';
60
61
  import preferConst from '../rules/prefer-const.js';
62
+ import preferDerivedOverDerivedBy from '../rules/prefer-derived-over-derived-by.js';
61
63
  import preferDestructuredStoreProps from '../rules/prefer-destructured-store-props.js';
62
64
  import preferStyleDirective from '../rules/prefer-style-directive.js';
63
65
  import preferSvelteReactivity from '../rules/prefer-svelte-reactivity.js';
@@ -116,6 +118,7 @@ export const rules = [
116
118
  noInspect,
117
119
  noNavigationWithoutBase,
118
120
  noNavigationWithoutResolve,
121
+ noNestedStyleTag,
119
122
  noNotFunctionHandler,
120
123
  noObjectInTextMustaches,
121
124
  noRawSpecialElements,
@@ -139,6 +142,7 @@ export const rules = [
139
142
  noUselessMustaches,
140
143
  preferClassDirective,
141
144
  preferConst,
145
+ preferDerivedOverDerivedBy,
142
146
  preferDestructuredStoreProps,
143
147
  preferStyleDirective,
144
148
  preferSvelteReactivity,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eslint-plugin-svelte",
3
- "version": "3.17.1",
3
+ "version": "3.19.0",
4
4
  "description": "ESLint plugin for Svelte using AST",
5
5
  "repository": {
6
6
  "type": "git",
@@ -45,7 +45,7 @@
45
45
  "postcss-load-config": "^3.1.4",
46
46
  "postcss-safe-parser": "^7.0.0",
47
47
  "semver": "^7.6.3",
48
- "svelte-eslint-parser": "^1.4.0"
48
+ "svelte-eslint-parser": "^1.7.0"
49
49
  },
50
50
  "devDependencies": {
51
51
  "@babel/core": "^7.28.3",
@@ -79,7 +79,7 @@
79
79
  "sass": "^1.92.0",
80
80
  "source-map-js": "^1.2.1",
81
81
  "stylus": "^0.64.0",
82
- "svelte": "^5.41.0",
82
+ "svelte": "^5.56.0",
83
83
  "svelte-i18n": "^4.0.1",
84
84
  "tsx": "^4.20.5",
85
85
  "type-coverage": "^2.29.7",