diff --git a/.github/ISSUE_TEMPLATE/06-bug-report-other.yaml b/.github/ISSUE_TEMPLATE/06-bug-report-other.yaml index ee44e376f654..3eb5e066206b 100644 --- a/.github/ISSUE_TEMPLATE/06-bug-report-other.yaml +++ b/.github/ISSUE_TEMPLATE/06-bug-report-other.yaml @@ -20,7 +20,11 @@ body: - label: I have [read the FAQ](https://typescript-eslint.io/linting/troubleshooting) and my problem is not listed. required: true - type: markdown - id: complexity-note + attributes: + value: | + **All typescript-eslint bug reports need an isolated reproduction** we can clone locally and get running without other projects or existing knowledge of your project. + If you can't provide one, your report will likely be closed without action. + - type: markdown attributes: value: | ### Note For Complex Issues diff --git a/.github/workflows/lock.yml b/.github/workflows/lock.yml index 202088e5b17a..ae557bfdd433 100644 --- a/.github/workflows/lock.yml +++ b/.github/workflows/lock.yml @@ -14,8 +14,9 @@ jobs: runs-on: ubuntu-latest steps: - - uses: dessant/lock-threads@v4 + - uses: dessant/lock-threads@v5.0.1 with: + add-issue-labels: 'locked due to age' github-token: ${{ github.token }} issue-inactive-days: '7' issue-lock-reason: 'resolved' diff --git a/.github/workflows/pr-labels.yml b/.github/workflows/pr-labels.yml new file mode 100644 index 000000000000..3ede15e78943 --- /dev/null +++ b/.github/workflows/pr-labels.yml @@ -0,0 +1,25 @@ +name: Pull Request Labels + +on: + pull_request: + types: [labeled, opened, synchronize, unlabeled] + +jobs: + label: + runs-on: ubuntu-latest + permissions: + issues: write + pull-requests: write + steps: + - id: changed-stable-configs + uses: tj-actions/changed-files@v44.5.2 + with: + files: packages/{eslint-plugin,typescript-eslint}/src/configs/{recommended,stylistic}* + - if: steps.changed-stable-configs.outputs.any_changed == 'true' + uses: mheap/github-action-required-labels@v5.4.1 + with: + add_comment: true + count: 1 + labels: breaking change + message: '🤖 Beep boop! PRs that change our stable preset configs must be labeled with `breaking change`.' + mode: minimum diff --git a/docs/Contributing.mdx b/docs/Contributing.mdx index 67f28ca8c34c..062477b1fedc 100644 --- a/docs/Contributing.mdx +++ b/docs/Contributing.mdx @@ -16,6 +16,6 @@ If you're new to open source, you may also find the [How to Contribute to Open S ## Next Steps -1. [Finding or opening an issue](./contributing/Issues.mdx): In the issue tab, find the pr currently tagged with `accepting prs` or create a new issue. -2. [Setting up a local environment](./contributing/Local_Development.mdx): Let's learn how to set up the local environment to start development. -3. [Making a PR](./contributing/Pull_Requests.mdx): you've got changes locally that address an issue, take a look at that topic. +1. [Finding or opening an issue](./contributing/Issues.mdx): In the issue tab, finding or creating an issue to work on. +2. [Setting up a local environment](./contributing/Local_Development.mdx): Learning how to set up the local environment to start development. +3. [Making a PR](./contributing/Pull_Requests.mdx): Sending in your changes to resolve an accepted issue. diff --git a/docs/contributing/Issues.mdx b/docs/contributing/Issues.mdx index 879600ae5d81..6a2861dfbeb6 100644 --- a/docs/contributing/Issues.mdx +++ b/docs/contributing/Issues.mdx @@ -34,6 +34,13 @@ We've found in the past that they result in accidental ["licked cookie"](https:/ If an issue has been marked as `accepting prs` and an open PR does not exist, feel free to send a PR. You don't need to ask for permission. +## Finding Issues + +Consider searching through: + +1. [Unassigned user-facing marked as `accepting prs` and `good first issue`](https://github.com/typescript-eslint/typescript-eslint/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22+label%3A%22accepting+prs%22+-label%3A%22repo+maintenance%22+no%3Aassignee): to find issues marked as good for a first-timer +2. [Unassigned user-facing marked as `accepting prs` without `good first issue`](https://github.com/typescript-eslint/typescript-eslint/issues?q=is%3Aopen+is%3Aissue+label%3A%22accepting+prs%22+-label%3A%22good+first+issue%22+-label%3A%22repo+maintenance%22+no%3Aassignee): once you've finished a few issues, to find more intermediate-level issues + ## Questions and Support Requests The issue tracker is not an appropriate place for questions or support requests. diff --git a/docs/contributing/Local_Development.mdx b/docs/contributing/Local_Development.mdx index 725e9354b32e..ad9e201d22b6 100644 --- a/docs/contributing/Local_Development.mdx +++ b/docs/contributing/Local_Development.mdx @@ -61,23 +61,6 @@ You can run `yarn test` in any package to run its tests. > [VS Code launch tasks](https://code.visualstudio.com/docs/editor/tasks) tasks are provided that allow [visual debugging](https://code.visualstudio.com/docs/editor/debugging) tests. -#### Code Coverage - -We aim for 100% code coverage in all PRs when possible, except in the `website/` package. -Coverage reports are generated locally whenever `yarn test` is run. - -The `codecov` bot should also comment on your PR with the percentage, as well as links to the line-by-line coverage of each file touched by your PR. - -#### Granular Unit Tests - -Most tests in most packages are set up as small, self-contained unit tests. -We generally prefer that to keep tests and their failure reports granular. - -For rule tests we recommend, when reasonable: - -- Including both `valid` and `invalid` code changes in most PRs that affect rule behavior -- Limiting to one error per `invalid` case - ### Type Checking All code should pass TypeScript type checking. diff --git a/docs/contributing/Pull_Requests.mdx b/docs/contributing/Pull_Requests.mdx index dd3a0e4f2d69..a0b9ad083b06 100644 --- a/docs/contributing/Pull_Requests.mdx +++ b/docs/contributing/Pull_Requests.mdx @@ -25,7 +25,26 @@ Please don't: - Comment on a closed PR - Reasoning: It is much easier for maintainers to not lose track of things if they are posted as issues. If you think there's a bug in typescript-eslint, the right way to ask is to [file a new issue](https://github.com/typescript-eslint/typescript-eslint/issues/new/choose). The issue templates include helpful & necessary practices such as making sure you're on the latest version of all our packages. You can provide the link to the relevant PR to add more context. -### Raising a PR +### Testing Changes + +#### Code Coverage + +We aim for 100% code coverage in all PRs when possible, except in the `website/` package. +Coverage reports are generated locally whenever `yarn test` is run. + +The `codecov` bot also comments on each PR with its percentage, as well as links to the line-by-line coverage of each file touched by the PR. + +#### Granular Unit Tests + +Most tests in most packages are set up as small, self-contained unit tests. +We generally prefer that to keep tests and their failure reports granular. + +For rule tests we recommend, when reasonable: + +- Including both `valid` and `invalid` code changes in most PRs that affect rule behavior +- Limiting to one error per `invalid` case + +### Raising the PR Once your changes are ready, you can raise a PR! 🙌 The title of your PR should match the following format: @@ -84,6 +103,14 @@ Once you've addressed all our feedback by making code changes and/or started a f Once the feedback is addressed, and the PR is approved, we'll ensure the branch is up to date with `main`, and merge it for you. +#### Updating Over Time + +You generally don't need to keep merging commits from `main` into your PR branch. +If you see merge conflicts or other intersections that worry you, then you can preemptively - but that's optional. + +If we think merge conflicts need to be resolved in order to merge and/or review a PR, we might do that for you as a courtesy _if_ we have time. +If not, we may ask you to. + ### Asking Questions If you need help and/or have a question, posting a comment in the PR is a great way to do so. diff --git a/docs/maintenance/Contributor_Tiers.mdx b/docs/maintenance/Contributor_Tiers.mdx index fe554936d1cb..154ae4ee4fed 100644 --- a/docs/maintenance/Contributor_Tiers.mdx +++ b/docs/maintenance/Contributor_Tiers.mdx @@ -95,7 +95,10 @@ We treat both the creation and review of issues and PRs along the rough scale of #6976 - , #6992 + ,{' '} + + #6992 + @@ -106,7 +109,10 @@ We treat both the creation and review of issues and PRs along the rough scale of #6780 - , #6910 + ,{' '} + + #6910 + @@ -117,7 +123,10 @@ We treat both the creation and review of issues and PRs along the rough scale of #6107 - , #6907 + ,{' '} + + #6907 + @@ -128,7 +137,10 @@ We treat both the creation and review of issues and PRs along the rough scale of #6084 - , #6777 + ,{' '} + + #6777 + diff --git a/docs/maintenance/Releases.mdx b/docs/maintenance/Releases.mdx index 59737a7683b2..12b9dd0bf82b 100644 --- a/docs/maintenance/Releases.mdx +++ b/docs/maintenance/Releases.mdx @@ -50,7 +50,7 @@ In parallel to the general PR flow of the major version: In parallel to the shared config changes work, make sure to test out the beta version on popular community projects willing to try it out. -1. Create a pinned issue offering to try out the new version's beta for consumers [example: [Try out v6 beta on various important community repos](https://github.com/typescript-eslint/typescript-eslint/issues/6760)] +1. Create a pinned issue offering to try out the new version's beta for consumers [example: [Try out v8 beta on various influential community repos](https://github.com/typescript-eslint/typescript-eslint/issues/9141)] - Ask each community project if they'd be interested in trying out the new version, such as in their Discord or on their issue tracker. - Each community project that's indicated willingness to receive a PR should have one. 1. Once the proposed _Shared Config Changes_ are merged into the `v${major}` branch, send a draft PR to each project with the new beta version. diff --git a/docs/maintenance/issues/Rule_Deprecations.mdx b/docs/maintenance/issues/Rule_Deprecations_And_Deletions.mdx similarity index 59% rename from docs/maintenance/issues/Rule_Deprecations.mdx rename to docs/maintenance/issues/Rule_Deprecations_And_Deletions.mdx index 1e89880390b6..34715e9e73c2 100644 --- a/docs/maintenance/issues/Rule_Deprecations.mdx +++ b/docs/maintenance/issues/Rule_Deprecations_And_Deletions.mdx @@ -1,6 +1,6 @@ --- -id: rule-deprecations -title: Rule Deprecations +id: rule-deprecations-and-deletions +title: Rule Deprecations, Renames, And Deletions --- Sometimes a rule that used to be 👍 does not age well and becomes 👎. @@ -14,12 +14,12 @@ In these cases, we aim to remove the old rule with minimal user disruption. ## Filing the Issue -Rule deprecations can be filed as a [new issue bypassing templates](https://github.com/typescript-eslint/typescript-eslint/issues/new). +Rule deprecations and renames can be filed as a [new issue bypassing templates](https://github.com/typescript-eslint/typescript-eslint/issues/new). Provide it an `## Overview` containing: - The rule name & link to its documentation page -- A clear explanation of why you believe it should be deprecated +- A clear explanation of why you believe it should be deprecated and/or renamed - Whether it exists in popular configs such as `eslint-config-airbnb-typescript` and `eslint-config-standard-with-typescript` - Sourcegraph queries showing how often it appears in user configs @@ -30,5 +30,8 @@ Provide it an `## Overview` containing: 1. In any minor/patch version, add [rule `meta` properties](https://eslint.org/docs/latest/developer-guide/working-with-rules#rule-basics): - `deprecated: true` - `replacedBy`, if applicable -2. In the next major version, you may delete the rule - - If the rule is relatively popular with users, consider leaving a documentation page as a tombstone pointing to the new relevant rule or docs (see [`camelcase`](/rules/camelcase/) as an example) +2. Search through open issues and PRs, and update the name in them accordingly: + - Deletions: close them with a link to the issue and deprecation PR + - Renames: update their title and explicitly mention in a comment that the rule has been renamed +3. In the next major version, you may delete the deprecated rule + - Leave a documentation page as a tombstone pointing to the new relevant rule or docs (see [`camelcase`](/rules/camelcase) as an example) diff --git a/docs/maintenance/pull-requests/Dependency_Version_Upgrades.mdx b/docs/maintenance/pull-requests/Dependency_Version_Upgrades.mdx index c184221d1e6f..15ae0340c358 100644 --- a/docs/maintenance/pull-requests/Dependency_Version_Upgrades.mdx +++ b/docs/maintenance/pull-requests/Dependency_Version_Upgrades.mdx @@ -134,6 +134,7 @@ We generally start the process of supporting a new TypeScript version just after - Whenever a PR is merged, change the respective heading's emoji from 🏗 to ✅ 1. Create a PR with a title like `feat: update TypeScript to X.Y-rc` and the following changes: - In the root `package.json`, add `|| X.Y.1-rc2` to the `devDependency` on `typescript` + - In the parser's `getLib`, update the `switch (target)` and its preceding comment as needed (see [#6782](https://github.com/typescript-eslint/typescript-eslint/pull/6782)) - Change the `SUPPORTED_TYPESCRIPT_VERSIONS` constant's `<` version to the next version of TypeScript - Change the `SUPPORTED_PRERELEASE_RANGES` constant to equal `['X.Y.1-rc']` - Rename and update `patches/typescript*` to the new TypeScript version diff --git a/docs/packages/ESLint_Plugin.mdx b/docs/packages/ESLint_Plugin.mdx index bf0777a005d3..263d7e4123a5 100644 --- a/docs/packages/ESLint_Plugin.mdx +++ b/docs/packages/ESLint_Plugin.mdx @@ -5,6 +5,8 @@ sidebar_label: eslint-plugin # `@typescript-eslint/eslint-plugin` + + > The TypeScript plugin for ESLint. ✨ :::info diff --git a/docs/packages/ESLint_Plugin_TSLint.mdx b/docs/packages/ESLint_Plugin_TSLint.mdx index f8f3d8dc8119..91cea06c45a0 100644 --- a/docs/packages/ESLint_Plugin_TSLint.mdx +++ b/docs/packages/ESLint_Plugin_TSLint.mdx @@ -5,6 +5,8 @@ sidebar_label: eslint-plugin-tslint # `@typescript-eslint/eslint-plugin-tslint` + + > ESLint plugin that allows running TSLint rules within ESLint to help you migrate from TSLint to ESLint. ✨ :::caution diff --git a/docs/packages/Parser.mdx b/docs/packages/Parser.mdx index 14ef7d19ff1d..c337e5655967 100644 --- a/docs/packages/Parser.mdx +++ b/docs/packages/Parser.mdx @@ -3,8 +3,13 @@ id: parser sidebar_label: parser --- +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + # `@typescript-eslint/parser` + + > An [ESLint parser](https://eslint.org/docs/user-guide/configuring/plugins#specifying-parser) used to parse TypeScript code into ESLint-compatible nodes, as well as provide backing TypeScript programs. ✨ This is necessary because TypeScript produces a different, incompatible AST format to the one that ESLint requires to work. @@ -276,7 +281,25 @@ For an option that allows linting files outside of your TSConfig file(s), see [` Specifies using TypeScript APIs to generate type information for rules. It will automatically detect the TSConfig for each file (like `project: true`), and will also allow type information to be computed for JavaScript files without the `allowJs` compiler option (unlike `project: true`). -```js + + + +```js title="eslint.config.js" +export default [ + { + languageOptions: { + parserOptions: { + projectService: true, + }, + }, + }, +]; +``` + + + + +```js title=".eslintrc.js" module.exports = { parser: '@typescript-eslint/parser', parserOptions: { @@ -285,6 +308,9 @@ module.exports = { }; ``` + + + **This setting or [`project`](#project) are required to use [rules which require type information](../getting-started/Typed_Linting.mdx)**. This option brings two main benefits over the older `project`: @@ -383,6 +409,24 @@ declare function createProgram( Example usage: + + + +```js title="eslint.config.js" +import * as parser from '@typescript-eslint/parser'; + +export default [ + { + parserOptions: { + programs: [parser.createProgram('tsconfig.json')], + }, + }, +]; +``` + + + + ```js title=".eslintrc.js" const parser = require('@typescript-eslint/parser'); @@ -392,3 +436,34 @@ module.exports = { }, }; ``` + + + + +### `withoutProjectParserOptions(parserOptions)` + +Removes options that prompt the parser to parse the project with type information. +In other words, you can use this if you are invoking the parser directly, to ensure that one file will be parsed in isolation, which is much faster. + +This is useful in cases where you invoke the parser directly, such as in an ESLint plugin context. + +```ts +declare function withoutProjectParserOptions( + options: TSESTreeOptions, +): TSESTreeOptions; +``` + +Example usage: + +```js title="somePlugin.js" +const parser = require('@typescript-eslint/parser'); + +function parse(path, content, context) { + const contextParserOptions = context.languageOptions?.parserOptions ?? {}; + const parserOptions = + parser.withoutProjectParserOptions(contextParserOptions); + + // Do something with the cleaned-up options eventually, such as invoking the parser + parser.parseForESLint(content, parserOptions); +} +``` diff --git a/docs/packages/Rule_Tester.mdx b/docs/packages/Rule_Tester.mdx index 25deac0e38b5..41b4c2cc3acd 100644 --- a/docs/packages/Rule_Tester.mdx +++ b/docs/packages/Rule_Tester.mdx @@ -7,6 +7,8 @@ import CodeBlock from '@theme/CodeBlock'; # `@typescript-eslint/rule-tester` + + > A utility for testing ESLint rules This is a fork of ESLint's built-in `RuleTester` to provide some better types and additional features for testing TypeScript rules. diff --git a/docs/packages/Scope_Manager.mdx b/docs/packages/Scope_Manager.mdx index cd33ac0fc751..0afbdd4690c7 100644 --- a/docs/packages/Scope_Manager.mdx +++ b/docs/packages/Scope_Manager.mdx @@ -5,6 +5,8 @@ sidebar_label: scope-manager # `@typescript-eslint/scope-manager` + + > A fork of [`eslint-scope`](https://github.com/eslint/eslint-scope), enhanced to support TypeScript functionality. ✨ A "scope analyser" traverses an AST and builds a model of how variables (and in our case, types) are defined and consumed by the source code. diff --git a/docs/packages/TypeScript_ESLint.mdx b/docs/packages/TypeScript_ESLint.mdx index 5e910850b80d..379c939c0be1 100644 --- a/docs/packages/TypeScript_ESLint.mdx +++ b/docs/packages/TypeScript_ESLint.mdx @@ -8,6 +8,8 @@ import TabItem from '@theme/TabItem'; # `typescript-eslint` + + > Tooling which enables you to use TypeScript with ESLint This package is the main entrypoint that you can use to consume our tooling with ESLint. diff --git a/docs/packages/TypeScript_ESTree.mdx b/docs/packages/TypeScript_ESTree.mdx index 7cf5d44c8eed..e8a27eb0f012 100644 --- a/docs/packages/TypeScript_ESTree.mdx +++ b/docs/packages/TypeScript_ESTree.mdx @@ -5,6 +5,8 @@ sidebar_label: typescript-estree # `@typescript-eslint/typescript-estree` + + > The underlying code used by [`@typescript-eslint/parser`](./Parser.mdx) that converts TypeScript source code into an ESTree-compatible form. ✨ This parser is designed to be generic and robust. diff --git a/docs/packages/Utils.mdx b/docs/packages/Utils.mdx index df05daa76909..ea7020466324 100644 --- a/docs/packages/Utils.mdx +++ b/docs/packages/Utils.mdx @@ -5,6 +5,8 @@ sidebar_label: utils # `@typescript-eslint/utils` + + > Utilities for working with TypeScript + ESLint together. ✨ This package contains public utilities for writing custom rules and plugins in TypeScript. diff --git a/docs/troubleshooting/FAQ.mdx b/docs/troubleshooting/FAQ.mdx index 578f359132e6..164ee17c32bc 100644 --- a/docs/troubleshooting/FAQ.mdx +++ b/docs/troubleshooting/FAQ.mdx @@ -115,9 +115,9 @@ These errors are caused by an ESLint config requesting type information be gener - If you **do** want to lint the file with [type-aware linting](../getting-started/Typed_Linting.mdx): - Check the `include` option of each of the TSConfigs that you provide to `parserOptions.project` - you must ensure that all files match an `include` glob, or else our tooling will not be able to find it. - If the file is a `.cjs`, `.js`, or `.mjs` file, make sure [`allowJs`](https://www.typescriptlang.org/tsconfig#allowJs) is enabled. - - If your file shouldn't be a part of one of your existing tsconfigs (for example, it is a script/tool local to the repo), then consider creating a new tsconfig (we advise calling it `tsconfig.eslint.json`) in your project root which lists this file in its `include`. For an example of this, you can check out the configuration we use in this repo: - - [`tsconfig.eslint.json`](https://github.com/typescript-eslint/typescript-eslint/blob/main/tsconfig.eslint.json) - - [`eslint.config.mjs`](https://github.com/typescript-eslint/typescript-eslint/blob/main/eslint.config.mjs) + - If your file shouldn't be a part of one of your existing tsconfigs (for example, it is a script/tool local to the repo), then consider creating a new tsconfig (we advise calling it `tsconfig.eslint.json`) in your project root which lists this file in its `include`. For an example of this, you can check out the configuration we previously used in this repo: + - [`tsconfig.eslint.json`](https://github.com/typescript-eslint/typescript-eslint/blob/958fecaef10a26792dc00e936e98cb19fd26d05f/tsconfig.eslint.json) + - [`eslint.config.mjs`](https://github.com/typescript-eslint/typescript-eslint/blob/958fecaef10a26792dc00e936e98cb19fd26d05f/.eslintrc.js) diff --git a/docs/troubleshooting/Formatting.mdx b/docs/troubleshooting/Formatting.mdx index c01fe4532dbd..814544d1daab 100644 --- a/docs/troubleshooting/Formatting.mdx +++ b/docs/troubleshooting/Formatting.mdx @@ -114,6 +114,11 @@ Per [ESLint's 2020 Changes to Rule Policies blog post](https://eslint.org/blog/2 We mirror the ESLint team's move away from _formatting_ and _stylistic_ rules. With the exception of bug fixes, no new formatting- or stylistic-related pull requests will be accepted into typescript-eslint. +:::note +The [`stylistic` configurations](../users/Shared_Configurations.mdx#stylistic) are not deprecated or recommended-against. +We'll continue to include those configs and their rules to help enforce TypeScript-related stylistic consistency for the foreseeable future. +::: + ## `eslint-stylistic` The downside of using a comprehensive formatter for formatting is that it will strictly apply opinions to code. diff --git a/docs/users/Shared_Configurations.mdx b/docs/users/Shared_Configurations.mdx index 5b52521eea08..59c7e26cb66d 100644 --- a/docs/users/Shared_Configurations.mdx +++ b/docs/users/Shared_Configurations.mdx @@ -14,6 +14,9 @@ import TabItem from '@theme/TabItem'; ## Getting Started +See [Getting Started > Quickstart](../getting-started/Quickstart.mdx) first to set up your ESLint configuration file. +[Packages > typescript-eslint](../packages/TypeScript_ESLint.mdx) includes more documentation on the `tseslint` helper. + ### Projects Without Type Checking If your project does not enable [typed linting](../getting-started/Typed_Linting.mdx), we suggest enabling the [`recommended`](#recommended) and [`stylistic`](#stylistic) configurations to start: @@ -181,10 +184,15 @@ module.exports = { Some rules also enabled in `recommended` default to more strict settings in this configuration. See [`configs/strict.ts`](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/src/configs/strict.ts) for the exact contents of this config. -:::caution +:::tip We recommend a TypeScript project extend from `plugin:@typescript-eslint/strict` only if a nontrivial percentage of its developers are highly proficient in TypeScript. ::: +:::warning +This configuration is not considered "stable" under Semantic Versioning (semver). +Its enabled rules and/or their options may change outside of major version updates. +::: + ### `strict-type-checked` Contains all of `recommended`, `recommended-type-checked`, and `strict`, along with additional strict rules that require type information. @@ -212,10 +220,15 @@ module.exports = { Some rules also enabled in `recommended-type-checked` default to more strict settings in this configuration. See [`configs/strict-type-checked.ts`](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/src/configs/strict-type-checked.ts) for the exact contents of this config. -:::caution +:::tip We recommend a TypeScript project extend from `plugin:@typescript-eslint/strict-type-checked` only if a nontrivial percentage of its developers are highly proficient in TypeScript. ::: +:::warning +This configuration is not considered "stable" under Semantic Versioning (semver). +Its enabled rules and/or their options may change outside of major version updates. +::: + ### `stylistic` Rules considered to be best practice for modern TypeScript codebases, but that do not impact program logic. @@ -290,6 +303,11 @@ We do not recommend TypeScript projects extend from `plugin:@typescript-eslint/a Many rules conflict with each other and/or are intended to be configured per-project. ::: +:::warning +This configuration is not considered "stable" under Semantic Versioning (semver). +Its enabled rules and/or their options may change outside of major version updates. +::: + ### `base` A minimal ruleset that sets only the required parser and plugin options needed to run typescript-eslint. @@ -363,6 +381,11 @@ module.exports = { +:::warning +This configuration is not considered "stable" under Semantic Versioning (semver). +Its enabled rules and/or their options may change outside of major version updates. +::: + ### `eslint-recommended` This ruleset is meant to be used after extending `eslint:recommended`. @@ -424,6 +447,11 @@ module.exports = { See [`configs/strict-type-checked-only.ts`](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/src/configs/strict-type-checked-only.ts) for the exact contents of this config. +:::warning +This configuration is not considered "stable" under Semantic Versioning (semver). +Its enabled rules and/or their options may change outside of major version updates. +::: + ### `stylistic-type-checked-only` A version of `stylistic` that _only_ contains type-checked rules and disables of any corresponding core ESLint rules. diff --git a/eslint.config.mjs b/eslint.config.mjs index b313a874d5de..487d8c4d6492 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -97,9 +97,8 @@ export default tseslint.config( // make sure we're not leveraging any deprecated APIs 'deprecation/deprecation': 'error', - // TODO(#7130): Investigate changing these in or removing these from presets + // TODO: https://github.com/typescript-eslint/typescript-eslint/issues/8538 '@typescript-eslint/no-confusing-void-expression': 'off', - '@typescript-eslint/prefer-string-starts-ends-with': 'off', // // our plugin :D @@ -136,6 +135,12 @@ export default tseslint.config( allowBitwiseExpressions: true, }, ], + '@typescript-eslint/prefer-string-starts-ends-with': [ + 'error', + { + allowSingleElementEquality: 'always', + }, + ], '@typescript-eslint/unbound-method': 'off', '@typescript-eslint/restrict-template-expressions': [ 'error', diff --git a/package.json b/package.json index 5a9a8ffdc559..5c0303b2ea20 100644 --- a/package.json +++ b/package.json @@ -112,7 +112,7 @@ "ncp": "^2.0.0", "netlify": "^13.1.14", "nx": "18.3.5", - "prettier": "3.2.5", + "prettier": "3.3.0", "pretty-format": "^29.7.0", "rimraf": "^5.0.5", "tmp": "^0.2.1", diff --git a/packages/ast-spec/src/base/UnaryExpressionBase.ts b/packages/ast-spec/src/base/UnaryExpressionBase.ts index feb681ccbc3c..6401c7e73fb2 100644 --- a/packages/ast-spec/src/base/UnaryExpressionBase.ts +++ b/packages/ast-spec/src/base/UnaryExpressionBase.ts @@ -1,10 +1,8 @@ -import type { UnaryExpression } from '../expression/UnaryExpression/spec'; -import type { LeftHandSideExpression } from '../unions/LeftHandSideExpression'; -import type { Literal } from '../unions/Literal'; +import type { Expression } from '../unions/Expression'; import type { BaseNode } from './BaseNode'; export interface UnaryExpressionBase extends BaseNode { operator: string; prefix: boolean; - argument: LeftHandSideExpression | Literal | UnaryExpression; + argument: Expression; } diff --git a/packages/ast-spec/src/declaration/TSDeclareFunction/fixtures/_error_/async/snapshots/1-TSESTree-Error.shot b/packages/ast-spec/src/declaration/TSDeclareFunction/fixtures/_error_/async/snapshots/1-TSESTree-Error.shot index 79f1c74928b4..b2dfc1ae3ec8 100644 --- a/packages/ast-spec/src/declaration/TSDeclareFunction/fixtures/_error_/async/snapshots/1-TSESTree-Error.shot +++ b/packages/ast-spec/src/declaration/TSDeclareFunction/fixtures/_error_/async/snapshots/1-TSESTree-Error.shot @@ -1,3 +1,8 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`AST Fixtures declaration TSDeclareFunction _error_ async TSESTree - Error 1`] = `"NO ERROR"`; +exports[`AST Fixtures declaration TSDeclareFunction _error_ async TSESTree - Error 1`] = ` +"TSError +> 1 | declare async function foo(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 'async' modifier cannot be used in an ambient context. + 2 |" +`; diff --git a/packages/ast-spec/src/declaration/TSDeclareFunction/fixtures/_error_/async/snapshots/3-Alignment-Error.shot b/packages/ast-spec/src/declaration/TSDeclareFunction/fixtures/_error_/async/snapshots/3-Alignment-Error.shot index 1f36cb0955fb..67e64f02351d 100644 --- a/packages/ast-spec/src/declaration/TSDeclareFunction/fixtures/_error_/async/snapshots/3-Alignment-Error.shot +++ b/packages/ast-spec/src/declaration/TSDeclareFunction/fixtures/_error_/async/snapshots/3-Alignment-Error.shot @@ -1,3 +1,3 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`AST Fixtures declaration TSDeclareFunction _error_ async Error Alignment 1`] = `"Babel errored but TSESTree didn't"`; +exports[`AST Fixtures declaration TSDeclareFunction _error_ async Error Alignment 1`] = `"Both errored"`; diff --git a/packages/ast-spec/src/declaration/TSDeclareFunction/fixtures/_error_/declare-with-body/snapshots/1-TSESTree-Error.shot b/packages/ast-spec/src/declaration/TSDeclareFunction/fixtures/_error_/declare-with-body/snapshots/1-TSESTree-Error.shot index ca0dba2a0c34..c8a50d813ca8 100644 --- a/packages/ast-spec/src/declaration/TSDeclareFunction/fixtures/_error_/declare-with-body/snapshots/1-TSESTree-Error.shot +++ b/packages/ast-spec/src/declaration/TSDeclareFunction/fixtures/_error_/declare-with-body/snapshots/1-TSESTree-Error.shot @@ -1,3 +1,8 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`AST Fixtures declaration TSDeclareFunction _error_ declare-with-body TSESTree - Error 1`] = `"NO ERROR"`; +exports[`AST Fixtures declaration TSDeclareFunction _error_ declare-with-body TSESTree - Error 1`] = ` +"TSError +> 1 | declare function foo(): void {}; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ An implementation cannot be declared in ambient contexts. + 2 |" +`; diff --git a/packages/ast-spec/src/declaration/TSDeclareFunction/fixtures/_error_/declare-with-body/snapshots/3-Alignment-Error.shot b/packages/ast-spec/src/declaration/TSDeclareFunction/fixtures/_error_/declare-with-body/snapshots/3-Alignment-Error.shot index 22b125b56439..5bb326776847 100644 --- a/packages/ast-spec/src/declaration/TSDeclareFunction/fixtures/_error_/declare-with-body/snapshots/3-Alignment-Error.shot +++ b/packages/ast-spec/src/declaration/TSDeclareFunction/fixtures/_error_/declare-with-body/snapshots/3-Alignment-Error.shot @@ -1,3 +1,3 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`AST Fixtures declaration TSDeclareFunction _error_ declare-with-body Error Alignment 1`] = `"Babel errored but TSESTree didn't"`; +exports[`AST Fixtures declaration TSDeclareFunction _error_ declare-with-body Error Alignment 1`] = `"Both errored"`; diff --git a/packages/ast-spec/src/declaration/TSDeclareFunction/fixtures/_error_/generator-ambient/fixture.ts b/packages/ast-spec/src/declaration/TSDeclareFunction/fixtures/_error_/generator-ambient/fixture.ts new file mode 100644 index 000000000000..3bc9eccf0977 --- /dev/null +++ b/packages/ast-spec/src/declaration/TSDeclareFunction/fixtures/_error_/generator-ambient/fixture.ts @@ -0,0 +1,3 @@ +declare module "x" { + function* foo(): any; +} diff --git a/packages/ast-spec/src/declaration/TSDeclareFunction/fixtures/_error_/generator-ambient/snapshots/1-TSESTree-Error.shot b/packages/ast-spec/src/declaration/TSDeclareFunction/fixtures/_error_/generator-ambient/snapshots/1-TSESTree-Error.shot new file mode 100644 index 000000000000..804b5bd1e90f --- /dev/null +++ b/packages/ast-spec/src/declaration/TSDeclareFunction/fixtures/_error_/generator-ambient/snapshots/1-TSESTree-Error.shot @@ -0,0 +1,10 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`AST Fixtures declaration TSDeclareFunction _error_ generator-ambient TSESTree - Error 1`] = ` +"TSError + 1 | declare module "x" { +> 2 | function* foo(): any; + | ^^^^^^^^^^^^^^^^^^^^^ A function signature cannot be declared as a generator. + 3 | } + 4 |" +`; diff --git a/packages/ast-spec/src/declaration/TSDeclareFunction/fixtures/_error_/generator-ambient/snapshots/2-Babel-Error.shot b/packages/ast-spec/src/declaration/TSDeclareFunction/fixtures/_error_/generator-ambient/snapshots/2-Babel-Error.shot new file mode 100644 index 000000000000..a91598605cdc --- /dev/null +++ b/packages/ast-spec/src/declaration/TSDeclareFunction/fixtures/_error_/generator-ambient/snapshots/2-Babel-Error.shot @@ -0,0 +1,3 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`AST Fixtures declaration TSDeclareFunction _error_ generator-ambient Babel - Error 1`] = `"NO ERROR"`; diff --git a/packages/ast-spec/src/declaration/TSDeclareFunction/fixtures/_error_/generator-ambient/snapshots/3-Alignment-Error.shot b/packages/ast-spec/src/declaration/TSDeclareFunction/fixtures/_error_/generator-ambient/snapshots/3-Alignment-Error.shot new file mode 100644 index 000000000000..9a14b2e1b4b2 --- /dev/null +++ b/packages/ast-spec/src/declaration/TSDeclareFunction/fixtures/_error_/generator-ambient/snapshots/3-Alignment-Error.shot @@ -0,0 +1,3 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`AST Fixtures declaration TSDeclareFunction _error_ generator-ambient Error Alignment 1`] = `"TSESTree errored but Babel didn't"`; diff --git a/packages/ast-spec/src/declaration/TSDeclareFunction/fixtures/_error_/generator-overload/fixture.ts b/packages/ast-spec/src/declaration/TSDeclareFunction/fixtures/_error_/generator-overload/fixture.ts new file mode 100644 index 000000000000..b3985fb6a6e5 --- /dev/null +++ b/packages/ast-spec/src/declaration/TSDeclareFunction/fixtures/_error_/generator-overload/fixture.ts @@ -0,0 +1,2 @@ +function* foo(): any; +function* foo(): any {} diff --git a/packages/ast-spec/src/declaration/TSDeclareFunction/fixtures/_error_/generator-overload/snapshots/1-TSESTree-Error.shot b/packages/ast-spec/src/declaration/TSDeclareFunction/fixtures/_error_/generator-overload/snapshots/1-TSESTree-Error.shot new file mode 100644 index 000000000000..50e0462fd03e --- /dev/null +++ b/packages/ast-spec/src/declaration/TSDeclareFunction/fixtures/_error_/generator-overload/snapshots/1-TSESTree-Error.shot @@ -0,0 +1,9 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`AST Fixtures declaration TSDeclareFunction _error_ generator-overload TSESTree - Error 1`] = ` +"TSError +> 1 | function* foo(): any; + | ^^^^^^^^^^^^^^^^^^^^^ A function signature cannot be declared as a generator. + 2 | function* foo(): any {} + 3 |" +`; diff --git a/packages/ast-spec/src/declaration/TSDeclareFunction/fixtures/_error_/generator-overload/snapshots/2-Babel-Error.shot b/packages/ast-spec/src/declaration/TSDeclareFunction/fixtures/_error_/generator-overload/snapshots/2-Babel-Error.shot new file mode 100644 index 000000000000..bf3d8c4f43d0 --- /dev/null +++ b/packages/ast-spec/src/declaration/TSDeclareFunction/fixtures/_error_/generator-overload/snapshots/2-Babel-Error.shot @@ -0,0 +1,3 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`AST Fixtures declaration TSDeclareFunction _error_ generator-overload Babel - Error 1`] = `"NO ERROR"`; diff --git a/packages/ast-spec/src/declaration/TSDeclareFunction/fixtures/_error_/generator-overload/snapshots/3-Alignment-Error.shot b/packages/ast-spec/src/declaration/TSDeclareFunction/fixtures/_error_/generator-overload/snapshots/3-Alignment-Error.shot new file mode 100644 index 000000000000..6c2cab494caa --- /dev/null +++ b/packages/ast-spec/src/declaration/TSDeclareFunction/fixtures/_error_/generator-overload/snapshots/3-Alignment-Error.shot @@ -0,0 +1,3 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`AST Fixtures declaration TSDeclareFunction _error_ generator-overload Error Alignment 1`] = `"TSESTree errored but Babel didn't"`; diff --git a/packages/ast-spec/src/declaration/TSDeclareFunction/fixtures/generator/fixture.ts b/packages/ast-spec/src/declaration/TSDeclareFunction/fixtures/_error_/generator/fixture.ts similarity index 100% rename from packages/ast-spec/src/declaration/TSDeclareFunction/fixtures/generator/fixture.ts rename to packages/ast-spec/src/declaration/TSDeclareFunction/fixtures/_error_/generator/fixture.ts diff --git a/packages/ast-spec/src/declaration/TSDeclareFunction/fixtures/_error_/generator/snapshots/1-TSESTree-Error.shot b/packages/ast-spec/src/declaration/TSDeclareFunction/fixtures/_error_/generator/snapshots/1-TSESTree-Error.shot new file mode 100644 index 000000000000..f5f1b9e07530 --- /dev/null +++ b/packages/ast-spec/src/declaration/TSDeclareFunction/fixtures/_error_/generator/snapshots/1-TSESTree-Error.shot @@ -0,0 +1,8 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`AST Fixtures declaration TSDeclareFunction _error_ generator TSESTree - Error 1`] = ` +"TSError +> 1 | declare function* foo(); + | ^^^^^^^^^^^^^^^^^^^^^^^^ Generators are not allowed in an ambient context. + 2 |" +`; diff --git a/packages/ast-spec/src/declaration/TSDeclareFunction/fixtures/_error_/generator/snapshots/2-Babel-Error.shot b/packages/ast-spec/src/declaration/TSDeclareFunction/fixtures/_error_/generator/snapshots/2-Babel-Error.shot new file mode 100644 index 000000000000..8cac9a65578f --- /dev/null +++ b/packages/ast-spec/src/declaration/TSDeclareFunction/fixtures/_error_/generator/snapshots/2-Babel-Error.shot @@ -0,0 +1,3 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`AST Fixtures declaration TSDeclareFunction _error_ generator Babel - Error 1`] = `"NO ERROR"`; diff --git a/packages/ast-spec/src/declaration/TSDeclareFunction/fixtures/_error_/generator/snapshots/3-Alignment-Error.shot b/packages/ast-spec/src/declaration/TSDeclareFunction/fixtures/_error_/generator/snapshots/3-Alignment-Error.shot new file mode 100644 index 000000000000..c04e69e4e12f --- /dev/null +++ b/packages/ast-spec/src/declaration/TSDeclareFunction/fixtures/_error_/generator/snapshots/3-Alignment-Error.shot @@ -0,0 +1,3 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`AST Fixtures declaration TSDeclareFunction _error_ generator Error Alignment 1`] = `"TSESTree errored but Babel didn't"`; diff --git a/packages/ast-spec/src/declaration/TSDeclareFunction/fixtures/async-ambient/fixture.ts b/packages/ast-spec/src/declaration/TSDeclareFunction/fixtures/async-ambient/fixture.ts new file mode 100644 index 000000000000..b7a7f4a7cd32 --- /dev/null +++ b/packages/ast-spec/src/declaration/TSDeclareFunction/fixtures/async-ambient/fixture.ts @@ -0,0 +1,3 @@ +declare module 'x' { + async function foo(): any; +} diff --git a/packages/ast-spec/src/declaration/TSDeclareFunction/fixtures/async-ambient/snapshots/1-TSESTree-AST.shot b/packages/ast-spec/src/declaration/TSDeclareFunction/fixtures/async-ambient/snapshots/1-TSESTree-AST.shot new file mode 100644 index 000000000000..7d2ab1f0cd84 --- /dev/null +++ b/packages/ast-spec/src/declaration/TSDeclareFunction/fixtures/async-ambient/snapshots/1-TSESTree-AST.shot @@ -0,0 +1,94 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`AST Fixtures declaration TSDeclareFunction async-ambient TSESTree - AST 1`] = ` +Program { + type: "Program", + body: [ + TSModuleDeclaration { + type: "TSModuleDeclaration", + body: TSModuleBlock { + type: "TSModuleBlock", + body: [ + TSDeclareFunction { + type: "TSDeclareFunction", + async: true, + declare: false, + expression: false, + generator: false, + id: Identifier { + type: "Identifier", + decorators: [], + name: "foo", + optional: false, + + range: [38, 41], + loc: { + start: { column: 17, line: 2 }, + end: { column: 20, line: 2 }, + }, + }, + params: [], + returnType: TSTypeAnnotation { + type: "TSTypeAnnotation", + typeAnnotation: TSAnyKeyword { + type: "TSAnyKeyword", + + range: [45, 48], + loc: { + start: { column: 24, line: 2 }, + end: { column: 27, line: 2 }, + }, + }, + + range: [43, 48], + loc: { + start: { column: 22, line: 2 }, + end: { column: 27, line: 2 }, + }, + }, + + range: [23, 49], + loc: { + start: { column: 2, line: 2 }, + end: { column: 28, line: 2 }, + }, + }, + ], + + range: [19, 51], + loc: { + start: { column: 19, line: 1 }, + end: { column: 1, line: 3 }, + }, + }, + declare: true, + global: false, + id: Literal { + type: "Literal", + raw: "'x'", + value: "x", + + range: [15, 18], + loc: { + start: { column: 15, line: 1 }, + end: { column: 18, line: 1 }, + }, + }, + kind: "module", + + range: [0, 51], + loc: { + start: { column: 0, line: 1 }, + end: { column: 1, line: 3 }, + }, + }, + ], + sourceType: "script", + + range: [0, 52], + loc: { + start: { column: 0, line: 1 }, + end: { column: 0, line: 4 }, + }, +} +`; diff --git a/packages/ast-spec/src/declaration/TSDeclareFunction/fixtures/async-ambient/snapshots/2-TSESTree-Tokens.shot b/packages/ast-spec/src/declaration/TSDeclareFunction/fixtures/async-ambient/snapshots/2-TSESTree-Tokens.shot new file mode 100644 index 000000000000..6057be3fa444 --- /dev/null +++ b/packages/ast-spec/src/declaration/TSDeclareFunction/fixtures/async-ambient/snapshots/2-TSESTree-Tokens.shot @@ -0,0 +1,136 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`AST Fixtures declaration TSDeclareFunction async-ambient TSESTree - Tokens 1`] = ` +[ + Identifier { + type: "Identifier", + value: "declare", + + range: [0, 7], + loc: { + start: { column: 0, line: 1 }, + end: { column: 7, line: 1 }, + }, + }, + Identifier { + type: "Identifier", + value: "module", + + range: [8, 14], + loc: { + start: { column: 8, line: 1 }, + end: { column: 14, line: 1 }, + }, + }, + String { + type: "String", + value: "'x'", + + range: [15, 18], + loc: { + start: { column: 15, line: 1 }, + end: { column: 18, line: 1 }, + }, + }, + Punctuator { + type: "Punctuator", + value: "{", + + range: [19, 20], + loc: { + start: { column: 19, line: 1 }, + end: { column: 20, line: 1 }, + }, + }, + Identifier { + type: "Identifier", + value: "async", + + range: [23, 28], + loc: { + start: { column: 2, line: 2 }, + end: { column: 7, line: 2 }, + }, + }, + Keyword { + type: "Keyword", + value: "function", + + range: [29, 37], + loc: { + start: { column: 8, line: 2 }, + end: { column: 16, line: 2 }, + }, + }, + Identifier { + type: "Identifier", + value: "foo", + + range: [38, 41], + loc: { + start: { column: 17, line: 2 }, + end: { column: 20, line: 2 }, + }, + }, + Punctuator { + type: "Punctuator", + value: "(", + + range: [41, 42], + loc: { + start: { column: 20, line: 2 }, + end: { column: 21, line: 2 }, + }, + }, + Punctuator { + type: "Punctuator", + value: ")", + + range: [42, 43], + loc: { + start: { column: 21, line: 2 }, + end: { column: 22, line: 2 }, + }, + }, + Punctuator { + type: "Punctuator", + value: ":", + + range: [43, 44], + loc: { + start: { column: 22, line: 2 }, + end: { column: 23, line: 2 }, + }, + }, + Identifier { + type: "Identifier", + value: "any", + + range: [45, 48], + loc: { + start: { column: 24, line: 2 }, + end: { column: 27, line: 2 }, + }, + }, + Punctuator { + type: "Punctuator", + value: ";", + + range: [48, 49], + loc: { + start: { column: 27, line: 2 }, + end: { column: 28, line: 2 }, + }, + }, + Punctuator { + type: "Punctuator", + value: "}", + + range: [50, 51], + loc: { + start: { column: 0, line: 3 }, + end: { column: 1, line: 3 }, + }, + }, +] +`; diff --git a/packages/ast-spec/src/declaration/TSDeclareFunction/fixtures/async-ambient/snapshots/3-Babel-AST.shot b/packages/ast-spec/src/declaration/TSDeclareFunction/fixtures/async-ambient/snapshots/3-Babel-AST.shot new file mode 100644 index 000000000000..c7ec3ee7ecc0 --- /dev/null +++ b/packages/ast-spec/src/declaration/TSDeclareFunction/fixtures/async-ambient/snapshots/3-Babel-AST.shot @@ -0,0 +1,89 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`AST Fixtures declaration TSDeclareFunction async-ambient Babel - AST 1`] = ` +Program { + type: "Program", + body: [ + TSModuleDeclaration { + type: "TSModuleDeclaration", + body: TSModuleBlock { + type: "TSModuleBlock", + body: [ + TSDeclareFunction { + type: "TSDeclareFunction", + async: true, + expression: false, + generator: false, + id: Identifier { + type: "Identifier", + name: "foo", + + range: [38, 41], + loc: { + start: { column: 17, line: 2 }, + end: { column: 20, line: 2 }, + }, + }, + params: [], + returnType: TSTypeAnnotation { + type: "TSTypeAnnotation", + typeAnnotation: TSAnyKeyword { + type: "TSAnyKeyword", + + range: [45, 48], + loc: { + start: { column: 24, line: 2 }, + end: { column: 27, line: 2 }, + }, + }, + + range: [43, 48], + loc: { + start: { column: 22, line: 2 }, + end: { column: 27, line: 2 }, + }, + }, + + range: [23, 49], + loc: { + start: { column: 2, line: 2 }, + end: { column: 28, line: 2 }, + }, + }, + ], + + range: [19, 51], + loc: { + start: { column: 19, line: 1 }, + end: { column: 1, line: 3 }, + }, + }, + declare: true, + id: Literal { + type: "Literal", + raw: "'x'", + value: "x", + + range: [15, 18], + loc: { + start: { column: 15, line: 1 }, + end: { column: 18, line: 1 }, + }, + }, + + range: [0, 51], + loc: { + start: { column: 0, line: 1 }, + end: { column: 1, line: 3 }, + }, + }, + ], + sourceType: "script", + + range: [0, 52], + loc: { + start: { column: 0, line: 1 }, + end: { column: 0, line: 4 }, + }, +} +`; diff --git a/packages/ast-spec/src/declaration/TSDeclareFunction/fixtures/async-ambient/snapshots/4-Babel-Tokens.shot b/packages/ast-spec/src/declaration/TSDeclareFunction/fixtures/async-ambient/snapshots/4-Babel-Tokens.shot new file mode 100644 index 000000000000..d78930b42a75 --- /dev/null +++ b/packages/ast-spec/src/declaration/TSDeclareFunction/fixtures/async-ambient/snapshots/4-Babel-Tokens.shot @@ -0,0 +1,136 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`AST Fixtures declaration TSDeclareFunction async-ambient Babel - Tokens 1`] = ` +[ + Identifier { + type: "Identifier", + value: "declare", + + range: [0, 7], + loc: { + start: { column: 0, line: 1 }, + end: { column: 7, line: 1 }, + }, + }, + Identifier { + type: "Identifier", + value: "module", + + range: [8, 14], + loc: { + start: { column: 8, line: 1 }, + end: { column: 14, line: 1 }, + }, + }, + String { + type: "String", + value: "'x'", + + range: [15, 18], + loc: { + start: { column: 15, line: 1 }, + end: { column: 18, line: 1 }, + }, + }, + Punctuator { + type: "Punctuator", + value: "{", + + range: [19, 20], + loc: { + start: { column: 19, line: 1 }, + end: { column: 20, line: 1 }, + }, + }, + Identifier { + type: "Identifier", + value: "async", + + range: [23, 28], + loc: { + start: { column: 2, line: 2 }, + end: { column: 7, line: 2 }, + }, + }, + Keyword { + type: "Keyword", + value: "function", + + range: [29, 37], + loc: { + start: { column: 8, line: 2 }, + end: { column: 16, line: 2 }, + }, + }, + Identifier { + type: "Identifier", + value: "foo", + + range: [38, 41], + loc: { + start: { column: 17, line: 2 }, + end: { column: 20, line: 2 }, + }, + }, + Punctuator { + type: "Punctuator", + value: "(", + + range: [41, 42], + loc: { + start: { column: 20, line: 2 }, + end: { column: 21, line: 2 }, + }, + }, + Punctuator { + type: "Punctuator", + value: ")", + + range: [42, 43], + loc: { + start: { column: 21, line: 2 }, + end: { column: 22, line: 2 }, + }, + }, + Punctuator { + type: "Punctuator", + value: ":", + + range: [43, 44], + loc: { + start: { column: 22, line: 2 }, + end: { column: 23, line: 2 }, + }, + }, + Identifier { + type: "Identifier", + value: "any", + + range: [45, 48], + loc: { + start: { column: 24, line: 2 }, + end: { column: 27, line: 2 }, + }, + }, + Punctuator { + type: "Punctuator", + value: ";", + + range: [48, 49], + loc: { + start: { column: 27, line: 2 }, + end: { column: 28, line: 2 }, + }, + }, + Punctuator { + type: "Punctuator", + value: "}", + + range: [50, 51], + loc: { + start: { column: 0, line: 3 }, + end: { column: 1, line: 3 }, + }, + }, +] +`; diff --git a/packages/ast-spec/src/declaration/TSDeclareFunction/fixtures/async-ambient/snapshots/5-AST-Alignment-AST.shot b/packages/ast-spec/src/declaration/TSDeclareFunction/fixtures/async-ambient/snapshots/5-AST-Alignment-AST.shot new file mode 100644 index 000000000000..435ae1de0ade --- /dev/null +++ b/packages/ast-spec/src/declaration/TSDeclareFunction/fixtures/async-ambient/snapshots/5-AST-Alignment-AST.shot @@ -0,0 +1,98 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`AST Fixtures declaration TSDeclareFunction async-ambient AST Alignment - AST 1`] = ` +"Snapshot Diff: +- TSESTree ++ Babel + + Program { + type: 'Program', + body: Array [ + TSModuleDeclaration { + type: 'TSModuleDeclaration', + body: TSModuleBlock { + type: 'TSModuleBlock', + body: Array [ + TSDeclareFunction { + type: 'TSDeclareFunction', + async: true, +- declare: false, + expression: false, + generator: false, + id: Identifier { + type: 'Identifier', +- decorators: Array [], + name: 'foo', +- optional: false, + + range: [38, 41], + loc: { + start: { column: 17, line: 2 }, + end: { column: 20, line: 2 }, + }, + }, + params: Array [], + returnType: TSTypeAnnotation { + type: 'TSTypeAnnotation', + typeAnnotation: TSAnyKeyword { + type: 'TSAnyKeyword', + + range: [45, 48], + loc: { + start: { column: 24, line: 2 }, + end: { column: 27, line: 2 }, + }, + }, + + range: [43, 48], + loc: { + start: { column: 22, line: 2 }, + end: { column: 27, line: 2 }, + }, + }, + + range: [23, 49], + loc: { + start: { column: 2, line: 2 }, + end: { column: 28, line: 2 }, + }, + }, + ], + + range: [19, 51], + loc: { + start: { column: 19, line: 1 }, + end: { column: 1, line: 3 }, + }, + }, + declare: true, +- global: false, + id: Literal { + type: 'Literal', + raw: '\\'x\\'', + value: 'x', + + range: [15, 18], + loc: { + start: { column: 15, line: 1 }, + end: { column: 18, line: 1 }, + }, + }, +- kind: 'module', + + range: [0, 51], + loc: { + start: { column: 0, line: 1 }, + end: { column: 1, line: 3 }, + }, + }, + ], + sourceType: 'script', + + range: [0, 52], + loc: { + start: { column: 0, line: 1 }, + end: { column: 0, line: 4 }, + }, + }" +`; diff --git a/packages/ast-spec/src/declaration/TSDeclareFunction/fixtures/generator/snapshots/6-AST-Alignment-Tokens.shot b/packages/ast-spec/src/declaration/TSDeclareFunction/fixtures/async-ambient/snapshots/6-AST-Alignment-Tokens.shot similarity index 52% rename from packages/ast-spec/src/declaration/TSDeclareFunction/fixtures/generator/snapshots/6-AST-Alignment-Tokens.shot rename to packages/ast-spec/src/declaration/TSDeclareFunction/fixtures/async-ambient/snapshots/6-AST-Alignment-Tokens.shot index 0c5870a8a61e..7dbd5a907254 100644 --- a/packages/ast-spec/src/declaration/TSDeclareFunction/fixtures/generator/snapshots/6-AST-Alignment-Tokens.shot +++ b/packages/ast-spec/src/declaration/TSDeclareFunction/fixtures/async-ambient/snapshots/6-AST-Alignment-Tokens.shot @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`AST Fixtures declaration TSDeclareFunction generator AST Alignment - Token 1`] = ` +exports[`AST Fixtures declaration TSDeclareFunction async-ambient AST Alignment - Token 1`] = ` "Snapshot Diff: Compared values have no visual difference." `; diff --git a/packages/ast-spec/src/declaration/TSDeclareFunction/fixtures/async-overload/fixture.ts b/packages/ast-spec/src/declaration/TSDeclareFunction/fixtures/async-overload/fixture.ts new file mode 100644 index 000000000000..31dd56160e4c --- /dev/null +++ b/packages/ast-spec/src/declaration/TSDeclareFunction/fixtures/async-overload/fixture.ts @@ -0,0 +1,2 @@ +async function foo(): any; +async function foo(): any {} diff --git a/packages/ast-spec/src/declaration/TSDeclareFunction/fixtures/async-overload/snapshots/1-TSESTree-AST.shot b/packages/ast-spec/src/declaration/TSDeclareFunction/fixtures/async-overload/snapshots/1-TSESTree-AST.shot new file mode 100644 index 000000000000..325d2fb0435a --- /dev/null +++ b/packages/ast-spec/src/declaration/TSDeclareFunction/fixtures/async-overload/snapshots/1-TSESTree-AST.shot @@ -0,0 +1,114 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`AST Fixtures declaration TSDeclareFunction async-overload TSESTree - AST 1`] = ` +Program { + type: "Program", + body: [ + TSDeclareFunction { + type: "TSDeclareFunction", + async: true, + declare: false, + expression: false, + generator: false, + id: Identifier { + type: "Identifier", + decorators: [], + name: "foo", + optional: false, + + range: [15, 18], + loc: { + start: { column: 15, line: 1 }, + end: { column: 18, line: 1 }, + }, + }, + params: [], + returnType: TSTypeAnnotation { + type: "TSTypeAnnotation", + typeAnnotation: TSAnyKeyword { + type: "TSAnyKeyword", + + range: [22, 25], + loc: { + start: { column: 22, line: 1 }, + end: { column: 25, line: 1 }, + }, + }, + + range: [20, 25], + loc: { + start: { column: 20, line: 1 }, + end: { column: 25, line: 1 }, + }, + }, + + range: [0, 26], + loc: { + start: { column: 0, line: 1 }, + end: { column: 26, line: 1 }, + }, + }, + FunctionDeclaration { + type: "FunctionDeclaration", + async: true, + body: BlockStatement { + type: "BlockStatement", + body: [], + + range: [53, 55], + loc: { + start: { column: 26, line: 2 }, + end: { column: 28, line: 2 }, + }, + }, + declare: false, + expression: false, + generator: false, + id: Identifier { + type: "Identifier", + decorators: [], + name: "foo", + optional: false, + + range: [42, 45], + loc: { + start: { column: 15, line: 2 }, + end: { column: 18, line: 2 }, + }, + }, + params: [], + returnType: TSTypeAnnotation { + type: "TSTypeAnnotation", + typeAnnotation: TSAnyKeyword { + type: "TSAnyKeyword", + + range: [49, 52], + loc: { + start: { column: 22, line: 2 }, + end: { column: 25, line: 2 }, + }, + }, + + range: [47, 52], + loc: { + start: { column: 20, line: 2 }, + end: { column: 25, line: 2 }, + }, + }, + + range: [27, 55], + loc: { + start: { column: 0, line: 2 }, + end: { column: 28, line: 2 }, + }, + }, + ], + sourceType: "script", + + range: [0, 56], + loc: { + start: { column: 0, line: 1 }, + end: { column: 0, line: 3 }, + }, +} +`; diff --git a/packages/ast-spec/src/declaration/TSDeclareFunction/fixtures/async-overload/snapshots/2-TSESTree-Tokens.shot b/packages/ast-spec/src/declaration/TSDeclareFunction/fixtures/async-overload/snapshots/2-TSESTree-Tokens.shot new file mode 100644 index 000000000000..213de06b48f9 --- /dev/null +++ b/packages/ast-spec/src/declaration/TSDeclareFunction/fixtures/async-overload/snapshots/2-TSESTree-Tokens.shot @@ -0,0 +1,176 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`AST Fixtures declaration TSDeclareFunction async-overload TSESTree - Tokens 1`] = ` +[ + Identifier { + type: "Identifier", + value: "async", + + range: [0, 5], + loc: { + start: { column: 0, line: 1 }, + end: { column: 5, line: 1 }, + }, + }, + Keyword { + type: "Keyword", + value: "function", + + range: [6, 14], + loc: { + start: { column: 6, line: 1 }, + end: { column: 14, line: 1 }, + }, + }, + Identifier { + type: "Identifier", + value: "foo", + + range: [15, 18], + loc: { + start: { column: 15, line: 1 }, + end: { column: 18, line: 1 }, + }, + }, + Punctuator { + type: "Punctuator", + value: "(", + + range: [18, 19], + loc: { + start: { column: 18, line: 1 }, + end: { column: 19, line: 1 }, + }, + }, + Punctuator { + type: "Punctuator", + value: ")", + + range: [19, 20], + loc: { + start: { column: 19, line: 1 }, + end: { column: 20, line: 1 }, + }, + }, + Punctuator { + type: "Punctuator", + value: ":", + + range: [20, 21], + loc: { + start: { column: 20, line: 1 }, + end: { column: 21, line: 1 }, + }, + }, + Identifier { + type: "Identifier", + value: "any", + + range: [22, 25], + loc: { + start: { column: 22, line: 1 }, + end: { column: 25, line: 1 }, + }, + }, + Punctuator { + type: "Punctuator", + value: ";", + + range: [25, 26], + loc: { + start: { column: 25, line: 1 }, + end: { column: 26, line: 1 }, + }, + }, + Identifier { + type: "Identifier", + value: "async", + + range: [27, 32], + loc: { + start: { column: 0, line: 2 }, + end: { column: 5, line: 2 }, + }, + }, + Keyword { + type: "Keyword", + value: "function", + + range: [33, 41], + loc: { + start: { column: 6, line: 2 }, + end: { column: 14, line: 2 }, + }, + }, + Identifier { + type: "Identifier", + value: "foo", + + range: [42, 45], + loc: { + start: { column: 15, line: 2 }, + end: { column: 18, line: 2 }, + }, + }, + Punctuator { + type: "Punctuator", + value: "(", + + range: [45, 46], + loc: { + start: { column: 18, line: 2 }, + end: { column: 19, line: 2 }, + }, + }, + Punctuator { + type: "Punctuator", + value: ")", + + range: [46, 47], + loc: { + start: { column: 19, line: 2 }, + end: { column: 20, line: 2 }, + }, + }, + Punctuator { + type: "Punctuator", + value: ":", + + range: [47, 48], + loc: { + start: { column: 20, line: 2 }, + end: { column: 21, line: 2 }, + }, + }, + Identifier { + type: "Identifier", + value: "any", + + range: [49, 52], + loc: { + start: { column: 22, line: 2 }, + end: { column: 25, line: 2 }, + }, + }, + Punctuator { + type: "Punctuator", + value: "{", + + range: [53, 54], + loc: { + start: { column: 26, line: 2 }, + end: { column: 27, line: 2 }, + }, + }, + Punctuator { + type: "Punctuator", + value: "}", + + range: [54, 55], + loc: { + start: { column: 27, line: 2 }, + end: { column: 28, line: 2 }, + }, + }, +] +`; diff --git a/packages/ast-spec/src/declaration/TSDeclareFunction/fixtures/async-overload/snapshots/3-Babel-AST.shot b/packages/ast-spec/src/declaration/TSDeclareFunction/fixtures/async-overload/snapshots/3-Babel-AST.shot new file mode 100644 index 000000000000..09174cb2b0e4 --- /dev/null +++ b/packages/ast-spec/src/declaration/TSDeclareFunction/fixtures/async-overload/snapshots/3-Babel-AST.shot @@ -0,0 +1,108 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`AST Fixtures declaration TSDeclareFunction async-overload Babel - AST 1`] = ` +Program { + type: "Program", + body: [ + TSDeclareFunction { + type: "TSDeclareFunction", + async: true, + expression: false, + generator: false, + id: Identifier { + type: "Identifier", + name: "foo", + + range: [15, 18], + loc: { + start: { column: 15, line: 1 }, + end: { column: 18, line: 1 }, + }, + }, + params: [], + returnType: TSTypeAnnotation { + type: "TSTypeAnnotation", + typeAnnotation: TSAnyKeyword { + type: "TSAnyKeyword", + + range: [22, 25], + loc: { + start: { column: 22, line: 1 }, + end: { column: 25, line: 1 }, + }, + }, + + range: [20, 25], + loc: { + start: { column: 20, line: 1 }, + end: { column: 25, line: 1 }, + }, + }, + + range: [0, 26], + loc: { + start: { column: 0, line: 1 }, + end: { column: 26, line: 1 }, + }, + }, + FunctionDeclaration { + type: "FunctionDeclaration", + async: true, + body: BlockStatement { + type: "BlockStatement", + body: [], + + range: [53, 55], + loc: { + start: { column: 26, line: 2 }, + end: { column: 28, line: 2 }, + }, + }, + expression: false, + generator: false, + id: Identifier { + type: "Identifier", + name: "foo", + + range: [42, 45], + loc: { + start: { column: 15, line: 2 }, + end: { column: 18, line: 2 }, + }, + }, + params: [], + returnType: TSTypeAnnotation { + type: "TSTypeAnnotation", + typeAnnotation: TSAnyKeyword { + type: "TSAnyKeyword", + + range: [49, 52], + loc: { + start: { column: 22, line: 2 }, + end: { column: 25, line: 2 }, + }, + }, + + range: [47, 52], + loc: { + start: { column: 20, line: 2 }, + end: { column: 25, line: 2 }, + }, + }, + + range: [27, 55], + loc: { + start: { column: 0, line: 2 }, + end: { column: 28, line: 2 }, + }, + }, + ], + sourceType: "script", + + range: [0, 56], + loc: { + start: { column: 0, line: 1 }, + end: { column: 0, line: 3 }, + }, +} +`; diff --git a/packages/ast-spec/src/declaration/TSDeclareFunction/fixtures/async-overload/snapshots/4-Babel-Tokens.shot b/packages/ast-spec/src/declaration/TSDeclareFunction/fixtures/async-overload/snapshots/4-Babel-Tokens.shot new file mode 100644 index 000000000000..ca4d328c84d9 --- /dev/null +++ b/packages/ast-spec/src/declaration/TSDeclareFunction/fixtures/async-overload/snapshots/4-Babel-Tokens.shot @@ -0,0 +1,176 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`AST Fixtures declaration TSDeclareFunction async-overload Babel - Tokens 1`] = ` +[ + Identifier { + type: "Identifier", + value: "async", + + range: [0, 5], + loc: { + start: { column: 0, line: 1 }, + end: { column: 5, line: 1 }, + }, + }, + Keyword { + type: "Keyword", + value: "function", + + range: [6, 14], + loc: { + start: { column: 6, line: 1 }, + end: { column: 14, line: 1 }, + }, + }, + Identifier { + type: "Identifier", + value: "foo", + + range: [15, 18], + loc: { + start: { column: 15, line: 1 }, + end: { column: 18, line: 1 }, + }, + }, + Punctuator { + type: "Punctuator", + value: "(", + + range: [18, 19], + loc: { + start: { column: 18, line: 1 }, + end: { column: 19, line: 1 }, + }, + }, + Punctuator { + type: "Punctuator", + value: ")", + + range: [19, 20], + loc: { + start: { column: 19, line: 1 }, + end: { column: 20, line: 1 }, + }, + }, + Punctuator { + type: "Punctuator", + value: ":", + + range: [20, 21], + loc: { + start: { column: 20, line: 1 }, + end: { column: 21, line: 1 }, + }, + }, + Identifier { + type: "Identifier", + value: "any", + + range: [22, 25], + loc: { + start: { column: 22, line: 1 }, + end: { column: 25, line: 1 }, + }, + }, + Punctuator { + type: "Punctuator", + value: ";", + + range: [25, 26], + loc: { + start: { column: 25, line: 1 }, + end: { column: 26, line: 1 }, + }, + }, + Identifier { + type: "Identifier", + value: "async", + + range: [27, 32], + loc: { + start: { column: 0, line: 2 }, + end: { column: 5, line: 2 }, + }, + }, + Keyword { + type: "Keyword", + value: "function", + + range: [33, 41], + loc: { + start: { column: 6, line: 2 }, + end: { column: 14, line: 2 }, + }, + }, + Identifier { + type: "Identifier", + value: "foo", + + range: [42, 45], + loc: { + start: { column: 15, line: 2 }, + end: { column: 18, line: 2 }, + }, + }, + Punctuator { + type: "Punctuator", + value: "(", + + range: [45, 46], + loc: { + start: { column: 18, line: 2 }, + end: { column: 19, line: 2 }, + }, + }, + Punctuator { + type: "Punctuator", + value: ")", + + range: [46, 47], + loc: { + start: { column: 19, line: 2 }, + end: { column: 20, line: 2 }, + }, + }, + Punctuator { + type: "Punctuator", + value: ":", + + range: [47, 48], + loc: { + start: { column: 20, line: 2 }, + end: { column: 21, line: 2 }, + }, + }, + Identifier { + type: "Identifier", + value: "any", + + range: [49, 52], + loc: { + start: { column: 22, line: 2 }, + end: { column: 25, line: 2 }, + }, + }, + Punctuator { + type: "Punctuator", + value: "{", + + range: [53, 54], + loc: { + start: { column: 26, line: 2 }, + end: { column: 27, line: 2 }, + }, + }, + Punctuator { + type: "Punctuator", + value: "}", + + range: [54, 55], + loc: { + start: { column: 27, line: 2 }, + end: { column: 28, line: 2 }, + }, + }, +] +`; diff --git a/packages/ast-spec/src/declaration/TSDeclareFunction/fixtures/async-overload/snapshots/5-AST-Alignment-AST.shot b/packages/ast-spec/src/declaration/TSDeclareFunction/fixtures/async-overload/snapshots/5-AST-Alignment-AST.shot new file mode 100644 index 000000000000..2515ee9a01e1 --- /dev/null +++ b/packages/ast-spec/src/declaration/TSDeclareFunction/fixtures/async-overload/snapshots/5-AST-Alignment-AST.shot @@ -0,0 +1,118 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`AST Fixtures declaration TSDeclareFunction async-overload AST Alignment - AST 1`] = ` +"Snapshot Diff: +- TSESTree ++ Babel + + Program { + type: 'Program', + body: Array [ + TSDeclareFunction { + type: 'TSDeclareFunction', + async: true, +- declare: false, + expression: false, + generator: false, + id: Identifier { + type: 'Identifier', +- decorators: Array [], + name: 'foo', +- optional: false, + + range: [15, 18], + loc: { + start: { column: 15, line: 1 }, + end: { column: 18, line: 1 }, + }, + }, + params: Array [], + returnType: TSTypeAnnotation { + type: 'TSTypeAnnotation', + typeAnnotation: TSAnyKeyword { + type: 'TSAnyKeyword', + + range: [22, 25], + loc: { + start: { column: 22, line: 1 }, + end: { column: 25, line: 1 }, + }, + }, + + range: [20, 25], + loc: { + start: { column: 20, line: 1 }, + end: { column: 25, line: 1 }, + }, + }, + + range: [0, 26], + loc: { + start: { column: 0, line: 1 }, + end: { column: 26, line: 1 }, + }, + }, + FunctionDeclaration { + type: 'FunctionDeclaration', + async: true, + body: BlockStatement { + type: 'BlockStatement', + body: Array [], + + range: [53, 55], + loc: { + start: { column: 26, line: 2 }, + end: { column: 28, line: 2 }, + }, + }, +- declare: false, + expression: false, + generator: false, + id: Identifier { + type: 'Identifier', +- decorators: Array [], + name: 'foo', +- optional: false, + + range: [42, 45], + loc: { + start: { column: 15, line: 2 }, + end: { column: 18, line: 2 }, + }, + }, + params: Array [], + returnType: TSTypeAnnotation { + type: 'TSTypeAnnotation', + typeAnnotation: TSAnyKeyword { + type: 'TSAnyKeyword', + + range: [49, 52], + loc: { + start: { column: 22, line: 2 }, + end: { column: 25, line: 2 }, + }, + }, + + range: [47, 52], + loc: { + start: { column: 20, line: 2 }, + end: { column: 25, line: 2 }, + }, + }, + + range: [27, 55], + loc: { + start: { column: 0, line: 2 }, + end: { column: 28, line: 2 }, + }, + }, + ], + sourceType: 'script', + + range: [0, 56], + loc: { + start: { column: 0, line: 1 }, + end: { column: 0, line: 3 }, + }, + }" +`; diff --git a/packages/ast-spec/src/declaration/TSDeclareFunction/fixtures/async-overload/snapshots/6-AST-Alignment-Tokens.shot b/packages/ast-spec/src/declaration/TSDeclareFunction/fixtures/async-overload/snapshots/6-AST-Alignment-Tokens.shot new file mode 100644 index 000000000000..b6b9fa4d737e --- /dev/null +++ b/packages/ast-spec/src/declaration/TSDeclareFunction/fixtures/async-overload/snapshots/6-AST-Alignment-Tokens.shot @@ -0,0 +1,6 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`AST Fixtures declaration TSDeclareFunction async-overload AST Alignment - Token 1`] = ` +"Snapshot Diff: +Compared values have no visual difference." +`; diff --git a/packages/ast-spec/src/declaration/TSDeclareFunction/fixtures/generator/snapshots/1-TSESTree-AST.shot b/packages/ast-spec/src/declaration/TSDeclareFunction/fixtures/generator/snapshots/1-TSESTree-AST.shot deleted file mode 100644 index b621f52027bf..000000000000 --- a/packages/ast-spec/src/declaration/TSDeclareFunction/fixtures/generator/snapshots/1-TSESTree-AST.shot +++ /dev/null @@ -1,42 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`AST Fixtures declaration TSDeclareFunction generator TSESTree - AST 1`] = ` -Program { - type: "Program", - body: [ - TSDeclareFunction { - type: "TSDeclareFunction", - async: false, - declare: true, - expression: false, - generator: true, - id: Identifier { - type: "Identifier", - decorators: [], - name: "foo", - optional: false, - - range: [18, 21], - loc: { - start: { column: 18, line: 1 }, - end: { column: 21, line: 1 }, - }, - }, - params: [], - - range: [0, 24], - loc: { - start: { column: 0, line: 1 }, - end: { column: 24, line: 1 }, - }, - }, - ], - sourceType: "script", - - range: [0, 25], - loc: { - start: { column: 0, line: 1 }, - end: { column: 0, line: 2 }, - }, -} -`; diff --git a/packages/ast-spec/src/declaration/TSDeclareFunction/fixtures/generator/snapshots/2-TSESTree-Tokens.shot b/packages/ast-spec/src/declaration/TSDeclareFunction/fixtures/generator/snapshots/2-TSESTree-Tokens.shot deleted file mode 100644 index 42e396c00992..000000000000 --- a/packages/ast-spec/src/declaration/TSDeclareFunction/fixtures/generator/snapshots/2-TSESTree-Tokens.shot +++ /dev/null @@ -1,76 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`AST Fixtures declaration TSDeclareFunction generator TSESTree - Tokens 1`] = ` -[ - Identifier { - type: "Identifier", - value: "declare", - - range: [0, 7], - loc: { - start: { column: 0, line: 1 }, - end: { column: 7, line: 1 }, - }, - }, - Keyword { - type: "Keyword", - value: "function", - - range: [8, 16], - loc: { - start: { column: 8, line: 1 }, - end: { column: 16, line: 1 }, - }, - }, - Punctuator { - type: "Punctuator", - value: "*", - - range: [16, 17], - loc: { - start: { column: 16, line: 1 }, - end: { column: 17, line: 1 }, - }, - }, - Identifier { - type: "Identifier", - value: "foo", - - range: [18, 21], - loc: { - start: { column: 18, line: 1 }, - end: { column: 21, line: 1 }, - }, - }, - Punctuator { - type: "Punctuator", - value: "(", - - range: [21, 22], - loc: { - start: { column: 21, line: 1 }, - end: { column: 22, line: 1 }, - }, - }, - Punctuator { - type: "Punctuator", - value: ")", - - range: [22, 23], - loc: { - start: { column: 22, line: 1 }, - end: { column: 23, line: 1 }, - }, - }, - Punctuator { - type: "Punctuator", - value: ";", - - range: [23, 24], - loc: { - start: { column: 23, line: 1 }, - end: { column: 24, line: 1 }, - }, - }, -] -`; diff --git a/packages/ast-spec/src/declaration/TSDeclareFunction/fixtures/generator/snapshots/3-Babel-AST.shot b/packages/ast-spec/src/declaration/TSDeclareFunction/fixtures/generator/snapshots/3-Babel-AST.shot deleted file mode 100644 index 26ccbf892c14..000000000000 --- a/packages/ast-spec/src/declaration/TSDeclareFunction/fixtures/generator/snapshots/3-Babel-AST.shot +++ /dev/null @@ -1,40 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`AST Fixtures declaration TSDeclareFunction generator Babel - AST 1`] = ` -Program { - type: "Program", - body: [ - TSDeclareFunction { - type: "TSDeclareFunction", - async: false, - declare: true, - expression: false, - generator: true, - id: Identifier { - type: "Identifier", - name: "foo", - - range: [18, 21], - loc: { - start: { column: 18, line: 1 }, - end: { column: 21, line: 1 }, - }, - }, - params: [], - - range: [0, 24], - loc: { - start: { column: 0, line: 1 }, - end: { column: 24, line: 1 }, - }, - }, - ], - sourceType: "script", - - range: [0, 25], - loc: { - start: { column: 0, line: 1 }, - end: { column: 0, line: 2 }, - }, -} -`; diff --git a/packages/ast-spec/src/declaration/TSDeclareFunction/fixtures/generator/snapshots/4-Babel-Tokens.shot b/packages/ast-spec/src/declaration/TSDeclareFunction/fixtures/generator/snapshots/4-Babel-Tokens.shot deleted file mode 100644 index 76c88da05a16..000000000000 --- a/packages/ast-spec/src/declaration/TSDeclareFunction/fixtures/generator/snapshots/4-Babel-Tokens.shot +++ /dev/null @@ -1,76 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`AST Fixtures declaration TSDeclareFunction generator Babel - Tokens 1`] = ` -[ - Identifier { - type: "Identifier", - value: "declare", - - range: [0, 7], - loc: { - start: { column: 0, line: 1 }, - end: { column: 7, line: 1 }, - }, - }, - Keyword { - type: "Keyword", - value: "function", - - range: [8, 16], - loc: { - start: { column: 8, line: 1 }, - end: { column: 16, line: 1 }, - }, - }, - Punctuator { - type: "Punctuator", - value: "*", - - range: [16, 17], - loc: { - start: { column: 16, line: 1 }, - end: { column: 17, line: 1 }, - }, - }, - Identifier { - type: "Identifier", - value: "foo", - - range: [18, 21], - loc: { - start: { column: 18, line: 1 }, - end: { column: 21, line: 1 }, - }, - }, - Punctuator { - type: "Punctuator", - value: "(", - - range: [21, 22], - loc: { - start: { column: 21, line: 1 }, - end: { column: 22, line: 1 }, - }, - }, - Punctuator { - type: "Punctuator", - value: ")", - - range: [22, 23], - loc: { - start: { column: 22, line: 1 }, - end: { column: 23, line: 1 }, - }, - }, - Punctuator { - type: "Punctuator", - value: ";", - - range: [23, 24], - loc: { - start: { column: 23, line: 1 }, - end: { column: 24, line: 1 }, - }, - }, -] -`; diff --git a/packages/ast-spec/src/declaration/TSDeclareFunction/fixtures/generator/snapshots/5-AST-Alignment-AST.shot b/packages/ast-spec/src/declaration/TSDeclareFunction/fixtures/generator/snapshots/5-AST-Alignment-AST.shot deleted file mode 100644 index f2de95210fab..000000000000 --- a/packages/ast-spec/src/declaration/TSDeclareFunction/fixtures/generator/snapshots/5-AST-Alignment-AST.shot +++ /dev/null @@ -1,46 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`AST Fixtures declaration TSDeclareFunction generator AST Alignment - AST 1`] = ` -"Snapshot Diff: -- TSESTree -+ Babel - - Program { - type: 'Program', - body: Array [ - TSDeclareFunction { - type: 'TSDeclareFunction', - async: false, - declare: true, - expression: false, - generator: true, - id: Identifier { - type: 'Identifier', -- decorators: Array [], - name: 'foo', -- optional: false, - - range: [18, 21], - loc: { - start: { column: 18, line: 1 }, - end: { column: 21, line: 1 }, - }, - }, - params: Array [], - - range: [0, 24], - loc: { - start: { column: 0, line: 1 }, - end: { column: 24, line: 1 }, - }, - }, - ], - sourceType: 'script', - - range: [0, 25], - loc: { - start: { column: 0, line: 1 }, - end: { column: 0, line: 2 }, - }, - }" -`; diff --git a/packages/ast-spec/src/declaration/TSDeclareFunction/spec.ts b/packages/ast-spec/src/declaration/TSDeclareFunction/spec.ts index 50cc07ec4248..ab75a908aa87 100644 --- a/packages/ast-spec/src/declaration/TSDeclareFunction/spec.ts +++ b/packages/ast-spec/src/declaration/TSDeclareFunction/spec.ts @@ -1,11 +1,55 @@ import type { AST_NODE_TYPES } from '../../ast-node-types'; import type { FunctionBase } from '../../base/FunctionBase'; -import type { BlockStatement } from '../../statement/BlockStatement/spec'; -// TODO(#1852) - async + declare are semantically invalid together -export interface TSDeclareFunction extends FunctionBase { +interface TSDeclareFunctionBase extends FunctionBase { type: AST_NODE_TYPES.TSDeclareFunction; - body: BlockStatement | undefined; + /** + * TS1183: An implementation cannot be declared in ambient contexts. + */ + body: undefined; + /** + * Whether the declaration has `declare` modifier. + */ declare: boolean; expression: false; } + +/** + * Function declaration with the `declare` keyword: + * ``` + * declare function foo(): void; + * ``` + */ +export interface TSDeclareFunctionWithDeclare extends TSDeclareFunctionBase { + /** + * TS1040: 'async' modifier cannot be used in an ambient context. + */ + async: false; + declare: true; + /** + * TS1221: Generators are not allowed in an ambient context. + */ + generator: false; +} + +/** + * Function declaration without the `declare` keyword: + * ``` + * function foo(): void; + * ``` + * This can either be an overload signature or a declaration in an ambient context + * (e.g. `declare module`) + */ + +export interface TSDeclareFunctionNoDeclare extends TSDeclareFunctionBase { + declare: false; + /** + * - TS1221: Generators are not allowed in an ambient context. + * - TS1222: An overload signature cannot be declared as a generator. + */ + generator: false; +} + +export type TSDeclareFunction = + | TSDeclareFunctionNoDeclare + | TSDeclareFunctionWithDeclare; diff --git a/packages/ast-spec/src/declaration/TSImportEqualsDeclaration/fixtures/_error_/external-module-ref-non-string-2/fixture.ts b/packages/ast-spec/src/declaration/TSImportEqualsDeclaration/fixtures/_error_/external-module-ref-non-string-2/fixture.ts new file mode 100644 index 000000000000..af81e9506a3d --- /dev/null +++ b/packages/ast-spec/src/declaration/TSImportEqualsDeclaration/fixtures/_error_/external-module-ref-non-string-2/fixture.ts @@ -0,0 +1 @@ +import F = require(1 + 1); diff --git a/packages/ast-spec/src/declaration/TSImportEqualsDeclaration/fixtures/_error_/external-module-ref-non-string-2/snapshots/1-TSESTree-Error.shot b/packages/ast-spec/src/declaration/TSImportEqualsDeclaration/fixtures/_error_/external-module-ref-non-string-2/snapshots/1-TSESTree-Error.shot new file mode 100644 index 000000000000..d3ac7ccd164c --- /dev/null +++ b/packages/ast-spec/src/declaration/TSImportEqualsDeclaration/fixtures/_error_/external-module-ref-non-string-2/snapshots/1-TSESTree-Error.shot @@ -0,0 +1,8 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`AST Fixtures declaration TSImportEqualsDeclaration _error_ external-module-ref-non-string-2 TSESTree - Error 1`] = ` +"TSError +> 1 | import F = require(1 + 1); + | ^^^^^ String literal expected. + 2 |" +`; diff --git a/packages/ast-spec/src/declaration/TSImportEqualsDeclaration/fixtures/_error_/external-module-ref-non-string-2/snapshots/2-Babel-Error.shot b/packages/ast-spec/src/declaration/TSImportEqualsDeclaration/fixtures/_error_/external-module-ref-non-string-2/snapshots/2-Babel-Error.shot new file mode 100644 index 000000000000..c665f97c9fc1 --- /dev/null +++ b/packages/ast-spec/src/declaration/TSImportEqualsDeclaration/fixtures/_error_/external-module-ref-non-string-2/snapshots/2-Babel-Error.shot @@ -0,0 +1,3 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`AST Fixtures declaration TSImportEqualsDeclaration _error_ external-module-ref-non-string-2 Babel - Error 1`] = `[SyntaxError: Unexpected token (1:19)]`; diff --git a/packages/ast-spec/src/declaration/TSImportEqualsDeclaration/fixtures/_error_/external-module-ref-non-string-2/snapshots/3-Alignment-Error.shot b/packages/ast-spec/src/declaration/TSImportEqualsDeclaration/fixtures/_error_/external-module-ref-non-string-2/snapshots/3-Alignment-Error.shot new file mode 100644 index 000000000000..67e53098fb9d --- /dev/null +++ b/packages/ast-spec/src/declaration/TSImportEqualsDeclaration/fixtures/_error_/external-module-ref-non-string-2/snapshots/3-Alignment-Error.shot @@ -0,0 +1,3 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`AST Fixtures declaration TSImportEqualsDeclaration _error_ external-module-ref-non-string-2 Error Alignment 1`] = `"Both errored"`; diff --git a/packages/ast-spec/src/declaration/TSImportEqualsDeclaration/fixtures/_error_/external-module-ref-non-string-3/fixture.ts b/packages/ast-spec/src/declaration/TSImportEqualsDeclaration/fixtures/_error_/external-module-ref-non-string-3/fixture.ts new file mode 100644 index 000000000000..fc7471ff2608 --- /dev/null +++ b/packages/ast-spec/src/declaration/TSImportEqualsDeclaration/fixtures/_error_/external-module-ref-non-string-3/fixture.ts @@ -0,0 +1 @@ +import F = require(`1`); diff --git a/packages/ast-spec/src/declaration/TSImportEqualsDeclaration/fixtures/_error_/external-module-ref-non-string-3/snapshots/1-TSESTree-Error.shot b/packages/ast-spec/src/declaration/TSImportEqualsDeclaration/fixtures/_error_/external-module-ref-non-string-3/snapshots/1-TSESTree-Error.shot new file mode 100644 index 000000000000..6bbae44b9c80 --- /dev/null +++ b/packages/ast-spec/src/declaration/TSImportEqualsDeclaration/fixtures/_error_/external-module-ref-non-string-3/snapshots/1-TSESTree-Error.shot @@ -0,0 +1,8 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`AST Fixtures declaration TSImportEqualsDeclaration _error_ external-module-ref-non-string-3 TSESTree - Error 1`] = ` +"TSError +> 1 | import F = require(\`1\`); + | ^^^ String literal expected. + 2 |" +`; diff --git a/packages/ast-spec/src/declaration/TSImportEqualsDeclaration/fixtures/_error_/external-module-ref-non-string-3/snapshots/2-Babel-Error.shot b/packages/ast-spec/src/declaration/TSImportEqualsDeclaration/fixtures/_error_/external-module-ref-non-string-3/snapshots/2-Babel-Error.shot new file mode 100644 index 000000000000..705e7473716b --- /dev/null +++ b/packages/ast-spec/src/declaration/TSImportEqualsDeclaration/fixtures/_error_/external-module-ref-non-string-3/snapshots/2-Babel-Error.shot @@ -0,0 +1,3 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`AST Fixtures declaration TSImportEqualsDeclaration _error_ external-module-ref-non-string-3 Babel - Error 1`] = `[SyntaxError: Unexpected token (1:19)]`; diff --git a/packages/ast-spec/src/declaration/TSImportEqualsDeclaration/fixtures/_error_/external-module-ref-non-string-3/snapshots/3-Alignment-Error.shot b/packages/ast-spec/src/declaration/TSImportEqualsDeclaration/fixtures/_error_/external-module-ref-non-string-3/snapshots/3-Alignment-Error.shot new file mode 100644 index 000000000000..ec7c941e43f8 --- /dev/null +++ b/packages/ast-spec/src/declaration/TSImportEqualsDeclaration/fixtures/_error_/external-module-ref-non-string-3/snapshots/3-Alignment-Error.shot @@ -0,0 +1,3 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`AST Fixtures declaration TSImportEqualsDeclaration _error_ external-module-ref-non-string-3 Error Alignment 1`] = `"Both errored"`; diff --git a/packages/ast-spec/src/declaration/TSImportEqualsDeclaration/fixtures/_error_/external-module-ref-non-string/snapshots/1-TSESTree-Error.shot b/packages/ast-spec/src/declaration/TSImportEqualsDeclaration/fixtures/_error_/external-module-ref-non-string/snapshots/1-TSESTree-Error.shot index 51aedf5fa316..9bb0b788a352 100644 --- a/packages/ast-spec/src/declaration/TSImportEqualsDeclaration/fixtures/_error_/external-module-ref-non-string/snapshots/1-TSESTree-Error.shot +++ b/packages/ast-spec/src/declaration/TSImportEqualsDeclaration/fixtures/_error_/external-module-ref-non-string/snapshots/1-TSESTree-Error.shot @@ -1,3 +1,8 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`AST Fixtures declaration TSImportEqualsDeclaration _error_ external-module-ref-non-string TSESTree - Error 1`] = `"NO ERROR"`; +exports[`AST Fixtures declaration TSImportEqualsDeclaration _error_ external-module-ref-non-string TSESTree - Error 1`] = ` +"TSError +> 1 | import F = require(1); + | ^ String literal expected. + 2 |" +`; diff --git a/packages/ast-spec/src/declaration/TSImportEqualsDeclaration/fixtures/_error_/external-module-ref-non-string/snapshots/3-Alignment-Error.shot b/packages/ast-spec/src/declaration/TSImportEqualsDeclaration/fixtures/_error_/external-module-ref-non-string/snapshots/3-Alignment-Error.shot index cda96c9ac3d2..cefcf4608939 100644 --- a/packages/ast-spec/src/declaration/TSImportEqualsDeclaration/fixtures/_error_/external-module-ref-non-string/snapshots/3-Alignment-Error.shot +++ b/packages/ast-spec/src/declaration/TSImportEqualsDeclaration/fixtures/_error_/external-module-ref-non-string/snapshots/3-Alignment-Error.shot @@ -1,3 +1,3 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`AST Fixtures declaration TSImportEqualsDeclaration _error_ external-module-ref-non-string Error Alignment 1`] = `"Babel errored but TSESTree didn't"`; +exports[`AST Fixtures declaration TSImportEqualsDeclaration _error_ external-module-ref-non-string Error Alignment 1`] = `"Both errored"`; diff --git a/packages/ast-spec/src/expression/CallExpression/spec.ts b/packages/ast-spec/src/expression/CallExpression/spec.ts index 3480b0a1041a..f380de25bc9d 100644 --- a/packages/ast-spec/src/expression/CallExpression/spec.ts +++ b/packages/ast-spec/src/expression/CallExpression/spec.ts @@ -2,11 +2,11 @@ import type { AST_NODE_TYPES } from '../../ast-node-types'; import type { BaseNode } from '../../base/BaseNode'; import type { TSTypeParameterInstantiation } from '../../special/TSTypeParameterInstantiation/spec'; import type { CallExpressionArgument } from '../../unions/CallExpressionArgument'; -import type { LeftHandSideExpression } from '../../unions/LeftHandSideExpression'; +import type { Expression } from '../../unions/Expression'; export interface CallExpression extends BaseNode { type: AST_NODE_TYPES.CallExpression; - callee: LeftHandSideExpression; + callee: Expression; arguments: CallExpressionArgument[]; typeArguments: TSTypeParameterInstantiation | undefined; optional: boolean; diff --git a/packages/ast-spec/src/expression/ClassExpression/spec.ts b/packages/ast-spec/src/expression/ClassExpression/spec.ts index dbd4936f67ca..71a7be13140a 100644 --- a/packages/ast-spec/src/expression/ClassExpression/spec.ts +++ b/packages/ast-spec/src/expression/ClassExpression/spec.ts @@ -5,5 +5,4 @@ export interface ClassExpression extends ClassBase { type: AST_NODE_TYPES.ClassExpression; abstract: false; declare: false; - decorators: []; } diff --git a/packages/ast-spec/src/expression/NewExpression/spec.ts b/packages/ast-spec/src/expression/NewExpression/spec.ts index af88c9a5d2b1..c4478bd476d8 100644 --- a/packages/ast-spec/src/expression/NewExpression/spec.ts +++ b/packages/ast-spec/src/expression/NewExpression/spec.ts @@ -2,11 +2,11 @@ import type { AST_NODE_TYPES } from '../../ast-node-types'; import type { BaseNode } from '../../base/BaseNode'; import type { TSTypeParameterInstantiation } from '../../special/TSTypeParameterInstantiation/spec'; import type { CallExpressionArgument } from '../../unions/CallExpressionArgument'; -import type { LeftHandSideExpression } from '../../unions/LeftHandSideExpression'; +import type { Expression } from '../../unions/Expression'; export interface NewExpression extends BaseNode { type: AST_NODE_TYPES.NewExpression; - callee: LeftHandSideExpression; + callee: Expression; arguments: CallExpressionArgument[]; typeArguments: TSTypeParameterInstantiation | undefined; } diff --git a/packages/ast-spec/src/expression/TaggedTemplateExpression/spec.ts b/packages/ast-spec/src/expression/TaggedTemplateExpression/spec.ts index a11bc58325eb..2f9c3fbb2882 100644 --- a/packages/ast-spec/src/expression/TaggedTemplateExpression/spec.ts +++ b/packages/ast-spec/src/expression/TaggedTemplateExpression/spec.ts @@ -1,12 +1,12 @@ import type { AST_NODE_TYPES } from '../../ast-node-types'; import type { BaseNode } from '../../base/BaseNode'; import type { TSTypeParameterInstantiation } from '../../special/TSTypeParameterInstantiation/spec'; -import type { LeftHandSideExpression } from '../../unions/LeftHandSideExpression'; +import type { Expression } from '../../unions/Expression'; import type { TemplateLiteral } from '../TemplateLiteral/spec'; export interface TaggedTemplateExpression extends BaseNode { + quasi: TemplateLiteral; + tag: Expression; type: AST_NODE_TYPES.TaggedTemplateExpression; typeArguments: TSTypeParameterInstantiation | undefined; - tag: LeftHandSideExpression; - quasi: TemplateLiteral; } diff --git a/packages/ast-spec/src/special/TSExternalModuleReference/spec.ts b/packages/ast-spec/src/special/TSExternalModuleReference/spec.ts index 1efd283fd8cf..debc2daff41b 100644 --- a/packages/ast-spec/src/special/TSExternalModuleReference/spec.ts +++ b/packages/ast-spec/src/special/TSExternalModuleReference/spec.ts @@ -1,9 +1,8 @@ import type { AST_NODE_TYPES } from '../../ast-node-types'; import type { BaseNode } from '../../base/BaseNode'; -import type { Expression } from '../../unions/Expression'; +import type { StringLiteral } from '../../expression/literal/StringLiteral/spec'; export interface TSExternalModuleReference extends BaseNode { type: AST_NODE_TYPES.TSExternalModuleReference; - // TODO(#1852) - this must be a string - expression: Expression; + expression: StringLiteral; } diff --git a/packages/ast-spec/tests/fixtures-with-differences-ast.shot b/packages/ast-spec/tests/fixtures-with-differences-ast.shot index 409216445120..fe7cf666fec3 100644 --- a/packages/ast-spec/tests/fixtures-with-differences-ast.shot +++ b/packages/ast-spec/tests/fixtures-with-differences-ast.shot @@ -56,8 +56,9 @@ exports[`AST Fixtures List fixtures with AST differences 1`] = ` "declaration/ImportDeclaration/fixtures/named-none/fixture.ts", "declaration/ImportDeclaration/fixtures/named-one/fixture.ts", "declaration/ImportDeclaration/fixtures/side-effect/fixture.ts", + "declaration/TSDeclareFunction/fixtures/async-ambient/fixture.ts", + "declaration/TSDeclareFunction/fixtures/async-overload/fixture.ts", "declaration/TSDeclareFunction/fixtures/empty/fixture.ts", - "declaration/TSDeclareFunction/fixtures/generator/fixture.ts", "declaration/TSDeclareFunction/fixtures/param-many/fixture.ts", "declaration/TSDeclareFunction/fixtures/param-one/fixture.ts", "declaration/TSDeclareFunction/fixtures/returnType/fixture.ts", diff --git a/packages/ast-spec/tests/fixtures-with-differences-errors.shot b/packages/ast-spec/tests/fixtures-with-differences-errors.shot index 3baa5a85efca..76663a6ec6c8 100644 --- a/packages/ast-spec/tests/fixtures-with-differences-errors.shot +++ b/packages/ast-spec/tests/fixtures-with-differences-errors.shot @@ -8,10 +8,7 @@ exports[`AST Fixtures List fixtures with Error differences 1`] = ` "declaration/ClassDeclaration/fixtures/_error_/missing-type-param/fixture.ts", "declaration/ExportNamedDeclaration/fixtures/_error_/assertion/fixture.ts", "declaration/FunctionDeclaration/fixtures/_error_/missing-type-param/fixture.ts", - "declaration/TSDeclareFunction/fixtures/_error_/async/fixture.ts", - "declaration/TSDeclareFunction/fixtures/_error_/declare-with-body/fixture.ts", "declaration/TSDeclareFunction/fixtures/_error_/missing-type-param/fixture.ts", - "declaration/TSImportEqualsDeclaration/fixtures/_error_/external-module-ref-non-string/fixture.ts", "declaration/TSImportEqualsDeclaration/fixtures/_error_/import-kind/fixture.ts", "declaration/TSInterfaceDeclaration/fixtures/_error_/missing-extends/fixture.ts", "declaration/TSInterfaceDeclaration/fixtures/_error_/missing-type-param/fixture.ts", @@ -49,6 +46,9 @@ exports[`AST Fixtures List fixtures with Error differences 1`] = ` "TSESTree errored but Babel didn't": [ "declaration/ExportAllDeclaration/fixtures/_error_/named-non-identifier/fixture.ts", "declaration/ExportNamedDeclaration/fixtures/_error_/aliased-literal/fixture.ts", + "declaration/TSDeclareFunction/fixtures/_error_/generator-ambient/fixture.ts", + "declaration/TSDeclareFunction/fixtures/_error_/generator-overload/fixture.ts", + "declaration/TSDeclareFunction/fixtures/_error_/generator/fixture.ts", "element/AccessorProperty/fixtures/_error_/modifier-abstract-accessor-with-value/fixture.ts", "legacy-fixtures/basics/fixtures/_error_/abstract-class-with-abstract-constructor/fixture.ts", "legacy-fixtures/expressions/fixtures/_error_/instantiation-expression/fixture.ts", diff --git a/packages/eslint-plugin-internal/src/rules/no-typescript-default-import.ts b/packages/eslint-plugin-internal/src/rules/no-typescript-default-import.ts index 0864b7bba553..594ac9f51c2f 100644 --- a/packages/eslint-plugin-internal/src/rules/no-typescript-default-import.ts +++ b/packages/eslint-plugin-internal/src/rules/no-typescript-default-import.ts @@ -1,5 +1,4 @@ import type { TSESTree } from '@typescript-eslint/utils'; -import { AST_NODE_TYPES } from '@typescript-eslint/utils'; import { createRule } from '../util'; @@ -61,10 +60,7 @@ export default createRule({ node: TSESTree.TSExternalModuleReference, ): void { const parent = node.parent as TSESTree.TSImportEqualsDeclaration; - if ( - node.expression.type === AST_NODE_TYPES.Literal && - node.expression.value === 'typescript' - ) { + if (node.expression.value === 'typescript') { context.report({ node, messageId: 'noTSDefaultImport', diff --git a/packages/eslint-plugin-internal/src/rules/plugin-test-formatting.ts b/packages/eslint-plugin-internal/src/rules/plugin-test-formatting.ts index a9be456e947f..57a372367fca 100644 --- a/packages/eslint-plugin-internal/src/rules/plugin-test-formatting.ts +++ b/packages/eslint-plugin-internal/src/rules/plugin-test-formatting.ts @@ -425,9 +425,7 @@ export default createRule({ } } - function isNoFormatTemplateTag( - tag: TSESTree.LeftHandSideExpression, - ): boolean { + function isNoFormatTemplateTag(tag: TSESTree.Expression): boolean { return tag.type === AST_NODE_TYPES.Identifier && tag.name === 'noFormat'; } diff --git a/packages/eslint-plugin/docs/rules/no-base-to-string.mdx b/packages/eslint-plugin/docs/rules/no-base-to-string.mdx index 5e6b531e4384..f959897d7e68 100644 --- a/packages/eslint-plugin/docs/rules/no-base-to-string.mdx +++ b/packages/eslint-plugin/docs/rules/no-base-to-string.mdx @@ -10,7 +10,7 @@ import TabItem from '@theme/TabItem'; > See **https://typescript-eslint.io/rules/no-base-to-string** for documentation. JavaScript will call `toString()` on an object when it is converted to a string, such as when `+` adding to a string or in `${}` template literals. -The default Object `.toString()` returns `"[object Object]"`, which is often not what was intended. +The default Object `.toString()` uses the format `"[object Object]"`, which is often not what was intended. This rule reports on stringified values that aren't primitives and don't define a more useful `.toString()` method. > Note that `Function` provides its own `.toString()` that returns the function's code. diff --git a/packages/eslint-plugin/docs/rules/no-misused-promises.mdx b/packages/eslint-plugin/docs/rules/no-misused-promises.mdx index ff044b712d12..3621829cb977 100644 --- a/packages/eslint-plugin/docs/rules/no-misused-promises.mdx +++ b/packages/eslint-plugin/docs/rules/no-misused-promises.mdx @@ -127,19 +127,18 @@ Examples of code for this rule with `checksVoidReturn: true`: ```ts option='{ "checksVoidReturn": true }' [1, 2, 3].forEach(async value => { - await doSomething(value); + await fetch(`/${value}`); }); -new Promise(async (resolve, reject) => { - await doSomething(); +new Promise(async (resolve, reject) => { + await fetch('/'); resolve(); }); -const eventEmitter = new EventEmitter(); -eventEmitter.on('some-event', async () => { - synchronousCall(); - await doSomething(); - otherSynchronousCall(); +document.addEventListener('click', async () => { + console.log('synchronous call'); + await fetch('/'); + console.log('synchronous call'); }); ``` @@ -169,8 +168,7 @@ new Promise((resolve, reject) => { }); // Name the async wrapper to call it later -const eventEmitter = new EventEmitter(); -eventEmitter.on('some-event', () => { +document.addEventListener('click', () => { const handler = async () => { await doSomething(); otherSynchronousCall(); @@ -210,30 +208,30 @@ Examples of code for this rule with `checksSpreads: true`: ```ts option='{ "checksSpreads": true }' -const getData = () => someAsyncOperation({ myArg: 'foo' }); +const getData = () => fetch('/'); -return { foo: 42, ...getData() }; +console.log({ foo: 42, ...getData() }); -const getData2 = async () => { - await someAsyncOperation({ myArg: 'foo' }); +const awaitData = async () => { + await fetch('/'); }; -return { foo: 42, ...getData2() }; +console.log({ foo: 42, ...awaitData() }); ``` ```ts option='{ "checksSpreads": true }' -const getData = () => someAsyncOperation({ myArg: 'foo' }); +const getData = () => fetch('/'); -return { foo: 42, ...(await getData()) }; +console.log({ foo: 42, ...(await getData()) }); -const getData2 = async () => { - await someAsyncOperation({ myArg: 'foo' }); +const awaitData = async () => { + await fetch('/'); }; -return { foo: 42, ...(await getData2()) }; +console.log({ foo: 42, ...(await awaitData()) }); ``` diff --git a/packages/eslint-plugin/docs/rules/no-unused-expressions.mdx b/packages/eslint-plugin/docs/rules/no-unused-expressions.mdx index 2f0805ba8de6..8d185e92c082 100644 --- a/packages/eslint-plugin/docs/rules/no-unused-expressions.mdx +++ b/packages/eslint-plugin/docs/rules/no-unused-expressions.mdx @@ -10,4 +10,44 @@ import TabItem from '@theme/TabItem'; > See **https://typescript-eslint.io/rules/no-unused-expressions** for documentation. 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. +It supports TypeScript-specific expressions: + +- Marks directives in modules declarations (`"use strict"`, etc.) as not unused +- Marks the following expressions as unused if their wrapped value expressions are unused: + - Assertion expressions: `x as number;`, `x!;`, `x;` + - Instantiation expressions: `Set;` + +Although the type expressions never have runtime side effects (that is, `x!;` is the same as `x;`), they can be used to assert types for testing purposes. + +## Examples + + + + +```ts +Set; +1 as number; +window!; +``` + + + + +```ts +function getSet() { + return Set; +} + +// Funtion calls are allowed, so type expressions that wrap function calls are allowed +getSet(); +getSet() as Set; +getSet()!; + +// Namespaces can have directives +namespace A { + 'use strict'; +} +``` + + + diff --git a/packages/eslint-plugin/docs/rules/prefer-readonly-parameter-types.mdx b/packages/eslint-plugin/docs/rules/prefer-readonly-parameter-types.mdx index e01ff6ffbf62..e2ddcef23048 100644 --- a/packages/eslint-plugin/docs/rules/prefer-readonly-parameter-types.mdx +++ b/packages/eslint-plugin/docs/rules/prefer-readonly-parameter-types.mdx @@ -402,3 +402,8 @@ function foo(arg: MyType) {} ## When Not To Use It If your project does not attempt to enforce strong immutability guarantees of parameters, you can avoid this rule. + +This rule is very strict on what it considers mutable. +Many types that describe themselves as readonly are considered mutable because they have mutable properties such as arrays or tuples. +To work around these limitations, you might need to use the rule's options. +In particular, the [`allow` option](#allow) can explicitly mark a type as readonly. diff --git a/packages/eslint-plugin/docs/rules/restrict-template-expressions.mdx b/packages/eslint-plugin/docs/rules/restrict-template-expressions.mdx index 3cf40a6e805d..f99cb0ab8e60 100644 --- a/packages/eslint-plugin/docs/rules/restrict-template-expressions.mdx +++ b/packages/eslint-plugin/docs/rules/restrict-template-expressions.mdx @@ -10,7 +10,7 @@ import TabItem from '@theme/TabItem'; > See **https://typescript-eslint.io/rules/restrict-template-expressions** for documentation. JavaScript automatically [converts an object to a string](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String#string_coercion) in a string context, such as when concatenating it with a string using `+` or embedding it in a template literal using `${}`. -The default `toString()` method of objects returns `"[object Object]"`, which is often not what was intended. +The default `toString()` method of objects uses the format `"[object Object]"`, which is often not what was intended. This rule reports on values used in a template literal string that aren't strings, optionally allowing other data types that provide useful stringification results. :::note diff --git a/packages/eslint-plugin/docs/rules/sort-type-constituents.mdx b/packages/eslint-plugin/docs/rules/sort-type-constituents.mdx index a4e29c2cdc43..188590aa38b1 100644 --- a/packages/eslint-plugin/docs/rules/sort-type-constituents.mdx +++ b/packages/eslint-plugin/docs/rules/sort-type-constituents.mdx @@ -9,6 +9,12 @@ import TabItem from '@theme/TabItem'; > > See **https://typescript-eslint.io/rules/sort-type-constituents** for documentation. +:::danger Deprecated +This rule has been deprecated in favor of the [`perfectionist/sort-intersection-types`](https://eslint-plugin-perfectionist.azat.io/rules/sort-intersection-types) and [`perfectionist/sort-union-types`](https://eslint-plugin-perfectionist.azat.io/rules/sort-union-types) rules. + +See [Docs: Deprecate sort-type-constituents in favor of eslint-plugin-perfectionist](https://github.com/typescript-eslint/typescript-eslint/issues/8915) and [eslint-plugin: Feature freeze naming and sorting stylistic rules](https://github.com/typescript-eslint/typescript-eslint/issues/8792) for more information. +::: + Sorting union (`|`) and intersection (`&`) types can help: - keep your codebase standardized @@ -19,15 +25,6 @@ This rule reports on any types that aren't sorted alphabetically. > Types are sorted case-insensitively and treating numbers like a human would, falling back to character code sorting in case of ties. -:::note -This rule is _feature frozen_: it will no longer receive new features such as new options. -It still will accept bug and documentation fixes for its existing area of features. - -Stylistic rules that enforce naming and/or sorting conventions tend to grow incomprehensibly complex as increasingly obscure features are requested. -This rule has reached the limit of what is reasonable for the typescript-eslint project to maintain. -See [eslint-plugin: Feature freeze naming and sorting stylistic rules](https://github.com/typescript-eslint/typescript-eslint/issues/8792) for more information. -::: - ## Examples diff --git a/packages/eslint-plugin/docs/rules/sort-type-union-intersection-members.mdx b/packages/eslint-plugin/docs/rules/sort-type-union-intersection-members.mdx new file mode 100644 index 000000000000..d8224c93c808 --- /dev/null +++ b/packages/eslint-plugin/docs/rules/sort-type-union-intersection-members.mdx @@ -0,0 +1,12 @@ +:::danger Deprecated + +This rule has been renamed to [`sort-type-constituents`](https://typescript-eslint.io/rules/sort-type-constituents). + +::: + + diff --git a/packages/eslint-plugin/src/configs/all.ts b/packages/eslint-plugin/src/configs/all.ts index 2beb655455f3..5c1e1d7725ac 100644 --- a/packages/eslint-plugin/src/configs/all.ts +++ b/packages/eslint-plugin/src/configs/all.ts @@ -144,7 +144,6 @@ export = { '@typescript-eslint/restrict-template-expressions': 'error', 'no-return-await': 'off', '@typescript-eslint/return-await': 'error', - '@typescript-eslint/sort-type-constituents': 'error', '@typescript-eslint/strict-boolean-expressions': 'error', '@typescript-eslint/switch-exhaustiveness-check': 'error', '@typescript-eslint/triple-slash-reference': 'error', diff --git a/packages/eslint-plugin/src/rules/init-declarations.ts b/packages/eslint-plugin/src/rules/init-declarations.ts index fabbe73898a5..4debac89cb26 100644 --- a/packages/eslint-plugin/src/rules/init-declarations.ts +++ b/packages/eslint-plugin/src/rules/init-declarations.ts @@ -28,7 +28,51 @@ export default createRule({ }, defaultOptions: ['always'], create(context, [mode]) { - const rules = baseRule.create(context); + // Make a custom context to adjust the the loc of reports where the base + // rule's behavior is a bit too aggressive with TS-specific syntax (namely, + // type annotations). + function getBaseContextOverride(): typeof context { + const reportOverride: typeof context.report = descriptor => { + if ('node' in descriptor && descriptor.loc == null) { + const { node, ...rest } = descriptor; + // We only want to special case the report loc when reporting on + // variables declarations that are not initialized. Declarations that + // _are_ initialized get reported by the base rule due to a setting to + // prohibit initializing variables entirely, in which case underlining + // the whole node including the type annotation and initializer is + // appropriate. + if ( + node.type === AST_NODE_TYPES.VariableDeclarator && + node.init == null + ) { + context.report({ + ...rest, + loc: getReportLoc(node), + }); + return; + } + } + + context.report(descriptor); + }; + + // `return { ...context, report: reportOverride }` isn't safe because the + // `context` object has some getters that need to be preserved. + // + // `return new Proxy(context, ...)` doesn't work because `context` has + // non-configurable properties that throw when constructing a Proxy. + // + // So, we'll just use Proxy on a dummy object and use the `get` trap to + // proxy `context`'s properties. + return new Proxy({} as typeof context, { + get: (target, prop, receiver): unknown => + prop === 'report' + ? reportOverride + : Reflect.get(context, prop, receiver), + }); + } + + const rules = baseRule.create(getBaseContextOverride()); return { 'VariableDeclaration:exit'(node: TSESTree.VariableDeclaration): void { @@ -65,3 +109,26 @@ export default createRule({ } }, }); + +/** + * When reporting an uninitialized variable declarator, get the loc excluding + * the type annotation. + */ +function getReportLoc( + node: TSESTree.VariableDeclarator, +): TSESTree.SourceLocation { + const start: TSESTree.Position = structuredClone(node.loc.start); + const end: TSESTree.Position = { + line: node.loc.start.line, + // `if (id.type === AST_NODE_TYPES.Identifier)` is a condition for + // reporting in the base rule (as opposed to things like destructuring + // assignment), so the type assertion should always be valid. + column: + node.loc.start.column + (node.id as TSESTree.Identifier).name.length, + }; + + return { + start, + end, + }; +} diff --git a/packages/eslint-plugin/src/rules/no-base-to-string.ts b/packages/eslint-plugin/src/rules/no-base-to-string.ts index 131579e8457e..0369e66fe66f 100644 --- a/packages/eslint-plugin/src/rules/no-base-to-string.ts +++ b/packages/eslint-plugin/src/rules/no-base-to-string.ts @@ -28,7 +28,7 @@ export default createRule({ }, messages: { baseToString: - "'{{name}}' {{certainty}} evaluate to '[object Object]' when stringified.", + "'{{name}}' {{certainty}} use Object's default stringification format ('[object Object]') when stringified.", }, schema: [ { diff --git a/packages/eslint-plugin/src/rules/no-implied-eval.ts b/packages/eslint-plugin/src/rules/no-implied-eval.ts index de2e0574929d..dee23d62d275 100644 --- a/packages/eslint-plugin/src/rules/no-implied-eval.ts +++ b/packages/eslint-plugin/src/rules/no-implied-eval.ts @@ -36,9 +36,7 @@ export default createRule({ const services = getParserServices(context); const checker = services.program.getTypeChecker(); - function getCalleeName( - node: TSESTree.LeftHandSideExpression, - ): string | null { + function getCalleeName(node: TSESTree.Expression): string | null { if (node.type === AST_NODE_TYPES.Identifier) { return node.name; } diff --git a/packages/eslint-plugin/src/rules/no-restricted-imports.ts b/packages/eslint-plugin/src/rules/no-restricted-imports.ts index 861bbad05cd9..abd0a891b05e 100644 --- a/packages/eslint-plugin/src/rules/no-restricted-imports.ts +++ b/packages/eslint-plugin/src/rules/no-restricted-imports.ts @@ -312,10 +312,7 @@ export default createRule({ node: TSESTree.TSImportEqualsDeclaration, ): void { if ( - node.moduleReference.type === - AST_NODE_TYPES.TSExternalModuleReference && - node.moduleReference.expression.type === AST_NODE_TYPES.Literal && - typeof node.moduleReference.expression.value === 'string' + node.moduleReference.type === AST_NODE_TYPES.TSExternalModuleReference ) { const synthesizedImport = { ...node, diff --git a/packages/eslint-plugin/src/rules/no-unsafe-argument.ts b/packages/eslint-plugin/src/rules/no-unsafe-argument.ts index d55d8f75b06e..08950fa8d732 100644 --- a/packages/eslint-plugin/src/rules/no-unsafe-argument.ts +++ b/packages/eslint-plugin/src/rules/no-unsafe-argument.ts @@ -165,7 +165,7 @@ export default createRule<[], MessageIds>({ function checkUnsafeArguments( args: TSESTree.Expression[] | TSESTree.CallExpressionArgument[], - callee: TSESTree.LeftHandSideExpression, + callee: TSESTree.Expression, node: | TSESTree.CallExpression | TSESTree.NewExpression diff --git a/packages/eslint-plugin/src/rules/no-unsafe-assignment.ts b/packages/eslint-plugin/src/rules/no-unsafe-assignment.ts index fc1008773aab..883783dbd527 100644 --- a/packages/eslint-plugin/src/rules/no-unsafe-assignment.ts +++ b/packages/eslint-plugin/src/rules/no-unsafe-assignment.ts @@ -37,17 +37,18 @@ export default createRule({ requiresTypeChecking: true, }, messages: { - anyAssignment: 'Unsafe assignment of an `any` value.', + anyAssignment: 'Unsafe assignment of an {{sender}} value.', anyAssignmentThis: [ - 'Unsafe assignment of an `any` value. `this` is typed as `any`.', + 'Unsafe assignment of an {{sender}} value. `this` is typed as `any`.', 'You can try to fix this by turning on the `noImplicitThis` compiler option, or adding a `this` parameter to the function.', ].join('\n'), - unsafeArrayPattern: 'Unsafe array destructuring of an `any` array value.', + unsafeArrayPattern: + 'Unsafe array destructuring of an {{sender}} array value.', unsafeArrayPatternFromTuple: - 'Unsafe array destructuring of a tuple element with an `any` value.', + 'Unsafe array destructuring of a tuple element with an {{sender}} value.', unsafeAssignment: 'Unsafe assignment of type {{sender}} to a variable of type {{receiver}}.', - unsafeArraySpread: 'Unsafe spread of an `any` value in an array.', + unsafeArraySpread: 'Unsafe spread of an {{sender}} value in an array.', }, schema: [], }, @@ -88,6 +89,7 @@ export default createRule({ context.report({ node: receiverNode, messageId: 'unsafeArrayPattern', + data: createData(senderType), }); return false; } @@ -126,6 +128,7 @@ export default createRule({ context.report({ node: receiverElement, messageId: 'unsafeArrayPatternFromTuple', + data: createData(senderType), }); // we want to report on every invalid element in the tuple didReport = true; @@ -212,6 +215,7 @@ export default createRule({ context.report({ node: receiverProperty.value, messageId: 'unsafeArrayPatternFromTuple', + data: createData(senderType), }); didReport = true; } else if ( @@ -275,7 +279,9 @@ export default createRule({ context.report({ node: reportingNode, messageId, + data: createData(senderType), }); + return true; } @@ -297,10 +303,7 @@ export default createRule({ context.report({ node: reportingNode, messageId: 'unsafeAssignment', - data: { - sender: checker.typeToString(sender), - receiver: checker.typeToString(receiver), - }, + data: createData(sender, receiver), }); return true; } @@ -315,6 +318,23 @@ export default createRule({ ComparisonType.None; } + function createData( + senderType: ts.Type, + receiverType?: ts.Type, + ): Readonly> | undefined { + if (receiverType) { + return { + sender: '`' + checker.typeToString(senderType) + '`', + receiver: '`' + checker.typeToString(receiverType) + '`', + }; + } + return { + sender: tsutils.isIntrinsicErrorType(senderType) + ? 'error typed' + : '`any`', + }; + } + return { 'VariableDeclarator[init != null]'( node: TSESTree.VariableDeclarator, @@ -383,6 +403,7 @@ export default createRule({ context.report({ node: node, messageId: 'unsafeArraySpread', + data: createData(restType), }); } }, diff --git a/packages/eslint-plugin/src/rules/sort-type-constituents.ts b/packages/eslint-plugin/src/rules/sort-type-constituents.ts index 2f1b8f5cbd34..de87dc2aa962 100644 --- a/packages/eslint-plugin/src/rules/sort-type-constituents.ts +++ b/packages/eslint-plugin/src/rules/sort-type-constituents.ts @@ -117,6 +117,11 @@ export type MessageIds = 'notSorted' | 'notSortedNamed' | 'suggestFix'; export default createRule({ name: 'sort-type-constituents', meta: { + deprecated: true, + replacedBy: [ + 'perfectionist/sort-intersection-types', + 'perfectionist/sort-union-types', + ], type: 'suggestion', docs: { description: diff --git a/packages/eslint-plugin/tests/docs-eslint-output-snapshots/no-base-to-string.shot b/packages/eslint-plugin/tests/docs-eslint-output-snapshots/no-base-to-string.shot index 21750093fe9a..f5bc9c118ff1 100644 --- a/packages/eslint-plugin/tests/docs-eslint-output-snapshots/no-base-to-string.shot +++ b/packages/eslint-plugin/tests/docs-eslint-output-snapshots/no-base-to-string.shot @@ -5,18 +5,18 @@ exports[`Validating rule docs no-base-to-string.mdx code examples ESLint output // Passing an object or class instance to string concatenation: '' + {}; - ~~ '{}' will evaluate to '[object Object]' when stringified. + ~~ '{}' will use Object's default stringification format ('[object Object]') when stringified. class MyClass {} const value = new MyClass(); value + ''; -~~~~~ 'value' will evaluate to '[object Object]' when stringified. +~~~~~ 'value' will use Object's default stringification format ('[object Object]') when stringified. // Interpolation and manual .toString() calls too: \`Value: \${value}\`; - ~~~~~ 'value' will evaluate to '[object Object]' when stringified. + ~~~~~ 'value' will use Object's default stringification format ('[object Object]') when stringified. ({}).toString(); - ~~ '{}' will evaluate to '[object Object]' when stringified. + ~~ '{}' will use Object's default stringification format ('[object Object]') when stringified. " `; diff --git a/packages/eslint-plugin/tests/docs-eslint-output-snapshots/no-misused-promises.shot b/packages/eslint-plugin/tests/docs-eslint-output-snapshots/no-misused-promises.shot index 3c1170e888c0..e42445e017bb 100644 --- a/packages/eslint-plugin/tests/docs-eslint-output-snapshots/no-misused-promises.shot +++ b/packages/eslint-plugin/tests/docs-eslint-output-snapshots/no-misused-promises.shot @@ -46,25 +46,24 @@ Options: { "checksVoidReturn": true } [1, 2, 3].forEach(async value => { ~~~~~~~~~~~~~~~~ Promise returned in function argument where a void return was expected. - await doSomething(value); + await fetch(\`/\${value}\`); ~~~~~~~~~~~~~~~~~~~~~~~~~~~ }); ~ -new Promise(async (resolve, reject) => { - ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Promise returned in function argument where a void return was expected. - await doSomething(); -~~~~~~~~~~~~~~~~~~~~~~ +new Promise(async (resolve, reject) => { + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Promise returned in function argument where a void return was expected. + await fetch('/'); +~~~~~~~~~~~~~~~~~~~ resolve(); ~~~~~~~~~~~~ }); ~ -const eventEmitter = new EventEmitter(); -eventEmitter.on('some-event', async () => { - synchronousCall(); - await doSomething(); - otherSynchronousCall(); +document.addEventListener('click', async () => { + console.log('synchronous call'); + await fetch('/'); + console.log('synchronous call'); }); " `; @@ -95,8 +94,7 @@ new Promise((resolve, reject) => { }); // Name the async wrapper to call it later -const eventEmitter = new EventEmitter(); -eventEmitter.on('some-event', () => { +document.addEventListener('click', () => { const handler = async () => { await doSomething(); otherSynchronousCall(); @@ -117,16 +115,17 @@ exports[`Validating rule docs no-misused-promises.mdx code examples ESLint outpu "Incorrect Options: { "checksSpreads": true } -const getData = () => someAsyncOperation({ myArg: 'foo' }); +const getData = () => fetch('/'); -return { foo: 42, ...getData() }; +console.log({ foo: 42, ...getData() }); + ~~~~~~~~~ Expected a non-Promise value to be spreaded in an object. -const getData2 = async () => { - await someAsyncOperation({ myArg: 'foo' }); +const awaitData = async () => { + await fetch('/'); }; -return { foo: 42, ...getData2() }; - ~~~~~~~~~~ Expected a non-Promise value to be spreaded in an object. +console.log({ foo: 42, ...awaitData() }); + ~~~~~~~~~~~ Expected a non-Promise value to be spreaded in an object. " `; @@ -134,14 +133,14 @@ exports[`Validating rule docs no-misused-promises.mdx code examples ESLint outpu "Correct Options: { "checksSpreads": true } -const getData = () => someAsyncOperation({ myArg: 'foo' }); +const getData = () => fetch('/'); -return { foo: 42, ...(await getData()) }; +console.log({ foo: 42, ...(await getData()) }); -const getData2 = async () => { - await someAsyncOperation({ myArg: 'foo' }); +const awaitData = async () => { + await fetch('/'); }; -return { foo: 42, ...(await getData2()) }; +console.log({ foo: 42, ...(await awaitData()) }); " `; diff --git a/packages/eslint-plugin/tests/docs-eslint-output-snapshots/no-unsafe-assignment.shot b/packages/eslint-plugin/tests/docs-eslint-output-snapshots/no-unsafe-assignment.shot index 2e1b94899b68..de10a87d5f42 100644 --- a/packages/eslint-plugin/tests/docs-eslint-output-snapshots/no-unsafe-assignment.shot +++ b/packages/eslint-plugin/tests/docs-eslint-output-snapshots/no-unsafe-assignment.shot @@ -29,13 +29,13 @@ class Foo { // generic position examples const x: Set = new Set(); - ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Unsafe assignment of type Set to a variable of type Set. + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Unsafe assignment of type \`Set\` to a variable of type \`Set\`. const x: Map = new Map(); - ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Unsafe assignment of type Map to a variable of type Map. + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Unsafe assignment of type \`Map\` to a variable of type \`Map\`. const x: Set = new Set(); - ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Unsafe assignment of type Set to a variable of type Set. + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Unsafe assignment of type \`Set\` to a variable of type \`Set\`. const x: Set>> = new Set>>(); - ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Unsafe assignment of type Set>> to a variable of type Set>>. + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Unsafe assignment of type \`Set>>\` to a variable of type \`Set>>\`. " `; diff --git a/packages/eslint-plugin/tests/docs-eslint-output-snapshots/no-unused-expressions.shot b/packages/eslint-plugin/tests/docs-eslint-output-snapshots/no-unused-expressions.shot new file mode 100644 index 000000000000..f0f2802b58a2 --- /dev/null +++ b/packages/eslint-plugin/tests/docs-eslint-output-snapshots/no-unused-expressions.shot @@ -0,0 +1,32 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Validating rule docs no-unused-expressions.mdx code examples ESLint output 1`] = ` +"Incorrect + +Set; +~~~~~~~~~~~~ Expected an assignment or function call and instead saw an expression. +1 as number; +~~~~~~~~~~~~ Expected an assignment or function call and instead saw an expression. +window!; +~~~~~~~~ Expected an assignment or function call and instead saw an expression. +" +`; + +exports[`Validating rule docs no-unused-expressions.mdx code examples ESLint output 2`] = ` +"Correct + +function getSet() { + return Set; +} + +// Funtion calls are allowed, so type expressions that wrap function calls are allowed +getSet(); +getSet() as Set; +getSet()!; + +// Namespaces can have directives +namespace A { + 'use strict'; +} +" +`; diff --git a/packages/eslint-plugin/tests/docs.test.ts b/packages/eslint-plugin/tests/docs.test.ts index 53d0838af93e..ebe19ec4d586 100644 --- a/packages/eslint-plugin/tests/docs.test.ts +++ b/packages/eslint-plugin/tests/docs.test.ts @@ -8,12 +8,16 @@ import { Linter } from '@typescript-eslint/utils/ts-eslint'; import fs from 'fs'; import { marked } from 'marked'; import type * as mdast from 'mdast'; -import type { fromMarkdown as FromMarkdown } from 'mdast-util-from-markdown' with { 'resolution-mode': 'import' }; -import type { mdxFromMarkdown as MdxFromMarkdown } from 'mdast-util-mdx' with { 'resolution-mode': 'import' }; -import type { mdxjs as Mdxjs } from 'micromark-extension-mdxjs' with { 'resolution-mode': 'import' }; +import type { fromMarkdown as FromMarkdown } from 'mdast-util-from-markdown' with { 'resolution-mode': + 'import' }; +import type { mdxFromMarkdown as MdxFromMarkdown } from 'mdast-util-mdx' with { 'resolution-mode': + 'import' }; +import type { mdxjs as Mdxjs } from 'micromark-extension-mdxjs' with { 'resolution-mode': + 'import' }; import path from 'path'; import { titleCase } from 'title-case'; -import type * as UnistUtilVisit from 'unist-util-visit' with { 'resolution-mode': 'import' }; +import type * as UnistUtilVisit from 'unist-util-visit' with { 'resolution-mode': + 'import' }; import rules from '../src/rules'; import { areOptionsValid } from './areOptionsValid'; @@ -158,11 +162,47 @@ describe('Validating rule docs', () => { 'no-duplicate-imports.mdx', 'no-parameter-properties.mdx', 'no-useless-template-literals.mdx', + 'sort-type-union-intersection-members.mdx', ...oldStylisticRules, ]); const rulesWithComplexOptions = new Set(['array-type', 'member-ordering']); + // TODO: whittle this list down to as few as possible + const rulesWithComplexOptionHeadings = new Set([ + 'ban-ts-comment', + 'ban-types', + 'consistent-type-exports', + 'consistent-type-imports', + 'explicit-function-return-type', + 'explicit-member-accessibility', + 'explicit-module-boundary-types', + 'no-base-to-string', + 'no-confusing-void-expression', + 'no-duplicate-type-constituents', + 'no-empty-interface', + 'no-empty-object-type', + 'no-explicit-any', + 'no-floating-promises', + 'no-inferrable-types', + 'no-invalid-void-type', + 'no-meaningless-void-operator', + 'no-misused-promises', + 'no-type-alias', + 'no-unnecessary-condition', + 'no-unnecessary-type-assertion', + 'parameter-properties', + 'prefer-nullish-coalescing', + 'prefer-optional-chain', + 'prefer-string-starts-ends-with', + 'promise-function-async', + 'restrict-template-expressions', + 'strict-boolean-expressions', + 'switch-exhaustiveness-check', + 'switch-exhaustiveness-check', + 'unbound-method', + ]); + it('All rules must have a corresponding rule doc', () => { const files = fs .readdirSync(docsRoot) @@ -263,22 +303,55 @@ describe('Validating rule docs', () => { Array.isArray(schema) && !rule.meta.docs?.extendsBaseRule ) { - test('each rule option should be mentioned in a heading', () => { - const headingTextAfterOptions = headings - .slice(headings.findIndex(header => header.text === 'Options')) - .map(header => header.text) - .join('\n'); + describe('rule options', () => { + const headingsAfterOptions = headings.slice( + headings.findIndex(header => header.text === 'Options'), + ); for (const schemaItem of schema) { if (schemaItem.type === 'object') { for (const property of Object.keys( schemaItem.properties as object, )) { - if (!headingTextAfterOptions.includes(`\`${property}\``)) { - throw new Error( - `At least one header should include \`${property}\`.`, + test(property, () => { + const correspondingHeadingIndex = + headingsAfterOptions.findIndex(heading => + heading.text.includes(`\`${property}\``), + ); + + if (correspondingHeadingIndex === -1) { + throw new Error( + `At least one header should include \`${property}\`.`, + ); + } + + if (rulesWithComplexOptionHeadings.has(ruleName)) { + return; + } + + const relevantChildren = tokens.slice( + tokens.indexOf( + headingsAfterOptions[correspondingHeadingIndex], + ), + tokens.indexOf( + headingsAfterOptions[correspondingHeadingIndex + 1], + ), ); - } + + for (const rawTab of [ + ``, + ``, + ]) { + if ( + !relevantChildren.some( + child => + child.type === 'html' && child.raw.includes(rawTab), + ) + ) { + throw new Error(`Missing option example tab: ${rawTab}`); + } + } + }); } } } diff --git a/packages/eslint-plugin/tests/eslint-rules/no-undef.test.ts b/packages/eslint-plugin/tests/eslint-rules/no-undef.test.ts index 252b6fe2f32b..a6ce89cc8d24 100644 --- a/packages/eslint-plugin/tests/eslint-rules/no-undef.test.ts +++ b/packages/eslint-plugin/tests/eslint-rules/no-undef.test.ts @@ -259,6 +259,11 @@ const obj = { }, }; `, + ` +class Foo { + [x: string]: any; +} + `, ], invalid: [ { @@ -396,5 +401,35 @@ function Foo() {} }, ], }, + { + code: ` +class Foo { + [x: Bar]: string; +} + `, + errors: [ + { + messageId: 'undef', + data: { + name: 'Bar', + }, + }, + ], + }, + { + code: ` +class Foo { + [x: string]: Bar; +} + `, + errors: [ + { + messageId: 'undef', + data: { + name: 'Bar', + }, + }, + ], + }, ], }); diff --git a/packages/eslint-plugin/tests/rules/consistent-return.test.ts b/packages/eslint-plugin/tests/rules/consistent-return.test.ts index 1489b835d8ba..cb74ca29b8c3 100644 --- a/packages/eslint-plugin/tests/rules/consistent-return.test.ts +++ b/packages/eslint-plugin/tests/rules/consistent-return.test.ts @@ -380,7 +380,7 @@ ruleTester.run('consistent-return', rule, { }, { code: ` - declare async function bar(): Promise; + declare function bar(): Promise; function foo(flag?: boolean): Promise { if (flag) { return bar(); diff --git a/packages/eslint-plugin/tests/rules/init-declarations.test.ts b/packages/eslint-plugin/tests/rules/init-declarations.test.ts index f284cd101853..2667377b2a7e 100644 --- a/packages/eslint-plugin/tests/rules/init-declarations.test.ts +++ b/packages/eslint-plugin/tests/rules/init-declarations.test.ts @@ -1,5 +1,4 @@ import { RuleTester } from '@typescript-eslint/rule-tester'; -import { AST_NODE_TYPES } from '@typescript-eslint/utils'; import rule from '../../src/rules/init-declarations'; @@ -381,7 +380,10 @@ declare namespace myLib1 { { messageId: 'initialized', data: { idName: 'foo' }, - type: AST_NODE_TYPES.VariableDeclarator, + line: 1, + column: 5, + endLine: 1, + endColumn: 8, }, ], }, @@ -392,7 +394,10 @@ declare namespace myLib1 { { messageId: 'initialized', data: { idName: 'foo' }, - type: AST_NODE_TYPES.VariableDeclarator, + line: 1, + column: 23, + endLine: 1, + endColumn: 26, }, ], }, @@ -407,12 +412,18 @@ var foo, { messageId: 'initialized', data: { idName: 'foo' }, - type: AST_NODE_TYPES.VariableDeclarator, + line: 2, + column: 5, + endLine: 2, + endColumn: 8, }, { messageId: 'initialized', data: { idName: 'baz' }, - type: AST_NODE_TYPES.VariableDeclarator, + line: 4, + column: 3, + endLine: 4, + endColumn: 6, }, ], }, @@ -428,7 +439,10 @@ function foo() { { messageId: 'initialized', data: { idName: 'bar' }, - type: AST_NODE_TYPES.VariableDeclarator, + line: 4, + column: 7, + endLine: 4, + endColumn: 10, }, ], }, @@ -444,7 +458,10 @@ function foo() { { messageId: 'initialized', data: { idName: 'foo' }, - type: AST_NODE_TYPES.VariableDeclarator, + line: 3, + column: 7, + endLine: 3, + endColumn: 10, }, ], }, @@ -455,7 +472,10 @@ function foo() { { messageId: 'initialized', data: { idName: 'a' }, - type: AST_NODE_TYPES.VariableDeclarator, + line: 1, + column: 5, + endLine: 1, + endColumn: 6, }, ], }, @@ -475,7 +495,10 @@ function foo() { { messageId: 'initialized', data: { idName: 'b' }, - type: AST_NODE_TYPES.VariableDeclarator, + line: 4, + column: 5, + endLine: 4, + endColumn: 6, }, ], }, @@ -492,12 +515,18 @@ function foo() { { messageId: 'initialized', data: { idName: 'a' }, - type: AST_NODE_TYPES.VariableDeclarator, + line: 3, + column: 7, + endLine: 3, + endColumn: 8, }, { messageId: 'initialized', data: { idName: 'c' }, - type: AST_NODE_TYPES.VariableDeclarator, + line: 5, + column: 7, + endLine: 5, + endColumn: 8, }, ], }, @@ -508,7 +537,10 @@ function foo() { { messageId: 'notInitialized', data: { idName: 'foo' }, - type: AST_NODE_TYPES.VariableDeclarator, + line: 1, + column: 5, + endLine: 1, + endColumn: 20, }, ], }, @@ -519,7 +551,10 @@ function foo() { { messageId: 'notInitialized', data: { idName: 'foo' }, - type: AST_NODE_TYPES.VariableDeclarator, + line: 1, + column: 5, + endLine: 1, + endColumn: 15, }, ], }, @@ -534,12 +569,18 @@ var foo, { messageId: 'notInitialized', data: { idName: 'bar' }, - type: AST_NODE_TYPES.VariableDeclarator, + line: 3, + column: 3, + endLine: 3, + endColumn: 10, }, { messageId: 'notInitialized', data: { idName: 'baz' }, - type: AST_NODE_TYPES.VariableDeclarator, + line: 4, + column: 3, + endLine: 4, + endColumn: 10, }, ], }, @@ -555,8 +596,10 @@ function foo() { { messageId: 'notInitialized', data: { idName: 'bar' }, - - type: AST_NODE_TYPES.VariableDeclarator, + line: 4, + column: 7, + endLine: 4, + endColumn: 16, }, ], }, @@ -567,7 +610,10 @@ function foo() { { messageId: 'notInitialized', data: { idName: 'a' }, - type: AST_NODE_TYPES.VariableDeclarator, + line: 1, + column: 5, + endLine: 1, + endColumn: 10, }, ], }, @@ -586,7 +632,10 @@ function foo() { { messageId: 'notInitialized', data: { idName: 'a' }, - type: AST_NODE_TYPES.VariableDeclarator, + line: 3, + column: 7, + endLine: 3, + endColumn: 16, }, ], }, @@ -603,7 +652,10 @@ function foo() { { messageId: 'notInitialized', data: { idName: 'c' }, - type: AST_NODE_TYPES.VariableDeclarator, + line: 5, + column: 7, + endLine: 5, + endColumn: 12, }, ], }, @@ -614,7 +666,10 @@ function foo() { { messageId: 'notInitialized', data: { idName: 'i' }, - type: AST_NODE_TYPES.VariableDeclarator, + line: 1, + column: 10, + endLine: 1, + endColumn: 15, }, ], }, @@ -628,7 +683,10 @@ for (var foo in []) { { messageId: 'notInitialized', data: { idName: 'foo' }, - type: AST_NODE_TYPES.VariableDeclarator, + line: 2, + column: 10, + endLine: 2, + endColumn: 13, }, ], }, @@ -642,7 +700,10 @@ for (var foo of []) { { messageId: 'notInitialized', data: { idName: 'foo' }, - type: AST_NODE_TYPES.VariableDeclarator, + line: 2, + column: 10, + endLine: 2, + endColumn: 13, }, ], }, @@ -657,7 +718,10 @@ function foo() { { messageId: 'initialized', data: { idName: 'bar' }, - type: AST_NODE_TYPES.VariableDeclarator, + line: 3, + column: 7, + endLine: 3, + endColumn: 10, }, ], }, @@ -670,7 +734,10 @@ function foo() { { messageId: 'notInitialized', data: { idName: 'arr' }, - type: AST_NODE_TYPES.VariableDeclarator, + line: 1, + column: 5, + endLine: 1, + endColumn: 34, }, ], }, @@ -681,7 +748,10 @@ function foo() { { messageId: 'notInitialized', data: { idName: 'arr' }, - type: AST_NODE_TYPES.VariableDeclarator, + line: 1, + column: 5, + endLine: 1, + endColumn: 33, }, ], }, @@ -698,7 +768,10 @@ const class1 = class NAME { { messageId: 'notInitialized', data: { idName: 'name1' }, - type: AST_NODE_TYPES.VariableDeclarator, + line: 4, + column: 9, + endLine: 4, + endColumn: 32, }, ], }, @@ -709,7 +782,10 @@ const class1 = class NAME { { messageId: 'initialized', data: { idName: 'arr' }, - type: AST_NODE_TYPES.VariableDeclarator, + line: 1, + column: 5, + endLine: 1, + endColumn: 8, }, ], }, @@ -720,7 +796,10 @@ const class1 = class NAME { { messageId: 'notInitialized', data: { idName: 'foo' }, - type: AST_NODE_TYPES.VariableDeclarator, + line: 1, + column: 13, + endLine: 1, + endColumn: 32, }, ], }, @@ -735,7 +814,10 @@ namespace myLib { { messageId: 'initialized', data: { idName: 'numberOfGreetings' }, - type: AST_NODE_TYPES.VariableDeclarator, + line: 3, + column: 7, + endLine: 3, + endColumn: 24, }, ], }, @@ -750,7 +832,10 @@ namespace myLib { { messageId: 'notInitialized', data: { idName: 'numberOfGreetings' }, - type: AST_NODE_TYPES.VariableDeclarator, + line: 3, + column: 7, + endLine: 3, + endColumn: 36, }, ], }, @@ -771,17 +856,26 @@ namespace myLib1 { { messageId: 'initialized', data: { idName: 'foo' }, - type: AST_NODE_TYPES.VariableDeclarator, + line: 3, + column: 9, + endLine: 3, + endColumn: 12, }, { messageId: 'initialized', data: { idName: 'bar' }, - type: AST_NODE_TYPES.VariableDeclarator, + line: 5, + column: 9, + endLine: 5, + endColumn: 12, }, { messageId: 'initialized', data: { idName: 'baz' }, - type: AST_NODE_TYPES.VariableDeclarator, + line: 7, + column: 11, + endLine: 7, + endColumn: 14, }, ], }, diff --git a/packages/eslint-plugin/tests/rules/naming-convention/naming-convention.test.ts b/packages/eslint-plugin/tests/rules/naming-convention/naming-convention.test.ts index 85fa8450ad21..0f956df18c07 100644 --- a/packages/eslint-plugin/tests/rules/naming-convention/naming-convention.test.ts +++ b/packages/eslint-plugin/tests/rules/naming-convention/naming-convention.test.ts @@ -466,11 +466,11 @@ ruleTester.run('naming-convention', rule, { { const camelCaseVar = 1; function camelCaseFunction() {} - declare function camelCaseDeclaredFunction() {}; + declare function camelCaseDeclaredFunction(); } const PascalCaseVar = 1; function PascalCaseFunction() {} - declare function PascalCaseDeclaredFunction() {}; + declare function PascalCaseDeclaredFunction(); `, options: [ { selector: 'default', format: ['camelCase'] }, @@ -1507,7 +1507,7 @@ ruleTester.run('naming-convention', rule, { code: ` const PascalCaseVar = 1; function PascalCaseFunction() {} - declare function PascalCaseDeclaredFunction() {}; + declare function PascalCaseDeclaredFunction(); `, options: [ { selector: 'default', format: ['snake_case'] }, diff --git a/packages/eslint-plugin/tests/rules/no-require-imports.test.ts b/packages/eslint-plugin/tests/rules/no-require-imports.test.ts index 4fc743026e56..0a9e56fc8520 100644 --- a/packages/eslint-plugin/tests/rules/no-require-imports.test.ts +++ b/packages/eslint-plugin/tests/rules/no-require-imports.test.ts @@ -255,7 +255,7 @@ var lib5 = require?.('lib5'), ], }, { - code: 'import pkg = require(`./package.json`);', + code: "import pkg = require('./package.json');", options: [{ allow: ['^some-package$'] }], errors: [ { diff --git a/packages/eslint-plugin/tests/rules/no-unsafe-assignment.test.ts b/packages/eslint-plugin/tests/rules/no-unsafe-assignment.test.ts index c910999fd27c..163286853e41 100644 --- a/packages/eslint-plugin/tests/rules/no-unsafe-assignment.test.ts +++ b/packages/eslint-plugin/tests/rules/no-unsafe-assignment.test.ts @@ -130,6 +130,7 @@ declare function Foo(props: Props): never; }, }, }, + { code: ` declare function Foo(props: { a: string }): never; @@ -195,7 +196,60 @@ class Foo { `, errors: [{ messageId: 'anyAssignment' }], }, + { + code: ` +const [x] = spooky; + `, + errors: [ + { + messageId: 'anyAssignment', + data: { sender: 'error typed', receiver: 'error typed' }, + }, + ], + }, + { + code: ` +const [[[x]]] = [spooky]; + `, + errors: [ + { + messageId: 'unsafeArrayPatternFromTuple', + data: { sender: 'error typed', receiver: 'error typed' }, + }, + ], + }, + { + code: ` +const { + x: { y: z }, +} = { x: spooky }; + `, + errors: [ + { + messageId: 'unsafeArrayPatternFromTuple', + data: { sender: 'error typed', receiver: 'error typed' }, + }, + { + messageId: 'anyAssignment', + data: { sender: 'error typed', receiver: 'error typed' }, + }, + ], + }, + { + code: ` +let value: number; +value = spooky; + `, + errors: [ + { + messageId: 'anyAssignment', + data: { + sender: 'error typed', + }, + }, + ], + }, { code: ` const [x] = 1 as any; @@ -215,8 +269,8 @@ const [x] = [] as any[]; { messageId: 'unsafeAssignment', data: { - sender: 'Set', - receiver: 'Set', + sender: '`Set`', + receiver: '`Set`', }, }, ], @@ -227,8 +281,8 @@ const [x] = [] as any[]; { messageId: 'unsafeAssignment', data: { - sender: 'Map', - receiver: 'Map', + sender: '`Map`', + receiver: '`Map`', }, }, ], @@ -239,8 +293,8 @@ const [x] = [] as any[]; { messageId: 'unsafeAssignment', data: { - sender: 'Set', - receiver: 'Set', + sender: '`Set`', + receiver: '`Set`', }, }, ], @@ -251,8 +305,8 @@ const [x] = [] as any[]; { messageId: 'unsafeAssignment', data: { - sender: 'Set>>', - receiver: 'Set>>', + sender: '`Set>>`', + receiver: '`Set>>`', }, }, ], @@ -327,8 +381,8 @@ const x = [...([] as any[])]; column: 43, endColumn: 70, data: { - sender: 'Set>>', - receiver: 'Set>>', + sender: '`Set>>`', + receiver: '`Set>>`', }, }, ], diff --git a/packages/eslint-plugin/tests/rules/no-unused-vars/no-unused-vars.test.ts b/packages/eslint-plugin/tests/rules/no-unused-vars/no-unused-vars.test.ts index c9e0c762d74f..b4706224efac 100644 --- a/packages/eslint-plugin/tests/rules/no-unused-vars/no-unused-vars.test.ts +++ b/packages/eslint-plugin/tests/rules/no-unused-vars/no-unused-vars.test.ts @@ -697,6 +697,18 @@ export type T = { }; `, ` +type Foo = string; +export class Bar { + [x: Foo]: any; +} + `, + ` +type Foo = string; +export class Bar { + [x: Foo]: Foo; +} + `, + ` namespace Foo { export const Foo = 1; } diff --git a/packages/parser/src/index.ts b/packages/parser/src/index.ts index 9ccf6b17ed8f..9799bc982d07 100644 --- a/packages/parser/src/index.ts +++ b/packages/parser/src/index.ts @@ -5,6 +5,7 @@ export { ParserServicesWithoutTypeInformation, clearCaches, createProgram, + withoutProjectParserOptions, } from '@typescript-eslint/typescript-estree'; // note - cannot migrate this to an import statement because it will make TSC copy the package.json to the dist folder diff --git a/packages/scope-manager/src/referencer/ClassVisitor.ts b/packages/scope-manager/src/referencer/ClassVisitor.ts index 94cb7ab59815..cf5d7022b258 100644 --- a/packages/scope-manager/src/referencer/ClassVisitor.ts +++ b/packages/scope-manager/src/referencer/ClassVisitor.ts @@ -320,6 +320,10 @@ class ClassVisitor extends Visitor { // intentionally skip } + protected TSIndexSignature(node: TSESTree.TSIndexSignature): void { + this.visitType(node); + } + protected StaticBlock(node: TSESTree.StaticBlock): void { this.#referencer.scopeManager.nestClassStaticBlockScope(node); diff --git a/packages/scope-manager/tests/fixtures/class/declaration/index-signature.ts b/packages/scope-manager/tests/fixtures/class/declaration/index-signature.ts new file mode 100644 index 000000000000..64b3581b128e --- /dev/null +++ b/packages/scope-manager/tests/fixtures/class/declaration/index-signature.ts @@ -0,0 +1,7 @@ +type Bar = number; + +class Foo { + [x: string]: any; + [y: Bar]: string; + [z: symbol]: Foo; +} diff --git a/packages/scope-manager/tests/fixtures/class/declaration/index-signature.ts.shot b/packages/scope-manager/tests/fixtures/class/declaration/index-signature.ts.shot new file mode 100644 index 000000000000..52fa3c5480ec --- /dev/null +++ b/packages/scope-manager/tests/fixtures/class/declaration/index-signature.ts.shot @@ -0,0 +1,98 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`class declaration index-signature 1`] = ` +ScopeManager { + variables: [ + ImplicitGlobalConstTypeVariable, + Variable$2 { + defs: [ + TypeDefinition$1 { + name: Identifier<"Bar">, + node: TSTypeAliasDeclaration$1, + }, + ], + name: "Bar", + references: [ + Reference$1 { + identifier: Identifier<"Bar">, + isRead: true, + isTypeReference: true, + isValueReference: false, + isWrite: false, + resolved: Variable$2, + }, + ], + isValueVariable: false, + isTypeVariable: true, + }, + Variable$3 { + defs: [ + ClassNameDefinition$2 { + name: Identifier<"Foo">, + node: ClassDeclaration$2, + }, + ], + name: "Foo", + references: [], + isValueVariable: true, + isTypeVariable: true, + }, + Variable$4 { + defs: [ + ClassNameDefinition$3 { + name: Identifier<"Foo">, + node: ClassDeclaration$2, + }, + ], + name: "Foo", + references: [ + Reference$2 { + identifier: Identifier<"Foo">, + isRead: true, + isTypeReference: true, + isValueReference: false, + isWrite: false, + resolved: Variable$4, + }, + ], + isValueVariable: true, + isTypeVariable: true, + }, + ], + scopes: [ + GlobalScope$1 { + block: Program$3, + isStrict: false, + references: [], + set: Map { + "const" => ImplicitGlobalConstTypeVariable, + "Bar" => Variable$2, + "Foo" => Variable$3, + }, + type: "global", + upper: null, + variables: [ + ImplicitGlobalConstTypeVariable, + Variable$2, + Variable$3, + ], + }, + ClassScope$2 { + block: ClassDeclaration$2, + isStrict: true, + references: [ + Reference$1, + Reference$2, + ], + set: Map { + "Foo" => Variable$4, + }, + type: "class", + upper: GlobalScope$1, + variables: [ + Variable$4, + ], + }, + ], +} +`; diff --git a/packages/typescript-eslint/src/configs/all.ts b/packages/typescript-eslint/src/configs/all.ts index 2cacebbfc7ca..f01ac17c8ddb 100644 --- a/packages/typescript-eslint/src/configs/all.ts +++ b/packages/typescript-eslint/src/configs/all.ts @@ -153,7 +153,6 @@ export default ( '@typescript-eslint/restrict-template-expressions': 'error', 'no-return-await': 'off', '@typescript-eslint/return-await': 'error', - '@typescript-eslint/sort-type-constituents': 'error', '@typescript-eslint/strict-boolean-expressions': 'error', '@typescript-eslint/switch-exhaustiveness-check': 'error', '@typescript-eslint/triple-slash-reference': 'error', diff --git a/packages/typescript-estree/src/convert.ts b/packages/typescript-estree/src/convert.ts index b58b0fff18c8..28c0b9848dc6 100644 --- a/packages/typescript-estree/src/convert.ts +++ b/packages/typescript-estree/src/convert.ts @@ -958,19 +958,46 @@ export class Converter { case SyntaxKind.FunctionDeclaration: { const isDeclare = hasModifier(SyntaxKind.DeclareKeyword, node); + const isAsync = hasModifier(SyntaxKind.AsyncKeyword, node); + const isGenerator = !!node.asteriskToken; + if (isDeclare) { + if (node.body) { + this.#throwError( + node, + 'An implementation cannot be declared in ambient contexts.', + ); + } else if (isAsync) { + this.#throwError( + node, + "'async' modifier cannot be used in an ambient context.", + ); + } else if (isGenerator) { + this.#throwError( + node, + 'Generators are not allowed in an ambient context.', + ); + } + } else { + if (!node.body && isGenerator) { + this.#throwError( + node, + 'A function signature cannot be declared as a generator.', + ); + } + } const result = this.createNode< TSESTree.FunctionDeclaration | TSESTree.TSDeclareFunction >(node, { - type: - isDeclare || !node.body - ? AST_NODE_TYPES.TSDeclareFunction - : AST_NODE_TYPES.FunctionDeclaration, - async: hasModifier(SyntaxKind.AsyncKeyword, node), + // declare implies no body due to the invariant above + type: !node.body + ? AST_NODE_TYPES.TSDeclareFunction + : AST_NODE_TYPES.FunctionDeclaration, + async: isAsync, body: this.convertChild(node.body) || undefined, declare: isDeclare, expression: false, - generator: !!node.asteriskToken, + generator: isGenerator, id: this.convertChild(node.name), params: this.convertParameters(node.parameters), returnType: node.type && this.convertTypeAnnotation(node.type, node), @@ -3053,6 +3080,9 @@ export class Converter { ); } case SyntaxKind.ExternalModuleReference: { + if (node.expression.kind !== SyntaxKind.StringLiteral) { + this.#throwError(node.expression, 'String literal expected.'); + } return this.createNode(node, { type: AST_NODE_TYPES.TSExternalModuleReference, expression: this.convertChild(node.expression), diff --git a/packages/typescript-estree/src/index.ts b/packages/typescript-estree/src/index.ts index c86262b7cd9d..5f6e8768d0c4 100644 --- a/packages/typescript-estree/src/index.ts +++ b/packages/typescript-estree/src/index.ts @@ -19,6 +19,7 @@ export { typescriptVersionIsAtLeast } from './version-check'; export * from './getModifiers'; export { TSError } from './node-utils'; export * from './clear-caches'; +export { withoutProjectParserOptions } from './withoutProjectParserOptions'; // note - cannot migrate this to an import statement because it will make TSC copy the package.json to the dist folder // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access diff --git a/packages/typescript-estree/src/withoutProjectParserOptions.ts b/packages/typescript-estree/src/withoutProjectParserOptions.ts new file mode 100644 index 000000000000..e0298e0f5632 --- /dev/null +++ b/packages/typescript-estree/src/withoutProjectParserOptions.ts @@ -0,0 +1,21 @@ +import type { TSESTreeOptions } from './parser-options'; + +/** + * Removes options that prompt the parser to parse the project with type + * information. In other words, you can use this if you are invoking the parser + * directly, to ensure that one file will be parsed in isolation, which is much, + * much faster. + * + * @see https://github.com/typescript-eslint/typescript-eslint/issues/8428 + */ +export function withoutProjectParserOptions( + opts: TSESTreeOptions, +): TSESTreeOptions { + // eslint-disable-next-line @typescript-eslint/no-unused-vars -- The variables are meant to be omitted + const { EXPERIMENTAL_useProjectService, project, ...rest } = opts as Record< + string, + unknown + >; + + return rest; +} diff --git a/packages/typescript-estree/tests/lib/withoutProjectParserOptions.test.ts b/packages/typescript-estree/tests/lib/withoutProjectParserOptions.test.ts new file mode 100644 index 000000000000..56a96ca2bd91 --- /dev/null +++ b/packages/typescript-estree/tests/lib/withoutProjectParserOptions.test.ts @@ -0,0 +1,14 @@ +import { withoutProjectParserOptions } from '../../src'; + +describe('withoutProjectParserOptions', () => { + it('removes only project parser options', () => { + const without = withoutProjectParserOptions({ + comment: true, + EXPERIMENTAL_useProjectService: true, + project: true, + }); + expect(without).toEqual({ + comment: true, + }); + }); +}); diff --git a/packages/website/blog/2022-09-18-automated-rule-docs-with-docusaurus-and-remark.md b/packages/website/blog/2022-09-18-automated-rule-docs-with-docusaurus-and-remark.md index 7606e3e9f364..8009c1f0004f 100644 --- a/packages/website/blog/2022-09-18-automated-rule-docs-with-docusaurus-and-remark.md +++ b/packages/website/blog/2022-09-18-automated-rule-docs-with-docusaurus-and-remark.md @@ -220,9 +220,3 @@ I'm excited to focus in particular on [Docs: Proofread rule docs for clarity (#4 We'd like to extend thanks to [Joshua Chen](https://github.com/Josh-Cena), one of the Docusaurus maintainers who also has been helping us with Docusaurus — and helped proofread [this blog post's PR](https://github.com/typescript-eslint/typescript-eslint/pull/5593). Without Joshua, this change would have taken us a great deal longer (if we'd have been able to tackle it at all). Thanks Joshua! 🤗 - -## Supporting typescript-eslint - -If you enjoyed this blog post and/or use typescript-eslint, please consider [supporting us on Open Collective](https://opencollective.com/typescript-eslint). -We're a small volunteer team and could use your support to make the ESLint experience on TypeScript great. -Thanks! 💖 diff --git a/packages/website/blog/2023-02-24-consistent-type-exports-and-imports-why-and-how.md b/packages/website/blog/2023-02-24-consistent-type-exports-and-imports-why-and-how.md index cde68190cd45..37df608f1226 100644 --- a/packages/website/blog/2023-02-24-consistent-type-exports-and-imports-why-and-how.md +++ b/packages/website/blog/2023-02-24-consistent-type-exports-and-imports-why-and-how.md @@ -183,9 +183,3 @@ If it detects an import that only imports specifiers with inline `type` qualifie You can read more about the rules' configuration options in their docs pages. See [our Getting Started docs](/getting-started) for more information on linting your TypeScript code with typescript-eslint. - -## Supporting typescript-eslint - -If you enjoyed this blog post and/or use typescript-eslint, please consider [supporting us on Open Collective](https://opencollective.com/typescript-eslint). -We're a small volunteer team and could use your support to make the ESLint experience on TypeScript great. -Thanks! 💖 diff --git a/packages/website/blog/2023-03-13-announcing-typescript-eslint-v6-beta.md b/packages/website/blog/2023-03-13-announcing-typescript-eslint-v6-beta.md index 49d3bbf416e9..a69f5f71a840 100644 --- a/packages/website/blog/2023-03-13-announcing-typescript-eslint-v6-beta.md +++ b/packages/website/blog/2023-03-13-announcing-typescript-eslint-v6-beta.md @@ -374,9 +374,3 @@ We'd like to extend a sincere _thank you_ to everybody who pitched in to make ty See the [v6.0.0 milestone](https://github.com/typescript-eslint/typescript-eslint/milestone/8) for the list of issues and associated merged pull requests. - -## Supporting typescript-eslint - -If you enjoyed this blog post and/or use typescript-eslint, please consider [supporting us on Open Collective](https://opencollective.com/typescript-eslint). -We're a small volunteer team and could use your support to make the ESLint experience on TypeScript great. -Thanks! 💖 diff --git a/packages/website/blog/2023-07-09-announcing-typescript-eslint-v6.md b/packages/website/blog/2023-07-09-announcing-typescript-eslint-v6.md index 0920b0047905..26e7f674e07b 100644 --- a/packages/website/blog/2023-07-09-announcing-typescript-eslint-v6.md +++ b/packages/website/blog/2023-07-09-announcing-typescript-eslint-v6.md @@ -699,9 +699,3 @@ We'd like to extend a sincere _thank you_ to everybody who pitched in to make ty - [TypeScript](https://github.com/microsoft/TypeScript/pull/54693) See the [v6.0.0 milestone](https://github.com/typescript-eslint/typescript-eslint/milestone/8) for the list of issues and associated merged pull requests. - -## Supporting typescript-eslint - -If you enjoyed this blog post and/or use typescript-eslint, please consider [supporting us on Open Collective](https://opencollective.com/typescript-eslint). -We're a small volunteer team and could use your support to make the ESLint experience on TypeScript great. -Thanks! 💖 diff --git a/packages/website/blog/2023-09-18-parser-options-project-true.md b/packages/website/blog/2023-09-18-parser-options-project-true.md index d279beae93b3..e2ffd4ef6d1f 100644 --- a/packages/website/blog/2023-09-18-parser-options-project-true.md +++ b/packages/website/blog/2023-09-18-parser-options-project-true.md @@ -144,9 +144,3 @@ We hope this option will eventually become the standard way to enable typed lint However, because it's so new and untested, we're keeping it under the `EXPERIMENTAL_` prefix for at least all of the `6.X` versions. See [Packages > Parser > `EXPERIMENTAL_useProjectService`](/packages/parser#experimental_useprojectservice) for more information. - -## Supporting typescript-eslint - -If you enjoyed this blog post and/or use typescript-eslint, please consider [supporting us on Open Collective](https://opencollective.com/typescript-eslint). -We're a small volunteer team and could use your support to make the ESLint experience on TypeScript great. -Thanks! 💖 diff --git a/packages/website/blog/2023-12-25-deprecating-formatting-rules.md b/packages/website/blog/2023-12-25-deprecating-formatting-rules.md index 6767fedc9904..c25ddbf6a490 100644 --- a/packages/website/blog/2023-12-25-deprecating-formatting-rules.md +++ b/packages/website/blog/2023-12-25-deprecating-formatting-rules.md @@ -32,6 +32,11 @@ Per semantic versioning, formatting-related rules will remain available for all **Our next major version, v7, will remove all deprecated rules.** +:::note +The [`stylistic` configurations](/users/configs#stylistic) are not deprecated or recommended-against. +We'll continue to include those configs and their rules to help enforce TypeScript-related stylistic consistency for the foreseeable future. +::: + ## Upgrading to ESLint Stylistic Although you can continue to use formatting rules in typescript-eslint for now, we don't plan on adding any new features or fixes to the rules. @@ -62,9 +67,3 @@ The equivalent stylistic rules for deprecated typescript-eslint rules are summar | [`@typescript-eslint/space-before-function-paren`](/rules/space-before-function-paren) | [`@stylistic/space-before-function-paren`](https://eslint.style/rules/ts/space-before-function-paren) | | [`@typescript-eslint/space-infix-ops`](/rules/space-infix-ops) | [`@stylistic/space-infix-ops`](https://eslint.style/rules/ts/space-infix-ops) | | [`@typescript-eslint/type-annotation-spacing`](/rules/type-annotation-spacing) | [`@stylistic/type-annotation-spacing`](https://eslint.style/rules/ts/type-annotation-spacing) | - -## Supporting typescript-eslint - -If you enjoyed this blog post and/or use typescript-eslint, please consider [supporting us on Open Collective](https://opencollective.com/typescript-eslint). -We're a small volunteer team and could use your support to make the ESLint experience on TypeScript great. -Thanks! 💖 diff --git a/packages/website/blog/2024-05-27-announcing-typescript-eslint-v8-beta.mdx b/packages/website/blog/2024-05-27-announcing-typescript-eslint-v8-beta.mdx index 1b3e75de2fbd..6bda8fcbf66e 100644 --- a/packages/website/blog/2024-05-27-announcing-typescript-eslint-v8-beta.mdx +++ b/packages/website/blog/2024-05-27-announcing-typescript-eslint-v8-beta.mdx @@ -239,6 +239,8 @@ If you author any ESLint rules that refer to the syntax mentioned by them, these - If your code handles mapped types, change from `node.typeParameter.constraint` to `node.constraint` and from `node.typeParameter.name` to `node.key` - [feat(ast-spec): remove deprecated type params](https://github.com/typescript-eslint/typescript-eslint/pull/8933) - If you haven't already you must stop using those removed AST properties that were already marked as `@deprecated` +- [fix(typescript-estree): add TSEnumBody node for TSEnumDeclaration body #8920](https://github.com/typescript-eslint/typescript-eslint/pull/8920) + - If your code handles enums, switch from `node.members` to `node.body.members` ### Custom Rule `meta.docs` Types @@ -349,9 +351,3 @@ We'd like to extend a sincere _thank you_ to everybody who pitched in to make ty See the [v8.0.0 milestone](https://github.com/typescript-eslint/typescript-eslint/milestone/9) for the list of issues and associated merged pull requests. - -## Supporting typescript-eslint - -If you enjoyed this blog post and/or use typescript-eslint, please consider [supporting us on Open Collective](https://opencollective.com/typescript-eslint). -We're a small volunteer team and could use your support to make the ESLint experience on TypeScript great. -Thanks! 💖 diff --git a/packages/website/docusaurus.config.mts b/packages/website/docusaurus.config.mts index 9e23f2b65323..a728e223b119 100644 --- a/packages/website/docusaurus.config.mts +++ b/packages/website/docusaurus.config.mts @@ -9,6 +9,7 @@ import type { UserThemeConfig as AlgoliaThemeConfig } from '@docusaurus/theme-se import type { Config } from '@docusaurus/types'; import { version } from './package.json'; +import { blogFooter } from './plugins/blog-footer'; import { generatedRuleDocs } from './plugins/generated-rule-docs'; import { rulesMeta } from './rulesMeta'; @@ -19,6 +20,8 @@ const githubUrl = 'https://github.com/typescript-eslint/typescript-eslint'; const presetClassicOptions: PresetClassicOptions = { blog: { blogSidebarCount: 'ALL', + // Allow Docusaurus TOC remark plugin to pick up the injected H2 + beforeDefaultRemarkPlugins: [blogFooter], remarkPlugins, }, docs: { @@ -287,6 +290,10 @@ const redirects: PluginRedirectOptions = { from: '/linting/typed-linting/monorepos', to: '/getting-started/typed-linting/monorepos', }, + { + from: '/maintenance/issues/rule-deprecations', + to: '/maintenance/issues/rule-deprecations-and-deletions', + }, ], }; diff --git a/packages/website/package.json b/packages/website/package.json index 7222cad68a7a..a5c649efedf4 100644 --- a/packages/website/package.json +++ b/packages/website/package.json @@ -25,6 +25,7 @@ "@docusaurus/theme-common": "^3.2.1", "@typescript-eslint/parser": "7.12.0", "@typescript-eslint/website-eslint": "7.12.0", + "@uiw/react-shields": "2.0.1", "clsx": "^2.1.0", "eslint": "*", "json5": "^2.2.3", diff --git a/packages/website/plugins/blog-footer.ts b/packages/website/plugins/blog-footer.ts new file mode 100644 index 000000000000..e943ea59328a --- /dev/null +++ b/packages/website/plugins/blog-footer.ts @@ -0,0 +1,52 @@ +import type * as mdast from 'mdast'; +import type { Plugin } from 'unified'; + +import { nodeIsParent } from './utils/nodes'; + +export const blogFooter: Plugin = () => { + return (root, file) => { + if ( + !nodeIsParent(root) || + !(file.value as string).includes('') + ) { + return; + } + + root.children.push( + { + children: [ + { + type: 'text', + value: 'Supporting typescript-eslint', + }, + ], + depth: 2, + type: 'heading', + } as mdast.Heading, + { + children: [ + { + type: 'text', + value: + 'If you enjoyed this blog post and/or use typescript-eslint, please consider ', + }, + { + children: [ + { + type: 'text', + value: 'supporting us on Open Collective', + }, + ], + url: 'https://opencollective.com/typescript-eslint', + type: 'link', + }, + { + type: 'text', + value: `. We're a small volunteer team and could use your support to make the ESLint experience on TypeScript great. Thanks! 💖`, + }, + ], + type: 'paragraph', + } as mdast.Paragraph, + ); + }; +}; diff --git a/packages/website/plugins/generated-rule-docs/RuleDocsPage.ts b/packages/website/plugins/generated-rule-docs/RuleDocsPage.ts index d5dc9c213fda..81b0fd368bed 100644 --- a/packages/website/plugins/generated-rule-docs/RuleDocsPage.ts +++ b/packages/website/plugins/generated-rule-docs/RuleDocsPage.ts @@ -1,8 +1,8 @@ import type { ESLintPluginRuleModule } from '@typescript-eslint/eslint-plugin/use-at-your-own-risk/rules'; import type * as unist from 'unist'; -import type { VFileWithStem } from './utils'; -import { findH2Index } from './utils'; +import type { VFileWithStem } from '../utils/rules'; +import { findH2Index } from '../utils/rules'; export interface RequiredHeadingIndices { howToUse: number; diff --git a/packages/website/plugins/generated-rule-docs/addESLintHashToCodeBlocksMeta.ts b/packages/website/plugins/generated-rule-docs/addESLintHashToCodeBlocksMeta.ts index ac59bea53559..636a14aa30cf 100644 --- a/packages/website/plugins/generated-rule-docs/addESLintHashToCodeBlocksMeta.ts +++ b/packages/website/plugins/generated-rule-docs/addESLintHashToCodeBlocksMeta.ts @@ -1,8 +1,9 @@ import type { MdxJsxFlowElement } from 'mdast-util-mdx'; import type * as unist from 'unist'; +import { nodeIsCode } from '../utils/nodes'; +import { convertToPlaygroundHash } from '../utils/rules'; import type { RuleDocsPage } from './RuleDocsPage'; -import { convertToPlaygroundHash, nodeIsCode } from './utils'; const optionRegex = /option='(?