Skip to content

Commit

Permalink
Added the require-event-dispatcher-types rule (#354)
Browse files Browse the repository at this point in the history
Co-authored-by: Yosuke Ota <otameshiyo23@gmail.com>
  • Loading branch information
marekdedic and ota-meshi committed Feb 4, 2023
1 parent 3464f23 commit 2f1d89a
Show file tree
Hide file tree
Showing 15 changed files with 170 additions and 0 deletions.
5 changes: 5 additions & 0 deletions .changeset/witty-donuts-jog.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"eslint-plugin-svelte": minor
---

Added the require-event-dispatcher-types rule
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -336,6 +336,7 @@ These rules relate to better ways of doing things to help you avoid problems:
| [svelte/no-unused-svelte-ignore](https://ota-meshi.github.io/eslint-plugin-svelte/rules/no-unused-svelte-ignore/) | disallow unused svelte-ignore comments | :star: |
| [svelte/no-useless-mustaches](https://ota-meshi.github.io/eslint-plugin-svelte/rules/no-useless-mustaches/) | disallow unnecessary mustache interpolations | :wrench: |
| [svelte/prefer-destructured-store-props](https://ota-meshi.github.io/eslint-plugin-svelte/rules/prefer-destructured-store-props/) | destructure values from object stores for better change tracking & fewer redraws | :bulb: |
| [svelte/require-event-dispatcher-types](https://ota-meshi.github.io/eslint-plugin-svelte/rules/require-event-dispatcher-types/) | require type parameters for createEventDispatcher | |
| [svelte/require-optimized-style-attribute](https://ota-meshi.github.io/eslint-plugin-svelte/rules/require-optimized-style-attribute/) | require style attributes that can be optimized | |
| [svelte/require-stores-init](https://ota-meshi.github.io/eslint-plugin-svelte/rules/require-stores-init/) | require initial value in store | |

Expand Down
1 change: 1 addition & 0 deletions docs/rules.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ These rules relate to better ways of doing things to help you avoid problems:
| [svelte/no-unused-svelte-ignore](./rules/no-unused-svelte-ignore.md) | disallow unused svelte-ignore comments | :star: |
| [svelte/no-useless-mustaches](./rules/no-useless-mustaches.md) | disallow unnecessary mustache interpolations | :wrench: |
| [svelte/prefer-destructured-store-props](./rules/prefer-destructured-store-props.md) | destructure values from object stores for better change tracking & fewer redraws | :bulb: |
| [svelte/require-event-dispatcher-types](./rules/require-event-dispatcher-types.md) | require type parameters for createEventDispatcher | |
| [svelte/require-optimized-style-attribute](./rules/require-optimized-style-attribute.md) | require style attributes that can be optimized | |
| [svelte/require-stores-init](./rules/require-stores-init.md) | require initial value in store | |

Expand Down
50 changes: 50 additions & 0 deletions docs/rules/require-event-dispatcher-types.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
---
pageClass: "rule-details"
sidebarDepth: 0
title: "svelte/require-event-dispatcher-types"
description: "require type parameters for createEventDispatcher"
---

# svelte/require-event-dispatcher-types

> require type parameters for createEventDispatcher
## :book: Rule Details

This rule is aimed to enforce type parameters when calling `createEventDispatcher`. Adding types makes all `dispatch` calls as well as all event listeners typechecked. For more information, see the [svelte docs](https://github.com/sveltejs/language-tools/blob/master/docs/preprocessors/typescript.md#typing-component-events).

<ESLintCodeBlock language="javascript">

<!--eslint-skip-->

```svelte
<script lang="ts">
/* eslint svelte/require-event-dispatcher-types: "error" */
import { createEventDispatcher } from "svelte"
/* ✓ GOOD */
const dispatch1 = createEventDispatcher<{ one: never; two: number }>()
const dispatch2 = createEventDispatcher<Record<string, never>>()
const dispatch3 = createEventDispatcher<any>()
const dispatch4 = createEventDispatcher<unknown>()
/* ✗ BAD */
const dispatch5 = createEventDispatcher()
</script>
```

</ESLintCodeBlock>

## :wrench: Options

Nothing.

## :rocket: Version

This rule was introduced in eslint-plugin-svelte v2.16.0

## :mag: Implementation

- [Rule source](https://github.com/ota-meshi/eslint-plugin-svelte/blob/main/src/rules/require-event-dispatcher-types.ts)
- [Test source](https://github.com/ota-meshi/eslint-plugin-svelte/blob/main/tests/src/rules/require-event-dispatcher-types.ts)
20 changes: 20 additions & 0 deletions src/rules/reference-helpers/svelte-createEventDispatcher.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import type { TSESTree } from "@typescript-eslint/types"
import { ReferenceTracker } from "eslint-utils"
import type { RuleContext } from "../../types"

/** Extract 'svelte createEventDispatcher' references */
export function* extractCreateEventDispatcherReferences(
context: RuleContext,
): Generator<TSESTree.CallExpression, void> {
const referenceTracker = new ReferenceTracker(context.getScope())
for (const { node } of referenceTracker.iterateEsmReferences({
svelte: {
[ReferenceTracker.ESM]: true,
createEventDispatcher: {
[ReferenceTracker.CALL]: true,
},
},
})) {
yield node as TSESTree.CallExpression
}
}
39 changes: 39 additions & 0 deletions src/rules/require-event-dispatcher-types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { createRule } from "../utils"
import { getLangValue } from "../utils/ast-utils"
import { extractCreateEventDispatcherReferences } from "./reference-helpers/svelte-createEventDispatcher"

export default createRule("require-event-dispatcher-types", {
meta: {
docs: {
description: "require type parameters for createEventDispatcher",
category: "Best Practices",
recommended: false,
},
schema: [],
messages: {
missingTypeParameter: `Type parameters missing for the createEventDispatcher function call.`,
},
type: "suggestion",
},
create(context) {
let isTs = false
return {
SvelteScriptElement(node) {
const lang = getLangValue(node)?.toLowerCase()
if (lang === "ts" || lang === "typescript") {
isTs = true
}
},
"Program:exit"() {
if (!isTs) {
return
}
for (const node of extractCreateEventDispatcherReferences(context)) {
if (node.typeParameters === undefined) {
context.report({ node, messageId: "missingTypeParameter" })
}
}
},
}
},
})
2 changes: 2 additions & 0 deletions src/utils/rules.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import noUselessMustaches from "../rules/no-useless-mustaches"
import preferClassDirective from "../rules/prefer-class-directive"
import preferDestructuredStoreProps from "../rules/prefer-destructured-store-props"
import preferStyleDirective from "../rules/prefer-style-directive"
import requireEventDispatcherTypes from "../rules/require-event-dispatcher-types"
import requireOptimizedStyleAttribute from "../rules/require-optimized-style-attribute"
import requireStoreCallbacksUseSetParam from "../rules/require-store-callbacks-use-set-param"
import requireStoreReactiveAccess from "../rules/require-store-reactive-access"
Expand Down Expand Up @@ -86,6 +87,7 @@ export const rules = [
preferClassDirective,
preferDestructuredStoreProps,
preferStyleDirective,
requireEventDispatcherTypes,
requireOptimizedStyleAttribute,
requireStoreCallbacksUseSetParam,
requireStoreReactiveAccess,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
- message: Type parameters missing for the createEventDispatcher function call.
line: 4
column: 20
suggestions: null
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<script lang="ts">
import { createEventDispatcher as ced } from "svelte"
const dispatch = ced()
</script>
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
- message: Type parameters missing for the createEventDispatcher function call.
line: 4
column: 20
suggestions: null
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<script lang="ts">
import { createEventDispatcher } from "svelte"
const dispatch = createEventDispatcher()
</script>
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<script lang="ts">
import { createEventDispatcher } from "svelte"
const dispatch1 = createEventDispatcher<{ one: never; two: number }>()
const dispatch2 = createEventDispatcher<Record<string, never>>()
const dispatch3 = createEventDispatcher<any>()
const dispatch4 = createEventDispatcher<unknown>()
</script>
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<script>
import { createEventDispatcher } from "svelte"
const dispatch = createEventDispatcher()
</script>
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<script lang="ts">
import { createEventDispatcher } from "./unknown"
const dispatch = createEventDispatcher()
</script>
16 changes: 16 additions & 0 deletions tests/src/rules/require-event-dispatcher-types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { RuleTester } from "eslint"
import rule from "../../../src/rules/require-event-dispatcher-types"
import { loadTestCases } from "../../utils/utils"

const tester = new RuleTester({
parserOptions: {
ecmaVersion: 2020,
sourceType: "module",
},
})

tester.run(
"require-event-dispatcher-types",
rule as any,
loadTestCases("require-event-dispatcher-types"),
)

0 comments on commit 2f1d89a

Please sign in to comment.