From fcb5e319620b1cc71ffe579760033813bd719410 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20D=C4=9Bdi=C4=8D?= Date: Fri, 10 Feb 2023 14:17:50 +0100 Subject: [PATCH] feat: Added the experimental-require-slot-types rule (#368) Co-authored-by: ota-meshi --- .changeset/strong-wombats-worry.md | 5 + README.md | 1 + docs/rules.md | 1 + docs/rules/experimental-require-slot-types.md | 113 ++++++++++++++++++ src/rules/experimental-require-slot-types.ts | 50 ++++++++ src/utils/rules.ts | 2 + .../invalid/no-slot-types01-errors.yaml | 4 + .../invalid/no-slot-types01-input.svelte | 4 + .../valid/has-slot-types01-input.svelte | 7 ++ .../valid/named-slot01-input.svelte | 7 ++ .../valid/no-slots01-input.svelte | 4 + .../valid/no-typescript01-input.svelte | 4 + .../rules/experimental-require-slot-types.ts | 16 +++ 13 files changed, 218 insertions(+) create mode 100644 .changeset/strong-wombats-worry.md create mode 100644 docs/rules/experimental-require-slot-types.md create mode 100644 src/rules/experimental-require-slot-types.ts create mode 100644 tests/fixtures/rules/experimental-require-slot-types/invalid/no-slot-types01-errors.yaml create mode 100644 tests/fixtures/rules/experimental-require-slot-types/invalid/no-slot-types01-input.svelte create mode 100644 tests/fixtures/rules/experimental-require-slot-types/valid/has-slot-types01-input.svelte create mode 100644 tests/fixtures/rules/experimental-require-slot-types/valid/named-slot01-input.svelte create mode 100644 tests/fixtures/rules/experimental-require-slot-types/valid/no-slots01-input.svelte create mode 100644 tests/fixtures/rules/experimental-require-slot-types/valid/no-typescript01-input.svelte create mode 100644 tests/src/rules/experimental-require-slot-types.ts diff --git a/.changeset/strong-wombats-worry.md b/.changeset/strong-wombats-worry.md new file mode 100644 index 000000000..aa6742bea --- /dev/null +++ b/.changeset/strong-wombats-worry.md @@ -0,0 +1,5 @@ +--- +"eslint-plugin-svelte": minor +--- + +feat: added the `svelte/experimental-require-slot-types` rule diff --git a/README.md b/README.md index 1919fcf15..4ae387077 100644 --- a/README.md +++ b/README.md @@ -379,6 +379,7 @@ These rules extend the rules provided by ESLint itself, or other plugins to work | Rule ID | Description | | |:--------|:------------|:---| +| [svelte/experimental-require-slot-types](https://ota-meshi.github.io/eslint-plugin-svelte/rules/experimental-require-slot-types/) | require slot type declaration using the `$$Slots` interface | | | [svelte/experimental-require-strict-events](https://ota-meshi.github.io/eslint-plugin-svelte/rules/experimental-require-strict-events/) | require the strictEvents attribute on ` + +No slots here! +``` + + + + + + + +```svelte + + + + +``` + + + + + + + +```svelte + + + + +``` + + + + + + + +```svelte + + + + +``` + + + + + + + +```svelte + + + + +``` + + + +## :wrench: Options + +Nothing. + +## :mag: Implementation + +- [Rule source](https://github.com/ota-meshi/eslint-plugin-svelte/blob/main/src/rules/experimental-require-slot-types.ts) +- [Test source](https://github.com/ota-meshi/eslint-plugin-svelte/blob/main/tests/src/rules/experimental-require-slot-types.ts) diff --git a/src/rules/experimental-require-slot-types.ts b/src/rules/experimental-require-slot-types.ts new file mode 100644 index 000000000..32f31ae5c --- /dev/null +++ b/src/rules/experimental-require-slot-types.ts @@ -0,0 +1,50 @@ +import { createRule } from "../utils" +import { getLangValue } from "../utils/ast-utils" + +export default createRule("experimental-require-slot-types", { + meta: { + docs: { + description: + "require slot type declaration using the `$$Slots` interface", + category: "Experimental", + recommended: false, + }, + schema: [], + messages: { + missingSlotsInterface: `The component must define the $$Slots interface.`, + }, + type: "suggestion", + }, + create(context) { + let isTs = false + let hasSlot = false + let hasInterface = false + return { + SvelteScriptElement(node) { + const lang = getLangValue(node)?.toLowerCase() + isTs = lang === "ts" || lang === "typescript" + }, + SvelteElement(node) { + if (node.name.type === "SvelteName" && node.name.name === "slot") { + hasSlot = true + } + }, + TSInterfaceDeclaration(node) { + if (node.id.name === "$$Slots") { + hasInterface = true + } + }, + "Program:exit"() { + if (isTs && hasSlot && !hasInterface) { + context.report({ + loc: { + line: 1, + column: 1, + }, + messageId: "missingSlotsInterface", + }) + } + }, + } + }, +}) diff --git a/src/utils/rules.ts b/src/utils/rules.ts index 5d051d4ba..bc8c4813c 100644 --- a/src/utils/rules.ts +++ b/src/utils/rules.ts @@ -3,6 +3,7 @@ import typescriptEslintNoUnnecessaryCondition from "../rules/@typescript-eslint/ import buttonHasType from "../rules/button-has-type" import commentDirective from "../rules/comment-directive" import derivedHasSameInputsOutputs from "../rules/derived-has-same-inputs-outputs" +import experimentalRequireSlotTypes from "../rules/experimental-require-slot-types" import experimentalRequireStrictEvents from "../rules/experimental-require-strict-events" import firstAttributeLinebreak from "../rules/first-attribute-linebreak" import htmlClosingBracketSpacing from "../rules/html-closing-bracket-spacing" @@ -56,6 +57,7 @@ export const rules = [ buttonHasType, commentDirective, derivedHasSameInputsOutputs, + experimentalRequireSlotTypes, experimentalRequireStrictEvents, firstAttributeLinebreak, htmlClosingBracketSpacing, diff --git a/tests/fixtures/rules/experimental-require-slot-types/invalid/no-slot-types01-errors.yaml b/tests/fixtures/rules/experimental-require-slot-types/invalid/no-slot-types01-errors.yaml new file mode 100644 index 000000000..628f8ecb1 --- /dev/null +++ b/tests/fixtures/rules/experimental-require-slot-types/invalid/no-slot-types01-errors.yaml @@ -0,0 +1,4 @@ +- message: The component must define the $$Slots interface. + line: 1 + column: 2 + suggestions: null diff --git a/tests/fixtures/rules/experimental-require-slot-types/invalid/no-slot-types01-input.svelte b/tests/fixtures/rules/experimental-require-slot-types/invalid/no-slot-types01-input.svelte new file mode 100644 index 000000000..2c02c7ef5 --- /dev/null +++ b/tests/fixtures/rules/experimental-require-slot-types/invalid/no-slot-types01-input.svelte @@ -0,0 +1,4 @@ + + + diff --git a/tests/fixtures/rules/experimental-require-slot-types/valid/has-slot-types01-input.svelte b/tests/fixtures/rules/experimental-require-slot-types/valid/has-slot-types01-input.svelte new file mode 100644 index 000000000..c902e4a06 --- /dev/null +++ b/tests/fixtures/rules/experimental-require-slot-types/valid/has-slot-types01-input.svelte @@ -0,0 +1,7 @@ + + + diff --git a/tests/fixtures/rules/experimental-require-slot-types/valid/named-slot01-input.svelte b/tests/fixtures/rules/experimental-require-slot-types/valid/named-slot01-input.svelte new file mode 100644 index 000000000..ac2713e44 --- /dev/null +++ b/tests/fixtures/rules/experimental-require-slot-types/valid/named-slot01-input.svelte @@ -0,0 +1,7 @@ + + + diff --git a/tests/fixtures/rules/experimental-require-slot-types/valid/no-slots01-input.svelte b/tests/fixtures/rules/experimental-require-slot-types/valid/no-slots01-input.svelte new file mode 100644 index 000000000..d0857d009 --- /dev/null +++ b/tests/fixtures/rules/experimental-require-slot-types/valid/no-slots01-input.svelte @@ -0,0 +1,4 @@ + + +content diff --git a/tests/fixtures/rules/experimental-require-slot-types/valid/no-typescript01-input.svelte b/tests/fixtures/rules/experimental-require-slot-types/valid/no-typescript01-input.svelte new file mode 100644 index 000000000..9cc1fe581 --- /dev/null +++ b/tests/fixtures/rules/experimental-require-slot-types/valid/no-typescript01-input.svelte @@ -0,0 +1,4 @@ + + + diff --git a/tests/src/rules/experimental-require-slot-types.ts b/tests/src/rules/experimental-require-slot-types.ts new file mode 100644 index 000000000..af9978d93 --- /dev/null +++ b/tests/src/rules/experimental-require-slot-types.ts @@ -0,0 +1,16 @@ +import { RuleTester } from "eslint" +import rule from "../../../src/rules/experimental-require-slot-types" +import { loadTestCases } from "../../utils/utils" + +const tester = new RuleTester({ + parserOptions: { + ecmaVersion: 2020, + sourceType: "module", + }, +}) + +tester.run( + "experimental-require-slot-types", + rule as any, + loadTestCases("experimental-require-slot-types"), +)