diff --git a/.cspell.json b/.cspell.json index 0516d1196d70..898e10932518 100644 --- a/.cspell.json +++ b/.cspell.json @@ -15,6 +15,7 @@ "**/node_modules/**", "packages/website/.docusaurus/**", "packages/website/build/**", + "packages/website/src/vendor/**", "yarn.lock" ], "dictionaries": [ @@ -41,8 +42,8 @@ "Airbnb", "Airbnb's", "ambiently", - "ASTs", "astexplorer", + "ASTs", "autofix", "autofixers", "autofixes", @@ -63,6 +64,7 @@ "ESLint", "ESLint's", "espree", + "esquery", "esrecurse", "estree", "IDE's", @@ -70,6 +72,7 @@ "IIFEs", "linebreaks", "markdownlint", + "lzstring", "necroing", "nocheck", "nullish", @@ -98,6 +101,7 @@ "transpiled", "transpiles", "transpiling", + "tsvfs", "tsconfigs", "tsutils", "typedef", diff --git a/.eslintignore b/.eslintignore index d47d9f2f23e4..b9323511dcb4 100644 --- a/.eslintignore +++ b/.eslintignore @@ -6,11 +6,15 @@ fixtures shared-fixtures coverage __snapshots__ +.docusaurus +build packages/eslint-plugin-tslint/tests packages/website/**/*.js packages/website/**/*.d.ts +packages/website-eslint/**/*.js +packages/website-eslint/**/*.d.ts # Files copied as part of the build packages/types/src/ast-spec.ts diff --git a/.eslintrc.js b/.eslintrc.js index 303f8d18b2a8..e131f7cb8f2f 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -205,7 +205,7 @@ module.exports = { 'jest/no-deprecated-functions': 'error', }, }, - // test utility scripts + // test utility scripts and website js files { files: ['tests/**/*.js'], rules: { diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index ffe68ca36030..450026bc51f3 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -15,7 +15,7 @@ The more relevant information you can include, the faster we can find the issue - [ ] I have tried restarting my IDE and the issue persists. - [ ] I have updated to the latest version of the packages. -- [ ] I have [read the FAQ](https://github.com/typescript-eslint/typescript-eslint/blob/master/docs/getting-started/linting/TROUBLESHOOTING.md) and my problem is not listed. +- [ ] I have [read the FAQ](https://github.com/typescript-eslint/typescript-eslint/blob/main/docs/linting/TROUBLESHOOTING.md) and my problem is not listed. **Repro** diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index 170b6977bc37..28f08c8cd892 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -3,8 +3,8 @@ contact_links: - name: FAQ about: Please check out our FAQ before filing new issues - url: https://github.com/typescript-eslint/typescript-eslint/blob/master/docs/getting-started/linting/TROUBLESHOOTING.md + url: https://github.com/typescript-eslint/typescript-eslint/blob/main/docs/linting/TROUBLESHOOTING.md - name: Getting Started Guide about: If you're looking for help setting up check out our getting started guide - url: https://github.com/typescript-eslint/typescript-eslint/tree/master/docs/getting-started + url: https://github.com/typescript-eslint/typescript-eslint/tree/main/docs/getting-started diff --git a/.github/ISSUE_TEMPLATE/documentation-request.md b/.github/ISSUE_TEMPLATE/documentation-request.md index bdd9aaa0fea1..be101fc13555 100644 --- a/.github/ISSUE_TEMPLATE/documentation-request.md +++ b/.github/ISSUE_TEMPLATE/documentation-request.md @@ -14,7 +14,7 @@ The more relevant information you can include, the faster we can find the issue --> - [ ] I have looked on [typescript-eslint.io](https://typescript-eslint.io). -- [ ] I have [read the FAQ](https://github.com/typescript-eslint/typescript-eslint/blob/master/docs/getting-started/linting/FAQ.md) and my problem is not listed. +- [ ] I have [read the FAQ](https://github.com/typescript-eslint/typescript-eslint/blob/main/docs/linting/TROUBLESHOOTING.md) and my problem is not listed. ## Suggested Changes diff --git a/.github/ISSUE_TEMPLATE/eslint-plugin-tslint.md b/.github/ISSUE_TEMPLATE/eslint-plugin-tslint.md index bcdc64856058..4be209dcc492 100644 --- a/.github/ISSUE_TEMPLATE/eslint-plugin-tslint.md +++ b/.github/ISSUE_TEMPLATE/eslint-plugin-tslint.md @@ -23,7 +23,7 @@ If you have a problem with a specific lint rule, please back out and select the - [ ] I have tried restarting my IDE and the issue persists. - [ ] I have updated to the latest version of the packages. -- [ ] I have [read the FAQ](https://github.com/typescript-eslint/typescript-eslint/blob/master/docs/getting-started/linting/TROUBLESHOOTING.md) and my problem is not listed. +- [ ] I have [read the FAQ](https://github.com/typescript-eslint/typescript-eslint/blob/main/docs/linting/TROUBLESHOOTING.md) and my problem is not listed. **Repro** diff --git a/.github/ISSUE_TEMPLATE/eslint-plugin-typescript.md b/.github/ISSUE_TEMPLATE/eslint-plugin-typescript.md index aa0b8f2fd960..ebd95cd8b98b 100644 --- a/.github/ISSUE_TEMPLATE/eslint-plugin-typescript.md +++ b/.github/ISSUE_TEMPLATE/eslint-plugin-typescript.md @@ -22,13 +22,13 @@ Are you opening an issue because the rule you're trying to use is not found? 1) Check the releases log: https://github.com/typescript-eslint/typescript-eslint/releases - If the rule isn't listed there, then chances are it hasn't been released to the main npm tag yet. 2) Try installing the `canary` tag: `npm i @typescript-eslint/eslint-plugin@canary`. - - The canary tag is built for every commit to master, so it contains the bleeding edge build. + - The canary tag is built for every commit to main, so it contains the bleeding edge build. 3) If ESLint still can't find the rule, then consider reporting an issue. --> - [ ] I have tried restarting my IDE and the issue persists. - [ ] I have updated to the latest version of the packages. -- [ ] I have [read the FAQ](https://github.com/typescript-eslint/typescript-eslint/blob/master/docs/getting-started/linting/TROUBLESHOOTING.md) and my problem is not listed. +- [ ] I have [read the FAQ](https://github.com/typescript-eslint/typescript-eslint/blob/main/docs/linting/TROUBLESHOOTING.md) and my problem is not listed. **Repro** diff --git a/.github/ISSUE_TEMPLATE/eslint-plugin-typescript.yml b/.github/ISSUE_TEMPLATE/eslint-plugin-typescript.yml index 36ab5d7985f9..462e59558faa 100644 --- a/.github/ISSUE_TEMPLATE/eslint-plugin-typescript.yml +++ b/.github/ISSUE_TEMPLATE/eslint-plugin-typescript.yml @@ -11,7 +11,7 @@ body: value: | Before opening a new issue: - Look for existing [open or closed rule proposals](https://github.com/typescript-eslint/typescript-eslint/issues?q=label%3A%22enhancement%3A+new+base+rule+extension%22) - - Look for [existing rules](https://github.com/typescript-eslint/typescript-eslint/tree/master/packages/eslint-plugin) + - Look for [existing rules](https://github.com/typescript-eslint/typescript-eslint/tree/main/packages/eslint-plugin) - Ensure that the rule you want to propose is related to types. If not, consider looking into [eslint-plugin-unicorn](https://github.com/sindresorhus/eslint-plugin-unicorn) - type: textarea validations: diff --git a/.github/ISSUE_TEMPLATE/typescript-eslint-parser.md b/.github/ISSUE_TEMPLATE/typescript-eslint-parser.md index 1292ac8e8a6b..b9ef34bc5333 100644 --- a/.github/ISSUE_TEMPLATE/typescript-eslint-parser.md +++ b/.github/ISSUE_TEMPLATE/typescript-eslint-parser.md @@ -24,7 +24,7 @@ If you have a problem with a specific lint rule, please back out and select the - [ ] I have tried restarting my IDE and the issue persists. - [ ] I have updated to the latest version of the packages. -- [ ] I have [read the FAQ](https://github.com/typescript-eslint/typescript-eslint/blob/master/docs/getting-started/linting/TROUBLESHOOTING.md) and my problem is not listed. +- [ ] I have [read the FAQ](https://github.com/typescript-eslint/typescript-eslint/blob/main/docs/linting/TROUBLESHOOTING.md) and my problem is not listed. **Repro** diff --git a/.github/ISSUE_TEMPLATE/typescript-eslint-scope-manager.md b/.github/ISSUE_TEMPLATE/typescript-eslint-scope-manager.md index 51464255ab42..570cd9a63764 100644 --- a/.github/ISSUE_TEMPLATE/typescript-eslint-scope-manager.md +++ b/.github/ISSUE_TEMPLATE/typescript-eslint-scope-manager.md @@ -24,7 +24,7 @@ If you have a problem with the parser, please back out and select the `@typescri - [ ] I have tried restarting my IDE and the issue persists. - [ ] I have updated to the latest version of the packages. -- [ ] I have [read the FAQ](https://github.com/typescript-eslint/typescript-eslint/blob/master/docs/getting-started/linting/TROUBLESHOOTING.md) and my problem is not listed. +- [ ] I have [read the FAQ](https://github.com/typescript-eslint/typescript-eslint/blob/main/docs/linting/TROUBLESHOOTING.md) and my problem is not listed. **Repro** diff --git a/.github/ISSUE_TEMPLATE/typescript-eslint-utils.md b/.github/ISSUE_TEMPLATE/typescript-eslint-utils.md index dd2960f2bb9c..f493f8342580 100644 --- a/.github/ISSUE_TEMPLATE/typescript-eslint-utils.md +++ b/.github/ISSUE_TEMPLATE/typescript-eslint-utils.md @@ -24,7 +24,7 @@ If you have a problem with the parser, please back out and select the `@typescri - [ ] I have tried restarting my IDE and the issue persists. - [ ] I have updated to the latest version of the packages. -- [ ] I have [read the FAQ](https://github.com/typescript-eslint/typescript-eslint/blob/master/docs/getting-started/linting/TROUBLESHOOTING.md) and my problem is not listed. +- [ ] I have [read the FAQ](https://github.com/typescript-eslint/typescript-eslint/blob/main/docs/linting/TROUBLESHOOTING.md) and my problem is not listed. **Repro** diff --git a/.github/ISSUE_TEMPLATE/typescript-estree.md b/.github/ISSUE_TEMPLATE/typescript-estree.md index 562bbfc0c840..98f9ef6bccfc 100644 --- a/.github/ISSUE_TEMPLATE/typescript-estree.md +++ b/.github/ISSUE_TEMPLATE/typescript-estree.md @@ -23,7 +23,7 @@ If you have a problem with a specific lint rule, please back out and select the - [ ] I have tried restarting my IDE and the issue persists. - [ ] I have updated to the latest version of the packages. -- [ ] I have [read the FAQ](https://github.com/typescript-eslint/typescript-eslint/blob/master/docs/getting-started/linting/TROUBLESHOOTING.md) and my problem is not listed. +- [ ] I have [read the FAQ](https://github.com/typescript-eslint/typescript-eslint/blob/main/docs/linting/TROUBLESHOOTING.md) and my problem is not listed. **Repro** diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index a93f9eea9143..3b6b7acdc2e5 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -7,7 +7,7 @@ Please fill out all fields below -- otherwise we may not be able to review your - [ ] Addresses an existing issue: fixes #000 - [ ] That issue was marked as [accepting prs](https://github.com/typescript-eslint/typescript-eslint/issues?q=is%3Aopen+is%3Aissue+label%3A%22accepting+prs%22) -- [ ] Steps in [CONTRIBUTING.md](https://github.com/typescript-eslint/typescript-eslint/blob/master/CONTRIBUTING.md) were taken +- [ ] Steps in [CONTRIBUTING.md](https://github.com/typescript-eslint/typescript-eslint/blob/main/CONTRIBUTING.md) were taken ## Overview diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5ccffc7b8042..5ebf7a934156 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -3,7 +3,7 @@ name: CI on: push: branches: - - master + - main - v5 pull_request: branches: @@ -310,7 +310,7 @@ jobs: linting_and_style, integration_tests, ] - if: github.repository == 'typescript-eslint/typescript-eslint' && github.ref == 'refs/heads/master' + if: github.repository == 'typescript-eslint/typescript-eslint' && github.ref == 'refs/heads/main' steps: - uses: actions/checkout@v2 # Fetch all history for all tags and branches in this job because lerna needs it diff --git a/.gitignore b/.gitignore index b9ffa3b3dc8b..c455b1df3ced 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,7 @@ yarn-error.log* packages/website/.docusaurus packages/website/.cache-loader packages/website/build +packages/website/static/sandbox # Runtime data pids diff --git a/.prettierignore b/.prettierignore index 3b8eebd16843..8c4dce359d7a 100644 --- a/.prettierignore +++ b/.prettierignore @@ -18,6 +18,7 @@ CHANGELOG.md packages/website/.docusaurus packages/website/build +packages/website/src/vendor # TODO - remove this once prettier supports TS4.1 packages/scope-manager/tests/fixtures/type-declaration/literal-type1.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index e8ff2456b4af..aca4e48f1ff4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,32 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [5.5.0](https://github.com/typescript-eslint/typescript-eslint/compare/v5.4.0...v5.5.0) (2021-11-29) + + +### Bug Fixes + +* **eslint-plugin:** [member-ordering] order literal names correctly in ([#4054](https://github.com/typescript-eslint/typescript-eslint/issues/4054)) ([d57141a](https://github.com/typescript-eslint/typescript-eslint/commit/d57141a3d13fad30a93ed99a6a15f4b0b369246a)) +* **eslint-plugin:** [no-duplicate-imports] remove unnecessary type checking for `node.source` ([#4196](https://github.com/typescript-eslint/typescript-eslint/issues/4196)) ([637722a](https://github.com/typescript-eslint/typescript-eslint/commit/637722a77667f6ed1e0cf1f0e752d61622ae8546)) +* **eslint-plugin:** [no-var-requires] do not report require created from createRequire ([#4221](https://github.com/typescript-eslint/typescript-eslint/issues/4221)) ([0040186](https://github.com/typescript-eslint/typescript-eslint/commit/0040186aa23692724986df22a71926e8a7ff9e02)) +* **eslint-plugin:** [prefer-for-of] do nor error when iterating over this ([#4176](https://github.com/typescript-eslint/typescript-eslint/issues/4176)) ([258ddb0](https://github.com/typescript-eslint/typescript-eslint/commit/258ddb0708b7a44959bd3ac399cbde912c8021c8)) +* **eslint-plugin:** [require-await] treat yield* asynciterable as an await ([#4125](https://github.com/typescript-eslint/typescript-eslint/issues/4125)) ([5a4ce6a](https://github.com/typescript-eslint/typescript-eslint/commit/5a4ce6a241b1d6c6caad87cad85c3741f0953e39)) +* **eslint-plugin:** remove all whitespaces in comparison [#4220](https://github.com/typescript-eslint/typescript-eslint/issues/4220) ([#4223](https://github.com/typescript-eslint/typescript-eslint/issues/4223)) ([853d799](https://github.com/typescript-eslint/typescript-eslint/commit/853d799428a061d9bf6a2e74b01dc49a1e4f3134)) +* **experimental-utils:** export RuleCreator interfaces ([#4199](https://github.com/typescript-eslint/typescript-eslint/issues/4199)) ([7821e4c](https://github.com/typescript-eslint/typescript-eslint/commit/7821e4c515ca2f11a14dcfa94dc77370da0287c5)) +* **experimental-utils:** fix types for eslint-utils ([#4173](https://github.com/typescript-eslint/typescript-eslint/issues/4173)) ([7079de2](https://github.com/typescript-eslint/typescript-eslint/commit/7079de26877a2313a7019845d4c33d0fc4d4b4a9)) +* **scope-manager:** support static class blocks ([#4211](https://github.com/typescript-eslint/typescript-eslint/issues/4211)) ([f8e9125](https://github.com/typescript-eslint/typescript-eslint/commit/f8e91256e0a721aaa906a5c40a92784da9433b53)) +* **visitor-keys:** add missing import assertion keys ([#4178](https://github.com/typescript-eslint/typescript-eslint/issues/4178)) ([9c38b7f](https://github.com/typescript-eslint/typescript-eslint/commit/9c38b7f7fc3ce471a8f720c4a2fbce01f3ee12a4)) + + +### Features + +* **eslint-plugin:** [member-ordering] add option to sort case insensitive ([#3896](https://github.com/typescript-eslint/typescript-eslint/issues/3896)) ([e3533d5](https://github.com/typescript-eslint/typescript-eslint/commit/e3533d5a6293a358b5eb0a6ed17da961a09b0ed3)) +* **eslint-plugin:** `array-type` distinguish whether readonly or not ([#4066](https://github.com/typescript-eslint/typescript-eslint/issues/4066)) ([314af44](https://github.com/typescript-eslint/typescript-eslint/commit/314af44bde3ccbebc620625b2931d77688525976)) + + + + + # [5.4.0](https://github.com/typescript-eslint/typescript-eslint/compare/v5.3.1...v5.4.0) (2021-11-15) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 9af92775588b..42ef14b19c5e 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -10,7 +10,7 @@ Before raising a bug, ensure you are using the latest version of our packages. W Finally, when raising a new issue, please fill out the issue template - **_please don't skip sections_**. -Please provide **_as much information as possible_**. This project is maintained by volunteers, so the more the more information you provide, the less likely we will have to waste everyone's time in asking you for more information. +Please provide **_as much information as possible_**. This project is maintained by volunteers, so the more information you provide, the less likely we will have to waste everyone's time in asking you for more information. If you have a particularly complex issue - consider creating a small, self-contained reproduction repo. This will help you in figuring out the exact problem, and will help us in reproducing and diagnosing the bug. @@ -24,7 +24,7 @@ Please refrain from leaving useless comments on issues. Comments like "+1", or " Please refrain from commenting on old, closed issues and PRs. Your issue is rarely related enough to a closed issue to warrant "necroing" a dead thread - raising a new issue means you can fill in the template, and make it easier for us to help you. Often times if you comment on a closed issue, we will just ask you to open a new issue, so please save everyone's time, and **_help us to help you_**. -Please refrain from commenting on `master` commits. Commit comments are not searchable, meaning that nobody else can discover your comments. Raise an issue and reference the commit instead so that everyone can see your comment, and you can fill out the template. +Please refrain from commenting on `main` commits. Commit comments are not searchable, meaning that nobody else can discover your comments. Raise an issue and reference the commit instead so that everyone can see your comment, and you can fill out the template. --- @@ -88,7 +88,7 @@ Within the body of your PR, make sure you reference the issue that you have work Make sure you use the "Fixes #xxx" format to reference issues, so that GitHub automatically closes the issues when we merge the PR. Also note that if you are fixing multiple issues at once, you can only reference one issue per line, and must put one "Fixes #xxx" per issue number. -In terms of your commit history - we do not care about the number, or style of commits in your history, because we squash merge every PR into master. Feel free to commit in whatever style you feel comfortable with. +In terms of your commit history - we do not care about the number, or style of commits in your history, because we squash merge every PR into `main`. Feel free to commit in whatever style you feel comfortable with. **_One thing we ask is to please avoid force pushing after you have raised a PR_**. GitHub is not able to track changes across force pushes, which makes it impossible to efficiently do incremental reviews. This slows us down, and means it will take longer for us to get your PR merged. @@ -98,4 +98,4 @@ With your PR raised, and the CI showing green, your PR will [sit in the queue to Please note that as this project is maintained by volunteers, it may take a while for us to get around to your PR (sometimes a month or more). Be patient, we'll get to it. Please refrain from commenting asking for a review, or similar bump comments. **_These just create spam for maintainers, and does not push your PR higher in the queue_**. -Once we have reviewed your PR, we will provide any feedback that needs addressing. If you feel a requested change is wrong, don't be afraid to discuss with us in the comments. Once the feedback is addressed, and the PR is reviewed, we'll ensure the branch is up to date with master, and merge it for you. +Once we have reviewed your PR, we will provide any feedback that needs addressing. If you feel a requested change is wrong, don't be afraid to discuss with us in the comments. Once the feedback is addressed, and the PR is reviewed, we'll ensure the branch is up to date with `main`, and merge it for you. diff --git a/README.md b/README.md index df0c05c590ef..41ff35f1af20 100644 --- a/README.md +++ b/README.md @@ -35,7 +35,7 @@ See https://typescript-eslint.io/docs/development/architecture/packages for more All of the packages are published with the same version number to make it easier to coordinate both releases and installations. -We publish a canary release on every successful merge to master, so **you never need to wait for a new stable version to make use of any updates**. +We publish a canary release on every successful merge to `main`, so **you never need to wait for a new stable version to make use of any updates**. Additionally, we promote the to the `latest` tag on NPM once per week, **on Mondays at 1 pm Eastern**. @@ -43,7 +43,7 @@ The latest version under the `latest` tag is: NPM Version -The latest version under the `canary` tag **(latest commit to master)** is: +The latest version under the `canary` tag **(latest commit to `main`)** is: NPM Version diff --git a/commitlint.config.js b/commitlint.config.js index 20ac415d9299..e03e6410a01e 100644 --- a/commitlint.config.js +++ b/commitlint.config.js @@ -1,18 +1,9 @@ -const path = require('path'); -const config = require('./package.json'); -const globby = require('globby'); +const workspace = require('./workspace.json'); -const workspaces = config.workspaces.packages.map(ws => - path.posix.join(ws, 'package.json'), +const packages = Object.keys(workspace.projects).map(name => + name.charAt(0) === '@' ? name.split('/')[1] : name, ); -const packages = globby - .sync(workspaces, { - cwd: __dirname, - }) - .map(pJson => require(path.join(__dirname, pJson)).name) - .map(name => (name.charAt(0) === '@' ? name.split('/')[1] : name)); - module.exports = { extends: ['@commitlint/config-conventional'], rules: { diff --git a/docs/development/CUSTOM_RULES.md b/docs/development/CUSTOM_RULES.md index 2c235725f5c7..ee220e523a1e 100644 --- a/docs/development/CUSTOM_RULES.md +++ b/docs/development/CUSTOM_RULES.md @@ -9,25 +9,44 @@ 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 main two changes to custom rules writing are: +The main three changes to custom rules writing are: +- [Utils Package](#utils-package): we recommend using `@typescript-eslint/experimental-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 -## AST Extensions +## Utils Package -`@typescript-eslint/estree` creates AST nodes for TypeScript syntax with names that begin with `TS`, such as `TSInterfaceDeclaration` and `TSTypeAnnotation`. -These nodes are treated just like any other AST node. -You can query for them in your rule selectors. +The `@typescript-eslint/experimental-utils` package acts as a replacement package for `eslint` that exports all the same objects and types, but with typescript-eslint support. +It also exports common utility functions and constants most custom typescript-eslint rules tend to use. + +:::caution +`@types/eslint` types are based on `@types/estree` and do not recognize typescript-eslint nodes and properties. +You should generally not need to import from `eslint` when writing custom typescript-eslint rules in TypeScript. +::: + +### `RuleCreator` -This rule written in JavaScript bans interfaces that start with a lower-case letter: +The recommended way to create custom ESLint rules that make use of typescript-eslint features and/or syntax is with the `ESLintUtils.RuleCreator` function exported by `@typescript-eslint/experimental-utils`. -```js -export const rule = { +It takes in a function that transforms a rule name into its documentation URL, then returns a function that takes in a rule module object. +`RuleCreator` will infer the allowed message IDs the rule is allowed to emit from the provided `meta.messages` object. + +This rule bans function declarations that start with a lower-case letter: + +```ts +import { ESLintUtils } from '@typescript-eslint/experimental-utils'; + +const createRule = ESLintUtils.RuleCreator( + name => `https://example.com/rule/${name}`, +); + +// Type: RuleModule<"uppercase", ...> +export const rule = createRule({ create(context) { return { - TSInterfaceDeclaration(node) { - if (/[a-z]/.test(node.id.name[0])) { + FunctionDeclaration(node) { + if (/^[a-z]/.test(node.id.name)) { context.report({ messageId: 'uppercase', node: node.id, @@ -39,7 +58,8 @@ export const rule = { meta: { docs: { category: 'Best Practices', - description: 'Interface names should start with an upper-case letter.', + description: + 'Function declaration names should start with an upper-case letter.', }, messages: { uppercase: 'Start this name with an upper-case letter.', @@ -47,75 +67,74 @@ export const rule = { type: 'suggestion', schema: [], }, -}; +}); ``` -### Writing Rules in TypeScript - -The `@typescript-eslint/experimental-utils` package acts as a replacement package for `eslint` that exports all the same objects and types, but with typescript-eslint support. - -:::caution -`@types/eslint` types are based on `@types/estree` and do not recognize typescript-eslint nodes and properties. -You should generally not need to import from `eslint` when writing custom typescript-eslint rules in TypeScript. -::: - -#### Rule Types - -`@typescript-eslint/experimental-utils` exports a `RuleModule` interface that allows specifying generics for: +`RuleCreator` rule creator functions return rules typed as the `RuleModule` interface exported by `@typescript-eslint/experimental-utils`. +It allows specifying generics for: - `MessageIds`: a union of string literal message IDs that may be reported -- `Options`: what options users may configure for the rule +- `Options`: what options users may configure for the rule (by default, `[]`) + +If the rule is able to take in rule options, declare them as a tuple type containing a single object of rule options: ```ts -import { TSESLint } from '@typescript-eslint/experimental-utils'; +import { ESLintUtils } from '@typescript-eslint/experimental-utils'; -export const rule: TSESLint.RuleModule<'uppercase', []> = { - create(context /* : Readonly> */) { - // ... +type MessageIds = 'lowercase' | 'uppercase'; + +type Options = [ + { + preferredCase?: 'lower' | 'upper'; }, -}; +]; + +// Type: RuleModule +export const rule = createRule({ + // ... +}); ``` -For groups of rules that share a common documentation URL, a `RuleCreator` function is exported. -It takes in a function that transforms a rule name into its documentation URL, then returns a function that takes in a rule module object. -The returned function is able to infer message IDs from `meta.messages`. +### Undocumented Rules + +Although it is generally not recommended to create custom rules without documentation, if you are sure you want to do this you can use the `ESLintUtils.RuleCreator.withoutDocs` function to directly create a rule. +It applies the same type inference as the `createRule`s above without enforcing a documentation URL. ```ts import { ESLintUtils } from '@typescript-eslint/experimental-utils'; -const createRule = ESLintUtils.RuleCreator( - name => `https://example.com/rule/${name}`, -); - -// Type: const rule: RuleModule<"uppercase", ...> -export const rule = createRule({ +export const rule = ESLintUtils.RuleCreator.withoutDocs({ create(context) { // ... }, meta: { - messages: { - uppercase: 'Start this name with an upper-case letter.', - }, // ... }, }); ``` -#### Node Types +:::caution +We recommend any custom ESLint rule include a descriptive error message and link to informative documentation. +::: -TypeScript types for nodes exist in a `TSESTree` namespace exported by `@typescript-eslint/experimental-utils`. -The above rule body could be better written in TypeScript with a type annotation on the `node`: +## AST Extensions -```ts -import { TSESLint, TSESTree } from '@typescript-eslint/experimental-utils'; +`@typescript-eslint/estree` creates AST nodes for TypeScript syntax with names that begin with `TS`, such as `TSInterfaceDeclaration` and `TSTypeAnnotation`. +These nodes are treated just like any other AST node. +You can query for them in your rule selectors. -// ... +This version of the above rule instead bans interface declaration names that start with a lower-case letter: + +```ts +import { ESLintUtils } from '@typescript-eslint/experimental-utils'; export const rule = createRule({ create(context) { return { - TSInterfaceDeclaration(node: TSESTree.TSInterfaceDeclaration) { - // ... + TSInterfaceDeclaration(node) { + if (/^[a-z]/.test(node.id.name)) { + // ... + } }, }; }, @@ -123,6 +142,11 @@ export const rule = createRule({ }); ``` +### Node Types + +TypeScript types for nodes exist in a `TSESTree` namespace exported by `@typescript-eslint/experimental-utils`. +The above rule body could be better written in TypeScript with a type annotation on the `node`: + An `AST_NODE_TYPES` enum is exported as well to hold the values for AST node `type` properties. `TSESTree.Node` is available as union type that uses its `type` member as a discriminant. @@ -148,6 +172,37 @@ export function describeNode(node: TSESTree.Node): string { } ``` +### Explicit Node Types + +Rule queries that use more features of [esquery](https://github.com/estools/esquery) such as targeting multiple node types may not be able to infer the type of the `node`. +In that case, it is best to add an explicit type declaration. + +This rule snippet targets name nodes of both function and interface declarations: + +```ts +import { + AST_NODE_TYPES, + ESLintUtils, +} from '@typescript-eslint/experimental-utils'; + +export const rule = createRule({ + create(context) { + return { + 'FunctionDeclaration, TSInterfaceDeclaration'( + node: + | AST_NODE_TYPES.FunctionDeclaration + | AST_NODE_TYPES.TSInterfaceDeclaration, + ) { + if (/^[a-z]/.test(node.id.name)) { + // ... + } + }, + }; + }, + // ... +}); +``` + ## Type Checking :::tip diff --git a/docs/development/architecture/PACKAGES.md b/docs/development/architecture/PACKAGES.md index 7e169187423e..a212629ce77e 100644 --- a/docs/development/architecture/PACKAGES.md +++ b/docs/development/architecture/PACKAGES.md @@ -69,10 +69,10 @@ Any custom rules you write generally will be as well. **TSLint is deprecated.** It is in your best interest to migrate off it entirely. See [Linting > TSLint](../../linting/TSLINT.md). ::: -[`@typescript-eslint/eslint-plugin-tslint`]: https://github.com/typescript-eslint/typescript-eslint/tree/master/packages/eslint-plugin-tslint -[`@typescript-eslint/eslint-plugin`]: https://github.com/typescript-eslint/typescript-eslint/tree/master/packages/eslint-plugin -[`@typescript-eslint/experimental-utils`]: https://github.com/typescript-eslint/typescript-eslint/tree/master/packages/experimental-utils -[`@typescript-eslint/parser`]: https://github.com/typescript-eslint/typescript-eslint/tree/master/packages/parser -[`@typescript-eslint/scope-manager`]: https://github.com/typescript-eslint/typescript-eslint/tree/master/packages/scope-manager -[`@typescript-eslint/typescript-estree`]: https://github.com/typescript-eslint/typescript-eslint/tree/master/packages/typescript-estree -[`@typescript-eslint/typescript-estree`]: https://github.com/typescript-eslint/typescript-eslint/tree/master/packages/typescript-estree +[`@typescript-eslint/eslint-plugin-tslint`]: https://github.com/typescript-eslint/typescript-eslint/tree/main/packages/eslint-plugin-tslint +[`@typescript-eslint/eslint-plugin`]: https://github.com/typescript-eslint/typescript-eslint/tree/main/packages/eslint-plugin +[`@typescript-eslint/experimental-utils`]: https://github.com/typescript-eslint/typescript-eslint/tree/main/packages/experimental-utils +[`@typescript-eslint/parser`]: https://github.com/typescript-eslint/typescript-eslint/tree/main/packages/parser +[`@typescript-eslint/scope-manager`]: https://github.com/typescript-eslint/typescript-eslint/tree/main/packages/scope-manager +[`@typescript-eslint/typescript-estree`]: https://github.com/typescript-eslint/typescript-eslint/tree/main/packages/typescript-estree +[`@typescript-eslint/typescript-estree`]: https://github.com/typescript-eslint/typescript-eslint/tree/main/packages/typescript-estree diff --git a/docs/linting/TSLINT.md b/docs/linting/TSLINT.md index 466604460540..0d7954dd3506 100644 --- a/docs/linting/TSLINT.md +++ b/docs/linting/TSLINT.md @@ -12,11 +12,11 @@ TSLint was a linter equivalent to ESLint that was written specifically to work d If you are looking for help in migrating from TSLint to ESLint, see [`tslint-to-eslint-config`](https://github.com/typescript-eslint/tslint-to-eslint-config). -You can look at [`the plugin ROADMAP.md`](https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/ROADMAP.md) for an up to date overview of how TSLint rules compare to the ones in this package. +You can look at [`the plugin ROADMAP.md`](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/ROADMAP.md) for an up to date overview of how TSLint rules compare to the ones in this package. There is also the ultimate fallback option of using both linters together for a while during your transition if you absolutely have to by using TSLint _within_ ESLint. -For this option, check out [`@typescript-eslint/eslint-plugin-tslint`](https://github.com/typescript-eslint/typescript-eslint/tree/master/packages/eslint-plugin-tslint). +For this option, check out [`@typescript-eslint/eslint-plugin-tslint`](https://github.com/typescript-eslint/typescript-eslint/tree/main/packages/eslint-plugin-tslint). ## Why Deprecate TSLint? diff --git a/lerna.json b/lerna.json index e15e8e338241..fe8d54785cd9 100644 --- a/lerna.json +++ b/lerna.json @@ -1,5 +1,5 @@ { - "version": "5.4.0", + "version": "5.5.0", "npmClient": "yarn", "useWorkspaces": true, "stream": true diff --git a/nx.json b/nx.json index ec90f4a1f5c2..5ff2e9c8d237 100644 --- a/nx.json +++ b/nx.json @@ -8,7 +8,7 @@ ".github/workflows/ci.yml": "*" }, "affected": { - "defaultBase": "master" + "defaultBase": "main" }, "workspaceLayout": { "libsDir": "packages" diff --git a/package.json b/package.json index 3080f73df881..fe8d5e25da31 100644 --- a/package.json +++ b/package.json @@ -33,10 +33,11 @@ "format": "prettier --write \"./**/*.{ts,tsx,js,jsx,json,md,css}\"", "generate-contributors": "yarn ts-node --transpile-only ./tools/generate-contributors.ts && yarn all-contributors generate", "generate-sponsors": "yarn ts-node --transpile-only ./tools/generate-sponsors.ts", - "lint-fix": "eslint . --ext .js,.ts --fix", + "generate-website-dts": "yarn ts-node --transpile-only ./tools/generate-website-dts.ts", + "lint-fix": "eslint . --ext .js,.jsx,.ts,.tsx --fix", "lint-markdown-fix": "yarn lint-markdown --fix", "lint-markdown": "markdownlint \"**/*.md\" --config=.markdownlint.json --ignore-path=.markdownlintignore", - "lint": "eslint . --ext .js,.ts", + "lint": "cross-env NODE_OPTIONS=\"--max-old-space-size=16384\" eslint . --ext .js,.jsx,.ts,.tsx", "postinstall": "yarn husky install && yarn build", "pre-commit": "yarn lint-staged", "pre-push": "yarn check-format", @@ -56,11 +57,10 @@ }, "devDependencies": { "@babel/code-frame": "^7.16.0", - "@babel/parser": "^7.16.2", + "@babel/parser": "^7.16.4", "@babel/types": "^7.16.0", - "@commitlint/cli": "^14.1.0", - "@commitlint/config-conventional": "^14.1.0", - "@commitlint/config-lerna-scopes": "^14.0.0", + "@commitlint/cli": "^15.0.0", + "@commitlint/config-conventional": "^15.0.0", "@nrwl/cli": "13.0.2", "@nrwl/nx-cloud": "12.5.1", "@nrwl/tao": "13.0.2", @@ -81,6 +81,7 @@ "@types/semver": "^7.3.9", "@types/tmp": "^0.2.2", "all-contributors-cli": "^6.20.0", + "cross-env": "^7.0.3", "cspell": "^5.12.3", "cz-conventional-changelog": "^3.3.0", "downlevel-dts": "^0.7.0", @@ -108,10 +109,10 @@ "ts-jest": "^27.0.5", "ts-node": "^10.4.0", "tslint": "^6.1.3", - "typescript": ">=3.3.1 <4.6.0 || 4.5.1-rc" + "typescript": ">=3.3.1 <4.6.0" }, "resolutions": { "@types/node": "^16.11.4", - "typescript": "4.5.1-rc" + "typescript": "4.5.2" } } diff --git a/packages/ast-spec/CHANGELOG.md b/packages/ast-spec/CHANGELOG.md index 381d1da416b9..720055dd131b 100644 --- a/packages/ast-spec/CHANGELOG.md +++ b/packages/ast-spec/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [5.5.0](https://github.com/typescript-eslint/typescript-eslint/compare/v5.4.0...v5.5.0) (2021-11-29) + +**Note:** Version bump only for package @typescript-eslint/ast-spec + + + + + # [5.4.0](https://github.com/typescript-eslint/typescript-eslint/compare/v5.3.1...v5.4.0) (2021-11-15) diff --git a/packages/ast-spec/package.json b/packages/ast-spec/package.json index f4d7903b50e9..5b95e79dc7e8 100644 --- a/packages/ast-spec/package.json +++ b/packages/ast-spec/package.json @@ -1,6 +1,6 @@ { "name": "@typescript-eslint/ast-spec", - "version": "5.4.0", + "version": "5.5.0", "description": "TypeScript-ESTree AST spec", "private": true, "keywords": [ diff --git a/packages/eslint-plugin-internal/CHANGELOG.md b/packages/eslint-plugin-internal/CHANGELOG.md index 424287ee0891..b63040d51d62 100644 --- a/packages/eslint-plugin-internal/CHANGELOG.md +++ b/packages/eslint-plugin-internal/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [5.5.0](https://github.com/typescript-eslint/typescript-eslint/compare/v5.4.0...v5.5.0) (2021-11-29) + +**Note:** Version bump only for package @typescript-eslint/eslint-plugin-internal + + + + + # [5.4.0](https://github.com/typescript-eslint/typescript-eslint/compare/v5.3.1...v5.4.0) (2021-11-15) **Note:** Version bump only for package @typescript-eslint/eslint-plugin-internal diff --git a/packages/eslint-plugin-internal/package.json b/packages/eslint-plugin-internal/package.json index 528dd573bfd2..e386f87827c4 100644 --- a/packages/eslint-plugin-internal/package.json +++ b/packages/eslint-plugin-internal/package.json @@ -1,6 +1,6 @@ { "name": "@typescript-eslint/eslint-plugin-internal", - "version": "5.4.0", + "version": "5.5.0", "private": true, "main": "dist/index.js", "scripts": { @@ -14,8 +14,8 @@ }, "dependencies": { "@types/prettier": "*", - "@typescript-eslint/experimental-utils": "5.4.0", - "@typescript-eslint/scope-manager": "5.4.0", + "@typescript-eslint/experimental-utils": "5.5.0", + "@typescript-eslint/scope-manager": "5.5.0", "prettier": "*" } } diff --git a/packages/eslint-plugin-tslint/CHANGELOG.md b/packages/eslint-plugin-tslint/CHANGELOG.md index 9e5f5d5ff536..8b720954cc34 100644 --- a/packages/eslint-plugin-tslint/CHANGELOG.md +++ b/packages/eslint-plugin-tslint/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [5.5.0](https://github.com/typescript-eslint/typescript-eslint/compare/v5.4.0...v5.5.0) (2021-11-29) + +**Note:** Version bump only for package @typescript-eslint/eslint-plugin-tslint + + + + + # [5.4.0](https://github.com/typescript-eslint/typescript-eslint/compare/v5.3.1...v5.4.0) (2021-11-15) **Note:** Version bump only for package @typescript-eslint/eslint-plugin-tslint diff --git a/packages/eslint-plugin-tslint/package.json b/packages/eslint-plugin-tslint/package.json index 01df74600445..8bd14955ba50 100644 --- a/packages/eslint-plugin-tslint/package.json +++ b/packages/eslint-plugin-tslint/package.json @@ -1,6 +1,6 @@ { "name": "@typescript-eslint/eslint-plugin-tslint", - "version": "5.4.0", + "version": "5.5.0", "main": "dist/index.js", "typings": "src/index.ts", "description": "TSLint wrapper plugin for ESLint", @@ -38,7 +38,7 @@ "typecheck": "tsc -p tsconfig.json --noEmit" }, "dependencies": { - "@typescript-eslint/experimental-utils": "5.4.0", + "@typescript-eslint/experimental-utils": "5.5.0", "lodash": "^4.17.21" }, "peerDependencies": { @@ -48,6 +48,6 @@ }, "devDependencies": { "@types/lodash": "*", - "@typescript-eslint/parser": "5.4.0" + "@typescript-eslint/parser": "5.5.0" } } diff --git a/packages/eslint-plugin/CHANGELOG.md b/packages/eslint-plugin/CHANGELOG.md index 193e8df0248c..4811c6a939ba 100644 --- a/packages/eslint-plugin/CHANGELOG.md +++ b/packages/eslint-plugin/CHANGELOG.md @@ -3,6 +3,28 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [5.5.0](https://github.com/typescript-eslint/typescript-eslint/compare/v5.4.0...v5.5.0) (2021-11-29) + + +### Bug Fixes + +* **eslint-plugin:** [member-ordering] order literal names correctly in ([#4054](https://github.com/typescript-eslint/typescript-eslint/issues/4054)) ([d57141a](https://github.com/typescript-eslint/typescript-eslint/commit/d57141a3d13fad30a93ed99a6a15f4b0b369246a)) +* **eslint-plugin:** [no-duplicate-imports] remove unnecessary type checking for `node.source` ([#4196](https://github.com/typescript-eslint/typescript-eslint/issues/4196)) ([637722a](https://github.com/typescript-eslint/typescript-eslint/commit/637722a77667f6ed1e0cf1f0e752d61622ae8546)) +* **eslint-plugin:** [no-var-requires] do not report require created from createRequire ([#4221](https://github.com/typescript-eslint/typescript-eslint/issues/4221)) ([0040186](https://github.com/typescript-eslint/typescript-eslint/commit/0040186aa23692724986df22a71926e8a7ff9e02)) +* **eslint-plugin:** [prefer-for-of] do nor error when iterating over this ([#4176](https://github.com/typescript-eslint/typescript-eslint/issues/4176)) ([258ddb0](https://github.com/typescript-eslint/typescript-eslint/commit/258ddb0708b7a44959bd3ac399cbde912c8021c8)) +* **eslint-plugin:** [require-await] treat yield* asynciterable as an await ([#4125](https://github.com/typescript-eslint/typescript-eslint/issues/4125)) ([5a4ce6a](https://github.com/typescript-eslint/typescript-eslint/commit/5a4ce6a241b1d6c6caad87cad85c3741f0953e39)) +* **eslint-plugin:** remove all whitespaces in comparison [#4220](https://github.com/typescript-eslint/typescript-eslint/issues/4220) ([#4223](https://github.com/typescript-eslint/typescript-eslint/issues/4223)) ([853d799](https://github.com/typescript-eslint/typescript-eslint/commit/853d799428a061d9bf6a2e74b01dc49a1e4f3134)) + + +### Features + +* **eslint-plugin:** [member-ordering] add option to sort case insensitive ([#3896](https://github.com/typescript-eslint/typescript-eslint/issues/3896)) ([e3533d5](https://github.com/typescript-eslint/typescript-eslint/commit/e3533d5a6293a358b5eb0a6ed17da961a09b0ed3)) +* **eslint-plugin:** `array-type` distinguish whether readonly or not ([#4066](https://github.com/typescript-eslint/typescript-eslint/issues/4066)) ([314af44](https://github.com/typescript-eslint/typescript-eslint/commit/314af44bde3ccbebc620625b2931d77688525976)) + + + + + # [5.4.0](https://github.com/typescript-eslint/typescript-eslint/compare/v5.3.1...v5.4.0) (2021-11-15) diff --git a/packages/eslint-plugin/README.md b/packages/eslint-plugin/README.md index 63a35e1881ed..1b71e9158a21 100644 --- a/packages/eslint-plugin/README.md +++ b/packages/eslint-plugin/README.md @@ -10,8 +10,8 @@ ## Getting Started -- **[You can find our Getting Started docs here](../../docs/getting-started/linting/README.md)** -- **[You can find our FAQ / Troubleshooting docs here](../../docs/getting-started/linting/TROUBLESHOOTING.md)** +- **[You can find our Getting Started docs here](../../docs/linting/README.md)** +- **[You can find our FAQ / Troubleshooting docs here](../../docs/linting/TROUBLESHOOTING.md)** These docs walk you through setting up ESLint, this plugin, and our parser. If you know what you're doing and just want to quick start, read on... @@ -87,7 +87,7 @@ Some highly valuable rules require type-checking in order to be implemented corr Pro Tip: For larger codebases you may want to consider splitting our linting into two separate stages: 1. fast feedback rules which operate purely based on syntax (no type-checking), 2. rules which are based on semantics (type-checking). -**[You can read more about linting with type information here](../../docs/getting-started/linting/TYPED_LINTING.md)** +**[You can read more about linting with type information here](../../docs/linting/TYPED_LINTING.md)** ## Supported Rules diff --git a/packages/eslint-plugin/ROADMAP.md b/packages/eslint-plugin/ROADMAP.md index 6a55ef2cb7d2..9dfc1952dd82 100644 --- a/packages/eslint-plugin/ROADMAP.md +++ b/packages/eslint-plugin/ROADMAP.md @@ -270,7 +270,7 @@ Relevant plugins: [`chai-expect-keywords`](https://github.com/gavinaiken/eslint- [4] Recommended config: `["error", { "terms": ["BUG", "HACK", "FIXME", "LATER", "LATER2", "TODO"], "location": "anywhere" }]`
[5] Does not check class fields. -[insecure-random]: https://github.com/desktop/desktop/blob/master/eslint-rules/insecure-random.js +[insecure-random]: https://github.com/desktop/desktop/blob/development/eslint-rules/insecure-random.js ### Security @@ -594,63 +594,63 @@ Relevant plugins: [`chai-expect-keywords`](https://github.com/gavinaiken/eslint- -[`@typescript-eslint/adjacent-overload-signatures`]: https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/adjacent-overload-signatures.md -[`@typescript-eslint/await-thenable`]: https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/await-thenable.md -[`@typescript-eslint/ban-types`]: https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/ban-types.md -[`@typescript-eslint/ban-ts-comment`]: https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/ban-ts-comment.md -[`@typescript-eslint/consistent-type-assertions`]: https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/consistent-type-assertions.md -[`@typescript-eslint/consistent-type-definitions`]: https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/consistent-type-definitions.md -[`@typescript-eslint/explicit-member-accessibility`]: https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/explicit-member-accessibility.md -[`@typescript-eslint/member-ordering`]: https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/member-ordering.md -[`@typescript-eslint/method-signature-style`]: https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/method-signature-style.md -[`@typescript-eslint/no-explicit-any`]: https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/no-explicit-any.md -[`@typescript-eslint/no-empty-interface`]: https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/no-empty-interface.md -[`@typescript-eslint/no-inferrable-types`]: https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/no-inferrable-types.md -[`@typescript-eslint/prefer-namespace-keyword`]: https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/prefer-namespace-keyword.md -[`@typescript-eslint/promise-function-async`]: https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/promise-function-async.md -[`@typescript-eslint/no-misused-promises`]: https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/no-misused-promises.md -[`@typescript-eslint/no-namespace`]: https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/no-namespace.md -[`@typescript-eslint/no-non-null-assertion`]: https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/no-non-null-assertion.md -[`@typescript-eslint/triple-slash-reference`]: https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/triple-slash-reference.md -[`@typescript-eslint/unbound-method`]: https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/unbound-method.md -[`@typescript-eslint/no-unnecessary-type-assertion`]: https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/no-unnecessary-type-assertion.md -[`@typescript-eslint/no-var-requires`]: https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/no-var-requires.md -[`@typescript-eslint/type-annotation-spacing`]: https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/type-annotation-spacing.md -[`@typescript-eslint/typedef`]: https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/typedef.md -[`@typescript-eslint/unified-signatures`]: https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/unified-signatures.md -[`@typescript-eslint/no-unnecessary-boolean-literal-compare`]: https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/no-unnecessary-boolean-literal-compare.md -[`@typescript-eslint/no-misused-new`]: https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/no-misused-new.md -[`@typescript-eslint/no-this-alias`]: https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/no-this-alias.md -[`@typescript-eslint/no-throw-literal`]: https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/no-throw-literal.md -[`@typescript-eslint/no-extraneous-class`]: https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/no-extraneous-class.md -[`@typescript-eslint/no-unused-vars`]: https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/no-unused-vars.md -[`@typescript-eslint/no-use-before-define`]: https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/no-use-before-define.md -[`@typescript-eslint/restrict-plus-operands`]: https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/restrict-plus-operands.md -[`@typescript-eslint/strict-boolean-expressions`]: https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/strict-boolean-expressions.md -[`@typescript-eslint/indent`]: https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/indent.md -[`@typescript-eslint/no-invalid-void-type`]: https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/no-invalid-void-type.md -[`@typescript-eslint/no-require-imports`]: https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/no-require-imports.md -[`@typescript-eslint/array-type`]: https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/array-type.md -[`@typescript-eslint/naming-convention`]: https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/naming-convention.md -[`@typescript-eslint/interface-name-prefix`]: https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/interface-name-prefix.md -[`@typescript-eslint/naming-convention`]: https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/naming-convention.md -[`@typescript-eslint/no-parameter-properties`]: https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/no-parameter-properties.md -[`@typescript-eslint/member-delimiter-style`]: https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/member-delimiter-style.md -[`@typescript-eslint/prefer-for-of`]: https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/prefer-for-of.md -[`@typescript-eslint/no-array-constructor`]: https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/no-array-constructor.md -[`@typescript-eslint/no-dynamic-delete`]: https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/no-dynamic-delete.md -[`@typescript-eslint/prefer-function-type`]: https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/prefer-function-type.md -[`@typescript-eslint/prefer-readonly`]: https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/prefer-readonly.md -[`@typescript-eslint/require-await`]: https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/require-await.md -[`@typescript-eslint/no-for-in-array`]: https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/no-for-in-array.md -[`@typescript-eslint/no-unnecessary-qualifier`]: https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/no-unnecessary-qualifier.md -[`@typescript-eslint/no-unnecessary-type-arguments`]: https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/no-unnecessary-type-arguments.md -[`@typescript-eslint/semi`]: https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/semi.md -[`@typescript-eslint/no-floating-promises`]: https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/no-floating-promises.md -[`@typescript-eslint/no-magic-numbers`]: https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/no-magic-numbers.md -[`@typescript-eslint/no-unsafe-member-access`]: https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/no-unsafe-member-access.md -[`@typescript-eslint/restrict-template-expressions`]: https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/restrict-template-expressions.md -[`@typescript-eslint/no-confusing-void-expression`]: https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/no-confusing-void-expression.md +[`@typescript-eslint/adjacent-overload-signatures`]: https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/docs/rules/adjacent-overload-signatures.md +[`@typescript-eslint/await-thenable`]: https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/docs/rules/await-thenable.md +[`@typescript-eslint/ban-types`]: https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/docs/rules/ban-types.md +[`@typescript-eslint/ban-ts-comment`]: https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/docs/rules/ban-ts-comment.md +[`@typescript-eslint/consistent-type-assertions`]: https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/docs/rules/consistent-type-assertions.md +[`@typescript-eslint/consistent-type-definitions`]: https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/docs/rules/consistent-type-definitions.md +[`@typescript-eslint/explicit-member-accessibility`]: https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/docs/rules/explicit-member-accessibility.md +[`@typescript-eslint/member-ordering`]: https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/docs/rules/member-ordering.md +[`@typescript-eslint/method-signature-style`]: https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/docs/rules/method-signature-style.md +[`@typescript-eslint/no-explicit-any`]: https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/docs/rules/no-explicit-any.md +[`@typescript-eslint/no-empty-interface`]: https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/docs/rules/no-empty-interface.md +[`@typescript-eslint/no-inferrable-types`]: https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/docs/rules/no-inferrable-types.md +[`@typescript-eslint/prefer-namespace-keyword`]: https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/docs/rules/prefer-namespace-keyword.md +[`@typescript-eslint/promise-function-async`]: https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/docs/rules/promise-function-async.md +[`@typescript-eslint/no-misused-promises`]: https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/docs/rules/no-misused-promises.md +[`@typescript-eslint/no-namespace`]: https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/docs/rules/no-namespace.md +[`@typescript-eslint/no-non-null-assertion`]: https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/docs/rules/no-non-null-assertion.md +[`@typescript-eslint/triple-slash-reference`]: https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/docs/rules/triple-slash-reference.md +[`@typescript-eslint/unbound-method`]: https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/docs/rules/unbound-method.md +[`@typescript-eslint/no-unnecessary-type-assertion`]: https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/docs/rules/no-unnecessary-type-assertion.md +[`@typescript-eslint/no-var-requires`]: https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/docs/rules/no-var-requires.md +[`@typescript-eslint/type-annotation-spacing`]: https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/docs/rules/type-annotation-spacing.md +[`@typescript-eslint/typedef`]: https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/docs/rules/typedef.md +[`@typescript-eslint/unified-signatures`]: https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/docs/rules/unified-signatures.md +[`@typescript-eslint/no-unnecessary-boolean-literal-compare`]: https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/docs/rules/no-unnecessary-boolean-literal-compare.md +[`@typescript-eslint/no-misused-new`]: https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/docs/rules/no-misused-new.md +[`@typescript-eslint/no-this-alias`]: https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/docs/rules/no-this-alias.md +[`@typescript-eslint/no-throw-literal`]: https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/docs/rules/no-throw-literal.md +[`@typescript-eslint/no-extraneous-class`]: https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/docs/rules/no-extraneous-class.md +[`@typescript-eslint/no-unused-vars`]: https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/docs/rules/no-unused-vars.md +[`@typescript-eslint/no-use-before-define`]: https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/docs/rules/no-use-before-define.md +[`@typescript-eslint/restrict-plus-operands`]: https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/docs/rules/restrict-plus-operands.md +[`@typescript-eslint/strict-boolean-expressions`]: https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/docs/rules/strict-boolean-expressions.md +[`@typescript-eslint/indent`]: https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/docs/rules/indent.md +[`@typescript-eslint/no-invalid-void-type`]: https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/docs/rules/no-invalid-void-type.md +[`@typescript-eslint/no-require-imports`]: https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/docs/rules/no-require-imports.md +[`@typescript-eslint/array-type`]: https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/docs/rules/array-type.md +[`@typescript-eslint/naming-convention`]: https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/docs/rules/naming-convention.md +[`@typescript-eslint/interface-name-prefix`]: https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/docs/rules/interface-name-prefix.md +[`@typescript-eslint/naming-convention`]: https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/docs/rules/naming-convention.md +[`@typescript-eslint/no-parameter-properties`]: https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/docs/rules/no-parameter-properties.md +[`@typescript-eslint/member-delimiter-style`]: https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/docs/rules/member-delimiter-style.md +[`@typescript-eslint/prefer-for-of`]: https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/docs/rules/prefer-for-of.md +[`@typescript-eslint/no-array-constructor`]: https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/docs/rules/no-array-constructor.md +[`@typescript-eslint/no-dynamic-delete`]: https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/docs/rules/no-dynamic-delete.md +[`@typescript-eslint/prefer-function-type`]: https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/docs/rules/prefer-function-type.md +[`@typescript-eslint/prefer-readonly`]: https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/docs/rules/prefer-readonly.md +[`@typescript-eslint/require-await`]: https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/docs/rules/require-await.md +[`@typescript-eslint/no-for-in-array`]: https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/docs/rules/no-for-in-array.md +[`@typescript-eslint/no-unnecessary-qualifier`]: https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/docs/rules/no-unnecessary-qualifier.md +[`@typescript-eslint/no-unnecessary-type-arguments`]: https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/docs/rules/no-unnecessary-type-arguments.md +[`@typescript-eslint/semi`]: https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/docs/rules/semi.md +[`@typescript-eslint/no-floating-promises`]: https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/docs/rules/no-floating-promises.md +[`@typescript-eslint/no-magic-numbers`]: https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/docs/rules/no-magic-numbers.md +[`@typescript-eslint/no-unsafe-member-access`]: https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/docs/rules/no-unsafe-member-access.md +[`@typescript-eslint/restrict-template-expressions`]: https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/docs/rules/restrict-template-expressions.md +[`@typescript-eslint/no-confusing-void-expression`]: https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/docs/rules/no-confusing-void-expression.md @@ -706,7 +706,7 @@ Relevant plugins: [`chai-expect-keywords`](https://github.com/gavinaiken/eslint- [plugin:compat]: https://github.com/amilajack/eslint-plugin-compat [`no-null/no-null`]: https://github.com/nene/eslint-plugin-no-null [`unicorn/filename-case`]: https://github.com/sindresorhus/eslint-plugin-unicorn/blob/master/docs/rules/filename-case.md -[`jest/no-focused-tests`]: https://github.com/jest-community/eslint-plugin-jest/blob/master/docs/rules/no-focused-tests.md +[`jest/no-focused-tests`]: https://github.com/jest-community/eslint-plugin-jest/blob/main/docs/rules/no-focused-tests.md [`jsx-a11y/heading-has-content`]: https://github.com/evcohen/eslint-plugin-jsx-a11y/blob/master/docs/rules/heading-has-content.md [`lodash/chaining`]: https://github.com/wix/eslint-plugin-lodash/blob/master/docs/rules/chaining.md [`deprecation/deprecation`]: https://github.com/gund/eslint-plugin-deprecation diff --git a/packages/eslint-plugin/docs/rules/adjacent-overload-signatures.md b/packages/eslint-plugin/docs/rules/adjacent-overload-signatures.md index 339425c8d427..120bcc4326e4 100644 --- a/packages/eslint-plugin/docs/rules/adjacent-overload-signatures.md +++ b/packages/eslint-plugin/docs/rules/adjacent-overload-signatures.md @@ -6,7 +6,9 @@ Grouping overloaded members together can improve readability of the code. This rule aims to standardize the way overloaded members are organized. -The following patterns are considered warnings: + + +### ❌ Incorrect ```ts declare namespace Foo { @@ -43,7 +45,7 @@ export function bar(): void; export function foo(sn: string | number): void; ``` -The following patterns are not warnings: +### ✅ Correct ```ts declare namespace Foo { @@ -84,7 +86,7 @@ export function foo(sn: string | number): void; If you don't care about the general structure of the code, then you will not need this rule. -## Compatibility +## Related To - TSLint: [adjacent-overload-signatures](https://palantir.github.io/tslint/rules/adjacent-overload-signatures/) diff --git a/packages/eslint-plugin/docs/rules/array-type.md b/packages/eslint-plugin/docs/rules/array-type.md index 66f0bcb750b4..e911ac190acc 100644 --- a/packages/eslint-plugin/docs/rules/array-type.md +++ b/packages/eslint-plugin/docs/rules/array-type.md @@ -33,14 +33,16 @@ The default config will enforce that all mutable and readonly arrays use the `'a Always use `T[]` or `readonly T[]` for all array types. -Incorrect code for `"array"`: + + +#### ❌ Incorrect ```ts const x: Array = ['a', 'b']; const y: ReadonlyArray = ['a', 'b']; ``` -Correct code for `"array"`: +#### ✅ Correct ```ts const x: string[] = ['a', 'b']; @@ -51,14 +53,16 @@ const y: readonly string[] = ['a', 'b']; Always use `Array` or `ReadonlyArray` for all array types. -Incorrect code for `"generic"`: + + +#### ❌ Incorrect ```ts const x: string[] = ['a', 'b']; const y: readonly string[] = ['a', 'b']; ``` -Correct code for `"generic"`: +#### ✅ Correct ```ts const x: Array = ['a', 'b']; @@ -70,7 +74,9 @@ const y: ReadonlyArray = ['a', 'b']; Use `T[]` or `readonly T[]` for simple types (i.e. types which are just primitive names or type references). Use `Array` or `ReadonlyArray` for all other types (union types, intersection types, object types, function types, etc). -Incorrect code for `"array-simple"`: + + +#### ❌ Incorrect ```ts const a: (string | number)[] = ['a', 'b']; @@ -81,7 +87,7 @@ const e: Array = ['a', 'b']; const f: ReadonlyArray = ['a', 'b']; ``` -Correct code for `"array-simple"`: +#### ✅ Correct ```ts const a: Array = ['a', 'b']; @@ -92,7 +98,7 @@ const e: string[] = ['a', 'b']; const f: readonly string[] = ['a', 'b']; ``` -## Combination matrix +## Combination Matrix This matrix lists all possible option combinations and their expected results for different types of Arrays. @@ -111,7 +117,7 @@ This matrix lists all possible option combinations and their expected results fo | `generic` | `array-simple` | `Array` | `Array` | `readonly number[]` | `ReadonlyArray` | | `generic` | `generic` | `Array` | `Array` | `ReadonlyArray` | `ReadonlyArray` | -## Related to +## Related To - TSLint: [array-type](https://palantir.github.io/tslint/rules/array-type/) diff --git a/packages/eslint-plugin/docs/rules/await-thenable.md b/packages/eslint-plugin/docs/rules/await-thenable.md index c4046cbf9883..55c0f87d0ebf 100644 --- a/packages/eslint-plugin/docs/rules/await-thenable.md +++ b/packages/eslint-plugin/docs/rules/await-thenable.md @@ -5,7 +5,11 @@ While it is valid JavaScript to await a non-`Promise`-like value (it will resolv ## Rule Details -Examples of **incorrect** code for this rule: +Examples of code for this rule: + + + +### ❌ Incorrect ```ts await 'value'; @@ -14,7 +18,7 @@ const createValue = () => 'value'; await createValue(); ``` -Examples of **correct** code for this rule: +### ✅ Correct ```ts await Promise.resolve('value'); @@ -28,7 +32,7 @@ await createValue(); If you want to allow code to `await` non-Promise values. This is generally not preferred, but can sometimes be useful for visual consistency. -## Related to +## Related To - TSLint: ['await-promise'](https://palantir.github.io/tslint/rules/await-promise) diff --git a/packages/eslint-plugin/docs/rules/ban-ts-comment.md b/packages/eslint-plugin/docs/rules/ban-ts-comment.md index f9727887603e..9b9e08c78949 100644 --- a/packages/eslint-plugin/docs/rules/ban-ts-comment.md +++ b/packages/eslint-plugin/docs/rules/ban-ts-comment.md @@ -41,7 +41,9 @@ const defaultOptions: Options = { A value of `true` for a particular directive means that this rule will report if it finds any usage of said directive. -For example, with the defaults above the following patterns are considered warnings for single line or comment block lines: + + +#### ❌ Incorrect ```ts if (false) { @@ -56,7 +58,7 @@ if (false) { } ``` -The following patterns are not warnings: +#### ✅ Correct ```ts if (false) { @@ -69,7 +71,11 @@ if (false) { A value of `'allow-with-description'` for a particular directive means that this rule will report if it finds a directive that does not have a description following the directive (on the same line). -For example, with `{ 'ts-expect-error': 'allow-with-description' }` the following patterns are considered a warning: +For example, with `{ 'ts-expect-error': 'allow-with-description' }`: + + + +#### ❌ Incorrect ```ts if (false) { @@ -82,7 +88,7 @@ if (false) { } ``` -The following patterns are not a warning: +#### ✅ Correct ```ts if (false) { @@ -101,7 +107,11 @@ if (false) { Use `minimumDescriptionLength` to set a minimum length for descriptions when using the `allow-with-description` option for a directive. -For example, with `{ 'ts-expect-error': 'allow-with-description', minimumDescriptionLength: 10 }` the following pattern is considered a warning: +For example, with `{ 'ts-expect-error': 'allow-with-description', minimumDescriptionLength: 10 }` the following pattern is: + + + +#### ❌ Incorrect ```ts if (false) { @@ -110,7 +120,7 @@ if (false) { } ``` -The following pattern is not a warning: +#### ✅ Correct ```ts if (false) { @@ -127,7 +137,7 @@ If you want to use all of the TypeScript directives. - TypeScript [Type Checking JavaScript Files](https://www.typescriptlang.org/docs/handbook/type-checking-javascript-files.html) -## Compatibility +## Related To - TSLint: [ban-ts-ignore](https://palantir.github.io/tslint/rules/ban-ts-ignore/) diff --git a/packages/eslint-plugin/docs/rules/ban-tslint-comment.md b/packages/eslint-plugin/docs/rules/ban-tslint-comment.md index 48e49c1ddcbf..121fcaf5ea9a 100644 --- a/packages/eslint-plugin/docs/rules/ban-tslint-comment.md +++ b/packages/eslint-plugin/docs/rules/ban-tslint-comment.md @@ -4,10 +4,12 @@ Useful when migrating from TSLint to ESLint. Once TSLint has been removed, this ## Rule Details -Examples of **incorrect** code for this rule: - All TSLint [rule flags](https://palantir.github.io/tslint/usage/rule-flags/) + + +### ❌ Incorrect + ```js /* tslint:disable */ /* tslint:enable */ @@ -18,10 +20,12 @@ someCode(); // tslint:disable-line // tslint:disable-next-line:rule1 rule2 rule3... ``` -Examples of **correct** code for this rule: +### ✅ Correct ```js // This is a comment that just happens to mention tslint +/* This is a multiline comment that just happens to mention tslint */ +someCode(); // This is a comment that just happens to mention tslint ``` ## When Not To Use It diff --git a/packages/eslint-plugin/docs/rules/ban-types.md b/packages/eslint-plugin/docs/rules/ban-types.md index 0fc1c3af94bc..cfa9635e1c01 100644 --- a/packages/eslint-plugin/docs/rules/ban-types.md +++ b/packages/eslint-plugin/docs/rules/ban-types.md @@ -76,7 +76,11 @@ The default options provide a set of "best practices", intended to provide safet - This is a point of confusion for many developers, who think it means "any object type". - See [this comment for more information](https://github.com/typescript-eslint/typescript-eslint/issues/2063#issuecomment-675156492). -**_Important note:_** the default options suggest using `Record`; this was a stylistic decision, as the built-in `Record` type is considered to look cleaner. +:::important + +The default options suggest using `Record`; this was a stylistic decision, as the built-in `Record` type is considered to look cleaner. + +:::
Default Options @@ -131,7 +135,11 @@ const defaultTypes = { ### Examples -Examples of **incorrect** code with the default options: +Examples of code with the default options: + + + +#### ❌ Incorrect ```ts // use lower-case primitives for consistency @@ -151,7 +159,7 @@ const curly1: {} = 1; const curly2: {} = { a: 'string' }; ``` -Examples of **correct** code with the default options: +#### ✅ Correct ```ts // use lower-case primitives for consistency @@ -173,7 +181,7 @@ const curly1: number = 1; const curly2: Record<'a', string> = { a: 'string' }; ``` -## Compatibility +## Related To - TSLint: [ban-types](https://palantir.github.io/tslint/rules/ban-types) diff --git a/packages/eslint-plugin/docs/rules/brace-style.md b/packages/eslint-plugin/docs/rules/brace-style.md index 87c123a43d7a..1e006ad0ef2d 100644 --- a/packages/eslint-plugin/docs/rules/brace-style.md +++ b/packages/eslint-plugin/docs/rules/brace-style.md @@ -5,7 +5,7 @@ This rule extends the base [`eslint/brace-style`](https://eslint.org/docs/rules/brace-style) rule. It adds support for `enum`, `interface`, `namespace` and `module` declarations. -## How to use +## How to Use ```jsonc { @@ -19,7 +19,11 @@ It adds support for `enum`, `interface`, `namespace` and `module` declarations. See [`eslint/brace-style` options](https://eslint.org/docs/rules/brace-style#options). -Taken with ❤️ [from ESLint core](https://github.com/eslint/eslint/blob/master/docs/rules/brace-style.md) + + +Taken with ❤️ [from ESLint core](https://github.com/eslint/eslint/blob/main/docs/rules/brace-style.md) + + ## Attributes diff --git a/packages/eslint-plugin/docs/rules/class-literal-property-style.md b/packages/eslint-plugin/docs/rules/class-literal-property-style.md index 220d858f03cb..1f29e6dbbea3 100644 --- a/packages/eslint-plugin/docs/rules/class-literal-property-style.md +++ b/packages/eslint-plugin/docs/rules/class-literal-property-style.md @@ -8,44 +8,52 @@ When writing TypeScript libraries that could be used by JavaScript users however This rule aims to ensure that literals exposed by classes are done so consistently, in one of the two style described above. By default this rule prefers the `fields` style as it means JS doesn't have to setup & teardown a function closure. -Note that this rule only checks for constant _literal_ values (string, template string, number, bigint, boolean, regexp, null). It does not check objects or arrays, because a readonly field behaves differently to a getter in those cases. It also does not check functions, as it is a common pattern to use readonly fields with arrow function values as auto-bound methods. +:::note + +This rule only checks for constant _literal_ values (string, template string, number, bigint, boolean, regexp, null). It does not check objects or arrays, because a readonly field behaves differently to a getter in those cases. It also does not check functions, as it is a common pattern to use readonly fields with arrow function values as auto-bound methods. This is because these types can be mutated and carry with them more complex implications about their usage. +::: + ### The `fields` style This style checks for any getter methods that return literal values, and requires them to be defined using fields with the `readonly` modifier instead. -Examples of **correct** code with the `fields` style: +Examples of code with the `fields` style: + + + +#### ❌ Incorrect ```ts /* eslint @typescript-eslint/class-literal-property-style: ["error", "fields"] */ class Mx { - public readonly myField1 = 1; - - // not a literal - public readonly myField2 = [1, 2, 3]; - - private readonly ['myField3'] = 'hello world'; + public static get myField1() { + return 1; + } - public get myField4() { - return `hello from ${window.location.href}`; + private get ['myField2']() { + return 'hello world'; } } ``` -Examples of **incorrect** code with the `fields` style: +#### ✅ Correct ```ts /* eslint @typescript-eslint/class-literal-property-style: ["error", "fields"] */ class Mx { - public static get myField1() { - return 1; - } + public readonly myField1 = 1; - private get ['myField2']() { - return 'hello world'; + // not a literal + public readonly myField2 = [1, 2, 3]; + + private readonly ['myField3'] = 'hello world'; + + public get myField4() { + return `hello from ${window.location.href}`; } } ``` @@ -56,7 +64,23 @@ This style checks for any `readonly` fields that are assigned literal values, an This style pairs well with the [`@typescript-eslint/prefer-readonly`](prefer-readonly.md) rule, as it will identify fields that can be `readonly`, and thus should be made into getters. -Examples of **correct** code with the `getters` style: +Examples of code with the `getters` style: + + + +#### ❌ Incorrect + +```ts +/* eslint @typescript-eslint/class-literal-property-style: ["error", "getters"] */ + +class Mx { + readonly myField1 = 1; + readonly myField2 = `hello world`; + private readonly myField3 = 'hello world'; +} +``` + +#### ✅ Correct ```ts /* eslint @typescript-eslint/class-literal-property-style: ["error", "getters"] */ @@ -78,18 +102,6 @@ class Mx { } ``` -Examples of **incorrect** code with the `getters` style: - -```ts -/* eslint @typescript-eslint/class-literal-property-style: ["error", "getters"] */ - -class Mx { - readonly myField1 = 1; - readonly myField2 = `hello world`; - private readonly myField3 = 'hello world'; -} -``` - ## When Not To Use It When you have no strong preference, or do not wish to enforce a particular style diff --git a/packages/eslint-plugin/docs/rules/comma-dangle.md b/packages/eslint-plugin/docs/rules/comma-dangle.md index 035248bcd46b..29d01bc5a965 100644 --- a/packages/eslint-plugin/docs/rules/comma-dangle.md +++ b/packages/eslint-plugin/docs/rules/comma-dangle.md @@ -29,9 +29,13 @@ This rule has a string option and an object option. - `"generics"` is for trailing comma in generic. (e.g. `function foo() {}`) - `"tuples"` is for trailing comma in tuple. (e.g. `type Foo = [string,]`) -- [See the other options allowed](https://github.com/eslint/eslint/blob/master/docs/rules/comma-dangle.md#options) +- [See the other options allowed](https://github.com/eslint/eslint/blob/main/docs/rules/comma-dangle.md#options) -Taken with ❤️ [from ESLint core](https://github.com/eslint/eslint/blob/master/docs/rules/comma-dangle.md) + + +Taken with ❤️ [from ESLint core](https://github.com/eslint/eslint/blob/main/docs/rules/comma-dangle.md) + + ## Attributes diff --git a/packages/eslint-plugin/docs/rules/comma-spacing.md b/packages/eslint-plugin/docs/rules/comma-spacing.md index 4ed084d8ee00..4fa965a7ff51 100644 --- a/packages/eslint-plugin/docs/rules/comma-spacing.md +++ b/packages/eslint-plugin/docs/rules/comma-spacing.md @@ -5,7 +5,7 @@ This rule extends the base [`eslint/comma-spacing`](https://eslint.org/docs/rules/comma-spacing) rule. It adds support for trailing comma in a types parameters list. -## How to use +## How to Use ```jsonc { @@ -19,7 +19,11 @@ It adds support for trailing comma in a types parameters list. See [`eslint/comma-spacing` options](https://eslint.org/docs/rules/comma-spacing#options). -Taken with ❤️ [from ESLint core](https://github.com/eslint/eslint/blob/master/docs/rules/comma-spacing.md) + + +Taken with ❤️ [from ESLint core](https://github.com/eslint/eslint/blob/main/docs/rules/comma-spacing.md) + + ## Attributes diff --git a/packages/eslint-plugin/docs/rules/consistent-indexed-object-style.md b/packages/eslint-plugin/docs/rules/consistent-indexed-object-style.md index 1aa12cb4484e..cf66c5b7654c 100644 --- a/packages/eslint-plugin/docs/rules/consistent-indexed-object-style.md +++ b/packages/eslint-plugin/docs/rules/consistent-indexed-object-style.md @@ -29,11 +29,17 @@ For example: } ``` -## Rule details +## Rule Details This rule enforces a consistent way to define records. -Examples of **incorrect** code with `record` option. +### `record` + +Examples of code with `record` option. + + + +#### ❌ Incorrect ```ts interface Foo { @@ -45,19 +51,25 @@ type Foo = { }; ``` -Examples of **correct** code with `record` option. +#### ✅ Correct ```ts type Foo = Record; ``` -Examples of **incorrect** code with `index-signature` option. +### `index-signature` + +Examples of code with `index-signature` option. + + + +#### ❌ Incorrect ```ts type Foo = Record; ``` -Examples of **correct** code with `index-signature` option. +#### ✅ Correct ```ts interface Foo { diff --git a/packages/eslint-plugin/docs/rules/consistent-type-assertions.md b/packages/eslint-plugin/docs/rules/consistent-type-assertions.md index 56a3cbddde2d..5b8d84b67669 100644 --- a/packages/eslint-plugin/docs/rules/consistent-type-assertions.md +++ b/packages/eslint-plugin/docs/rules/consistent-type-assertions.md @@ -50,7 +50,11 @@ The const assertion `const x = { foo: 1 } as const`, introduced in TypeScript 3. Assertions to `any` are also ignored by this option. -Examples of **incorrect** code for `{ assertionStyle: 'as', objectLiteralTypeAssertions: 'never' }` (and for `{ assertionStyle: 'as', objectLiteralTypeAssertions: 'allow-as-parameter' }`) +Examples of code for `{ assertionStyle: 'as', objectLiteralTypeAssertions: 'never' }` + + + +#### ❌ Incorrect ```ts const x = { ... } as T; @@ -60,7 +64,7 @@ function foo() { } ``` -Examples of **correct** code for `{ assertionStyle: 'as', objectLiteralTypeAssertions: 'never' }`. +#### ✅ Correct ```ts const x: T = { ... }; @@ -72,7 +76,23 @@ function foo(): T { } ``` -Examples of **correct** code for `{ assertionStyle: 'as', objectLiteralTypeAssertions: 'allow-as-parameter' }`. + + +Examples of code for `{ assertionStyle: 'as', objectLiteralTypeAssertions: 'allow-as-parameter' }` + + + +#### ❌ Incorrect + +```ts +const x = { ... } as T; + +function foo() { + return { ... } as T; +} +``` + +#### ✅ Correct ```tsx const x: T = { ... }; @@ -84,11 +104,13 @@ function foo() { throw { bar: 5 } as Foo } const foo = ; ``` + + ## When Not To Use It If you do not want to enforce consistent type assertions. -## Compatibility +## Related To - TSLint: [no-angle-bracket-type-assertion](https://palantir.github.io/tslint/rules/no-angle-bracket-type-assertion/) - TSLint: [no-object-literal-type-assertion](https://palantir.github.io/tslint/rules/no-object-literal-type-assertion/) diff --git a/packages/eslint-plugin/docs/rules/consistent-type-definitions.md b/packages/eslint-plugin/docs/rules/consistent-type-definitions.md index 6f91d480e52d..85c2033b7730 100644 --- a/packages/eslint-plugin/docs/rules/consistent-type-definitions.md +++ b/packages/eslint-plugin/docs/rules/consistent-type-definitions.md @@ -32,15 +32,19 @@ For example: } ``` -## Rule Details +### `interface` -Examples of **incorrect** code with `interface` option. +Examples of code with `interface` option. + + + +#### ❌ Incorrect ```ts type T = { x: number }; ``` -Examples of **correct** code with `interface` option. +#### ✅ Correct ```ts type T = string; @@ -51,7 +55,13 @@ interface T { } ``` -Examples of **incorrect** code with `type` option. +### `type` + +Examples of code with `type` option. + + + +#### ❌ Incorrect ```ts interface T { @@ -59,7 +69,7 @@ interface T { } ``` -Examples of **correct** code with `type` option. +#### ✅ Correct ```ts type T = { x: number }; @@ -69,7 +79,7 @@ type T = { x: number }; If you specifically want to use an interface or type literal for stylistic reasons, you can disable this rule. -## Compatibility +## Related To - TSLint: [interface-over-type-literal](https://palantir.github.io/tslint/rules/interface-over-type-literal/) diff --git a/packages/eslint-plugin/docs/rules/consistent-type-exports.md b/packages/eslint-plugin/docs/rules/consistent-type-exports.md index cb3b01f51dfe..71303d3659e8 100644 --- a/packages/eslint-plugin/docs/rules/consistent-type-exports.md +++ b/packages/eslint-plugin/docs/rules/consistent-type-exports.md @@ -9,14 +9,18 @@ transpilers to drop exports without knowing the types of the dependencies. This rule aims to standardize the use of type exports style across a codebase. -Given a class `Button`, and an interface `ButtonProps`, examples of **correct** code: +Given a class `Button`, and an interface `ButtonProps`, examples of code: + + + +### ❌ Incorrect ```ts export { Button } from 'some-library'; export type { ButtonProps } from 'some-library'; ``` -Examples of **incorrect** code: +### ✅ Correct ```ts export { Button, ButtonProps } from 'some-library'; diff --git a/packages/eslint-plugin/docs/rules/default-param-last.md b/packages/eslint-plugin/docs/rules/default-param-last.md index 0eb9aa429df4..859bfd6ebb5b 100644 --- a/packages/eslint-plugin/docs/rules/default-param-last.md +++ b/packages/eslint-plugin/docs/rules/default-param-last.md @@ -5,7 +5,9 @@ This rule extends the base [`eslint/default-param-last`](https://eslint.org/docs/rules/default-param-last) rule. It adds support for optional parameters. -Examples of **incorrect** code for this rule: + + +### ❌ Incorrect ```ts /* eslint @typescript-eslint/default-param-last: ["error"] */ @@ -21,7 +23,7 @@ class Foo { } ``` -Examples of **correct** code for this rule: +### ✅ Correct ```ts /* eslint @typescript-eslint/default-param-last: ["error"] */ @@ -39,7 +41,7 @@ class Foo { } ``` -## How to use +## How to Use ```jsonc { @@ -53,7 +55,11 @@ class Foo { See [`eslint/default-param-last` options](https://eslint.org/docs/rules/default-param-last#options). -Taken with ❤️ [from ESLint core](https://github.com/eslint/eslint/blob/master/docs/rules/default-param-last.md) + + +Taken with ❤️ [from ESLint core](https://github.com/eslint/eslint/blob/main/docs/rules/default-param-last.md) + + ## Attributes diff --git a/packages/eslint-plugin/docs/rules/dot-notation.md b/packages/eslint-plugin/docs/rules/dot-notation.md index bc2b4f340f21..be4f23fd7763 100644 --- a/packages/eslint-plugin/docs/rules/dot-notation.md +++ b/packages/eslint-plugin/docs/rules/dot-notation.md @@ -8,7 +8,7 @@ It adds: - Support for optionally ignoring computed `private` and/or `protected` member access. - Compatibility with TypeScript's `noPropertyAccessFromIndexSignature` option. -## How to use +## How to Use ```jsonc { @@ -80,7 +80,11 @@ x['hello'] = 123; If the TypeScript compiler option `noPropertyAccessFromIndexSignature` is set to `true`, then the above code is always allowed, even if `allowIndexSignaturePropertyAccess` is `false`. -Taken with ❤️ [from ESLint core](https://github.com/eslint/eslint/blob/master/docs/rules/dot-notation.md) + + +Taken with ❤️ [from ESLint core](https://github.com/eslint/eslint/blob/main/docs/rules/dot-notation.md) + + ## Attributes diff --git a/packages/eslint-plugin/docs/rules/explicit-function-return-type.md b/packages/eslint-plugin/docs/rules/explicit-function-return-type.md index 27e731d3d84f..4c199878bb1f 100644 --- a/packages/eslint-plugin/docs/rules/explicit-function-return-type.md +++ b/packages/eslint-plugin/docs/rules/explicit-function-return-type.md @@ -9,7 +9,9 @@ shouldn't. This rule aims to ensure that the values returned from functions are of the expected type. -The following patterns are considered warnings: + + +### ❌ Incorrect ```ts // Should indicate that no value is returned (void) @@ -33,7 +35,7 @@ class Test { } ``` -The following patterns are not warnings: +### ✅ Correct ```ts // No return value should be expected (void) @@ -108,7 +110,11 @@ If you are working on a codebase within which you lint non-TypeScript code (i.e. ### `allowExpressions` -Examples of **incorrect** code for this rule with `{ allowExpressions: true }`: +Examples of code for this rule with `{ allowExpressions: true }`: + + + +#### ❌ Incorrect ```ts function test() {} @@ -118,7 +124,7 @@ const fn = () => {}; export default () => {}; ``` -Examples of **correct** code for this rule with `{ allowExpressions: true }`: +#### ✅ Correct ```ts node.addEventListener('click', () => {}); @@ -130,7 +136,11 @@ const foo = arr.map(i => i * i); ### `allowTypedFunctionExpressions` -Examples of **incorrect** code for this rule with `{ allowTypedFunctionExpressions: true }`: +Examples of code for this rule with `{ allowTypedFunctionExpressions: true }`: + + + +#### ❌ Incorrect ```ts let arrowFn = () => 'test'; @@ -144,7 +154,7 @@ let objectProp = { }; ``` -Examples of additional **correct** code for this rule with `{ allowTypedFunctionExpressions: true }`: +#### ✅ Correct ```ts type FuncType = () => string; @@ -184,7 +194,11 @@ functionWithObjectArg({ ### `allowHigherOrderFunctions` -Examples of **incorrect** code for this rule with `{ allowHigherOrderFunctions: true }`: +Examples of code for this rule with `{ allowHigherOrderFunctions: true }`: + + + +#### ❌ Incorrect ```ts var arrowFn = () => () => {}; @@ -194,7 +208,7 @@ function fn() { } ``` -Examples of **correct** code for this rule with `{ allowHigherOrderFunctions: true }`: +#### ✅ Correct ```ts var arrowFn = () => (): void => {}; @@ -206,14 +220,18 @@ function fn() { ### `allowDirectConstAssertionInArrowFunctions` -Examples of **incorrect** code for this rule with `{ allowDirectConstAssertionInArrowFunctions: true }`: +Examples of code for this rule with `{ allowDirectConstAssertionInArrowFunctions: true }`: + + + +#### ❌ Incorrect ```ts const func = (value: number) => ({ type: 'X', value } as any); const func = (value: number) => ({ type: 'X', value } as Action); ``` -Examples of **correct** code for this rule with `{ allowDirectConstAssertionInArrowFunctions: true }`: +#### ✅ Correct ```ts const func = (value: number) => ({ foo: 'bar', value } as const); @@ -222,7 +240,11 @@ const func = () => x as const; ### `allowConciseArrowFunctionExpressionsStartingWithVoid` -Examples of **incorrect** code for this rule with `{ allowConciseArrowFunctionExpressionsStartingWithVoid: true }`: +Examples of code for this rule with `{ allowConciseArrowFunctionExpressionsStartingWithVoid: true }`: + + + +#### ❌ Incorrect ```ts var join = (a: string, b: string) => `${a}${b}`; @@ -232,7 +254,7 @@ const log = (message: string) => { }; ``` -Examples of **correct** code for this rule with `{ allowConciseArrowFunctionExpressionsStartingWithVoid: true }`: +#### ✅ Correct ```ts var log = (message: string) => void console.log(message); diff --git a/packages/eslint-plugin/docs/rules/explicit-member-accessibility.md b/packages/eslint-plugin/docs/rules/explicit-member-accessibility.md index f65af54c4d5d..6f6d97ccefe2 100644 --- a/packages/eslint-plugin/docs/rules/explicit-member-accessibility.md +++ b/packages/eslint-plugin/docs/rules/explicit-member-accessibility.md @@ -343,7 +343,7 @@ If you think defaulting to public is a good default, then you should consider us - TypeScript [Accessibility Modifiers](https://www.typescriptlang.org/docs/handbook/classes.html#public-private-and-protected-modifiers) -## Compatibility +## Related To - TSLint: [member-access](http://palantir.github.io/tslint/rules/member-access/) diff --git a/packages/eslint-plugin/docs/rules/explicit-module-boundary-types.md b/packages/eslint-plugin/docs/rules/explicit-module-boundary-types.md index fe3b92b7b3d0..f857b813ba90 100644 --- a/packages/eslint-plugin/docs/rules/explicit-module-boundary-types.md +++ b/packages/eslint-plugin/docs/rules/explicit-module-boundary-types.md @@ -6,7 +6,9 @@ Explicit types for function return values and arguments makes it clear to any ca This rule aims to ensure that the values returned from a module are of the expected type. -The following patterns are considered warnings: + + +### ❌ Incorrect ```ts // Should indicate that no value is returned (void) @@ -34,7 +36,7 @@ export class Test { } ``` -The following patterns are not warnings: +### ✅ Correct ```ts // Function is not exported @@ -128,13 +130,17 @@ If you are working on a codebase within which you lint non-TypeScript code (i.e. ### `allowArgumentsExplicitlyTypedAsAny` -Examples of **incorrect** code for this rule with `{ allowArgumentsExplicitlyTypedAsAny: false }`: +Examples of code for this rule with `{ allowArgumentsExplicitlyTypedAsAny: false }`: + + + +#### ❌ Incorrect ```ts export const func = (value: any): number => value + 1; ``` -Examples of **correct** code for this rule with `{ allowArgumentsExplicitlyTypedAsAny: true }`: +#### ✅ Correct ```ts export const func = (value: number): number => value + 1; @@ -142,7 +148,11 @@ export const func = (value: number): number => value + 1; ### `allowDirectConstAssertionInArrowFunctions` -Examples of **incorrect** code for this rule with `{ allowDirectConstAssertionInArrowFunctions: false }`: +Examples of code for this rule with `{ allowDirectConstAssertionInArrowFunctions: false }`: + + + +#### ❌ Incorrect ```ts export const func = (value: number) => ({ type: 'X', value }); @@ -153,7 +163,7 @@ export const foo = () => export const bar = () => 1; ``` -Examples of **correct** code for this rule with `{ allowDirectConstAssertionInArrowFunctions: true }`: +#### ✅ Correct ```ts export const func = (value: number) => ({ type: 'X', value } as const); @@ -181,21 +191,25 @@ You may pass function/method names you would like this rule to ignore, like so: ### `allowHigherOrderFunctions` -Examples of **incorrect** code for this rule with `{ allowHigherOrderFunctions: false }`: +Examples of code for this rule with `{ allowHigherOrderFunctions: false }`: + + + +#### ❌ Incorrect ```ts -export const arrowFn = () => (): void => {}; +export const arrowFn = () => () => {}; export function fn() { - return function (): void {}; + return function () {}; } export function foo(outer: string) { - return function (inner: string): void {}; + return function (inner: string) {}; } ``` -Examples of **correct** code for this rule with `{ allowHigherOrderFunctions: true }`: +#### ✅ Correct ```ts export const arrowFn = () => (): void => {}; @@ -211,7 +225,11 @@ export function foo(outer: string) { ### `allowTypedFunctionExpressions` -Examples of **incorrect** code for this rule with `{ allowTypedFunctionExpressions: false }`: +Examples of code for this rule with `{ allowTypedFunctionExpressions: false }`: + + + +#### ❌ Incorrect ```ts export let arrowFn = () => 'test'; @@ -227,7 +245,7 @@ export let objectProp = { export const foo = bar => {}; ``` -Examples of additional **correct** code for this rule with `{ allowTypedFunctionExpressions: true }`: +#### ✅ Correct ```ts type FuncType = () => string; diff --git a/packages/eslint-plugin/docs/rules/func-call-spacing.md b/packages/eslint-plugin/docs/rules/func-call-spacing.md index 0070c5633030..4560bea5e4be 100644 --- a/packages/eslint-plugin/docs/rules/func-call-spacing.md +++ b/packages/eslint-plugin/docs/rules/func-call-spacing.md @@ -5,7 +5,7 @@ This rule extends the base [`eslint/func-call-spacing`](https://eslint.org/docs/rules/func-call-spacing) rule. It adds support for generic type parameters on function calls. -## How to use +## How to Use ```jsonc { @@ -19,7 +19,11 @@ It adds support for generic type parameters on function calls. See [`eslint/func-call-spacing` options](https://eslint.org/docs/rules/func-call-spacing#options). -Taken with ❤️ [from ESLint core](https://github.com/eslint/eslint/blob/master/docs/rules/func-call-spacing.md) + + +Taken with ❤️ [from ESLint core](https://github.com/eslint/eslint/blob/main/docs/rules/func-call-spacing.md) + + ## Attributes diff --git a/packages/eslint-plugin/docs/rules/indent.md b/packages/eslint-plugin/docs/rules/indent.md index 1c8af15906c8..b787231aade5 100644 --- a/packages/eslint-plugin/docs/rules/indent.md +++ b/packages/eslint-plugin/docs/rules/indent.md @@ -1,13 +1,15 @@ # Enforce consistent indentation (`indent`) -## PLEASE READ THIS ISSUE BEFORE USING THIS RULE [#1824](https://github.com/typescript-eslint/typescript-eslint/issues/1824) +## Warning + +PLEASE READ THIS ISSUE BEFORE USING THIS RULE [#1824](https://github.com/typescript-eslint/typescript-eslint/issues/1824) ## Rule Details This rule extends the base [`eslint/indent`](https://eslint.org/docs/rules/indent) rule. It adds support for TypeScript nodes. -## How to use +## How to Use ```jsonc { @@ -21,7 +23,11 @@ It adds support for TypeScript nodes. See [`eslint/indent` options](https://eslint.org/docs/rules/indent#options). -Taken with ❤️ [from ESLint core](https://github.com/eslint/eslint/blob/master/docs/rules/indent.md) + + +Taken with ❤️ [from ESLint core](https://github.com/eslint/eslint/blob/main/docs/rules/indent.md) + + ## Attributes diff --git a/packages/eslint-plugin/docs/rules/init-declarations.md b/packages/eslint-plugin/docs/rules/init-declarations.md index 53783e23759b..566e01c1d637 100644 --- a/packages/eslint-plugin/docs/rules/init-declarations.md +++ b/packages/eslint-plugin/docs/rules/init-declarations.md @@ -5,7 +5,7 @@ This rule extends the base [`eslint/init-declarations`](https://eslint.org/docs/rules/init-declarations) rule. It adds support for TypeScript's `declare` variables. -## How to use +## How to Use ```jsonc { @@ -19,7 +19,11 @@ It adds support for TypeScript's `declare` variables. See [`eslint/init-declarations` options](https://eslint.org/docs/rules/init-declarations#options). -Taken with ❤️ [from ESLint core](https://github.com/eslint/eslint/blob/master/docs/rules/init-declarations.md) + + +Taken with ❤️ [from ESLint core](https://github.com/eslint/eslint/blob/main/docs/rules/init-declarations.md) + + ## Attributes diff --git a/packages/eslint-plugin/docs/rules/keyword-spacing.md b/packages/eslint-plugin/docs/rules/keyword-spacing.md index f4d4403bd400..0e812a3b74ae 100644 --- a/packages/eslint-plugin/docs/rules/keyword-spacing.md +++ b/packages/eslint-plugin/docs/rules/keyword-spacing.md @@ -5,7 +5,7 @@ This rule extends the base [`eslint/keyword-spacing`](https://eslint.org/docs/rules/keyword-spacing) rule. This version adds support for generic type parameters on function calls. -## How to use +## How to Use ```jsonc { @@ -19,7 +19,11 @@ This version adds support for generic type parameters on function calls. See [`eslint/keyword-spacing` options](https://eslint.org/docs/rules/keyword-spacing#options). -Taken with ❤️ [from ESLint core](https://github.com/eslint/eslint/blob/master/docs/rules/keyword-spacing.md) + + +Taken with ❤️ [from ESLint core](https://github.com/eslint/eslint/blob/main/docs/rules/keyword-spacing.md) + + ## Attributes diff --git a/packages/eslint-plugin/docs/rules/lines-between-class-members.md b/packages/eslint-plugin/docs/rules/lines-between-class-members.md index ab40dfdbdf3e..0e8fe0d2fa1d 100644 --- a/packages/eslint-plugin/docs/rules/lines-between-class-members.md +++ b/packages/eslint-plugin/docs/rules/lines-between-class-members.md @@ -30,7 +30,7 @@ This rule has a string option and an object option. - `"exceptAfterOverload": true` (default) - Skip checking empty lines after overload class members - `"exceptAfterOverload": false` - **do not** skip checking empty lines after overload class members -- [See the other options allowed](https://github.com/eslint/eslint/blob/master/docs/rules/lines-between-class-members.md#options) +- [See the other options allowed](https://github.com/eslint/eslint/blob/main/docs/rules/lines-between-class-members.md#options) ### `exceptAfterOverload: true` @@ -70,7 +70,11 @@ class foo { } ``` -Taken with ❤️ [from ESLint core](https://github.com/eslint/eslint/blob/master/docs/rules/lines-between-class-members.md) + + +Taken with ❤️ [from ESLint core](https://github.com/eslint/eslint/blob/main/docs/rules/lines-between-class-members.md) + + ## Attributes diff --git a/packages/eslint-plugin/docs/rules/member-delimiter-style.md b/packages/eslint-plugin/docs/rules/member-delimiter-style.md index 966a1a7fbdac..4a979cd9fbe5 100644 --- a/packages/eslint-plugin/docs/rules/member-delimiter-style.md +++ b/packages/eslint-plugin/docs/rules/member-delimiter-style.md @@ -149,7 +149,11 @@ For example, to require commas for `type`s, and semicolons for multiline `interf ## Examples -Examples of **incorrect** code for this rule with the default config: +Examples of code for this rule with the default config: + + + +### ❌ Incorrect ```ts @@ -178,7 +182,7 @@ type FooBar = { name: string, greet(): string } type FooBar = { name: string; greet(): string; } ``` -Examples of **correct** code for this rule with the default config: +### ✅ Correct ```ts diff --git a/packages/eslint-plugin/docs/rules/member-ordering.md b/packages/eslint-plugin/docs/rules/member-ordering.md index 5924b18aaaa7..40112c777c03 100644 --- a/packages/eslint-plugin/docs/rules/member-ordering.md +++ b/packages/eslint-plugin/docs/rules/member-ordering.md @@ -26,10 +26,10 @@ These options allow to specify how to group the members and sort their groups. type TypeOptions = | { memberTypes: Array | 'never', - order?: 'alphabetically' | 'as-written', + order?: 'alphabetically' | 'alphabetically-case-insensitive' | 'as-written', } | { - order: 'alphabetically', + order: 'alphabetically' | 'alphabetically-case-insensitive' | 'as-written', }; { @@ -956,21 +956,21 @@ It is possible to sort all members within a group alphabetically. #### Configuration: `{ "default": { "memberTypes": , "order": "alphabetically" } }` -This will apply the default order (see above) and enforce an alphabetic order within each group. +This will apply the default order (see above) and enforce an alphabetic case-sensitive order within each group. ##### Incorrect examples ```ts interface Foo { + B: x; a: x; - b: x; c: x; new (): Bar; (): Baz; + B(): void; a(): void; - b(): void; c(): void; // Wrong group order, should be placed before all field definitions @@ -982,8 +982,8 @@ interface Foo { interface Foo { [a: string]: number; + B: x; a: x; - b: x; c: x; new (): Bar; @@ -991,7 +991,7 @@ interface Foo { // Wrong alphabetic order within group c(): void; - b(): void; + B(): void; a(): void; } ``` @@ -1017,11 +1017,78 @@ interface Foo { Note: Wrong alphabetic order `b(): void` should come after `a: b`. +### Sorting alphabetically case-insensitive within member groups + +It is possible to sort all members within a group alphabetically with case insensitivity. + +#### Configuration: `{ "default": { "memberTypes": , "order": "alphabetically-case-insensitive" } }` + +This will apply the default order (see above) and enforce an alphabetic case-insensitive order within each group. + +##### Incorrect examples + +```ts +interface Foo { + a: x; + B: x; + c: x; + + new (): Bar; + (): Baz; + + a(): void; + b(): void; + C(): void; + + // Wrong group order, should be placed before all field definitions + [a: string]: number; +} +``` + +```ts +interface Foo { + [a: string]: number; + + a: x; + B: x; + c: x; + + new (): Bar; + (): Baz; + + // Wrong alphabetic order within group + C(): void; + b(): void; + a(): void; +} +``` + +### Sorting alphabetically case-insensitive while ignoring member groups + +It is also possible to sort all members with case insensitivity and ignore the member groups completely. + +#### Configuration: `{ "default": { "memberTypes": "never", "order": "alphabetically-case-insensitive" } }` + +##### Incorrect example + +```ts +interface Foo { + B(): void; + a: number; + + [a: string]: number; // Order doesn't matter (no sortable identifier) + new (): Bar; // Order doesn't matter (no sortable identifier) + (): Baz; // Order doesn't matter (no sortable identifier) +} +``` + +Note: Wrong alphabetic order `B(): void` should come after `a: number`. + ## When Not To Use It If you don't care about the general structure of your classes and interfaces, then you will not need this rule. -## Compatibility +## Related To - TSLint: [member-ordering](https://palantir.github.io/tslint/rules/member-ordering/) diff --git a/packages/eslint-plugin/docs/rules/method-signature-style.md b/packages/eslint-plugin/docs/rules/method-signature-style.md index acfc4317db97..412150683553 100644 --- a/packages/eslint-plugin/docs/rules/method-signature-style.md +++ b/packages/eslint-plugin/docs/rules/method-signature-style.md @@ -32,9 +32,13 @@ This rule accepts one string option: The default is `"property"`. -## Rule Details +### `property` -Examples of **incorrect** code with `property` option. +Examples of code with `property` option. + + + +#### ❌ Incorrect ```ts interface T1 { @@ -50,7 +54,7 @@ interface T3 { } ``` -Examples of **correct** code with `property` option. +#### ✅ Correct ```ts interface T1 { @@ -67,7 +71,13 @@ interface T3 { } ``` -Examples of **incorrect** code with `method` option. +### `method` + +Examples of code with `method` option. + + + +#### ❌ Incorrect ```ts interface T1 { @@ -78,7 +88,7 @@ type T2 = { }; ``` -Examples of **correct** code with `method` option. +#### ✅ Correct ```ts interface T1 { diff --git a/packages/eslint-plugin/docs/rules/no-array-constructor.md b/packages/eslint-plugin/docs/rules/no-array-constructor.md index dd9dd25bc140..5eae0cf8645a 100644 --- a/packages/eslint-plugin/docs/rules/no-array-constructor.md +++ b/packages/eslint-plugin/docs/rules/no-array-constructor.md @@ -5,7 +5,9 @@ This rule extends the base [`eslint/no-array-constructor`](https://eslint.org/docs/rules/no-array-constructor) rule. It adds support for the generically typed `Array` constructor (`new Array()`). -Examples of **incorrect** code for this rule: + + +### ❌ Incorrect ```ts /*eslint no-array-constructor: "error"*/ @@ -14,7 +16,7 @@ Array(0, 1, 2); new Array(0, 1, 2); ``` -Examples of **correct** code for this rule: +### ✅ Correct ```ts /*eslint no-array-constructor: "error"*/ @@ -26,7 +28,7 @@ Array(500); new Array(someOtherArray.length); ``` -## How to use +## How to Use ```jsonc { @@ -40,7 +42,11 @@ new Array(someOtherArray.length); See [`eslint/no-array-constructor` options](https://eslint.org/docs/rules/no-array-constructor#options). -Taken with ❤️ [from ESLint core](https://github.com/eslint/eslint/blob/master/docs/rules/no-array-constructor.md) + + +Taken with ❤️ [from ESLint core](https://github.com/eslint/eslint/blob/main/docs/rules/no-array-constructor.md) + + ## Attributes diff --git a/packages/eslint-plugin/docs/rules/no-base-to-string.md b/packages/eslint-plugin/docs/rules/no-base-to-string.md index 3daf5624792b..5dd59bb2bb58 100644 --- a/packages/eslint-plugin/docs/rules/no-base-to-string.md +++ b/packages/eslint-plugin/docs/rules/no-base-to-string.md @@ -13,7 +13,9 @@ This rule has some overlap with [`restrict-plus-operands`](./restrict-plus-opera This rule prevents accidentally defaulting to the base Object `.toString()` method. -Examples of **incorrect** code for this rule: + + +### ❌ Incorrect ```ts // Passing an object or class instance to string concatenation: @@ -28,7 +30,7 @@ value + ''; ({}.toString()); ``` -Examples of **correct** code for this rule: +### ✅ Correct ```ts // These types all have useful .toString()s diff --git a/packages/eslint-plugin/docs/rules/no-confusing-non-null-assertion.md b/packages/eslint-plugin/docs/rules/no-confusing-non-null-assertion.md index cac0acbc2434..78655b69db82 100644 --- a/packages/eslint-plugin/docs/rules/no-confusing-non-null-assertion.md +++ b/packages/eslint-plugin/docs/rules/no-confusing-non-null-assertion.md @@ -10,7 +10,9 @@ a !== b; // not equals test(`!==`) a! === b; // a non-null assertions(`!`) and an triple equals test(`===`) ``` -Examples of **incorrect** code for this rule: + + +### ❌ Incorrect ```ts interface Foo { @@ -23,7 +25,7 @@ const isEqualsBar = foo.bar! == 'hello'; const isEqualsNum = 1 + foo.num! == 2; ``` -Examples of **correct** code for this rule: +### ✅ Correct ```ts diff --git a/packages/eslint-plugin/docs/rules/no-confusing-void-expression.md b/packages/eslint-plugin/docs/rules/no-confusing-void-expression.md index 3ea101e18b4a..0ca36a150cd1 100644 --- a/packages/eslint-plugin/docs/rules/no-confusing-void-expression.md +++ b/packages/eslint-plugin/docs/rules/no-confusing-void-expression.md @@ -9,7 +9,9 @@ This rule provides automatic fixes for most common cases. ## Examples -Examples of **incorrect** code for this rule: + + +### ❌ Incorrect ```ts // somebody forgot that `alert` doesn't return anything @@ -29,7 +31,7 @@ function doSomething() { } ``` -Examples of **correct** code for this rule: +### ✅ Correct ```ts // just a regular void function in a statement position @@ -144,7 +146,7 @@ The return type of a function can be inspected by going to its definition or hov If you don't care about being explicit about the void type in actual code then don't use this rule. Also, if you prefer concise coding style then also don't use it. -## Related to +## Related To - TSLint: ['no-void-expression'](https://palantir.github.io/tslint/rules/no-void-expression/) diff --git a/packages/eslint-plugin/docs/rules/no-dupe-class-members.md b/packages/eslint-plugin/docs/rules/no-dupe-class-members.md index 121c07a46703..ed7a38923d3f 100644 --- a/packages/eslint-plugin/docs/rules/no-dupe-class-members.md +++ b/packages/eslint-plugin/docs/rules/no-dupe-class-members.md @@ -5,7 +5,7 @@ This rule extends the base [`eslint/no-dupe-class-members`](https://eslint.org/docs/rules/no-dupe-class-members) rule. It adds support for TypeScript's method overload definitions. -## How to use +## How to Use ```jsonc { @@ -19,7 +19,11 @@ It adds support for TypeScript's method overload definitions. See [`eslint/no-dupe-class-members` options](https://eslint.org/docs/rules/no-dupe-class-members#options). -Taken with ❤️ [from ESLint core](https://github.com/eslint/eslint/blob/master/docs/rules/no-dupe-class-members.md) + + +Taken with ❤️ [from ESLint core](https://github.com/eslint/eslint/blob/main/docs/rules/no-dupe-class-members.md) + + ## Attributes diff --git a/packages/eslint-plugin/docs/rules/no-duplicate-imports.md b/packages/eslint-plugin/docs/rules/no-duplicate-imports.md index 1cc99ddd0977..193d69e873db 100644 --- a/packages/eslint-plugin/docs/rules/no-duplicate-imports.md +++ b/packages/eslint-plugin/docs/rules/no-duplicate-imports.md @@ -5,7 +5,7 @@ This rule extends the base [`eslint/no-duplicate-imports`](https://eslint.org/docs/rules/no-duplicate-imports) rule. This version adds support for type-only import and export. -## How to use +## How to Use ```jsonc { @@ -19,7 +19,11 @@ This version adds support for type-only import and export. See [`eslint/no-duplicate-imports` options](https://eslint.org/docs/rules/no-duplicate-imports#options). -Taken with ❤️ [from ESLint core](https://github.com/eslint/eslint/blob/master/docs/rules/no-duplicate-imports.md) + + +Taken with ❤️ [from ESLint core](https://github.com/eslint/eslint/blob/main/docs/rules/no-duplicate-imports.md) + + ## Attributes diff --git a/packages/eslint-plugin/docs/rules/no-dynamic-delete.md b/packages/eslint-plugin/docs/rules/no-dynamic-delete.md index 4d12eef8ffe8..63e41781d5e3 100644 --- a/packages/eslint-plugin/docs/rules/no-dynamic-delete.md +++ b/packages/eslint-plugin/docs/rules/no-dynamic-delete.md @@ -8,7 +8,22 @@ Using the `delete` operator on keys that aren't runtime constants could be a sig Using `Object`s with added and removed keys can cause occasional edge case bugs, such as if a key is named `"hasOwnProperty"`. Consider using a `Map` or `Set` if you’re storing collections of objects. -Examples of **correct** code with this rule: + + +### ❌ Incorrect + +```ts +// Can be replaced with the constant equivalents, such as container.aaa +delete container['aaa']; +delete container['Infinity']; + +// Dynamic, difficult-to-reason-about lookups +const name = 'name'; +delete container[name]; +delete container[name.toUpperCase()]; +``` + +### ✅ Correct ```ts const container: { [i: string]: number } = { @@ -23,19 +38,6 @@ delete container[7]; delete container['-Infinity']; ``` -Examples of **incorrect** code with this rule: - -```ts -// Can be replaced with the constant equivalents, such as container.aaa -delete container['aaa']; -delete container['Infinity']; - -// Dynamic, difficult-to-reason-about lookups -const name = 'name'; -delete container[name]; -delete container[name.toUpperCase()]; -``` - ## When Not To Use It When you know your keys are safe to delete, this rule can be unnecessary. @@ -44,7 +46,7 @@ Some environments such as older browsers might not support `Map` and `Set`. Do not consider this rule as performance advice before profiling your code's bottlenecks. Even repeated minor performance slowdowns likely do not significantly affect your application's general perceived speed. -## Related to +## Related To - TSLint: [no-dynamic-delete](https://palantir.github.io/tslint/rules/no-dynamic-delete) diff --git a/packages/eslint-plugin/docs/rules/no-empty-function.md b/packages/eslint-plugin/docs/rules/no-empty-function.md index b7d0fb99f5f2..e380be0a29b1 100644 --- a/packages/eslint-plugin/docs/rules/no-empty-function.md +++ b/packages/eslint-plugin/docs/rules/no-empty-function.md @@ -7,7 +7,7 @@ It adds support for handling TypeScript specific code that would otherwise trigg One example of valid TypeScript specific code that would otherwise trigger the `no-empty-function` rule is the use of [parameter properties](https://www.typescriptlang.org/docs/handbook/classes.html#parameter-properties) in constructor functions. -## How to use +## How to Use ```jsonc { @@ -75,7 +75,7 @@ class Foo { } ``` -## How to use +## How to Use ```jsonc { @@ -85,9 +85,11 @@ class Foo { } ``` ---- + -Taken with ❤️ [from ESLint core](https://github.com/eslint/eslint/blob/master/docs/rules/no-empty-function.md) +Taken with ❤️ [from ESLint core](https://github.com/eslint/eslint/blob/main/docs/rules/no-empty-function.md) + + ## Attributes diff --git a/packages/eslint-plugin/docs/rules/no-empty-interface.md b/packages/eslint-plugin/docs/rules/no-empty-interface.md index 5eca9c8af107..ddeab6579189 100644 --- a/packages/eslint-plugin/docs/rules/no-empty-interface.md +++ b/packages/eslint-plugin/docs/rules/no-empty-interface.md @@ -7,7 +7,9 @@ the interface is equivalent to an empty object (`{}`). In both cases it can be o This rule aims to ensure that only meaningful interfaces are declared in the code. -The following patterns are considered warnings: + + +### ❌ Incorrect ```ts // an empty interface @@ -20,7 +22,7 @@ interface Bar extends Foo {} interface Baz {} ``` -The following patterns are not warnings: +### ✅ Correct ```ts // an interface with any number of members @@ -38,6 +40,8 @@ interface Bar { interface Baz extends Foo, Bar {} ``` + + ### Options This rule accepts a single object option with the following default configuration: @@ -59,7 +63,7 @@ This rule accepts a single object option with the following default configuratio If you don't care about having empty/meaningless interfaces, then you will not need this rule. -## Compatibility +## Related To - TSLint: [no-empty-interface](https://palantir.github.io/tslint/rules/no-empty-interface/) diff --git a/packages/eslint-plugin/docs/rules/no-explicit-any.md b/packages/eslint-plugin/docs/rules/no-explicit-any.md index 85256e33c716..7e3435b3b58d 100644 --- a/packages/eslint-plugin/docs/rules/no-explicit-any.md +++ b/packages/eslint-plugin/docs/rules/no-explicit-any.md @@ -11,7 +11,9 @@ TypeScript has a compiler flag for `--noImplicitAny` that will prevent an `any` type from being implied by the compiler, but doesn't prevent `any` from being explicitly used. -The following patterns are considered warnings: + + +### ❌ Incorrect ```ts const age: any = 'seventeen'; @@ -49,7 +51,7 @@ function greet(param: Array): string {} function greet(param: Array): Array {} ``` -The following patterns are not warnings: +### ✅ Correct ```ts const age: number = 17; @@ -176,7 +178,7 @@ and you want to be able to specify `any`. - TypeScript [any type](https://www.typescriptlang.org/docs/handbook/basic-types.html#any) -## Compatibility +## Related To - TSLint: [no-any](https://palantir.github.io/tslint/rules/no-any/) diff --git a/packages/eslint-plugin/docs/rules/no-extra-non-null-assertion.md b/packages/eslint-plugin/docs/rules/no-extra-non-null-assertion.md index 8eb27f805c4e..d55167ff2b58 100644 --- a/packages/eslint-plugin/docs/rules/no-extra-non-null-assertion.md +++ b/packages/eslint-plugin/docs/rules/no-extra-non-null-assertion.md @@ -2,7 +2,11 @@ ## Rule Details -Examples of **incorrect** code for this rule: +Examples of code for this rule: + + + +### ❌ Incorrect ```ts const foo: { bar: number } | null = null; @@ -21,7 +25,7 @@ function foo(bar?: { n: number }) { } ``` -Examples of **correct** code for this rule: +### ✅ Correct ```ts const foo: { bar: number } | null = null; @@ -40,7 +44,7 @@ function foo(bar?: { n: number }) { } ``` -## How to use +## How to Use ```json { diff --git a/packages/eslint-plugin/docs/rules/no-extra-parens.md b/packages/eslint-plugin/docs/rules/no-extra-parens.md index 8a748ac5a231..1ae12a47be38 100644 --- a/packages/eslint-plugin/docs/rules/no-extra-parens.md +++ b/packages/eslint-plugin/docs/rules/no-extra-parens.md @@ -5,7 +5,7 @@ This rule extends the base [`eslint/no-extra-parens`](https://eslint.org/docs/rules/no-extra-parens) rule. It adds support for TypeScript type assertions. -## How to use +## How to Use ```jsonc { @@ -19,7 +19,11 @@ It adds support for TypeScript type assertions. See [`eslint/no-extra-parens` options](https://eslint.org/docs/rules/no-extra-parens#options). -Taken with ❤️ [from ESLint core](https://github.com/eslint/eslint/blob/master/docs/rules/no-extra-parens.md) + + +Taken with ❤️ [from ESLint core](https://github.com/eslint/eslint/blob/main/docs/rules/no-extra-parens.md) + + ## Attributes diff --git a/packages/eslint-plugin/docs/rules/no-extra-semi.md b/packages/eslint-plugin/docs/rules/no-extra-semi.md index 2d43f5223ee6..504ff588de45 100644 --- a/packages/eslint-plugin/docs/rules/no-extra-semi.md +++ b/packages/eslint-plugin/docs/rules/no-extra-semi.md @@ -5,7 +5,7 @@ This rule extends the base [`eslint/no-extra-semi`](https://eslint.org/docs/rules/no-extra-semi) rule. It adds support for class properties. -## How to use +## How to Use ```jsonc { @@ -19,7 +19,11 @@ It adds support for class properties. See [`eslint/no-extra-semi` options](https://eslint.org/docs/rules/no-extra-semi#options). -Taken with ❤️ [from ESLint core](https://github.com/eslint/eslint/blob/master/docs/rules/no-extra-semi.md) + + +Taken with ❤️ [from ESLint core](https://github.com/eslint/eslint/blob/main/docs/rules/no-extra-semi.md) + + ## Attributes diff --git a/packages/eslint-plugin/docs/rules/no-extraneous-class.md b/packages/eslint-plugin/docs/rules/no-extraneous-class.md index 5a57c71ec4f0..72303e3c25b1 100644 --- a/packages/eslint-plugin/docs/rules/no-extraneous-class.md +++ b/packages/eslint-plugin/docs/rules/no-extraneous-class.md @@ -9,7 +9,11 @@ From TSLint’s docs: > Users who come from a Java-style OO language may wrap their utility functions in an extra class, > instead of putting them at the top level. -Examples of **incorrect** code for this rule: +Examples of code for this rule: + + + +### ❌ Incorrect ```ts class EmptyClass {} @@ -29,7 +33,7 @@ class StaticOnly { } ``` -Examples of **correct** code for this rule: +### ✅ Correct ```ts class EmptyClass extends SuperClass {} @@ -46,7 +50,7 @@ const StaticOnly = { }; ``` -### Options +## Options This rule accepts a single object option. @@ -75,7 +79,7 @@ const defaultOptions: Options = { You can disable this rule if you don’t have anyone who would make these kinds of mistakes on your team or if you use classes as namespaces. -## Compatibility +## Related To [`no-unnecessary-class`](https://palantir.github.io/tslint/rules/no-unnecessary-class/) from TSLint diff --git a/packages/eslint-plugin/docs/rules/no-floating-promises.md b/packages/eslint-plugin/docs/rules/no-floating-promises.md index 1e7d0bf3299a..1322f468d47f 100644 --- a/packages/eslint-plugin/docs/rules/no-floating-promises.md +++ b/packages/eslint-plugin/docs/rules/no-floating-promises.md @@ -8,7 +8,11 @@ either calling `.then()` with two arguments or `.catch()` with one argument. ## Rule Details -Examples of **incorrect** code for this rule: +Examples of code for this rule: + + + +### ❌ Incorrect ```ts const promise = new Promise((resolve, reject) => resolve('value')); @@ -24,7 +28,7 @@ Promise.reject('value').catch(); Promise.reject('value').finally(); ``` -Examples of **correct** code for this rule: +### ✅ Correct ```ts const promise = new Promise((resolve, reject) => resolve('value')); @@ -99,7 +103,7 @@ await(async function () { If you do not use Promise-like values in your codebase, or want to allow them to remain unhandled. -## Related to +## Related To - TSLint: ['no-floating-promises'](https://palantir.github.io/tslint/rules/no-floating-promises/) diff --git a/packages/eslint-plugin/docs/rules/no-for-in-array.md b/packages/eslint-plugin/docs/rules/no-for-in-array.md index 61ab8e0c1186..9fbed4ca02fb 100644 --- a/packages/eslint-plugin/docs/rules/no-for-in-array.md +++ b/packages/eslint-plugin/docs/rules/no-for-in-array.md @@ -19,7 +19,11 @@ for (const [index, value] of array.entries()) { ... } for (let i = 0; i < array.length; i++) { ... } ``` -Examples of **incorrect** code for this rule: +Examples of code for this rule: + + + +### ❌ Incorrect ```js for (const x in [3, 4, 5]) { @@ -27,7 +31,7 @@ for (const x in [3, 4, 5]) { } ``` -Examples of **correct** code for this rule: +### ✅ Correct ```js for (const x in { a: 3, b: 4, c: 5 }) { @@ -39,7 +43,7 @@ for (const x in { a: 3, b: 4, c: 5 }) { If you want to iterate through a loop using the indices in an array as strings, you can turn off this rule. -## Related to +## Related To - TSLint: ['no-for-in-array'](https://palantir.github.io/tslint/rules/no-for-in-array/) diff --git a/packages/eslint-plugin/docs/rules/no-implicit-any-catch.md b/packages/eslint-plugin/docs/rules/no-implicit-any-catch.md index 81ac098690e2..f9dd35695c17 100644 --- a/packages/eslint-plugin/docs/rules/no-implicit-any-catch.md +++ b/packages/eslint-plugin/docs/rules/no-implicit-any-catch.md @@ -10,7 +10,11 @@ The `noImplicitAny` flag in TypeScript does not cover this for backwards compati This rule requires an explicit type to be declared on a catch clause variable. -The following pattern is considered a warning: +Examples of code for this rule: + + + +### ❌ Incorrect ```ts try { @@ -20,7 +24,7 @@ try { } ``` -The following pattern is **_not_** considered a warning: +### ✅ Correct @@ -73,7 +77,7 @@ If you are not using TypeScript 4.0 (or greater), then you will not be able to u ## Further Reading -- [TypeScript 4.0 Beta Release Notes](https://devblogs.microsoft.com/typescript/announcing-typescript-4-0-beta/#unknown-on-catch) +- [TypeScript 4.0 Release Notes](https://devblogs.microsoft.com/typescript/announcing-typescript-4-0/#unknown-on-catch) ## Attributes diff --git a/packages/eslint-plugin/docs/rules/no-implied-eval.md b/packages/eslint-plugin/docs/rules/no-implied-eval.md index 2a89bed488fe..1a1fe3d87a75 100644 --- a/packages/eslint-plugin/docs/rules/no-implied-eval.md +++ b/packages/eslint-plugin/docs/rules/no-implied-eval.md @@ -23,7 +23,11 @@ The best practice is to avoid using `new Function()` or `execScript()` and alway This rule aims to eliminate implied `eval()` through the use of `new Function()`, `setTimeout()`, `setInterval()`, `setImmediate()` or `execScript()`. -Examples of **incorrect** code for this rule: +Examples of code for this rule: + + + +### ❌ Incorrect ```ts /* eslint @typescript-eslint/no-implied-eval: "error" */ @@ -51,7 +55,7 @@ setTimeout(fn(), 100); const fn = new Function('a', 'b', 'return a + b'); ``` -Examples of **correct** code for this rule: +### ✅ Correct ```ts /* eslint @typescript-eslint/no-implied-eval: "error" */ @@ -88,7 +92,7 @@ class Foo { setTimeout(Foo.fn, 100); ``` -## How to use +## How to Use ```jsonc { @@ -102,7 +106,11 @@ setTimeout(Foo.fn, 100); If you want to allow `new Function()` or `setTimeout()`, `setInterval()`, `setImmediate()` and `execScript()` with string arguments, then you can safely disable this rule. -Taken with ❤️ [from ESLint core](https://github.com/eslint/eslint/blob/master/docs/rules/no-implied-eval.md) + + +Taken with ❤️ [from ESLint core](https://github.com/eslint/eslint/blob/main/docs/rules/no-implied-eval.md) + + ## Attributes diff --git a/packages/eslint-plugin/docs/rules/no-inferrable-types.md b/packages/eslint-plugin/docs/rules/no-inferrable-types.md index f94c58671f86..de82c8719f7c 100644 --- a/packages/eslint-plugin/docs/rules/no-inferrable-types.md +++ b/packages/eslint-plugin/docs/rules/no-inferrable-types.md @@ -29,7 +29,52 @@ The default options are: } ``` -With these options, the following patterns are valid: +With these options, the following patterns are: + + + +#### ❌ Incorrect + +```ts +const a: bigint = 10n; +const a: bigint = -10n; +const a: bigint = BigInt(10); +const a: bigint = -BigInt(10); +const a: boolean = false; +const a: boolean = true; +const a: boolean = Boolean(null); +const a: boolean = !0; +const a: number = 10; +const a: number = +10; +const a: number = -10; +const a: number = Number('1'); +const a: number = +Number('1'); +const a: number = -Number('1'); +const a: number = Infinity; +const a: number = +Infinity; +const a: number = -Infinity; +const a: number = NaN; +const a: number = +NaN; +const a: number = -NaN; +const a: null = null; +const a: RegExp = /a/; +const a: RegExp = RegExp('a'); +const a: RegExp = new RegExp('a'); +const a: string = 'str'; +const a: string = `str`; +const a: string = String(1); +const a: symbol = Symbol('a'); +const a: undefined = undefined; +const a: undefined = void someValue; + +class Foo { + prop: number = 5; +} + +function fn(a: number = 5, b: boolean = true) {} +``` + +#### ✅ Correct ```ts const a = 10n; @@ -72,46 +117,7 @@ function fn(a = 5, b = true) {} function fn(a: number, b: boolean, c: string) {} ``` -The following are invalid: - -```ts -const a: bigint = 10n; -const a: bigint = -10n; -const a: bigint = BigInt(10); -const a: bigint = -BigInt(10); -const a: boolean = false; -const a: boolean = true; -const a: boolean = Boolean(null); -const a: boolean = !0; -const a: number = 10; -const a: number = +10; -const a: number = -10; -const a: number = Number('1'); -const a: number = +Number('1'); -const a: number = -Number('1'); -const a: number = Infinity; -const a: number = +Infinity; -const a: number = -Infinity; -const a: number = NaN; -const a: number = +NaN; -const a: number = -NaN; -const a: null = null; -const a: RegExp = /a/; -const a: RegExp = RegExp('a'); -const a: RegExp = new RegExp('a'); -const a: string = 'str'; -const a: string = `str`; -const a: string = String(1); -const a: symbol = Symbol('a'); -const a: undefined = undefined; -const a: undefined = void someValue; - -class Foo { - prop: number = 5; -} - -function fn(a: number = 5, b: boolean = true) {} -``` + ### `ignoreParameters` @@ -141,7 +147,7 @@ If you do not want to enforce inferred types. TypeScript [Inference](https://www.typescriptlang.org/docs/handbook/type-inference.html) -## Compatibility +## Related To TSLint: [no-inferrable-types](https://palantir.github.io/tslint/rules/no-inferrable-types/) diff --git a/packages/eslint-plugin/docs/rules/no-invalid-this.md b/packages/eslint-plugin/docs/rules/no-invalid-this.md index 86d7a03eba5c..becbc9d6186a 100644 --- a/packages/eslint-plugin/docs/rules/no-invalid-this.md +++ b/packages/eslint-plugin/docs/rules/no-invalid-this.md @@ -5,7 +5,7 @@ This rule extends the base [`eslint/no-invalid-this`](https://eslint.org/docs/rules/no-invalid-this) rule. It adds support for TypeScript's `this` parameters. -## How to use +## How to Use ```jsonc { @@ -23,7 +23,11 @@ See [`eslint/no-invalid-this` options](https://eslint.org/docs/rules/no-invalid- When you are indifferent as to how your variables are initialized. -Taken with ❤️ [from ESLint core](https://github.com/eslint/eslint/blob/master/docs/rules/no-invalid-this.md) + + +Taken with ❤️ [from ESLint core](https://github.com/eslint/eslint/blob/main/docs/rules/no-invalid-this.md) + + ## Attributes diff --git a/packages/eslint-plugin/docs/rules/no-invalid-void-type.md b/packages/eslint-plugin/docs/rules/no-invalid-void-type.md index 7ba223348938..e36fe8f97e1f 100644 --- a/packages/eslint-plugin/docs/rules/no-invalid-void-type.md +++ b/packages/eslint-plugin/docs/rules/no-invalid-void-type.md @@ -14,7 +14,11 @@ If you need this - use the `undefined` type instead. This rule aims to ensure that the `void` type is only used in valid places. -The following patterns are considered warnings: +Examples of code for this rule: + + + +### ❌ Incorrect ```ts type PossibleValues = string | number | void; @@ -35,7 +39,7 @@ class MyClass { } ``` -The following patterns are not considered warnings: +### ✅ Correct ```ts type NoOp = () => void; @@ -49,7 +53,7 @@ async function promiseMeSomething(): Promise {} type stillVoid = void | never; ``` -### Options +## Options ```ts interface Options { @@ -63,7 +67,7 @@ const defaultOptions: Options = { }; ``` -#### `allowInGenericTypeArguments` +### `allowInGenericTypeArguments` This option lets you control if `void` can be used as a valid value for generic type parameters. @@ -99,7 +103,7 @@ type AllowedVoid = Ex.Mx.Tx; type AllowedVoidUnion = void | Ex.Mx.Tx; ``` -#### `allowAsThisParameter` +### `allowAsThisParameter` This option allows specifying a `this` parameter of a function to be `void` when set to `true`. This pattern can be useful to explicitly label function types that do not use a `this` argument. [See the TypeScript docs for more information](https://www.typescriptlang.org/docs/handbook/functions.html#this-parameters-in-callbacks). @@ -121,7 +125,7 @@ class Example { If you don't care about if `void` is used with other types, or in invalid places, then you don't need this rule. -## Compatibility +## Related To - TSLint: [invalid-void](https://palantir.github.io/tslint/rules/invalid-void/) diff --git a/packages/eslint-plugin/docs/rules/no-loop-func.md b/packages/eslint-plugin/docs/rules/no-loop-func.md index 0ca7ea515166..a0cde8aee765 100644 --- a/packages/eslint-plugin/docs/rules/no-loop-func.md +++ b/packages/eslint-plugin/docs/rules/no-loop-func.md @@ -5,7 +5,7 @@ This rule extends the base [`eslint/no-loop-func`](https://eslint.org/docs/rules/no-loop-func) rule. It adds support for TypeScript types. -## How to use +## How to Use ```jsonc { @@ -19,7 +19,11 @@ It adds support for TypeScript types. See [`eslint/no-loop-func` options](https://eslint.org/docs/rules/no-loop-func#options). -Taken with ❤️ [from ESLint core](https://github.com/eslint/eslint/blob/master/docs/rules/no-loop-func.md) + + +Taken with ❤️ [from ESLint core](https://github.com/eslint/eslint/blob/main/docs/rules/no-loop-func.md) + + ## Attributes diff --git a/packages/eslint-plugin/docs/rules/no-loss-of-precision.md b/packages/eslint-plugin/docs/rules/no-loss-of-precision.md index d2e9fb23f6cd..24b3753f8b32 100644 --- a/packages/eslint-plugin/docs/rules/no-loss-of-precision.md +++ b/packages/eslint-plugin/docs/rules/no-loss-of-precision.md @@ -6,7 +6,7 @@ This rule extends the base [`eslint/no-loss-of-precision`](https://eslint.org/do It adds support for [numeric separators](https://github.com/tc39/proposal-numeric-separator). Note that this rule requires ESLint v7. -## How to use +## How to Use ```jsonc { @@ -16,7 +16,11 @@ Note that this rule requires ESLint v7. } ``` -Taken with ❤️ [from ESLint core](https://github.com/eslint/eslint/blob/master/docs/rules/no-loss-of-precision.md) + + +Taken with ❤️ [from ESLint core](https://github.com/eslint/eslint/blob/main/docs/rules/no-loss-of-precision.md) + + ## Attributes diff --git a/packages/eslint-plugin/docs/rules/no-magic-numbers.md b/packages/eslint-plugin/docs/rules/no-magic-numbers.md index 26739600713d..61ea6627f18b 100644 --- a/packages/eslint-plugin/docs/rules/no-magic-numbers.md +++ b/packages/eslint-plugin/docs/rules/no-magic-numbers.md @@ -9,7 +9,7 @@ It adds support for: - `enum` members (`enum Foo { bar = 1 }`), - `readonly` class properties (`class Foo { readonly bar = 1 }`). -## How to use +## How to Use ```jsonc { @@ -116,7 +116,11 @@ class Foo { } ``` -Taken with ❤️ [from ESLint core](https://github.com/eslint/eslint/blob/master/docs/rules/no-magic-numbers.md) + + +Taken with ❤️ [from ESLint core](https://github.com/eslint/eslint/blob/main/docs/rules/no-magic-numbers.md) + + ## Attributes diff --git a/packages/eslint-plugin/docs/rules/no-meaningless-void-operator.md b/packages/eslint-plugin/docs/rules/no-meaningless-void-operator.md index c9479679f414..f1ddff9e219d 100644 --- a/packages/eslint-plugin/docs/rules/no-meaningless-void-operator.md +++ b/packages/eslint-plugin/docs/rules/no-meaningless-void-operator.md @@ -8,7 +8,11 @@ The `void` operator is a useful tool to convey the programmer's intent to discar This rule helps an author catch API changes where previously a value was being discarded at a call site, but the callee changed so it no longer returns a value. When combined with [no-unused-expressions](https://eslint.org/docs/rules/no-unused-expressions), it also helps _readers_ of the code by ensuring consistency: a statement that looks like `void foo();` is **always** discarding a return value, and a statement that looks like `foo();` is **never** discarding a return value. -Examples of **incorrect** code for this rule: +Examples of code for this rule: + + + +### ❌ Incorrect ```ts void (() => {})(); @@ -17,7 +21,7 @@ function foo() {} void foo(); ``` -Examples of **correct** code for this rule: +### ✅ Correct ```ts (() => {})(); @@ -32,7 +36,7 @@ function bar(x: number) { void bar(); // discarding a number ``` -### Options +## Options This rule accepts a single object option with the following default configuration: diff --git a/packages/eslint-plugin/docs/rules/no-misused-new.md b/packages/eslint-plugin/docs/rules/no-misused-new.md index a920ba03cbf0..72cc5eabf711 100644 --- a/packages/eslint-plugin/docs/rules/no-misused-new.md +++ b/packages/eslint-plugin/docs/rules/no-misused-new.md @@ -4,7 +4,11 @@ Warns on apparent attempts to define constructors for interfaces or `new` for cl ## Rule Details -Examples of **incorrect** code for this rule. +Examples of code for this rule: + + + +### ❌ Incorrect ```ts class C { @@ -17,7 +21,7 @@ interface I { } ``` -Examples of **correct** code for this rule. +### ✅ Correct ```ts class C { @@ -36,7 +40,7 @@ interface I { } ``` -## Compatibility +## Related To - TSLint: [no-misused-new](https://palantir.github.io/tslint/rules/no-misused-new/) diff --git a/packages/eslint-plugin/docs/rules/no-misused-promises.md b/packages/eslint-plugin/docs/rules/no-misused-promises.md index 7e4f28170a6a..7be1938e7435 100644 --- a/packages/eslint-plugin/docs/rules/no-misused-promises.md +++ b/packages/eslint-plugin/docs/rules/no-misused-promises.md @@ -7,7 +7,63 @@ functions are handled/awaited. ## Rule Details -Examples of **incorrect** code for this rule with `checksConditionals: true`: +This rule accepts a single option which is an object with `checksConditionals` +and `checksVoidReturn` properties indicating which types of misuse to flag. +Both are enabled by default + +## Options + +```ts +type Options = [ + { + checksConditionals?: boolean; + checksVoidReturn?: boolean; + }, +]; + +const defaultOptions: Options = [ + { + checksConditionals: true, + checksVoidReturn: true, + }, +]; +``` + +If you don't want functions that return promises where a void return is +expected to be checked, your configuration will look like this: + +```json +{ + "@typescript-eslint/no-misused-promises": [ + "error", + { + "checksVoidReturn": false + } + ] +} +``` + +Likewise, if you don't want to check conditionals, you can configure the rule +like this: + +```json +{ + "@typescript-eslint/no-misused-promises": [ + "error", + { + "checksConditionals": false + } + ] +} +``` + +### `checksConditionals: true` + +Examples of code for this rule with `checksConditionals: true`: + + + +#### ❌ Incorrect ```ts const promise = Promise.resolve('value'); @@ -23,7 +79,7 @@ while (promise) { } ``` -Examples of **correct** code with `checksConditionals: true`: +#### ✅ Correct ```ts const promise = Promise.resolve('value'); @@ -40,9 +96,15 @@ while (await promise) { } ``` ---- + + +### `checksVoidReturn: true` -Examples of **incorrect** code for this rule with `checksVoidReturn: true`: +Examples of code for this rule with `checksVoidReturn: true`: + + + +#### ❌ Incorrect ```ts [1, 2, 3].forEach(async value => { @@ -62,7 +124,7 @@ eventEmitter.on('some-event', async () => { }); ``` -Examples of **correct** code with `checksVoidReturn: true`: +#### ✅ Correct ```ts // for-of puts `await` in outer context @@ -104,53 +166,21 @@ eventEmitter.on('some-event', () => { }); ``` -## Options - -This rule accepts a single option which is an object with `checksConditionals` -and `checksVoidReturn` properties indicating which types of misuse to flag. -Both are enabled by default - -If you don't want functions that return promises where a void return is -expected to be checked, your configuration will look like this: - -```json -{ - "@typescript-eslint/no-misused-promises": [ - "error", - { - "checksVoidReturn": false - } - ] -} -``` - -Likewise, if you don't want to check conditionals, you can configure the rule -like this: - -```json -{ - "@typescript-eslint/no-misused-promises": [ - "error", - { - "checksConditionals": false - } - ] -} -``` + ## When Not To Use It If you do not use Promises in your codebase or are not concerned with possible misuses of them outside of what the TypeScript compiler will check. -## Related to - -- [`no-floating-promises`](./no-floating-promises.md) - ## Further Reading - [TypeScript void function assignability](https://github.com/Microsoft/TypeScript/wiki/FAQ#why-are-functions-returning-non-void-assignable-to-function-returning-void) +## Related To + +- [`no-floating-promises`](./no-floating-promises.md) + ## Attributes - [x] ✅ Recommended diff --git a/packages/eslint-plugin/docs/rules/no-namespace.md b/packages/eslint-plugin/docs/rules/no-namespace.md index 465dd58b3e00..88328741bb5f 100644 --- a/packages/eslint-plugin/docs/rules/no-namespace.md +++ b/packages/eslint-plugin/docs/rules/no-namespace.md @@ -18,7 +18,11 @@ or more of the following you may pass an object with the options set as follows: - `allowDefinitionFiles` set to `true` will allow you to `declare` and use custom TypeScript modules and namespaces inside definition files (Default: `true`). -Examples of **incorrect** code for the default `{ "allowDeclarations": false, "allowDefinitionFiles": true }` options: +Examples of code for the default `{ "allowDeclarations": false, "allowDefinitionFiles": true }` options: + + + +### ❌ Incorrect ```ts module foo {} @@ -28,7 +32,7 @@ declare module foo {} declare namespace foo {} ``` -Examples of **correct** code for the default `{ "allowDeclarations": false, "allowDefinitionFiles": true }` options: +### ✅ Correct ```ts declare module 'foo' {} @@ -36,16 +40,22 @@ declare module 'foo' {} // anything inside a d.ts file ``` + + ### `allowDeclarations` -Examples of **incorrect** code for the `{ "allowDeclarations": true }` option: +Examples of code for the `{ "allowDeclarations": true }` option: + + + +#### ❌ Incorrect ```ts module foo {} namespace foo {} ``` -Examples of **correct** code for the `{ "allowDeclarations": true }` option: +#### ✅ Correct ```ts declare module 'foo' {} @@ -61,7 +71,13 @@ declare module foo { } ``` -Examples of **incorrect** code for the `{ "allowDeclarations": false }` option: + + +Examples of code for the `{ "allowDeclarations": false }` option: + + + +#### ❌ Incorrect ```ts module foo {} @@ -70,7 +86,7 @@ declare module foo {} declare namespace foo {} ``` -Examples of **correct** code for the `{ "allowDeclarations": false }` option: +#### ✅ Correct ```ts declare module 'foo' {} @@ -78,7 +94,11 @@ declare module 'foo' {} ### `allowDefinitionFiles` -Examples of **incorrect** code for the `{ "allowDefinitionFiles": true }` option: +Examples of code for the `{ "allowDefinitionFiles": true }` option: + + + +#### ❌ Incorrect ```ts // if outside a d.ts file @@ -92,7 +112,7 @@ declare module foo {} declare namespace foo {} ``` -Examples of **correct** code for the `{ "allowDefinitionFiles": true }` option: +#### ✅ Correct ```ts declare module 'foo' {} @@ -110,7 +130,7 @@ If you are using the ES2015 module syntax, then you will not need this rule. - [Namespaces](https://www.typescriptlang.org/docs/handbook/namespaces.html) - [Namespaces and Modules](https://www.typescriptlang.org/docs/handbook/namespaces-and-modules.html) -## Compatibility +## Related To - TSLint: [no-namespace](https://palantir.github.io/tslint/rules/no-namespace/) diff --git a/packages/eslint-plugin/docs/rules/no-non-null-asserted-nullish-coalescing.md b/packages/eslint-plugin/docs/rules/no-non-null-asserted-nullish-coalescing.md index 2eb8ff2585d6..498ac053d6f9 100644 --- a/packages/eslint-plugin/docs/rules/no-non-null-asserted-nullish-coalescing.md +++ b/packages/eslint-plugin/docs/rules/no-non-null-asserted-nullish-coalescing.md @@ -5,7 +5,11 @@ The nullish coalescing operator is designed to provide a default value when dealing with `null` or `undefined`. Using non-null assertions in the left operand of the nullish coalescing operator is redundant. -Examples of **incorrect** code for this rule: +Examples of code for this rule: + + + +### ❌ Incorrect ```ts /* eslint @typescript-eslint/no-non-null-asserted-nullish-coalescing: "error" */ @@ -23,7 +27,7 @@ x = foo(); x! ?? ''; ``` -Examples of **correct** code for this rule: +### ✅ Correct ```ts /* eslint @typescript-eslint/no-non-null-asserted-nullish-coalescing: "error" */ diff --git a/packages/eslint-plugin/docs/rules/no-non-null-asserted-optional-chain.md b/packages/eslint-plugin/docs/rules/no-non-null-asserted-optional-chain.md index 16a22b480358..0a59902883d3 100644 --- a/packages/eslint-plugin/docs/rules/no-non-null-asserted-optional-chain.md +++ b/packages/eslint-plugin/docs/rules/no-non-null-asserted-optional-chain.md @@ -5,7 +5,11 @@ Optional chain expressions are designed to return `undefined` if the optional property is nullish. Using non-null assertions after an optional chain expression is wrong, and introduces a serious type safety hole into your code. -Examples of **incorrect** code for this rule: +Examples of code for this rule: + + + +### ❌ Incorrect ```ts /* eslint @typescript-eslint/no-non-null-asserted-optional-chain: "error" */ @@ -21,7 +25,7 @@ foo?.bar!(); foo?.bar!().baz; ``` -Examples of **correct** code for this rule: +### ✅ Correct ```ts /* eslint @typescript-eslint/no-non-null-asserted-optional-chain: "error" */ diff --git a/packages/eslint-plugin/docs/rules/no-non-null-assertion.md b/packages/eslint-plugin/docs/rules/no-non-null-assertion.md index 1eaebff625cc..cbd76d1bb1f4 100644 --- a/packages/eslint-plugin/docs/rules/no-non-null-assertion.md +++ b/packages/eslint-plugin/docs/rules/no-non-null-assertion.md @@ -4,7 +4,11 @@ Using non-null assertions cancels the benefits of the strict null-checking mode. -Examples of **incorrect** code for this rule: +Examples of code for this rule: + + + +### ❌ Incorrect ```ts interface Foo { @@ -15,7 +19,7 @@ const foo: Foo = getFoo(); const includesBaz: boolean = foo.bar!.includes('baz'); ``` -Examples of **correct** code for this rule: +### ✅ Correct ```ts interface Foo { diff --git a/packages/eslint-plugin/docs/rules/no-parameter-properties.md b/packages/eslint-plugin/docs/rules/no-parameter-properties.md index 6bde9296b5be..eb4944364b66 100644 --- a/packages/eslint-plugin/docs/rules/no-parameter-properties.md +++ b/packages/eslint-plugin/docs/rules/no-parameter-properties.md @@ -24,7 +24,11 @@ If you would like to allow certain types of parameter properties then you may pa ### default -Examples of **incorrect** code for this rule with no options at all: +Examples of code for this rule with no options at all: + + + +#### ❌ Incorrect ```ts class Foo { @@ -56,7 +60,7 @@ class Foo { } ``` -Examples of **correct** code for this rule with no options at all: +#### ✅ Correct ```ts class Foo { @@ -66,7 +70,11 @@ class Foo { ### readonly -Examples of **incorrect** code for the `{ "allows": ["readonly"] }` options: +Examples of code for the `{ "allows": ["readonly"] }` options: + + + +#### ❌ Incorrect ```ts class Foo { @@ -94,7 +102,7 @@ class Foo { } ``` -Examples of **correct** code for the `{ "allows": ["readonly"] }` options: +#### ✅ Correct ```ts class Foo { @@ -108,7 +116,11 @@ class Foo { ### private -Examples of **incorrect** code for the `{ "allows": ["private"] }` options: +Examples of code for the `{ "allows": ["private"] }` options: + + + +#### ❌ Incorrect ```ts class Foo { @@ -136,7 +148,7 @@ class Foo { } ``` -Examples of **correct** code for the `{ "allows": ["private"] }` options: +#### ✅ Correct ```ts class Foo { @@ -150,7 +162,11 @@ class Foo { ### protected -Examples of **incorrect** code for the `{ "allows": ["protected"] }` options: +Examples of code for the `{ "allows": ["protected"] }` options: + + + +#### ❌ Incorrect ```ts class Foo { @@ -178,7 +194,7 @@ class Foo { } ``` -Examples of **correct** code for the `{ "allows": ["protected"] }` options: +#### ✅ Correct ```ts class Foo { @@ -192,7 +208,11 @@ class Foo { ### public -Examples of **incorrect** code for the `{ "allows": ["public"] }` options: +Examples of code for the `{ "allows": ["public"] }` options: + + + +#### ❌ Incorrect ```ts class Foo { @@ -220,7 +240,7 @@ class Foo { } ``` -Examples of **correct** code for the `{ "allows": ["public"] }` options: +#### ✅ Correct ```ts class Foo { @@ -234,7 +254,11 @@ class Foo { ### private readonly -Examples of **incorrect** code for the `{ "allows": ["private readonly"] }` options: +Examples of code for the `{ "allows": ["private readonly"] }` options: + + + +#### ❌ Incorrect ```ts class Foo { @@ -262,7 +286,7 @@ class Foo { } ``` -Examples of **correct** code for the `{ "allows": ["private readonly"] }` options: +#### ✅ Correct ```ts class Foo { @@ -276,7 +300,11 @@ class Foo { ### protected readonly -Examples of **incorrect** code for the `{ "allows": ["protected readonly"] }` options: +Examples of code for the `{ "allows": ["protected readonly"] }` options: + + + +#### ❌ Incorrect ```ts class Foo { @@ -304,7 +332,7 @@ class Foo { } ``` -Examples of **correct** code for the `{ "allows": ["protected readonly"] }` options: +#### ✅ Correct ```ts class Foo { @@ -318,7 +346,11 @@ class Foo { ### public readonly -Examples of **incorrect** code for the `{ "allows": ["public readonly"] }` options: +Examples of code for the `{ "allows": ["public readonly"] }` options: + + + +#### ❌ Incorrect ```ts class Foo { @@ -346,7 +378,7 @@ class Foo { } ``` -Examples of **correct** code for the `{ "allows": ["public readonly"] }` options: +#### ✅ Correct ```ts class Foo { @@ -362,7 +394,7 @@ class Foo { If you don't care about the using parameter properties in constructors, then you will not need this rule. -## Compatibility +## Related To - TSLint: [no-parameter-properties](https://palantir.github.io/tslint/rules/no-parameter-properties/) diff --git a/packages/eslint-plugin/docs/rules/no-redeclare.md b/packages/eslint-plugin/docs/rules/no-redeclare.md index 98a4cf78ba3a..2e6efa958236 100644 --- a/packages/eslint-plugin/docs/rules/no-redeclare.md +++ b/packages/eslint-plugin/docs/rules/no-redeclare.md @@ -5,7 +5,7 @@ This rule extends the base [`eslint/no-redeclare`](https://eslint.org/docs/rules/no-redeclare) rule. It adds support for TypeScript function overloads, and declaration merging. -## How to use +## How to Use ```jsonc { @@ -77,7 +77,11 @@ type something = string; const something = 2; ``` -Taken with ❤️ [from ESLint core](https://github.com/eslint/eslint/blob/master/docs/rules/no-redeclare.md) + + +Taken with ❤️ [from ESLint core](https://github.com/eslint/eslint/blob/main/docs/rules/no-redeclare.md) + + ## Attributes diff --git a/packages/eslint-plugin/docs/rules/no-require-imports.md b/packages/eslint-plugin/docs/rules/no-require-imports.md index 2bc3eca93f97..9a4680796d02 100644 --- a/packages/eslint-plugin/docs/rules/no-require-imports.md +++ b/packages/eslint-plugin/docs/rules/no-require-imports.md @@ -4,7 +4,11 @@ Prefer the newer ES6-style imports over `require()`. ## Rule Details -Examples of **incorrect** code for this rule: +Examples of code for this rule: + + + +### ❌ Incorrect ```ts var lib = require('lib'); @@ -14,7 +18,7 @@ var lib5 = require('lib5'), import lib8 = require('lib8'); ``` -Examples of **correct** code for this rule: +### ✅ Correct ```ts import { l } from 'lib'; @@ -29,7 +33,7 @@ import lib10 from 'lib10'; If you don't care about TypeScript module syntax, then you will not need this rule. -## Compatibility +## Related To - TSLint: [no-require-imports](https://palantir.github.io/tslint/rules/no-require-imports/) diff --git a/packages/eslint-plugin/docs/rules/no-restricted-imports.md b/packages/eslint-plugin/docs/rules/no-restricted-imports.md index 359cfd4f084f..52a8daa2fd6e 100644 --- a/packages/eslint-plugin/docs/rules/no-restricted-imports.md +++ b/packages/eslint-plugin/docs/rules/no-restricted-imports.md @@ -4,7 +4,7 @@ This rule extends the base [`eslint/no-restricted-imports`](https://eslint.org/docs/rules/no-restricted-imports) rule. -## How to use +## How to Use ```jsonc { @@ -41,19 +41,11 @@ You can specify this option for a specific path or pattern as follows: When set to `true`, the rule will allow [Type-Only Imports](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-8.html#type-only-imports-and-export). -Examples of **correct** code with the above config: +Examples of code with the above config: -```ts -import { foo } from 'other-module'; - -import type foo from 'import-foo'; -export type { Foo } from 'import-foo'; + -import type baz from 'import-baz'; -export type { Baz } from 'import-baz'; -``` - -Example of **incorrect** code with the above config: +#### ❌ Incorrect ```ts import foo from 'import-foo'; @@ -63,6 +55,18 @@ import baz from 'import-baz'; export { Baz } from 'import-baz'; ``` +#### ✅ Correct + +```ts +import { foo } from 'other-module'; + +import type foo from 'import-foo'; +export type { Foo } from 'import-foo'; + +import type baz from 'import-baz'; +export type { Baz } from 'import-baz'; +``` + ## Attributes - [ ] ✅ Recommended diff --git a/packages/eslint-plugin/docs/rules/no-shadow.md b/packages/eslint-plugin/docs/rules/no-shadow.md index 681ac7b25616..e3da57987b44 100644 --- a/packages/eslint-plugin/docs/rules/no-shadow.md +++ b/packages/eslint-plugin/docs/rules/no-shadow.md @@ -5,7 +5,7 @@ This rule extends the base [`eslint/no-shadow`](https://eslint.org/docs/rules/no-shadow) rule. It adds support for TypeScript's `this` parameters and global augmentation, and adds options for TypeScript features. -## How to use +## How to Use ```jsonc { @@ -84,7 +84,11 @@ const test = 1; type Func = (test: string) => typeof test; ``` -Taken with ❤️ [from ESLint core](https://github.com/eslint/eslint/blob/master/docs/rules/no-shadow.md) + + +Taken with ❤️ [from ESLint core](https://github.com/eslint/eslint/blob/main/docs/rules/no-shadow.md) + + ## Attributes diff --git a/packages/eslint-plugin/docs/rules/no-this-alias.md b/packages/eslint-plugin/docs/rules/no-this-alias.md index 67cb36df2e4a..8265fe7efdd3 100644 --- a/packages/eslint-plugin/docs/rules/no-this-alias.md +++ b/packages/eslint-plugin/docs/rules/no-this-alias.md @@ -35,7 +35,7 @@ Examples of **correct** code for this rule: (see the rationale above) -### Options +## Options You can pass an object option: @@ -55,7 +55,7 @@ You can pass an object option: If you need to assign `this` to variables, you shouldn’t use this rule. -## Related to +## Related To - TSLint: [`no-this-assignment`](https://palantir.github.io/tslint/rules/no-this-assignment/) diff --git a/packages/eslint-plugin/docs/rules/no-throw-literal.md b/packages/eslint-plugin/docs/rules/no-throw-literal.md index a67d422cdc43..bffbed755f3f 100644 --- a/packages/eslint-plugin/docs/rules/no-throw-literal.md +++ b/packages/eslint-plugin/docs/rules/no-throw-literal.md @@ -9,7 +9,11 @@ This rule restricts what can be thrown as an exception. When it was first create This rule is aimed at maintaining consistency when throwing exception by disallowing to throw literals and other expressions which cannot possibly be an `Error` object. -Examples of **incorrect** code for this rule: +Examples of code for this rule: + + + +### ❌ Incorrect ```ts /*eslint @typescript-eslint/no-throw-literal: "error"*/ @@ -42,7 +46,7 @@ const foo = { throw foo.bar; ``` -Examples of **correct** code for this rule: +### ✅ Correct ```ts /*eslint @typescript-eslint/no-throw-literal: "error"*/ @@ -79,7 +83,7 @@ class CustomError extends Error { throw new CustomError(); ``` -## How to use +## How to Use ```jsonc { @@ -91,7 +95,11 @@ throw new CustomError(); --- -Taken with ❤️ [from ESLint core](https://github.com/eslint/eslint/blob/master/docs/rules/no-throw-literal.md) + + +Taken with ❤️ [from ESLint core](https://github.com/eslint/eslint/blob/main/docs/rules/no-throw-literal.md) + + ## Attributes diff --git a/packages/eslint-plugin/docs/rules/no-type-alias.md b/packages/eslint-plugin/docs/rules/no-type-alias.md index a915516d065b..e2cca34b0a78 100644 --- a/packages/eslint-plugin/docs/rules/no-type-alias.md +++ b/packages/eslint-plugin/docs/rules/no-type-alias.md @@ -591,7 +591,7 @@ callback, etc. that would cause the code to be unreadable or impractical. - [Advance Types](https://www.typescriptlang.org/docs/handbook/advanced-types.html) -## Related to +## Related To - TSLint: [interface-over-type-literal](https://palantir.github.io/tslint/rules/interface-over-type-literal/) diff --git a/packages/eslint-plugin/docs/rules/no-unnecessary-boolean-literal-compare.md b/packages/eslint-plugin/docs/rules/no-unnecessary-boolean-literal-compare.md index f39f663ddfb4..4f81aec9a846 100644 --- a/packages/eslint-plugin/docs/rules/no-unnecessary-boolean-literal-compare.md +++ b/packages/eslint-plugin/docs/rules/no-unnecessary-boolean-literal-compare.md @@ -18,7 +18,11 @@ distinguish between strict and loose equality. Any example below that uses `===` would be treated the same way if `==` was used, and any example below that uses `!==` would be treated the same way if `!=` was used. -Examples of **incorrect** code for this rule: +Examples of code for this rule: + + + +### ❌ Incorrect ```ts declare const someCondition: boolean; @@ -26,7 +30,7 @@ if (someCondition === true) { } ``` -Examples of **correct** code for this rule +### ✅ Correct ```ts declare const someCondition: boolean; @@ -70,7 +74,11 @@ const defaults = { ### `allowComparingNullableBooleansToTrue` -Examples of **incorrect** code for this rule with `{ allowComparingNullableBooleansToTrue: false }`: +Examples of code for this rule with `{ allowComparingNullableBooleansToTrue: false }`: + + + +#### ❌ Incorrect ```ts declare const someUndefinedCondition: boolean | undefined; @@ -82,7 +90,7 @@ if (someNullCondition !== true) { } ``` -Examples of **correct** code for this rule with `{ allowComparingNullableBooleansToTrue: false }`: +#### ✅ Correct ```ts declare const someUndefinedCondition: boolean | undefined; @@ -96,7 +104,11 @@ if (!someNullCondition) { ### `allowComparingNullableBooleansToFalse` -Examples of **incorrect** code for this rule with `{ allowComparingNullableBooleansToFalse: false }`: +Examples of code for this rule with `{ allowComparingNullableBooleansToFalse: false }`: + + + +#### ❌ Incorrect ```ts declare const someUndefinedCondition: boolean | undefined; @@ -108,7 +120,7 @@ if (someNullCondition !== false) { } ``` -Examples of **correct** code for this rule with `{ allowComparingNullableBooleansToFalse: false }`: +#### ✅ Correct ```ts declare const someUndefinedCondition: boolean | undefined; @@ -133,7 +145,7 @@ if (!(someNullCondition ?? true)) { | `nullableBooleanVar === false` | `nullableBooleanVar ?? true` | Only checked/fixed if the `allowComparingNullableBooleansToFalse` option is `false` | | `nullableBooleanVar !== false` | `!(nullableBooleanVar ?? true)` | Only checked/fixed if the `allowComparingNullableBooleansToFalse` option is `false` | -## Related to +## Related To - TSLint: [no-boolean-literal-compare](https://palantir.github.io/tslint/rules/no-boolean-literal-compare) diff --git a/packages/eslint-plugin/docs/rules/no-unnecessary-condition.md b/packages/eslint-plugin/docs/rules/no-unnecessary-condition.md index 0dda0b0d8b2b..a6d029ab09fe 100644 --- a/packages/eslint-plugin/docs/rules/no-unnecessary-condition.md +++ b/packages/eslint-plugin/docs/rules/no-unnecessary-condition.md @@ -8,7 +8,13 @@ The following expressions are checked: - Conditions for `if`, `for`, `while`, and `do-while` statements - Base values of optional chain expressions -Examples of **incorrect** code for this rule: +## Rule Details + +Examples of code for this rule: + + + +### ❌ Incorrect ```ts function head(items: T[]) { @@ -36,7 +42,7 @@ function bar(arg: string) { ].filter(t => t); // number[] is always truthy ``` -Examples of **correct** code for this rule: +### ✅ Correct ```ts function head(items: T[]) { @@ -103,7 +109,6 @@ The main downside to using this rule is the need for type information. ## Related To - ESLint: [no-constant-condition](https://eslint.org/docs/rules/no-constant-condition) - `no-unnecessary-condition` is essentially a stronger version of `no-constant-condition`, but requires type information. - - [strict-boolean-expressions](./strict-boolean-expressions.md) - a more opinionated version of `no-unnecessary-condition`. `strict-boolean-expressions` enforces a specific code style, while `no-unnecessary-condition` is about correctness. ## Attributes diff --git a/packages/eslint-plugin/docs/rules/no-unnecessary-qualifier.md b/packages/eslint-plugin/docs/rules/no-unnecessary-qualifier.md index 2983307bbdb0..3df839c1756f 100644 --- a/packages/eslint-plugin/docs/rules/no-unnecessary-qualifier.md +++ b/packages/eslint-plugin/docs/rules/no-unnecessary-qualifier.md @@ -5,7 +5,11 @@ This rule aims to let users know when a namespace or enum qualifier is unnecessary, whether used for a type or for a value. -Examples of **incorrect** code for this rule: +Examples of code for this rule: + + + +### ❌ Incorrect ```ts namespace A { @@ -37,7 +41,7 @@ namespace A { } ``` -Examples of **correct** code for this rule: +### ✅ Correct ```ts namespace X { @@ -74,7 +78,7 @@ namespace X { If you don't care about having unneeded namespace or enum qualifiers, then you don't need to use this rule. -## Further Reading +## Related To - TSLint: [no-unnecessary-qualifier](https://palantir.github.io/tslint/rules/no-unnecessary-qualifier/) diff --git a/packages/eslint-plugin/docs/rules/no-unnecessary-type-arguments.md b/packages/eslint-plugin/docs/rules/no-unnecessary-type-arguments.md index 83d1041974d7..82668377296f 100644 --- a/packages/eslint-plugin/docs/rules/no-unnecessary-type-arguments.md +++ b/packages/eslint-plugin/docs/rules/no-unnecessary-type-arguments.md @@ -13,7 +13,11 @@ function f() {} It is redundant to provide an explicit type parameter equal to that default. -Examples of **incorrect** code for this rule: +Examples of code for this rule: + + + +### ❌ Incorrect ```ts function f() {} @@ -31,7 +35,7 @@ interface I {} class Impl implements I {} ``` -Examples of **correct** code for this rule: +### ✅ Correct ```ts function f() {} @@ -48,7 +52,7 @@ interface I {} class Impl implements I {} ``` -## Related to +## Related To - TSLint: [use-default-type-parameter](https://palantir.github.io/tslint/rules/use-default-type-parameter) diff --git a/packages/eslint-plugin/docs/rules/no-unnecessary-type-assertion.md b/packages/eslint-plugin/docs/rules/no-unnecessary-type-assertion.md index cfca8f20b1c3..f97d0fab1b9b 100644 --- a/packages/eslint-plugin/docs/rules/no-unnecessary-type-assertion.md +++ b/packages/eslint-plugin/docs/rules/no-unnecessary-type-assertion.md @@ -6,7 +6,11 @@ This rule prohibits using a type assertion that does not change the type of an e This rule aims to prevent unnecessary type assertions. -Examples of **incorrect** code for this rule: +Examples of code for this rule: + + + +### ❌ Incorrect ```ts const foo = 3; @@ -33,7 +37,7 @@ function foo(x: number): number { } ``` -Examples of **correct** code for this rule: +### ✅ Correct ```ts const foo = 3; @@ -53,7 +57,7 @@ function foo(x: number | undefined): number { } ``` -### Options +## Options This rule optionally takes an object with a single property `typesToIgnore`, which can be set to a list of type names to ignore. @@ -68,7 +72,7 @@ const foo: Foo = 3; If you don't care about having no-op type assertions in your code, then you can turn off this rule. -## Related to +## Related To - TSLint: [`no-unnecessary-type-assertion`](https://palantir.github.io/tslint/rules/no-unnecessary-type-assertion/) diff --git a/packages/eslint-plugin/docs/rules/no-unnecessary-type-constraint.md b/packages/eslint-plugin/docs/rules/no-unnecessary-type-constraint.md index dd0d5025f9ad..b69dab2c157d 100644 --- a/packages/eslint-plugin/docs/rules/no-unnecessary-type-constraint.md +++ b/packages/eslint-plugin/docs/rules/no-unnecessary-type-constraint.md @@ -10,7 +10,11 @@ When not provided, type parameters happen to default to: It is therefore redundant to `extend` from these types in later versions of TypeScript. -Examples of **incorrect** code for this rule: +Examples of code for this rule: + + + +### ❌ Incorrect ```ts interface FooAny {} @@ -34,7 +38,7 @@ function QuuzAny() {} function QuuzUnknown() {} ``` -Examples of **correct** code for this rule: +### ✅ Correct ```ts interface Foo {} diff --git a/packages/eslint-plugin/docs/rules/no-unsafe-argument.md b/packages/eslint-plugin/docs/rules/no-unsafe-argument.md index a197ff65cab5..96d994bc67f5 100644 --- a/packages/eslint-plugin/docs/rules/no-unsafe-argument.md +++ b/packages/eslint-plugin/docs/rules/no-unsafe-argument.md @@ -9,7 +9,11 @@ This rule disallows calling a function with `any` in its arguments, and it will This rule also disallows spreading a tuple type with one of its elements typed as `any`. This rule also compares the argument's type to the variable's type to ensure you don't pass an unsafe `any` in a generic position to a receiver that's expecting a specific type. For example, it will error if you assign `Set` to an argument declared as `Set`. -Examples of **incorrect** code for this rule: +Examples of code for this rule: + + + +### ❌ Incorrect ```ts declare function foo(arg1: string, arg2: number, arg2: string): void; @@ -36,7 +40,7 @@ declare function baz(arg1: Set, arg2: Map): void; foo(new Set(), new Map()); ``` -Examples of **correct** code for this rule: +### ✅ Correct ```ts declare function foo(arg1: string, arg2: number, arg2: string): void; @@ -54,6 +58,8 @@ declare function baz(arg1: Set, arg2: Map): void; foo(new Set(), new Map()); ``` + + There are cases where the rule allows passing an argument of `any` to `unknown`. Example of `any` to `unknown` assignment that are allowed. @@ -63,7 +69,7 @@ declare function foo(arg1: unknown, arg2: Set, arg3: unknown[]): void; foo(1 as any, new Set(), [] as any[]); ``` -## Related to +## Related To - [`no-explicit-any`](./no-explicit-any.md) - TSLint: [`no-unsafe-any`](https://palantir.github.io/tslint/rules/no-unsafe-any/) diff --git a/packages/eslint-plugin/docs/rules/no-unsafe-assignment.md b/packages/eslint-plugin/docs/rules/no-unsafe-assignment.md index 36542927eed0..41ecc2ce1649 100644 --- a/packages/eslint-plugin/docs/rules/no-unsafe-assignment.md +++ b/packages/eslint-plugin/docs/rules/no-unsafe-assignment.md @@ -8,7 +8,11 @@ Assigning an `any` typed value to a variable can be hard to pick up on, particul This rule disallows assigning `any` to a variable, and assigning `any[]` to an array destructuring. This rule also compares the assigned type to the variable's type to ensure you don't assign an unsafe `any` in a generic position to a receiver that's expecting a specific type. For example, it will error if you assign `Set` to a variable declared as `Set`. -Examples of **incorrect** code for this rule: +Examples of code for this rule: + + + +### ❌ Incorrect ```ts const x = 1 as any, @@ -33,7 +37,7 @@ const x: Set = new Set(); const x: Set>> = new Set>>(); ``` -Examples of **correct** code for this rule: +### ✅ Correct ```ts const x = 1, @@ -56,6 +60,8 @@ const x: Set = new Set(); const x: Set>> = new Set>>(); ``` + + There are cases where the rule allows assignment of `any` to `unknown`. Example of `any` to `unknown` assignment that are allowed. @@ -66,7 +72,7 @@ const x: unknown[] = y as any[]; const x: Set = y as Set; ``` -## Related to +## Related To - [`no-explicit-any`](./no-explicit-any.md) - TSLint: [`no-unsafe-any`](https://palantir.github.io/tslint/rules/no-unsafe-any/) diff --git a/packages/eslint-plugin/docs/rules/no-unsafe-call.md b/packages/eslint-plugin/docs/rules/no-unsafe-call.md index 0dfb019bbaea..6a2e3933c51d 100644 --- a/packages/eslint-plugin/docs/rules/no-unsafe-call.md +++ b/packages/eslint-plugin/docs/rules/no-unsafe-call.md @@ -7,7 +7,11 @@ The arguments to, and return value of calling an `any` typed variable are not ch This rule disallows calling any variable that is typed as `any`. -Examples of **incorrect** code for this rule: +Examples of code for this rule: + + + +### ❌ Incorrect ```ts declare const anyVar: any; @@ -26,7 +30,7 @@ anyVar`foo`; nestedAny.prop`foo`; ``` -Examples of **correct** code for this rule: +### ✅ Correct ```ts declare const typedVar: () => void; @@ -42,7 +46,7 @@ new Map(); String.raw`foo`; ``` -## Related to +## Related To - [`no-explicit-any`](./no-explicit-any.md) - TSLint: [`no-unsafe-any`](https://palantir.github.io/tslint/rules/no-unsafe-any/) diff --git a/packages/eslint-plugin/docs/rules/no-unsafe-member-access.md b/packages/eslint-plugin/docs/rules/no-unsafe-member-access.md index 39c982d5debc..df9ca7fc26ab 100644 --- a/packages/eslint-plugin/docs/rules/no-unsafe-member-access.md +++ b/packages/eslint-plugin/docs/rules/no-unsafe-member-access.md @@ -7,7 +7,11 @@ Member access on `any` typed variables is not checked at all by TypeScript, so i This rule disallows member access on any variable that is typed as `any`. -Examples of **incorrect** code for this rule: +Examples of code for this rule: + + + +### ❌ Incorrect ```ts declare const anyVar: any; @@ -30,7 +34,7 @@ arr[anyVar]; nestedAny[anyVar]; ``` -Examples of **correct** code for this rule: +### ✅ Correct ```ts declare const properlyTyped: { prop: { a: string } }; @@ -48,7 +52,7 @@ arr[idx]; arr[idx++]; ``` -## Related to +## Related To - [`no-explicit-any`](./no-explicit-any.md) - TSLint: [`no-unsafe-any`](https://palantir.github.io/tslint/rules/no-unsafe-any/) diff --git a/packages/eslint-plugin/docs/rules/no-unsafe-return.md b/packages/eslint-plugin/docs/rules/no-unsafe-return.md index e089757a1771..4cecc7c39c15 100644 --- a/packages/eslint-plugin/docs/rules/no-unsafe-return.md +++ b/packages/eslint-plugin/docs/rules/no-unsafe-return.md @@ -8,7 +8,11 @@ Returned `any` typed values are not checked at all by TypeScript, so it creates This rule disallows returning `any` or `any[]` from a function. This rule also compares the return type to the function's declared/inferred return type to ensure you don't return an unsafe `any` in a generic position to a receiver that's expecting a specific type. For example, it will error if you return `Set` from a function declared as returning `Set`. -Examples of **incorrect** code for this rule: +Examples of code for this rule: + + + +### ❌ Incorrect ```ts function foo1() { @@ -49,7 +53,7 @@ type TAssign = () => Set; const assignability2: TAssign = () => new Set([true]); ``` -Examples of **correct** code for this rule: +### ✅ Correct ```ts function foo1() { @@ -69,6 +73,8 @@ type TAssign = () => Set; const assignability2: TAssign = () => new Set(['foo']); ``` + + There are cases where the rule allows to return `any` to `unknown`. Examples of `any` to `unknown` return that are allowed. @@ -83,7 +89,7 @@ function foo2(): unknown[] { } ``` -## Related to +## Related To - [`no-explicit-any`](./no-explicit-any.md) - TSLint: [`no-unsafe-any`](https://palantir.github.io/tslint/rules/no-unsafe-any/) diff --git a/packages/eslint-plugin/docs/rules/no-unused-expressions.md b/packages/eslint-plugin/docs/rules/no-unused-expressions.md index 760fca411932..8c7beea07b42 100644 --- a/packages/eslint-plugin/docs/rules/no-unused-expressions.md +++ b/packages/eslint-plugin/docs/rules/no-unused-expressions.md @@ -5,7 +5,7 @@ This rule extends the base [`eslint/no-unused-expressions`](https://eslint.org/docs/rules/no-unused-expressions) rule. It adds support for optional call expressions `x?.()`, and directive in module declarations. -## How to use +## How to Use ```jsonc { @@ -19,7 +19,11 @@ It adds support for optional call expressions `x?.()`, and directive in module d See [`eslint/no-unused-expressions` options](https://eslint.org/docs/rules/no-unused-expressions#options). -Taken with ❤️ [from ESLint core](https://github.com/eslint/eslint/blob/master/docs/rules/no-unused-expressions.md) + + +Taken with ❤️ [from ESLint core](https://github.com/eslint/eslint/blob/main/docs/rules/no-unused-expressions.md) + + ## Attributes diff --git a/packages/eslint-plugin/docs/rules/no-unused-vars.md b/packages/eslint-plugin/docs/rules/no-unused-vars.md index 0878d325c1ef..6387b5940446 100644 --- a/packages/eslint-plugin/docs/rules/no-unused-vars.md +++ b/packages/eslint-plugin/docs/rules/no-unused-vars.md @@ -5,7 +5,7 @@ This rule extends the base [`eslint/no-unused-vars`](https://eslint.org/docs/rules/no-unused-vars) rule. It adds support for TypeScript features, such as types. -## How to use +## How to Use ```jsonc { @@ -19,7 +19,11 @@ It adds support for TypeScript features, such as types. See [`eslint/no-unused-vars` options](https://eslint.org/docs/rules/no-unused-vars#options). -Taken with ❤️ [from ESLint core](https://github.com/eslint/eslint/blob/master/docs/rules/no-unused-vars.md) + + +Taken with ❤️ [from ESLint core](https://github.com/eslint/eslint/blob/main/docs/rules/no-unused-vars.md) + + ## Attributes diff --git a/packages/eslint-plugin/docs/rules/no-use-before-define.md b/packages/eslint-plugin/docs/rules/no-use-before-define.md index c6344fb04efc..038b8fbb9b42 100644 --- a/packages/eslint-plugin/docs/rules/no-use-before-define.md +++ b/packages/eslint-plugin/docs/rules/no-use-before-define.md @@ -5,7 +5,7 @@ This rule extends the base [`eslint/no-use-before-define`](https://eslint.org/docs/rules/no-use-before-define) rule. It adds support for `type`, `interface` and `enum` declarations. -## How to use +## How to Use ```jsonc { @@ -40,7 +40,11 @@ const defaultOptions: Options = { If this is `true`, this rule warns every reference to a enum before the enum declaration. If this is `false`, this rule will ignore references to enums, when the reference is in a child scope. -Examples of **incorrect** code for the `{ "enums": true }` option: +Examples of code for the `{ "enums": true }` option: + + + +#### ❌ Incorrect ```ts /*eslint no-use-before-define: ["error", { "enums": true }]*/ @@ -52,7 +56,7 @@ enum Foo { } ``` -Examples of **correct** code for the `{ "enums": false }` option: +#### ✅ Correct ```ts /*eslint no-use-before-define: ["error", { "enums": false }]*/ @@ -101,7 +105,11 @@ enum Enum {} See [`eslint/no-use-before-define` options](https://eslint.org/docs/rules/no-use-before-define#options). -Taken with ❤️ [from ESLint core](https://github.com/eslint/eslint/blob/master/docs/rules/no-use-before-define.md) + + +Taken with ❤️ [from ESLint core](https://github.com/eslint/eslint/blob/main/docs/rules/no-use-before-define.md) + + ## Attributes diff --git a/packages/eslint-plugin/docs/rules/no-useless-constructor.md b/packages/eslint-plugin/docs/rules/no-useless-constructor.md index bca548db536c..a5ef7f0b664a 100644 --- a/packages/eslint-plugin/docs/rules/no-useless-constructor.md +++ b/packages/eslint-plugin/docs/rules/no-useless-constructor.md @@ -9,7 +9,7 @@ It adds support for: - `public` constructors when there is no superclass, - constructors with only parameter properties. -## How to use +## How to Use ```jsonc { @@ -23,7 +23,11 @@ It adds support for: See [`eslint/no-useless-constructor` options](https://eslint.org/docs/rules/no-useless-constructor#options). -Taken with ❤️ [from ESLint core](https://github.com/eslint/eslint/blob/master/docs/rules/no-useless-constructor.md) + + +Taken with ❤️ [from ESLint core](https://github.com/eslint/eslint/blob/main/docs/rules/no-useless-constructor.md) + + ## Attributes diff --git a/packages/eslint-plugin/docs/rules/no-var-requires.md b/packages/eslint-plugin/docs/rules/no-var-requires.md index 19373a892838..7c5a46fa4bc3 100644 --- a/packages/eslint-plugin/docs/rules/no-var-requires.md +++ b/packages/eslint-plugin/docs/rules/no-var-requires.md @@ -4,7 +4,11 @@ In other words, the use of forms such as `var foo = require("foo")` are banned. ## Rule Details -Examples of **incorrect** code for this rule: +Examples of code for this rule: + + + +### ❌ Incorrect ```ts var foo = require('foo'); @@ -12,7 +16,7 @@ const foo = require('foo'); let foo = require('foo'); ``` -Examples of **correct** code for this rule: +### ✅ Correct ```ts import foo = require('foo'); @@ -24,7 +28,7 @@ import foo from 'foo'; If you don't care about TypeScript module syntax, then you will not need this rule. -## Compatibility +## Related To - TSLint: [no-var-requires](https://palantir.github.io/tslint/rules/no-var-requires/) diff --git a/packages/eslint-plugin/docs/rules/non-nullable-type-assertion-style.md b/packages/eslint-plugin/docs/rules/non-nullable-type-assertion-style.md index a152ee122058..f4fe4320f9d3 100644 --- a/packages/eslint-plugin/docs/rules/non-nullable-type-assertion-style.md +++ b/packages/eslint-plugin/docs/rules/non-nullable-type-assertion-style.md @@ -4,7 +4,11 @@ This rule detects when an `as` cast is doing the same job as a `!` would, and su ## Rule Details -Examples of **incorrect** code for this rule: +Examples of code for this rule: + + + +### ❌ Incorrect ```ts const maybe = Math.random() > 0.5 ? '' : undefined; @@ -13,7 +17,7 @@ const definitely = maybe as string; const alsoDefinitely = maybe; ``` -Examples of **correct** code for this rule: +### ✅ Correct ```ts const maybe = Math.random() > 0.5 ? '' : undefined; diff --git a/packages/eslint-plugin/docs/rules/object-curly-spacing.md b/packages/eslint-plugin/docs/rules/object-curly-spacing.md index cccc94747d87..31c1277868f4 100644 --- a/packages/eslint-plugin/docs/rules/object-curly-spacing.md +++ b/packages/eslint-plugin/docs/rules/object-curly-spacing.md @@ -5,7 +5,7 @@ This rule extends the base [`eslint/object-curly-spacing`](https://eslint.org/docs/rules/object-curly-spacing) rule. It adds support for TypeScript's object types. -## How to use +## How to Use ```jsonc { @@ -19,7 +19,11 @@ It adds support for TypeScript's object types. See [`eslint/object-curly-spacing` options](https://eslint.org/docs/rules/object-curly-spacing#options). -Taken with ❤️ [from ESLint core](https://github.com/eslint/eslint/blob/master/docs/rules/object-curly-spacing.md) + + +Taken with ❤️ [from ESLint core](https://github.com/eslint/eslint/blob/main/docs/rules/object-curly-spacing.md) + + ## Attributes diff --git a/packages/eslint-plugin/docs/rules/padding-line-between-statements.md b/packages/eslint-plugin/docs/rules/padding-line-between-statements.md index e0cdb725ad68..fd008c0fff5e 100644 --- a/packages/eslint-plugin/docs/rules/padding-line-between-statements.md +++ b/packages/eslint-plugin/docs/rules/padding-line-between-statements.md @@ -6,7 +6,7 @@ This rule extends the base [`eslint/padding-line-between-statements`](https://es **It adds support for TypeScript constructs such as `interface` and `type`.** -## How to use +## How to Use ```jsonc { @@ -47,7 +47,11 @@ In addition to options provided by ESLint, `interface` and `type` can be used as **Note:** ESLint `cjs-export` and `cjs-import` statement types are renamed to `exports` and `require` respectively. -Taken with ❤️ [from ESLint core](https://github.com/eslint/eslint/blob/master/docs/rules/padding-line-between-statements.md) + + +Taken with ❤️ [from ESLint core](https://github.com/eslint/eslint/blob/main/docs/rules/padding-line-between-statements.md) + + ## Attributes diff --git a/packages/eslint-plugin/docs/rules/prefer-as-const.md b/packages/eslint-plugin/docs/rules/prefer-as-const.md index 94c21dac9d0d..e873bb5a1f77 100644 --- a/packages/eslint-plugin/docs/rules/prefer-as-const.md +++ b/packages/eslint-plugin/docs/rules/prefer-as-const.md @@ -4,7 +4,11 @@ This rule recommends usage of `const` assertion when type primitive value is equ ## Rule Details -Examples of **incorrect** code for this rule: +Examples of code for this rule: + + + +### ❌ Incorrect ```ts let bar: 2 = 2; @@ -12,7 +16,7 @@ let foo = <'bar'>'bar'; let foo = { bar: 'baz' as 'baz' }; ``` -Examples of **correct** code for this rule: +### ✅ Correct ```ts let foo = 'bar'; @@ -23,6 +27,8 @@ let foo = 'bar'; let foo = { bar: 'baz' }; ``` + + ## When Not To Use It If you are using TypeScript < 3.4 diff --git a/packages/eslint-plugin/docs/rules/prefer-enum-initializers.md b/packages/eslint-plugin/docs/rules/prefer-enum-initializers.md index b7aeb21a772f..7baefc1b65ed 100644 --- a/packages/eslint-plugin/docs/rules/prefer-enum-initializers.md +++ b/packages/eslint-plugin/docs/rules/prefer-enum-initializers.md @@ -25,7 +25,11 @@ enum Status { } ``` -Examples of **incorrect** code for this rule: +Examples of code for this rule: + + + +### ❌ Incorrect ```ts enum Status { @@ -45,7 +49,7 @@ enum Color { } ``` -Examples of **correct** code for this rule: +### ✅ Correct ```ts enum Status { diff --git a/packages/eslint-plugin/docs/rules/prefer-for-of.md b/packages/eslint-plugin/docs/rules/prefer-for-of.md index 919531136a47..bb58a598ed5f 100644 --- a/packages/eslint-plugin/docs/rules/prefer-for-of.md +++ b/packages/eslint-plugin/docs/rules/prefer-for-of.md @@ -6,7 +6,11 @@ This rule recommends a for-of loop when the loop index is only used to read from For cases where the index is only used to read from the array being iterated, a for-of loop is easier to read and write. -Examples of **incorrect** code for this rule: +Examples of code for this rule: + + + +### ❌ Incorrect ```js for (let i = 0; i < arr.length; i++) { @@ -14,7 +18,7 @@ for (let i = 0; i < arr.length; i++) { } ``` -Examples of **correct** code for this rule: +### ✅ Correct ```js for (const x of arr) { @@ -36,7 +40,7 @@ for (let i = 0; i < arr.length; i++) { If you transpile for browsers that do not support for-of loops, you may wish to use traditional for loops that produce more compact code. -## Related to +## Related To - TSLint: ['prefer-for-of'](https://palantir.github.io/tslint/rules/prefer-for-of/) diff --git a/packages/eslint-plugin/docs/rules/prefer-function-type.md b/packages/eslint-plugin/docs/rules/prefer-function-type.md index 007f3baa01a0..c67a61bfa5e1 100644 --- a/packages/eslint-plugin/docs/rules/prefer-function-type.md +++ b/packages/eslint-plugin/docs/rules/prefer-function-type.md @@ -4,7 +4,11 @@ This rule suggests using a function type instead of an interface or object type literal with a single call signature. -Examples of **incorrect** code for this rule: +Examples of code for this rule: + + + +### ❌ Incorrect ```ts interface Foo { @@ -31,7 +35,7 @@ interface MixinMethod { } ``` -Examples of **correct** code for this rule: +### ✅ Correct ```ts interface Foo { diff --git a/packages/eslint-plugin/docs/rules/prefer-includes.md b/packages/eslint-plugin/docs/rules/prefer-includes.md index 29c37563fd40..57eb97b271f7 100644 --- a/packages/eslint-plugin/docs/rules/prefer-includes.md +++ b/packages/eslint-plugin/docs/rules/prefer-includes.md @@ -15,7 +15,11 @@ There are such types: `String`, `Array`, `ReadonlyArray`, and typed arrays. Additionally, this rule reports the tests of simple regular expressions in favor of `String#includes`. -Examples of **incorrect** code for this rule: +Examples of code for this rule: + + + +### ❌ Incorrect ```ts let str: string; @@ -39,7 +43,7 @@ userDefined.indexOf(value) >= 0; /foo/.test(str); ``` -Examples of **correct** code for this rule: +### ✅ Correct ```ts let array: any[]; diff --git a/packages/eslint-plugin/docs/rules/prefer-literal-enum-member.md b/packages/eslint-plugin/docs/rules/prefer-literal-enum-member.md index 94891c46235c..eb952dfd407b 100644 --- a/packages/eslint-plugin/docs/rules/prefer-literal-enum-member.md +++ b/packages/eslint-plugin/docs/rules/prefer-literal-enum-member.md @@ -25,7 +25,11 @@ This rule is meant to prevent unexpected results in code by requiring the use of - `allowBitwiseExpressions` set to `true` will allow you to use bitwise expressions in enum initializer (Default: `false`). -Examples of **incorrect** code for this rule: +Examples of code for this rule: + + + +### ❌ Incorrect ```ts const str = 'Test'; @@ -38,7 +42,7 @@ enum Invalid { } ``` -Examples of **correct** code for this rule: +### ✅ Correct ```ts enum Valid { @@ -50,9 +54,15 @@ enum Valid { } ``` + + ### `allowBitwiseExpressions` -Examples of **incorrect** code for the `{ "allowBitwiseExpressions": true }` option: +Examples of code for the `{ "allowBitwiseExpressions": true }` option: + + + +#### ❌ Incorrect ```ts const x = 1; @@ -67,7 +77,7 @@ enum Foo { } ``` -Examples of **correct** code for the `{ "allowBitwiseExpressions": true }` option: +#### ✅ Correct ```ts enum Foo { diff --git a/packages/eslint-plugin/docs/rules/prefer-namespace-keyword.md b/packages/eslint-plugin/docs/rules/prefer-namespace-keyword.md index 4723cbd06f6d..a0ddd28500fc 100644 --- a/packages/eslint-plugin/docs/rules/prefer-namespace-keyword.md +++ b/packages/eslint-plugin/docs/rules/prefer-namespace-keyword.md @@ -17,7 +17,7 @@ If you are using the ES2015 module syntax, then you will not need this rule. - [Namespaces](https://www.typescriptlang.org/docs/handbook/namespaces.html) - [Namespaces and Modules](https://www.typescriptlang.org/docs/handbook/namespaces-and-modules.html) -## Compatibility +## Related To - TSLint: [no-internal-module](https://palantir.github.io/tslint/rules/no-internal-module/) diff --git a/packages/eslint-plugin/docs/rules/prefer-optional-chain.md b/packages/eslint-plugin/docs/rules/prefer-optional-chain.md index 22c596b4375d..179ba9887332 100644 --- a/packages/eslint-plugin/docs/rules/prefer-optional-chain.md +++ b/packages/eslint-plugin/docs/rules/prefer-optional-chain.md @@ -44,7 +44,11 @@ Because the optional chain operator _only_ chains when the property value is `nu This rule aims enforce the usage of the safer operator. -Examples of **incorrect** code for this rule: +Examples of code for this rule: + + + +### ❌ Incorrect ```ts foo && foo.a && foo.a.b && foo.a.b.c; @@ -60,7 +64,7 @@ foo && foo.a.b.c.d.e; ``` -Examples of **correct** code for this rule: +### ✅ Correct ```ts foo?.a?.b?.c; diff --git a/packages/eslint-plugin/docs/rules/prefer-readonly-parameter-types.md b/packages/eslint-plugin/docs/rules/prefer-readonly-parameter-types.md index b6a57c4b0692..6790b55113e8 100644 --- a/packages/eslint-plugin/docs/rules/prefer-readonly-parameter-types.md +++ b/packages/eslint-plugin/docs/rules/prefer-readonly-parameter-types.md @@ -15,7 +15,11 @@ A type is considered readonly if: - it is a readonly tuple type whose elements are all considered readonly. - it is an object type whose properties are all marked as readonly, and whose values are all considered readonly. -Examples of **incorrect** code for this rule: +Examples of code for this rule: + + + +### ❌ Incorrect ```ts function array1(arg: string[]) {} // array is not readonly @@ -57,7 +61,7 @@ interface Foo { } ``` -Examples of **correct** code for this rule: +### ✅ Correct ```ts function array1(arg: readonly string[]) {} @@ -139,7 +143,11 @@ const defaultOptions: Options = { This option allows you to enable or disable the checking of parameter properties. Because parameter properties create properties on the class, it may be undesirable to force them to be readonly. -Examples of **incorrect** code for this rule with `{checkParameterProperties: true}`: +Examples of code for this rule with `{checkParameterProperties: true}`: + + + +#### ❌ Incorrect ```ts class Foo { @@ -147,7 +155,7 @@ class Foo { } ``` -Examples of **correct** code for this rule with `{checkParameterProperties: true}`: +#### ✅ Correct ```ts class Foo { @@ -155,6 +163,8 @@ class Foo { } ``` + + Examples of **correct** code for this rule with `{checkParameterProperties: false}`: ```ts @@ -170,7 +180,11 @@ class Foo { This option allows you to ignore parameters which don't explicitly specify a type. This may be desirable in cases where an external dependency specifies a callback with mutable parameters, and manually annotating the callback's parameters is undesirable. -Examples of **incorrect** code for this rule with `{ignoreInferredTypes: true}`: +Examples of code for this rule with `{ignoreInferredTypes: true}`: + + + +#### ❌ Incorrect ```ts import { acceptsCallback, CallbackOptions } from 'external-dependency'; @@ -193,7 +207,7 @@ export const acceptsCallback: AcceptsCallback;
-Examples of **correct** code for this rule with `{ignoreInferredTypes: true}`: +#### ✅ Correct ```ts import { acceptsCallback } from 'external-dependency'; @@ -220,7 +234,11 @@ export const acceptsCallback: AcceptsCallback; This option allows you to treat all mutable methods as though they were readonly. This may be desirable in when you are never reassigning methods. -Examples of **incorrect** code for this rule with `{treatMethodsAsReadonly: false}`: +Examples of code for this rule with `{treatMethodsAsReadonly: false}`: + + + +#### ❌ Incorrect ```ts type MyType = { @@ -230,7 +248,7 @@ type MyType = { function foo(arg: MyType) {} ``` -Examples of **correct** code for this rule with `{treatMethodsAsReadonly: false}`: +#### ✅ Correct ```ts type MyType = Readonly<{ @@ -246,6 +264,8 @@ type MyOtherType = { function bar(arg: MyOtherType) {} ``` + + Examples of **correct** code for this rule with `{treatMethodsAsReadonly: true}`: ```ts diff --git a/packages/eslint-plugin/docs/rules/prefer-readonly.md b/packages/eslint-plugin/docs/rules/prefer-readonly.md index df41adb8c838..6e336d8c7956 100644 --- a/packages/eslint-plugin/docs/rules/prefer-readonly.md +++ b/packages/eslint-plugin/docs/rules/prefer-readonly.md @@ -7,7 +7,11 @@ This rule enforces that private members are marked as `readonly` if they're neve Member variables with the privacy `private` are never permitted to be modified outside of their declaring class. If that class never modifies their value, they may safely be marked as `readonly`. -Examples of **incorrect** code for this rule: +Examples of code for this rule: + + + +### ❌ Incorrect ```ts class Container { @@ -25,7 +29,7 @@ class Container { } ``` -Examples of **correct** code for this rule: +### ✅ Correct ```ts class Container { @@ -58,25 +62,27 @@ You may pass `"onlyInlineLambdas": true` as a rule option within an object to re } ``` -Example of **correct** code for the `{ "onlyInlineLambdas": true }` options: +Example of code for the `{ "onlyInlineLambdas": true }` options: + +#### ❌ Incorrect ```ts class Container { - private neverModifiedPrivate = 'unchanged'; + private onClick = () => { + /* ... */ + }; } ``` -Example of **incorrect** code for the `{ "onlyInlineLambdas": true }` options: +#### ✅ Correct ```ts class Container { - private onClick = () => { - /* ... */ - }; + private neverModifiedPrivate = 'unchanged'; } ``` -## Related to +## Related To - TSLint: ['prefer-readonly'](https://palantir.github.io/tslint/rules/prefer-readonly) diff --git a/packages/eslint-plugin/docs/rules/prefer-reduce-type-parameter.md b/packages/eslint-plugin/docs/rules/prefer-reduce-type-parameter.md index 04bd5803a4a9..8ad9d675b53c 100644 --- a/packages/eslint-plugin/docs/rules/prefer-reduce-type-parameter.md +++ b/packages/eslint-plugin/docs/rules/prefer-reduce-type-parameter.md @@ -17,7 +17,11 @@ This means that TypeScript doesn't have to try to infer the type, and avoids the This rule looks for calls to `Array#reduce`, and warns if an initial value is being passed & casted, suggesting instead to pass the cast type to `Array#reduce` as its generic parameter. -Examples of **incorrect** code for this rule: +Examples of code for this rule: + + + +### ❌ Incorrect ```ts [1, 2, 3].reduce((arr, num) => arr.concat(num * 2), [] as number[]); @@ -31,7 +35,7 @@ Examples of **incorrect** code for this rule: ); ``` -Examples of **correct** code for this rule: +### ✅ Correct ```ts [1, 2, 3].reduce((arr, num) => arr.concat(num * 2), []); diff --git a/packages/eslint-plugin/docs/rules/prefer-regexp-exec.md b/packages/eslint-plugin/docs/rules/prefer-regexp-exec.md index 32209cf731c5..138ab1c1ded3 100644 --- a/packages/eslint-plugin/docs/rules/prefer-regexp-exec.md +++ b/packages/eslint-plugin/docs/rules/prefer-regexp-exec.md @@ -12,7 +12,11 @@ From [`String#match` on MDN](https://developer.mozilla.org/en-US/docs/Web/JavaSc `RegExp#exec` may also be slightly faster than `String#match`; this is the reason to choose it as the preferred usage. -Examples of **incorrect** code for this rule: +Examples of code for this rule: + + + +### ❌ Incorrect ```ts 'something'.match(/thing/); @@ -24,7 +28,7 @@ const search = /thing/; text.match(search); ``` -Examples of **correct** code for this rule: +### ✅ Correct ```ts /thing/.exec('something'); diff --git a/packages/eslint-plugin/docs/rules/prefer-return-this-type.md b/packages/eslint-plugin/docs/rules/prefer-return-this-type.md index c61c462889f9..acc8520a97d9 100644 --- a/packages/eslint-plugin/docs/rules/prefer-return-this-type.md +++ b/packages/eslint-plugin/docs/rules/prefer-return-this-type.md @@ -43,7 +43,13 @@ const cat = new Cat(); cat.eat().meow(); ``` -Examples of **incorrect** code for this rule: +## Rule Details + +Examples of code for this rule: + + + +### ❌ Incorrect ```ts class Foo { @@ -59,7 +65,7 @@ class Foo { } ``` -Examples of **correct** code for this rule: +### ✅ Correct ```ts class Foo { diff --git a/packages/eslint-plugin/docs/rules/prefer-string-starts-ends-with.md b/packages/eslint-plugin/docs/rules/prefer-string-starts-ends-with.md index 0979653f8560..d67c7bd7d7ec 100644 --- a/packages/eslint-plugin/docs/rules/prefer-string-starts-ends-with.md +++ b/packages/eslint-plugin/docs/rules/prefer-string-starts-ends-with.md @@ -8,7 +8,11 @@ Since ES2015 has added `String#startsWith` and `String#endsWith`, this rule repo This rule is aimed at enforcing a consistent way to check whether a string starts or ends with a specific string. -Examples of **incorrect** code for this rule: +Examples of code for this rule: + + + +### ❌ Incorrect ```ts let foo: string; @@ -32,7 +36,7 @@ foo.match(/bar$/) != null; /bar$/.test(foo); ``` -Examples of **correct** code for this rule: +### ✅ Correct ```ts foo.startsWith('bar'); diff --git a/packages/eslint-plugin/docs/rules/prefer-ts-expect-error.md b/packages/eslint-plugin/docs/rules/prefer-ts-expect-error.md index 2ae69fe7d19d..faf22348aaa2 100644 --- a/packages/eslint-plugin/docs/rules/prefer-ts-expect-error.md +++ b/packages/eslint-plugin/docs/rules/prefer-ts-expect-error.md @@ -14,7 +14,11 @@ This directive operates in the same manner as `@ts-ignore`, but will error if th This rule looks for usages of `@ts-ignore`, and flags them to be replaced with `@ts-expect-error`. -Examples of **incorrect** code for this rule: +Examples of code for this rule: + + + +### ❌ Incorrect ```ts // @ts-ignore @@ -35,7 +39,7 @@ const isOptionEnabled = (key: string): boolean => { }; ``` -Examples of **correct** code for this rule: +### ✅ Correct ```ts // @ts-expect-error diff --git a/packages/eslint-plugin/docs/rules/promise-function-async.md b/packages/eslint-plugin/docs/rules/promise-function-async.md index 7638e6bc5920..f587f6e54891 100644 --- a/packages/eslint-plugin/docs/rules/promise-function-async.md +++ b/packages/eslint-plugin/docs/rules/promise-function-async.md @@ -12,7 +12,11 @@ This rule's practice removes a requirement for creating code to handle both case ## Rule Details -Examples of **incorrect** code for this rule +Examples of code for this rule + + + +### ❌ Incorrect ```ts const arrowFunctionReturnsPromise = () => Promise.resolve('value'); @@ -22,7 +26,7 @@ function functionReturnsPromise() { } ``` -Examples of **correct** code for this rule +### ✅ Correct ```ts const arrowFunctionReturnsPromise = async () => Promise.resolve('value'); diff --git a/packages/eslint-plugin/docs/rules/quotes.md b/packages/eslint-plugin/docs/rules/quotes.md index 0801bebe5cc6..825aeaf4ffaf 100644 --- a/packages/eslint-plugin/docs/rules/quotes.md +++ b/packages/eslint-plugin/docs/rules/quotes.md @@ -5,7 +5,7 @@ This rule extends the base [`eslint/quotes`](https://eslint.org/docs/rules/quotes) rule. It adds support for TypeScript features which allow quoted names, but not backtick quoted names. -## How to use +## How to Use ```jsonc { @@ -19,7 +19,11 @@ It adds support for TypeScript features which allow quoted names, but not backti See [`eslint/quotes` options](https://eslint.org/docs/rules/quotes#options). -Taken with ❤️ [from ESLint core](https://github.com/eslint/eslint/blob/master/docs/rules/quotes.md) + + +Taken with ❤️ [from ESLint core](https://github.com/eslint/eslint/blob/main/docs/rules/quotes.md) + + ## Attributes diff --git a/packages/eslint-plugin/docs/rules/require-array-sort-compare.md b/packages/eslint-plugin/docs/rules/require-array-sort-compare.md index f05bd66e9f54..87e7ed552857 100644 --- a/packages/eslint-plugin/docs/rules/require-array-sort-compare.md +++ b/packages/eslint-plugin/docs/rules/require-array-sort-compare.md @@ -23,7 +23,11 @@ https://www.ecma-international.org/ecma-262/9.0/#sec-sortcompare This rule aims to ensure all calls of the native `Array#sort` method provide a `compareFunction`, while ignoring calls to user-defined `sort` methods. -Examples of **incorrect** code for this rule: +Examples of code for this rule: + + + +### ❌ Incorrect ```ts const array: any[]; @@ -35,7 +39,7 @@ array.sort(); stringArray.sort(); ``` -Examples of **correct** code for this rule: +### ✅ Correct ```ts const array: any[]; @@ -66,7 +70,11 @@ const defaults = { ### `ignoreStringArrays` -Examples of **incorrect** code for this rule with `{ ignoreStringArrays: true }`: +Examples of code for this rule with `{ ignoreStringArrays: true }`: + + + +#### ❌ Incorrect ```ts const one = 1; @@ -75,7 +83,7 @@ const three = 3; [one, two, three].sort(); ``` -Examples of **correct** code for this rule with `{ ignoreStringArrays: true }`: +#### ✅ Correct ```ts const one = '1'; diff --git a/packages/eslint-plugin/docs/rules/require-await.md b/packages/eslint-plugin/docs/rules/require-await.md index a0eeda41eaf7..baa155b435bc 100644 --- a/packages/eslint-plugin/docs/rules/require-await.md +++ b/packages/eslint-plugin/docs/rules/require-await.md @@ -15,7 +15,7 @@ async function returnsPromise1() { const returnsPromise2 = () => returnsPromise1(); ``` -## How to use +## How to Use ```jsonc { @@ -29,7 +29,11 @@ const returnsPromise2 = () => returnsPromise1(); See [`eslint/require-await` options](https://eslint.org/docs/rules/require-await#options). -Taken with ❤️ [from ESLint core](https://github.com/eslint/eslint/blob/master/docs/rules/require-await.md) + + +Taken with ❤️ [from ESLint core](https://github.com/eslint/eslint/blob/main/docs/rules/require-await.md) + + ## Attributes diff --git a/packages/eslint-plugin/docs/rules/restrict-plus-operands.md b/packages/eslint-plugin/docs/rules/restrict-plus-operands.md index 5299b7345268..7ad23b607638 100644 --- a/packages/eslint-plugin/docs/rules/restrict-plus-operands.md +++ b/packages/eslint-plugin/docs/rules/restrict-plus-operands.md @@ -1,19 +1,25 @@ # When adding two variables, operands must both be of type number or of type string (`restrict-plus-operands`) -Examples of **correct** code: +## Rule Details -```ts -var foo = parseInt('5.5', 10) + 10; -var foo = 1n + 1n; -``` +Examples of code for this rule: -Examples of **incorrect** code: + + +### ❌ Incorrect ```ts var foo = '5.5' + 5; var foo = 1n + 1; ``` +### ✅ Correct + +```ts +var foo = parseInt('5.5', 10) + 10; +var foo = 1n + 1n; +``` + ## Options This rule has an object option: @@ -23,7 +29,11 @@ This rule has an object option: ### `checkCompoundAssignments` -Examples of **incorrect** code for the `{ "checkCompoundAssignments": true }` option: +Examples of code for the `{ "checkCompoundAssignments": true }` option: + + + +#### ❌ Incorrect ```ts /*eslint @typescript-eslint/restrict-plus-operands: ["error", { "checkCompoundAssignments": true }]*/ @@ -35,7 +45,7 @@ let bar: string = ''; bar += 0; ``` -Examples of **correct** code for the `{ "checkCompoundAssignments": true }` option: +#### ✅ Correct ```ts /*eslint @typescript-eslint/restrict-plus-operands: ["error", { "checkCompoundAssignments": true }]*/ @@ -47,13 +57,15 @@ let bar = ''; bar += 'test'; ``` +## How to Use + ```json { "@typescript-eslint/restrict-plus-operands": "error" } ``` -## Compatibility +## Related To - TSLint: [restrict-plus-operands](https://palantir.github.io/tslint/rules/restrict-plus-operands/) diff --git a/packages/eslint-plugin/docs/rules/restrict-template-expressions.md b/packages/eslint-plugin/docs/rules/restrict-template-expressions.md index d56eb6ae74b9..771af5469e35 100644 --- a/packages/eslint-plugin/docs/rules/restrict-template-expressions.md +++ b/packages/eslint-plugin/docs/rules/restrict-template-expressions.md @@ -1,17 +1,12 @@ # Enforce template literal expressions to be of string type (`restrict-template-expressions`) -Examples of **correct** code: +## Rule Details -```ts -const arg = 'foo'; -const msg1 = `arg = ${arg}`; -const msg2 = `arg = ${arg || 'default'}`; +Examples of code for this rule: -const stringWithKindProp: string & { _kind?: 'MyString' } = 'foo'; -const msg3 = `stringWithKindProp = ${stringWithKindProp}`; -``` + -Examples of **incorrect** code: +### ❌ Incorrect ```ts const arg1 = [1, 2]; @@ -21,6 +16,17 @@ const arg2 = { name: 'Foo' }; const msg2 = `arg2 = ${arg2 || null}`; ``` +### ✅ Correct + +```ts +const arg = 'foo'; +const msg1 = `arg = ${arg}`; +const msg2 = `arg = ${arg || 'default'}`; + +const stringWithKindProp: string & { _kind?: 'MyString' } = 'foo'; +const msg3 = `stringWithKindProp = ${stringWithKindProp}`; +``` + ## Options The rule accepts an options object with the following properties: diff --git a/packages/eslint-plugin/docs/rules/return-await.md b/packages/eslint-plugin/docs/rules/return-await.md index df8813ff9c0f..cefe5d157536 100644 --- a/packages/eslint-plugin/docs/rules/return-await.md +++ b/packages/eslint-plugin/docs/rules/return-await.md @@ -7,7 +7,7 @@ Returning an awaited promise can make sense for better stack trace information a This rule builds on top of the [`eslint/no-return-await`](https://eslint.org/docs/rules/no-return-await) rule. It expands upon the base rule to add support for optionally requiring `return await` in certain cases. -## How to use +## How to Use ```jsonc { @@ -35,7 +35,11 @@ Specifically: - if you `return` a promise within a `catch`, and there **_is a_** `finally`, then it **_must_** be `await`ed. - if you `return` a promise within a `finally`, then it **_must not_** be `await`ed. -Examples of **incorrect** code with `in-try-catch`: +Examples of code with `in-try-catch`: + + + +#### ❌ Incorrect ```ts async function invalidInTryCatch1() { @@ -81,7 +85,7 @@ async function invalidInTryCatch6() { } ``` -Examples of **correct** code with `in-try-catch`: +#### ✅ Correct ```ts async function validInTryCatch1() { @@ -131,7 +135,11 @@ async function validInTryCatch6() { Requires that all returned promises are `await`ed. -Examples of **incorrect** code with `always`: +Examples of code with `always`: + + + +#### ❌ Incorrect ```ts async function invalidAlways1() { @@ -149,7 +157,7 @@ async function invalidAlways3() { } ``` -Examples of **correct** code with `always`: +#### ✅ Correct ```ts async function validAlways1() { @@ -171,7 +179,11 @@ async function validAlways3() { Disallows all `await`ing any returned promises. -Examples of **incorrect** code with `never`: +Examples of code with `never`: + + + +#### ❌ Incorrect ```ts async function invalidNever1() { @@ -189,7 +201,7 @@ async function invalidNever3() { } ``` -Examples of **correct** code with `never`: +#### ✅ Correct ```ts async function validNever1() { diff --git a/packages/eslint-plugin/docs/rules/semi.md b/packages/eslint-plugin/docs/rules/semi.md index 15b13d80b4a4..2953c7670e17 100644 --- a/packages/eslint-plugin/docs/rules/semi.md +++ b/packages/eslint-plugin/docs/rules/semi.md @@ -9,7 +9,7 @@ It adds support for TypeScript features that require semicolons. See also the [`@typescript-eslint/member-delimiter-style`](member-delimiter-style.md) rule, which allows you to specify the delimiter for `type` and `interface` members. -## How to use +## How to Use ```jsonc { @@ -23,7 +23,11 @@ See also the [`@typescript-eslint/member-delimiter-style`](member-delimiter-styl See [`eslint/semi` options](https://eslint.org/docs/rules/semi#options). -Taken with ❤️ [from ESLint core](https://github.com/eslint/eslint/blob/master/docs/rules/semi.md) + + +Taken with ❤️ [from ESLint core](https://github.com/eslint/eslint/blob/main/docs/rules/semi.md) + + ## Attributes diff --git a/packages/eslint-plugin/docs/rules/sort-type-union-intersection-members.md b/packages/eslint-plugin/docs/rules/sort-type-union-intersection-members.md index c275dc5ee052..749b88075bf9 100644 --- a/packages/eslint-plugin/docs/rules/sort-type-union-intersection-members.md +++ b/packages/eslint-plugin/docs/rules/sort-type-union-intersection-members.md @@ -23,7 +23,11 @@ function compare(a, b) { In other words, the types are sorted alphabetically, case-insensitively and treating numbers like a human would, falling back to character code sorting in case of ties. -Examples of **incorrect** code for this rule: +Examples of code for this rule: + + + +### ❌ Incorrect ```ts type T1 = B | A; @@ -53,7 +57,7 @@ type T4 = | any; ``` -Examples of **correct** code for this rule: +### ✅ Correct ```ts type T1 = A | B; diff --git a/packages/eslint-plugin/docs/rules/space-before-function-paren.md b/packages/eslint-plugin/docs/rules/space-before-function-paren.md index 29378534d40c..d9dd3dd234ec 100644 --- a/packages/eslint-plugin/docs/rules/space-before-function-paren.md +++ b/packages/eslint-plugin/docs/rules/space-before-function-paren.md @@ -5,7 +5,7 @@ This rule extends the base [`eslint/space-before-function-paren`](https://eslint.org/docs/rules/space-before-function-paren) rule. It adds support for generic type parameters on function calls. -## How to use +## How to Use ```jsonc { @@ -19,7 +19,11 @@ It adds support for generic type parameters on function calls. See [`eslint/space-before-function-paren` options](https://eslint.org/docs/rules/space-before-function-paren#options). -Taken with ❤️ [from ESLint core](https://github.com/eslint/eslint/blob/master/docs/rules/space-before-function-paren.md) + + +Taken with ❤️ [from ESLint core](https://github.com/eslint/eslint/blob/main/docs/rules/space-before-function-paren.md) + + ## Attributes diff --git a/packages/eslint-plugin/docs/rules/space-infix-ops.md b/packages/eslint-plugin/docs/rules/space-infix-ops.md index 030edc2f69c9..1f2d90445270 100644 --- a/packages/eslint-plugin/docs/rules/space-infix-ops.md +++ b/packages/eslint-plugin/docs/rules/space-infix-ops.md @@ -10,7 +10,7 @@ enum MyEnum { } ``` -## How to use +## How to Use ```jsonc { @@ -23,7 +23,11 @@ enum MyEnum { See [`eslint/space-infix-ops` options](https://eslint.org/docs/rules/space-infix-ops#options). -Taken with ❤️ [from ESLint core](https://github.com/eslint/eslint/blob/master/docs/rules/space-infix-ops.md) + + +Taken with ❤️ [from ESLint core](https://github.com/eslint/eslint/blob/main/docs/rules/space-infix-ops.md) + + ## Attributes diff --git a/packages/eslint-plugin/docs/rules/strict-boolean-expressions.md b/packages/eslint-plugin/docs/rules/strict-boolean-expressions.md index 1d5b0435fe17..61cc9efcc753 100644 --- a/packages/eslint-plugin/docs/rules/strict-boolean-expressions.md +++ b/packages/eslint-plugin/docs/rules/strict-boolean-expressions.md @@ -15,7 +15,11 @@ The following nodes are considered boolean expressions and their type is checked ## Examples -Examples of **incorrect** code for this rule: +Examples of code for this rule: + + + +### ❌ Incorrect ```ts // nullable numbers are considered unsafe by default @@ -47,7 +51,7 @@ while (obj) { } ``` -Examples of **correct** code for this rule: +### ✅ Correct ```tsx // Using logical operator short-circuiting is allowed @@ -155,7 +159,7 @@ You should be using `strictNullChecks` to ensure complete type-safety in your co If for some reason you cannot turn on `strictNullChecks`, but still want to use this rule - you can use this option to allow it - but know that the behavior of this rule is _undefined_ with the compiler option turned off. We will not accept bug reports if you are using this option. -## Fixes and suggestions +## Fixes and Suggestions This rule provides following fixes and suggestions for particular types in boolean context: @@ -190,7 +194,6 @@ This rule provides following fixes and suggestions for particular types in boole ## Related To - TSLint: [strict-boolean-expressions](https://palantir.github.io/tslint/rules/strict-boolean-expressions) - - [no-unnecessary-condition](./no-unnecessary-condition.md) - Similar rule which reports always-truthy and always-falsy values in conditions ## Attributes diff --git a/packages/eslint-plugin/docs/rules/switch-exhaustiveness-check.md b/packages/eslint-plugin/docs/rules/switch-exhaustiveness-check.md index b5f3ad11f3c6..4f11c66eb428 100644 --- a/packages/eslint-plugin/docs/rules/switch-exhaustiveness-check.md +++ b/packages/eslint-plugin/docs/rules/switch-exhaustiveness-check.md @@ -2,7 +2,13 @@ Union type may have a lot of parts. It's easy to forget to consider all cases in switch. This rule reminds which parts are missing. If domain of the problem requires to have only a partial switch, developer may _explicitly_ add a default clause. -Examples of **incorrect** code for this rule: +## Rule Details + +Examples of code for this rule: + + + +### ❌ Incorrect ```ts type Day = @@ -25,7 +31,7 @@ switch (day) { } ``` -Examples of **correct** code for this rule: +### ✅ Correct ```ts type Day = @@ -72,7 +78,7 @@ switch (day) { } ``` -or +### ✅ Correct ```ts type Day = diff --git a/packages/eslint-plugin/docs/rules/triple-slash-reference.md b/packages/eslint-plugin/docs/rules/triple-slash-reference.md index 89bf247650de..bb32ea5b7596 100644 --- a/packages/eslint-plugin/docs/rules/triple-slash-reference.md +++ b/packages/eslint-plugin/docs/rules/triple-slash-reference.md @@ -50,7 +50,7 @@ If you want to ban use of one or all of the triple slash reference directives, o If you want to use all flavors of triple slash reference directives. -## Compatibility +## Related To - TSLint: [no-reference](http://palantir.github.io/tslint/rules/no-reference/) - TSLint: [no-reference-import](https://palantir.github.io/tslint/rules/no-reference-import/) diff --git a/packages/eslint-plugin/docs/rules/type-annotation-spacing.md b/packages/eslint-plugin/docs/rules/type-annotation-spacing.md index cb6b694a15b9..b1ca3e149504 100644 --- a/packages/eslint-plugin/docs/rules/type-annotation-spacing.md +++ b/packages/eslint-plugin/docs/rules/type-annotation-spacing.md @@ -45,7 +45,11 @@ This rule has an object option: ### defaults -Examples of **incorrect** code for this rule with no options at all: +Examples of code for this rule with no options at all: + + + +#### ❌ Incorrect ```ts @@ -74,7 +78,7 @@ type Foo = () =>{}; type Foo = ()=> {}; ``` -Examples of **correct** code for this rule with no options at all: +#### ✅ Correct ```ts @@ -91,7 +95,11 @@ type Foo = () => {}; ### after -Examples of **incorrect** code for this rule with `{ "before": false, "after": true }`: +Examples of code for this rule with `{ "before": false, "after": true }`: + + + +#### ❌ Incorrect ```ts @@ -120,7 +128,7 @@ type Foo = () =>{}; type Foo = () => {}; ``` -Examples of **correct** code for this rule with `{ "before": false, "after": true }`: +#### ✅ Correct ```ts @@ -137,7 +145,11 @@ type Foo = ()=> {}; ### before -Examples of **incorrect** code for this rule with `{ "before": true, "after": true }` options: +Examples of code for this rule with `{ "before": true, "after": true }` options: + + + +#### ❌ Incorrect ```ts @@ -166,7 +178,7 @@ type Foo = () =>{}; type Foo = ()=> {}; ``` -Examples of **correct** code for this rule with `{ "before": true, "after": true }` options: +#### ✅ Correct ```ts @@ -183,7 +195,11 @@ type Foo = () => {}; ### overrides - colon -Examples of **incorrect** code for this rule with `{ "before": false, "after": false, overrides: { colon: { before: true, after: true }} }` options: +Examples of code for this rule with `{ "before": false, "after": false, overrides: { colon: { before: true, after: true }} }` options: + + + +#### ❌ Incorrect ```ts @@ -212,7 +228,7 @@ type Foo = ()=> {}; type Foo = () => {}; ``` -Examples of **correct** code for this rule with `{ "before": false, "after": false, overrides: { colon: { before: true, after: true }} }` options: +#### ✅ Correct ```ts @@ -233,7 +249,11 @@ type Foo = ()=>{}; ### overrides - arrow -Examples of **incorrect** code for this rule with `{ "before": false, "after": false, overrides: { arrow: { before: true, after: true }} }` options: +Examples of code for this rule with `{ "before": false, "after": false, overrides: { arrow: { before: true, after: true }} }` options: + + + +#### ❌ Incorrect ```ts @@ -262,7 +282,7 @@ type Foo = () =>{}; type Foo = ()=> {}; ``` -Examples of **correct** code for this rule with `{ "before": false, "after": false, overrides: { arrow: { before: true, after: true }} }` options: +#### ✅ Correct ```ts @@ -286,7 +306,7 @@ If you don't want to enforce spacing for your type annotations, you can safely t - [TypeScript Type System](https://basarat.gitbooks.io/typescript/docs/types/type-system.html) - [Type Inference](https://www.typescriptlang.org/docs/handbook/type-inference.html) -## Compatibility +## Related To - TSLint: [`typedef-whitespace`](https://palantir.github.io/tslint/rules/typedef-whitespace/) diff --git a/packages/eslint-plugin/docs/rules/typedef.md b/packages/eslint-plugin/docs/rules/typedef.md index e3de5baf5e56..e4ae60a80523 100644 --- a/packages/eslint-plugin/docs/rules/typedef.md +++ b/packages/eslint-plugin/docs/rules/typedef.md @@ -77,14 +77,18 @@ For example, with the following configuration: Whether to enforce type annotations on variables declared using array destructuring. -Examples of **incorrect** code with `{ "arrayDestructuring": true }`: +Examples of code with `{ "arrayDestructuring": true }`: + + + +#### ❌ Incorrect ```ts const [a] = [1]; const [b, c] = [1, 2]; ``` -Examples of **correct** code with `{ "arrayDestructuring": true }`: +#### ✅ Correct ```ts const [a]: number[] = [1]; @@ -99,7 +103,11 @@ for (const [key, val] of new Map([['key', 1]])) { Whether to enforce type annotations for parameters of arrow functions. -Examples of **incorrect** code with `{ "arrowParameter": true }`: +Examples of code with `{ "arrowParameter": true }`: + + + +#### ❌ Incorrect ```ts const logsSize = size => console.log(size); @@ -111,7 +119,7 @@ const mapper = { }; ``` -Examples of **correct** code with `{ "arrowParameter": true }`: +#### ✅ Correct ```ts const logsSize = (size: number) => console.log(size); @@ -127,7 +135,11 @@ const mapper = { Whether to enforce type annotations on member variables of classes. -Examples of **incorrect** code with `{ "memberVariableDeclaration": true }`: +Examples of code with `{ "memberVariableDeclaration": true }`: + + + +#### ❌ Incorrect ```ts class ContainsText { @@ -136,7 +148,7 @@ class ContainsText { } ``` -Examples of **correct** code with `{ "memberVariableDeclaration": true }`: +#### ✅ Correct ```ts class ContainsText { @@ -149,14 +161,18 @@ class ContainsText { Whether to enforce type annotations on variables declared using object destructuring. -Examples of **incorrect** code with `{ "objectDestructuring": true }`: +Examples of code with `{ "objectDestructuring": true }`: + + + +#### ❌ Incorrect ```ts const { length } = 'text'; const [b, c] = Math.random() ? [1, 2] : [3, 4]; ``` -Examples of **correct** code with `{ "objectDestructuring": true }`: +#### ✅ Correct ```ts const { length }: { length: number } = 'text'; @@ -170,7 +186,11 @@ for (const { key, val } of [{ key: 'key', val: 1 }]) { Whether to enforce type annotations for parameters of functions and methods. -Examples of **incorrect** code with `{ "parameter": true }`: +Examples of code with `{ "parameter": true }`: + + + +#### ❌ Incorrect ```ts function logsSize(size): void { @@ -198,7 +218,7 @@ class Logger { } ``` -Examples of **correct** code with `{ "parameter": true }`: +#### ✅ Correct ```ts function logsSize(size: number): void { @@ -230,7 +250,11 @@ class Logger { Whether to enforce type annotations for properties of interfaces and types. -Examples of **incorrect** code with `{ "propertyDeclaration": true }`: +Examples of code with `{ "propertyDeclaration": true }`: + + + +#### ❌ Incorrect ```ts type Members = { @@ -239,7 +263,7 @@ type Members = { }; ``` -Examples of **correct** code with `{ "propertyDeclaration": true }`: +#### ✅ Correct ```ts type Members = { @@ -252,7 +276,11 @@ type Members = { Whether to enforce type annotations for variable declarations, excluding array and object destructuring. -Examples of **incorrect** code with `{ "variableDeclaration": true }`: +Examples of code with `{ "variableDeclaration": true }`: + + + +#### ❌ Incorrect ```ts const text = 'text'; @@ -260,7 +288,7 @@ let initialText = 'text'; let delayedText; ``` -Examples of **correct** code with `{ "variableDeclaration": true }`: +#### ✅ Correct ```ts const text: string = 'text'; @@ -272,13 +300,17 @@ let delayedText: string; Ignore variable declarations for non-arrow and arrow functions. -Examples of **incorrect** code with `{ "variableDeclaration": true, "variableDeclarationIgnoreFunction": true }`: +Examples of code with `{ "variableDeclaration": true, "variableDeclarationIgnoreFunction": true }`: + + + +#### ❌ Incorrect ```ts const text = 'text'; ``` -Examples of **correct** code with `{ "variableDeclaration": true, "variableDeclarationIgnoreFunction": true }`: +#### ✅ Correct ```ts const a = (): void => {}; @@ -303,7 +335,7 @@ In general, if you do not consider the cost of writing unnecessary type annotati - [TypeScript Type System](https://basarat.gitbooks.io/typescript/docs/types/type-system.html) - [Type Inference](https://www.typescriptlang.org/docs/handbook/type-inference.html) -## Compatibility +## Related To - TSLint: [`typedef`](https://palantir.github.io/tslint/rules/typedef) diff --git a/packages/eslint-plugin/docs/rules/unbound-method.md b/packages/eslint-plugin/docs/rules/unbound-method.md index 22a9a9039b39..aa9a8c40d090 100644 --- a/packages/eslint-plugin/docs/rules/unbound-method.md +++ b/packages/eslint-plugin/docs/rules/unbound-method.md @@ -10,7 +10,11 @@ If you're working with `jest`, you can use [`eslint-plugin-jest`'s version of th ## Rule Details -Examples of **incorrect** code for this rule +Examples of code for this rule + + + +### ❌ Incorrect ```ts class MyClass { @@ -37,7 +41,7 @@ const arith = { const { double } = arith; ``` -Examples of **correct** code for this rule +### ✅ Correct ```ts class MyClass { diff --git a/packages/eslint-plugin/docs/rules/unified-signatures.md b/packages/eslint-plugin/docs/rules/unified-signatures.md index f9d6b3a49e69..f71435dc3e01 100644 --- a/packages/eslint-plugin/docs/rules/unified-signatures.md +++ b/packages/eslint-plugin/docs/rules/unified-signatures.md @@ -6,29 +6,33 @@ Warns for any two overloads that could be unified into one by using a union or a This rule aims to keep the source code as maintainable as possible by reducing the amount of overloads. -Examples of **incorrect** code for this rule: +Examples of code for this rule: + + + +### ❌ Incorrect ```ts -function f(x: number): void; -function f(x: string): void; +function x(x: number): void; +function x(x: string): void; ``` ```ts -f(): void; -f(...x: number[]): void; +function y(): void; +function y(...x: number[]): void; ``` -Examples of **correct** code for this rule: +### ✅ Correct ```ts -function f(x: number | string): void; +function x(x: number | string): void; ``` ```ts -function f(x?: ...number[]): void; +function y(...x: number[]): void; ``` -## Related to +## Related To - TSLint: [`unified-signatures`](https://palantir.github.io/tslint/rules/unified-signatures/) diff --git a/packages/eslint-plugin/package.json b/packages/eslint-plugin/package.json index 0c797871a425..db47b6544d2e 100644 --- a/packages/eslint-plugin/package.json +++ b/packages/eslint-plugin/package.json @@ -1,6 +1,6 @@ { "name": "@typescript-eslint/eslint-plugin", - "version": "5.4.0", + "version": "5.5.0", "description": "TypeScript plugin for ESLint", "keywords": [ "eslint", @@ -44,8 +44,8 @@ "typecheck": "tsc -p tsconfig.json --noEmit" }, "dependencies": { - "@typescript-eslint/experimental-utils": "5.4.0", - "@typescript-eslint/scope-manager": "5.4.0", + "@typescript-eslint/experimental-utils": "5.5.0", + "@typescript-eslint/scope-manager": "5.5.0", "debug": "^4.3.2", "functional-red-black-tree": "^1.0.1", "ignore": "^5.1.8", @@ -60,6 +60,7 @@ "chalk": "^4.1.2", "marked": "^3.0.7", "prettier": "*", + "title-case": "^3.0.3", "typescript": "*" }, "peerDependencies": { diff --git a/packages/eslint-plugin/src/rules/array-type.ts b/packages/eslint-plugin/src/rules/array-type.ts index da2bfdef518b..35045fdd9850 100644 --- a/packages/eslint-plugin/src/rules/array-type.ts +++ b/packages/eslint-plugin/src/rules/array-type.ts @@ -80,9 +80,9 @@ type Options = [ ]; type MessageIds = | 'errorStringGeneric' - | 'errorStringGenericSimple' | 'errorStringArray' - | 'errorStringArraySimple'; + | 'errorStringArraySimple' + | 'errorStringGenericSimple'; const arrayOption = { enum: ['array', 'generic', 'array-simple'] }; @@ -98,13 +98,13 @@ export default util.createRule({ fixable: 'code', messages: { errorStringGeneric: - "Array type using '{{type}}[]' is forbidden. Use 'Array<{{type}}>' instead.", - errorStringGenericSimple: - "Array type using '{{type}}[]' is forbidden for non-simple types. Use 'Array<{{type}}>' instead.", + "Array type using '{{readonlyPrefix}}{{type}}[]' is forbidden. Use '{{className}}<{{type}}>' instead.", errorStringArray: - "Array type using 'Array<{{type}}>' is forbidden. Use '{{type}}[]' instead.", + "Array type using '{{className}}<{{type}}>' is forbidden. Use '{{readonlyPrefix}}{{type}}[]' instead.", errorStringArraySimple: - "Array type using 'Array<{{type}}>' is forbidden for simple types. Use '{{type}}[]' instead.", + "Array type using '{{className}}<{{type}}>' is forbidden for simple types. Use '{{readonlyPrefix}}{{type}}[]' instead.", + errorStringGenericSimple: + "Array type using '{{readonlyPrefix}}{{type}}[]' is forbidden for non-simple types. Use '{{className}}<{{type}}>' instead.", }, schema: [ { @@ -163,6 +163,8 @@ export default util.createRule({ node: errorNode, messageId, data: { + className: isReadonly ? 'ReadonlyArray' : 'Array', + readonlyPrefix: isReadonly ? 'readonly ' : '', type: getMessageType(node.elementType), }, fix(fixer) { @@ -216,6 +218,8 @@ export default util.createRule({ node, messageId, data: { + className: isReadonlyArrayType ? 'ReadonlyArray' : 'Array', + readonlyPrefix, type: 'any', }, fix(fixer) { @@ -250,6 +254,8 @@ export default util.createRule({ node, messageId, data: { + className: isReadonlyArrayType ? 'ReadonlyArray' : 'Array', + readonlyPrefix, type: getMessageType(type), }, fix(fixer) { diff --git a/packages/eslint-plugin/src/rules/ban-ts-comment.ts b/packages/eslint-plugin/src/rules/ban-ts-comment.ts index a9e0caf0fe47..020b0a33a3b2 100644 --- a/packages/eslint-plugin/src/rules/ban-ts-comment.ts +++ b/packages/eslint-plugin/src/rules/ban-ts-comment.ts @@ -99,7 +99,7 @@ export default util.createRule<[Options], MessageIds>({ create(context, [options]) { /* The regex used are taken from the ones used in the official TypeScript repo - - https://github.com/microsoft/TypeScript/blob/master/src/compiler/scanner.ts#L281-L289 + https://github.com/microsoft/TypeScript/blob/main/src/compiler/scanner.ts#L281-L289 */ const commentDirectiveRegExSingleLine = /^\/*\s*@ts-(expect-error|ignore|check|nocheck)(.*)/; diff --git a/packages/eslint-plugin/src/rules/ban-types.ts b/packages/eslint-plugin/src/rules/ban-types.ts index dea2e79c4b0e..ad0620f2185d 100644 --- a/packages/eslint-plugin/src/rules/ban-types.ts +++ b/packages/eslint-plugin/src/rules/ban-types.ts @@ -25,7 +25,7 @@ export type Options = [ export type MessageIds = 'bannedTypeMessage'; function removeSpaces(str: string): string { - return str.replace(/ /g, ''); + return str.replace(/\s/g, ''); } function stringifyNode( diff --git a/packages/eslint-plugin/src/rules/member-ordering.ts b/packages/eslint-plugin/src/rules/member-ordering.ts index c1e30adc2ef5..baaf32aa3553 100644 --- a/packages/eslint-plugin/src/rules/member-ordering.ts +++ b/packages/eslint-plugin/src/rules/member-ordering.ts @@ -8,9 +8,14 @@ import * as util from '../util'; export type MessageIds = 'incorrectGroupOrder' | 'incorrectOrder'; +type Order = + | 'alphabetically' + | 'alphabetically-case-insensitive' + | 'as-written'; + interface SortedOrderConfig { memberTypes?: string[] | 'never'; - order: 'alphabetically' | 'as-written'; + order: Order; } type OrderConfig = string[] | SortedOrderConfig | 'never'; @@ -46,7 +51,7 @@ const objectConfig = (memberTypes: string[]): JSONSchema.JSONSchema4 => ({ }, order: { type: 'string', - enum: ['alphabetically', 'as-written'], + enum: ['alphabetically', 'alphabetically-case-insensitive', 'as-written'], }, }, additionalProperties: false, @@ -265,6 +270,31 @@ function getNodeType(node: Member): string | null { } } +/** + * Gets the raw string value of a member's name + */ +function getMemberRawName( + member: + | TSESTree.MethodDefinition + | TSESTree.TSMethodSignature + | TSESTree.TSAbstractMethodDefinition + | TSESTree.PropertyDefinition + | TSESTree.TSAbstractPropertyDefinition + | TSESTree.Property + | TSESTree.TSPropertySignature, + sourceCode: TSESLint.SourceCode, +): string { + const { name, type } = util.getNameFromMember(member, sourceCode); + + if (type === util.MemberNameType.Quoted) { + return name.substr(1, name.length - 2); + } + if (type === util.MemberNameType.Private) { + return name.substr(1); + } + return name; +} + /** * Gets the member name based on the member type. * @@ -280,12 +310,12 @@ function getMemberName( case AST_NODE_TYPES.TSMethodSignature: case AST_NODE_TYPES.TSAbstractPropertyDefinition: case AST_NODE_TYPES.PropertyDefinition: - return util.getNameFromMember(node, sourceCode).name; + return getMemberRawName(node, sourceCode); case AST_NODE_TYPES.TSAbstractMethodDefinition: case AST_NODE_TYPES.MethodDefinition: return node.kind === 'constructor' ? 'constructor' - : util.getNameFromMember(node, sourceCode).name; + : getMemberRawName(node, sourceCode); case AST_NODE_TYPES.TSConstructSignatureDeclaration: return 'new'; case AST_NODE_TYPES.TSCallSignatureDeclaration: @@ -539,10 +569,14 @@ export default util.createRule({ * Checks if the members are alphabetically sorted. * * @param members Members to be validated. + * @param caseSensitive indicates if the alpha ordering is case sensitive or not. * * @return True if all members are correctly sorted. */ - function checkAlphaSort(members: Member[]): boolean { + function checkAlphaSort( + members: Member[], + caseSensitive: boolean, + ): boolean { let previousName = ''; let isCorrectlySorted = true; @@ -552,7 +586,11 @@ export default util.createRule({ // Note: Not all members have names if (name) { - if (name < previousName) { + if ( + caseSensitive + ? name < previousName + : name.toLowerCase() < previousName.toLowerCase() + ) { context.report({ node: member, messageId: 'incorrectOrder', @@ -589,7 +627,7 @@ export default util.createRule({ } // Standardize config - let order = null; + let order: Order | null = null; let memberTypes; if (Array.isArray(orderConfig)) { @@ -599,6 +637,10 @@ export default util.createRule({ memberTypes = orderConfig.memberTypes; } + const hasAlphaSort = order?.startsWith('alphabetically'); + const alphaSortIsCaseSensitive = + order !== 'alphabetically-case-insensitive'; + // Check order if (Array.isArray(memberTypes)) { const grouped = checkGroupSort(members, memberTypes, supportsModifiers); @@ -607,11 +649,14 @@ export default util.createRule({ return; } - if (order === 'alphabetically') { - grouped.some(groupMember => !checkAlphaSort(groupMember)); + if (hasAlphaSort) { + grouped.some( + groupMember => + !checkAlphaSort(groupMember, alphaSortIsCaseSensitive), + ); } - } else if (order === 'alphabetically') { - checkAlphaSort(members); + } else if (hasAlphaSort) { + checkAlphaSort(members, alphaSortIsCaseSensitive); } } diff --git a/packages/eslint-plugin/src/rules/no-duplicate-imports.ts b/packages/eslint-plugin/src/rules/no-duplicate-imports.ts index b2ded1a2ea73..f270e7600d1a 100644 --- a/packages/eslint-plugin/src/rules/no-duplicate-imports.ts +++ b/packages/eslint-plugin/src/rules/no-duplicate-imports.ts @@ -54,16 +54,6 @@ export default util.createRule({ }); } - function isStringLiteral( - node: TSESTree.Node | null, - ): node is TSESTree.StringLiteral { - return ( - !!node && - node.type === AST_NODE_TYPES.Literal && - typeof node.value === 'string' - ); - } - function isAllMemberImport(node: TSESTree.ImportDeclaration): boolean { return node.specifiers.every( specifier => specifier.type === AST_NODE_TYPES.ImportSpecifier, @@ -71,7 +61,7 @@ export default util.createRule({ } function checkTypeImport(node: TSESTree.ImportDeclaration): void { - if (isStringLiteral(node.source)) { + if (node.source) { const value = node.source.value; const isMemberImport = isAllMemberImport(node); if ( @@ -96,7 +86,7 @@ export default util.createRule({ function checkTypeExport( node: TSESTree.ExportNamedDeclaration | TSESTree.ExportAllDeclaration, ): void { - if (isStringLiteral(node.source)) { + if (node.source) { const value = node.source.value; if (typeExports.has(value)) { report('exportType', node, value); diff --git a/packages/eslint-plugin/src/rules/no-var-requires.ts b/packages/eslint-plugin/src/rules/no-var-requires.ts index b599d4cb87a6..1bf51efe8102 100644 --- a/packages/eslint-plugin/src/rules/no-var-requires.ts +++ b/packages/eslint-plugin/src/rules/no-var-requires.ts @@ -1,4 +1,5 @@ import { + ASTUtils, AST_NODE_TYPES, TSESTree, } from '@typescript-eslint/experimental-utils'; @@ -43,10 +44,14 @@ export default util.createRule({ AST_NODE_TYPES.VariableDeclarator, ].includes(parent.type) ) { - context.report({ - node, - messageId: 'noVarReqs', - }); + const variable = ASTUtils.findVariable(context.getScope(), 'require'); + + if (!variable?.identifiers.length) { + context.report({ + node, + messageId: 'noVarReqs', + }); + } } }, }; diff --git a/packages/eslint-plugin/src/rules/padding-line-between-statements.ts b/packages/eslint-plugin/src/rules/padding-line-between-statements.ts index 334b6928c630..13a84c234195 100644 --- a/packages/eslint-plugin/src/rules/padding-line-between-statements.ts +++ b/packages/eslint-plugin/src/rules/padding-line-between-statements.ts @@ -16,7 +16,7 @@ import * as util from '../util'; * We have tried to keep the implementation as close as possible to the eslint implementation, to make * patching easier for future contributors. * - * Reference rule - https://github.com/eslint/eslint/blob/master/lib/rules/padding-line-between-statements.js + * Reference rule - https://github.com/eslint/eslint/blob/main/lib/rules/padding-line-between-statements.js */ type NodeTest = ( diff --git a/packages/eslint-plugin/src/rules/prefer-for-of.ts b/packages/eslint-plugin/src/rules/prefer-for-of.ts index e1c3a0580166..453d126c813e 100644 --- a/packages/eslint-plugin/src/rules/prefer-for-of.ts +++ b/packages/eslint-plugin/src/rules/prefer-for-of.ts @@ -174,6 +174,7 @@ export default util.createRule({ !contains(body, id) || (node !== undefined && node.type === AST_NODE_TYPES.MemberExpression && + node.object.type !== AST_NODE_TYPES.ThisExpression && node.property === id && sourceCode.getText(node.object) === arrayText && !isAssignee(node)) diff --git a/packages/eslint-plugin/src/rules/require-await.ts b/packages/eslint-plugin/src/rules/require-await.ts index a1e92e8f91d8..f0935b202ea9 100644 --- a/packages/eslint-plugin/src/rules/require-await.ts +++ b/packages/eslint-plugin/src/rules/require-await.ts @@ -115,16 +115,22 @@ export default util.createRule({ if (node?.argument?.type === AST_NODE_TYPES.Literal) { // making this `false` as for literals we don't need to check the definition // eg : async function* run() { yield* 1 } - scopeInfo.isAsyncYield = false; + scopeInfo.isAsyncYield ||= false; } const tsNode = parserServices.esTreeNodeToTSNodeMap.get(node?.argument); const type = checker.getTypeAtLocation(tsNode); - const symbol = type.getSymbol(); - - // async function* test1() {yield* asyncGenerator() } - if (symbol?.getName() === 'AsyncGenerator') { - scopeInfo.isAsyncYield = true; + const typesToCheck = expandUnionOrIntersectionType(type); + for (const type of typesToCheck) { + const asyncIterator = tsutils.getWellKnownSymbolPropertyOfType( + type, + 'asyncIterator', + checker, + ); + if (asyncIterator !== undefined) { + scopeInfo.isAsyncYield = true; + break; + } } } @@ -230,3 +236,10 @@ function getFunctionHeadLoc( end, }; } + +function expandUnionOrIntersectionType(type: ts.Type): ts.Type[] { + if (type.isUnionOrIntersection()) { + return type.types.flatMap(expandUnionOrIntersectionType); + } + return [type]; +} diff --git a/packages/eslint-plugin/src/util/getWrappingFixer.ts b/packages/eslint-plugin/src/util/getWrappingFixer.ts index 00c00e748829..d5efa8bfca76 100644 --- a/packages/eslint-plugin/src/util/getWrappingFixer.ts +++ b/packages/eslint-plugin/src/util/getWrappingFixer.ts @@ -1,10 +1,10 @@ import { AST_NODE_TYPES, TSESLint, + ASTUtils, TSESTree, } from '@typescript-eslint/experimental-utils'; import { SourceCode } from '@typescript-eslint/experimental-utils/src/ts-eslint'; -import * as util from '../util'; interface WrappingFixerParams { /** Source code. */ @@ -57,7 +57,7 @@ export function getWrappingFixer( if (isWeakPrecedenceParent(node)) { // we wrapped the node in some expression which very likely has a different precedence than original wrapped node // let's wrap the whole expression in parens just in case - if (!util.isParenthesized(node, sourceCode)) { + if (!ASTUtils.isParenthesized(node, sourceCode)) { code = `(${code})`; } } diff --git a/packages/eslint-plugin/tests/docs.test.ts b/packages/eslint-plugin/tests/docs.test.ts index 42c8af85ce68..61487b5169f0 100644 --- a/packages/eslint-plugin/tests/docs.test.ts +++ b/packages/eslint-plugin/tests/docs.test.ts @@ -3,6 +3,7 @@ import path from 'path'; import marked from 'marked'; import rules from '../src/rules'; +import { titleCase } from 'title-case'; const docsRoot = path.resolve(__dirname, '../docs/rules'); const rulesData = Object.entries(rules); @@ -11,18 +12,20 @@ function createRuleLink(ruleName: string): string { return `[\`@typescript-eslint/${ruleName}\`](./docs/rules/${ruleName}.md)`; } +function parseMarkdownFile(filePath: string): marked.TokensList { + const file = fs.readFileSync(filePath, 'utf-8'); + + return marked.lexer(file, { + gfm: true, + silent: false, + }); +} + function parseReadme(): { base: marked.Tokens.Table; extension: marked.Tokens.Table; } { - const readmeRaw = fs.readFileSync( - path.resolve(__dirname, '../README.md'), - 'utf8', - ); - const readme = marked.lexer(readmeRaw, { - gfm: true, - silent: false, - }); + const readme = parseMarkdownFile(path.resolve(__dirname, '../README.md')); // find the table const rulesTables = readme.filter( @@ -56,11 +59,7 @@ describe('Validating rule docs', () => { const filePath = path.join(docsRoot, `${ruleName}.md`); it(`Description of ${ruleName}.md must match`, () => { // validate if description of rule is same as in docs - const file = fs.readFileSync(filePath, 'utf-8'); - const tokens = marked.lexer(file, { - gfm: true, - silent: false, - }); + const tokens = parseMarkdownFile(filePath); // Rule title not found. // Rule title does not match the rule metadata. @@ -71,12 +70,21 @@ describe('Validating rule docs', () => { }); }); - it('Attributes in the docs must match the metadata', () => { - const file = fs.readFileSync(filePath, 'utf-8'); - const tokens = marked.lexer(file, { - gfm: true, - silent: false, - }); + it(`Headers in ${ruleName}.md must be title-cased`, () => { + const tokens = parseMarkdownFile(filePath); + + // Get all H2 headers objects as the other levels are variable by design. + const headers = tokens.filter( + token => token.type === 'heading' && token.depth === 2, + ) as marked.Tokens.Heading[]; + + headers.forEach(header => + expect(header.text).toBe(titleCase(header.text)), + ); + }); + + it(`Attributes in ${ruleName}.md must match the metadata`, () => { + const tokens = parseMarkdownFile(filePath); // Verify attributes header exists const attributesHeaderIndex = tokens.findIndex( diff --git a/packages/eslint-plugin/tests/rules/array-type.test.ts b/packages/eslint-plugin/tests/rules/array-type.test.ts index 3c2c1893a3f8..eafd9a912cf3 100644 --- a/packages/eslint-plugin/tests/rules/array-type.test.ts +++ b/packages/eslint-plugin/tests/rules/array-type.test.ts @@ -375,7 +375,7 @@ function bazFunction(baz: Arr>) { errors: [ { messageId: 'errorStringArray', - data: { type: 'number' }, + data: { className: 'Array', readonlyPrefix: '', type: 'number' }, line: 1, column: 8, }, @@ -388,7 +388,7 @@ function bazFunction(baz: Arr>) { errors: [ { messageId: 'errorStringArray', - data: { type: 'T' }, + data: { className: 'Array', readonlyPrefix: '', type: 'T' }, line: 1, column: 8, }, @@ -401,7 +401,11 @@ function bazFunction(baz: Arr>) { errors: [ { messageId: 'errorStringArray', - data: { type: 'number' }, + data: { + className: 'ReadonlyArray', + readonlyPrefix: 'readonly ', + type: 'number', + }, line: 1, column: 8, }, @@ -414,7 +418,11 @@ function bazFunction(baz: Arr>) { errors: [ { messageId: 'errorStringArray', - data: { type: 'T' }, + data: { + className: 'ReadonlyArray', + readonlyPrefix: 'readonly ', + type: 'T', + }, line: 1, column: 8, }, @@ -427,7 +435,7 @@ function bazFunction(baz: Arr>) { errors: [ { messageId: 'errorStringArray', - data: { type: 'number' }, + data: { className: 'Array', readonlyPrefix: '', type: 'number' }, line: 1, column: 8, }, @@ -440,7 +448,7 @@ function bazFunction(baz: Arr>) { errors: [ { messageId: 'errorStringArray', - data: { type: 'T' }, + data: { className: 'Array', readonlyPrefix: '', type: 'T' }, line: 1, column: 8, }, @@ -453,7 +461,11 @@ function bazFunction(baz: Arr>) { errors: [ { messageId: 'errorStringArray', - data: { type: 'number' }, + data: { + className: 'ReadonlyArray', + readonlyPrefix: 'readonly ', + type: 'number', + }, line: 1, column: 8, }, @@ -466,7 +478,11 @@ function bazFunction(baz: Arr>) { errors: [ { messageId: 'errorStringArray', - data: { type: 'T' }, + data: { + className: 'ReadonlyArray', + readonlyPrefix: 'readonly ', + type: 'T', + }, line: 1, column: 8, }, @@ -479,7 +495,7 @@ function bazFunction(baz: Arr>) { errors: [ { messageId: 'errorStringArray', - data: { type: 'number' }, + data: { className: 'Array', readonlyPrefix: '', type: 'number' }, line: 1, column: 8, }, @@ -492,7 +508,7 @@ function bazFunction(baz: Arr>) { errors: [ { messageId: 'errorStringArray', - data: { type: 'T' }, + data: { className: 'Array', readonlyPrefix: '', type: 'T' }, line: 1, column: 8, }, @@ -505,7 +521,11 @@ function bazFunction(baz: Arr>) { errors: [ { messageId: 'errorStringArraySimple', - data: { type: 'number' }, + data: { + className: 'ReadonlyArray', + readonlyPrefix: 'readonly ', + type: 'number', + }, line: 1, column: 8, }, @@ -518,7 +538,11 @@ function bazFunction(baz: Arr>) { errors: [ { messageId: 'errorStringGenericSimple', - data: { type: 'T' }, + data: { + className: 'ReadonlyArray', + readonlyPrefix: 'readonly ', + type: 'T', + }, line: 1, column: 8, }, @@ -531,7 +555,7 @@ function bazFunction(baz: Arr>) { errors: [ { messageId: 'errorStringArray', - data: { type: 'number' }, + data: { className: 'Array', readonlyPrefix: '', type: 'number' }, line: 1, column: 8, }, @@ -544,7 +568,7 @@ function bazFunction(baz: Arr>) { errors: [ { messageId: 'errorStringArray', - data: { type: 'T' }, + data: { className: 'Array', readonlyPrefix: '', type: 'T' }, line: 1, column: 8, }, @@ -557,7 +581,11 @@ function bazFunction(baz: Arr>) { errors: [ { messageId: 'errorStringGeneric', - data: { type: 'number' }, + data: { + className: 'ReadonlyArray', + readonlyPrefix: 'readonly ', + type: 'number', + }, line: 1, column: 8, }, @@ -570,7 +598,11 @@ function bazFunction(baz: Arr>) { errors: [ { messageId: 'errorStringGeneric', - data: { type: 'T' }, + data: { + className: 'ReadonlyArray', + readonlyPrefix: 'readonly ', + type: 'T', + }, line: 1, column: 8, }, @@ -583,7 +615,7 @@ function bazFunction(baz: Arr>) { errors: [ { messageId: 'errorStringArraySimple', - data: { type: 'number' }, + data: { className: 'Array', readonlyPrefix: '', type: 'number' }, line: 1, column: 8, }, @@ -596,7 +628,7 @@ function bazFunction(baz: Arr>) { errors: [ { messageId: 'errorStringGenericSimple', - data: { type: 'T' }, + data: { className: 'Array', readonlyPrefix: '', type: 'T' }, line: 1, column: 8, }, @@ -609,7 +641,11 @@ function bazFunction(baz: Arr>) { errors: [ { messageId: 'errorStringArraySimple', - data: { type: 'number' }, + data: { + className: 'ReadonlyArray', + readonlyPrefix: 'readonly ', + type: 'number', + }, line: 1, column: 8, }, @@ -622,7 +658,11 @@ function bazFunction(baz: Arr>) { errors: [ { messageId: 'errorStringGenericSimple', - data: { type: 'T' }, + data: { + className: 'ReadonlyArray', + readonlyPrefix: 'readonly ', + type: 'T', + }, line: 1, column: 8, }, @@ -635,7 +675,7 @@ function bazFunction(baz: Arr>) { errors: [ { messageId: 'errorStringArraySimple', - data: { type: 'number' }, + data: { className: 'Array', readonlyPrefix: '', type: 'number' }, line: 1, column: 8, }, @@ -648,7 +688,7 @@ function bazFunction(baz: Arr>) { errors: [ { messageId: 'errorStringGenericSimple', - data: { type: 'T' }, + data: { className: 'Array', readonlyPrefix: '', type: 'T' }, line: 1, column: 8, }, @@ -661,7 +701,11 @@ function bazFunction(baz: Arr>) { errors: [ { messageId: 'errorStringArray', - data: { type: 'number' }, + data: { + className: 'ReadonlyArray', + readonlyPrefix: 'readonly ', + type: 'number', + }, line: 1, column: 8, }, @@ -674,7 +718,11 @@ function bazFunction(baz: Arr>) { errors: [ { messageId: 'errorStringArray', - data: { type: 'T' }, + data: { + className: 'ReadonlyArray', + readonlyPrefix: 'readonly ', + type: 'T', + }, line: 1, column: 8, }, @@ -687,7 +735,7 @@ function bazFunction(baz: Arr>) { errors: [ { messageId: 'errorStringArraySimple', - data: { type: 'number' }, + data: { className: 'Array', readonlyPrefix: '', type: 'number' }, line: 1, column: 8, }, @@ -700,7 +748,7 @@ function bazFunction(baz: Arr>) { errors: [ { messageId: 'errorStringGenericSimple', - data: { type: 'T' }, + data: { className: 'Array', readonlyPrefix: '', type: 'T' }, line: 1, column: 8, }, @@ -713,7 +761,11 @@ function bazFunction(baz: Arr>) { errors: [ { messageId: 'errorStringArraySimple', - data: { type: 'number' }, + data: { + className: 'ReadonlyArray', + readonlyPrefix: 'readonly ', + type: 'number', + }, line: 1, column: 8, }, @@ -726,7 +778,11 @@ function bazFunction(baz: Arr>) { errors: [ { messageId: 'errorStringGenericSimple', - data: { type: 'T' }, + data: { + className: 'ReadonlyArray', + readonlyPrefix: 'readonly ', + type: 'T', + }, line: 1, column: 8, }, @@ -739,7 +795,7 @@ function bazFunction(baz: Arr>) { errors: [ { messageId: 'errorStringArraySimple', - data: { type: 'number' }, + data: { className: 'Array', readonlyPrefix: '', type: 'number' }, line: 1, column: 8, }, @@ -752,7 +808,7 @@ function bazFunction(baz: Arr>) { errors: [ { messageId: 'errorStringGenericSimple', - data: { type: 'T' }, + data: { className: 'Array', readonlyPrefix: '', type: 'T' }, line: 1, column: 8, }, @@ -765,7 +821,11 @@ function bazFunction(baz: Arr>) { errors: [ { messageId: 'errorStringGeneric', - data: { type: 'number' }, + data: { + className: 'ReadonlyArray', + readonlyPrefix: 'readonly ', + type: 'number', + }, line: 1, column: 8, }, @@ -778,7 +838,11 @@ function bazFunction(baz: Arr>) { errors: [ { messageId: 'errorStringGeneric', - data: { type: 'T' }, + data: { + className: 'ReadonlyArray', + readonlyPrefix: 'readonly ', + type: 'T', + }, line: 1, column: 8, }, @@ -791,7 +855,7 @@ function bazFunction(baz: Arr>) { errors: [ { messageId: 'errorStringGeneric', - data: { type: 'number' }, + data: { className: 'Array', readonlyPrefix: '', type: 'number' }, line: 1, column: 8, }, @@ -804,7 +868,7 @@ function bazFunction(baz: Arr>) { errors: [ { messageId: 'errorStringGeneric', - data: { type: 'T' }, + data: { className: 'Array', readonlyPrefix: '', type: 'T' }, line: 1, column: 8, }, @@ -817,7 +881,11 @@ function bazFunction(baz: Arr>) { errors: [ { messageId: 'errorStringGeneric', - data: { type: 'number' }, + data: { + className: 'ReadonlyArray', + readonlyPrefix: 'readonly ', + type: 'number', + }, line: 1, column: 8, }, @@ -830,7 +898,11 @@ function bazFunction(baz: Arr>) { errors: [ { messageId: 'errorStringGeneric', - data: { type: 'T' }, + data: { + className: 'ReadonlyArray', + readonlyPrefix: 'readonly ', + type: 'T', + }, line: 1, column: 8, }, @@ -843,7 +915,7 @@ function bazFunction(baz: Arr>) { errors: [ { messageId: 'errorStringGeneric', - data: { type: 'number' }, + data: { className: 'Array', readonlyPrefix: '', type: 'number' }, line: 1, column: 8, }, @@ -856,7 +928,7 @@ function bazFunction(baz: Arr>) { errors: [ { messageId: 'errorStringGeneric', - data: { type: 'T' }, + data: { className: 'Array', readonlyPrefix: '', type: 'T' }, line: 1, column: 8, }, @@ -869,7 +941,11 @@ function bazFunction(baz: Arr>) { errors: [ { messageId: 'errorStringArray', - data: { type: 'number' }, + data: { + className: 'ReadonlyArray', + readonlyPrefix: 'readonly ', + type: 'number', + }, line: 1, column: 8, }, @@ -882,7 +958,11 @@ function bazFunction(baz: Arr>) { errors: [ { messageId: 'errorStringArray', - data: { type: 'T' }, + data: { + className: 'ReadonlyArray', + readonlyPrefix: 'readonly ', + type: 'T', + }, line: 1, column: 8, }, @@ -895,7 +975,7 @@ function bazFunction(baz: Arr>) { errors: [ { messageId: 'errorStringGeneric', - data: { type: 'number' }, + data: { className: 'Array', readonlyPrefix: '', type: 'number' }, line: 1, column: 8, }, @@ -908,7 +988,7 @@ function bazFunction(baz: Arr>) { errors: [ { messageId: 'errorStringGeneric', - data: { type: 'T' }, + data: { className: 'Array', readonlyPrefix: '', type: 'T' }, line: 1, column: 8, }, @@ -921,7 +1001,11 @@ function bazFunction(baz: Arr>) { errors: [ { messageId: 'errorStringArraySimple', - data: { type: 'number' }, + data: { + className: 'ReadonlyArray', + readonlyPrefix: 'readonly ', + type: 'number', + }, line: 1, column: 8, }, @@ -934,7 +1018,11 @@ function bazFunction(baz: Arr>) { errors: [ { messageId: 'errorStringGenericSimple', - data: { type: 'T' }, + data: { + className: 'ReadonlyArray', + readonlyPrefix: 'readonly ', + type: 'T', + }, line: 1, column: 8, }, @@ -947,7 +1035,7 @@ function bazFunction(baz: Arr>) { errors: [ { messageId: 'errorStringGeneric', - data: { type: 'number' }, + data: { className: 'Array', readonlyPrefix: '', type: 'number' }, line: 1, column: 8, }, @@ -960,7 +1048,7 @@ function bazFunction(baz: Arr>) { errors: [ { messageId: 'errorStringGeneric', - data: { type: 'T' }, + data: { className: 'Array', readonlyPrefix: '', type: 'T' }, line: 1, column: 8, }, @@ -973,7 +1061,11 @@ function bazFunction(baz: Arr>) { errors: [ { messageId: 'errorStringGeneric', - data: { type: 'number' }, + data: { + className: 'ReadonlyArray', + readonlyPrefix: 'readonly ', + type: 'number', + }, line: 1, column: 8, }, @@ -986,7 +1078,11 @@ function bazFunction(baz: Arr>) { errors: [ { messageId: 'errorStringGeneric', - data: { type: 'T' }, + data: { + className: 'ReadonlyArray', + readonlyPrefix: 'readonly ', + type: 'T', + }, line: 1, column: 8, }, @@ -1001,7 +1097,7 @@ function bazFunction(baz: Arr>) { errors: [ { messageId: 'errorStringArray', - data: { type: 'Bar' }, + data: { className: 'Array', readonlyPrefix: '', type: 'Bar' }, line: 1, column: 15, }, @@ -1014,7 +1110,7 @@ function bazFunction(baz: Arr>) { errors: [ { messageId: 'errorStringGeneric', - data: { type: 'Bar' }, + data: { className: 'Array', readonlyPrefix: '', type: 'Bar' }, line: 1, column: 21, }, @@ -1027,7 +1123,7 @@ function bazFunction(baz: Arr>) { errors: [ { messageId: 'errorStringGeneric', - data: { type: 'Bar' }, + data: { className: 'Array', readonlyPrefix: '', type: 'Bar' }, line: 1, column: 27, }, @@ -1040,13 +1136,13 @@ function bazFunction(baz: Arr>) { errors: [ { messageId: 'errorStringArray', - data: { type: 'Bar' }, + data: { className: 'Array', readonlyPrefix: '', type: 'Bar' }, line: 1, column: 17, }, { messageId: 'errorStringArray', - data: { type: 'Bar' }, + data: { className: 'Array', readonlyPrefix: '', type: 'Bar' }, line: 1, column: 30, }, @@ -1059,7 +1155,7 @@ function bazFunction(baz: Arr>) { errors: [ { messageId: 'errorStringArraySimple', - data: { type: 'undefined' }, + data: { className: 'Array', readonlyPrefix: '', type: 'undefined' }, line: 1, column: 8, }, @@ -1072,7 +1168,7 @@ function bazFunction(baz: Arr>) { errors: [ { messageId: 'errorStringArraySimple', - data: { type: 'string' }, + data: { className: 'Array', readonlyPrefix: '', type: 'string' }, line: 1, column: 20, }, @@ -1085,7 +1181,7 @@ function bazFunction(baz: Arr>) { errors: [ { messageId: 'errorStringArraySimple', - data: { type: 'any' }, + data: { className: 'Array', readonlyPrefix: '', type: 'any' }, line: 1, column: 8, }, @@ -1098,7 +1194,7 @@ function bazFunction(baz: Arr>) { errors: [ { messageId: 'errorStringGenericSimple', - data: { type: 'T' }, + data: { className: 'Array', readonlyPrefix: '', type: 'T' }, line: 1, column: 24, }, @@ -1111,7 +1207,7 @@ function bazFunction(baz: Arr>) { errors: [ { messageId: 'errorStringArraySimple', - data: { type: 'T' }, + data: { className: 'Array', readonlyPrefix: '', type: 'T' }, line: 1, column: 15, }, @@ -1130,7 +1226,7 @@ let yyyy: Arr>>> = [[[['2']]]]; errors: [ { messageId: 'errorStringGenericSimple', - data: { type: 'T' }, + data: { className: 'Array', readonlyPrefix: '', type: 'T' }, line: 3, column: 15, }, @@ -1157,7 +1253,7 @@ interface ArrayClass { errors: [ { messageId: 'errorStringArraySimple', - data: { type: 'T' }, + data: { className: 'Array', readonlyPrefix: '', type: 'T' }, line: 3, column: 8, }, @@ -1178,7 +1274,7 @@ function barFunction(bar: Array>) { errors: [ { messageId: 'errorStringGenericSimple', - data: { type: 'T' }, + data: { className: 'Array', readonlyPrefix: '', type: 'T' }, line: 2, column: 27, }, @@ -1191,7 +1287,7 @@ function barFunction(bar: Array>) { errors: [ { messageId: 'errorStringGenericSimple', - data: { type: 'T' }, + data: { className: 'Array', readonlyPrefix: '', type: 'T' }, line: 1, column: 13, }, @@ -1204,7 +1300,7 @@ function barFunction(bar: Array>) { errors: [ { messageId: 'errorStringGenericSimple', - data: { type: 'T' }, + data: { className: 'Array', readonlyPrefix: '', type: 'T' }, line: 1, column: 17, }, @@ -1217,7 +1313,7 @@ function barFunction(bar: Array>) { errors: [ { messageId: 'errorStringGenericSimple', - data: { type: 'T' }, + data: { className: 'Array', readonlyPrefix: '', type: 'T' }, line: 1, column: 24, }, @@ -1230,7 +1326,11 @@ function barFunction(bar: Array>) { errors: [ { messageId: 'errorStringArraySimple', - data: { type: 'fooName.BarType' }, + data: { + className: 'Array', + readonlyPrefix: '', + type: 'fooName.BarType', + }, line: 1, column: 8, }, @@ -1243,7 +1343,7 @@ function barFunction(bar: Array>) { errors: [ { messageId: 'errorStringGenericSimple', - data: { type: 'T' }, + data: { className: 'Array', readonlyPrefix: '', type: 'T' }, line: 1, column: 8, }, @@ -1256,7 +1356,7 @@ function barFunction(bar: Array>) { errors: [ { messageId: 'errorStringArray', - data: { type: 'undefined' }, + data: { className: 'Array', readonlyPrefix: '', type: 'undefined' }, line: 1, column: 8, }, @@ -1269,7 +1369,7 @@ function barFunction(bar: Array>) { errors: [ { messageId: 'errorStringArray', - data: { type: 'string' }, + data: { className: 'Array', readonlyPrefix: '', type: 'string' }, line: 1, column: 20, }, @@ -1282,7 +1382,7 @@ function barFunction(bar: Array>) { errors: [ { messageId: 'errorStringArray', - data: { type: 'any' }, + data: { className: 'Array', readonlyPrefix: '', type: 'any' }, line: 1, column: 8, }, @@ -1295,7 +1395,7 @@ function barFunction(bar: Array>) { errors: [ { messageId: 'errorStringArray', - data: { type: 'T' }, + data: { className: 'Array', readonlyPrefix: '', type: 'T' }, line: 1, column: 15, }, @@ -1314,7 +1414,7 @@ let yyyy: Arr[][]> = [[[['2']]]]; errors: [ { messageId: 'errorStringArray', - data: { type: 'T' }, + data: { className: 'Array', readonlyPrefix: '', type: 'T' }, line: 3, column: 15, }, @@ -1339,7 +1439,7 @@ interface ArrayClass { errors: [ { messageId: 'errorStringArray', - data: { type: 'T' }, + data: { className: 'Array', readonlyPrefix: '', type: 'T' }, line: 3, column: 8, }, @@ -1360,7 +1460,7 @@ function fooFunction(foo: ArrayClass[]) { errors: [ { messageId: 'errorStringArray', - data: { type: 'T' }, + data: { className: 'Array', readonlyPrefix: '', type: 'T' }, line: 2, column: 27, }, @@ -1373,7 +1473,7 @@ function fooFunction(foo: ArrayClass[]) { errors: [ { messageId: 'errorStringArray', - data: { type: 'T' }, + data: { className: 'Array', readonlyPrefix: '', type: 'T' }, line: 1, column: 13, }, @@ -1386,7 +1486,7 @@ function fooFunction(foo: ArrayClass[]) { errors: [ { messageId: 'errorStringArray', - data: { type: 'T' }, + data: { className: 'Array', readonlyPrefix: '', type: 'T' }, line: 1, column: 17, }, @@ -1399,7 +1499,7 @@ function fooFunction(foo: ArrayClass[]) { errors: [ { messageId: 'errorStringArray', - data: { type: 'T' }, + data: { className: 'Array', readonlyPrefix: '', type: 'T' }, line: 1, column: 24, }, @@ -1412,7 +1512,7 @@ function fooFunction(foo: ArrayClass[]) { errors: [ { messageId: 'errorStringArray', - data: { type: 'any' }, + data: { className: 'Array', readonlyPrefix: '', type: 'any' }, line: 1, column: 8, }, @@ -1425,7 +1525,7 @@ function fooFunction(foo: ArrayClass[]) { errors: [ { messageId: 'errorStringArray', - data: { type: 'any' }, + data: { className: 'Array', readonlyPrefix: '', type: 'any' }, line: 1, column: 8, }, @@ -1438,7 +1538,7 @@ function fooFunction(foo: ArrayClass[]) { errors: [ { messageId: 'errorStringArraySimple', - data: { type: 'any' }, + data: { className: 'Array', readonlyPrefix: '', type: 'any' }, line: 1, column: 8, }, @@ -1463,7 +1563,7 @@ function fooFunction(foo: ArrayClass[]) { errors: [ { messageId: 'errorStringGeneric', - data: { type: 'number' }, + data: { className: 'Array', readonlyPrefix: '', type: 'number' }, line: 1, column: 31, }, @@ -1476,7 +1576,7 @@ function fooFunction(foo: ArrayClass[]) { errors: [ { messageId: 'errorStringGeneric', - data: { type: 'string' }, + data: { className: 'Array', readonlyPrefix: '', type: 'string' }, line: 1, column: 8, }, @@ -1489,7 +1589,7 @@ function fooFunction(foo: ArrayClass[]) { errors: [ { messageId: 'errorStringGeneric', - data: { type: 'T' }, + data: { className: 'Array', readonlyPrefix: '', type: 'T' }, line: 1, column: 24, }, @@ -1508,7 +1608,7 @@ let yyyy: Arr>>> = [[[['2']]]]; errors: [ { messageId: 'errorStringGeneric', - data: { type: 'T' }, + data: { className: 'Array', readonlyPrefix: '', type: 'T' }, line: 3, column: 15, }, @@ -1533,7 +1633,7 @@ interface ArrayClass { errors: [ { messageId: 'errorStringGeneric', - data: { type: 'T' }, + data: { className: 'Array', readonlyPrefix: '', type: 'T' }, line: 4, column: 8, }, @@ -1554,7 +1654,7 @@ function barFunction(bar: Array>) { errors: [ { messageId: 'errorStringGeneric', - data: { type: 'T' }, + data: { className: 'Array', readonlyPrefix: '', type: 'T' }, line: 2, column: 27, }, @@ -1567,7 +1667,7 @@ function barFunction(bar: Array>) { errors: [ { messageId: 'errorStringGeneric', - data: { type: 'T' }, + data: { className: 'Array', readonlyPrefix: '', type: 'T' }, line: 1, column: 13, }, @@ -1580,7 +1680,7 @@ function barFunction(bar: Array>) { errors: [ { messageId: 'errorStringGeneric', - data: { type: 'T' }, + data: { className: 'Array', readonlyPrefix: '', type: 'T' }, line: 1, column: 17, }, @@ -1593,7 +1693,7 @@ function barFunction(bar: Array>) { errors: [ { messageId: 'errorStringGeneric', - data: { type: 'T' }, + data: { className: 'Array', readonlyPrefix: '', type: 'T' }, line: 1, column: 24, }, @@ -1614,7 +1714,7 @@ interface FooInterface { errors: [ { messageId: 'errorStringGeneric', - data: { type: 'string' }, + data: { className: 'Array', readonlyPrefix: '', type: 'string' }, line: 3, column: 18, }, @@ -1628,7 +1728,7 @@ interface FooInterface { errors: [ { messageId: 'errorStringArray', - data: { type: 'T' }, + data: { className: 'Array', readonlyPrefix: '', type: 'T' }, line: 1, column: 28, }, @@ -1642,7 +1742,7 @@ interface FooInterface { errors: [ { messageId: 'errorStringGeneric', - data: { type: 'T' }, + data: { className: 'Array', readonlyPrefix: '', type: 'T' }, line: 1, column: 28, }, @@ -1655,7 +1755,11 @@ interface FooInterface { errors: [ { messageId: 'errorStringArray', - data: { type: 'object' }, + data: { + className: 'ReadonlyArray', + readonlyPrefix: 'readonly ', + type: 'object', + }, line: 1, column: 12, }, diff --git a/packages/eslint-plugin/tests/rules/ban-types.test.ts b/packages/eslint-plugin/tests/rules/ban-types.test.ts index 9dbd5344be50..64e6bcd33086 100644 --- a/packages/eslint-plugin/tests/rules/ban-types.test.ts +++ b/packages/eslint-plugin/tests/rules/ban-types.test.ts @@ -394,10 +394,13 @@ let b: Foo; code: noFormat` let foo: {} = {}; let bar: { } = {}; +let baz: { +} = {}; `, output: ` let foo: object = {}; let bar: object = {}; +let baz: object = {}; `, options: [ { @@ -428,6 +431,15 @@ let bar: object = {}; line: 3, column: 10, }, + { + messageId: 'bannedTypeMessage', + data: { + name: '{}', + customMessage: ' Use object instead.', + }, + line: 4, + column: 10, + }, ], }, { diff --git a/packages/eslint-plugin/tests/rules/member-ordering-alphabetically-case-insensitive-order.test.ts b/packages/eslint-plugin/tests/rules/member-ordering-alphabetically-case-insensitive-order.test.ts new file mode 100644 index 000000000000..db62c3151010 --- /dev/null +++ b/packages/eslint-plugin/tests/rules/member-ordering-alphabetically-case-insensitive-order.test.ts @@ -0,0 +1,660 @@ +import rule, { + defaultOrder, + MessageIds, + Options, +} from '../../src/rules/member-ordering'; +import { RuleTester } from '../RuleTester'; +import { TSESLint } from '@typescript-eslint/experimental-utils'; + +const ruleTester = new RuleTester({ + parser: '@typescript-eslint/parser', +}); + +const sortedCiWithoutGrouping: TSESLint.RunTests = { + valid: [ + // default option + interface + lower/upper case + { + code: ` +interface Foo { + a : b; + B : b; +} + `, + options: [ + { + default: { + memberTypes: 'never', + order: 'alphabetically-case-insensitive', + }, + }, + ], + }, + + // default option + type literal + lower/upper case + { + code: ` +type Foo = { + a : b; + B : b; +} + `, + options: [ + { + default: { + memberTypes: 'never', + order: 'alphabetically-case-insensitive', + }, + }, + ], + }, + + // default option + class + lower/upper case + { + code: ` +class Foo { + public static a : string; + public static B : string; +} + `, + options: [ + { + default: { + memberTypes: 'never', + order: 'alphabetically-case-insensitive', + }, + }, + ], + }, + + // default option + class expression + lower/upper case + { + code: ` +const foo = class Foo { + public static a : string; + public static B : string; +} + `, + options: [ + { + default: { + memberTypes: 'never', + order: 'alphabetically-case-insensitive', + }, + }, + ], + }, + + // default option + class + decorators + { + code: ` +class Foo { + public static a : string; + @Dec() static B : string; + public static c : string; +} + `, + options: [ + { + default: { + memberTypes: 'never', + order: 'alphabetically-case-insensitive', + }, + }, + ], + }, + ], + invalid: [ + // default option + interface + wrong order (multiple) + { + code: ` +interface Foo { + c : string; + B : string; + a : string; +} + `, + options: [ + { + default: { + memberTypes: 'never', + order: 'alphabetically-case-insensitive', + }, + }, + ], + errors: [ + { + messageId: 'incorrectOrder', + data: { + member: 'B', + beforeMember: 'c', + }, + }, + { + messageId: 'incorrectOrder', + data: { + member: 'a', + beforeMember: 'B', + }, + }, + ], + }, + + // default option + interface + lower/upper case wrong order + { + code: ` +interface Foo { + B : b; + a : b; +} + `, + options: [ + { + default: { + memberTypes: 'never', + order: 'alphabetically-case-insensitive', + }, + }, + ], + errors: [ + { + messageId: 'incorrectOrder', + data: { + member: 'a', + beforeMember: 'B', + }, + }, + ], + }, + + // default option + type literal + lower/upper case wrong order + { + code: ` +type Foo = { + B : b; + a : b; +} + `, + options: [ + { + default: { + memberTypes: 'never', + order: 'alphabetically-case-insensitive', + }, + }, + ], + errors: [ + { + messageId: 'incorrectOrder', + data: { + member: 'a', + beforeMember: 'B', + }, + }, + ], + }, + + // default option + class + lower/upper case wrong order + { + code: ` +class Foo { + public static B : string; + public static a : string; +} + `, + options: [ + { + default: { + memberTypes: 'never', + order: 'alphabetically-case-insensitive', + }, + }, + ], + errors: [ + { + messageId: 'incorrectOrder', + data: { + member: 'a', + beforeMember: 'B', + }, + }, + ], + }, + + // default option + class expression + lower/upper case wrong order + { + code: ` +const foo = class Foo { + public static B : string; + public static a : string; +} + `, + options: [ + { + default: { + memberTypes: 'never', + order: 'alphabetically-case-insensitive', + }, + }, + ], + errors: [ + { + messageId: 'incorrectOrder', + data: { + member: 'a', + beforeMember: 'B', + }, + }, + ], + }, + ], +}; + +const sortedCiWithGrouping: TSESLint.RunTests = { + valid: [ + // default option + interface + default order + alphabetically + { + code: ` +interface Foo { + [a: string] : number; + + () : Baz; + + a : x; + B : x; + c : x; + + new () : Bar; + + a() : void; + B() : void; + c() : void; +} + `, + options: [ + { + default: { + memberTypes: defaultOrder, + order: 'alphabetically-case-insensitive', + }, + }, + ], + }, + + // default option + interface + custom order + alphabetically + { + code: ` +interface Foo { + new () : Bar; + + a() : void; + B() : void; + c() : void; + + a : x; + B : x; + c : x; + + [a: string] : number; + () : Baz; +} + `, + options: [ + { + default: { + memberTypes: ['constructor', 'method', 'field'], + order: 'alphabetically-case-insensitive', + }, + }, + ], + }, + + // default option + type literal + default order + alphabetically + { + code: ` +type Foo = { + [a: string] : number; + + () : Baz; + + a : x; + B : x; + c : x; + + new () : Bar; + + a() : void; + B() : void; + c() : void; +} + `, + options: [ + { + default: { + memberTypes: defaultOrder, + order: 'alphabetically-case-insensitive', + }, + }, + ], + }, + + // default option + type literal + custom order + alphabetically + { + code: ` +type Foo = { + [a: string] : number; + + new () : Bar; + + a() : void; + B() : void; + c() : void; + + a : x; + B : x; + c : x; + + () : Baz; +} + `, + options: [ + { + default: { + memberTypes: ['constructor', 'method', 'field'], + order: 'alphabetically-case-insensitive', + }, + }, + ], + }, + + // default option + class + default order + alphabetically + { + code: ` +class Foo { + public static a: string; + protected static b: string = ""; + private static c: string = ""; + + public d: string = ""; + protected E: string = ""; + private f: string = ""; + + constructor() {} +} + `, + options: [ + { + default: { + memberTypes: defaultOrder, + order: 'alphabetically-case-insensitive', + }, + }, + ], + }, + // default option + class + decorators + default order + alphabetically + { + code: ` +class Foo { + public static a: string; + protected static b: string = ""; + private static c: string = ""; + + @Dec() public d: string; + @Dec() protected E: string; + @Dec() private f: string; + + public g: string = ""; + protected h: string = ""; + private i: string = ""; + + constructor() {} +} + `, + options: [ + { + default: { + memberTypes: defaultOrder, + order: 'alphabetically-case-insensitive', + }, + }, + ], + }, + + // default option + class + custom order + alphabetically + { + code: ` +class Foo { + constructor() {} + + public d: string = ""; + protected E: string = ""; + private f: string = ""; + + public static a: string; + protected static b: string = ""; + private static c: string = ""; +} + `, + options: [ + { + default: { + memberTypes: ['constructor', 'instance-field', 'static-field'], + order: 'alphabetically-case-insensitive', + }, + }, + ], + }, + + // default option + class expression + default order + alphabetically + { + code: ` +const foo = class Foo { + public static a: string; + protected static b: string = ""; + private static c: string = ""; + + public d: string = ""; + protected E: string = ""; + private f: string = ""; + + constructor() {} +} + `, + options: [ + { + default: { + memberTypes: defaultOrder, + order: 'alphabetically-case-insensitive', + }, + }, + ], + }, + + // default option + class expression + custom order + alphabetically + { + code: ` +const foo = class Foo { + constructor() {} + + public d: string = ""; + protected E: string = ""; + private f: string = ""; + + public static a: string; + protected static b: string = ""; + private static c: string = ""; +} + `, + options: [ + { + default: { + memberTypes: ['constructor', 'instance-field', 'static-field'], + order: 'alphabetically-case-insensitive', + }, + }, + ], + }, + ], + invalid: [ + // default option + interface + wrong order within group and wrong group order + alphabetically + { + code: ` +interface Foo { + [a: string] : number; + + a : x; + B : x; + c : x; + + c() : void; + B() : void; + a() : void; + + () : Baz; + + new () : Bar; +} + `, + options: [ + { + default: { + memberTypes: defaultOrder, + order: 'alphabetically-case-insensitive', + }, + }, + ], + errors: [ + { + messageId: 'incorrectGroupOrder', + data: { + name: 'call', + rank: 'field', + }, + }, + { + messageId: 'incorrectGroupOrder', + data: { + name: 'new', + rank: 'method', + }, + }, + ], + }, + + // default option + type literal + wrong order within group and wrong group order + alphabetically + { + code: ` +type Foo = { + [a: string] : number; + + a : x; + B : x; + c : x; + + c() : void; + B() : void; + a() : void; + + () : Baz; + + new () : Bar; +} + `, + options: [ + { + default: { + memberTypes: defaultOrder, + order: 'alphabetically-case-insensitive', + }, + }, + ], + errors: [ + { + messageId: 'incorrectGroupOrder', + data: { + name: 'call', + rank: 'field', + }, + }, + { + messageId: 'incorrectGroupOrder', + data: { + name: 'new', + rank: 'method', + }, + }, + ], + }, + + // default option + class + wrong order within group and wrong group order + alphabetically + { + code: ` +class Foo { + public static c: string = ""; + public static B: string = ""; + public static a: string; + + constructor() {} + + public d: string = ""; +} + `, + options: [ + { + default: { + memberTypes: defaultOrder, + order: 'alphabetically-case-insensitive', + }, + }, + ], + errors: [ + { + messageId: 'incorrectGroupOrder', + data: { + name: 'd', + rank: 'public constructor', + }, + }, + ], + }, + + // default option + class expression + wrong order within group and wrong group order + alphabetically + { + code: ` +const foo = class Foo { + public static c: string = ""; + public static B: string = ""; + public static a: string; + + constructor() {} + + public d: string = ""; +} + `, + options: [ + { + default: { + memberTypes: defaultOrder, + order: 'alphabetically-case-insensitive', + }, + }, + ], + errors: [ + { + messageId: 'incorrectGroupOrder', + data: { + name: 'd', + rank: 'public constructor', + }, + }, + ], + }, + ], +}; + +ruleTester.run('member-ordering-alphabetically-case-insensitive-order', rule, { + valid: [...sortedCiWithoutGrouping.valid, ...sortedCiWithGrouping.valid], + invalid: [ + ...sortedCiWithoutGrouping.invalid, + ...sortedCiWithGrouping.invalid, + ], +}); diff --git a/packages/eslint-plugin/tests/rules/member-ordering-alphabetically-order.test.ts b/packages/eslint-plugin/tests/rules/member-ordering-alphabetically-order.test.ts new file mode 100644 index 000000000000..1386aee7909e --- /dev/null +++ b/packages/eslint-plugin/tests/rules/member-ordering-alphabetically-order.test.ts @@ -0,0 +1,2691 @@ +import rule, { + defaultOrder, + MessageIds, + Options, +} from '../../src/rules/member-ordering'; +import { RuleTester } from '../RuleTester'; +import { TSESLint } from '@typescript-eslint/experimental-utils'; + +const ruleTester = new RuleTester({ + parser: '@typescript-eslint/parser', +}); +const sortedWithoutGroupingDefaultOption: TSESLint.RunTests< + MessageIds, + Options +> = { + valid: [ + // default option + interface + multiple types + { + code: ` +interface Foo { + (): Foo; + a(): Foo; + b(): Foo; +} + `, + options: [ + { default: { memberTypes: defaultOrder, order: 'alphabetically' } }, + ], + }, + + // default option + interface + lower/upper case + { + code: ` +interface Foo { + A : b; + a : b; +} + `, + options: [{ default: { memberTypes: 'never', order: 'alphabetically' } }], + }, + + // default option + interface + numbers + { + code: ` +interface Foo { + a1 : b; + aa : b; +} + `, + options: [{ default: { memberTypes: 'never', order: 'alphabetically' } }], + }, + + // default option + interface + literal properties + { + code: ` +interface Foo { + a : Foo; + 'b.c' : Foo; + "b.d" : Foo; +} + `, + options: [{ default: { order: 'alphabetically' } }], + }, + + // default option + type literal + multiple types + { + code: ` +type Foo = { + a : b; + [a: string] : number; + b() : void; + () : Baz; + new () : Bar; +} + `, + options: [{ default: { memberTypes: 'never', order: 'alphabetically' } }], + }, + + // default option + type literal + lower/upper case + { + code: ` +type Foo = { + A : b; + a : b; +} + `, + options: [{ default: { memberTypes: 'never', order: 'alphabetically' } }], + }, + + // default option + type literal + numbers + { + code: ` +type Foo = { + a1 : b; + aa : b; +} + `, + options: [{ default: { memberTypes: 'never', order: 'alphabetically' } }], + }, + + // default option + type + literal properties + { + code: ` +type Foo = { + a : Foo; + 'b.c' : Foo; + "b.d" : Foo; +} + `, + options: [{ default: { order: 'alphabetically' } }], + }, + + // default option + class + multiple types + { + code: ` +class Foo { + public static a : string; + protected static b : string = ""; + private static c : string = ""; + constructor() {} + @Dec() d: string; + public e : string = ""; + @Dec() f : string = ""; + protected g : string = ""; + private h : string = ""; +} + `, + options: [{ default: { memberTypes: 'never', order: 'alphabetically' } }], + }, + + // default option + class + lower/upper case + { + code: ` +class Foo { + public static A : string; + public static a : string; +} + `, + options: [{ default: { memberTypes: 'never', order: 'alphabetically' } }], + }, + + // default option + class + numbers + { + code: ` +class Foo { + public static a1 : string; + public static aa : string; +} + `, + options: [{ default: { memberTypes: 'never', order: 'alphabetically' } }], + }, + + // default option + class expression + multiple types + { + code: ` +const foo = class Foo { + public static a : string; + protected static b : string = ""; + private static c : string = ""; + constructor() {} + public d : string = ""; + protected e : string = ""; + private f : string = ""; +} + `, + options: [{ default: { memberTypes: 'never', order: 'alphabetically' } }], + }, + + // default option + class expression + lower/upper case + { + code: ` +const foo = class Foo { + public static A : string; + public static a : string; +} + `, + options: [{ default: { memberTypes: 'never', order: 'alphabetically' } }], + }, + + // default option + class expression + numbers + { + code: ` +const foo = class Foo { + public static a1 : string; + public static aa : string; +} + `, + options: [{ default: { memberTypes: 'never', order: 'alphabetically' } }], + }, + + // default option + class + decorators + { + code: ` +class Foo { + public static a : string; + @Dec() static b : string; + public static c : string; +} + `, + options: [{ default: { memberTypes: 'never', order: 'alphabetically' } }], + }, + + // default option + private identifiers + { + code: ` +class Foo { + #a = 1; + #b = 2; + #c = 3; +} + `, + options: [ + { default: { memberTypes: defaultOrder, order: 'alphabetically' } }, + ], + }, + ], + invalid: [ + // default option + interface + wrong order + { + code: ` +interface Foo { + b() : void; + a : b; + [a: string] : number; + new () : Bar; + () : Baz; +} + `, + options: [{ default: { memberTypes: 'never', order: 'alphabetically' } }], + errors: [ + { + messageId: 'incorrectOrder', + data: { + member: 'a', + beforeMember: 'b', + }, + }, + { + messageId: 'incorrectOrder', + data: { + member: 'call', + beforeMember: 'new', + }, + }, + ], + }, + + // default option + interface + literal properties + { + code: ` +interface Foo { + "b.d" : Foo; + 'b.c' : Foo; + a : Foo; +} + `, + options: [{ default: { order: 'alphabetically' } }], + errors: [ + { + messageId: 'incorrectOrder', + data: { + member: 'b.c', + beforeMember: 'b.d', + }, + }, + { + messageId: 'incorrectOrder', + data: { + member: 'a', + beforeMember: 'b.c', + }, + }, + ], + }, + + // default option + interface + wrong order (multiple) + { + code: ` +interface Foo { + c : string; + b : string; + a : string; +} + `, + options: [{ default: { memberTypes: 'never', order: 'alphabetically' } }], + errors: [ + { + messageId: 'incorrectOrder', + data: { + member: 'b', + beforeMember: 'c', + }, + }, + { + messageId: 'incorrectOrder', + data: { + member: 'a', + beforeMember: 'b', + }, + }, + ], + }, + + // default option + type literal + wrong order + { + code: ` +type Foo = { + b() : void; + a : b; + [a: string] : number; + new () : Bar; + () : Baz; +} + `, + options: [{ default: { memberTypes: 'never', order: 'alphabetically' } }], + errors: [ + { + messageId: 'incorrectOrder', + data: { + member: 'a', + beforeMember: 'b', + }, + }, + { + messageId: 'incorrectOrder', + data: { + member: 'call', + beforeMember: 'new', + }, + }, + ], + }, + + // default option + type + literal properties + { + code: ` +type Foo = { + "b.d" : Foo; + 'b.c' : Foo; + a : Foo; +} + `, + options: [{ default: { order: 'alphabetically' } }], + errors: [ + { + messageId: 'incorrectOrder', + data: { + member: 'b.c', + beforeMember: 'b.d', + }, + }, + { + messageId: 'incorrectOrder', + data: { + member: 'a', + beforeMember: 'b.c', + }, + }, + ], + }, + + // default option + type literal + wrong order (multiple) + { + code: ` +type Foo = { + c : string; + b : string; + a : string; +} + `, + options: [{ default: { memberTypes: 'never', order: 'alphabetically' } }], + errors: [ + { + messageId: 'incorrectOrder', + data: { + member: 'b', + beforeMember: 'c', + }, + }, + { + messageId: 'incorrectOrder', + data: { + member: 'a', + beforeMember: 'b', + }, + }, + ], + }, + + // default option + class + wrong order + { + code: ` +class Foo { + protected static b : string = ""; + public static a : string; + private static c : string = ""; + constructor() {} + public d : string = ""; + protected e : string = ""; + private f : string = ""; +} + `, + options: [{ default: { memberTypes: 'never', order: 'alphabetically' } }], + errors: [ + { + messageId: 'incorrectOrder', + data: { + member: 'a', + beforeMember: 'b', + }, + }, + ], + }, + + // default option + class + wrong order (multiple) + { + code: ` +class Foo { + public static c: string; + public static b: string; + public static a: string; +} + `, + options: [{ default: { memberTypes: 'never', order: 'alphabetically' } }], + errors: [ + { + messageId: 'incorrectOrder', + data: { + member: 'b', + beforeMember: 'c', + }, + }, + { + messageId: 'incorrectOrder', + data: { + member: 'a', + beforeMember: 'b', + }, + }, + ], + }, + + // default option + class expression + wrong order + { + code: ` +const foo = class Foo { + protected static b : string = ""; + public static a : string; + private static c : string = ""; + constructor() {} + public d : string = ""; + protected e : string = ""; + private f : string = ""; +} + `, + options: [{ default: { memberTypes: 'never', order: 'alphabetically' } }], + errors: [ + { + messageId: 'incorrectOrder', + data: { + member: 'a', + beforeMember: 'b', + }, + }, + ], + }, + + // default option + class expression + wrong order (multiple) + { + code: ` +const foo = class Foo { + public static c: string; + public static b: string; + public static a: string; +} + `, + options: [{ default: { memberTypes: 'never', order: 'alphabetically' } }], + errors: [ + { + messageId: 'incorrectOrder', + data: { + member: 'b', + beforeMember: 'c', + }, + }, + { + messageId: 'incorrectOrder', + data: { + member: 'a', + beforeMember: 'b', + }, + }, + ], + }, + ], +}; + +const sortedWithoutGroupingClassesOption: TSESLint.RunTests< + MessageIds, + Options +> = { + valid: [ + // classes option + interface + multiple types --> Only member group order is checked (default config) + { + code: ` +interface Foo { + [a: string] : number; + () : Baz; + c : b; + new () : Bar; + b() : void; +} + `, + options: [{ classes: { memberTypes: 'never', order: 'alphabetically' } }], + }, + + // classes option + interface + lower/upper case --> Only member group order is checked (default config) + { + code: ` +interface Foo { + a : b; + A : b; +} + `, + options: [{ classes: { memberTypes: 'never', order: 'alphabetically' } }], + }, + + // classes option + interface + numbers --> Only member group order is checked (default config) + { + code: ` +interface Foo { + aa : b; + a1 : b; +} + `, + options: [{ classes: { memberTypes: 'never', order: 'alphabetically' } }], + }, + + // classes option + type literal + multiple types --> Only member group order is checked (default config) + { + code: ` +type Foo = { + [a: string] : number; + () : Baz; + c : b; + new () : Bar; + b() : void; +} + `, + options: [{ classes: { memberTypes: 'never', order: 'alphabetically' } }], + }, + + // classes option + type literal + lower/upper case --> Only member group order is checked (default config) + { + code: ` +type Foo = { + a : b; + A : b; +} + `, + options: [{ classes: { memberTypes: 'never', order: 'alphabetically' } }], + }, + + // classes option + type literal + numbers --> Only member group order is checked (default config) + { + code: ` +type Foo = { + aa : b; + a1 : b; +} + `, + options: [{ classes: { memberTypes: 'never', order: 'alphabetically' } }], + }, + + // classes option + class + multiple types + { + code: ` +class Foo { + public static a : string; + protected static b : string = ""; + @Dec() private static c : string = ""; + constructor() {} + public d : string = ""; + protected e : string = ""; + @Dec() + private f : string = ""; +} + `, + options: [{ classes: { memberTypes: 'never', order: 'alphabetically' } }], + }, + + // classes option + class + lower/upper case + { + code: ` +class Foo { + public static A : string; + public static a : string; +} + `, + options: [{ classes: { memberTypes: 'never', order: 'alphabetically' } }], + }, + + // classes option + class + numbers + { + code: ` +class Foo { + public static a1 : string; + public static aa : string; +} + `, + options: [{ classes: { memberTypes: 'never', order: 'alphabetically' } }], + }, + + // classes option + class expression + multiple types --> Only member group order is checked (default config) + { + code: ` +const foo = class Foo { + public static a : string; + protected static b : string = ""; + private static c : string = ""; + public d : string = ""; + protected e : string = ""; + private f : string = ""; + constructor() {} +} + `, + options: [{ classes: { memberTypes: 'never', order: 'alphabetically' } }], + }, + + // classes option + class expression + lower/upper case --> Only member group order is checked (default config) + { + code: ` +const foo = class Foo { + public static a : string; + public static A : string; +} + `, + options: [{ classes: { memberTypes: 'never', order: 'alphabetically' } }], + }, + + // classes option + class expression + numbers --> Only member group order is checked (default config) + { + code: ` +const foo = class Foo { + public static aa : string; + public static a1 : string; +} + `, + options: [{ classes: { memberTypes: 'never', order: 'alphabetically' } }], + }, + ], + invalid: [ + // classes option + class + wrong order + { + code: ` +class Foo { + protected static b : string = ""; + public static a : string; + private static c : string = ""; + constructor() {} + public d : string = ""; + protected e : string = ""; + private f : string = ""; +} + `, + options: [{ classes: { memberTypes: 'never', order: 'alphabetically' } }], + errors: [ + { + messageId: 'incorrectOrder', + data: { + member: 'a', + beforeMember: 'b', + }, + }, + ], + }, + + // classes option + class + wrong order (multiple) + { + code: ` +class Foo { + public static c: string; + public static b: string; + public static a: string; +} + `, + options: [{ classes: { memberTypes: 'never', order: 'alphabetically' } }], + errors: [ + { + messageId: 'incorrectOrder', + data: { + member: 'b', + beforeMember: 'c', + }, + }, + { + messageId: 'incorrectOrder', + data: { + member: 'a', + beforeMember: 'b', + }, + }, + ], + }, + ], +}; + +const sortedWithoutGroupingClassExpressionsOption: TSESLint.RunTests< + MessageIds, + Options +> = { + valid: [ + // classExpressions option + interface + multiple types --> Only member group order is checked (default config) + { + code: ` +interface Foo { + [a: string] : number; + () : Baz; + c : b; + new () : Bar; + b() : void; +} + `, + options: [ + { classExpressions: { memberTypes: 'never', order: 'alphabetically' } }, + ], + }, + + // classExpressions option + interface + lower/upper case --> Only member group order is checked (default config) + { + code: ` +interface Foo { + a : b; + A : b; +} + `, + options: [ + { classExpressions: { memberTypes: 'never', order: 'alphabetically' } }, + ], + }, + + // classExpressions option + interface + numbers --> Only member group order is checked (default config) + { + code: ` +interface Foo { + aa : b; + a1 : b; +} + `, + options: [ + { classExpressions: { memberTypes: 'never', order: 'alphabetically' } }, + ], + }, + + // classExpressions option + type literal + multiple types --> Only member group order is checked (default config) + { + code: ` +type Foo = { + [a: string] : number; + () : Baz; + c : b; + new () : Bar; + b() : void; +} + `, + options: [ + { classExpressions: { memberTypes: 'never', order: 'alphabetically' } }, + ], + }, + + // classExpressions option + type literal + lower/upper case --> Only member group order is checked (default config) + { + code: ` +type Foo = { + a : b; + A : b; +} + `, + options: [ + { classExpressions: { memberTypes: 'never', order: 'alphabetically' } }, + ], + }, + + // classExpressions option + type literal + numbers --> Only member group order is checked (default config) + { + code: ` +type Foo = { + aa : b; + a1 : b; +} + `, + options: [ + { classExpressions: { memberTypes: 'never', order: 'alphabetically' } }, + ], + }, + + // classExpressions option + class + multiple types --> Only member group order is checked (default config) + { + code: ` +class Foo { + public static a : string; + protected static b : string = ""; + private static c : string = ""; + public d : string = ""; + protected e : string = ""; + private f : string = ""; + constructor() {} +} + `, + options: [ + { classExpressions: { memberTypes: 'never', order: 'alphabetically' } }, + ], + }, + + // classExpressions option + class + lower/upper case --> Only member group order is checked (default config) + { + code: ` +class Foo { + public static a : string; + public static A : string; +} + `, + options: [ + { classExpressions: { memberTypes: 'never', order: 'alphabetically' } }, + ], + }, + + // classExpressions option + class + numbers --> Only member group order is checked (default config) + { + code: ` +class Foo { + public static aa : string; + public static a1 : string; +} + `, + options: [ + { classExpressions: { memberTypes: 'never', order: 'alphabetically' } }, + ], + }, + + // classExpressions option + class expression + multiple types + { + code: ` +const foo = class Foo { + public static a : string; + protected static b : string = ""; + private static c : string = ""; + constructor() {} + public d : string = ""; + protected e : string = ""; + private f : string = ""; +} + `, + options: [ + { classExpressions: { memberTypes: 'never', order: 'alphabetically' } }, + ], + }, + + // classExpressions option + class expression + lower/upper case + { + code: ` +const foo = class Foo { + public static A : string; + public static a : string; +} + `, + options: [ + { classExpressions: { memberTypes: 'never', order: 'alphabetically' } }, + ], + }, + + // classExpressions option + class expression + numbers + { + code: ` +const foo = class Foo { + public static a1 : string; + public static aa : string; +} + `, + options: [ + { classExpressions: { memberTypes: 'never', order: 'alphabetically' } }, + ], + }, + ], + invalid: [ + // classExpressions option + class expression + wrong order + { + code: ` +const foo = class Foo { + protected static b : string = ""; + public static a : string; + private static c : string = ""; + constructor() {} + public d : string = ""; + protected e : string = ""; + private f : string = ""; +} + `, + options: [ + { classExpressions: { memberTypes: 'never', order: 'alphabetically' } }, + ], + errors: [ + { + messageId: 'incorrectOrder', + data: { + member: 'a', + beforeMember: 'b', + }, + }, + ], + }, + + // classExpressions option + class expression + wrong order (multiple) + { + code: ` +const foo = class Foo { + public static c: string; + public static b: string; + public static a: string; +} + `, + options: [ + { classExpressions: { memberTypes: 'never', order: 'alphabetically' } }, + ], + errors: [ + { + messageId: 'incorrectOrder', + data: { + member: 'b', + beforeMember: 'c', + }, + }, + { + messageId: 'incorrectOrder', + data: { + member: 'a', + beforeMember: 'b', + }, + }, + ], + }, + ], +}; + +const sortedWithoutGroupingInterfacesOption: TSESLint.RunTests< + MessageIds, + Options +> = { + valid: [ + // interfaces option + interface + multiple types + { + code: ` +interface Foo { + [a: string] : number; + a : b; + b() : void; + () : Baz; + new () : Bar; +} + `, + options: [ + { interfaces: { memberTypes: 'never', order: 'alphabetically' } }, + ], + }, + + // interfaces option + interface + lower/upper case + { + code: ` +interface Foo { + A : b; + a : b; +} + `, + options: [ + { interfaces: { memberTypes: 'never', order: 'alphabetically' } }, + ], + }, + + // interfaces option + interface + numbers + { + code: ` +interface Foo { + a1 : b; + aa : b; +} + `, + options: [ + { interfaces: { memberTypes: 'never', order: 'alphabetically' } }, + ], + }, + + // interfaces option + type literal + multiple types --> Only member group order is checked (default config) + { + code: ` +type Foo = { + [a: string] : number; + () : Baz; + c : b; + new () : Bar; + b() : void; +} + `, + options: [ + { interfaces: { memberTypes: 'never', order: 'alphabetically' } }, + ], + }, + + // interfaces option + type literal + lower/upper case --> Only member group order is checked (default config) + { + code: ` +type Foo = { + a : b; + A : b; +} + `, + options: [ + { interfaces: { memberTypes: 'never', order: 'alphabetically' } }, + ], + }, + + // interfaces option + type literal + numbers --> Only member group order is checked (default config) + { + code: ` +type Foo = { + aa : b; + a1 : b; +} + `, + options: [ + { interfaces: { memberTypes: 'never', order: 'alphabetically' } }, + ], + }, + + // interfaces option + class + multiple types --> Only member group order is checked (default config) + { + code: ` +class Foo { + public static a : string; + protected static b : string = ""; + private static c : string = ""; + public d : string = ""; + protected e : string = ""; + private f : string = ""; + constructor() {} +} + `, + options: [ + { interfaces: { memberTypes: 'never', order: 'alphabetically' } }, + ], + }, + + // interfaces option + class + lower/upper case --> Only member group order is checked (default config) + { + code: ` +class Foo { + public static a : string; + public static A : string; +} + `, + options: [ + { interfaces: { memberTypes: 'never', order: 'alphabetically' } }, + ], + }, + + // interfaces option + class + numbers --> Only member group order is checked (default config) + { + code: ` +class Foo { + public static aa : string; + public static a1 : string; +} + `, + options: [ + { interfaces: { memberTypes: 'never', order: 'alphabetically' } }, + ], + }, + + // interfaces option + class expression + multiple types --> Only member group order is checked (default config) + { + code: ` +const foo = class Foo { + public static a : string; + protected static b : string = ""; + private static c : string = ""; + public d : string = ""; + protected e : string = ""; + private f : string = ""; + constructor() {} +} + `, + options: [ + { interfaces: { memberTypes: 'never', order: 'alphabetically' } }, + ], + }, + + // interfaces option + class expression + lower/upper case --> Only member group order is checked (default config) + { + code: ` +const foo = class Foo { + public static a : string; + public static A : string; +} + `, + options: [ + { interfaces: { memberTypes: 'never', order: 'alphabetically' } }, + ], + }, + + // interfaces option + class expression + numbers --> Only member group order is checked (default config) + { + code: ` +const foo = class Foo { + public static aa : string; + public static a1 : string; +} + `, + options: [ + { interfaces: { memberTypes: 'never', order: 'alphabetically' } }, + ], + }, + ], + invalid: [ + // interfaces option + interface + wrong order + { + code: ` +interface Foo { + b() : void; + a : b; + [a: string] : number; + new () : Bar; + () : Baz; +} + `, + options: [ + { interfaces: { memberTypes: 'never', order: 'alphabetically' } }, + ], + errors: [ + { + messageId: 'incorrectOrder', + data: { + member: 'a', + beforeMember: 'b', + }, + }, + { + messageId: 'incorrectOrder', + data: { + member: 'call', + beforeMember: 'new', + }, + }, + ], + }, + + // interfaces option + interface + wrong order (multiple) + { + code: ` +interface Foo { + c : string; + b : string; + a : string; +} + `, + options: [ + { interfaces: { memberTypes: 'never', order: 'alphabetically' } }, + ], + errors: [ + { + messageId: 'incorrectOrder', + data: { + member: 'b', + beforeMember: 'c', + }, + }, + { + messageId: 'incorrectOrder', + data: { + member: 'a', + beforeMember: 'b', + }, + }, + ], + }, + ], +}; + +const sortedWithoutGroupingTypeLiteralsOption: TSESLint.RunTests< + MessageIds, + Options +> = { + valid: [ + // typeLiterals option + interface + multiple types --> Only member group order is checked (default config) + { + code: ` +interface Foo { + [a: string] : number; + () : Baz; + c : b; + new () : Bar; + b() : void; +} + `, + options: [ + { typeLiterals: { memberTypes: 'never', order: 'alphabetically' } }, + ], + }, + + // typeLiterals option + interface + lower/upper case --> Only member group order is checked (default config) + { + code: ` +interface Foo { + a : b; + A : b; +} + `, + options: [ + { typeLiterals: { memberTypes: 'never', order: 'alphabetically' } }, + ], + }, + + // typeLiterals option + interface + numbers --> Only member group order is checked (default config) + { + code: ` +interface Foo { + aa : b; + a1 : b; +} + `, + options: [ + { typeLiterals: { memberTypes: 'never', order: 'alphabetically' } }, + ], + }, + + // typeLiterals option + type literal + multiple types + { + code: ` +type Foo = { + [a: string] : number; + a : b; + b() : void; + () : Baz; + new () : Bar; +} + `, + options: [ + { typeLiterals: { memberTypes: 'never', order: 'alphabetically' } }, + ], + }, + + // typeLiterals option + type literal + lower/upper case + { + code: ` +type Foo = { + A : b; + a : b; +} + `, + options: [ + { typeLiterals: { memberTypes: 'never', order: 'alphabetically' } }, + ], + }, + + // typeLiterals option + type literal + numbers + { + code: ` +type Foo = { + a1 : b; + aa : b; +} + `, + options: [ + { typeLiterals: { memberTypes: 'never', order: 'alphabetically' } }, + ], + }, + + // typeLiterals option + class + multiple types --> Only member group order is checked (default config) + { + code: ` +class Foo { + public static a : string; + protected static b : string = ""; + private static c : string = ""; + public d : string = ""; + protected e : string = ""; + private f : string = ""; + constructor() {} +} + `, + options: [ + { typeLiterals: { memberTypes: 'never', order: 'alphabetically' } }, + ], + }, + + // typeLiterals option + class + lower/upper case --> Only member group order is checked (default config) + { + code: ` +class Foo { + public static a : string; + public static A : string; +} + `, + options: [ + { typeLiterals: { memberTypes: 'never', order: 'alphabetically' } }, + ], + }, + + // typeLiterals option + class + numbers --> Only member group order is checked (default config) + { + code: ` +class Foo { + public static aa : string; + public static a1 : string; +} + `, + options: [ + { typeLiterals: { memberTypes: 'never', order: 'alphabetically' } }, + ], + }, + + // typeLiterals option + class expression + multiple types --> Only member group order is checked (default config) + { + code: ` +const foo = class Foo { + public static a : string; + protected static b : string = ""; + private static c : string = ""; + public d : string = ""; + protected e : string = ""; + private f : string = ""; + constructor() {} +} + `, + options: [ + { typeLiterals: { memberTypes: 'never', order: 'alphabetically' } }, + ], + }, + + // typeLiterals option + class expression + lower/upper case --> Only member group order is checked (default config) + { + code: ` +const foo = class Foo { + public static a : string; + public static A : string; +} + `, + options: [ + { typeLiterals: { memberTypes: 'never', order: 'alphabetically' } }, + ], + }, + + // typeLiterals option + class expression + numbers --> Only member group order is checked (default config) + { + code: ` +const foo = class Foo { + public static aa : string; + public static a1 : string; +} + `, + options: [ + { typeLiterals: { memberTypes: 'never', order: 'alphabetically' } }, + ], + }, + ], + invalid: [ + // typeLiterals option + type literal + wrong order + { + code: ` +type Foo = { + b() : void; + a : b; + [a: string] : number; + new () : Bar; + () : Baz; +} + `, + options: [ + { typeLiterals: { memberTypes: 'never', order: 'alphabetically' } }, + ], + errors: [ + { + messageId: 'incorrectOrder', + data: { + member: 'a', + beforeMember: 'b', + }, + }, + { + messageId: 'incorrectOrder', + data: { + member: 'call', + beforeMember: 'new', + }, + }, + ], + }, + + // typeLiterals option + type literal + wrong order (multiple) + { + code: ` +type Foo = { + c : string; + b : string; + a : string; +} + `, + options: [ + { typeLiterals: { memberTypes: 'never', order: 'alphabetically' } }, + ], + errors: [ + { + messageId: 'incorrectOrder', + data: { + member: 'b', + beforeMember: 'c', + }, + }, + { + messageId: 'incorrectOrder', + data: { + member: 'a', + beforeMember: 'b', + }, + }, + ], + }, + ], +}; + +const sortedWithGroupingDefaultOption: TSESLint.RunTests = + { + valid: [ + // default option + interface + default order + alphabetically + { + code: ` +interface Foo { + [a: string] : number; + + () : Baz; + + a : x; + b : x; + c : x; + + new () : Bar; + + a() : void; + b() : void; + c() : void; +} + `, + options: [ + { default: { memberTypes: defaultOrder, order: 'alphabetically' } }, + ], + }, + + // default option + interface + custom order + alphabetically + { + code: ` +interface Foo { + new () : Bar; + + a() : void; + b() : void; + c() : void; + + a : x; + b : x; + c : x; + + [a: string] : number; + () : Baz; +} + `, + options: [ + { + default: { + memberTypes: ['constructor', 'method', 'field'], + order: 'alphabetically', + }, + }, + ], + }, + + // default option + type literal + default order + alphabetically + { + code: ` +type Foo = { + [a: string] : number; + + () : Baz; + + a : x; + b : x; + c : x; + + new () : Bar; + + a() : void; + b() : void; + c() : void; +} + `, + options: [ + { default: { memberTypes: defaultOrder, order: 'alphabetically' } }, + ], + }, + + // default option + type literal + custom order + alphabetically + { + code: ` +type Foo = { + [a: string] : number; + + new () : Bar; + + a() : void; + b() : void; + c() : void; + + a : x; + b : x; + c : x; + + () : Baz; +} + `, + options: [ + { + default: { + memberTypes: ['constructor', 'method', 'field'], + order: 'alphabetically', + }, + }, + ], + }, + + // default option + class + default order + alphabetically + { + code: ` +class Foo { + public static a: string; + protected static b: string = ""; + private static c: string = ""; + + public d: string = ""; + protected e: string = ""; + private f: string = ""; + + constructor() {} +} + `, + options: [ + { default: { memberTypes: defaultOrder, order: 'alphabetically' } }, + ], + }, + + // default option + class + defaultOrder + alphabetically + { + code: ` +class Foo { + public static a: string; + protected static b: string = ""; + private static c: string = ""; + + public d: string = ""; + protected e: string = ""; + private f: string = ""; + + constructor() {} + + get h() {} + + set g() {} +} + `, + options: [ + { + default: { + memberTypes: defaultOrder, + order: 'alphabetically', + }, + }, + ], + }, + + // default option + class + custom + alphabetically + { + code: ` +class Foo { + get a() {} + + @Bar + get b() {} + + set c() {} + + @Bar + set d() {} +} + `, + options: [ + { + default: { + memberTypes: ['get', 'decorated-get', 'set', 'decorated-set'], + order: 'alphabetically', + }, + }, + ], + }, + + // default option + class + decorators + default order + alphabetically + { + code: ` +class Foo { + public static a: string; + protected static b: string = ""; + private static c: string = ""; + + @Dec() public d: string; + @Dec() protected e: string; + @Dec() private f: string; + + public g: string = ""; + protected h: string = ""; + private i: string = ""; + + constructor() {} +} + `, + options: [ + { default: { memberTypes: defaultOrder, order: 'alphabetically' } }, + ], + }, + + // default option + class + custom order + alphabetically + { + code: ` +class Foo { + constructor() {} + + public d: string = ""; + protected e: string = ""; + private f: string = ""; + + public static a: string; + protected static b: string = ""; + private static c: string = ""; +} + `, + options: [ + { + default: { + memberTypes: ['constructor', 'instance-field', 'static-field'], + order: 'alphabetically', + }, + }, + ], + }, + + // default option + class expression + default order + alphabetically + { + code: ` +const foo = class Foo { + public static a: string; + protected static b: string = ""; + private static c: string = ""; + + public d: string = ""; + protected e: string = ""; + private f: string = ""; + + constructor() {} +} + `, + options: [ + { default: { memberTypes: defaultOrder, order: 'alphabetically' } }, + ], + }, + + // default option + class expression + custom order + alphabetically + { + code: ` +const foo = class Foo { + constructor() {} + + public d: string = ""; + protected e: string = ""; + private f: string = ""; + + public static a: string; + protected static b: string = ""; + private static c: string = ""; +} + `, + options: [ + { + default: { + memberTypes: ['constructor', 'instance-field', 'static-field'], + order: 'alphabetically', + }, + }, + ], + }, + ], + invalid: [ + // default option + class + wrong order within group and wrong group order + alphabetically + { + code: ` +class FooTestGetter { + public static a: string; + protected static b: string = ""; + private static c: string = ""; + + public d: string = ""; + protected e: string = ""; + private f: string = ""; + + get h() {} + + set g() {} + + constructor() {} +} + `, + options: [ + { + default: { + memberTypes: defaultOrder, + order: 'alphabetically', + }, + }, + ], + errors: [ + { + messageId: 'incorrectGroupOrder', + data: { + name: 'constructor', + rank: 'public instance get', + }, + }, + ], + }, + + // default option + class + custom + alphabetically + { + code: ` +class Foo { + @Bar + get a() {} + + get b() {} + + @Bar + set c() {} + + set d() {} +} + `, + options: [ + { + default: { + memberTypes: ['get', 'decorated-get', 'set', 'decorated-set'], + order: 'alphabetically', + }, + }, + ], + errors: [ + { + messageId: 'incorrectGroupOrder', + data: { + name: 'b', + rank: 'decorated get', + }, + }, + { + messageId: 'incorrectGroupOrder', + data: { + name: 'd', + rank: 'decorated set', + }, + }, + ], + }, + + // default option + class + wrong order within group and wrong group order + alphabetically + { + code: ` +class FooTestGetter { + public static a: string; + protected static b: string = ""; + private static c: string = ""; + + public d: string = ""; + protected e: string = ""; + private f: string = ""; + + set g() {} + + constructor() {} + + get h() {} +} + `, + options: [ + { + default: { + memberTypes: defaultOrder, + order: 'alphabetically', + }, + }, + ], + errors: [ + { + messageId: 'incorrectGroupOrder', + data: { + name: 'constructor', + rank: 'public instance set', + }, + }, + { + messageId: 'incorrectGroupOrder', + data: { + name: 'h', + rank: 'public instance set', + }, + }, + ], + }, + + // default option + interface + wrong order within group and wrong group order + alphabetically + { + code: ` +interface Foo { + [a: string] : number; + + a : x; + b : x; + c : x; + + c() : void; + b() : void; + a() : void; + + () : Baz; + + new () : Bar; +} + `, + options: [ + { default: { memberTypes: defaultOrder, order: 'alphabetically' } }, + ], + errors: [ + { + messageId: 'incorrectGroupOrder', + data: { + name: 'call', + rank: 'field', + }, + }, + { + messageId: 'incorrectGroupOrder', + data: { + name: 'new', + rank: 'method', + }, + }, + ], + }, + + // default option + type literal + wrong order within group and wrong group order + alphabetically + { + code: ` +type Foo = { + [a: string] : number; + + a : x; + b : x; + c : x; + + c() : void; + b() : void; + a() : void; + + () : Baz; + + new () : Bar; +} + `, + options: [ + { default: { memberTypes: defaultOrder, order: 'alphabetically' } }, + ], + errors: [ + { + messageId: 'incorrectGroupOrder', + data: { + name: 'call', + rank: 'field', + }, + }, + { + messageId: 'incorrectGroupOrder', + data: { + name: 'new', + rank: 'method', + }, + }, + ], + }, + + // default option + class + wrong order within group and wrong group order + alphabetically + { + code: ` +class Foo { + public static c: string = ""; + public static b: string = ""; + public static a: string; + + constructor() {} + + public d: string = ""; +} + `, + options: [ + { default: { memberTypes: defaultOrder, order: 'alphabetically' } }, + ], + errors: [ + { + messageId: 'incorrectGroupOrder', + data: { + name: 'd', + rank: 'public constructor', + }, + }, + ], + }, + + // default option + class expression + wrong order within group and wrong group order + alphabetically + { + code: ` +const foo = class Foo { + public static c: string = ""; + public static b: string = ""; + public static a: string; + + constructor() {} + + public d: string = ""; +} + `, + options: [ + { default: { memberTypes: defaultOrder, order: 'alphabetically' } }, + ], + errors: [ + { + messageId: 'incorrectGroupOrder', + data: { + name: 'd', + rank: 'public constructor', + }, + }, + ], + }, + // default option + class + decorators + custom order + wrong order within group and wrong group order + alphabetically + { + code: ` +class Foo { + @Dec() a1: string; + @Dec() + a3: string; + @Dec() + a2: string; + + constructor() {} + + b1: string; + b2: string; + + public c(): void; + @Dec() d(): void +} + `, + options: [ + { + default: { + memberTypes: [ + 'decorated-field', + 'field', + 'constructor', + 'decorated-method', + ], + order: 'alphabetically', + }, + }, + ], + errors: [ + { + messageId: 'incorrectGroupOrder', + data: { + name: 'b1', + rank: 'constructor', + }, + }, + { + messageId: 'incorrectGroupOrder', + data: { + name: 'b2', + rank: 'constructor', + }, + }, + ], + }, + ], + }; + +const sortedWithGroupingClassesOption: TSESLint.RunTests = + { + valid: [ + // classes option + interface + alphabetically --> Default order applies + { + code: ` +interface Foo { + [a: string] : number; + + () : Baz; + + c : x; + b : x; + a : x; + + new () : Bar; + + c() : void; + b() : void; + a() : void; +} + `, + options: [{ classes: { order: 'alphabetically' } }], + }, + + // classes option + type literal + alphabetically --> Default order applies + { + code: ` +type Foo = { + [a: string] : number; + + () : Baz; + + c : x; + b : x; + a : x; + + new () : Bar; + + c() : void; + b() : void; + a() : void; +} + `, + options: [{ classes: { order: 'alphabetically' } }], + }, + + // classes option + class + default order + alphabetically + { + code: ` +class Foo { + public static a: string; + protected static b: string = ""; + private static c: string = ""; + + public d: string = ""; + protected e: string = ""; + private f: string = ""; + + constructor() {} +} + `, + options: [ + { classes: { memberTypes: defaultOrder, order: 'alphabetically' } }, + ], + }, + + // classes option + class + custom order + alphabetically + { + code: ` +class Foo { + constructor() {} + + public d: string = ""; + protected e: string = ""; + private f: string = ""; + + public static a: string; + protected static b: string = ""; + private static c: string = ""; +} + `, + options: [ + { + classes: { + memberTypes: ['constructor', 'instance-field', 'static-field'], + order: 'alphabetically', + }, + }, + ], + }, + + // classes option + class expression + alphabetically --> Default order applies + { + code: ` +const foo = class Foo { + public static a: string; + protected static b: string = ""; + private static c: string = ""; + + public d: string = ""; + protected e: string = ""; + private f: string = ""; + + constructor() {} +} + `, + options: [{ classes: { order: 'alphabetically' } }], + }, + ], + invalid: [ + // default option + class + wrong order within group and wrong group order + alphabetically + { + code: ` +class Foo { + public static c: string = ""; + public static b: string = ""; + public static a: string; + + constructor() {} + + public d: string = ""; +} + `, + options: [ + { default: { memberTypes: defaultOrder, order: 'alphabetically' } }, + ], + errors: [ + { + messageId: 'incorrectGroupOrder', + data: { + name: 'd', + rank: 'public constructor', + }, + }, + ], + }, + ], + }; + +const sortedWithGroupingClassExpressionsOption: TSESLint.RunTests< + MessageIds, + Options +> = { + valid: [ + // classExpressions option + interface + alphabetically --> Default order applies + { + code: ` +interface Foo { + [a: string] : number; + + () : Baz; + + c : x; + b : x; + a : x; + + new () : Bar; + + c() : void; + b() : void; + a() : void; +} + `, + options: [{ classExpressions: { order: 'alphabetically' } }], + }, + + // classExpressions option + type literal + alphabetically --> Default order applies + { + code: ` +type Foo = { + [a: string] : number; + + () : Baz; + + c : x; + b : x; + a : x; + + new () : Bar; + + c() : void; + b() : void; + a() : void; +} + `, + options: [{ classExpressions: { order: 'alphabetically' } }], + }, + + // classExpressions option + class + alphabetically --> Default order applies + { + code: ` +class Foo { + public static a: string; + protected static b: string = ""; + private static c: string = ""; + + public d: string = ""; + protected e: string = ""; + private f: string = ""; + + constructor() {} +} + `, + options: [{ classExpressions: { order: 'alphabetically' } }], + }, + + // classExpressions option + class expression + default order + alphabetically + { + code: ` +const foo = class Foo { + public static a: string; + protected static b: string = ""; + private static c: string = ""; + + public d: string = ""; + protected e: string = ""; + private f: string = ""; + + constructor() {} +} + `, + options: [ + { + classExpressions: { + memberTypes: defaultOrder, + order: 'alphabetically', + }, + }, + ], + }, + + // classExpressions option + class expression + custom order + alphabetically + { + code: ` +const foo = class Foo { + constructor() {} + + public d: string = ""; + protected e: string = ""; + private f: string = ""; + + public static a: string; + protected static b: string = ""; + private static c: string = ""; +} + `, + options: [ + { + classExpressions: { + memberTypes: ['constructor', 'instance-field', 'static-field'], + order: 'alphabetically', + }, + }, + ], + }, + ], + invalid: [ + // default option + class expression + wrong order within group and wrong group order + alphabetically + { + code: ` +const foo = class Foo { + public static c: string = ""; + public static b: string = ""; + public static a: string; + + constructor() {} + + public d: string = ""; +} + `, + options: [ + { default: { memberTypes: defaultOrder, order: 'alphabetically' } }, + ], + errors: [ + { + messageId: 'incorrectGroupOrder', + data: { + name: 'd', + rank: 'public constructor', + }, + }, + ], + }, + ], +}; + +const sortedWithGroupingInterfacesOption: TSESLint.RunTests< + MessageIds, + Options +> = { + valid: [ + // interfaces option + interface + default order + alphabetically + { + code: ` +interface Foo { + [a: string] : number; + + a : x; + b : x; + c : x; + + a() : void; + b() : void; + c() : void; + + new () : Bar; + + () : Baz; +} + `, + options: [ + { + interfaces: { + memberTypes: ['signature', 'field', 'method', 'constructor'], + order: 'alphabetically', + }, + }, + ], + }, + + // interfaces option + interface + custom order + alphabetically + { + code: ` +interface Foo { + new () : Bar; + + a() : void; + b() : void; + c() : void; + + a : x; + b : x; + c : x; + + [a: string] : number; + () : Baz; +} + `, + options: [ + { + interfaces: { + memberTypes: ['constructor', 'method', 'field'], + order: 'alphabetically', + }, + }, + ], + }, + + // interfaces option + type literal + alphabetically --> Default order applies + { + code: ` +type Foo = { + [a: string] : number; + + () : Baz; + + c : x; + b : x; + a : x; + + new () : Bar; + + c() : void; + b() : void; + a() : void; +} + `, + options: [{ interfaces: { order: 'alphabetically' } }], + }, + + // interfaces option + class + alphabetically --> Default order applies + { + code: ` +class Foo { + public static a: string; + protected static b: string = ""; + private static c: string = ""; + + public d: string = ""; + protected e: string = ""; + private f: string = ""; + + constructor() {} +} + `, + options: [{ interfaces: { order: 'alphabetically' } }], + }, + + // interfaces option + class expression + alphabetically --> Default order applies + { + code: ` +const foo = class Foo { + public static a: string; + protected static b: string = ""; + private static c: string = ""; + + public d: string = ""; + protected e: string = ""; + private f: string = ""; + + constructor() {} +} + `, + options: [{ interfaces: { order: 'alphabetically' } }], + }, + ], + invalid: [ + // default option + interface + wrong order within group and wrong group order + alphabetically + { + code: ` +interface Foo { + [a: string] : number; + + a : x; + b : x; + c : x; + + c() : void; + b() : void; + a() : void; + + () : Baz; + + new () : Bar; +} + `, + options: [ + { default: { memberTypes: defaultOrder, order: 'alphabetically' } }, + ], + errors: [ + { + messageId: 'incorrectGroupOrder', + data: { + name: 'call', + rank: 'field', + }, + }, + { + messageId: 'incorrectGroupOrder', + data: { + name: 'new', + rank: 'method', + }, + }, + ], + }, + ], +}; + +const sortedWithGroupingTypeLiteralsOption: TSESLint.RunTests< + MessageIds, + Options +> = { + valid: [ + // typeLiterals option + interface + alphabetically --> Default order applies + { + code: ` +interface Foo { + [a: string] : number; + + () : Baz; + + c : x; + b : x; + a : x; + + new () : Bar; + + c() : void; + b() : void; + a() : void; +} + `, + options: [{ typeLiterals: { order: 'alphabetically' } }], + }, + + // typeLiterals option + type literal + default order + alphabetically + { + code: ` +type Foo = { + [a: string] : number; + + a : x; + b : x; + c : x; + + a() : void; + b() : void; + c() : void; + + new () : Bar; + + () : Baz; +} + `, + options: [ + { + typeLiterals: { + memberTypes: ['signature', 'field', 'method', 'constructor'], + order: 'alphabetically', + }, + }, + ], + }, + + // typeLiterals option + type literal + custom order + alphabetically + { + code: ` +type Foo = { + new () : Bar; + + a() : void; + b() : void; + c() : void; + + a : x; + b : x; + c : x; + + [a: string] : number; + () : Baz; +} + `, + options: [ + { + typeLiterals: { + memberTypes: ['constructor', 'method', 'field'], + order: 'alphabetically', + }, + }, + ], + }, + + // typeLiterals option + class + alphabetically --> Default order applies + { + code: ` +class Foo { + public static a: string; + protected static b: string = ""; + private static c: string = ""; + + public d: string = ""; + protected e: string = ""; + private f: string = ""; + + constructor() {} +} + `, + options: [{ typeLiterals: { order: 'alphabetically' } }], + }, + + // typeLiterals option + class expression + alphabetically --> Default order applies + { + code: ` +const foo = class Foo { + public static a: string; + protected static b: string = ""; + private static c: string = ""; + + public d: string = ""; + protected e: string = ""; + private f: string = ""; + + constructor() {} +} + `, + options: [{ typeLiterals: { order: 'alphabetically' } }], + }, + ], + invalid: [ + // default option + type literal + wrong order within group and wrong group order + alphabetically + { + code: ` +type Foo = { + [a: string] : number; + + a : x; + b : x; + c : x; + + c() : void; + b() : void; + a() : void; + + () : Baz; + + new () : Bar; +} + `, + options: [ + { default: { memberTypes: defaultOrder, order: 'alphabetically' } }, + ], + errors: [ + { + messageId: 'incorrectGroupOrder', + data: { + name: 'call', + rank: 'field', + }, + }, + { + messageId: 'incorrectGroupOrder', + data: { + name: 'new', + rank: 'method', + }, + }, + ], + }, + + // default option + private identifiers + { + code: ` +class Foo { + #c = 3; + #b = 2; + #a = 1; +} + `, + options: [ + { default: { memberTypes: defaultOrder, order: 'alphabetically' } }, + ], + errors: [ + { + messageId: 'incorrectOrder', + line: 4, + column: 3, + }, + { + messageId: 'incorrectOrder', + line: 5, + column: 3, + }, + ], + }, + ], +}; + +const sortedWithoutGrouping = { + valid: [ + ...sortedWithoutGroupingDefaultOption.valid, + ...sortedWithoutGroupingClassesOption.valid, + ...sortedWithoutGroupingClassExpressionsOption.valid, + ...sortedWithoutGroupingInterfacesOption.valid, + ...sortedWithoutGroupingTypeLiteralsOption.valid, + ], + invalid: [ + ...sortedWithoutGroupingDefaultOption.invalid, + ...sortedWithoutGroupingClassesOption.invalid, + ...sortedWithoutGroupingClassExpressionsOption.invalid, + ...sortedWithoutGroupingInterfacesOption.invalid, + ...sortedWithoutGroupingTypeLiteralsOption.invalid, + ], +}; + +const sortedWithGrouping = { + valid: [ + ...sortedWithGroupingDefaultOption.valid, + ...sortedWithGroupingClassesOption.valid, + ...sortedWithGroupingClassExpressionsOption.valid, + ...sortedWithGroupingInterfacesOption.valid, + ...sortedWithGroupingTypeLiteralsOption.valid, + ], + invalid: [ + ...sortedWithGroupingDefaultOption.invalid, + ...sortedWithGroupingClassesOption.invalid, + ...sortedWithGroupingClassExpressionsOption.invalid, + ...sortedWithGroupingInterfacesOption.invalid, + ...sortedWithGroupingTypeLiteralsOption.invalid, + ], +}; + +ruleTester.run('member-ordering-alphabetically-order', rule, { + valid: [...sortedWithoutGrouping.valid, ...sortedWithGrouping.valid], + invalid: [...sortedWithoutGrouping.invalid, ...sortedWithGrouping.invalid], +}); diff --git a/packages/eslint-plugin/tests/rules/member-ordering.test.ts b/packages/eslint-plugin/tests/rules/member-ordering.test.ts index 97b05cfdeffc..cbab8ced2a37 100644 --- a/packages/eslint-plugin/tests/rules/member-ordering.test.ts +++ b/packages/eslint-plugin/tests/rules/member-ordering.test.ts @@ -1,8 +1,4 @@ -import rule, { - defaultOrder, - MessageIds, - Options, -} from '../../src/rules/member-ordering'; +import rule, { MessageIds, Options } from '../../src/rules/member-ordering'; import { RuleTester } from '../RuleTester'; import { TSESLint } from '@typescript-eslint/experimental-utils'; @@ -3830,2611 +3826,4 @@ class Foo { ], }; -const sortedWithoutGroupingDefaultOption: TSESLint.RunTests< - MessageIds, - Options -> = { - valid: [ - // default option + interface + multiple types - { - code: ` -interface Foo { - (): Foo; - a(): Foo; - b(): Foo; -} - `, - options: [ - { default: { memberTypes: defaultOrder, order: 'alphabetically' } }, - ], - }, - - // default option + interface + lower/upper case - { - code: ` -interface Foo { - A : b; - a : b; -} - `, - options: [{ default: { memberTypes: 'never', order: 'alphabetically' } }], - }, - - // default option + interface + numbers - { - code: ` -interface Foo { - a1 : b; - aa : b; -} - `, - options: [{ default: { memberTypes: 'never', order: 'alphabetically' } }], - }, - - // default option + type literal + multiple types - { - code: ` -type Foo = { - a : b; - [a: string] : number; - b() : void; - () : Baz; - new () : Bar; -} - `, - options: [{ default: { memberTypes: 'never', order: 'alphabetically' } }], - }, - - // default option + type literal + lower/upper case - { - code: ` -type Foo = { - A : b; - a : b; -} - `, - options: [{ default: { memberTypes: 'never', order: 'alphabetically' } }], - }, - - // default option + type literal + numbers - { - code: ` -type Foo = { - a1 : b; - aa : b; -} - `, - options: [{ default: { memberTypes: 'never', order: 'alphabetically' } }], - }, - - // default option + class + multiple types - { - code: ` -class Foo { - public static a : string; - protected static b : string = ""; - private static c : string = ""; - constructor() {} - @Dec() d: string; - public e : string = ""; - @Dec() f : string = ""; - protected g : string = ""; - private h : string = ""; -} - `, - options: [{ default: { memberTypes: 'never', order: 'alphabetically' } }], - }, - - // default option + class + lower/upper case - { - code: ` -class Foo { - public static A : string; - public static a : string; -} - `, - options: [{ default: { memberTypes: 'never', order: 'alphabetically' } }], - }, - - // default option + class + numbers - { - code: ` -class Foo { - public static a1 : string; - public static aa : string; -} - `, - options: [{ default: { memberTypes: 'never', order: 'alphabetically' } }], - }, - - // default option + class expression + multiple types - { - code: ` -const foo = class Foo { - public static a : string; - protected static b : string = ""; - private static c : string = ""; - constructor() {} - public d : string = ""; - protected e : string = ""; - private f : string = ""; -} - `, - options: [{ default: { memberTypes: 'never', order: 'alphabetically' } }], - }, - - // default option + class expression + lower/upper case - { - code: ` -const foo = class Foo { - public static A : string; - public static a : string; -} - `, - options: [{ default: { memberTypes: 'never', order: 'alphabetically' } }], - }, - - // default option + class expression + numbers - { - code: ` -const foo = class Foo { - public static a1 : string; - public static aa : string; -} - `, - options: [{ default: { memberTypes: 'never', order: 'alphabetically' } }], - }, - - // default option + class + decorators - { - code: ` -class Foo { - public static a : string; - @Dec() static b : string; - public static c : string; -} - `, - options: [{ default: { memberTypes: 'never', order: 'alphabetically' } }], - }, - - // default option + private identifiers - { - code: ` -class Foo { - #a = 1; - #b = 2; - #c = 3; -} - `, - options: [ - { default: { memberTypes: defaultOrder, order: 'alphabetically' } }, - ], - }, - ], - invalid: [ - // default option + interface + wrong order - { - code: ` -interface Foo { - b() : void; - a : b; - [a: string] : number; - new () : Bar; - () : Baz; -} - `, - options: [{ default: { memberTypes: 'never', order: 'alphabetically' } }], - errors: [ - { - messageId: 'incorrectOrder', - data: { - member: 'a', - beforeMember: 'b', - }, - }, - { - messageId: 'incorrectOrder', - data: { - member: 'call', - beforeMember: 'new', - }, - }, - ], - }, - - // default option + interface + wrong order (multiple) - { - code: ` -interface Foo { - c : string; - b : string; - a : string; -} - `, - options: [{ default: { memberTypes: 'never', order: 'alphabetically' } }], - errors: [ - { - messageId: 'incorrectOrder', - data: { - member: 'b', - beforeMember: 'c', - }, - }, - { - messageId: 'incorrectOrder', - data: { - member: 'a', - beforeMember: 'b', - }, - }, - ], - }, - - // default option + type literal + wrong order - { - code: ` -type Foo = { - b() : void; - a : b; - [a: string] : number; - new () : Bar; - () : Baz; -} - `, - options: [{ default: { memberTypes: 'never', order: 'alphabetically' } }], - errors: [ - { - messageId: 'incorrectOrder', - data: { - member: 'a', - beforeMember: 'b', - }, - }, - { - messageId: 'incorrectOrder', - data: { - member: 'call', - beforeMember: 'new', - }, - }, - ], - }, - - // default option + type literal + wrong order (multiple) - { - code: ` -type Foo = { - c : string; - b : string; - a : string; -} - `, - options: [{ default: { memberTypes: 'never', order: 'alphabetically' } }], - errors: [ - { - messageId: 'incorrectOrder', - data: { - member: 'b', - beforeMember: 'c', - }, - }, - { - messageId: 'incorrectOrder', - data: { - member: 'a', - beforeMember: 'b', - }, - }, - ], - }, - - // default option + class + wrong order - { - code: ` -class Foo { - protected static b : string = ""; - public static a : string; - private static c : string = ""; - constructor() {} - public d : string = ""; - protected e : string = ""; - private f : string = ""; -} - `, - options: [{ default: { memberTypes: 'never', order: 'alphabetically' } }], - errors: [ - { - messageId: 'incorrectOrder', - data: { - member: 'a', - beforeMember: 'b', - }, - }, - ], - }, - - // default option + class + wrong order (multiple) - { - code: ` -class Foo { - public static c: string; - public static b: string; - public static a: string; -} - `, - options: [{ default: { memberTypes: 'never', order: 'alphabetically' } }], - errors: [ - { - messageId: 'incorrectOrder', - data: { - member: 'b', - beforeMember: 'c', - }, - }, - { - messageId: 'incorrectOrder', - data: { - member: 'a', - beforeMember: 'b', - }, - }, - ], - }, - - // default option + class expression + wrong order - { - code: ` -const foo = class Foo { - protected static b : string = ""; - public static a : string; - private static c : string = ""; - constructor() {} - public d : string = ""; - protected e : string = ""; - private f : string = ""; -} - `, - options: [{ default: { memberTypes: 'never', order: 'alphabetically' } }], - errors: [ - { - messageId: 'incorrectOrder', - data: { - member: 'a', - beforeMember: 'b', - }, - }, - ], - }, - - // default option + class expression + wrong order (multiple) - { - code: ` -const foo = class Foo { - public static c: string; - public static b: string; - public static a: string; -} - `, - options: [{ default: { memberTypes: 'never', order: 'alphabetically' } }], - errors: [ - { - messageId: 'incorrectOrder', - data: { - member: 'b', - beforeMember: 'c', - }, - }, - { - messageId: 'incorrectOrder', - data: { - member: 'a', - beforeMember: 'b', - }, - }, - ], - }, - ], -}; - -const sortedWithoutGroupingClassesOption: TSESLint.RunTests< - MessageIds, - Options -> = { - valid: [ - // classes option + interface + multiple types --> Only member group order is checked (default config) - { - code: ` -interface Foo { - [a: string] : number; - () : Baz; - c : b; - new () : Bar; - b() : void; -} - `, - options: [{ classes: { memberTypes: 'never', order: 'alphabetically' } }], - }, - - // classes option + interface + lower/upper case --> Only member group order is checked (default config) - { - code: ` -interface Foo { - a : b; - A : b; -} - `, - options: [{ classes: { memberTypes: 'never', order: 'alphabetically' } }], - }, - - // classes option + interface + numbers --> Only member group order is checked (default config) - { - code: ` -interface Foo { - aa : b; - a1 : b; -} - `, - options: [{ classes: { memberTypes: 'never', order: 'alphabetically' } }], - }, - - // classes option + type literal + multiple types --> Only member group order is checked (default config) - { - code: ` -type Foo = { - [a: string] : number; - () : Baz; - c : b; - new () : Bar; - b() : void; -} - `, - options: [{ classes: { memberTypes: 'never', order: 'alphabetically' } }], - }, - - // classes option + type literal + lower/upper case --> Only member group order is checked (default config) - { - code: ` -type Foo = { - a : b; - A : b; -} - `, - options: [{ classes: { memberTypes: 'never', order: 'alphabetically' } }], - }, - - // classes option + type literal + numbers --> Only member group order is checked (default config) - { - code: ` -type Foo = { - aa : b; - a1 : b; -} - `, - options: [{ classes: { memberTypes: 'never', order: 'alphabetically' } }], - }, - - // classes option + class + multiple types - { - code: ` -class Foo { - public static a : string; - protected static b : string = ""; - @Dec() private static c : string = ""; - constructor() {} - public d : string = ""; - protected e : string = ""; - @Dec() - private f : string = ""; -} - `, - options: [{ classes: { memberTypes: 'never', order: 'alphabetically' } }], - }, - - // classes option + class + lower/upper case - { - code: ` -class Foo { - public static A : string; - public static a : string; -} - `, - options: [{ classes: { memberTypes: 'never', order: 'alphabetically' } }], - }, - - // classes option + class + numbers - { - code: ` -class Foo { - public static a1 : string; - public static aa : string; -} - `, - options: [{ classes: { memberTypes: 'never', order: 'alphabetically' } }], - }, - - // classes option + class expression + multiple types --> Only member group order is checked (default config) - { - code: ` -const foo = class Foo { - public static a : string; - protected static b : string = ""; - private static c : string = ""; - public d : string = ""; - protected e : string = ""; - private f : string = ""; - constructor() {} -} - `, - options: [{ classes: { memberTypes: 'never', order: 'alphabetically' } }], - }, - - // classes option + class expression + lower/upper case --> Only member group order is checked (default config) - { - code: ` -const foo = class Foo { - public static a : string; - public static A : string; -} - `, - options: [{ classes: { memberTypes: 'never', order: 'alphabetically' } }], - }, - - // classes option + class expression + numbers --> Only member group order is checked (default config) - { - code: ` -const foo = class Foo { - public static aa : string; - public static a1 : string; -} - `, - options: [{ classes: { memberTypes: 'never', order: 'alphabetically' } }], - }, - ], - invalid: [ - // classes option + class + wrong order - { - code: ` -class Foo { - protected static b : string = ""; - public static a : string; - private static c : string = ""; - constructor() {} - public d : string = ""; - protected e : string = ""; - private f : string = ""; -} - `, - options: [{ classes: { memberTypes: 'never', order: 'alphabetically' } }], - errors: [ - { - messageId: 'incorrectOrder', - data: { - member: 'a', - beforeMember: 'b', - }, - }, - ], - }, - - // classes option + class + wrong order (multiple) - { - code: ` -class Foo { - public static c: string; - public static b: string; - public static a: string; -} - `, - options: [{ classes: { memberTypes: 'never', order: 'alphabetically' } }], - errors: [ - { - messageId: 'incorrectOrder', - data: { - member: 'b', - beforeMember: 'c', - }, - }, - { - messageId: 'incorrectOrder', - data: { - member: 'a', - beforeMember: 'b', - }, - }, - ], - }, - ], -}; - -const sortedWithoutGroupingClassExpressionsOption: TSESLint.RunTests< - MessageIds, - Options -> = { - valid: [ - // classExpressions option + interface + multiple types --> Only member group order is checked (default config) - { - code: ` -interface Foo { - [a: string] : number; - () : Baz; - c : b; - new () : Bar; - b() : void; -} - `, - options: [ - { classExpressions: { memberTypes: 'never', order: 'alphabetically' } }, - ], - }, - - // classExpressions option + interface + lower/upper case --> Only member group order is checked (default config) - { - code: ` -interface Foo { - a : b; - A : b; -} - `, - options: [ - { classExpressions: { memberTypes: 'never', order: 'alphabetically' } }, - ], - }, - - // classExpressions option + interface + numbers --> Only member group order is checked (default config) - { - code: ` -interface Foo { - aa : b; - a1 : b; -} - `, - options: [ - { classExpressions: { memberTypes: 'never', order: 'alphabetically' } }, - ], - }, - - // classExpressions option + type literal + multiple types --> Only member group order is checked (default config) - { - code: ` -type Foo = { - [a: string] : number; - () : Baz; - c : b; - new () : Bar; - b() : void; -} - `, - options: [ - { classExpressions: { memberTypes: 'never', order: 'alphabetically' } }, - ], - }, - - // classExpressions option + type literal + lower/upper case --> Only member group order is checked (default config) - { - code: ` -type Foo = { - a : b; - A : b; -} - `, - options: [ - { classExpressions: { memberTypes: 'never', order: 'alphabetically' } }, - ], - }, - - // classExpressions option + type literal + numbers --> Only member group order is checked (default config) - { - code: ` -type Foo = { - aa : b; - a1 : b; -} - `, - options: [ - { classExpressions: { memberTypes: 'never', order: 'alphabetically' } }, - ], - }, - - // classExpressions option + class + multiple types --> Only member group order is checked (default config) - { - code: ` -class Foo { - public static a : string; - protected static b : string = ""; - private static c : string = ""; - public d : string = ""; - protected e : string = ""; - private f : string = ""; - constructor() {} -} - `, - options: [ - { classExpressions: { memberTypes: 'never', order: 'alphabetically' } }, - ], - }, - - // classExpressions option + class + lower/upper case --> Only member group order is checked (default config) - { - code: ` -class Foo { - public static a : string; - public static A : string; -} - `, - options: [ - { classExpressions: { memberTypes: 'never', order: 'alphabetically' } }, - ], - }, - - // classExpressions option + class + numbers --> Only member group order is checked (default config) - { - code: ` -class Foo { - public static aa : string; - public static a1 : string; -} - `, - options: [ - { classExpressions: { memberTypes: 'never', order: 'alphabetically' } }, - ], - }, - - // classExpressions option + class expression + multiple types - { - code: ` -const foo = class Foo { - public static a : string; - protected static b : string = ""; - private static c : string = ""; - constructor() {} - public d : string = ""; - protected e : string = ""; - private f : string = ""; -} - `, - options: [ - { classExpressions: { memberTypes: 'never', order: 'alphabetically' } }, - ], - }, - - // classExpressions option + class expression + lower/upper case - { - code: ` -const foo = class Foo { - public static A : string; - public static a : string; -} - `, - options: [ - { classExpressions: { memberTypes: 'never', order: 'alphabetically' } }, - ], - }, - - // classExpressions option + class expression + numbers - { - code: ` -const foo = class Foo { - public static a1 : string; - public static aa : string; -} - `, - options: [ - { classExpressions: { memberTypes: 'never', order: 'alphabetically' } }, - ], - }, - ], - invalid: [ - // classExpressions option + class expression + wrong order - { - code: ` -const foo = class Foo { - protected static b : string = ""; - public static a : string; - private static c : string = ""; - constructor() {} - public d : string = ""; - protected e : string = ""; - private f : string = ""; -} - `, - options: [ - { classExpressions: { memberTypes: 'never', order: 'alphabetically' } }, - ], - errors: [ - { - messageId: 'incorrectOrder', - data: { - member: 'a', - beforeMember: 'b', - }, - }, - ], - }, - - // classExpressions option + class expression + wrong order (multiple) - { - code: ` -const foo = class Foo { - public static c: string; - public static b: string; - public static a: string; -} - `, - options: [ - { classExpressions: { memberTypes: 'never', order: 'alphabetically' } }, - ], - errors: [ - { - messageId: 'incorrectOrder', - data: { - member: 'b', - beforeMember: 'c', - }, - }, - { - messageId: 'incorrectOrder', - data: { - member: 'a', - beforeMember: 'b', - }, - }, - ], - }, - ], -}; - -const sortedWithoutGroupingInterfacesOption: TSESLint.RunTests< - MessageIds, - Options -> = { - valid: [ - // interfaces option + interface + multiple types - { - code: ` -interface Foo { - [a: string] : number; - a : b; - b() : void; - () : Baz; - new () : Bar; -} - `, - options: [ - { interfaces: { memberTypes: 'never', order: 'alphabetically' } }, - ], - }, - - // interfaces option + interface + lower/upper case - { - code: ` -interface Foo { - A : b; - a : b; -} - `, - options: [ - { interfaces: { memberTypes: 'never', order: 'alphabetically' } }, - ], - }, - - // interfaces option + interface + numbers - { - code: ` -interface Foo { - a1 : b; - aa : b; -} - `, - options: [ - { interfaces: { memberTypes: 'never', order: 'alphabetically' } }, - ], - }, - - // interfaces option + type literal + multiple types --> Only member group order is checked (default config) - { - code: ` -type Foo = { - [a: string] : number; - () : Baz; - c : b; - new () : Bar; - b() : void; -} - `, - options: [ - { interfaces: { memberTypes: 'never', order: 'alphabetically' } }, - ], - }, - - // interfaces option + type literal + lower/upper case --> Only member group order is checked (default config) - { - code: ` -type Foo = { - a : b; - A : b; -} - `, - options: [ - { interfaces: { memberTypes: 'never', order: 'alphabetically' } }, - ], - }, - - // interfaces option + type literal + numbers --> Only member group order is checked (default config) - { - code: ` -type Foo = { - aa : b; - a1 : b; -} - `, - options: [ - { interfaces: { memberTypes: 'never', order: 'alphabetically' } }, - ], - }, - - // interfaces option + class + multiple types --> Only member group order is checked (default config) - { - code: ` -class Foo { - public static a : string; - protected static b : string = ""; - private static c : string = ""; - public d : string = ""; - protected e : string = ""; - private f : string = ""; - constructor() {} -} - `, - options: [ - { interfaces: { memberTypes: 'never', order: 'alphabetically' } }, - ], - }, - - // interfaces option + class + lower/upper case --> Only member group order is checked (default config) - { - code: ` -class Foo { - public static a : string; - public static A : string; -} - `, - options: [ - { interfaces: { memberTypes: 'never', order: 'alphabetically' } }, - ], - }, - - // interfaces option + class + numbers --> Only member group order is checked (default config) - { - code: ` -class Foo { - public static aa : string; - public static a1 : string; -} - `, - options: [ - { interfaces: { memberTypes: 'never', order: 'alphabetically' } }, - ], - }, - - // interfaces option + class expression + multiple types --> Only member group order is checked (default config) - { - code: ` -const foo = class Foo { - public static a : string; - protected static b : string = ""; - private static c : string = ""; - public d : string = ""; - protected e : string = ""; - private f : string = ""; - constructor() {} -} - `, - options: [ - { interfaces: { memberTypes: 'never', order: 'alphabetically' } }, - ], - }, - - // interfaces option + class expression + lower/upper case --> Only member group order is checked (default config) - { - code: ` -const foo = class Foo { - public static a : string; - public static A : string; -} - `, - options: [ - { interfaces: { memberTypes: 'never', order: 'alphabetically' } }, - ], - }, - - // interfaces option + class expression + numbers --> Only member group order is checked (default config) - { - code: ` -const foo = class Foo { - public static aa : string; - public static a1 : string; -} - `, - options: [ - { interfaces: { memberTypes: 'never', order: 'alphabetically' } }, - ], - }, - ], - invalid: [ - // interfaces option + interface + wrong order - { - code: ` -interface Foo { - b() : void; - a : b; - [a: string] : number; - new () : Bar; - () : Baz; -} - `, - options: [ - { interfaces: { memberTypes: 'never', order: 'alphabetically' } }, - ], - errors: [ - { - messageId: 'incorrectOrder', - data: { - member: 'a', - beforeMember: 'b', - }, - }, - { - messageId: 'incorrectOrder', - data: { - member: 'call', - beforeMember: 'new', - }, - }, - ], - }, - - // interfaces option + interface + wrong order (multiple) - { - code: ` -interface Foo { - c : string; - b : string; - a : string; -} - `, - options: [ - { interfaces: { memberTypes: 'never', order: 'alphabetically' } }, - ], - errors: [ - { - messageId: 'incorrectOrder', - data: { - member: 'b', - beforeMember: 'c', - }, - }, - { - messageId: 'incorrectOrder', - data: { - member: 'a', - beforeMember: 'b', - }, - }, - ], - }, - ], -}; - -const sortedWithoutGroupingTypeLiteralsOption: TSESLint.RunTests< - MessageIds, - Options -> = { - valid: [ - // typeLiterals option + interface + multiple types --> Only member group order is checked (default config) - { - code: ` -interface Foo { - [a: string] : number; - () : Baz; - c : b; - new () : Bar; - b() : void; -} - `, - options: [ - { typeLiterals: { memberTypes: 'never', order: 'alphabetically' } }, - ], - }, - - // typeLiterals option + interface + lower/upper case --> Only member group order is checked (default config) - { - code: ` -interface Foo { - a : b; - A : b; -} - `, - options: [ - { typeLiterals: { memberTypes: 'never', order: 'alphabetically' } }, - ], - }, - - // typeLiterals option + interface + numbers --> Only member group order is checked (default config) - { - code: ` -interface Foo { - aa : b; - a1 : b; -} - `, - options: [ - { typeLiterals: { memberTypes: 'never', order: 'alphabetically' } }, - ], - }, - - // typeLiterals option + type literal + multiple types - { - code: ` -type Foo = { - [a: string] : number; - a : b; - b() : void; - () : Baz; - new () : Bar; -} - `, - options: [ - { typeLiterals: { memberTypes: 'never', order: 'alphabetically' } }, - ], - }, - - // typeLiterals option + type literal + lower/upper case - { - code: ` -type Foo = { - A : b; - a : b; -} - `, - options: [ - { typeLiterals: { memberTypes: 'never', order: 'alphabetically' } }, - ], - }, - - // typeLiterals option + type literal + numbers - { - code: ` -type Foo = { - a1 : b; - aa : b; -} - `, - options: [ - { typeLiterals: { memberTypes: 'never', order: 'alphabetically' } }, - ], - }, - - // typeLiterals option + class + multiple types --> Only member group order is checked (default config) - { - code: ` -class Foo { - public static a : string; - protected static b : string = ""; - private static c : string = ""; - public d : string = ""; - protected e : string = ""; - private f : string = ""; - constructor() {} -} - `, - options: [ - { typeLiterals: { memberTypes: 'never', order: 'alphabetically' } }, - ], - }, - - // typeLiterals option + class + lower/upper case --> Only member group order is checked (default config) - { - code: ` -class Foo { - public static a : string; - public static A : string; -} - `, - options: [ - { typeLiterals: { memberTypes: 'never', order: 'alphabetically' } }, - ], - }, - - // typeLiterals option + class + numbers --> Only member group order is checked (default config) - { - code: ` -class Foo { - public static aa : string; - public static a1 : string; -} - `, - options: [ - { typeLiterals: { memberTypes: 'never', order: 'alphabetically' } }, - ], - }, - - // typeLiterals option + class expression + multiple types --> Only member group order is checked (default config) - { - code: ` -const foo = class Foo { - public static a : string; - protected static b : string = ""; - private static c : string = ""; - public d : string = ""; - protected e : string = ""; - private f : string = ""; - constructor() {} -} - `, - options: [ - { typeLiterals: { memberTypes: 'never', order: 'alphabetically' } }, - ], - }, - - // typeLiterals option + class expression + lower/upper case --> Only member group order is checked (default config) - { - code: ` -const foo = class Foo { - public static a : string; - public static A : string; -} - `, - options: [ - { typeLiterals: { memberTypes: 'never', order: 'alphabetically' } }, - ], - }, - - // typeLiterals option + class expression + numbers --> Only member group order is checked (default config) - { - code: ` -const foo = class Foo { - public static aa : string; - public static a1 : string; -} - `, - options: [ - { typeLiterals: { memberTypes: 'never', order: 'alphabetically' } }, - ], - }, - ], - invalid: [ - // typeLiterals option + type literal + wrong order - { - code: ` -type Foo = { - b() : void; - a : b; - [a: string] : number; - new () : Bar; - () : Baz; -} - `, - options: [ - { typeLiterals: { memberTypes: 'never', order: 'alphabetically' } }, - ], - errors: [ - { - messageId: 'incorrectOrder', - data: { - member: 'a', - beforeMember: 'b', - }, - }, - { - messageId: 'incorrectOrder', - data: { - member: 'call', - beforeMember: 'new', - }, - }, - ], - }, - - // typeLiterals option + type literal + wrong order (multiple) - { - code: ` -type Foo = { - c : string; - b : string; - a : string; -} - `, - options: [ - { typeLiterals: { memberTypes: 'never', order: 'alphabetically' } }, - ], - errors: [ - { - messageId: 'incorrectOrder', - data: { - member: 'b', - beforeMember: 'c', - }, - }, - { - messageId: 'incorrectOrder', - data: { - member: 'a', - beforeMember: 'b', - }, - }, - ], - }, - ], -}; - -const sortedWithGroupingDefaultOption: TSESLint.RunTests = - { - valid: [ - // default option + interface + default order + alphabetically - { - code: ` -interface Foo { - [a: string] : number; - - () : Baz; - - a : x; - b : x; - c : x; - - new () : Bar; - - a() : void; - b() : void; - c() : void; -} - `, - options: [ - { default: { memberTypes: defaultOrder, order: 'alphabetically' } }, - ], - }, - - // default option + interface + custom order + alphabetically - { - code: ` -interface Foo { - new () : Bar; - - a() : void; - b() : void; - c() : void; - - a : x; - b : x; - c : x; - - [a: string] : number; - () : Baz; -} - `, - options: [ - { - default: { - memberTypes: ['constructor', 'method', 'field'], - order: 'alphabetically', - }, - }, - ], - }, - - // default option + type literal + default order + alphabetically - { - code: ` -type Foo = { - [a: string] : number; - - () : Baz; - - a : x; - b : x; - c : x; - - new () : Bar; - - a() : void; - b() : void; - c() : void; -} - `, - options: [ - { default: { memberTypes: defaultOrder, order: 'alphabetically' } }, - ], - }, - - // default option + type literal + custom order + alphabetically - { - code: ` -type Foo = { - [a: string] : number; - - new () : Bar; - - a() : void; - b() : void; - c() : void; - - a : x; - b : x; - c : x; - - () : Baz; -} - `, - options: [ - { - default: { - memberTypes: ['constructor', 'method', 'field'], - order: 'alphabetically', - }, - }, - ], - }, - - // default option + class + default order + alphabetically - { - code: ` -class Foo { - public static a: string; - protected static b: string = ""; - private static c: string = ""; - - public d: string = ""; - protected e: string = ""; - private f: string = ""; - - constructor() {} -} - `, - options: [ - { default: { memberTypes: defaultOrder, order: 'alphabetically' } }, - ], - }, - - // default option + class + defaultOrder + alphabetically - { - code: ` -class Foo { - public static a: string; - protected static b: string = ""; - private static c: string = ""; - - public d: string = ""; - protected e: string = ""; - private f: string = ""; - - constructor() {} - - get h() {} - - set g() {} -} - `, - options: [ - { - default: { - memberTypes: defaultOrder, - order: 'alphabetically', - }, - }, - ], - }, - - // default option + class + custom + alphabetically - { - code: ` -class Foo { - get a() {} - - @Bar - get b() {} - - set c() {} - - @Bar - set d() {} -} - `, - options: [ - { - default: { - memberTypes: ['get', 'decorated-get', 'set', 'decorated-set'], - order: 'alphabetically', - }, - }, - ], - }, - - // default option + class + decorators + default order + alphabetically - { - code: ` -class Foo { - public static a: string; - protected static b: string = ""; - private static c: string = ""; - - @Dec() public d: string; - @Dec() protected e: string; - @Dec() private f: string; - - public g: string = ""; - protected h: string = ""; - private i: string = ""; - - constructor() {} -} - `, - options: [ - { default: { memberTypes: defaultOrder, order: 'alphabetically' } }, - ], - }, - - // default option + class + custom order + alphabetically - { - code: ` -class Foo { - constructor() {} - - public d: string = ""; - protected e: string = ""; - private f: string = ""; - - public static a: string; - protected static b: string = ""; - private static c: string = ""; -} - `, - options: [ - { - default: { - memberTypes: ['constructor', 'instance-field', 'static-field'], - order: 'alphabetically', - }, - }, - ], - }, - - // default option + class expression + default order + alphabetically - { - code: ` -const foo = class Foo { - public static a: string; - protected static b: string = ""; - private static c: string = ""; - - public d: string = ""; - protected e: string = ""; - private f: string = ""; - - constructor() {} -} - `, - options: [ - { default: { memberTypes: defaultOrder, order: 'alphabetically' } }, - ], - }, - - // default option + class expression + custom order + alphabetically - { - code: ` -const foo = class Foo { - constructor() {} - - public d: string = ""; - protected e: string = ""; - private f: string = ""; - - public static a: string; - protected static b: string = ""; - private static c: string = ""; -} - `, - options: [ - { - default: { - memberTypes: ['constructor', 'instance-field', 'static-field'], - order: 'alphabetically', - }, - }, - ], - }, - ], - invalid: [ - // default option + class + wrong order within group and wrong group order + alphabetically - { - code: ` -class FooTestGetter { - public static a: string; - protected static b: string = ""; - private static c: string = ""; - - public d: string = ""; - protected e: string = ""; - private f: string = ""; - - get h() {} - - set g() {} - - constructor() {} -} - `, - options: [ - { - default: { - memberTypes: defaultOrder, - order: 'alphabetically', - }, - }, - ], - errors: [ - { - messageId: 'incorrectGroupOrder', - data: { - name: 'constructor', - rank: 'public instance get', - }, - }, - ], - }, - - // default option + class + custom + alphabetically - { - code: ` -class Foo { - @Bar - get a() {} - - get b() {} - - @Bar - set c() {} - - set d() {} -} - `, - options: [ - { - default: { - memberTypes: ['get', 'decorated-get', 'set', 'decorated-set'], - order: 'alphabetically', - }, - }, - ], - errors: [ - { - messageId: 'incorrectGroupOrder', - data: { - name: 'b', - rank: 'decorated get', - }, - }, - { - messageId: 'incorrectGroupOrder', - data: { - name: 'd', - rank: 'decorated set', - }, - }, - ], - }, - - // default option + class + wrong order within group and wrong group order + alphabetically - { - code: ` -class FooTestGetter { - public static a: string; - protected static b: string = ""; - private static c: string = ""; - - public d: string = ""; - protected e: string = ""; - private f: string = ""; - - set g() {} - - constructor() {} - - get h() {} -} - `, - options: [ - { - default: { - memberTypes: defaultOrder, - order: 'alphabetically', - }, - }, - ], - errors: [ - { - messageId: 'incorrectGroupOrder', - data: { - name: 'constructor', - rank: 'public instance set', - }, - }, - { - messageId: 'incorrectGroupOrder', - data: { - name: 'h', - rank: 'public instance set', - }, - }, - ], - }, - - // default option + interface + wrong order within group and wrong group order + alphabetically - { - code: ` -interface Foo { - [a: string] : number; - - a : x; - b : x; - c : x; - - c() : void; - b() : void; - a() : void; - - () : Baz; - - new () : Bar; -} - `, - options: [ - { default: { memberTypes: defaultOrder, order: 'alphabetically' } }, - ], - errors: [ - { - messageId: 'incorrectGroupOrder', - data: { - name: 'call', - rank: 'field', - }, - }, - { - messageId: 'incorrectGroupOrder', - data: { - name: 'new', - rank: 'method', - }, - }, - ], - }, - - // default option + type literal + wrong order within group and wrong group order + alphabetically - { - code: ` -type Foo = { - [a: string] : number; - - a : x; - b : x; - c : x; - - c() : void; - b() : void; - a() : void; - - () : Baz; - - new () : Bar; -} - `, - options: [ - { default: { memberTypes: defaultOrder, order: 'alphabetically' } }, - ], - errors: [ - { - messageId: 'incorrectGroupOrder', - data: { - name: 'call', - rank: 'field', - }, - }, - { - messageId: 'incorrectGroupOrder', - data: { - name: 'new', - rank: 'method', - }, - }, - ], - }, - - // default option + class + wrong order within group and wrong group order + alphabetically - { - code: ` -class Foo { - public static c: string = ""; - public static b: string = ""; - public static a: string; - - constructor() {} - - public d: string = ""; -} - `, - options: [ - { default: { memberTypes: defaultOrder, order: 'alphabetically' } }, - ], - errors: [ - { - messageId: 'incorrectGroupOrder', - data: { - name: 'd', - rank: 'public constructor', - }, - }, - ], - }, - - // default option + class expression + wrong order within group and wrong group order + alphabetically - { - code: ` -const foo = class Foo { - public static c: string = ""; - public static b: string = ""; - public static a: string; - - constructor() {} - - public d: string = ""; -} - `, - options: [ - { default: { memberTypes: defaultOrder, order: 'alphabetically' } }, - ], - errors: [ - { - messageId: 'incorrectGroupOrder', - data: { - name: 'd', - rank: 'public constructor', - }, - }, - ], - }, - // default option + class + decorators + custom order + wrong order within group and wrong group order + alphabetically - { - code: ` -class Foo { - @Dec() a1: string; - @Dec() - a3: string; - @Dec() - a2: string; - - constructor() {} - - b1: string; - b2: string; - - public c(): void; - @Dec() d(): void -} - `, - options: [ - { - default: { - memberTypes: [ - 'decorated-field', - 'field', - 'constructor', - 'decorated-method', - ], - order: 'alphabetically', - }, - }, - ], - errors: [ - { - messageId: 'incorrectGroupOrder', - data: { - name: 'b1', - rank: 'constructor', - }, - }, - { - messageId: 'incorrectGroupOrder', - data: { - name: 'b2', - rank: 'constructor', - }, - }, - ], - }, - ], - }; - -const sortedWithGroupingClassesOption: TSESLint.RunTests = - { - valid: [ - // classes option + interface + alphabetically --> Default order applies - { - code: ` -interface Foo { - [a: string] : number; - - () : Baz; - - c : x; - b : x; - a : x; - - new () : Bar; - - c() : void; - b() : void; - a() : void; -} - `, - options: [{ classes: { order: 'alphabetically' } }], - }, - - // classes option + type literal + alphabetically --> Default order applies - { - code: ` -type Foo = { - [a: string] : number; - - () : Baz; - - c : x; - b : x; - a : x; - - new () : Bar; - - c() : void; - b() : void; - a() : void; -} - `, - options: [{ classes: { order: 'alphabetically' } }], - }, - - // classes option + class + default order + alphabetically - { - code: ` -class Foo { - public static a: string; - protected static b: string = ""; - private static c: string = ""; - - public d: string = ""; - protected e: string = ""; - private f: string = ""; - - constructor() {} -} - `, - options: [ - { classes: { memberTypes: defaultOrder, order: 'alphabetically' } }, - ], - }, - - // classes option + class + custom order + alphabetically - { - code: ` -class Foo { - constructor() {} - - public d: string = ""; - protected e: string = ""; - private f: string = ""; - - public static a: string; - protected static b: string = ""; - private static c: string = ""; -} - `, - options: [ - { - classes: { - memberTypes: ['constructor', 'instance-field', 'static-field'], - order: 'alphabetically', - }, - }, - ], - }, - - // classes option + class expression + alphabetically --> Default order applies - { - code: ` -const foo = class Foo { - public static a: string; - protected static b: string = ""; - private static c: string = ""; - - public d: string = ""; - protected e: string = ""; - private f: string = ""; - - constructor() {} -} - `, - options: [{ classes: { order: 'alphabetically' } }], - }, - ], - invalid: [ - // default option + class + wrong order within group and wrong group order + alphabetically - { - code: ` -class Foo { - public static c: string = ""; - public static b: string = ""; - public static a: string; - - constructor() {} - - public d: string = ""; -} - `, - options: [ - { default: { memberTypes: defaultOrder, order: 'alphabetically' } }, - ], - errors: [ - { - messageId: 'incorrectGroupOrder', - data: { - name: 'd', - rank: 'public constructor', - }, - }, - ], - }, - ], - }; - -const sortedWithGroupingClassExpressionsOption: TSESLint.RunTests< - MessageIds, - Options -> = { - valid: [ - // classExpressions option + interface + alphabetically --> Default order applies - { - code: ` -interface Foo { - [a: string] : number; - - () : Baz; - - c : x; - b : x; - a : x; - - new () : Bar; - - c() : void; - b() : void; - a() : void; -} - `, - options: [{ classExpressions: { order: 'alphabetically' } }], - }, - - // classExpressions option + type literal + alphabetically --> Default order applies - { - code: ` -type Foo = { - [a: string] : number; - - () : Baz; - - c : x; - b : x; - a : x; - - new () : Bar; - - c() : void; - b() : void; - a() : void; -} - `, - options: [{ classExpressions: { order: 'alphabetically' } }], - }, - - // classExpressions option + class + alphabetically --> Default order applies - { - code: ` -class Foo { - public static a: string; - protected static b: string = ""; - private static c: string = ""; - - public d: string = ""; - protected e: string = ""; - private f: string = ""; - - constructor() {} -} - `, - options: [{ classExpressions: { order: 'alphabetically' } }], - }, - - // classExpressions option + class expression + default order + alphabetically - { - code: ` -const foo = class Foo { - public static a: string; - protected static b: string = ""; - private static c: string = ""; - - public d: string = ""; - protected e: string = ""; - private f: string = ""; - - constructor() {} -} - `, - options: [ - { - classExpressions: { - memberTypes: defaultOrder, - order: 'alphabetically', - }, - }, - ], - }, - - // classExpressions option + class expression + custom order + alphabetically - { - code: ` -const foo = class Foo { - constructor() {} - - public d: string = ""; - protected e: string = ""; - private f: string = ""; - - public static a: string; - protected static b: string = ""; - private static c: string = ""; -} - `, - options: [ - { - classExpressions: { - memberTypes: ['constructor', 'instance-field', 'static-field'], - order: 'alphabetically', - }, - }, - ], - }, - ], - invalid: [ - // default option + class expression + wrong order within group and wrong group order + alphabetically - { - code: ` -const foo = class Foo { - public static c: string = ""; - public static b: string = ""; - public static a: string; - - constructor() {} - - public d: string = ""; -} - `, - options: [ - { default: { memberTypes: defaultOrder, order: 'alphabetically' } }, - ], - errors: [ - { - messageId: 'incorrectGroupOrder', - data: { - name: 'd', - rank: 'public constructor', - }, - }, - ], - }, - ], -}; - -const sortedWithGroupingInterfacesOption: TSESLint.RunTests< - MessageIds, - Options -> = { - valid: [ - // interfaces option + interface + default order + alphabetically - { - code: ` -interface Foo { - [a: string] : number; - - a : x; - b : x; - c : x; - - a() : void; - b() : void; - c() : void; - - new () : Bar; - - () : Baz; -} - `, - options: [ - { - interfaces: { - memberTypes: ['signature', 'field', 'method', 'constructor'], - order: 'alphabetically', - }, - }, - ], - }, - - // interfaces option + interface + custom order + alphabetically - { - code: ` -interface Foo { - new () : Bar; - - a() : void; - b() : void; - c() : void; - - a : x; - b : x; - c : x; - - [a: string] : number; - () : Baz; -} - `, - options: [ - { - interfaces: { - memberTypes: ['constructor', 'method', 'field'], - order: 'alphabetically', - }, - }, - ], - }, - - // interfaces option + type literal + alphabetically --> Default order applies - { - code: ` -type Foo = { - [a: string] : number; - - () : Baz; - - c : x; - b : x; - a : x; - - new () : Bar; - - c() : void; - b() : void; - a() : void; -} - `, - options: [{ interfaces: { order: 'alphabetically' } }], - }, - - // interfaces option + class + alphabetically --> Default order applies - { - code: ` -class Foo { - public static a: string; - protected static b: string = ""; - private static c: string = ""; - - public d: string = ""; - protected e: string = ""; - private f: string = ""; - - constructor() {} -} - `, - options: [{ interfaces: { order: 'alphabetically' } }], - }, - - // interfaces option + class expression + alphabetically --> Default order applies - { - code: ` -const foo = class Foo { - public static a: string; - protected static b: string = ""; - private static c: string = ""; - - public d: string = ""; - protected e: string = ""; - private f: string = ""; - - constructor() {} -} - `, - options: [{ interfaces: { order: 'alphabetically' } }], - }, - ], - invalid: [ - // default option + interface + wrong order within group and wrong group order + alphabetically - { - code: ` -interface Foo { - [a: string] : number; - - a : x; - b : x; - c : x; - - c() : void; - b() : void; - a() : void; - - () : Baz; - - new () : Bar; -} - `, - options: [ - { default: { memberTypes: defaultOrder, order: 'alphabetically' } }, - ], - errors: [ - { - messageId: 'incorrectGroupOrder', - data: { - name: 'call', - rank: 'field', - }, - }, - { - messageId: 'incorrectGroupOrder', - data: { - name: 'new', - rank: 'method', - }, - }, - ], - }, - ], -}; - -const sortedWithGroupingTypeLiteralsOption: TSESLint.RunTests< - MessageIds, - Options -> = { - valid: [ - // typeLiterals option + interface + alphabetically --> Default order applies - { - code: ` -interface Foo { - [a: string] : number; - - () : Baz; - - c : x; - b : x; - a : x; - - new () : Bar; - - c() : void; - b() : void; - a() : void; -} - `, - options: [{ typeLiterals: { order: 'alphabetically' } }], - }, - - // typeLiterals option + type literal + default order + alphabetically - { - code: ` -type Foo = { - [a: string] : number; - - a : x; - b : x; - c : x; - - a() : void; - b() : void; - c() : void; - - new () : Bar; - - () : Baz; -} - `, - options: [ - { - typeLiterals: { - memberTypes: ['signature', 'field', 'method', 'constructor'], - order: 'alphabetically', - }, - }, - ], - }, - - // typeLiterals option + type literal + custom order + alphabetically - { - code: ` -type Foo = { - new () : Bar; - - a() : void; - b() : void; - c() : void; - - a : x; - b : x; - c : x; - - [a: string] : number; - () : Baz; -} - `, - options: [ - { - typeLiterals: { - memberTypes: ['constructor', 'method', 'field'], - order: 'alphabetically', - }, - }, - ], - }, - - // typeLiterals option + class + alphabetically --> Default order applies - { - code: ` -class Foo { - public static a: string; - protected static b: string = ""; - private static c: string = ""; - - public d: string = ""; - protected e: string = ""; - private f: string = ""; - - constructor() {} -} - `, - options: [{ typeLiterals: { order: 'alphabetically' } }], - }, - - // typeLiterals option + class expression + alphabetically --> Default order applies - { - code: ` -const foo = class Foo { - public static a: string; - protected static b: string = ""; - private static c: string = ""; - - public d: string = ""; - protected e: string = ""; - private f: string = ""; - - constructor() {} -} - `, - options: [{ typeLiterals: { order: 'alphabetically' } }], - }, - ], - invalid: [ - // default option + type literal + wrong order within group and wrong group order + alphabetically - { - code: ` -type Foo = { - [a: string] : number; - - a : x; - b : x; - c : x; - - c() : void; - b() : void; - a() : void; - - () : Baz; - - new () : Bar; -} - `, - options: [ - { default: { memberTypes: defaultOrder, order: 'alphabetically' } }, - ], - errors: [ - { - messageId: 'incorrectGroupOrder', - data: { - name: 'call', - rank: 'field', - }, - }, - { - messageId: 'incorrectGroupOrder', - data: { - name: 'new', - rank: 'method', - }, - }, - ], - }, - - // default option + private identifiers - { - code: ` -class Foo { - #c = 3; - #b = 2; - #a = 1; -} - `, - options: [ - { default: { memberTypes: defaultOrder, order: 'alphabetically' } }, - ], - errors: [ - { - messageId: 'incorrectOrder', - line: 4, - column: 3, - }, - { - messageId: 'incorrectOrder', - line: 5, - column: 3, - }, - ], - }, - ], -}; - -const sortedWithoutGrouping = { - valid: [ - ...sortedWithoutGroupingDefaultOption.valid, - ...sortedWithoutGroupingClassesOption.valid, - ...sortedWithoutGroupingClassExpressionsOption.valid, - ...sortedWithoutGroupingInterfacesOption.valid, - ...sortedWithoutGroupingTypeLiteralsOption.valid, - ], - invalid: [ - ...sortedWithoutGroupingDefaultOption.invalid, - ...sortedWithoutGroupingClassesOption.invalid, - ...sortedWithoutGroupingClassExpressionsOption.invalid, - ...sortedWithoutGroupingInterfacesOption.invalid, - ...sortedWithoutGroupingTypeLiteralsOption.invalid, - ], -}; - -const sortedWithGrouping = { - valid: [ - ...sortedWithGroupingDefaultOption.valid, - ...sortedWithGroupingClassesOption.valid, - ...sortedWithGroupingClassExpressionsOption.valid, - ...sortedWithGroupingInterfacesOption.valid, - ...sortedWithGroupingTypeLiteralsOption.valid, - ], - invalid: [ - ...sortedWithGroupingDefaultOption.invalid, - ...sortedWithGroupingClassesOption.invalid, - ...sortedWithGroupingClassExpressionsOption.invalid, - ...sortedWithGroupingInterfacesOption.invalid, - ...sortedWithGroupingTypeLiteralsOption.invalid, - ], -}; - -ruleTester.run('member-ordering', rule, { - valid: [ - ...grouped.valid, - ...sortedWithoutGrouping.valid, - ...sortedWithGrouping.valid, - ], - invalid: [ - ...grouped.invalid, - ...sortedWithoutGrouping.invalid, - ...sortedWithGrouping.invalid, - ], -}); +ruleTester.run('member-ordering', rule, grouped); diff --git a/packages/eslint-plugin/tests/rules/no-var-requires.test.ts b/packages/eslint-plugin/tests/rules/no-var-requires.test.ts index 8c6ef638a16c..1e38c482218d 100644 --- a/packages/eslint-plugin/tests/rules/no-var-requires.test.ts +++ b/packages/eslint-plugin/tests/rules/no-var-requires.test.ts @@ -10,6 +10,11 @@ ruleTester.run('no-var-requires', rule, { "import foo = require('foo');", "require('foo');", "require?.('foo');", + ` +import { createRequire } from 'module'; +const require = createRequire('foo'); +const json = require('./some.json'); + `, ], invalid: [ { diff --git a/packages/eslint-plugin/tests/rules/prefer-for-of.test.ts b/packages/eslint-plugin/tests/rules/prefer-for-of.test.ts index d2d7319b3b1d..02c7d6478f03 100644 --- a/packages/eslint-plugin/tests/rules/prefer-for-of.test.ts +++ b/packages/eslint-plugin/tests/rules/prefer-for-of.test.ts @@ -180,6 +180,16 @@ for (var c = 0; c < arr.length; c++) { ` for (var d = 0; d < arr.length; d++) doMath?.(d); `, + ` +for (let i = 0; i < test.length; ++i) { + this[i]; +} + `, + ` +for (let i = 0; i < this.length; ++i) { + yield this[i]; +} + `, ], invalid: [ { @@ -357,6 +367,30 @@ for (let i = 0; i < arr.length; i++) { code: ` for (let i = 0; i < arr.length; i++) { ({ foo: obj[arr[i]] } = { foo: 1 }); +} + `, + errors: [ + { + messageId: 'preferForOf', + }, + ], + }, + { + code: ` +for (let i = 0; i < this.item.length; ++i) { + this.item[i]; +} + `, + errors: [ + { + messageId: 'preferForOf', + }, + ], + }, + { + code: ` +for (let i = 0; i < this.array.length; ++i) { + yield this.array[i]; } `, errors: [ diff --git a/packages/eslint-plugin/tests/rules/require-await.test.ts b/packages/eslint-plugin/tests/rules/require-await.test.ts index ec2b178ecbb4..f385bc8402a2 100644 --- a/packages/eslint-plugin/tests/rules/require-await.test.ts +++ b/packages/eslint-plugin/tests/rules/require-await.test.ts @@ -159,6 +159,57 @@ async function* asyncGenerator() { } async function* test1() { yield* asyncGenerator(); +} + `, + ` +async function* asyncGenerator() { + await Promise.resolve(); + yield 1; +} +async function* test1() { + yield* asyncGenerator(); + yield* 2; +} + `, + ` +async function* test(source: AsyncIterable) { + yield* source; +} + `, + ` +async function* test(source: Iterable & AsyncIterable) { + yield* source; +} + `, + ` +async function* test(source: Iterable | AsyncIterable) { + yield* source; +} + `, + ` +type MyType = { + [Symbol.iterator](): Iterator; + [Symbol.asyncIterator](): AsyncIterator; +}; +async function* test(source: MyType) { + yield* source; +} + `, + ` +type MyType = { + [Symbol.asyncIterator]: () => AsyncIterator; +}; +async function* test(source: MyType) { + yield* source; +} + `, + ` +type MyFunctionType = () => AsyncIterator; +type MyType = { + [Symbol.asyncIterator]: MyFunctionType; +}; +async function* test(source: MyType) { + yield* source; } `, 'const foo: () => void = async function* () {};', @@ -294,6 +345,41 @@ async function* asyncGenerator() { }, { code: ` +async function* asyncGenerator(source: Iterable) { + yield* source; +} + `, + errors: [ + { + messageId: 'missingAwait', + data: { + name: "Async generator function 'asyncGenerator'", + }, + }, + ], + }, + { + code: ` +function isAsyncIterable(value: unknown): value is AsyncIterable { + return true; +} +async function* asyncGenerator(source: Iterable | AsyncIterable) { + if (!isAsyncIterable(source)) { + yield* source; + } +} + `, + errors: [ + { + messageId: 'missingAwait', + data: { + name: "Async generator function 'asyncGenerator'", + }, + }, + ], + }, + { + code: ` function* syncGenerator() { yield 1; } diff --git a/packages/experimental-utils/CHANGELOG.md b/packages/experimental-utils/CHANGELOG.md index cf30afab5028..10256e8efef0 100644 --- a/packages/experimental-utils/CHANGELOG.md +++ b/packages/experimental-utils/CHANGELOG.md @@ -3,6 +3,18 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [5.5.0](https://github.com/typescript-eslint/typescript-eslint/compare/v5.4.0...v5.5.0) (2021-11-29) + + +### Bug Fixes + +* **experimental-utils:** export RuleCreator interfaces ([#4199](https://github.com/typescript-eslint/typescript-eslint/issues/4199)) ([7821e4c](https://github.com/typescript-eslint/typescript-eslint/commit/7821e4c515ca2f11a14dcfa94dc77370da0287c5)) +* **experimental-utils:** fix types for eslint-utils ([#4173](https://github.com/typescript-eslint/typescript-eslint/issues/4173)) ([7079de2](https://github.com/typescript-eslint/typescript-eslint/commit/7079de26877a2313a7019845d4c33d0fc4d4b4a9)) + + + + + # [5.4.0](https://github.com/typescript-eslint/typescript-eslint/compare/v5.3.1...v5.4.0) (2021-11-15) diff --git a/packages/experimental-utils/README.md b/packages/experimental-utils/README.md index 208578bfc113..7f9f6b242078 100644 --- a/packages/experimental-utils/README.md +++ b/packages/experimental-utils/README.md @@ -16,7 +16,7 @@ i.e. treat it as a `0.x.y` package. Feel free to use it now, and let us know what utilities you need or send us PRs with utilities you build on top of it. -Once it is stable, it will be renamed to `@typescript-eslint/util` for a `4.0.0` release. +Once it is stable, it will be renamed to `@typescript-eslint/util` for a `6.0.0` release. ## Exports diff --git a/packages/experimental-utils/package.json b/packages/experimental-utils/package.json index de02417cddd9..bb36d8c602f9 100644 --- a/packages/experimental-utils/package.json +++ b/packages/experimental-utils/package.json @@ -1,6 +1,6 @@ { "name": "@typescript-eslint/experimental-utils", - "version": "5.4.0", + "version": "5.5.0", "description": "(Experimental) Utilities for working with TypeScript + ESLint together", "keywords": [ "eslint", @@ -40,9 +40,9 @@ }, "dependencies": { "@types/json-schema": "^7.0.9", - "@typescript-eslint/scope-manager": "5.4.0", - "@typescript-eslint/types": "5.4.0", - "@typescript-eslint/typescript-estree": "5.4.0", + "@typescript-eslint/scope-manager": "5.5.0", + "@typescript-eslint/types": "5.5.0", + "@typescript-eslint/typescript-estree": "5.5.0", "eslint-scope": "^5.1.1", "eslint-utils": "^3.0.0" }, diff --git a/packages/experimental-utils/src/ast-utils/eslint-utils/ReferenceTracker.ts b/packages/experimental-utils/src/ast-utils/eslint-utils/ReferenceTracker.ts index 40e5fa7c1793..a8d1acb3ca92 100644 --- a/packages/experimental-utils/src/ast-utils/eslint-utils/ReferenceTracker.ts +++ b/packages/experimental-utils/src/ast-utils/eslint-utils/ReferenceTracker.ts @@ -83,7 +83,7 @@ namespace ReferenceTracker { node: TSESTree.Node; path: readonly string[]; type: ReferenceType; - entry: T; + info: T; } } diff --git a/packages/experimental-utils/src/ast-utils/eslint-utils/astUtilities.ts b/packages/experimental-utils/src/ast-utils/eslint-utils/astUtilities.ts index 8666ba8704de..69f6d5e0190e 100644 --- a/packages/experimental-utils/src/ast-utils/eslint-utils/astUtilities.ts +++ b/packages/experimental-utils/src/ast-utils/eslint-utils/astUtilities.ts @@ -25,6 +25,7 @@ const getFunctionNameWithKind = eslintUtils.getFunctionNameWithKind as ( | TSESTree.FunctionDeclaration | TSESTree.FunctionExpression | TSESTree.ArrowFunctionExpression, + sourceCode?: TSESLint.SourceCode, ) => string; /** @@ -38,7 +39,8 @@ const getPropertyName = eslintUtils.getPropertyName as ( node: | TSESTree.MemberExpression | TSESTree.Property - | TSESTree.MethodDefinition, + | TSESTree.MethodDefinition + | TSESTree.PropertyDefinition, initialScope?: TSESLint.Scope.Scope, ) => string | null; diff --git a/packages/experimental-utils/src/eslint-utils/RuleCreator.ts b/packages/experimental-utils/src/eslint-utils/RuleCreator.ts index bfbd54f22467..2bd24323e782 100644 --- a/packages/experimental-utils/src/eslint-utils/RuleCreator.ts +++ b/packages/experimental-utils/src/eslint-utils/RuleCreator.ts @@ -8,12 +8,12 @@ import { import { applyDefault } from './applyDefault'; // we automatically add the url -type NamedCreateRuleMetaDocs = Omit; -type NamedCreateRuleMeta = { +export type NamedCreateRuleMetaDocs = Omit; +export type NamedCreateRuleMeta = { docs: NamedCreateRuleMetaDocs; } & Omit, 'docs'>; -interface CreateAndOptions< +export interface RuleCreateAndOptions< TOptions extends readonly unknown[], TMessageIds extends string, TRuleListener extends RuleListener, @@ -25,19 +25,19 @@ interface CreateAndOptions< defaultOptions: Readonly; } -interface RuleWithMeta< +export interface RuleWithMeta< TOptions extends readonly unknown[], TMessageIds extends string, TRuleListener extends RuleListener, -> extends CreateAndOptions { +> extends RuleCreateAndOptions { meta: RuleMetaData; } -interface RuleWithMetaAndName< +export interface RuleWithMetaAndName< TOptions extends readonly unknown[], TMessageIds extends string, TRuleListener extends RuleListener, -> extends CreateAndOptions { +> extends RuleCreateAndOptions { meta: NamedCreateRuleMeta; name: string; } @@ -48,7 +48,7 @@ interface RuleWithMetaAndName< * @param urlCreator Creates a documentation URL for a given rule name. * @returns Function to create a rule with the docs URL format. */ -function RuleCreator(urlCreator: (ruleName: string) => string) { +export function RuleCreator(urlCreator: (ruleName: string) => string) { // This function will get much easier to call when this is merged https://github.com/Microsoft/TypeScript/pull/26349 // TODO - when the above PR lands; add type checking for the context.report `data` property return function createNamedRule< @@ -106,5 +106,3 @@ function createRule< } RuleCreator.withoutDocs = createRule; - -export { RuleCreator }; diff --git a/packages/parser/CHANGELOG.md b/packages/parser/CHANGELOG.md index f1271aa86b0f..c653f6252fc2 100644 --- a/packages/parser/CHANGELOG.md +++ b/packages/parser/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [5.5.0](https://github.com/typescript-eslint/typescript-eslint/compare/v5.4.0...v5.5.0) (2021-11-29) + +**Note:** Version bump only for package @typescript-eslint/parser + + + + + # [5.4.0](https://github.com/typescript-eslint/typescript-eslint/compare/v5.3.1...v5.4.0) (2021-11-15) **Note:** Version bump only for package @typescript-eslint/parser diff --git a/packages/parser/README.md b/packages/parser/README.md index f928738ceb22..3eaa23b93f67 100644 --- a/packages/parser/README.md +++ b/packages/parser/README.md @@ -1,6 +1,6 @@

TypeScript ESLint Parser

-

An ESLint parser which leverages TypeScript ESTree to allow for ESLint to lint TypeScript source code.

+

An ESLint parser which leverages TypeScript ESTree to allow for ESLint to lint TypeScript source code.

CI @@ -10,7 +10,7 @@ ## Getting Started -**[You can find our Getting Started docs here](../../docs/getting-started/linting/README.md)** +**[You can find our Getting Started docs here](../../docs/linting/README.md)** These docs walk you through setting up ESLint, this parser, and our plugin. If you know what you're doing and just want to quick start, read on... @@ -41,7 +41,7 @@ The core rules built into ESLint, such as `indent` have no knowledge of such con Instead, you also need to make use of one more plugins which will add or extend rules with TypeScript-specific features. -By far the most common case will be installing the [`@typescript-eslint/eslint-plugin`](https://github.com/typescript-eslint/typescript-eslint/tree/master/packages/eslint-plugin) plugin, but there are also other relevant options available such a [`@typescript-eslint/eslint-plugin-tslint`](https://github.com/typescript-eslint/typescript-eslint/tree/master/packages/eslint-plugin-tslint). +By far the most common case will be installing the [`@typescript-eslint/eslint-plugin`](https://github.com/typescript-eslint/typescript-eslint/tree/main/packages/eslint-plugin) plugin, but there are also other relevant options available such a [`@typescript-eslint/eslint-plugin-tslint`](https://github.com/typescript-eslint/typescript-eslint/tree/main/packages/eslint-plugin-tslint). ## Configuration diff --git a/packages/parser/package.json b/packages/parser/package.json index 77faaa2a13d5..a73757d0ebf8 100644 --- a/packages/parser/package.json +++ b/packages/parser/package.json @@ -1,6 +1,6 @@ { "name": "@typescript-eslint/parser", - "version": "5.4.0", + "version": "5.5.0", "description": "An ESLint custom parser which leverages TypeScript ESTree", "main": "dist/index.js", "types": "dist/index.d.ts", @@ -44,14 +44,14 @@ "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" }, "dependencies": { - "@typescript-eslint/scope-manager": "5.4.0", - "@typescript-eslint/types": "5.4.0", - "@typescript-eslint/typescript-estree": "5.4.0", + "@typescript-eslint/scope-manager": "5.5.0", + "@typescript-eslint/types": "5.5.0", + "@typescript-eslint/typescript-estree": "5.5.0", "debug": "^4.3.2" }, "devDependencies": { "@types/glob": "*", - "@typescript-eslint/experimental-utils": "5.4.0", + "@typescript-eslint/experimental-utils": "5.5.0", "glob": "*", "typescript": "*" }, diff --git a/packages/scope-manager/CHANGELOG.md b/packages/scope-manager/CHANGELOG.md index d79959c2b011..8e741b1a6ebb 100644 --- a/packages/scope-manager/CHANGELOG.md +++ b/packages/scope-manager/CHANGELOG.md @@ -3,6 +3,17 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [5.5.0](https://github.com/typescript-eslint/typescript-eslint/compare/v5.4.0...v5.5.0) (2021-11-29) + + +### Bug Fixes + +* **scope-manager:** support static class blocks ([#4211](https://github.com/typescript-eslint/typescript-eslint/issues/4211)) ([f8e9125](https://github.com/typescript-eslint/typescript-eslint/commit/f8e91256e0a721aaa906a5c40a92784da9433b53)) + + + + + # [5.4.0](https://github.com/typescript-eslint/typescript-eslint/compare/v5.3.1...v5.4.0) (2021-11-15) diff --git a/packages/scope-manager/README.md b/packages/scope-manager/README.md index 5e62019ba877..26d08ac2fd7f 100644 --- a/packages/scope-manager/README.md +++ b/packages/scope-manager/README.md @@ -14,7 +14,7 @@ You probably don't want to use it directly. ## Getting Started -**[You can find our Getting Started docs here](../../docs/getting-started/linting/README.md)** +**[You can find our Getting Started docs here](../../docs/linting/README.md)** ## Installation diff --git a/packages/scope-manager/package.json b/packages/scope-manager/package.json index e5f670a4ecde..bd07f5e19e1d 100644 --- a/packages/scope-manager/package.json +++ b/packages/scope-manager/package.json @@ -1,6 +1,6 @@ { "name": "@typescript-eslint/scope-manager", - "version": "5.4.0", + "version": "5.5.0", "description": "TypeScript scope analyser for ESLint", "keywords": [ "eslint", @@ -39,12 +39,12 @@ "typecheck": "tsc -p tsconfig.json --noEmit" }, "dependencies": { - "@typescript-eslint/types": "5.4.0", - "@typescript-eslint/visitor-keys": "5.4.0" + "@typescript-eslint/types": "5.5.0", + "@typescript-eslint/visitor-keys": "5.5.0" }, "devDependencies": { "@types/glob": "*", - "@typescript-eslint/typescript-estree": "5.4.0", + "@typescript-eslint/typescript-estree": "5.5.0", "glob": "*", "jest-specific-snapshot": "*", "make-dir": "*", diff --git a/packages/scope-manager/src/ScopeManager.ts b/packages/scope-manager/src/ScopeManager.ts index 3f360710cb0b..5a53fe6fbf8c 100644 --- a/packages/scope-manager/src/ScopeManager.ts +++ b/packages/scope-manager/src/ScopeManager.ts @@ -20,6 +20,7 @@ import { WithScope, } from './scope'; import { ClassFieldInitializerScope } from './scope/ClassFieldInitializerScope'; +import { ClassStaticBlockScope } from './scope/ClassStaticBlockScope'; import { Variable } from './variable'; @@ -178,6 +179,15 @@ class ScopeManager { ); } + public nestClassStaticBlockScope( + node: ClassStaticBlockScope['block'], + ): ClassStaticBlockScope { + assert(this.currentScope); + return this.nestScope( + new ClassStaticBlockScope(this, this.currentScope, node), + ); + } + public nestConditionalTypeScope( node: ConditionalTypeScope['block'], ): ConditionalTypeScope { diff --git a/packages/scope-manager/src/scope/ClassStaticBlockScope.ts b/packages/scope-manager/src/scope/ClassStaticBlockScope.ts new file mode 100644 index 000000000000..40a5d54b37cf --- /dev/null +++ b/packages/scope-manager/src/scope/ClassStaticBlockScope.ts @@ -0,0 +1,21 @@ +import { TSESTree } from '@typescript-eslint/types'; +import { Scope } from './Scope'; +import { ScopeBase } from './ScopeBase'; +import { ScopeType } from './ScopeType'; +import { ScopeManager } from '../ScopeManager'; + +class ClassStaticBlockScope extends ScopeBase< + ScopeType.classStaticBlock, + TSESTree.Expression, + Scope +> { + constructor( + scopeManager: ScopeManager, + upperScope: ClassStaticBlockScope['upper'], + block: ClassStaticBlockScope['block'], + ) { + super(scopeManager, ScopeType.classStaticBlock, upperScope, block, false); + } +} + +export { ClassStaticBlockScope }; diff --git a/packages/scope-manager/src/scope/Scope.ts b/packages/scope-manager/src/scope/Scope.ts index 966e52a9456a..e6237a09119e 100644 --- a/packages/scope-manager/src/scope/Scope.ts +++ b/packages/scope-manager/src/scope/Scope.ts @@ -1,6 +1,7 @@ import { BlockScope } from './BlockScope'; import { CatchScope } from './CatchScope'; import { ClassFieldInitializerScope } from './ClassFieldInitializerScope'; +import { ClassStaticBlockScope } from './ClassStaticBlockScope'; import { ClassScope } from './ClassScope'; import { ConditionalTypeScope } from './ConditionalTypeScope'; import { ForScope } from './ForScope'; @@ -21,6 +22,7 @@ type Scope = | CatchScope | ClassScope | ClassFieldInitializerScope + | ClassStaticBlockScope | ConditionalTypeScope | ForScope | FunctionExpressionNameScope diff --git a/packages/scope-manager/src/scope/ScopeBase.ts b/packages/scope-manager/src/scope/ScopeBase.ts index 34c9a338b5b1..18196d9cce99 100644 --- a/packages/scope-manager/src/scope/ScopeBase.ts +++ b/packages/scope-manager/src/scope/ScopeBase.ts @@ -126,6 +126,7 @@ const generator = createIdGenerator(); type VariableScope = GlobalScope | FunctionScope | ModuleScope | TSModuleScope; const VARIABLE_SCOPE_TYPES = new Set([ ScopeType.classFieldInitializer, + ScopeType.classStaticBlock, ScopeType.function, ScopeType.global, ScopeType.module, diff --git a/packages/scope-manager/src/scope/ScopeType.ts b/packages/scope-manager/src/scope/ScopeType.ts index 8a63ffbdea7f..cd8bb1a61bde 100644 --- a/packages/scope-manager/src/scope/ScopeType.ts +++ b/packages/scope-manager/src/scope/ScopeType.ts @@ -3,6 +3,7 @@ enum ScopeType { catch = 'catch', class = 'class', classFieldInitializer = 'class-field-initializer', + classStaticBlock = 'class-static-block', conditionalType = 'conditionalType', for = 'for', function = 'function', diff --git a/packages/shared-fixtures/CHANGELOG.md b/packages/shared-fixtures/CHANGELOG.md index 11566177cce0..d4bb6943fabe 100644 --- a/packages/shared-fixtures/CHANGELOG.md +++ b/packages/shared-fixtures/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [5.5.0](https://github.com/typescript-eslint/typescript-eslint/compare/v5.4.0...v5.5.0) (2021-11-29) + +**Note:** Version bump only for package @typescript-eslint/shared-fixtures + + + + + # [5.4.0](https://github.com/typescript-eslint/typescript-eslint/compare/v5.3.1...v5.4.0) (2021-11-15) diff --git a/packages/shared-fixtures/package.json b/packages/shared-fixtures/package.json index 427749abb174..281da3efab4a 100644 --- a/packages/shared-fixtures/package.json +++ b/packages/shared-fixtures/package.json @@ -1,5 +1,5 @@ { "name": "@typescript-eslint/shared-fixtures", - "version": "5.4.0", + "version": "5.5.0", "private": true } diff --git a/packages/types/CHANGELOG.md b/packages/types/CHANGELOG.md index 1528085b88a0..8fbc3556ff6e 100644 --- a/packages/types/CHANGELOG.md +++ b/packages/types/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [5.5.0](https://github.com/typescript-eslint/typescript-eslint/compare/v5.4.0...v5.5.0) (2021-11-29) + +**Note:** Version bump only for package @typescript-eslint/types + + + + + # [5.4.0](https://github.com/typescript-eslint/typescript-eslint/compare/v5.3.1...v5.4.0) (2021-11-15) **Note:** Version bump only for package @typescript-eslint/types diff --git a/packages/types/package.json b/packages/types/package.json index 89b70fa634d2..918fc8c600d7 100644 --- a/packages/types/package.json +++ b/packages/types/package.json @@ -1,6 +1,6 @@ { "name": "@typescript-eslint/types", - "version": "5.4.0", + "version": "5.5.0", "description": "Types for the TypeScript-ESTree AST spec", "keywords": [ "eslint", diff --git a/packages/typescript-estree/CHANGELOG.md b/packages/typescript-estree/CHANGELOG.md index db35c5bd78e9..713725fdc910 100644 --- a/packages/typescript-estree/CHANGELOG.md +++ b/packages/typescript-estree/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [5.5.0](https://github.com/typescript-eslint/typescript-eslint/compare/v5.4.0...v5.5.0) (2021-11-29) + +**Note:** Version bump only for package @typescript-eslint/typescript-estree + + + + + # [5.4.0](https://github.com/typescript-eslint/typescript-eslint/compare/v5.3.1...v5.4.0) (2021-11-15) diff --git a/packages/typescript-estree/README.md b/packages/typescript-estree/README.md index 25eb1e928294..a2827701b269 100644 --- a/packages/typescript-estree/README.md +++ b/packages/typescript-estree/README.md @@ -10,7 +10,7 @@ ## Getting Started -**[You can find our Getting Started docs here](../../docs/getting-started/linting/README.md)** +**[You can find our Getting Started docs here](../../docs/linting/README.md)** ## About @@ -72,7 +72,7 @@ interface ParseOptions { * NOTE: this setting does not effect known file types (.js, .jsx, .ts, .tsx, .json) because the * TypeScript compiler has its own internal handling for known file extensions. * - * For the exact behavior, see https://github.com/typescript-eslint/typescript-eslint/tree/master/packages/parser#parseroptionsecmafeaturesjsx + * For the exact behavior, see https://github.com/typescript-eslint/typescript-eslint/tree/main/packages/parser#parseroptionsecmafeaturesjsx */ jsx?: boolean; @@ -367,7 +367,7 @@ Please check the current list of open and known issues and ensure the issue has A couple of years after work on this parser began, the TypeScript Team at Microsoft began [officially supporting TypeScript parsing via Babel](https://blogs.msdn.microsoft.com/typescript/2018/08/27/typescript-and-babel-7/). -I work closely with the TypeScript Team and we are gradually aligning the AST of this project with the one produced by Babel's parser. To that end, I have created a full test harness to compare the ASTs of the two projects which runs on every PR, please see [the code](https://github.com/typescript-eslint/typescript-eslint/tree/master/packages/typescript-estree/tests/ast-alignment) for more details. +I work closely with the TypeScript Team and we are gradually aligning the AST of this project with the one produced by Babel's parser. To that end, I have created a full test harness to compare the ASTs of the two projects which runs on every PR, please see [the code](https://github.com/typescript-eslint/typescript-eslint/tree/main/packages/typescript-estree/tests/ast-alignment) for more details. ## Debugging diff --git a/packages/typescript-estree/package.json b/packages/typescript-estree/package.json index 9a88c15b723a..7003cb12fdc3 100644 --- a/packages/typescript-estree/package.json +++ b/packages/typescript-estree/package.json @@ -1,6 +1,6 @@ { "name": "@typescript-eslint/typescript-estree", - "version": "5.4.0", + "version": "5.5.0", "description": "A parser that converts TypeScript source code into an ESTree compatible form", "main": "dist/index.js", "types": "dist/index.d.ts", @@ -41,8 +41,8 @@ "typecheck": "tsc -p tsconfig.json --noEmit" }, "dependencies": { - "@typescript-eslint/types": "5.4.0", - "@typescript-eslint/visitor-keys": "5.4.0", + "@typescript-eslint/types": "5.5.0", + "@typescript-eslint/visitor-keys": "5.5.0", "debug": "^4.3.2", "globby": "^11.0.4", "is-glob": "^4.0.3", @@ -59,7 +59,7 @@ "@types/is-glob": "*", "@types/semver": "*", "@types/tmp": "*", - "@typescript-eslint/shared-fixtures": "5.4.0", + "@typescript-eslint/shared-fixtures": "5.5.0", "glob": "*", "jest-specific-snapshot": "*", "make-dir": "*", diff --git a/packages/typescript-estree/src/parser-options.ts b/packages/typescript-estree/src/parser-options.ts index fdd71cc55b55..77af781a2588 100644 --- a/packages/typescript-estree/src/parser-options.ts +++ b/packages/typescript-estree/src/parser-options.ts @@ -70,7 +70,7 @@ interface ParseOptions { * NOTE: this setting does not effect known file types (.js, .jsx, .ts, .tsx, .json) because the * TypeScript compiler has its own internal handling for known file extensions. * - * For the exact behavior, see https://github.com/typescript-eslint/typescript-eslint/tree/master/packages/parser#parseroptionsecmafeaturesjsx + * For the exact behavior, see https://github.com/typescript-eslint/typescript-eslint/tree/main/packages/parser#parseroptionsecmafeaturesjsx */ jsx?: boolean; diff --git a/packages/typescript-estree/tests/ast-alignment/utils.ts b/packages/typescript-estree/tests/ast-alignment/utils.ts index 14a9f3213bcd..186eae71efea 100644 --- a/packages/typescript-estree/tests/ast-alignment/utils.ts +++ b/packages/typescript-estree/tests/ast-alignment/utils.ts @@ -263,24 +263,6 @@ export function preprocessBabylonAST(ast: File): any { Object.keys(node).forEach(key => delete node[key]); Object.assign(node, typeAnnotation); }, - /* - * Babel's AST has no `assertions` property if there are no assertions. - */ - ImportDeclaration(node) { - if (!node.assertions) { - node.assertions = []; - } - }, - ExportNamedDeclaration(node) { - if (!node.assertions) { - node.assertions = []; - } - }, - ExportAllDeclaration(node) { - if (!node.assertions) { - node.assertions = []; - } - }, }, ); } diff --git a/packages/visitor-keys/CHANGELOG.md b/packages/visitor-keys/CHANGELOG.md index ff7812258068..034a16fbc662 100644 --- a/packages/visitor-keys/CHANGELOG.md +++ b/packages/visitor-keys/CHANGELOG.md @@ -3,6 +3,17 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [5.5.0](https://github.com/typescript-eslint/typescript-eslint/compare/v5.4.0...v5.5.0) (2021-11-29) + + +### Bug Fixes + +* **visitor-keys:** add missing import assertion keys ([#4178](https://github.com/typescript-eslint/typescript-eslint/issues/4178)) ([9c38b7f](https://github.com/typescript-eslint/typescript-eslint/commit/9c38b7f7fc3ce471a8f720c4a2fbce01f3ee12a4)) + + + + + # [5.4.0](https://github.com/typescript-eslint/typescript-eslint/compare/v5.3.1...v5.4.0) (2021-11-15) diff --git a/packages/visitor-keys/package.json b/packages/visitor-keys/package.json index f10bda2222eb..d1ccf7556f5f 100644 --- a/packages/visitor-keys/package.json +++ b/packages/visitor-keys/package.json @@ -1,6 +1,6 @@ { "name": "@typescript-eslint/visitor-keys", - "version": "5.4.0", + "version": "5.5.0", "description": "Visitor keys used to help traverse the TypeScript-ESTree AST", "keywords": [ "eslint", @@ -38,7 +38,7 @@ "typecheck": "tsc -p tsconfig.json --noEmit" }, "dependencies": { - "@typescript-eslint/types": "5.4.0", + "@typescript-eslint/types": "5.5.0", "eslint-visitor-keys": "^3.0.0" }, "devDependencies": { diff --git a/packages/visitor-keys/src/visitor-keys.ts b/packages/visitor-keys/src/visitor-keys.ts index 4f160677e666..85e424308197 100644 --- a/packages/visitor-keys/src/visitor-keys.ts +++ b/packages/visitor-keys/src/visitor-keys.ts @@ -19,9 +19,6 @@ const additionalKeys: AdditionalKeys = { // Stage 3 Import Assertions ImportAttribute: ['key', 'value'], - // ES2020 - ImportExpression: ['source'], - // Additional Properties. ArrayPattern: ['decorators', 'elements', 'typeAnnotation'], ArrowFunctionExpression: ['typeParameters', 'params', 'returnType', 'body'], @@ -45,9 +42,13 @@ const additionalKeys: AdditionalKeys = { 'implements', 'body', ], + ExportAllDeclaration: ['exported', 'source', 'assertions'], + ExportNamedDeclaration: ['declaration', 'specifiers', 'source', 'assertions'], FunctionDeclaration: ['id', 'typeParameters', 'params', 'returnType', 'body'], FunctionExpression: ['id', 'typeParameters', 'params', 'returnType', 'body'], Identifier: ['decorators', 'typeAnnotation'], + ImportDeclaration: ['specifiers', 'source', 'assertions'], + ImportExpression: ['source', 'attributes'], MethodDefinition: ['decorators', 'key', 'value'], NewExpression: ['callee', 'typeParameters', 'arguments'], ObjectPattern: ['decorators', 'properties', 'typeAnnotation'], diff --git a/packages/website-eslint/CHANGELOG.md b/packages/website-eslint/CHANGELOG.md new file mode 100644 index 000000000000..55ef091566e5 --- /dev/null +++ b/packages/website-eslint/CHANGELOG.md @@ -0,0 +1,8 @@ +# Change Log + +All notable changes to this project will be documented in this file. +See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +# [5.5.0](https://github.com/typescript-eslint/typescript-eslint/compare/v5.4.0...v5.5.0) (2021-11-29) + +**Note:** Version bump only for package @typescript-eslint/website-eslint diff --git a/packages/website-eslint/package.json b/packages/website-eslint/package.json new file mode 100644 index 000000000000..03a3ac2af9fe --- /dev/null +++ b/packages/website-eslint/package.json @@ -0,0 +1,36 @@ +{ + "name": "@typescript-eslint/website-eslint", + "version": "5.5.0", + "private": true, + "description": "ESLint which works in browsers.", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "types": "types/index.d.ts", + "main": "dist/index.js", + "files": [ + "dist" + ], + "scripts": { + "build": "rollup --config=rollup.config.js", + "format": "prettier --write \"./**/*.{ts,js,json,md}\" --ignore-path ../../.prettierignore" + }, + "dependencies": { + "@typescript-eslint/experimental-utils": "5.5.0", + "@typescript-eslint/types": "5.5.0" + }, + "devDependencies": { + "@rollup/plugin-commonjs": "^21.0.1", + "@rollup/plugin-json": "^4.1.0", + "@rollup/plugin-node-resolve": "^13.0.6", + "@rollup/pluginutils": "^3.1.0", + "@typescript-eslint/eslint-plugin": "5.5.0", + "@typescript-eslint/parser": "5.5.0", + "@typescript-eslint/scope-manager": "5.5.0", + "@typescript-eslint/typescript-estree": "5.5.0", + "@typescript-eslint/visitor-keys": "5.5.0", + "eslint": "*", + "rollup": "^2.59.0", + "semver": "^7.3.5" + } +} diff --git a/packages/website-eslint/project.json b/packages/website-eslint/project.json new file mode 100644 index 000000000000..ac6ff89fcb81 --- /dev/null +++ b/packages/website-eslint/project.json @@ -0,0 +1,5 @@ +{ + "root": "packages/website-eslint", + "type": "library", + "implicitDependencies": [] +} diff --git a/packages/website-eslint/rollup-plugin/replace.js b/packages/website-eslint/rollup-plugin/replace.js new file mode 100644 index 000000000000..b8f3143c9050 --- /dev/null +++ b/packages/website-eslint/rollup-plugin/replace.js @@ -0,0 +1,94 @@ +const path = require('path'); +const Module = require('module'); +const rollupPluginUtils = require('@rollup/pluginutils'); +const MagicString = require('magic-string'); + +function toAbsolute(id) { + return id.startsWith('./') ? path.resolve(id) : require.resolve(id); +} + +function log(opts, message, type = 'info') { + if (opts.verbose) { + console.log('rollup-plugin-replace > [' + type + ']', message); + } +} + +function createMatcher(it) { + if (typeof it === 'function') { + return it; + } else { + return rollupPluginUtils.createFilter(it); + } +} + +module.exports = (options = {}) => { + const aliasesCache = new Map(); + const aliases = (options.alias || []).map(item => { + return { + match: item.match, + matcher: createMatcher(item.match), + target: item.target, + absoluteTarget: toAbsolute(item.target), + }; + }); + const replaces = (options.replace || []).map(item => { + return { + match: item.match, + test: item.test, + replace: + typeof item.replace === 'string' ? () => item.replace : item.replace, + + matcher: createMatcher(item.match), + }; + }); + + return { + name: 'rollup-plugin-replace', + resolveId(id, importerPath) { + const importeePath = + id.startsWith('./') || id.startsWith('../') + ? Module.createRequire(importerPath).resolve(id) + : id; + + let result = aliasesCache.get(importeePath); + if (result) { + return result; + } + + result = aliases.find(item => item.matcher(importeePath)); + if (result) { + aliasesCache.set(importeePath, result.absoluteTarget); + log(options, `${importeePath} as ${result.target}`, 'resolve'); + return result.absoluteTarget; + } + + return null; + }, + transform(code, id) { + let hasReplacements = false; + let magicString = new MagicString(code); + + replaces.forEach(item => { + if (item.matcher && !item.matcher(id)) { + return; + } + + let match = item.test.exec(code); + let start, end; + while (match) { + hasReplacements = true; + start = match.index; + end = start + match[0].length; + magicString.overwrite(start, end, item.replace(match)); + match = item.test.global ? item.test.exec(code) : null; + } + }); + + if (!hasReplacements) { + return; + } + log(options, id, 'replace'); + return { code: magicString.toString() }; + }, + }; +}; diff --git a/packages/website-eslint/rollup.config.js b/packages/website-eslint/rollup.config.js new file mode 100644 index 000000000000..f1edc2885126 --- /dev/null +++ b/packages/website-eslint/rollup.config.js @@ -0,0 +1,103 @@ +import commonjs from '@rollup/plugin-commonjs'; +import json from '@rollup/plugin-json'; +import resolve from '@rollup/plugin-node-resolve'; +const replace = require('./rollup-plugin/replace'); + +module.exports = { + input: 'src/linter/linter.js', + output: { + format: 'amd', + interop: 'auto', + freeze: false, + file: 'dist/index.js', + }, + external: ['vs/language/typescript/tsWorker'], + plugins: [ + replace({ + // verbose: true, + alias: [ + { + // those files should be omitted, we do not want them to be exposed to web + match: [ + /eslint\/lib\/(rule-tester|eslint|cli-engine|init)\//u, + /eslint\/lib\/cli\.js$/, + /experimental-utils\/dist\/eslint-utils\/RuleTester\.js$/, + /experimental-utils\/dist\/ts-eslint\/CLIEngine\.js$/, + /experimental-utils\/dist\/ts-eslint\/RuleTester\.js$/, + /typescript-estree\/dist\/create-program\/createWatchProgram\.js/, + /typescript-estree\/dist\/create-program\/createProjectProgram\.js/, + /typescript-estree\/dist\/create-program\/createIsolatedProgram\.js/, + /experimental-utils\/dist\/ts-eslint\/ESLint\.js/, + // 'eslint/lib/shared/ajv.js', + // 'eslint/lib/shared/runtime-info.js', + ], + target: './src/mock/empty.js', + }, + { + // use window.ts instead of bundling typescript + match: /typescript$/u, + target: './src/mock/typescript.js', + }, + { + // assert for web + match: /^assert$/u, + target: './src/mock/assert.js', + }, + { + // path for web + match: /^path$/u, + target: './src/mock/path.js', + }, + { + // util for web + match: /^util$/u, + target: './src/mock/util.js', + }, + { + // semver simplified, solve issue with circular dependencies + match: /semver$/u, + target: './src/mock/semver.js', + }, + ], + replace: [ + { + // we do not want dynamic imports + match: /eslint\/lib\/linter\/rules\.js$/u, + test: /require\(this\._rules\[ruleId\]\)/u, + replace: 'null', + }, + { + // esquery has both browser and node versions, we are bundling browser version that has different export + test: /esquery\.parse\(/u, + replace: 'esquery.default.parse(', + }, + { + // esquery has both browser and node versions, we are bundling browser version that has different export + test: /esquery\.matches\(/u, + replace: 'esquery.default.matches(', + }, + { + // replace all process.env.NODE_DEBUG with false + test: /process\.env\.NODE_DEBUG/u, + replace: 'false', + }, + { + // replace all process.env.TIMING with false + test: /process\.env\.TIMING/u, + replace: 'false', + }, + { + // replace all process.env.IGNORE_TEST_WIN32 with true + test: /process\.env\.IGNORE_TEST_WIN32/u, + replace: 'true', + }, + ], + }), + resolve({ + browser: true, + preferBuiltins: false, + }), + commonjs(), + json({ preferConst: true }), + ], +}; diff --git a/packages/website-eslint/src/linter/CompilerHost.js b/packages/website-eslint/src/linter/CompilerHost.js new file mode 100644 index 000000000000..2b7fdec25d07 --- /dev/null +++ b/packages/website-eslint/src/linter/CompilerHost.js @@ -0,0 +1,48 @@ +import { getDefaultLibFileName } from 'typescript'; + +export class CompilerHost { + constructor(files, sourceFiles) { + this.files = files; + this.sourceFiles = sourceFiles; + } + + fileExists(name) { + return !!this.files[name]; + } + + getCanonicalFileName(name) { + return name; + } + + getCurrentDirectory() { + return '/'; + } + + getDirectories() { + return []; + } + + getDefaultLibFileName(options) { + return '/' + getDefaultLibFileName(options); + } + + getNewLine() { + return '\n'; + } + + useCaseSensitiveFileNames() { + return true; + } + + writeFile() { + return null; + } + + readFile(name) { + return this.files[name]; + } + + getSourceFile(name) { + return this.sourceFiles[name]; + } +} diff --git a/packages/website-eslint/src/linter/config.js b/packages/website-eslint/src/linter/config.js new file mode 100644 index 000000000000..06e9ec28b3e7 --- /dev/null +++ b/packages/website-eslint/src/linter/config.js @@ -0,0 +1,24 @@ +export const extra = { + code: '', + comment: true, + comments: [], + createDefaultProgram: false, + debugLevel: new Set(), + errorOnTypeScriptSyntacticAndSemanticIssues: false, + errorOnUnknownASTType: false, + extraFileExtensions: [], + filePath: '', + jsx: false, + loc: true, + log: console.log, + preserveNodeMaps: true, + projects: [], + range: true, + strict: false, + tokens: [], + tsconfigRootDir: '/', + EXPERIMENTAL_useSourceOfProjectReferenceRedirect: false, + singleRun: false, + programs: null, + moduleResolver: '', +}; diff --git a/packages/website-eslint/src/linter/create-ast-program.js b/packages/website-eslint/src/linter/create-ast-program.js new file mode 100644 index 000000000000..ea0444fd2491 --- /dev/null +++ b/packages/website-eslint/src/linter/create-ast-program.js @@ -0,0 +1,44 @@ +import { + createProgram, + createSourceFile, + ScriptTarget, + ScriptKind, + JsxEmit, + ModuleKind, +} from 'typescript'; +import { CompilerHost } from './CompilerHost'; + +export function createASTProgram(code, parserOptions) { + const isJsx = !!parserOptions?.ecmaFeatures?.jsx; + const fileName = isJsx ? '/demo.tsx' : '/demo.ts'; + const files = { + [fileName]: code, + }; + const sourceFiles = { + [fileName]: createSourceFile( + fileName, + code, + ScriptTarget.Latest, + true, + isJsx ? ScriptKind.TSX : ScriptKind.TS, + ), + }; + const compilerHost = new CompilerHost(files, sourceFiles); + const compilerOptions = { + noResolve: true, + strict: true, + target: ScriptTarget.Latest, + jsx: isJsx ? JsxEmit.React : undefined, + module: ModuleKind.ES2015, + }; + const program = createProgram( + Object.keys(files), + compilerOptions, + compilerHost, + ); + const ast = program.getSourceFile(fileName); + return { + ast, + program, + }; +} diff --git a/packages/website-eslint/src/linter/linter.js b/packages/website-eslint/src/linter/linter.js new file mode 100644 index 000000000000..4b798674eeb6 --- /dev/null +++ b/packages/website-eslint/src/linter/linter.js @@ -0,0 +1,50 @@ +import 'vs/language/typescript/tsWorker'; +import { parseForESLint } from './parser'; +import { Linter } from 'eslint'; +import rules from '@typescript-eslint/eslint-plugin/dist/rules'; + +const PARSER_NAME = '@typescript-eslint/parser'; + +export function loadLinter() { + const linter = new Linter(); + let storedAST; + + linter.defineParser(PARSER_NAME, { + parseForESLint(code, options) { + const toParse = parseForESLint(code, options); + storedAST = toParse.ast; + return toParse; + }, // parse(code: string, options: ParserOptions): ParseForESLintResult['ast'] { + // const toParse = parseForESLint(code, options); + // storedAST = toParse.ast; + // return toParse.ast; + // }, + }); + + for (const name of Object.keys(rules)) { + linter.defineRule(`@typescript-eslint/${name}`, rules[name]); + } + + const ruleNames = Array.from(linter.getRules()).map(value => { + return { + name: value[0], + description: value[1]?.meta?.docs?.description, + }; + }); + + return { + ruleNames: ruleNames, + + getAst() { + return storedAST; + }, + + lint(code, parserOptions, rules) { + return linter.verify(code, { + parser: PARSER_NAME, + parserOptions, + rules, + }); + }, + }; +} diff --git a/packages/website-eslint/src/linter/parser.js b/packages/website-eslint/src/linter/parser.js new file mode 100644 index 000000000000..282f67c0da7a --- /dev/null +++ b/packages/website-eslint/src/linter/parser.js @@ -0,0 +1,47 @@ +import { analyze } from '@typescript-eslint/scope-manager/dist/analyze'; +import { visitorKeys } from '@typescript-eslint/visitor-keys/dist/visitor-keys'; +import { astConverter } from '@typescript-eslint/typescript-estree/dist/ast-converter'; +import { createASTProgram } from './create-ast-program.js'; +import { extra } from './config.js'; + +function parseAndGenerateServices(code, options) { + const { ast, program } = createASTProgram(code, options); + const { estree, astMaps } = astConverter( + ast, + { ...extra, code, jsx: options.jsx ?? false }, + true, + ); + + return { + ast: estree, + services: { + hasFullTypeInformation: true, + program, + esTreeNodeToTSNodeMap: astMaps.esTreeNodeToTSNodeMap, + tsNodeToESTreeNodeMap: astMaps.tsNodeToESTreeNodeMap, + }, + }; +} + +export function parseForESLint(code, parserOptions) { + const { ast, services } = parseAndGenerateServices(code, { + ...parserOptions, + jsx: parserOptions.ecmaFeatures?.jsx ?? false, + useJSXTextNode: true, + projectFolderIgnoreList: [], + }); + + const scopeManager = analyze(ast, { + ecmaVersion: + parserOptions.ecmaVersion === 'latest' ? 1e8 : parserOptions.ecmaVersion, + globalReturn: parserOptions.ecmaFeatures?.globalReturn ?? false, + sourceType: parserOptions.sourceType ?? 'script', + }); + + return { + ast, + services, + scopeManager, + visitorKeys, + }; +} diff --git a/packages/website-eslint/src/mock/assert.js b/packages/website-eslint/src/mock/assert.js new file mode 100644 index 000000000000..70cbf7a4fbce --- /dev/null +++ b/packages/website-eslint/src/mock/assert.js @@ -0,0 +1,107 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +// resolves . and .. elements in a path array with directory names there +// must be no slashes, empty elements, or device names (c:\) in the array +// (so also no leading and trailing slashes - it does not distinguish +// relative and absolute paths) +class AssertionError extends Error { + constructor(options) { + super(options); + + this.actual = options.actual; + this.expected = options.expected; + this.operator = options.operator; + if (options.message) { + this.message = options.message; + this.generatedMessage = false; + } else { + this.message = ''; + this.generatedMessage = true; + } + const stackStartFunction = options.stackStartFunction || fail; + if (Error.captureStackTrace) { + Error.captureStackTrace(this, stackStartFunction); + } else { + // non v8 browsers so we can have a stacktrace + const err = new Error(); + if (err.stack) { + let out = err.stack; + + // try to strip useless frames + const fn_name = + typeof stackStartFunction === 'function' + ? stackStartFunction.name + : stackStartFunction.toString(); + const idx = out.indexOf('\n' + fn_name); + if (idx >= 0) { + // once we have located the function frame + // we need to strip out everything before it (and its line) + const next_line = out.indexOf('\n', idx + 1); + out = out.substring(next_line + 1); + } + + this.stack = out; + } + } + } +} + +function fail(actual, expected, message, operator, stackStartFunction) { + throw new AssertionError({ + message: message, + actual: actual, + expected: expected, + operator: operator, + stackStartFunction: stackStartFunction, + }); +} + +function assert(value, message) { + if (!value) { + fail(value, true, message, '==', assert); + } +} +assert.equal = function equal(actual, expected, message) { + if (actual != expected) { + fail(actual, expected, message, '==', equal); + } +}; +assert.strictEqual = function strictEqual(actual, expected, message) { + if (actual !== expected) { + fail(actual, expected, message, '===', strictEqual); + } +}; +assert.notStrictEqual = function notStrictEqual(actual, expected, message) { + if (actual === expected) { + fail(actual, expected, message, '!==', notStrictEqual); + } +}; +assert.notEqual = function notEqual(actual, expected, message) { + if (actual == expected) { + fail(actual, expected, message, '!=', notEqual); + } +}; +assert.assert = assert.ok = assert; +assert.fail = fail; +assert.AssertionError = AssertionError; + +module.exports = assert; diff --git a/packages/website-eslint/src/mock/empty.js b/packages/website-eslint/src/mock/empty.js new file mode 100644 index 000000000000..ff8b4c56321a --- /dev/null +++ b/packages/website-eslint/src/mock/empty.js @@ -0,0 +1 @@ +export default {}; diff --git a/packages/website-eslint/src/mock/path.js b/packages/website-eslint/src/mock/path.js new file mode 100644 index 000000000000..3d04551e2e79 --- /dev/null +++ b/packages/website-eslint/src/mock/path.js @@ -0,0 +1,244 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +// resolves . and .. elements in a path array with directory names there +// must be no slashes, empty elements, or device names (c:\) in the array +// (so also no leading and trailing slashes - it does not distinguish +// relative and absolute paths) +function normalizeArray(parts, allowAboveRoot) { + // if the path tries to go above the root, `up` ends up > 0 + let up = 0; + for (let i = parts.length - 1; i >= 0; i--) { + const last = parts[i]; + if (last === '.') { + parts.splice(i, 1); + } else if (last === '..') { + parts.splice(i, 1); + up++; + } else if (up) { + parts.splice(i, 1); + up--; + } + } + + // if the path is allowed to go above the root, restore leading ..s + if (allowAboveRoot) { + for (; up--; up) { + parts.unshift('..'); + } + } + + return parts; +} + +// Split a filename into [root, dir, basename, ext], unix version +// 'root' is just a slash, or nothing. +const splitPathRe = + /^(\/?|)([\s\S]*?)((?:\.{1,2}|[^\/]+?|)(\.[^.\/]*|))(?:[\/]*)$/; +const splitPath = function (filename) { + return splitPathRe.exec(filename).slice(1); +}; + +// path.resolve([from ...], to) +// posix version +export function resolve() { + let resolvedPath = '', + resolvedAbsolute = false; + + for (let i = arguments.length - 1; i >= -1 && !resolvedAbsolute; i--) { + const path = i >= 0 ? arguments[i] : '/'; + + // Skip empty and invalid entries + if (typeof path !== 'string') { + throw new TypeError('Arguments to path.resolve must be strings'); + } else if (!path) { + continue; + } + + resolvedPath = path + '/' + resolvedPath; + resolvedAbsolute = path.charAt(0) === '/'; + } + + // At this point the path should be resolved to a full absolute path, but + // handle relative paths to be safe (might happen when process.cwd() fails) + + // Normalize the path + resolvedPath = normalizeArray( + filter(resolvedPath.split('/'), function (p) { + return !!p; + }), + !resolvedAbsolute, + ).join('/'); + + return (resolvedAbsolute ? '/' : '') + resolvedPath || '.'; +} + +// path.normalize(path) +// posix version +export function normalize(path) { + let isPathAbsolute = isAbsolute(path), + trailingSlash = substr(path, -1) === '/'; + + // Normalize the path + path = normalizeArray( + filter(path.split('/'), function (p) { + return !!p; + }), + !isPathAbsolute, + ).join('/'); + + if (!path && !isPathAbsolute) { + path = '.'; + } + if (path && trailingSlash) { + path += '/'; + } + + return (isPathAbsolute ? '/' : '') + path; +} + +// posix version +export function isAbsolute(path) { + return path.charAt(0) === '/'; +} + +// posix version +export function join() { + const paths = Array.prototype.slice.call(arguments, 0); + return normalize( + filter(paths, function (p, index) { + if (typeof p !== 'string') { + throw new TypeError('Arguments to path.join must be strings'); + } + return p; + }).join('/'), + ); +} + +// path.relative(from, to) +// posix version +export function relative(from, to) { + from = resolve(from).substr(1); + to = resolve(to).substr(1); + + function trim(arr) { + let start = 0; + for (; start < arr.length; start++) { + if (arr[start] !== '') break; + } + + var end = arr.length - 1; + for (; end >= 0; end--) { + if (arr[end] !== '') break; + } + + if (start > end) return []; + return arr.slice(start, end - start + 1); + } + + const fromParts = trim(from.split('/')); + const toParts = trim(to.split('/')); + + const length = Math.min(fromParts.length, toParts.length); + let samePartsLength = length; + for (let i = 0; i < length; i++) { + if (fromParts[i] !== toParts[i]) { + samePartsLength = i; + break; + } + } + + const outputParts = []; + for (let i = samePartsLength; i < fromParts.length; i++) { + outputParts.push('..'); + } + + outputParts = outputParts.concat(toParts.slice(samePartsLength)); + + return outputParts.join('/'); +} + +export var sep = '/'; +export var delimiter = ':'; + +export function dirname(path) { + const result = splitPath(path); + const root = result[0]; + let dir = result[1]; + + if (!root && !dir) { + // No dirname whatsoever + return '.'; + } + + if (dir) { + // It has a dirname, strip trailing slash + dir = dir.substr(0, dir.length - 1); + } + + return root + dir; +} + +export function basename(path, ext) { + let f = splitPath(path)[2]; + // TODO: make this comparison case-insensitive on windows? + if (ext && f.substr(-1 * ext.length) === ext) { + f = f.substr(0, f.length - ext.length); + } + return f; +} + +export function extname(path) { + return splitPath(path)[3]; +} + +export default { + extname: extname, + basename: basename, + dirname: dirname, + sep: sep, + delimiter: delimiter, + relative: relative, + join: join, + isAbsolute: isAbsolute, + normalize: normalize, + resolve: resolve, +}; + +function filter(xs, f) { + if (xs.filter) return xs.filter(f); + const res = []; + for (let i = 0; i < xs.length; i++) { + if (f(xs[i], i, xs)) res.push(xs[i]); + } + return res; +} + +// String.prototype.substr - negative index don't work in IE8 +const substr = + 'ab'.substr(-1) === 'b' + ? function (str, start, len) { + return str.substr(start, len); + } + : function (str, start, len) { + if (start < 0) start = str.length + start; + return str.substr(start, len); + }; diff --git a/packages/website-eslint/src/mock/semver.js b/packages/website-eslint/src/mock/semver.js new file mode 100644 index 000000000000..ba292927fe90 --- /dev/null +++ b/packages/website-eslint/src/mock/semver.js @@ -0,0 +1,4 @@ +import satisfies from 'semver/functions/satisfies'; +import major from 'semver/functions/major'; + +export { satisfies, major }; diff --git a/packages/website-eslint/src/mock/typescript.js b/packages/website-eslint/src/mock/typescript.js new file mode 100644 index 000000000000..324b844294b5 --- /dev/null +++ b/packages/website-eslint/src/mock/typescript.js @@ -0,0 +1 @@ +module.exports = window.ts; diff --git a/packages/website-eslint/src/mock/util.js b/packages/website-eslint/src/mock/util.js new file mode 100644 index 000000000000..3e5cd5e0e71b --- /dev/null +++ b/packages/website-eslint/src/mock/util.js @@ -0,0 +1,7 @@ +const util = {}; + +util.inspect = function (value) { + return value; +}; + +export default util; diff --git a/packages/website-eslint/types/index.d.ts b/packages/website-eslint/types/index.d.ts new file mode 100644 index 000000000000..eaa86160cba7 --- /dev/null +++ b/packages/website-eslint/types/index.d.ts @@ -0,0 +1,34 @@ +import type { TSESLint } from '@typescript-eslint/experimental-utils'; +import type { ParserOptions } from '@typescript-eslint/types'; + +export type LintMessage = TSESLint.Linter.LintMessage; +export type RuleFix = TSESLint.RuleFix; +export type RulesRecord = TSESLint.Linter.RulesRecord; +export type RuleEntry = TSESLint.Linter.RuleEntry; +export type ParseForESLintResult = TSESLint.Linter.ESLintParseResult; +export type ESLintAST = ParseForESLintResult['ast']; + +export interface WebLinter { + ruleNames: { name: string; description?: string }[]; + + getAst(): ESLintAST; + + lint( + code: string, + parserOptions: ParserOptions, + rules?: RulesRecord, + ): LintMessage[]; +} + +export interface LinterLoader { + loadLinter(): WebLinter; +} + +export type { TSESTree } from '@typescript-eslint/types'; + +export type { + DebugLevel, + EcmaVersion, + ParserOptions, + SourceType, +} from '@typescript-eslint/types'; diff --git a/packages/website/.eslintrc.js b/packages/website/.eslintrc.js new file mode 100644 index 000000000000..8b30fb624986 --- /dev/null +++ b/packages/website/.eslintrc.js @@ -0,0 +1,32 @@ +module.exports = { + extends: [ + '../../.eslintrc.js', + 'plugin:jsx-a11y/recommended', + 'plugin:react/recommended', + 'plugin:react-hooks/recommended', + ], + plugins: ['jsx-a11y', 'react', 'react-hooks'], + overrides: [ + { + files: [ + './src/pages/*.tsx', + './src/components/**/*.tsx', + './src/components/hooks/*.ts', + ], + rules: { + 'import/no-default-export': 'off', + }, + }, + ], + rules: { + 'react/jsx-no-target-blank': 'off', + 'react/no-unescaped-entities': 'off', + '@typescript-eslint/internal/prefer-ast-types-enum': 'off', + 'react-hooks/exhaustive-deps': 'off', // TODO: enable it later + }, + settings: { + react: { + version: 'detect', + }, + }, +}; diff --git a/packages/website/CHANGELOG.md b/packages/website/CHANGELOG.md index 00e80eef0adb..7ca28a97a50f 100644 --- a/packages/website/CHANGELOG.md +++ b/packages/website/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [5.5.0](https://github.com/typescript-eslint/typescript-eslint/compare/v5.4.0...v5.5.0) (2021-11-29) + +**Note:** Version bump only for package website + + + + + # [5.4.0](https://github.com/typescript-eslint/typescript-eslint/compare/v5.3.1...v5.4.0) (2021-11-15) diff --git a/packages/website/README.md b/packages/website/README.md index 725d0d7faee6..fd7bb57cb428 100644 --- a/packages/website/README.md +++ b/packages/website/README.md @@ -26,4 +26,4 @@ This command generates static content into the `build` directory and can be serv ## Deployment -The website is deployed automatically using Netlify. Each pull request into the master branch will have a unique preview deployment generated for it. +The website is deployed automatically using Netlify. Each pull request into the `main` branch will have a unique preview deployment generated for it. diff --git a/packages/website/docusaurus.config.js b/packages/website/docusaurus.config.js index f46afb187678..8eee26a28962 100644 --- a/packages/website/docusaurus.config.js +++ b/packages/website/docusaurus.config.js @@ -4,12 +4,12 @@ const lightCodeTheme = require('prism-react-renderer/themes/github'); const darkCodeTheme = require('prism-react-renderer/themes/dracula'); -const sponsors = require('./data/sponsors.json'); - const remarkPlugins = [ [require('@docusaurus/remark-plugin-npm2yarn'), { sync: true }], ]; +const beforeDefaultRemarkPlugins = [[require('remark-docusaurus-tabs'), {}]]; + const githubUrl = 'https://github.com/typescript-eslint/typescript-eslint'; /** @type {import('@docusaurus/types').Config} */ @@ -24,10 +24,8 @@ const config = { organizationName: 'typescript-eslint', projectName: 'typescript-eslint', clientModules: [require.resolve('./src/clientModules.js')], - customFields: { - sponsors, - }, plugins: [ + require.resolve('./webpack.plugin'), '@docusaurus/plugin-debug', [ '@docusaurus/theme-classic', @@ -35,7 +33,14 @@ const config = { customCss: require.resolve('./src/css/custom.css'), }, ], - ['@docusaurus/plugin-content-pages', { remarkPlugins }], + '@docusaurus/theme-search-algolia', + [ + '@docusaurus/plugin-content-pages', + { + beforeDefaultRemarkPlugins, + remarkPlugins, + }, + ], [ '@docusaurus/plugin-content-docs', { @@ -43,7 +48,8 @@ const config = { path: '../eslint-plugin/docs/rules', sidebarPath: require.resolve('./sidebars/sidebar.rules.js'), routeBasePath: 'rules', - editUrl: `${githubUrl}/edit/master/packages/website/`, + editUrl: `${githubUrl}/edit/main/packages/website/`, + beforeDefaultRemarkPlugins, remarkPlugins, }, ], @@ -54,22 +60,32 @@ const config = { path: '../../docs', routeBasePath: 'docs', sidebarPath: require.resolve('./sidebars/sidebar.base.js'), - editUrl: `${githubUrl}/edit/master/packages/website/`, + editUrl: `${githubUrl}/edit/main/packages/website/`, + beforeDefaultRemarkPlugins, remarkPlugins, }, ], ], themeConfig: - /** @type {import('@docusaurus/preset-classic').ThemeConfig} */ + /** @type {import('@docusaurus/theme-common').UserThemeConfig} */ ({ - // sidebarCollapsible: false, + algolia: { + appId: 'BH4D9OD16A', + apiKey: '1ad6b47d4e742c4c0653877b5511c602', + indexName: 'typescript-eslint', + }, + metadatas: [ + { name: 'msapplication-TileColor', content: '#443fd4' }, + { name: 'theme-color', content: '#443fd4' }, + ], navbar: { title: 'TypeScript ESLint', // hideOnScroll: true, logo: { alt: 'TypeScript ESLint', + height: '32px', src: 'img/logo.svg', - // srcDark: 'img/logo-dark.svg', + width: '32px', }, // style: 'primary', items: [ @@ -85,6 +101,12 @@ const config = { label: 'Rules', position: 'left', }, + { + to: 'play', + activeBasePath: 'play', + position: 'right', + label: 'Playground', + }, { href: githubUrl, position: 'right', @@ -105,21 +127,37 @@ const config = { prism: { theme: lightCodeTheme, darkTheme: darkCodeTheme, + additionalLanguages: ['ignore'], }, tableOfContents: { maxHeadingLevel: 4, minHeadingLevel: 2, }, }), + // Misleading API name, but these are just tags + stylesheets: [ + { + rel: 'apple-touch-icon', + sizes: '180x180', + href: '/img/favicon/apple-touch-icon.png', + }, + { + rel: 'icon', + type: 'image/png', + sizes: '32x32', + href: '/img/favicon/favicon-32x32.png', + }, + { + rel: 'icon', + type: 'image/png', + sizes: '16x16', + href: '/img/favicon/favicon-16x16.png', + }, + { + rel: 'manifest', + href: '/img/favicon/site.webmanifest', + }, + ], }; -/* - - - - - - -*/ - module.exports = config; diff --git a/packages/website/package.json b/packages/website/package.json index 60cc537aa45d..2058c4598359 100644 --- a/packages/website/package.json +++ b/packages/website/package.json @@ -1,6 +1,6 @@ { "name": "website", - "version": "5.4.0", + "version": "5.5.0", "private": true, "scripts": { "build": "docusaurus build", @@ -11,25 +11,23 @@ "swizzle": "docusaurus swizzle" }, "dependencies": { - "@babel/runtime": "7.16.0", + "@babel/runtime": "7.16.3", "@docusaurus/core": "2.0.0-beta.9", "@docusaurus/plugin-content-docs": "^2.0.0-beta.9", "@docusaurus/plugin-content-pages": "^2.0.0-beta.9", "@docusaurus/plugin-debug": "^2.0.0-beta.9", "@docusaurus/remark-plugin-npm2yarn": "^2.0.0-beta.9", "@docusaurus/theme-classic": "^2.0.0-beta.9", + "@docusaurus/theme-search-algolia": "^2.0.0-beta.9", "@mdx-js/react": "1.6.22", - "@typescript-eslint/eslint-plugin": "5.4.0", - "@typescript-eslint/parser": "5.4.0", - "@typescript-eslint/scope-manager": "5.4.0", - "@typescript-eslint/types": "5.4.0", - "@typescript-eslint/typescript-estree": "5.4.0", - "@typescript-eslint/visitor-keys": "5.4.0", + "@typescript-eslint/website-eslint": "5.5.0", "clsx": "^1.1.1", "eslint": "*", - "konamimojisplosion": "^0.4.3", + "konamimojisplosion": "^0.5.1", + "lzstring.ts": "^2.0.2", "react": "^17.0.2", "react-dom": "^17.0.2", + "remark-docusaurus-tabs": "^0.2.0", "typescript": "*" }, "devDependencies": { @@ -37,8 +35,13 @@ "@types/react": "^17.0.34", "@types/react-helmet": "^6.1.4", "@types/react-router-dom": "^5.3.2", + "copy-webpack-plugin": "^9.1.0", + "eslint-plugin-jsx-a11y": "^6.5.1", + "eslint-plugin-react": "^7.27.1", + "eslint-plugin-react-hooks": "^4.3.0", "globby": "^11.0.4", - "webpack": "^5.61.0" + "monaco-editor": "^0.30.1", + "webpack": "^5.64.0" }, "browserslist": { "production": [ diff --git a/packages/website/src/clientModules.js b/packages/website/src/clientModules.js index bcb346be41a0..e140bca1019f 100644 --- a/packages/website/src/clientModules.js +++ b/packages/website/src/clientModules.js @@ -1,3 +1,3 @@ -if (typeof window !== 'undefined' && typeof window.document !== 'undefined') { +if (typeof window !== 'undefined') { require('konamimojisplosion').initializeKonamimojisplosion(); } diff --git a/packages/website/src/components/OptionsSelector.module.css b/packages/website/src/components/OptionsSelector.module.css new file mode 100644 index 000000000000..5ff62a8c5b00 --- /dev/null +++ b/packages/website/src/components/OptionsSelector.module.css @@ -0,0 +1,81 @@ +.optionLabel { + cursor: pointer; +} + +.optionItem, +.optionLabel { + align-items: center; + display: flex; + flex: 0 0 1.5rem; + flex-direction: row; + font-size: 0.75rem; + margin: 0 0; + padding: 0.4rem 0.8rem; + transition: background-color var(--ifm-transition-fast) + var(--ifm-transition-timing-default), + color var(--ifm-transition-fast) var(--ifm-transition-timing-default); + color: var(--ifm-font-color-secondary); + justify-content: space-between; + border: none; + background: transparent; + font-family: var(--ifm-font-family-base); + box-sizing: border-box; + line-height: var(--ifm-line-height-base); +} + +.optionLabel:hover { + background-color: var(--ifm-color-emphasis-100); + color: var(--ifm-font-color-primary); +} + +.clickableIcon { + cursor: pointer; +} + +.clickableIcon:hover { + color: var(--ifm-color-primary); +} + +.optionInput { + display: block; + width: 90%; + padding: 0.4rem 0.6rem; + line-height: 1; + font-size: 0.8rem; + font-weight: 500; + font-family: inherit; + border-radius: 6px; + -webkit-appearance: none; + color: var(--ifm-font-color-secondary); + border: 1px solid var(--ifm-color-emphasis-100); + background: var(--ifm-color-emphasis-200); + transition: border 0.3s ease; +} +.optionInput::placeholder { + color: var(--ifm-color-emphasis-700); +} +.optionInput:focus { + outline: none; + border-color: var(--ifm-color-primary); +} + +.optionSelect { + line-height: 1; + font-size: 0.8rem; + font-weight: 500; + border-radius: 6px; + font-family: inherit; + width: 50%; + box-shadow: none; + background-image: none; + padding: 0.4rem 0.6rem; + appearance: none; + color: var(--ifm-font-color-secondary); + border: 1px solid var(--ifm-color-emphasis-100); + background: var(--ifm-color-emphasis-200); + transition: border 0.3s ease; +} +.optionSelect:focus { + outline: none; + border-color: var(--ifm-color-primary); +} diff --git a/packages/website/src/components/OptionsSelector.tsx b/packages/website/src/components/OptionsSelector.tsx new file mode 100644 index 000000000000..71f83a95902a --- /dev/null +++ b/packages/website/src/components/OptionsSelector.tsx @@ -0,0 +1,192 @@ +/* eslint-disable jsx-a11y/label-has-associated-control */ +import React, { useCallback, useState } from 'react'; + +import ConfigEslint from './config/ConfigEslint'; +import ConfigTypeScript from './config/ConfigTypeScript'; +import Expander from './layout/Expander'; +import Dropdown from './inputs/Dropdown'; +import Checkbox from './inputs/Checkbox'; +import Tooltip from './inputs/Tooltip'; +import EditIcon from './icons/EditIcon'; +import CopyIcon from './icons/CopyIcon'; + +import { createMarkdown } from './lib/markdown'; + +import type { RuleDetails } from './types'; + +import styles from './OptionsSelector.module.css'; + +import type { + CompilerFlags, + ConfigModel, + SourceType, + RulesRecord, +} from './types'; + +export interface OptionsSelectorParams { + readonly ruleOptions: RuleDetails[]; + readonly state: ConfigModel; + readonly setState: (cfg: Partial) => void; + readonly tsVersions: readonly string[]; + readonly isLoading: boolean; +} + +function OptionsSelector({ + ruleOptions, + state, + setState, + tsVersions, + isLoading, +}: OptionsSelectorParams): JSX.Element { + const [eslintModal, setEslintModal] = useState(false); + const [typeScriptModal, setTypeScriptModal] = useState(false); + const [copyLink, setCopyLink] = useState(false); + const [copyMarkdown, setCopyMarkdown] = useState(false); + + const updateTS = useCallback((version: string) => { + setState({ ts: version }); + }, []); + + const updateRules = useCallback((rules: RulesRecord) => { + setState({ rules: rules }); + setEslintModal(false); + }, []); + + const updateTsConfig = useCallback((config: CompilerFlags) => { + setState({ tsConfig: config }); + setTypeScriptModal(false); + }, []); + + const copyLinkToClipboard = useCallback(async () => { + await navigator.clipboard.writeText(document.location.toString()); + setCopyLink(true); + }, []); + + const copyMarkdownToClipboard = useCallback(async () => { + if (isLoading) { + return; + } + await navigator.clipboard.writeText(createMarkdown(state)); + setCopyMarkdown(true); + }, [state, isLoading]); + + const openIssue = useCallback(() => { + if (isLoading) { + return; + } + window + .open( + `https://github.com/typescript-eslint/typescript-eslint/issues/new?body=${encodeURIComponent( + createMarkdown(state), + )}`, + '_blank', + ) + ?.focus(); + }, [state, isLoading]); + + return ( + <> + {state.rules && ruleOptions.length > 0 && ( + + )} + + + + + + + + + + + + + + + + + + + + ); +} + +export default OptionsSelector; diff --git a/packages/website/src/components/Playground.module.css b/packages/website/src/components/Playground.module.css new file mode 100644 index 000000000000..6c5a9fdad71e --- /dev/null +++ b/packages/website/src/components/Playground.module.css @@ -0,0 +1,43 @@ +.options { + width: 20rem; + background: var(--ifm-background-surface-color); + overflow: auto; +} + +.sourceCode { + height: 100%; + width: 50%; + border: 1px solid var(--ifm-color-emphasis-100); +} + +.sourceCodeStandalone { + width: 100%; +} + +.codeBlocks { + display: flex; + flex-direction: row; + height: 100%; + width: calc(100vw - 20rem); +} + +.astViewer { + height: 100%; + width: 50%; + border: 1px solid var(--ifm-color-emphasis-100); + overflow: auto; + background: var(--ifm-background-surface-color); + word-wrap: initial; + white-space: nowrap; + background: var(--code-editor-bg); +} + +.codeContainer { + display: flex; + flex-direction: row; + position: fixed; + width: 100%; + height: calc(100% - var(--ifm-navbar-height)); + top: var(--ifm-navbar-height); + z-index: var(--ifm-z-index-fixed); +} diff --git a/packages/website/src/components/Playground.tsx b/packages/website/src/components/Playground.tsx new file mode 100644 index 000000000000..f0c7903e5f27 --- /dev/null +++ b/packages/website/src/components/Playground.tsx @@ -0,0 +1,106 @@ +import React, { useCallback, useState } from 'react'; +import type Monaco from 'monaco-editor'; +import clsx from 'clsx'; +import useThemeContext from '@theme/hooks/useThemeContext'; + +import styles from './Playground.module.css'; +import Loader from './layout/Loader'; + +import useHashState from './hooks/useHashState'; +import OptionsSelector from './OptionsSelector'; +import ASTViewer from './ast/ASTViewer'; +import { LoadingEditor } from './editor/LoadingEditor'; +import { EditorEmbed } from './editor/EditorEmbed'; +import type { RuleDetails } from './types'; + +import type { TSESTree } from '@typescript-eslint/website-eslint'; + +function Playground(): JSX.Element { + const [state, setState] = useHashState({ + jsx: false, + showAST: false, + sourceType: 'module', + code: '', + ts: process.env.TS_VERSION, + rules: {}, + tsConfig: {}, + }); + const { isDarkTheme } = useThemeContext(); + const [ast, setAST] = useState(); + const [ruleNames, setRuleNames] = useState([]); + const [isLoading, setIsLoading] = useState(true); + const [tsVersions, setTSVersion] = useState([]); + const [selectedNode, setSelectedNode] = useState(null); + const [position, setPosition] = useState(null); + + const updateSelectedNode = useCallback( + (node: TSESTree.Node | null) => { + if ( + !node || + !selectedNode || + selectedNode.range[0] !== node.range[0] || + selectedNode.range[1] !== node.range[1] + ) { + setSelectedNode(node); + } + }, + [selectedNode], + ); + + return ( +

+
+ +
+
+
+ {isLoading && } + + setState({ code: code })} + onLoaded={(ruleNames, tsVersions): void => { + setRuleNames(ruleNames); + setTSVersion(tsVersions); + setIsLoading(false); + }} + onSelect={setPosition} + /> +
+ {state.showAST && ( +
+ {ast && ( + + )} +
+ )} +
+
+ ); +} + +export default Playground; diff --git a/packages/website/src/components/ast/ASTViewer.module.css b/packages/website/src/components/ast/ASTViewer.module.css new file mode 100644 index 000000000000..ebb4b3470dcb --- /dev/null +++ b/packages/website/src/components/ast/ASTViewer.module.css @@ -0,0 +1,78 @@ +.list, +.subList { + cursor: default; + box-sizing: border-box; + margin: 0; + list-style: none; + padding-left: 1.5rem; + font-family: Consolas, "Courier New", monospace; + font-weight: normal; + font-size: 13px; + font-feature-settings: "liga" 0, "calt" 0; + line-height: 18px; + letter-spacing: 0px; +} + +.nonExpand, +.expand { + border-left: 0.1rem dashed var(--ifm-color-emphasis-200); +} + +.expand.open::before { + content: '+'; +} + +.selected { + background: var(--code-line-decoration); +} + +.expand::before { + content: '-'; + margin-left: -1rem; + width: 1rem; + display: inline-block; +} + +.valueBody { + min-width: 300px; + width: fit-content; +} + +.tokenName { + color: #2aa198; +} + +.propName { + color: #b58900; +} + +.propNumber { + color: #268bd2; +} + +.propEmpty { + color: var(--ifm-color-emphasis-400); +} + +.propRegExp { + color: #b58900; +} + +.propBoolean { + color: #b58900; +} + +.propString { + color: #15aa15; +} + +.hidden { + color: var(--ifm-color-emphasis-400); +} + +.clickable { + cursor: pointer; +} +.clickable:hover { + text-decoration: underline; +} diff --git a/packages/website/src/components/ast/ASTViewer.tsx b/packages/website/src/components/ast/ASTViewer.tsx new file mode 100644 index 000000000000..7c234f91a238 --- /dev/null +++ b/packages/website/src/components/ast/ASTViewer.tsx @@ -0,0 +1,49 @@ +import React, { useEffect, useState } from 'react'; +import styles from './ASTViewer.module.css'; + +import type { TSESTree } from '@typescript-eslint/website-eslint'; +import type { Position } from './types'; + +import { ElementObject } from './Elements'; +import type Monaco from 'monaco-editor'; + +function ASTViewer(props: { + ast: TSESTree.Node | string; + position?: Monaco.Position | null; + onSelectNode: (node: TSESTree.Node | null) => void; +}): JSX.Element { + const [selection, setSelection] = useState(() => + props.position + ? { + line: props.position.lineNumber, + column: props.position.column - 1, + } + : null, + ); + + useEffect(() => { + setSelection( + props.position + ? { + line: props.position.lineNumber, + column: props.position.column - 1, + } + : null, + ); + }, [props.position]); + + return typeof props.ast === 'string' ? ( +
{props.ast}
+ ) : ( +
+ +
+ ); +} + +export default ASTViewer; diff --git a/packages/website/src/components/ast/Elements.tsx b/packages/website/src/components/ast/Elements.tsx new file mode 100644 index 000000000000..9dba9eff491a --- /dev/null +++ b/packages/website/src/components/ast/Elements.tsx @@ -0,0 +1,218 @@ +import React, { + SyntheticEvent, + useCallback, + useEffect, + useRef, + useState, +} from 'react'; +import clsx from 'clsx'; + +import type { TSESTree } from '@typescript-eslint/website-eslint'; +import type { GenericParams } from './types'; + +import { scrollIntoViewIfNeeded } from '@site/src/components/lib/scroll-into'; +import { + filterRecord, + hasChildInRange, + isArrayInRange, + isEsNode, + isInRange, + isRecord, +} from './selection'; + +import PropertyNameComp from '@site/src/components/ast/PropertyName'; +import PropertyValueComp from '@site/src/components/ast/PropertyValue'; +import styles from '@site/src/components/ast/ASTViewer.module.css'; + +export const PropertyName = React.memo(PropertyNameComp); +export const PropertyValue = React.memo(PropertyValueComp); + +export function ElementArray(props: GenericParams): JSX.Element { + const [isComplex, setIsComplex] = useState(() => + isRecord(props.value), + ); + const [isExpanded, setIsExpanded] = useState( + () => + isComplex || props.value.some(item => isInRange(props.selection, item)), + ); + + useEffect(() => { + setIsComplex( + props.value.some(item => typeof item === 'object' && item !== null), + ); + }, [props.value]); + + useEffect(() => { + if (isComplex && !isExpanded) { + setIsExpanded(isArrayInRange(props.selection, props.value)); + } + }, [props.value, props.selection]); + + return ( +
+ setIsExpanded(!isExpanded)} + /> + [ + {isExpanded ? ( +
+ {props.value.map((item, index) => { + return ( + + ); + })} +
+ ) : !isComplex ? ( + + {props.value.map((item, index) => ( + + {index > 0 && ', '} + + + ))} + + ) : ( + + {props.value.length} {props.value.length > 1 ? 'elements' : 'element'} + + )} + ] +
+ ); +} + +export function ElementObject( + props: GenericParams>, +): JSX.Element { + const [isExpanded, setIsExpanded] = useState(() => { + return isInRange(props.selection, props.value); + }); + const [isSelected, setIsSelected] = useState( + () => + isInRange(props.selection, props.value) && props.value.type !== 'Program', + ); + const listItem = useRef(null); + + const onMouseEnter = useCallback( + (e: SyntheticEvent) => { + if (isEsNode(props.value)) { + props.onSelectNode(props.value as TSESTree.Node); + e.stopPropagation(); + e.preventDefault(); + } + }, + [props.value], + ); + + const onMouseLeave = useCallback( + (_e: SyntheticEvent) => { + if (isEsNode(props.value)) { + props.onSelectNode(null); + } + }, + [props.value], + ); + + useEffect(() => { + const selected = isInRange(props.selection, props.value); + + setIsSelected( + selected && + props.value.type !== 'Program' && + !hasChildInRange(props.selection, props.value), + ); + + if (selected && !isExpanded) { + setIsExpanded(isInRange(props.selection, props.value)); + } + }, [props.selection, props.value]); + + useEffect(() => { + if (listItem.current && isSelected) { + scrollIntoViewIfNeeded(listItem.current); + } + }, [isSelected, listItem]); + + return ( +
+ setIsExpanded(!isExpanded)} + /> + {'{'} + {isExpanded ? ( +
+ {filterRecord(props.value).map((item, index) => ( + + ))} +
+ ) : ( + + {filterRecord(props.value) + .map(item => item[0]) + .join(', ')} + + )} + {'}'} +
+ ); +} + +export function ElementItem(props: GenericParams): JSX.Element { + if (Array.isArray(props.value)) { + return ( + + ); + } else if ( + typeof props.value === 'object' && + props.value && + props.value.constructor === Object + ) { + return ( + } + selection={props.selection} + onSelectNode={props.onSelectNode} + /> + ); + } + return ( +
+ {props.name && {props.name}} + {props.name && : } + +
+ ); +} diff --git a/packages/website/src/components/ast/PropertyName.tsx b/packages/website/src/components/ast/PropertyName.tsx new file mode 100644 index 000000000000..f8768a6c4a2a --- /dev/null +++ b/packages/website/src/components/ast/PropertyName.tsx @@ -0,0 +1,27 @@ +import React, { SyntheticEvent } from 'react'; +import clsx from 'clsx'; +import styles from './ASTViewer.module.css'; + +export default function PropertyName(props: { + name?: string; + propName?: string; + onClick?: (e: SyntheticEvent) => void; + onMouseEnter?: (e: SyntheticEvent) => void; +}): JSX.Element { + return ( + // eslint-disable-next-line jsx-a11y/click-events-have-key-events,jsx-a11y/no-static-element-interactions + + {props.propName && ( + + {props.propName} + + )} + {props.propName && : } + {props.name && ( + + {props.name} + + )} + + ); +} diff --git a/packages/website/src/components/ast/PropertyValue.tsx b/packages/website/src/components/ast/PropertyValue.tsx new file mode 100644 index 000000000000..48f3ba119a68 --- /dev/null +++ b/packages/website/src/components/ast/PropertyValue.tsx @@ -0,0 +1,25 @@ +import React from 'react'; +import styles from './ASTViewer.module.css'; + +export default function PropertyValue(props: { value: unknown }): JSX.Element { + if (typeof props.value === 'string') { + return ( + {JSON.stringify(props.value)} + ); + } else if (typeof props.value === 'number') { + return {props.value}; + } else if (typeof props.value === 'bigint') { + return {String(props.value)}n; + } else if (props.value instanceof RegExp) { + return {String(props.value)}; + } else if (typeof props.value === 'undefined' || props.value === null) { + return {String(props.value)}; + } else if (typeof props.value === 'boolean') { + return ( + + {props.value ? 'true' : 'false'} + + ); + } + return {String(props.value)}; +} diff --git a/packages/website/src/components/ast/selection.ts b/packages/website/src/components/ast/selection.ts new file mode 100644 index 000000000000..9a882d2bf9af --- /dev/null +++ b/packages/website/src/components/ast/selection.ts @@ -0,0 +1,79 @@ +import type { TSESTree } from '@typescript-eslint/website-eslint'; +import type { Position } from './types'; + +export const propsToFilter = ['parent', 'comments', 'tokens', 'loc']; + +export function filterRecord( + values: TSESTree.Node | Record, +): [string, unknown][] { + return Object.entries(values).filter( + item => !propsToFilter.includes(item[0]), + ); +} + +export function isNode(node: unknown): node is TSESTree.Node { + return Boolean( + typeof node === 'object' && node && 'type' in node && 'loc' in node, + ); +} + +export function isWithinNode( + loc: Position, + start: Position, + end: Position, +): boolean { + const canStart = + start.line < loc.line || + (start.line === loc.line && start.column <= loc.column); + const canEnd = + end.line > loc.line || (end.line === loc.line && end.column >= loc.column); + return canStart && canEnd; +} + +export function isRecord(value: unknown): value is Record { + return Boolean( + typeof value === 'object' && value && value.constructor === Object, + ); +} + +export function isEsNode( + value: unknown, +): value is Record & TSESTree.BaseNode { + return isRecord(value) && 'type' in value && 'loc' in value; +} + +export function isInRange( + position: Position | null | undefined, + value: unknown, +): boolean { + return Boolean( + position && + isEsNode(value) && + isWithinNode(position, value.loc.start, value.loc.end), + ); +} + +export function isArrayInRange( + position: Position | null | undefined, + value: unknown, +): boolean { + return Boolean( + position && + Array.isArray(value) && + value.some(item => isInRange(position, item)), + ); +} + +export function hasChildInRange( + position: Position | null | undefined, + value: unknown, +): boolean { + return Boolean( + position && + isEsNode(value) && + filterRecord(value).some( + ([, item]) => + isInRange(position, item) || isArrayInRange(position, item), + ), + ); +} diff --git a/packages/website/src/components/ast/types.ts b/packages/website/src/components/ast/types.ts new file mode 100644 index 000000000000..53a565599ab9 --- /dev/null +++ b/packages/website/src/components/ast/types.ts @@ -0,0 +1,15 @@ +import type { TSESTree } from '@typescript-eslint/website-eslint'; + +export interface Position { + line: number; + column: number; +} + +export interface GenericParams { + readonly propName?: string; + readonly name?: string; + readonly value: V; + readonly level: string; + readonly selection?: Position | null; + readonly onSelectNode: (node: TSESTree.Node | null) => void; +} diff --git a/packages/website/src/components/config/ConfigEditor.module.css b/packages/website/src/components/config/ConfigEditor.module.css new file mode 100644 index 000000000000..2ca4bb4c1cc9 --- /dev/null +++ b/packages/website/src/components/config/ConfigEditor.module.css @@ -0,0 +1,82 @@ +.search { + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + background: var(--ifm-navbar-search-input-background-color) + var(--ifm-navbar-search-input-icon) no-repeat 0.75rem center / 1rem 1rem; + border: none; + color: var(--ifm-navbar-search-input-color); + cursor: text; + display: inline-block; + height: 2rem; + padding: 0 0.5rem 0 2.25rem; + font-size: 0.9rem; + flex: 1; + border-radius: 0.2rem; +} + +.searchResult, .searchResultGroup { + align-items: center; + display: flex; + flex: 0 0 1.5rem; + flex-direction: row; + font-size: 0.75rem; + padding: 0.4rem 0.8rem; + color: var(--ifm-font-color-secondary); + justify-content: space-between; +} + +.searchResult { + margin: 0 0; + transition: background-color var(--ifm-transition-fast) + var(--ifm-transition-timing-default), + color var(--ifm-transition-fast) var(--ifm-transition-timing-default); +} + +.searchResultGroup { + font-size: 1.3rem; + margin: 0.2rem 0; +} + +.searchResult:nth-child(even), .searchResultGroup:nth-child(even) { + background: var(--ifm-color-emphasis-100); +} + +.searchResult:nth-child(even) { + background: var(--ifm-color-emphasis-100); +} + +.searchResult:hover { + background: var(--ifm-color-emphasis-200); +} + +.searchResultContainer { + overflow: auto; + height: 50vh; +} + +.searchResultName { + font-size: 1.2em; +} + +.editJson { + height: 2rem; +} + +.searchBar { + display: flex; + column-gap: 0.5rem; + margin-bottom: 0.5rem; + justify-content: flex-end; +} + +.textarea { + background: var(--ifm-navbar-search-input-background-color); + border: none; + color: var(--ifm-navbar-search-input-color); + cursor: text; + width: 100%; + max-width: 100%; + min-width: 100%; + padding: 1rem; +} diff --git a/packages/website/src/components/config/ConfigEditor.tsx b/packages/website/src/components/config/ConfigEditor.tsx new file mode 100644 index 000000000000..0880ff7e6e70 --- /dev/null +++ b/packages/website/src/components/config/ConfigEditor.tsx @@ -0,0 +1,235 @@ +import React, { useCallback, useEffect, useReducer, useState } from 'react'; +import clsx from 'clsx'; + +import styles from './ConfigEditor.module.css'; + +import Text from '../inputs/Text'; +import Checkbox from '../inputs/Checkbox'; +import useFocus from '../hooks/useFocus'; +import Modal from '@site/src/components/modals/Modal'; + +export interface ConfigOptionsField { + key: string; + label?: string; + defaults?: unknown[]; +} + +export interface ConfigOptionsType { + heading: string; + fields: ConfigOptionsField[]; +} + +export type ConfigEditorValues = Record; + +export interface ConfigEditorProps { + readonly options: ConfigOptionsType[]; + readonly values: ConfigEditorValues; + readonly isOpen: boolean; + readonly header: string; + readonly jsonField: string; + readonly onClose: (config: ConfigEditorValues) => void; +} + +function reducerJson( + _state: string, + action: string | { field: string; value: ConfigEditorValues }, +): string { + if (typeof action === 'string') { + return action; + } else if (action && typeof action === 'object') { + return JSON.stringify( + { + [action.field]: action.value, + }, + null, + 2, + ); + } + throw new Error(); +} + +function isRecord(data: unknown): data is Record { + return Boolean(data && typeof data === 'object'); +} + +function reducerObject( + state: ConfigEditorValues, + action: + | { type: 'init'; config?: ConfigEditorValues } + | { + type: 'toggle'; + checked: boolean; + default: unknown[] | undefined; + name: string; + } + | { type: 'json'; field: string; code: string }, +): ConfigEditorValues { + switch (action.type) { + case 'init': { + return action.config ?? {}; + } + case 'toggle': { + const newState = { ...state }; + if (action.checked) { + newState[action.name] = action.default ? action.default[0] : true; + } else if (action.name in newState) { + delete newState[action.name]; + } + return newState; + } + case 'json': { + try { + const parsed: unknown = JSON.parse(action.code); + if (isRecord(parsed)) { + const item = parsed[action.field]; + if (item && isRecord(item)) { + return item; + } + } + } catch { + // eslint-disable-next-line no-console + console.error('ERROR parsing json'); + } + return state; + } + } + // @ts-expect-error: Safeguard + throw new Error(); +} + +function filterConfig( + options: ConfigOptionsType[], + filter: string, +): ConfigOptionsType[] { + return options + .map(group => ({ + heading: group.heading, + fields: group.fields.filter(item => String(item.key).includes(filter)), + })) + .filter(group => group.fields.length > 0); +} + +function isDefault(value: unknown, defaults?: unknown[]): boolean { + return defaults ? defaults.includes(value) : value === true; +} + +function ConfigEditor(props: ConfigEditorProps): JSX.Element { + const [filter, setFilter] = useState(''); + const [editJson, setEditJson] = useState(false); + const [config, setConfig] = useReducer(reducerObject, {}); + const [jsonCode, setJsonCode] = useReducer(reducerJson, ''); + const [filterInput, setFilterFocus] = useFocus(); + const [jsonInput, setJsonFocus] = useFocus(); + + const onClose = useCallback(() => { + if (editJson) { + props.onClose( + reducerObject(config, { + type: 'json', + field: props.jsonField, + code: jsonCode, + }), + ); + } else { + props.onClose(config); + } + }, [props.onClose, props.jsonField, jsonCode, config]); + + useEffect(() => { + setConfig({ type: 'init', config: props.values }); + }, [props.values]); + + useEffect(() => { + if (props.isOpen) { + if (!editJson) { + setFilterFocus(); + } else { + setJsonFocus(); + } + } + }, [editJson, props.isOpen]); + + const changeEditType = useCallback(() => { + if (editJson) { + setConfig({ type: 'json', field: props.jsonField, code: jsonCode }); + } else { + setJsonCode({ field: props.jsonField, value: config }); + } + setEditJson(!editJson); + }, [editJson, config, jsonCode, props.jsonField]); + + return ( + +
+ {!editJson && ( + + )} + +
+ {editJson && ( +