diff --git a/.changeset/loud-zoos-kiss.md b/.changeset/loud-zoos-kiss.md new file mode 100644 index 000000000..74689a93a --- /dev/null +++ b/.changeset/loud-zoos-kiss.md @@ -0,0 +1,5 @@ +--- +'eslint-plugin-svelte': minor +--- + +feat: added the no-useless-children-snippet rule diff --git a/README.md b/README.md index 72cad6960..35c16c8e8 100644 --- a/README.md +++ b/README.md @@ -366,6 +366,7 @@ These rules relate to better ways of doing things to help you avoid problems: | [svelte/no-svelte-internal](https://sveltejs.github.io/eslint-plugin-svelte/rules/no-svelte-internal/) | svelte/internal will be removed in Svelte 6. | | | [svelte/no-unused-class-name](https://sveltejs.github.io/eslint-plugin-svelte/rules/no-unused-class-name/) | disallow the use of a class in the template without a corresponding style | | | [svelte/no-unused-svelte-ignore](https://sveltejs.github.io/eslint-plugin-svelte/rules/no-unused-svelte-ignore/) | disallow unused svelte-ignore comments | :star: | +| [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 | | | [svelte/no-useless-mustaches](https://sveltejs.github.io/eslint-plugin-svelte/rules/no-useless-mustaches/) | disallow unnecessary mustache interpolations | :wrench: | | [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: | | [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: | diff --git a/docs/rules.md b/docs/rules.md index c5f74fca0..047b2ab29 100644 --- a/docs/rules.md +++ b/docs/rules.md @@ -63,6 +63,7 @@ These rules relate to better ways of doing things to help you avoid problems: | [svelte/no-svelte-internal](./rules/no-svelte-internal.md) | svelte/internal will be removed in Svelte 6. | | | [svelte/no-unused-class-name](./rules/no-unused-class-name.md) | disallow the use of a class in the template without a corresponding style | | | [svelte/no-unused-svelte-ignore](./rules/no-unused-svelte-ignore.md) | disallow unused svelte-ignore comments | :star: | +| [svelte/no-useless-children-snippet](./rules/no-useless-children-snippet.md) | disallow explicit children snippet where it's not needed | | | [svelte/no-useless-mustaches](./rules/no-useless-mustaches.md) | disallow unnecessary mustache interpolations | :wrench: | | [svelte/prefer-const](./rules/prefer-const.md) | Require `const` declarations for variables that are never reassigned after declared | :wrench: | | [svelte/prefer-destructured-store-props](./rules/prefer-destructured-store-props.md) | destructure values from object stores for better change tracking & fewer redraws | :bulb: | diff --git a/docs/rules/no-useless-children-snippet.md b/docs/rules/no-useless-children-snippet.md new file mode 100644 index 000000000..b5e94e087 --- /dev/null +++ b/docs/rules/no-useless-children-snippet.md @@ -0,0 +1,57 @@ +--- +pageClass: 'rule-details' +sidebarDepth: 0 +title: 'svelte/no-useless-children-snippet' +description: "disallow explicit children snippet where it's not needed" +--- + +# svelte/no-useless-children-snippet + +> disallow explicit children snippet where it's not needed + +- :exclamation: **_This rule has not been released yet._** + +## :book: Rule Details + +Any content inside component tags that is not a snippet declaration implicitly becomes part of the children snippet. Thus, declaring the children snippet explicitly is only necessary when the snippet has parameters. + + + +```svelte + + + + + {#snippet bar()} + Hello + {/snippet} + + + + {#snippet children(val)} + Hello {val} + {/snippet} + + +Hello + + + + {#snippet children()} + Hello + {/snippet} + +``` + +## :wrench: Options + +Nothing. + +## :mag: Implementation + +- [Rule source](https://github.com/sveltejs/eslint-plugin-svelte/blob/main/packages/eslint-plugin-svelte/src/rules/no-useless-children-snippet.ts) +- [Test source](https://github.com/sveltejs/eslint-plugin-svelte/blob/main/packages/eslint-plugin-svelte/tests/src/rules/no-useless-children-snippet.ts) diff --git a/packages/eslint-plugin-svelte/src/rule-types.ts b/packages/eslint-plugin-svelte/src/rule-types.ts index 0492d4c37..b07451a1b 100644 --- a/packages/eslint-plugin-svelte/src/rule-types.ts +++ b/packages/eslint-plugin-svelte/src/rule-types.ts @@ -260,6 +260,11 @@ export interface RuleOptions { * @see https://sveltejs.github.io/eslint-plugin-svelte/rules/no-unused-svelte-ignore/ */ 'svelte/no-unused-svelte-ignore'?: Linter.RuleEntry<[]> + /** + * disallow explicit children snippet where it's not needed + * @see https://sveltejs.github.io/eslint-plugin-svelte/rules/no-useless-children-snippet/ + */ + 'svelte/no-useless-children-snippet'?: Linter.RuleEntry<[]> /** * disallow unnecessary mustache interpolations * @see https://sveltejs.github.io/eslint-plugin-svelte/rules/no-useless-mustaches/ diff --git a/packages/eslint-plugin-svelte/src/rules/no-useless-children-snippet.ts b/packages/eslint-plugin-svelte/src/rules/no-useless-children-snippet.ts new file mode 100644 index 000000000..a9e730bcf --- /dev/null +++ b/packages/eslint-plugin-svelte/src/rules/no-useless-children-snippet.ts @@ -0,0 +1,29 @@ +import { createRule } from '../utils/index.js'; + +export default createRule('no-useless-children-snippet', { + meta: { + docs: { + description: "disallow explicit children snippet where it's not needed", + category: 'Best Practices', + recommended: false + }, + schema: [], + messages: { + uselessSnippet: 'Found an unnecessary children snippet.' + }, + type: 'suggestion' + }, + create(context) { + return { + SvelteSnippetBlock(node) { + if ( + node.parent.type === 'SvelteElement' && + node.id.name === 'children' && + node.params.length === 0 + ) { + context.report({ node, messageId: 'uselessSnippet' }); + } + } + }; + } +}); diff --git a/packages/eslint-plugin-svelte/src/utils/rules.ts b/packages/eslint-plugin-svelte/src/utils/rules.ts index aab6375d9..cb0cbe896 100644 --- a/packages/eslint-plugin-svelte/src/utils/rules.ts +++ b/packages/eslint-plugin-svelte/src/utils/rules.ts @@ -51,6 +51,7 @@ import noTrailingSpaces from '../rules/no-trailing-spaces.js'; import noUnknownStyleDirectiveProperty from '../rules/no-unknown-style-directive-property.js'; import noUnusedClassName from '../rules/no-unused-class-name.js'; import noUnusedSvelteIgnore from '../rules/no-unused-svelte-ignore.js'; +import noUselessChildrenSnippet from '../rules/no-useless-children-snippet.js'; import noUselessMustaches from '../rules/no-useless-mustaches.js'; import preferClassDirective from '../rules/prefer-class-directive.js'; import preferConst from '../rules/prefer-const.js'; @@ -121,6 +122,7 @@ export const rules = [ noUnknownStyleDirectiveProperty, noUnusedClassName, noUnusedSvelteIgnore, + noUselessChildrenSnippet, noUselessMustaches, preferClassDirective, preferConst, diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-useless-children-snippet/invalid/_requirements.json b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-useless-children-snippet/invalid/_requirements.json new file mode 100644 index 000000000..498661308 --- /dev/null +++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-useless-children-snippet/invalid/_requirements.json @@ -0,0 +1,3 @@ +{ + "svelte": ">=5.0.0" +} diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-useless-children-snippet/invalid/children-snippet01-errors.yaml b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-useless-children-snippet/invalid/children-snippet01-errors.yaml new file mode 100644 index 000000000..1459ef6c5 --- /dev/null +++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-useless-children-snippet/invalid/children-snippet01-errors.yaml @@ -0,0 +1,4 @@ +- message: Found an unnecessary children snippet. + line: 2 + column: 3 + suggestions: null diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-useless-children-snippet/invalid/children-snippet01-input.svelte b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-useless-children-snippet/invalid/children-snippet01-input.svelte new file mode 100644 index 000000000..e1e35ee9a --- /dev/null +++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-useless-children-snippet/invalid/children-snippet01-input.svelte @@ -0,0 +1,5 @@ + + {#snippet children()} + Hello + {/snippet} + diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-useless-children-snippet/valid/_requirements.json b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-useless-children-snippet/valid/_requirements.json new file mode 100644 index 000000000..498661308 --- /dev/null +++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-useless-children-snippet/valid/_requirements.json @@ -0,0 +1,3 @@ +{ + "svelte": ">=5.0.0" +} diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-useless-children-snippet/valid/implicit-snippet01-input.svelte b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-useless-children-snippet/valid/implicit-snippet01-input.svelte new file mode 100644 index 000000000..a1285d01d --- /dev/null +++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-useless-children-snippet/valid/implicit-snippet01-input.svelte @@ -0,0 +1,3 @@ + + Hello + diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-useless-children-snippet/valid/named-snippet01-input.svelte b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-useless-children-snippet/valid/named-snippet01-input.svelte new file mode 100644 index 000000000..16adcc4c9 --- /dev/null +++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-useless-children-snippet/valid/named-snippet01-input.svelte @@ -0,0 +1,5 @@ + + {#snippet bar()} + Hello + {/snippet} + diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-useless-children-snippet/valid/snippet-with-params01-input.svelte b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-useless-children-snippet/valid/snippet-with-params01-input.svelte new file mode 100644 index 000000000..b4218676f --- /dev/null +++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-useless-children-snippet/valid/snippet-with-params01-input.svelte @@ -0,0 +1,5 @@ + + {#snippet children(val)} + Hello {val} + {/snippet} + diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-useless-children-snippet/valid/standalone-snippet01-input.svelte b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-useless-children-snippet/valid/standalone-snippet01-input.svelte new file mode 100644 index 000000000..29bba271a --- /dev/null +++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-useless-children-snippet/valid/standalone-snippet01-input.svelte @@ -0,0 +1,5 @@ +{#snippet children()} + Hello +{/snippet} + +{@render children()} diff --git a/packages/eslint-plugin-svelte/tests/src/rules/no-useless-children-snippet.ts b/packages/eslint-plugin-svelte/tests/src/rules/no-useless-children-snippet.ts new file mode 100644 index 000000000..90fae266d --- /dev/null +++ b/packages/eslint-plugin-svelte/tests/src/rules/no-useless-children-snippet.ts @@ -0,0 +1,12 @@ +import { RuleTester } from '../../utils/eslint-compat'; +import rule from '../../../src/rules/no-useless-children-snippet'; +import { loadTestCases } from '../../utils/utils'; + +const tester = new RuleTester({ + languageOptions: { + ecmaVersion: 2020, + sourceType: 'module' + } +}); + +tester.run('no-useless-children-snippet', rule as any, loadTestCases('no-useless-children-snippet'));