Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

docs: add guide for building ESLint plugins#9684

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes fromall commits
Commits
Show all changes
12 commits
Select commitHold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 23 additions & 25 deletionsdocs/developers/Custom_Rules.mdx
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -10,11 +10,12 @@ You should be familiar with [ESLint's developer guide](https://eslint.org/docs/d
:::

As long as you are using `@typescript-eslint/parser` as the `parser` in your ESLint configuration, custom ESLint rules generally work the same way for JavaScript and TypeScript code.
The mainthree changes to custom rules writing are:
The mainfour changes to custom rules writing are:

- [Utils Package](#utils-package): we recommend using `@typescript-eslint/utils` to create custom rules
- [AST Extensions](#ast-extensions): targeting TypeScript-specific syntax in your rule selectors
- [Typed Rules](#typed-rules): using the TypeScript type checker to inform rule logic
- [Testing](#testing): using `@typescript-eslint/rule-tester`'s `RuleTester` instead of ESLint core's

## Utils Package

Expand DownExpand Up@@ -305,7 +306,6 @@ This rule bans for-of looping over an enum by using the TypeScript type checker

```ts
import { ESLintUtils } from '@typescript-eslint/utils';
import * as tsutils from 'ts-api-utils';
Copy link
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

There's really no need to ask users to load in a full third-party-package for a single enum helper function. Especially not for a package outside of the org's ownership.

import * as ts from 'typescript';

export const rule = createRule({
Expand All@@ -316,10 +316,10 @@ export const rule = createRule({
const services = ESLintUtils.getParserServices(context);

// 2. Find the TS type for the ES node
const type = services.getTypeAtLocation(node);
const type = services.getTypeAtLocation(node.right);

// 3. Check the TS type using the TypeScript APIs
if (tsutils.isTypeFlagSet(type,ts.TypeFlags.EnumLike)) {
// 3. Check the TS type's backing symbol for being an enum
if (type.symbol.flags &ts.SymbolFlags.Enum) {
context.report({
messageId: 'loopOverEnum',
node: node.right,
Expand DownExpand Up@@ -348,11 +348,15 @@ Rules can retrieve their full backing TypeScript type checker with `services.pro
This can be necessary for TypeScript APIs not wrapped by the parser services.
:::

:::caution
We recommend against changing rule logic based solely on whether `services.program` exists.
### Conditional Type Information

We recommend _against_ changing rule logic based solely on whether `services.program` exists.
In our experience, users are generally surprised when rules behave differently with or without type information.
Additionally, if they misconfigure their ESLint config, they may not realize why the rule started behaving differently.
Consider either gating type checking behind an explicit option for the rule or creating two versions of the rule instead.

:::tip
Documentation generators such as [`eslint-doc-generator`](https://github.com/bmish/eslint-doc-generator) can automatically indicate in a rule's docs whether it needs type information.
:::

## Testing
Expand All@@ -364,15 +368,13 @@ Below is a quick-start guide. For more in-depth docs and examples [see the `@typ

### Testing Untyped Rules

For rules that don't need type information,passing just the `parser` will do:
For rules that don't need type information,no constructor parameters are necessary:

```ts
import { RuleTester } from '@typescript-eslint/rule-tester';
import rule from './my-rule';

const ruleTester = new RuleTester({
parser: '@typescript-eslint/parser',
});
const ruleTester = new RuleTester();

ruleTester.run('my-rule', rule, {
valid: [
Expand All@@ -387,17 +389,21 @@ ruleTester.run('my-rule', rule, {
### Testing Typed Rules

For rules that do need type information, `parserOptions` must be passed in as well.
Tests must have at least an absolute `tsconfigRootDir` path provided as well as a relative `project` path from that directory:
We recommend using `parserOptions.projectService` with options to allow a defaultproject for each test file.

```ts
import { RuleTester } from '@typescript-eslint/rule-tester';
import rule from './my-typed-rule';

const ruleTester = new RuleTester({
parser: '@typescript-eslint/parser',
parserOptions: {
project: './tsconfig.json',
tsconfigRootDir: __dirname,
languageOptions: {
parserOptions: {
projectService: {
allowDefaultProjectForFiles: ['*.ts*'],
defaultProject: 'tsconfig.json',
},
tsconfigRootDir: __dirname,
},
},
});

Expand All@@ -411,12 +417,4 @@ ruleTester.run('my-typed-rule', rule, {
});
```

:::note
For now, `RuleTester` requires the following physical files be present on disk for typed rules:

- `tsconfig.json`: tsconfig used as the test "project"
- One of the following two files:
- `file.ts`: blank test file used for normal TS tests
- `react.tsx`: blank test file used for tests with `parserOptions: { ecmaFeatures: { jsx: true } }`

:::
See [_Rule Tester_ > _Type-Aware Testing_](../packages/Rule_Tester.mdx#type-aware-testing) for more details.
91 changes: 91 additions & 0 deletionsdocs/developers/ESLint_Plugins.mdx
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
---
id: eslint-plugins
sidebar_label: Building ESLint Plugins
title: ESLint Plugins
---

:::important
This page describes how to write your own custom ESLint plugins using typescript-eslint.
You should be familiar with [ESLint's plugins guide](https://eslint.org/docs/latest/extend/plugins) and [typescript-eslint Custom Rules](./Custom_Rules.mdx) before writing custom plugins.
:::

Custom plugins that support TypeScript code and typed linting look very similar to any other ESLint plugin.
Follow the same general steps as [ESLint's plugins guide > _Creating a plugin_](https://eslint.org/docs/latest/extend/plugins#creating-a-plugin) to set up your plugin.
The required differences are noted on this page.

:::tip
See [**`eslint-plugin-example-typed-linting`**](https://github.com/typescript-eslint/examples/tree/main/packages/eslint-plugin-example-typed-linting) for an example plugin that supports typed linting.
:::

## Package Dependencies

Your plugin should have the following `package.json` entries.

For all `@typescript-eslint` and `typescript-eslint` packages, keep them at the same semver versions.
As an example, you might set each of them to `^8.1.2` or `^7.12.0 || ^8.0.0`.

### `dependencies`

[`@typescript-eslint/utils`](../packages/Utils.mdx) is required for the [`RuleCreator` factory to create rules](#rulecreator-usage).

### `devDependencies`

[`@typescript-eslint/rule-tester`](../packages/Rule_Tester.mdx) is strongly recommended to be able to [test rules with our `RuleTester`](./Custom_Rules.mdx).

### `peerDependencies`

Include the following to enforce the version range allowed without making users' package managers install them:

- `@typescript-eslint/parser` and any other parsers users are expected to be using
- `eslint`
- `typescript`

Those are all packages consumers are expected to be using already.

## `RuleCreator` Usage

We recommend including at least the following three properties in your plugin's [`RuleCreator` extra rule docs types](./Custom_Rules.mdx#extra-rule-docs-types):

- `description: string`: a succinct description of what the rule does
- `recommended?: boolean`: whether the rule exists in your plugin's shared _"`recommended`"_ config
- `requiresTypeChecking?: boolean`: whether the rule will use type information, for documentation generators such as [`eslint-doc-generator`](https://github.com/bmish/eslint-doc-generator)

For example, from [`eslint-plugin-example-typed-linting`'s `utils.ts`](https://github.com/typescript-eslint/examples/blob/main/packages/eslint-plugin-example-typed-linting/src/utils.ts):

```ts
import { ESLintUtils } from '@typescript-eslint/utils';

export interface ExamplePluginDocs {
description: string;
recommended?: boolean;
requiresTypeChecking?: boolean;
}

export const createRule = ESLintUtils.RuleCreator<ExamplePluginDocs>(
name =>
`https://github.com/your/eslint-plugin-example/tree/main/docs/${name}.md`,
);
```

## Type Checking and Configs

Most ESLint plugins export a _"`recommended`"_ [ESLint shared config](https://eslint.org/docs/latest/extend/shareable-configs).
Many ESLint users assume enabling a plugin's `recommended` config is enough to enable all its relevant rules.

However, at the same time, not all users want to or are able to enabled typed linting.
If your plugin's rules heavily use type information, it might be difficult to enable those in a `recommended` config.

You have roughly two options:

- Have your plugin's `recommended` config require enabling type information
- Have a separate config with a name like `recommendedTypeChecked`

Either way, explicitly mention the strategy taken in your docs.

:::info
Per [_Custom Rules_ > _Conditional Type Information_](./Custom_Rules.mdx#conditional-type-information), we recommend not changing rule logic based on whether type information is available.
:::

:::tip
See [**`eslint-plugin-example-typed-linting`**](https://github.com/typescript-eslint/examples/tree/main/packages/eslint-plugin-example-typed-linting) for an example plugin that supports typed linting.
:::
43 changes: 19 additions & 24 deletionsdocs/packages/Rule_Tester.mdx
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -36,9 +36,11 @@ ruleTester.run('my-rule', rule, {
// you can enable JSX parsing by passing parserOptions.ecmaFeatures.jsx = true
{
code: 'const z = <div />;',
parserOptions: {
ecmaFeatures: {
jsx: true,
languageOptions: {
parserOptions: {
ecmaFeatures: {
jsx: true,
},
},
},
},
Expand DownExpand Up@@ -120,26 +122,11 @@ ruleTester.run('my-rule', rule, {

### Type-Aware Testing

Type-aware rules can be tested in almost exactly the same way, except you need to create some files on disk.
We require files on disk due to a limitation with TypeScript in that it requires physical files on disk to initialize the project.
We suggest creating a `fixture` folder nearby that contains three files:

1. `file.ts` - this should be an empty file.
2. `react.tsx` - this should be an empty file.
3. `tsconfig.json` - this should be the config to use for your test, for example:
```json
{
"compilerOptions": {
"strict": true
},
"include": ["file.ts", "react.tsx"]
}
```

:::caution
It's important to note that both `file.ts` and `react.tsx` must both be empty files!
The rule tester will automatically use the string content from your tests - the empty files are just there for initialization.
:::
Type-aware rules can be tested in almost exactly the same way as regular code, using `parserOptions.projectService`.
Most rule tests can use settings like:

- `allowDefaultProjectForFiles: ["*.ts*"]`: to include files in your tests
- `defaultProject: "tsconfig.json"`: to use the same TSConfig as other files

You can then test your rule by providing the type-aware config:

Expand All@@ -148,8 +135,11 @@ const ruleTester = new RuleTester({
// Added lines start
languageOptions: {
parserOptions: {
projectServices: {
allowDefaultProject: ['*.ts*'],
defaultProject: 'tsconfig.json',
},
tsconfigRootDir: './path/to/your/folder/fixture',
project: './tsconfig.json',
},
},
// Added lines end
Expand All@@ -158,6 +148,11 @@ const ruleTester = new RuleTester({

With that config the parser will automatically run in type-aware mode and you can write tests just like before.

When not specified with a `filename` option, `RuleTester` uses the following test file names:

- `file.ts`: by default
- `react.tsx`: if `parserOptions.ecmaFeatures.jsx` is enabled

### Test Dependency Constraints

Sometimes it's desirable to test your rule against multiple versions of a dependency to ensure backwards and forwards compatibility.
Expand Down
2 changes: 1 addition & 1 deletionpackages/website/sidebars/sidebar.base.js
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -88,7 +88,7 @@ module.exports = {
},
{
collapsible: false,
items: ['developers/custom-rules'],
items: ['developers/custom-rules', 'developers/eslint-plugins'],
label: 'Developers',
link: {
id: 'developers',
Expand Down

[8]ページ先頭

©2009-2025 Movatter.jp