diff --git a/.cspell.json b/.cspell.json
index edd2532abf92..21ca6f8fc237 100644
--- a/.cspell.json
+++ b/.cspell.json
@@ -72,6 +72,7 @@
"esquery",
"esrecurse",
"estree",
+ "globby",
"IDE's",
"IIFE",
"IIFEs",
@@ -121,6 +122,7 @@
"unoptimized",
"unprefixed",
"upsert",
+ "warnonunsupportedtypescriptversion",
"Zacher"
],
"overrides": [
diff --git a/.eslintrc.js b/.eslintrc.js
index 7380b874d88f..99f3f5d3e41d 100644
--- a/.eslintrc.js
+++ b/.eslintrc.js
@@ -40,6 +40,11 @@ module.exports = {
tsconfigRootDir: __dirname,
warnOnUnsupportedTypeScriptVersion: false,
EXPERIMENTAL_useSourceOfProjectReferenceRedirect: false,
+ cacheLifetime: {
+ // we pretty well never create/change tsconfig structure - so need to ever evict the cache
+ // in the rare case that we do - just need to manually restart their IDE.
+ glob: 'Infinity',
+ },
},
rules: {
// make sure we're not leveraging any deprecated APIs
@@ -106,6 +111,13 @@ module.exports = {
//
curly: ['error', 'all'],
+ eqeqeq: [
+ 'error',
+ 'always',
+ {
+ null: 'never',
+ },
+ ],
'no-mixed-operators': 'error',
'no-console': 'error',
'no-process-exit': 'error',
@@ -186,6 +198,8 @@ module.exports = {
// enforce a sort order across the codebase
'simple-import-sort/imports': 'error',
+
+ 'one-var': ['error', 'never'],
},
overrides: [
// all test files
diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md
index 3e72e13f7a95..b50374076204 100644
--- a/.github/pull_request_template.md
+++ b/.github/pull_request_template.md
@@ -8,7 +8,7 @@ Otherwise we may not be able to review your PR.
- [ ] Addresses an existing open issue: fixes #000
- [ ] That issue was marked as [accepting prs](https://github.com/typescript-eslint/typescript-eslint/issues?q=is%3Aopen+is%3Aissue+label%3A%22accepting+prs%22)
-- [ ] Steps in [CONTRIBUTING.md](https://github.com/typescript-eslint/typescript-eslint/blob/main/CONTRIBUTING.md) were taken
+- [ ] Steps in [Contributing](https://typescript-eslint.io/contributing) were taken
## Overview
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 5a3384f1eaa1..def7977921ba 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -144,7 +144,7 @@ jobs:
- name: Install
uses: ./.github/actions/prepare-install
with:
- node-version: ${{ env.PRIMARY_NODE_VERSION }}
+ node-version: ${{ matrix.node-version }}
- name: Build
uses: ./.github/actions/prepare-build
diff --git a/.gitignore b/.gitignore
index 2ce061c3d4d7..0973f04542e0 100644
--- a/.gitignore
+++ b/.gitignore
@@ -69,6 +69,9 @@ jspm_packages/
# Editor-specific metadata folders
.vs
+# nodejs cpu profiles
+*.cpuprofile
+
.DS_Store
.idea
dist
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9ad61e896afe..951438d13b65 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -3,6 +3,27 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
+# [5.51.0](https://github.com/typescript-eslint/typescript-eslint/compare/v5.50.0...v5.51.0) (2023-02-06)
+
+
+### Bug Fixes
+
+* **eslint-plugin:** [sort-type-constituents] fixed behavior change ([#6384](https://github.com/typescript-eslint/typescript-eslint/issues/6384)) ([5bf7f7f](https://github.com/typescript-eslint/typescript-eslint/commit/5bf7f7fe48aee61a676dfbe829c2a5e9e44cd552)), closes [#6339](https://github.com/typescript-eslint/typescript-eslint/issues/6339)
+* **eslint-plugin:** do not use .at(), Node 14 does not support it ([#6402](https://github.com/typescript-eslint/typescript-eslint/issues/6402)) ([077ed1b](https://github.com/typescript-eslint/typescript-eslint/commit/077ed1b5be844df35b7fba554ddae579b3144787))
+
+
+### Features
+
+* **eslint-plugin:** [naming-convention] improve performance by removing unnecessary selectors ([#6376](https://github.com/typescript-eslint/typescript-eslint/issues/6376)) ([3647a1c](https://github.com/typescript-eslint/typescript-eslint/commit/3647a1c1bbcfe6551647632fc2d978fa90881de1))
+* **eslint-plugin:** [no-floating-promises] error on logical expression ([#6356](https://github.com/typescript-eslint/typescript-eslint/issues/6356)) ([f330e06](https://github.com/typescript-eslint/typescript-eslint/commit/f330e0651548d55163ddc3233c90fd3cbe37c9c0))
+* **eslint-plugin:** [no-import-type-side-effects] add rule to warn against runtime side effects with `verbatimModuleSyntax` ([#6394](https://github.com/typescript-eslint/typescript-eslint/issues/6394)) ([b14d3be](https://github.com/typescript-eslint/typescript-eslint/commit/b14d3be0f305d71e0adfc9381e9de993898b2b43))
+* **eslint-plugin:** [strict-boolean-expressions] add allow nullable enum to strict boolean expressions ([#6096](https://github.com/typescript-eslint/typescript-eslint/issues/6096)) ([d4747cd](https://github.com/typescript-eslint/typescript-eslint/commit/d4747cd8cc9dad2bf2cb64e1c0e8980ce34d82c7))
+* **typescript-estree:** cache project glob resolution ([#6367](https://github.com/typescript-eslint/typescript-eslint/issues/6367)) ([afae837](https://github.com/typescript-eslint/typescript-eslint/commit/afae8374df64101627808ccfeb5b715c865e910f))
+
+
+
+
+
# [5.50.0](https://github.com/typescript-eslint/typescript-eslint/compare/v5.49.0...v5.50.0) (2023-01-31)
diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md
index b1c6bfe4f0d3..53748bda2658 100644
--- a/CONTRIBUTORS.md
+++ b/CONTRIBUTORS.md
@@ -10,77 +10,54 @@ Thanks goes to these wonderful people:
 Brad Zacher |
 Armano |
 Josh Goldberg |
-  Oleksandr T. |
+  Michaël De Boey |
-  Michaël De Boey |
 Reyad Attiyat |
-  Gareth Jones |
 Sosuke Suzuki |
+  Gareth Jones |
 Patricio Trevino |
+  Joshua Chen |
-  Joshua Chen |
 YeonJuan |
 Nicholas C. Zakas |
 Jed Fox |
-  Rafael Santana |
-
-
 Ben Lichtman |
 Nikita |
+
+
 Taeheon Kim |
 Scott O'Hara |
 Retsam |
-
-
 Kai Cataldo |
 Rasmus Eneman |
-  Rebecca Stevens |
-  Toru Nagashima |
-  Yosuke Ota |
+  Toru Nagashima |
+  Yosuke Ota |
 JounQin |
 Lucas Azzola |
 Simen Bekkhus |
-  Danny Fritz |
-  Ika |
+  Danny Fritz |
+  Omri Luzon |
 cherryblossom |
-  mackie |
+  Mackie Underdown |
 Bryan Mishkin |
-  Kanitkorn Sujautra |
-  Omri Luzon |
+  Kanitkorn Sujautra |
+  Sviatoslav Zaytsev |
 Zzzen |
 Anix |
-  Daniil Dubrava |
 Pete Gonzalez |
-  ldrick |
-  Susisu |
+  ldrick |
 SHIMA RYUHEI |
-  Gavin Barron |
-  Kevin Partington |
-  Lucas Duailibe |
-
-  Niles Salter |
-  Pavel Birukov |
-  Shahar "Dawn" Or |
-  kmin-jeong |
-  koooge |
-
-
-  thomas michael wallace |
-  Juan García |
-  Daniel Nixon |
-  Yasar Siddiqui |
-  Yusuke Tanaka |
diff --git a/docs/Architecture.md b/docs/Architecture.mdx
similarity index 100%
rename from docs/Architecture.md
rename to docs/Architecture.mdx
diff --git a/docs/Custom_Rules.md b/docs/Custom_Rules.mdx
similarity index 100%
rename from docs/Custom_Rules.md
rename to docs/Custom_Rules.mdx
diff --git a/docs/Getting_Started.md b/docs/Getting_Started.mdx
similarity index 94%
rename from docs/Getting_Started.md
rename to docs/Getting_Started.mdx
index 91c978322f48..cdaf39899a9a 100644
--- a/docs/Getting_Started.md
+++ b/docs/Getting_Started.mdx
@@ -73,6 +73,6 @@ ESLint will lint all TypeScript compatible files within the current folder, and
## Next Steps
-We provide a plethora of powerful rules that utilize the power of TypeScript's type information. [Visit the next page for a setup guide](./linting/Typed_Linting.md 'Visit the next page for a typed rules setup guide').
+We provide a plethora of powerful rules that utilize the power of TypeScript's type information. [Visit the next page for a setup guide](./linting/Typed_Linting.mdx 'Visit the next page for a typed rules setup guide').
-If you're having problems getting this working, please have a look at our [Troubleshooting & FAQs](./linting/Troubleshooting.md).
+If you're having problems getting this working, please have a look at our [Troubleshooting & FAQs](./linting/Troubleshooting.mdx).
diff --git a/docs/MAINTENANCE.md b/docs/Maintenance.mdx
similarity index 100%
rename from docs/MAINTENANCE.md
rename to docs/Maintenance.mdx
diff --git a/docs/architecture/ESLint_Plugin.mdx b/docs/architecture/ESLint_Plugin.mdx
index eca601415550..6db78ddfcebe 100644
--- a/docs/architecture/ESLint_Plugin.mdx
+++ b/docs/architecture/ESLint_Plugin.mdx
@@ -8,7 +8,7 @@ sidebar_label: eslint-plugin
> The TypeScript plugin for ESLint. ✨
:::info
-See [Getting Started](../Getting_Started.md) for documentation on how to lint your TypeScript code with ESLint.
+See [Getting Started](../Getting_Started.mdx) for documentation on how to lint your TypeScript code with ESLint.
:::
`@typescript-eslint/eslint-plugin` is an ESLint plugin used to load in custom rules and rule configurations lists from typescript-eslint.
diff --git a/docs/architecture/ESLint_Plugin_TSLint.mdx b/docs/architecture/ESLint_Plugin_TSLint.mdx
index 582d93aab8ef..525b1c072477 100644
--- a/docs/architecture/ESLint_Plugin_TSLint.mdx
+++ b/docs/architecture/ESLint_Plugin_TSLint.mdx
@@ -8,8 +8,8 @@ sidebar_label: eslint-plugin-tslint
> ESLint plugin that allows running TSLint rules within ESLint to help you migrate from TSLint to ESLint. ✨
:::caution
-Per [What About TSLint?](../linting/troubleshooting/TSLint.md), we highly recommend migrating off TSLint.
-See [Getting Started](../Getting_Started.md) for documentation on how to lint your TypeScript code with ESLint.
+Per [What About TSLint?](../linting/troubleshooting/TSLint.mdx), we highly recommend migrating off TSLint.
+See [Getting Started](../Getting_Started.mdx) for documentation on how to lint your TypeScript code with ESLint.
:::
## Installation
diff --git a/docs/architecture/Parser.mdx b/docs/architecture/Parser.mdx
index d8d4f05ee43d..5039dc1bec55 100644
--- a/docs/architecture/Parser.mdx
+++ b/docs/architecture/Parser.mdx
@@ -30,6 +30,9 @@ The following additional configuration options are available by specifying them
```ts
interface ParserOptions {
+ cacheLifetime?: {
+ glob?: number | 'Infinity';
+ };
ecmaFeatures?: {
jsx?: boolean;
globalReturn?: boolean;
@@ -49,6 +52,14 @@ interface ParserOptions {
}
```
+### `cacheLifetime`
+
+This option allows you to granularly control our internal cache expiry lengths.
+
+You can specify the number of seconds as an integer number, or the string 'Infinity' if you never want the cache to expire.
+
+By default cache entries will be evicted after 30 seconds, or will persist indefinitely if the parser infers that it is a single run.
+
### `ecmaFeatures`
Optional additional options to describe how to parse the raw syntax.
diff --git a/docs/architecture/TypeScript-ESTree.mdx b/docs/architecture/TypeScript-ESTree.mdx
index 066030b0afee..f74aa65e2234 100644
--- a/docs/architecture/TypeScript-ESTree.mdx
+++ b/docs/architecture/TypeScript-ESTree.mdx
@@ -226,7 +226,23 @@ interface ParseAndGenerateServicesOptions extends ParseOptions {
allowAutomaticSingleRunInference?: boolean;
/**
- * Path to a file exporting a custom ModuleResolver.
+ * Granular control of the expiry lifetime of our internal caches.
+ * You can specify the number of seconds as an integer number, or the string
+ * 'Infinity' if you never want the cache to expire.
+ *
+ * By default cache entries will be evicted after 30 seconds, or will persist
+ * indefinitely if `allowAutomaticSingleRunInference = true` AND the parser
+ * infers that it is a single run.
+ */
+ cacheLifetime?: {
+ /**
+ * Glob resolution for `parserOptions.project` values.
+ */
+ glob?: number | 'Infinity';
+ };
+
+ /**
+ * Path to a file exporting a custom `ModuleResolver`.
*/
moduleResolver?: string;
}
@@ -273,6 +289,23 @@ const { ast, services } = parseAndGenerateServices(code, {
});
```
+##### `ModuleResolver`
+
+The `moduleResolver` option allows you to specify the path to a module with a custom module resolver implementation. The module is expected to adhere to the following interface:
+
+```ts
+interface ModuleResolver {
+ version: 1;
+ resolveModuleNames(
+ moduleNames: string[],
+ containingFile: string,
+ reusedNames: string[] | undefined,
+ redirectedReference: ts.ResolvedProjectReference | undefined,
+ options: ts.CompilerOptions,
+ ): (ts.ResolvedModule | undefined)[];
+}
+```
+
#### `parseWithNodeMaps(code, options)`
Parses the given string of code with the options provided and returns both the ESTree-compatible AST as well as the node maps.
diff --git a/docs/linting/CONFIGURATIONS.mdx b/docs/linting/CONFIGURATIONS.mdx
index aec668a16dde..10d3f6293d54 100644
--- a/docs/linting/CONFIGURATIONS.mdx
+++ b/docs/linting/CONFIGURATIONS.mdx
@@ -18,7 +18,7 @@ Most projects should extend from at least one of:
- [`strict`](#strict): Additional strict rules that can also catch bugs but are more opinionated than recommended rules.
:::tip
-We recommend most projects use [`recommended-requiring-type-checking`](#recommended-requiring-type-checking) (which requires [typed linting](./Typed_Linting.md)).
+We recommend most projects use [`recommended-requiring-type-checking`](#recommended-requiring-type-checking) (which requires [typed linting](./Typed_Linting.mdx)).
:::
:::note
diff --git a/docs/linting/Troubleshooting.md b/docs/linting/Troubleshooting.mdx
similarity index 99%
rename from docs/linting/Troubleshooting.md
rename to docs/linting/Troubleshooting.mdx
index 67e4316608f2..cb59af284478 100644
--- a/docs/linting/Troubleshooting.md
+++ b/docs/linting/Troubleshooting.mdx
@@ -35,11 +35,11 @@ If you don't find an existing extension rule, or the extension rule doesn't work
- If you **do not** want to lint the file:
- Use [one of the options ESLint offers](https://eslint.org/docs/latest/user-guide/configuring/ignoring-code) to ignore files, namely a `.eslintignore` file, or `ignorePatterns` config.
- If you **do** want to lint the file:
- - If you **do not** want to lint the file with [type-aware linting](./Typed_Linting.md):
+ - If you **do not** want to lint the file with [type-aware linting](./Typed_Linting.mdx):
- Use [ESLint's `overrides` configuration](https://eslint.org/docs/latest/user-guide/configuring/configuration-files#configuration-based-on-glob-patterns) to configure the file to not be parsed with type information.
- A popular setup is to omit the above additions from top-level configuration and only apply them to TypeScript files via an override.
- Alternatively, you can add `parserOptions: { project: null }` to an override for the files you wish to exclude. Note that `{ project: undefined }` will not work.
- - If you **do** want to lint the file with [type-aware linting](./Typed_Linting.md):
+ - If you **do** want to lint the file with [type-aware linting](./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 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)
@@ -64,7 +64,7 @@ For example, many projects have files like:
In that case, viewing the `.eslintrc.cjs` in an IDE with the ESLint extension will show the error notice that the file couldn't be linted because it isn't included in `tsconfig.json`.
-See our docs on [type aware linting](./Typed_Linting.md) for more information.
+See our docs on [type aware linting](./Typed_Linting.mdx) for more information.
## I get errors telling me "The file must be included in at least one of the projects provided"
diff --git a/docs/linting/Typed_Linting.md b/docs/linting/Typed_Linting.mdx
similarity index 90%
rename from docs/linting/Typed_Linting.md
rename to docs/linting/Typed_Linting.mdx
index 65e2c875fff9..7d9de2f25bb1 100644
--- a/docs/linting/Typed_Linting.md
+++ b/docs/linting/Typed_Linting.mdx
@@ -30,7 +30,7 @@ In more detail:
- `plugin:@typescript-eslint/recommended-requiring-type-checking` is another [recommended configuration](./CONFIGURATIONS.mdx) we provide. This one contains recommended rules that additionally require type information.
- `parserOptions.project` tells our parser the relative path where your project's `tsconfig.json` is.
- - If your project is a multi-package monorepo, see [our docs on configuring a monorepo](./typed-linting/Monorepos.md).
+ - If your project is a multi-package monorepo, see [our docs on configuring a monorepo](./typed-linting/Monorepos.mdx).
- `parserOptions.tsconfigRootDir` tells our parser the absolute path of your project's root directory (see [Parser#tsconfigRootDir](../architecture/Parser.mdx#tsconfigRootDir)).
With that done, run the same lint command you ran before.
@@ -53,8 +53,8 @@ This means that generally they usually only run a complete lint before a push, o
### I get errors telling me "The file must be included in at least one of the projects provided"
You're using an outdated version of `@typescript-eslint/parser`.
-Update to the latest version to see a more informative version of this error message, explained in our [Troubleshooting and FAQs page](./Troubleshooting.md#i-get-errors-telling-me-eslint-was-configured-to-run--however-that-tsconfig-does-not--none-of-those-tsconfigs-include-this-file).
+Update to the latest version to see a more informative version of this error message, explained in our [Troubleshooting and FAQs page](./Troubleshooting.mdx#i-get-errors-telling-me-eslint-was-configured-to-run--however-that-tsconfig-does-not--none-of-those-tsconfigs-include-this-file).
## Troubleshooting
-If you're having problems getting this working, please have a look at our [Troubleshooting and FAQs page](./Troubleshooting.md).
+If you're having problems getting this working, please have a look at our [Troubleshooting and FAQs page](./Troubleshooting.mdx).
diff --git a/docs/linting/troubleshooting/Formatting.md b/docs/linting/troubleshooting/Formatting.mdx
similarity index 100%
rename from docs/linting/troubleshooting/Formatting.md
rename to docs/linting/troubleshooting/Formatting.mdx
diff --git a/docs/linting/troubleshooting/Performance.md b/docs/linting/troubleshooting/Performance.md
index 6f696a7a05f0..f4d0f9e3d539 100644
--- a/docs/linting/troubleshooting/Performance.md
+++ b/docs/linting/troubleshooting/Performance.md
@@ -3,7 +3,7 @@ id: performance-troubleshooting
title: Performance Troubleshooting
---
-As mentioned in the [type-aware linting doc](../Typed_Linting.md), if you're using type-aware linting, your lint times should be roughly the same as your build times.
+As mentioned in the [type-aware linting doc](../Typed_Linting.mdx), if you're using type-aware linting, your lint times should be roughly the same as your build times.
If you're experiencing times much slower than that, then there are a few common culprits.
@@ -53,7 +53,7 @@ Across a large codebase, these can add up, and severely impact performance.
We recommend not using this rule, and instead using a tool like [`prettier`](https://www.npmjs.com/package/prettier) to enforce a standardized formatting.
-See our [documentation on formatting](./Formatting.md) for more information.
+See our [documentation on formatting](./Formatting.mdx) for more information.
## `eslint-plugin-prettier`
diff --git a/docs/linting/troubleshooting/TSLint.md b/docs/linting/troubleshooting/TSLint.mdx
similarity index 100%
rename from docs/linting/troubleshooting/TSLint.md
rename to docs/linting/troubleshooting/TSLint.mdx
diff --git a/docs/linting/typed-linting/Monorepos.md b/docs/linting/typed-linting/Monorepos.mdx
similarity index 96%
rename from docs/linting/typed-linting/Monorepos.md
rename to docs/linting/typed-linting/Monorepos.mdx
index e55d20647796..7b01b81d6622 100644
--- a/docs/linting/typed-linting/Monorepos.md
+++ b/docs/linting/typed-linting/Monorepos.mdx
@@ -38,7 +38,7 @@ Be sure to update your `.eslintrc.js` to point at this new config file.
## One `tsconfig.json` per package (and an optional one in the root)
-The `parserOptions.project` option introduced in [Linting with Type Information](../Typed_Linting.md) accepts an array of relative paths.
+The `parserOptions.project` option introduced in [Linting with Type Information](../Typed_Linting.mdx) accepts an array of relative paths.
Paths may be provided as [Node globs](https://github.com/isaacs/node-glob/blob/f5a57d3d6e19b324522a3fa5bdd5075fd1aa79d1/README.md#glob-primer).
For each file being linted, the first matching project path will be used as its backing TSConfig.
@@ -104,4 +104,4 @@ As an interim workaround, consider one of the following:
## Troubleshooting
-If you're having problems getting this working, please have a look at our [Troubleshooting FAQ](../Troubleshooting.md).
+If you're having problems getting this working, please have a look at our [Troubleshooting FAQ](../Troubleshooting.mdx).
diff --git a/docs/maintenance/BRANDING.md b/docs/maintenance/Branding.mdx
similarity index 92%
rename from docs/maintenance/BRANDING.md
rename to docs/maintenance/Branding.mdx
index 25c08b216e65..d4e84d90deae 100644
--- a/docs/maintenance/BRANDING.md
+++ b/docs/maintenance/Branding.mdx
@@ -31,7 +31,12 @@ You can call it _blurple_ if you want.
Our logo is also a halfway between [ESLint's logo](https://en.wikipedia.org/wiki/ESLint#/media/File:ESLint_logo.svg) and [TypeScript's logo](https://en.wikipedia.org/wiki/TypeScript#/media/File:Typescript.svg):
-
+
- [Logo PNG download](/img/logo.png)
- [Logo SVG download](/img/logo.svg)
diff --git a/docs/maintenance/ISSUES.md b/docs/maintenance/Issues.mdx
similarity index 99%
rename from docs/maintenance/ISSUES.md
rename to docs/maintenance/Issues.mdx
index fb5eb655dd69..fc8a89711dee 100644
--- a/docs/maintenance/ISSUES.md
+++ b/docs/maintenance/Issues.mdx
@@ -105,7 +105,7 @@ We avoid features that:
- Are only relevant for a minority of users, as they aren't likely worth the maintenance burden
- Aren't TypeScript-specific (e.g. should be in ESLint core instead)
- Are only relevant with specific userland frameworks or libraries, such as Jest or React
-- Are for "formatting" functionality (we [strongly recommend users use a separate dedicated formatter](../linting/troubleshooting/Formatting.md))
+- Are for "formatting" functionality (we [strongly recommend users use a separate dedicated formatter](../linting/troubleshooting/Formatting.mdx))
#### ✨ Rule Enhancements
diff --git a/docs/maintenance/PULL_REQUESTS.md b/docs/maintenance/Pull_Requests.mdx
similarity index 100%
rename from docs/maintenance/PULL_REQUESTS.md
rename to docs/maintenance/Pull_Requests.mdx
diff --git a/docs/maintenance/RELEASES.md b/docs/maintenance/Releases.mdx
similarity index 100%
rename from docs/maintenance/RELEASES.md
rename to docs/maintenance/Releases.mdx
diff --git a/docs/maintenance/Versioning.md b/docs/maintenance/Versioning.mdx
similarity index 95%
rename from docs/maintenance/Versioning.md
rename to docs/maintenance/Versioning.mdx
index d9a4224dbf8a..68a007c89c1a 100644
--- a/docs/maintenance/Versioning.md
+++ b/docs/maintenance/Versioning.mdx
@@ -14,11 +14,21 @@ Additionally, we promote to the `latest` tag on NPM once per week, **on Mondays
The latest version under the `latest` tag is:
-
+
+
+
The latest version under the `canary` tag **(latest commit to `main`)** is:
-
+
+
+
:::note
The only exception to the automated publishes described above is when we are in the final phases of creating the next major version of the libraries - e.g. going from `1.x.x` to `2.x.x`.
diff --git a/docs/maintenance/issues/Rule_Deprecations.md b/docs/maintenance/issues/Rule_Deprecations.mdx
similarity index 100%
rename from docs/maintenance/issues/Rule_Deprecations.md
rename to docs/maintenance/issues/Rule_Deprecations.mdx
diff --git a/docs/maintenance/versioning/dependant-version-upgrades.mdx b/docs/maintenance/versioning/dependant-version-upgrades.mdx
index 49ba51ce1e46..bf1a17f28296 100644
--- a/docs/maintenance/versioning/dependant-version-upgrades.mdx
+++ b/docs/maintenance/versioning/dependant-version-upgrades.mdx
@@ -21,7 +21,7 @@ Whenever you discover any new areas of work that are blocked by dropping an old
1. Upgrade the root `package.json` `devDependency` to the latest ESLint
1. Add the new major version to the explicit `peerDependency` versions
1. Check [`eslint-visitor-keys`](https://www.npmjs.com/package/eslint-visitor-keys) for a new version to be upgraded to as well.
-1. Update [Versioning > ESLint](../Versioning.md#eslint)
+1. Update [Versioning > ESLint](../Versioning.mdx#eslint)
### Removing Support for an Old ESLint Version
@@ -32,7 +32,7 @@ Whenever you discover any new areas of work that are blocked by dropping an old
- `/eslint.*5/i`
- `/todo.*eslint.*5/i`
- `/todo.*eslint/i`
-1. Update [Versioning > ESLint](../Versioning.md#eslint)
+1. Update [Versioning > ESLint](../Versioning.mdx#eslint)
See [chore: drop support for ESLint v6](https://github.com/typescript-eslint/typescript-eslint/pull/5972) for reference.
@@ -88,8 +88,8 @@ We generally start the process of supporting a new TypeScript version just after
- In the root `package.json`, change the `devDependency` on `typescript` to `~X.Y.3`
- Rename and update `patches/typescript*` to the new TypeScript version
- Any other changes made necessary due to changes in TypeScript between the RC version and stable version
- - Update the supported version range in [Versioning](../Versioning.md)
-1. Update [Versioning > TypeScript](../Versioning.md#typescript)
+ - Update the supported version range in [Versioning](../Versioning.mdx)
+1. Update [Versioning > TypeScript](../Versioning.mdx#typescript)
1. Send a PR that updates this documentation page to point to your newer issues and PRs
- Also update any of these steps if you go with a different process
@@ -108,7 +108,7 @@ A single PR can remove support for old TypeScript versions as a breaking change:
1. Update the root `package.json` `devDependency`
1. Update the `SUPPORTED_TYPESCRIPT_VERSIONS` constant in `warnAboutTSVersion.ts`
1. Update the `versions` constant in `version-check.ts`
-1. Update [Versioning > TypeScript](../Versioning.md#typescript)
+1. Update [Versioning > TypeScript](../Versioning.mdx#typescript)
1. Search for source code comments (excluding `CHANGELOG.md` files) that mention a now-unsupported version of TypeScript.
- For example, to remove support for v4.3, searches might include:
- `4.3`
diff --git a/jest.config.base.js b/jest.config.base.js
index 707c023f2695..48c306a70c06 100644
--- a/jest.config.base.js
+++ b/jest.config.base.js
@@ -18,7 +18,6 @@ module.exports = {
'json',
'node',
],
- resolver: '/../../tests/jest-resolver.js',
setupFilesAfterEnv: ['console-fail-test/setup.js'],
testRegex: ['./tests/.+\\.test\\.ts$', './tests/.+\\.spec\\.ts$'],
transform: {
diff --git a/lerna.json b/lerna.json
index e150bc530f7f..719e543d7471 100644
--- a/lerna.json
+++ b/lerna.json
@@ -1,5 +1,5 @@
{
- "version": "5.50.0",
+ "version": "5.51.0",
"npmClient": "yarn",
"useWorkspaces": true,
"stream": true
diff --git a/package.json b/package.json
index f8b24d34edf6..b8ee71a11c11 100644
--- a/package.json
+++ b/package.json
@@ -79,7 +79,6 @@
"cross-fetch": "^3.1.5",
"cspell": "^6.0.0",
"downlevel-dts": ">=0.10.0",
- "enhanced-resolve": "^5.9.3",
"eslint": "^8.15.0",
"eslint-plugin-deprecation": "^1.3.2",
"eslint-plugin-eslint-comments": "^3.2.0",
diff --git a/packages/ast-spec/CHANGELOG.md b/packages/ast-spec/CHANGELOG.md
index 3b4168eff864..4b57fd921e85 100644
--- a/packages/ast-spec/CHANGELOG.md
+++ b/packages/ast-spec/CHANGELOG.md
@@ -3,6 +3,14 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
+# [5.51.0](https://github.com/typescript-eslint/typescript-eslint/compare/v5.50.0...v5.51.0) (2023-02-06)
+
+**Note:** Version bump only for package @typescript-eslint/ast-spec
+
+
+
+
+
# [5.50.0](https://github.com/typescript-eslint/typescript-eslint/compare/v5.49.0...v5.50.0) (2023-01-31)
diff --git a/packages/ast-spec/package.json b/packages/ast-spec/package.json
index c5e160a5c415..c1e472c43655 100644
--- a/packages/ast-spec/package.json
+++ b/packages/ast-spec/package.json
@@ -1,6 +1,6 @@
{
"name": "@typescript-eslint/ast-spec",
- "version": "5.50.0",
+ "version": "5.51.0",
"description": "Complete specification for the TypeScript-ESTree AST",
"private": true,
"keywords": [
diff --git a/packages/eslint-plugin-internal/CHANGELOG.md b/packages/eslint-plugin-internal/CHANGELOG.md
index 0cd6ad00030d..1cec28fd8aea 100644
--- a/packages/eslint-plugin-internal/CHANGELOG.md
+++ b/packages/eslint-plugin-internal/CHANGELOG.md
@@ -3,6 +3,14 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
+# [5.51.0](https://github.com/typescript-eslint/typescript-eslint/compare/v5.50.0...v5.51.0) (2023-02-06)
+
+**Note:** Version bump only for package @typescript-eslint/eslint-plugin-internal
+
+
+
+
+
# [5.50.0](https://github.com/typescript-eslint/typescript-eslint/compare/v5.49.0...v5.50.0) (2023-01-31)
**Note:** Version bump only for package @typescript-eslint/eslint-plugin-internal
diff --git a/packages/eslint-plugin-internal/package.json b/packages/eslint-plugin-internal/package.json
index 7b81526e7edd..f5d0e2b957e5 100644
--- a/packages/eslint-plugin-internal/package.json
+++ b/packages/eslint-plugin-internal/package.json
@@ -1,6 +1,6 @@
{
"name": "@typescript-eslint/eslint-plugin-internal",
- "version": "5.50.0",
+ "version": "5.51.0",
"private": true,
"main": "dist/index.js",
"scripts": {
@@ -14,9 +14,9 @@
},
"dependencies": {
"@types/prettier": "*",
- "@typescript-eslint/scope-manager": "5.50.0",
- "@typescript-eslint/type-utils": "5.50.0",
- "@typescript-eslint/utils": "5.50.0",
+ "@typescript-eslint/scope-manager": "5.51.0",
+ "@typescript-eslint/type-utils": "5.51.0",
+ "@typescript-eslint/utils": "5.51.0",
"prettier": "*"
}
}
diff --git a/packages/eslint-plugin-tslint/CHANGELOG.md b/packages/eslint-plugin-tslint/CHANGELOG.md
index b2551812889f..992cc4adb477 100644
--- a/packages/eslint-plugin-tslint/CHANGELOG.md
+++ b/packages/eslint-plugin-tslint/CHANGELOG.md
@@ -3,6 +3,14 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
+# [5.51.0](https://github.com/typescript-eslint/typescript-eslint/compare/v5.50.0...v5.51.0) (2023-02-06)
+
+**Note:** Version bump only for package @typescript-eslint/eslint-plugin-tslint
+
+
+
+
+
# [5.50.0](https://github.com/typescript-eslint/typescript-eslint/compare/v5.49.0...v5.50.0) (2023-01-31)
**Note:** Version bump only for package @typescript-eslint/eslint-plugin-tslint
diff --git a/packages/eslint-plugin-tslint/package.json b/packages/eslint-plugin-tslint/package.json
index 23bf62f3fc5c..764c382788dd 100644
--- a/packages/eslint-plugin-tslint/package.json
+++ b/packages/eslint-plugin-tslint/package.json
@@ -1,6 +1,6 @@
{
"name": "@typescript-eslint/eslint-plugin-tslint",
- "version": "5.50.0",
+ "version": "5.51.0",
"main": "dist/index.js",
"typings": "src/index.ts",
"description": "ESLint plugin that wraps a TSLint configuration and lints the whole source using TSLint",
@@ -38,7 +38,7 @@
"typecheck": "tsc -p tsconfig.json --noEmit"
},
"dependencies": {
- "@typescript-eslint/utils": "5.50.0",
+ "@typescript-eslint/utils": "5.51.0",
"lodash": "^4.17.21"
},
"peerDependencies": {
@@ -48,6 +48,6 @@
},
"devDependencies": {
"@types/lodash": "*",
- "@typescript-eslint/parser": "5.50.0"
+ "@typescript-eslint/parser": "5.51.0"
}
}
diff --git a/packages/eslint-plugin/CHANGELOG.md b/packages/eslint-plugin/CHANGELOG.md
index 25cce92ccc4f..3df0f2a57df7 100644
--- a/packages/eslint-plugin/CHANGELOG.md
+++ b/packages/eslint-plugin/CHANGELOG.md
@@ -3,6 +3,26 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
+# [5.51.0](https://github.com/typescript-eslint/typescript-eslint/compare/v5.50.0...v5.51.0) (2023-02-06)
+
+
+### Bug Fixes
+
+* **eslint-plugin:** [sort-type-constituents] fixed behavior change ([#6384](https://github.com/typescript-eslint/typescript-eslint/issues/6384)) ([5bf7f7f](https://github.com/typescript-eslint/typescript-eslint/commit/5bf7f7fe48aee61a676dfbe829c2a5e9e44cd552)), closes [#6339](https://github.com/typescript-eslint/typescript-eslint/issues/6339)
+* **eslint-plugin:** do not use .at(), Node 14 does not support it ([#6402](https://github.com/typescript-eslint/typescript-eslint/issues/6402)) ([077ed1b](https://github.com/typescript-eslint/typescript-eslint/commit/077ed1b5be844df35b7fba554ddae579b3144787))
+
+
+### Features
+
+* **eslint-plugin:** [naming-convention] improve performance by removing unnecessary selectors ([#6376](https://github.com/typescript-eslint/typescript-eslint/issues/6376)) ([3647a1c](https://github.com/typescript-eslint/typescript-eslint/commit/3647a1c1bbcfe6551647632fc2d978fa90881de1))
+* **eslint-plugin:** [no-floating-promises] error on logical expression ([#6356](https://github.com/typescript-eslint/typescript-eslint/issues/6356)) ([f330e06](https://github.com/typescript-eslint/typescript-eslint/commit/f330e0651548d55163ddc3233c90fd3cbe37c9c0))
+* **eslint-plugin:** [no-import-type-side-effects] add rule to warn against runtime side effects with `verbatimModuleSyntax` ([#6394](https://github.com/typescript-eslint/typescript-eslint/issues/6394)) ([b14d3be](https://github.com/typescript-eslint/typescript-eslint/commit/b14d3be0f305d71e0adfc9381e9de993898b2b43))
+* **eslint-plugin:** [strict-boolean-expressions] add allow nullable enum to strict boolean expressions ([#6096](https://github.com/typescript-eslint/typescript-eslint/issues/6096)) ([d4747cd](https://github.com/typescript-eslint/typescript-eslint/commit/d4747cd8cc9dad2bf2cb64e1c0e8980ce34d82c7))
+
+
+
+
+
# [5.50.0](https://github.com/typescript-eslint/typescript-eslint/compare/v5.49.0...v5.50.0) (2023-01-31)
diff --git a/packages/eslint-plugin/docs/rules/consistent-type-imports.md b/packages/eslint-plugin/docs/rules/consistent-type-imports.md
index 045b7c2fa7da..baa41eccda18 100644
--- a/packages/eslint-plugin/docs/rules/consistent-type-imports.md
+++ b/packages/eslint-plugin/docs/rules/consistent-type-imports.md
@@ -95,3 +95,9 @@ If you are using [type-aware linting](https://typescript-eslint.io/linting/typed
## When Not To Use It
- If you specifically want to use both import kinds for stylistic reasons, you can disable this rule.
+
+## Related To
+
+- [`no-import-type-side-effects`](./no-import-type-side-effects.md)
+- [`import/consistent-type-specifier-style`](https://github.com/import-js/eslint-plugin-import/blob/main/docs/rules/consistent-type-specifier-style.md)
+- [`import/no-duplicates` with `{"prefer-inline": true}`](https://github.com/import-js/eslint-plugin-import/blob/main/docs/rules/no-duplicates.md#inline-type-imports)
diff --git a/packages/eslint-plugin/docs/rules/key-spacing.md b/packages/eslint-plugin/docs/rules/key-spacing.md
index 3bfcf5f389f0..35108c28f862 100644
--- a/packages/eslint-plugin/docs/rules/key-spacing.md
+++ b/packages/eslint-plugin/docs/rules/key-spacing.md
@@ -8,5 +8,5 @@ description: 'Enforce consistent spacing between property names and type annotat
## Examples
-This rule extends the base [`eslint/keyword-spacing`](https://eslint.org/docs/rules/key-spacing) rule.
+This rule extends the base [`eslint/key-spacing`](https://eslint.org/docs/rules/key-spacing) rule.
This version adds support for type annotations on interfaces, classes and type literals properties.
diff --git a/packages/eslint-plugin/docs/rules/no-import-type-side-effects.md b/packages/eslint-plugin/docs/rules/no-import-type-side-effects.md
new file mode 100644
index 000000000000..35b8f2c5282e
--- /dev/null
+++ b/packages/eslint-plugin/docs/rules/no-import-type-side-effects.md
@@ -0,0 +1,75 @@
+---
+description: 'Enforce the use of top-level import type qualifier when an import only has specifiers with inline type qualifiers.'
+---
+
+> 🛑 This file is source code, not the primary documentation location! 🛑
+>
+> See **https://typescript-eslint.io/rules/no-import-type-side-effects** for documentation.
+
+The [`--verbatimModuleSyntax`](https://www.typescriptlang.org/tsconfig#verbatimModuleSyntax) compiler option causes TypeScript to do simple and predictable transpilation on import declarations.
+Namely, it completely removes import declarations with a top-level `type` qualifier, and it removes any import specifiers with an inline `type` qualifier.
+
+The latter behavior does have one potentially surprising effect in that in certain cases TS can leave behind a "side effect" import at runtime:
+
+```ts
+import { type A, type B } from 'mod';
+
+// is transpiled to
+
+import {} from 'mod';
+// which is the same as
+import 'mod';
+```
+
+For the rare case of needing to import for side effects, this may be desirable - but for most cases you will not want to leave behind an unnecessary side effect import.
+
+## Examples
+
+This rule enforces that you use a top-level `type` qualifier for imports when it only imports specifiers with an inline `type` qualifier
+
+
+
+### ❌ Incorrect
+
+```ts
+import { type A } from 'mod';
+import { type A as AA } from 'mod';
+import { type A, type B } from 'mod';
+import { type A as AA, type B as BB } from 'mod';
+```
+
+### ✅ Correct
+
+```ts
+import type { A } from 'mod';
+import type { A as AA } from 'mod';
+import type { A, B } from 'mod';
+import type { A as AA, B as BB } from 'mod';
+
+import T from 'mod';
+import type T from 'mod';
+
+import * as T from 'mod';
+import type * as T from 'mod';
+
+import { T } from 'mod';
+import type { T } from 'mod';
+import { T, U } from 'mod';
+import type { T, U } from 'mod';
+import { type T, U } from 'mod';
+import { T, type U } from 'mod';
+
+import type T, { U } from 'mod';
+import T, { type U } from 'mod';
+```
+
+## When Not To Use It
+
+- If you want to leave behind side effect imports, then you shouldn't use this rule.
+- If you're not using TypeScript 5.0's `verbatimModuleSyntax` option, then you don't need this rule.
+
+## Related To
+
+- [`consistent-type-imports`](./consistent-type-imports.md)
+- [`import/consistent-type-specifier-style`](https://github.com/import-js/eslint-plugin-import/blob/main/docs/rules/consistent-type-specifier-style.md)
+- [`import/no-duplicates` with `{"prefer-inline": true}`](https://github.com/import-js/eslint-plugin-import/blob/main/docs/rules/no-duplicates.md#inline-type-imports)
diff --git a/packages/eslint-plugin/docs/rules/strict-boolean-expressions.md b/packages/eslint-plugin/docs/rules/strict-boolean-expressions.md
index b7e8c13a9be2..45a7f8f94962 100644
--- a/packages/eslint-plugin/docs/rules/strict-boolean-expressions.md
+++ b/packages/eslint-plugin/docs/rules/strict-boolean-expressions.md
@@ -124,6 +124,12 @@ Allows `number | null | undefined` in a boolean context.
This is unsafe because nullable numbers can be either a falsy number or nullish.
Set this to `true` if you don't mind implicitly treating zero or NaN the same as a nullish value.
+### `allowNullableEnum`
+
+Allows `enum | null | undefined` in a boolean context.
+This is unsafe because nullable enums can be either a falsy number or nullish.
+Set this to `true` if you don't mind implicitly treating an enum whose value is zero the same as a nullish value.
+
### `allowAny`
Allows `any` in a boolean context.
diff --git a/packages/eslint-plugin/package.json b/packages/eslint-plugin/package.json
index 724b3eea0f1c..0a4fa810a7e4 100644
--- a/packages/eslint-plugin/package.json
+++ b/packages/eslint-plugin/package.json
@@ -1,6 +1,6 @@
{
"name": "@typescript-eslint/eslint-plugin",
- "version": "5.50.0",
+ "version": "5.51.0",
"description": "TypeScript plugin for ESLint",
"keywords": [
"eslint",
@@ -44,9 +44,9 @@
"typecheck": "tsc -p tsconfig.json --noEmit"
},
"dependencies": {
- "@typescript-eslint/scope-manager": "5.50.0",
- "@typescript-eslint/type-utils": "5.50.0",
- "@typescript-eslint/utils": "5.50.0",
+ "@typescript-eslint/scope-manager": "5.51.0",
+ "@typescript-eslint/type-utils": "5.51.0",
+ "@typescript-eslint/utils": "5.51.0",
"debug": "^4.3.4",
"grapheme-splitter": "^1.0.4",
"ignore": "^5.2.0",
diff --git a/packages/eslint-plugin/src/configs/all.ts b/packages/eslint-plugin/src/configs/all.ts
index 452035c4ebf2..eb3856f10c3b 100644
--- a/packages/eslint-plugin/src/configs/all.ts
+++ b/packages/eslint-plugin/src/configs/all.ts
@@ -55,7 +55,6 @@ export = {
'no-dupe-class-members': 'off',
'@typescript-eslint/no-dupe-class-members': 'error',
'@typescript-eslint/no-duplicate-enum-values': 'error',
- 'no-duplicate-imports': 'off',
'@typescript-eslint/no-dynamic-delete': 'error',
'no-empty-function': 'off',
'@typescript-eslint/no-empty-function': 'error',
@@ -71,6 +70,7 @@ export = {
'@typescript-eslint/no-for-in-array': 'error',
'no-implied-eval': 'off',
'@typescript-eslint/no-implied-eval': 'error',
+ '@typescript-eslint/no-import-type-side-effects': 'error',
'@typescript-eslint/no-inferrable-types': 'error',
'no-invalid-this': 'off',
'@typescript-eslint/no-invalid-this': 'error',
@@ -88,7 +88,6 @@ export = {
'@typescript-eslint/no-non-null-asserted-nullish-coalescing': 'error',
'@typescript-eslint/no-non-null-asserted-optional-chain': 'error',
'@typescript-eslint/no-non-null-assertion': 'error',
- '@typescript-eslint/parameter-properties': 'error',
'no-redeclare': 'off',
'@typescript-eslint/no-redeclare': 'error',
'@typescript-eslint/no-redundant-type-constituents': 'error',
@@ -128,6 +127,7 @@ export = {
'@typescript-eslint/object-curly-spacing': 'error',
'padding-line-between-statements': 'off',
'@typescript-eslint/padding-line-between-statements': 'error',
+ '@typescript-eslint/parameter-properties': 'error',
'@typescript-eslint/prefer-as-const': 'error',
'@typescript-eslint/prefer-enum-initializers': 'error',
'@typescript-eslint/prefer-for-of': 'error',
diff --git a/packages/eslint-plugin/src/configs/strict.ts b/packages/eslint-plugin/src/configs/strict.ts
index ccd44b85a0a1..99b4e83b5081 100644
--- a/packages/eslint-plugin/src/configs/strict.ts
+++ b/packages/eslint-plugin/src/configs/strict.ts
@@ -8,8 +8,8 @@ export = {
'@typescript-eslint/array-type': 'warn',
'@typescript-eslint/ban-tslint-comment': 'warn',
'@typescript-eslint/class-literal-property-style': 'warn',
- '@typescript-eslint/consistent-indexed-object-style': 'warn',
'@typescript-eslint/consistent-generic-constructors': 'warn',
+ '@typescript-eslint/consistent-indexed-object-style': 'warn',
'@typescript-eslint/consistent-type-assertions': 'warn',
'@typescript-eslint/consistent-type-definitions': 'warn',
'dot-notation': 'off',
diff --git a/packages/eslint-plugin/src/rules/adjacent-overload-signatures.ts b/packages/eslint-plugin/src/rules/adjacent-overload-signatures.ts
index 5f5ddfc0aad6..498a9bf5ae1b 100644
--- a/packages/eslint-plugin/src/rules/adjacent-overload-signatures.ts
+++ b/packages/eslint-plugin/src/rules/adjacent-overload-signatures.ts
@@ -65,7 +65,7 @@ export default util.createRule({
case AST_NODE_TYPES.TSDeclareFunction:
case AST_NODE_TYPES.FunctionDeclaration: {
const name = member.id?.name ?? null;
- if (name === null) {
+ if (name == null) {
return null;
}
return {
@@ -143,7 +143,7 @@ export default util.createRule({
members.forEach(member => {
const method = getMemberMethod(member);
- if (method === null) {
+ if (method == null) {
lastMethod = null;
return;
}
diff --git a/packages/eslint-plugin/src/rules/ban-types.ts b/packages/eslint-plugin/src/rules/ban-types.ts
index dce411405155..f21dda8a249a 100644
--- a/packages/eslint-plugin/src/rules/ban-types.ts
+++ b/packages/eslint-plugin/src/rules/ban-types.ts
@@ -36,7 +36,7 @@ function stringifyNode(
function getCustomMessage(
bannedType: null | string | { message?: string; fixWith?: string },
): string {
- if (bannedType === null) {
+ if (bannedType == null) {
return '';
}
diff --git a/packages/eslint-plugin/src/rules/comma-spacing.ts b/packages/eslint-plugin/src/rules/comma-spacing.ts
index fda50d1b2e4a..a1ebcc181f28 100644
--- a/packages/eslint-plugin/src/rules/comma-spacing.ts
+++ b/packages/eslint-plugin/src/rules/comma-spacing.ts
@@ -68,7 +68,7 @@ export default createRule({
let previousToken = sourceCode.getFirstToken(node);
for (const element of node.elements) {
let token: TSESTree.Token | null;
- if (element === null) {
+ if (element == null) {
token = sourceCode.getTokenAfter(previousToken!);
if (token && isCommaToken(token)) {
ignoredTokens.add(token);
diff --git a/packages/eslint-plugin/src/rules/consistent-type-imports.ts b/packages/eslint-plugin/src/rules/consistent-type-imports.ts
index a812116f2f8b..4c5cf771901d 100644
--- a/packages/eslint-plugin/src/rules/consistent-type-imports.ts
+++ b/packages/eslint-plugin/src/rules/consistent-type-imports.ts
@@ -1,5 +1,5 @@
import type { TSESLint, TSESTree } from '@typescript-eslint/utils';
-import { AST_NODE_TYPES, AST_TOKEN_TYPES } from '@typescript-eslint/utils';
+import { AST_NODE_TYPES } from '@typescript-eslint/utils';
import * as util from '../util';
@@ -32,18 +32,6 @@ interface ReportValueImport {
inlineTypeSpecifiers: TSESTree.ImportSpecifier[];
}
-function isImportToken(
- token: TSESTree.Token,
-): token is TSESTree.KeywordToken & { value: 'import' } {
- return token.type === AST_TOKEN_TYPES.Keyword && token.value === 'import';
-}
-
-function isTypeToken(
- token: TSESTree.Token,
-): token is TSESTree.IdentifierToken & { value: 'type' } {
- return token.type === AST_TOKEN_TYPES.Identifier && token.value === 'type';
-}
-
type MessageIds =
| 'typeOverValue'
| 'someImportsAreOnlyTypes'
@@ -751,7 +739,7 @@ export default util.createRule({
) {
if (report.typeSpecifiers.length === node.specifiers.length) {
const importToken = util.nullThrows(
- sourceCode.getFirstToken(node, isImportToken),
+ sourceCode.getFirstToken(node, util.isImportKeyword),
util.NullThrowsReasons.MissingToken('import', node.type),
);
// import type Type from 'foo'
@@ -800,7 +788,7 @@ export default util.createRule({
// import type Foo from 'foo'
// ^^^^^ insert
const importToken = util.nullThrows(
- sourceCode.getFirstToken(node, isImportToken),
+ sourceCode.getFirstToken(node, util.isImportKeyword),
util.NullThrowsReasons.MissingToken('import', node.type),
);
yield fixer.insertTextAfter(importToken, ' type');
@@ -945,14 +933,14 @@ export default util.createRule({
// import type Foo from 'foo'
// ^^^^ remove
const importToken = util.nullThrows(
- sourceCode.getFirstToken(node, isImportToken),
+ sourceCode.getFirstToken(node, util.isImportKeyword),
util.NullThrowsReasons.MissingToken('import', node.type),
);
const typeToken = util.nullThrows(
sourceCode.getFirstTokenBetween(
importToken,
node.specifiers[0]?.local ?? node.source,
- isTypeToken,
+ util.isTypeKeyword,
),
util.NullThrowsReasons.MissingToken('type', node.type),
);
@@ -970,7 +958,7 @@ export default util.createRule({
// import { type Foo } from 'foo'
// ^^^^ remove
const typeToken = util.nullThrows(
- sourceCode.getFirstToken(node, isTypeToken),
+ sourceCode.getFirstToken(node, util.isTypeKeyword),
util.NullThrowsReasons.MissingToken('type', node.type),
);
const afterToken = util.nullThrows(
diff --git a/packages/eslint-plugin/src/rules/index.ts b/packages/eslint-plugin/src/rules/index.ts
index f7e51fdabd58..bbddfc8d4709 100644
--- a/packages/eslint-plugin/src/rules/index.ts
+++ b/packages/eslint-plugin/src/rules/index.ts
@@ -48,6 +48,7 @@ import noFloatingPromises from './no-floating-promises';
import noForInArray from './no-for-in-array';
import noImplicitAnyCatch from './no-implicit-any-catch';
import noImpliedEval from './no-implied-eval';
+import noImportTypeSideEffects from './no-import-type-side-effects';
import noInferrableTypes from './no-inferrable-types';
import noInvalidThis from './no-invalid-this';
import noInvalidVoidType from './no-invalid-void-type';
@@ -180,6 +181,7 @@ export default {
'no-for-in-array': noForInArray,
'no-implicit-any-catch': noImplicitAnyCatch,
'no-implied-eval': noImpliedEval,
+ 'no-import-type-side-effects': noImportTypeSideEffects,
'no-inferrable-types': noInferrableTypes,
'no-invalid-this': noInvalidThis,
'no-invalid-void-type': noInvalidVoidType,
diff --git a/packages/eslint-plugin/src/rules/key-spacing.ts b/packages/eslint-plugin/src/rules/key-spacing.ts
index 587d2674f4f3..2562107ee050 100644
--- a/packages/eslint-plugin/src/rules/key-spacing.ts
+++ b/packages/eslint-plugin/src/rules/key-spacing.ts
@@ -14,6 +14,16 @@ const baseSchema = Array.isArray(baseRule.meta.schema)
? baseRule.meta.schema[0]
: baseRule.meta.schema;
+/**
+ * TODO: replace with native .at() once Node 14 stops being supported
+ */
+function at(arr: T[], position: number): T | undefined {
+ if (position < 0) {
+ return arr[arr.length + position];
+ }
+ return arr[position];
+}
+
export default util.createRule({
name: 'key-spacing',
meta: {
@@ -41,7 +51,7 @@ export default util.createRule({
function adjustedColumn(position: TSESTree.Position): number {
const line = position.line - 1; // position.line is 1-indexed
return util.getStringLength(
- sourceCode.lines[line].slice(0, position.column),
+ at(sourceCode.lines, line)!.slice(0, position.column),
);
}
@@ -87,7 +97,7 @@ export default util.createRule({
return code.slice(
0,
sourceCode.getTokenAfter(
- node.parameters.at(-1)!,
+ at(node.parameters, -1)!,
util.isClosingBracketToken,
)!.range[1] - node.range[0],
);
@@ -102,7 +112,7 @@ export default util.createRule({
return getLastTokenBeforeColon(
node.type !== AST_NODE_TYPES.TSIndexSignature
? node.key
- : node.parameters.at(-1)!,
+ : at(node.parameters, -1)!,
).loc.end;
}
@@ -202,7 +212,7 @@ export default util.createRule({
if (
leadingComments.length &&
leadingComments[0].loc.start.line - groupEndLine <= 1 &&
- candidateValueStartLine - leadingComments.at(-1)!.loc.end.line <= 1
+ candidateValueStartLine - at(leadingComments, -1)!.loc.end.line <= 1
) {
for (let i = 1; i < leadingComments.length; i++) {
if (
@@ -373,7 +383,7 @@ export default util.createRule({
let prevNode: TSESTree.Node | undefined = undefined;
for (const node of members) {
- let prevAlignedNode = currentAlignGroup.at(-1);
+ let prevAlignedNode = at(currentAlignGroup, -1);
if (prevAlignedNode !== prevNode) {
prevAlignedNode = undefined;
}
diff --git a/packages/eslint-plugin/src/rules/member-ordering.ts b/packages/eslint-plugin/src/rules/member-ordering.ts
index 81e0ae6484e9..f8db5ade0caf 100644
--- a/packages/eslint-plugin/src/rules/member-ordering.ts
+++ b/packages/eslint-plugin/src/rules/member-ordering.ts
@@ -489,7 +489,7 @@ function getRank(
): number {
const type = getNodeType(node);
- if (type === null) {
+ if (type == null) {
// shouldn't happen but just in case, put it on the end
return orderConfig.length - 1;
}
@@ -842,7 +842,7 @@ export default util.createRule({
supportsModifiers,
);
- if (grouped === null) {
+ if (grouped == null) {
return false;
}
diff --git a/packages/eslint-plugin/src/rules/naming-convention-utils/types.ts b/packages/eslint-plugin/src/rules/naming-convention-utils/types.ts
index e900b4c5f171..d5c15994b8bb 100644
--- a/packages/eslint-plugin/src/rules/naming-convention-utils/types.ts
+++ b/packages/eslint-plugin/src/rules/naming-convention-utils/types.ts
@@ -64,7 +64,7 @@ type ValidatorFunction = (
node: TSESTree.Identifier | TSESTree.PrivateIdentifier | TSESTree.Literal,
modifiers?: Set,
) => void;
-type ParsedOptions = Record;
+type ParsedOptions = Record;
type Context = Readonly>;
export type {
diff --git a/packages/eslint-plugin/src/rules/naming-convention-utils/validator.ts b/packages/eslint-plugin/src/rules/naming-convention-utils/validator.ts
index e96ff19e3748..c2b87ccc33b1 100644
--- a/packages/eslint-plugin/src/rules/naming-convention-utils/validator.ts
+++ b/packages/eslint-plugin/src/rules/naming-convention-utils/validator.ts
@@ -101,25 +101,25 @@ function createValidator(
let name: string | null = originalName;
name = validateUnderscore('leading', config, name, node, originalName);
- if (name === null) {
+ if (name == null) {
// fail
return;
}
name = validateUnderscore('trailing', config, name, node, originalName);
- if (name === null) {
+ if (name == null) {
// fail
return;
}
name = validateAffix('prefix', config, name, node, originalName);
- if (name === null) {
+ if (name == null) {
// fail
return;
}
name = validateAffix('suffix', config, name, node, originalName);
- if (name === null) {
+ if (name == null) {
// fail
return;
}
@@ -383,7 +383,7 @@ function createValidator(
modifiers: Set,
): boolean {
const formats = config.format;
- if (formats === null || formats.length === 0) {
+ if (!formats?.length) {
return true;
}
@@ -427,7 +427,7 @@ function isCorrectType(
context: Context,
selector: Selectors,
): boolean {
- if (config.types === null) {
+ if (config.types == null) {
return true;
}
diff --git a/packages/eslint-plugin/src/rules/naming-convention.ts b/packages/eslint-plugin/src/rules/naming-convention.ts
index 3b500b0b347e..f5a59614851d 100644
--- a/packages/eslint-plugin/src/rules/naming-convention.ts
+++ b/packages/eslint-plugin/src/rules/naming-convention.ts
@@ -95,7 +95,7 @@ export default util.createRule({
.getParserServices(context, true)
.program.getCompilerOptions();
function handleMember(
- validator: ValidatorFunction | null,
+ validator: ValidatorFunction,
node:
| TSESTree.PropertyNonComputedName
| TSESTree.PropertyDefinitionNonComputedName
@@ -215,176 +215,198 @@ export default util.createRule({
);
}
- return {
+ const selectors: {
+ readonly [k in keyof TSESLint.RuleListener]: Readonly<{
+ validator: ValidatorFunction;
+ handler: (
+ node: Parameters>[0],
+ validator: ValidatorFunction,
+ ) => void;
+ }>;
+ } = {
// #region variable
- VariableDeclarator(node: TSESTree.VariableDeclarator): void {
- const validator = validators.variable;
- if (!validator) {
- return;
- }
- const identifiers = getIdentifiersFromPattern(node.id);
+ VariableDeclarator: {
+ validator: validators.variable,
+ handler: (node, validator): void => {
+ const identifiers = getIdentifiersFromPattern(node.id);
- const baseModifiers = new Set();
- const parent = node.parent;
- if (parent?.type === AST_NODE_TYPES.VariableDeclaration) {
- if (parent.kind === 'const') {
- baseModifiers.add(Modifiers.const);
- }
+ const baseModifiers = new Set();
+ const parent = node.parent;
+ if (parent?.type === AST_NODE_TYPES.VariableDeclaration) {
+ if (parent.kind === 'const') {
+ baseModifiers.add(Modifiers.const);
+ }
- if (isGlobal(context.getScope())) {
- baseModifiers.add(Modifiers.global);
+ if (isGlobal(context.getScope())) {
+ baseModifiers.add(Modifiers.global);
+ }
}
- }
- identifiers.forEach(id => {
- const modifiers = new Set(baseModifiers);
+ identifiers.forEach(id => {
+ const modifiers = new Set(baseModifiers);
- if (isDestructured(id)) {
- modifiers.add(Modifiers.destructured);
- }
+ if (isDestructured(id)) {
+ modifiers.add(Modifiers.destructured);
+ }
- if (isExported(parent, id.name, context.getScope())) {
- modifiers.add(Modifiers.exported);
- }
+ if (isExported(parent, id.name, context.getScope())) {
+ modifiers.add(Modifiers.exported);
+ }
- if (isUnused(id.name)) {
- modifiers.add(Modifiers.unused);
- }
+ if (isUnused(id.name)) {
+ modifiers.add(Modifiers.unused);
+ }
- if (isAsyncVariableIdentifier(id)) {
- modifiers.add(Modifiers.async);
- }
+ if (isAsyncVariableIdentifier(id)) {
+ modifiers.add(Modifiers.async);
+ }
- validator(id, modifiers);
- });
+ validator(id, modifiers);
+ });
+ },
},
// #endregion
// #region function
- 'FunctionDeclaration, TSDeclareFunction, FunctionExpression'(
- node:
- | TSESTree.FunctionDeclaration
- | TSESTree.TSDeclareFunction
- | TSESTree.FunctionExpression,
- ): void {
- const validator = validators.function;
- if (!validator || node.id === null) {
- return;
- }
+ 'FunctionDeclaration, TSDeclareFunction, FunctionExpression': {
+ validator: validators.function,
+ handler: (
+ node:
+ | TSESTree.FunctionDeclaration
+ | TSESTree.TSDeclareFunction
+ | TSESTree.FunctionExpression,
+ validator,
+ ): void => {
+ if (node.id == null) {
+ return;
+ }
- const modifiers = new Set();
- // functions create their own nested scope
- const scope = context.getScope().upper;
+ const modifiers = new Set();
+ // functions create their own nested scope
+ const scope = context.getScope().upper;
- if (isGlobal(scope)) {
- modifiers.add(Modifiers.global);
- }
+ if (isGlobal(scope)) {
+ modifiers.add(Modifiers.global);
+ }
- if (isExported(node, node.id.name, scope)) {
- modifiers.add(Modifiers.exported);
- }
+ if (isExported(node, node.id.name, scope)) {
+ modifiers.add(Modifiers.exported);
+ }
- if (isUnused(node.id.name, scope)) {
- modifiers.add(Modifiers.unused);
- }
+ if (isUnused(node.id.name, scope)) {
+ modifiers.add(Modifiers.unused);
+ }
- if (node.async) {
- modifiers.add(Modifiers.async);
- }
+ if (node.async) {
+ modifiers.add(Modifiers.async);
+ }
- validator(node.id, modifiers);
+ validator(node.id, modifiers);
+ },
},
// #endregion function
// #region parameter
- 'FunctionDeclaration, TSDeclareFunction, TSEmptyBodyFunctionExpression, FunctionExpression, ArrowFunctionExpression'(
- node:
- | TSESTree.FunctionDeclaration
- | TSESTree.TSDeclareFunction
- | TSESTree.TSEmptyBodyFunctionExpression
- | TSESTree.FunctionExpression
- | TSESTree.ArrowFunctionExpression,
- ): void {
- const validator = validators.parameter;
- if (!validator) {
- return;
- }
-
- node.params.forEach(param => {
- if (param.type === AST_NODE_TYPES.TSParameterProperty) {
- return;
- }
-
- const identifiers = getIdentifiersFromPattern(param);
-
- identifiers.forEach(i => {
- const modifiers = new Set();
-
- if (isDestructured(i)) {
- modifiers.add(Modifiers.destructured);
- }
-
- if (isUnused(i.name)) {
- modifiers.add(Modifiers.unused);
- }
-
- validator(i, modifiers);
- });
- });
- },
+ 'FunctionDeclaration, TSDeclareFunction, TSEmptyBodyFunctionExpression, FunctionExpression, ArrowFunctionExpression':
+ {
+ validator: validators.parameter,
+ handler: (
+ node:
+ | TSESTree.FunctionDeclaration
+ | TSESTree.TSDeclareFunction
+ | TSESTree.TSEmptyBodyFunctionExpression
+ | TSESTree.FunctionExpression
+ | TSESTree.ArrowFunctionExpression,
+ validator,
+ ): void => {
+ node.params.forEach(param => {
+ if (param.type === AST_NODE_TYPES.TSParameterProperty) {
+ return;
+ }
+
+ const identifiers = getIdentifiersFromPattern(param);
+
+ identifiers.forEach(i => {
+ const modifiers = new Set();
+
+ if (isDestructured(i)) {
+ modifiers.add(Modifiers.destructured);
+ }
+
+ if (isUnused(i.name)) {
+ modifiers.add(Modifiers.unused);
+ }
+
+ validator(i, modifiers);
+ });
+ });
+ },
+ },
// #endregion parameter
// #region parameterProperty
- TSParameterProperty(node): void {
- const validator = validators.parameterProperty;
- if (!validator) {
- return;
- }
-
- const modifiers = getMemberModifiers(node);
+ TSParameterProperty: {
+ validator: validators.parameterProperty,
+ handler: (node, validator): void => {
+ const modifiers = getMemberModifiers(node);
- const identifiers = getIdentifiersFromPattern(node.parameter);
+ const identifiers = getIdentifiersFromPattern(node.parameter);
- identifiers.forEach(i => {
- validator(i, modifiers);
- });
+ identifiers.forEach(i => {
+ validator(i, modifiers);
+ });
+ },
},
// #endregion parameterProperty
// #region property
- ':not(ObjectPattern) > Property[computed = false][kind = "init"][value.type != "ArrowFunctionExpression"][value.type != "FunctionExpression"][value.type != "TSEmptyBodyFunctionExpression"]'(
- node: TSESTree.PropertyNonComputedName,
- ): void {
- const modifiers = new Set([Modifiers.public]);
- handleMember(validators.objectLiteralProperty, node, modifiers);
- },
-
- ':matches(PropertyDefinition, TSAbstractPropertyDefinition)[computed = false][value.type != "ArrowFunctionExpression"][value.type != "FunctionExpression"][value.type != "TSEmptyBodyFunctionExpression"]'(
- node:
- | TSESTree.PropertyDefinitionNonComputedName
- | TSESTree.TSAbstractPropertyDefinitionNonComputedName,
- ): void {
- const modifiers = getMemberModifiers(node);
- handleMember(validators.classProperty, node, modifiers);
- },
-
- 'TSPropertySignature[computed = false]'(
- node: TSESTree.TSPropertySignatureNonComputedName,
- ): void {
- const modifiers = new Set([Modifiers.public]);
- if (node.readonly) {
- modifiers.add(Modifiers.readonly);
- }
+ ':not(ObjectPattern) > Property[computed = false][kind = "init"][value.type != "ArrowFunctionExpression"][value.type != "FunctionExpression"][value.type != "TSEmptyBodyFunctionExpression"]':
+ {
+ validator: validators.objectLiteralProperty,
+ handler: (
+ node: TSESTree.PropertyNonComputedName,
+ validator,
+ ): void => {
+ const modifiers = new Set([Modifiers.public]);
+ handleMember(validator, node, modifiers);
+ },
+ },
+
+ ':matches(PropertyDefinition, TSAbstractPropertyDefinition)[computed = false][value.type != "ArrowFunctionExpression"][value.type != "FunctionExpression"][value.type != "TSEmptyBodyFunctionExpression"]':
+ {
+ validator: validators.classProperty,
+ handler: (
+ node:
+ | TSESTree.PropertyDefinitionNonComputedName
+ | TSESTree.TSAbstractPropertyDefinitionNonComputedName,
+ validator,
+ ): void => {
+ const modifiers = getMemberModifiers(node);
+ handleMember(validator, node, modifiers);
+ },
+ },
+
+ 'TSPropertySignature[computed = false]': {
+ validator: validators.typeProperty,
+ handler: (
+ node: TSESTree.TSPropertySignatureNonComputedName,
+ validator,
+ ): void => {
+ const modifiers = new Set([Modifiers.public]);
+ if (node.readonly) {
+ modifiers.add(Modifiers.readonly);
+ }
- handleMember(validators.typeProperty, node, modifiers);
+ handleMember(validator, node, modifiers);
+ },
},
// #endregion property
@@ -395,18 +417,22 @@ export default util.createRule({
'Property[computed = false][kind = "init"][value.type = "ArrowFunctionExpression"]',
'Property[computed = false][kind = "init"][value.type = "FunctionExpression"]',
'Property[computed = false][kind = "init"][value.type = "TSEmptyBodyFunctionExpression"]',
- ].join(', ')](
- node:
- | TSESTree.PropertyNonComputedName
- | TSESTree.TSMethodSignatureNonComputedName,
- ): void {
- const modifiers = new Set([Modifiers.public]);
-
- if (isAsyncMemberOrProperty(node)) {
- modifiers.add(Modifiers.async);
- }
+ ].join(', ')]: {
+ validator: validators.objectLiteralMethod,
+ handler: (
+ node:
+ | TSESTree.PropertyNonComputedName
+ | TSESTree.TSMethodSignatureNonComputedName,
+ validator,
+ ): void => {
+ const modifiers = new Set([Modifiers.public]);
+
+ if (isAsyncMemberOrProperty(node)) {
+ modifiers.add(Modifiers.async);
+ }
- handleMember(validators.objectLiteralMethod, node, modifiers);
+ handleMember(validator, node, modifiers);
+ },
},
[[
@@ -414,203 +440,218 @@ export default util.createRule({
':matches(PropertyDefinition, TSAbstractPropertyDefinition)[computed = false][value.type = "FunctionExpression"]',
':matches(PropertyDefinition, TSAbstractPropertyDefinition)[computed = false][value.type = "TSEmptyBodyFunctionExpression"]',
':matches(MethodDefinition, TSAbstractMethodDefinition)[computed = false][kind = "method"]',
- ].join(', ')](
- node:
- | TSESTree.PropertyDefinitionNonComputedName
- | TSESTree.TSAbstractPropertyDefinitionNonComputedName
- | TSESTree.MethodDefinitionNonComputedName
- | TSESTree.TSAbstractMethodDefinitionNonComputedName,
- ): void {
- const modifiers = getMemberModifiers(node);
-
- if (isAsyncMemberOrProperty(node)) {
- modifiers.add(Modifiers.async);
- }
+ ].join(', ')]: {
+ validator: validators.classMethod,
+ handler: (
+ node:
+ | TSESTree.PropertyDefinitionNonComputedName
+ | TSESTree.TSAbstractPropertyDefinitionNonComputedName
+ | TSESTree.MethodDefinitionNonComputedName
+ | TSESTree.TSAbstractMethodDefinitionNonComputedName,
+ validator,
+ ): void => {
+ const modifiers = getMemberModifiers(node);
+
+ if (isAsyncMemberOrProperty(node)) {
+ modifiers.add(Modifiers.async);
+ }
- handleMember(validators.classMethod, node, modifiers);
+ handleMember(validator, node, modifiers);
+ },
},
- 'TSMethodSignature[computed = false]'(
- node: TSESTree.TSMethodSignatureNonComputedName,
- ): void {
- const modifiers = new Set([Modifiers.public]);
- handleMember(validators.typeMethod, node, modifiers);
+ 'TSMethodSignature[computed = false]': {
+ validator: validators.typeMethod,
+ handler: (
+ node: TSESTree.TSMethodSignatureNonComputedName,
+ validator,
+ ): void => {
+ const modifiers = new Set([Modifiers.public]);
+ handleMember(validator, node, modifiers);
+ },
},
// #endregion method
// #region accessor
- 'Property[computed = false]:matches([kind = "get"], [kind = "set"])'(
- node: TSESTree.PropertyNonComputedName,
- ): void {
- const modifiers = new Set([Modifiers.public]);
- handleMember(validators.accessor, node, modifiers);
+ 'Property[computed = false]:matches([kind = "get"], [kind = "set"])': {
+ validator: validators.accessor,
+ handler: (node: TSESTree.PropertyNonComputedName, validator): void => {
+ const modifiers = new Set([Modifiers.public]);
+ handleMember(validator, node, modifiers);
+ },
},
- 'MethodDefinition[computed = false]:matches([kind = "get"], [kind = "set"])'(
- node: TSESTree.MethodDefinitionNonComputedName,
- ): void {
- const modifiers = getMemberModifiers(node);
- handleMember(validators.accessor, node, modifiers);
- },
+ 'MethodDefinition[computed = false]:matches([kind = "get"], [kind = "set"])':
+ {
+ validator: validators.accessor,
+ handler: (
+ node: TSESTree.MethodDefinitionNonComputedName,
+ validator,
+ ): void => {
+ const modifiers = getMemberModifiers(node);
+ handleMember(validator, node, modifiers);
+ },
+ },
// #endregion accessor
// #region enumMember
// computed is optional, so can't do [computed = false]
- 'TSEnumMember[computed != true]'(
- node: TSESTree.TSEnumMemberNonComputedName,
- ): void {
- const validator = validators.enumMember;
- if (!validator) {
- return;
- }
-
- const id = node.id;
- const modifiers = new Set();
-
- if (requiresQuoting(id, compilerOptions.target)) {
- modifiers.add(Modifiers.requiresQuotes);
- }
+ 'TSEnumMember[computed != true]': {
+ validator: validators.enumMember,
+ handler: (
+ node: TSESTree.TSEnumMemberNonComputedName,
+ validator,
+ ): void => {
+ const id = node.id;
+ const modifiers = new Set();
+
+ if (requiresQuoting(id, compilerOptions.target)) {
+ modifiers.add(Modifiers.requiresQuotes);
+ }
- validator(id, modifiers);
+ validator(id, modifiers);
+ },
},
// #endregion enumMember
// #region class
- 'ClassDeclaration, ClassExpression'(
- node: TSESTree.ClassDeclaration | TSESTree.ClassExpression,
- ): void {
- const validator = validators.class;
- if (!validator) {
- return;
- }
-
- const id = node.id;
- if (id === null) {
- return;
- }
+ 'ClassDeclaration, ClassExpression': {
+ validator: validators.class,
+ handler: (
+ node: TSESTree.ClassDeclaration | TSESTree.ClassExpression,
+ validator,
+ ): void => {
+ const id = node.id;
+ if (id == null) {
+ return;
+ }
- const modifiers = new Set();
- // classes create their own nested scope
- const scope = context.getScope().upper;
+ const modifiers = new Set();
+ // classes create their own nested scope
+ const scope = context.getScope().upper;
- if (node.abstract) {
- modifiers.add(Modifiers.abstract);
- }
+ if (node.abstract) {
+ modifiers.add(Modifiers.abstract);
+ }
- if (isExported(node, id.name, scope)) {
- modifiers.add(Modifiers.exported);
- }
+ if (isExported(node, id.name, scope)) {
+ modifiers.add(Modifiers.exported);
+ }
- if (isUnused(id.name, scope)) {
- modifiers.add(Modifiers.unused);
- }
+ if (isUnused(id.name, scope)) {
+ modifiers.add(Modifiers.unused);
+ }
- validator(id, modifiers);
+ validator(id, modifiers);
+ },
},
// #endregion class
// #region interface
- TSInterfaceDeclaration(node): void {
- const validator = validators.interface;
- if (!validator) {
- return;
- }
-
- const modifiers = new Set();
- const scope = context.getScope();
+ TSInterfaceDeclaration: {
+ validator: validators.interface,
+ handler: (node, validator): void => {
+ const modifiers = new Set();
+ const scope = context.getScope();
- if (isExported(node, node.id.name, scope)) {
- modifiers.add(Modifiers.exported);
- }
+ if (isExported(node, node.id.name, scope)) {
+ modifiers.add(Modifiers.exported);
+ }
- if (isUnused(node.id.name, scope)) {
- modifiers.add(Modifiers.unused);
- }
+ if (isUnused(node.id.name, scope)) {
+ modifiers.add(Modifiers.unused);
+ }
- validator(node.id, modifiers);
+ validator(node.id, modifiers);
+ },
},
// #endregion interface
// #region typeAlias
- TSTypeAliasDeclaration(node): void {
- const validator = validators.typeAlias;
- if (!validator) {
- return;
- }
+ TSTypeAliasDeclaration: {
+ validator: validators.typeAlias,
+ handler: (node, validator): void => {
+ const modifiers = new Set();
+ const scope = context.getScope();
- const modifiers = new Set();
- const scope = context.getScope();
-
- if (isExported(node, node.id.name, scope)) {
- modifiers.add(Modifiers.exported);
- }
+ if (isExported(node, node.id.name, scope)) {
+ modifiers.add(Modifiers.exported);
+ }
- if (isUnused(node.id.name, scope)) {
- modifiers.add(Modifiers.unused);
- }
+ if (isUnused(node.id.name, scope)) {
+ modifiers.add(Modifiers.unused);
+ }
- validator(node.id, modifiers);
+ validator(node.id, modifiers);
+ },
},
// #endregion typeAlias
// #region enum
- TSEnumDeclaration(node): void {
- const validator = validators.enum;
- if (!validator) {
- return;
- }
-
- const modifiers = new Set();
- // enums create their own nested scope
- const scope = context.getScope().upper;
+ TSEnumDeclaration: {
+ validator: validators.enum,
+ handler: (node, validator): void => {
+ const modifiers = new Set();
+ // enums create their own nested scope
+ const scope = context.getScope().upper;
- if (isExported(node, node.id.name, scope)) {
- modifiers.add(Modifiers.exported);
- }
+ if (isExported(node, node.id.name, scope)) {
+ modifiers.add(Modifiers.exported);
+ }
- if (isUnused(node.id.name, scope)) {
- modifiers.add(Modifiers.unused);
- }
+ if (isUnused(node.id.name, scope)) {
+ modifiers.add(Modifiers.unused);
+ }
- validator(node.id, modifiers);
+ validator(node.id, modifiers);
+ },
},
// #endregion enum
// #region typeParameter
- 'TSTypeParameterDeclaration > TSTypeParameter'(
- node: TSESTree.TSTypeParameter,
- ): void {
- const validator = validators.typeParameter;
- if (!validator) {
- return;
- }
-
- const modifiers = new Set();
- const scope = context.getScope();
+ 'TSTypeParameterDeclaration > TSTypeParameter': {
+ validator: validators.typeParameter,
+ handler: (node: TSESTree.TSTypeParameter, validator): void => {
+ const modifiers = new Set();
+ const scope = context.getScope();
- if (isUnused(node.name.name, scope)) {
- modifiers.add(Modifiers.unused);
- }
+ if (isUnused(node.name.name, scope)) {
+ modifiers.add(Modifiers.unused);
+ }
- validator(node.name, modifiers);
+ validator(node.name, modifiers);
+ },
},
// #endregion typeParameter
};
+
+ return Object.fromEntries(
+ Object.entries(selectors)
+ .map(([selector, { validator, handler }]) => {
+ return [
+ selector,
+ (node: Parameters[0]): void => {
+ handler(node, validator);
+ },
+ ] as const;
+ })
+ .filter((s): s is NonNullable => s != null),
+ );
},
});
diff --git a/packages/eslint-plugin/src/rules/no-floating-promises.ts b/packages/eslint-plugin/src/rules/no-floating-promises.ts
index 61829743e81b..c4ce3db8e1cc 100644
--- a/packages/eslint-plugin/src/rules/no-floating-promises.ts
+++ b/packages/eslint-plugin/src/rules/no-floating-promises.ts
@@ -4,6 +4,7 @@ import * as tsutils from 'tsutils';
import * as ts from 'typescript';
import * as util from '../util';
+import { OperatorPrecedence } from '../util';
type Options = [
{
@@ -66,7 +67,6 @@ export default util.createRule({
create(context, [options]) {
const parserServices = util.getParserServices(context);
const checker = parserServices.program.getTypeChecker();
- const sourceCode = context.getSourceCode();
return {
ExpressionStatement(node): void {
@@ -88,10 +88,21 @@ export default util.createRule({
suggest: [
{
messageId: 'floatingFixVoid',
- fix(fixer): TSESLint.RuleFix {
- let code = sourceCode.getText(node);
- code = `void ${code}`;
- return fixer.replaceText(node, code);
+ fix(fixer): TSESLint.RuleFix | TSESLint.RuleFix[] {
+ const tsNode = parserServices.esTreeNodeToTSNodeMap.get(
+ node.expression,
+ );
+ if (isHigherPrecedenceThanUnary(tsNode)) {
+ return fixer.insertTextBefore(node, 'void ');
+ } else {
+ return [
+ fixer.insertTextBefore(node, 'void ('),
+ fixer.insertTextAfterRange(
+ [expression.range[1], expression.range[1]],
+ ')',
+ ),
+ ];
+ }
},
},
],
@@ -116,7 +127,7 @@ export default util.createRule({
const tsNode = parserServices.esTreeNodeToTSNodeMap.get(
node.expression,
);
- if (isHigherPrecedenceThanAwait(tsNode)) {
+ if (isHigherPrecedenceThanUnary(tsNode)) {
return fixer.insertTextBefore(node, 'await ');
} else {
return [
@@ -136,16 +147,12 @@ export default util.createRule({
},
};
- function isHigherPrecedenceThanAwait(node: ts.Node): boolean {
+ function isHigherPrecedenceThanUnary(node: ts.Node): boolean {
const operator = tsutils.isBinaryExpression(node)
? node.operatorToken.kind
: ts.SyntaxKind.Unknown;
const nodePrecedence = util.getOperatorPrecedence(node.kind, operator);
- const awaitPrecedence = util.getOperatorPrecedence(
- ts.SyntaxKind.AwaitExpression,
- ts.SyntaxKind.Unknown,
- );
- return nodePrecedence > awaitPrecedence;
+ return nodePrecedence > OperatorPrecedence.Unary;
}
function isAsyncIife(node: TSESTree.ExpressionStatement): boolean {
@@ -214,6 +221,11 @@ export default util.createRule({
// `new Promise()`), the promise is not handled because it doesn't have the
// necessary then/catch call at the end of the chain.
return true;
+ } else if (node.type === AST_NODE_TYPES.LogicalExpression) {
+ return (
+ isUnhandledPromise(checker, node.left) ||
+ isUnhandledPromise(checker, node.right)
+ );
}
// We conservatively return false for all other types of expressions because
diff --git a/packages/eslint-plugin/src/rules/no-implied-eval.ts b/packages/eslint-plugin/src/rules/no-implied-eval.ts
index 0ae6698c533c..d88cd05ff6f4 100644
--- a/packages/eslint-plugin/src/rules/no-implied-eval.ts
+++ b/packages/eslint-plugin/src/rules/no-implied-eval.ts
@@ -135,7 +135,7 @@ export default util.createRule({
node: TSESTree.NewExpression | TSESTree.CallExpression,
): void {
const calleeName = getCalleeName(node.callee);
- if (calleeName === null) {
+ if (calleeName == null) {
return;
}
diff --git a/packages/eslint-plugin/src/rules/no-import-type-side-effects.ts b/packages/eslint-plugin/src/rules/no-import-type-side-effects.ts
new file mode 100644
index 000000000000..ce80a654afe5
--- /dev/null
+++ b/packages/eslint-plugin/src/rules/no-import-type-side-effects.ts
@@ -0,0 +1,76 @@
+import type { TSESLint, TSESTree } from '@typescript-eslint/utils';
+import { AST_NODE_TYPES } from '@typescript-eslint/utils';
+
+import * as util from '../util';
+
+type Options = [];
+type MessageIds = 'useTopLevelQualifier';
+
+export default util.createRule({
+ name: 'no-import-type-side-effects',
+ meta: {
+ type: 'problem',
+ docs: {
+ description:
+ 'Enforce the use of top-level import type qualifier when an import only has specifiers with inline type qualifiers',
+ recommended: false,
+ },
+ fixable: 'code',
+ messages: {
+ useTopLevelQualifier:
+ 'TypeScript will only remove the inline type specifiers which will leave behind a side effect import at runtime. Convert this to a top-level type qualifier to properly remove the entire import.',
+ },
+ schema: [],
+ },
+ defaultOptions: [],
+ create(context) {
+ const sourceCode = context.getSourceCode();
+ return {
+ 'ImportDeclaration[importKind!="type"]'(
+ node: TSESTree.ImportDeclaration,
+ ): void {
+ const specifiers: TSESTree.ImportSpecifier[] = [];
+ for (const specifier of node.specifiers) {
+ if (
+ specifier.type !== AST_NODE_TYPES.ImportSpecifier ||
+ specifier.importKind !== 'type'
+ ) {
+ return;
+ }
+ specifiers.push(specifier);
+ }
+
+ context.report({
+ node,
+ messageId: 'useTopLevelQualifier',
+ fix(fixer) {
+ const fixes: TSESLint.RuleFix[] = [];
+ for (const specifier of specifiers) {
+ const qualifier = util.nullThrows(
+ sourceCode.getFirstToken(specifier, util.isTypeKeyword),
+ util.NullThrowsReasons.MissingToken(
+ 'type keyword',
+ 'import specifier',
+ ),
+ );
+ fixes.push(
+ fixer.removeRange([
+ qualifier.range[0],
+ specifier.imported.range[0],
+ ]),
+ );
+ }
+
+ const importKeyword = util.nullThrows(
+ sourceCode.getFirstToken(node, util.isImportKeyword),
+ util.NullThrowsReasons.MissingToken('import keyword', 'import'),
+ );
+ fixes.push(fixer.insertTextAfter(importKeyword, ' type'));
+
+ return fixes;
+ },
+ });
+ },
+ };
+ },
+});
diff --git a/packages/eslint-plugin/src/rules/no-inferrable-types.ts b/packages/eslint-plugin/src/rules/no-inferrable-types.ts
index effbed48eaaf..1bc83c07c701 100644
--- a/packages/eslint-plugin/src/rules/no-inferrable-types.ts
+++ b/packages/eslint-plugin/src/rules/no-inferrable-types.ts
@@ -147,7 +147,7 @@ export default util.createRule({
}
case AST_NODE_TYPES.TSNullKeyword:
- return init.type === AST_NODE_TYPES.Literal && init.value === null;
+ return init.type === AST_NODE_TYPES.Literal && init.value == null;
case AST_NODE_TYPES.TSStringKeyword:
return (
diff --git a/packages/eslint-plugin/src/rules/no-loss-of-precision.ts b/packages/eslint-plugin/src/rules/no-loss-of-precision.ts
index 7b9492972e8e..2c0d84364e4b 100644
--- a/packages/eslint-plugin/src/rules/no-loss-of-precision.ts
+++ b/packages/eslint-plugin/src/rules/no-loss-of-precision.ts
@@ -25,7 +25,7 @@ export default util.createRule({
},
defaultOptions: [],
create(context) {
- /* istanbul ignore if */ if (baseRule === null) {
+ /* istanbul ignore if */ if (baseRule == null) {
throw new Error(
'@typescript-eslint/no-loss-of-precision requires at least ESLint v7.1.0',
);
diff --git a/packages/eslint-plugin/src/rules/no-misused-promises.ts b/packages/eslint-plugin/src/rules/no-misused-promises.ts
index b6914ae2c392..15bf0c501d15 100644
--- a/packages/eslint-plugin/src/rules/no-misused-promises.ts
+++ b/packages/eslint-plugin/src/rules/no-misused-promises.ts
@@ -250,7 +250,7 @@ export default util.createRule({
function checkVariableDeclaration(node: TSESTree.VariableDeclarator): void {
const tsNode = parserServices.esTreeNodeToTSNodeMap.get(node);
- if (tsNode.initializer === undefined || node.init === null) {
+ if (tsNode.initializer === undefined || node.init == null) {
return;
}
const varType = checker.getTypeAtLocation(tsNode.name);
@@ -344,7 +344,7 @@ export default util.createRule({
function checkReturnStatement(node: TSESTree.ReturnStatement): void {
const tsNode = parserServices.esTreeNodeToTSNodeMap.get(node);
- if (tsNode.expression === undefined || node.argument === null) {
+ if (tsNode.expression === undefined || node.argument == null) {
return;
}
const contextualType = checker.getContextualType(tsNode.expression);
@@ -368,7 +368,7 @@ export default util.createRule({
const tsNode = parserServices.esTreeNodeToTSNodeMap.get(node);
const value = tsNode.initializer;
if (
- node.value === null ||
+ node.value == null ||
value === undefined ||
!ts.isJsxExpression(value) ||
value.expression === undefined
diff --git a/packages/eslint-plugin/src/rules/no-non-null-asserted-nullish-coalescing.ts b/packages/eslint-plugin/src/rules/no-non-null-asserted-nullish-coalescing.ts
index 8706703c9bab..a79fa4062b1f 100644
--- a/packages/eslint-plugin/src/rules/no-non-null-asserted-nullish-coalescing.ts
+++ b/packages/eslint-plugin/src/rules/no-non-null-asserted-nullish-coalescing.ts
@@ -27,7 +27,7 @@ function isDefinitionWithAssignment(definition: Definition): boolean {
const variableDeclarator = definition.node;
return (
- variableDeclarator.definite === true || variableDeclarator.init !== null
+ variableDeclarator.definite === true || variableDeclarator.init != null
);
}
diff --git a/packages/eslint-plugin/src/rules/no-unnecessary-condition.ts b/packages/eslint-plugin/src/rules/no-unnecessary-condition.ts
index 5a6872f57484..42f12748af90 100644
--- a/packages/eslint-plugin/src/rules/no-unnecessary-condition.ts
+++ b/packages/eslint-plugin/src/rules/no-unnecessary-condition.ts
@@ -384,7 +384,7 @@ export default createRule({
| TSESTree.ForStatement
| TSESTree.WhileStatement,
): void {
- if (node.test === null) {
+ if (node.test == null) {
// e.g. `for(;;)`
return;
}
diff --git a/packages/eslint-plugin/src/rules/no-unnecessary-qualifier.ts b/packages/eslint-plugin/src/rules/no-unnecessary-qualifier.ts
index fbf3b41e9668..632ad6c5ba0f 100644
--- a/packages/eslint-plugin/src/rules/no-unnecessary-qualifier.ts
+++ b/packages/eslint-plugin/src/rules/no-unnecessary-qualifier.ts
@@ -53,7 +53,7 @@ export default util.createRule({
const alias = tryGetAliasedSymbol(symbol, checker);
- return alias !== null && symbolIsNamespaceInScope(alias);
+ return alias != null && symbolIsNamespaceInScope(alias);
}
function getSymbolInScope(
diff --git a/packages/eslint-plugin/src/rules/no-use-before-define.ts b/packages/eslint-plugin/src/rules/no-use-before-define.ts
index 5153ed47fcd8..b88cd82d39d7 100644
--- a/packages/eslint-plugin/src/rules/no-use-before-define.ts
+++ b/packages/eslint-plugin/src/rules/no-use-before-define.ts
@@ -21,7 +21,7 @@ function parseOptions(options: string | Config | null): Required {
if (typeof options === 'string') {
functions = options !== 'nofunc';
- } else if (typeof options === 'object' && options !== null) {
+ } else if (typeof options === 'object' && options != null) {
functions = options.functions !== false;
classes = options.classes !== false;
enums = options.enums !== false;
@@ -64,7 +64,7 @@ function isOuterEnum(
reference: TSESLint.Scope.Reference,
): boolean {
return (
- variable.defs[0].type == DefinitionType.TSEnumName &&
+ variable.defs[0].type === DefinitionType.TSEnumName &&
variable.scope.variableScope !== reference.from.variableScope
);
}
diff --git a/packages/eslint-plugin/src/rules/prefer-for-of.ts b/packages/eslint-plugin/src/rules/prefer-for-of.ts
index 9bb8802a7da7..ddde074a234d 100644
--- a/packages/eslint-plugin/src/rules/prefer-for-of.ts
+++ b/packages/eslint-plugin/src/rules/prefer-for-of.ts
@@ -24,8 +24,7 @@ export default util.createRule({
node: TSESTree.Node | null,
): node is TSESTree.VariableDeclaration {
return (
- node !== null &&
- node.type === AST_NODE_TYPES.VariableDeclaration &&
+ node?.type === AST_NODE_TYPES.VariableDeclaration &&
node.kind !== 'const' &&
node.declarations.length === 1
);
@@ -39,7 +38,7 @@ export default util.createRule({
}
function isZeroInitialized(node: TSESTree.VariableDeclarator): boolean {
- return node.init !== null && isLiteral(node.init, 0);
+ return node.init != null && isLiteral(node.init, 0);
}
function isMatchingIdentifier(
@@ -54,8 +53,7 @@ export default util.createRule({
name: string,
): TSESTree.Expression | null {
if (
- node !== null &&
- node.type === AST_NODE_TYPES.BinaryExpression &&
+ node?.type === AST_NODE_TYPES.BinaryExpression &&
node.operator === '<' &&
isMatchingIdentifier(node.left, name) &&
node.right.type === AST_NODE_TYPES.MemberExpression &&
diff --git a/packages/eslint-plugin/src/rules/prefer-function-type.ts b/packages/eslint-plugin/src/rules/prefer-function-type.ts
index 95b3ee5d33b7..db5dde69d001 100644
--- a/packages/eslint-plugin/src/rules/prefer-function-type.ts
+++ b/packages/eslint-plugin/src/rules/prefer-function-type.ts
@@ -82,8 +82,7 @@ export default util.createRule({
typeof member.returnType !== 'undefined'
) {
if (
- tsThisTypes !== null &&
- tsThisTypes.length > 0 &&
+ tsThisTypes?.length &&
node.type === AST_NODE_TYPES.TSInterfaceDeclaration
) {
// the message can be confusing if we don't point directly to the `this` node instead of the whole member
@@ -205,7 +204,7 @@ export default util.createRule({
// inside an interface keep track of all ThisType references.
// unless it's inside a nested type literal in which case it's invalid code anyway
// we don't want to incorrectly say "it refers to name" while typescript says it's completely invalid.
- if (literalNesting === 0 && tsThisTypes !== null) {
+ if (literalNesting === 0 && tsThisTypes != null) {
tsThisTypes.push(node);
}
},
diff --git a/packages/eslint-plugin/src/rules/prefer-includes.ts b/packages/eslint-plugin/src/rules/prefer-includes.ts
index 9c50ce118f80..720d5fbe8092 100644
--- a/packages/eslint-plugin/src/rules/prefer-includes.ts
+++ b/packages/eslint-plugin/src/rules/prefer-includes.ts
@@ -38,7 +38,7 @@ export default createRule({
function isNumber(node: TSESTree.Node, value: number): boolean {
const evaluated = getStaticValue(node, globalScope);
- return evaluated !== null && evaluated.value === value;
+ return evaluated != null && evaluated.value === value;
}
function isPositiveCheck(node: TSESTree.BinaryExpression): boolean {
diff --git a/packages/eslint-plugin/src/rules/prefer-regexp-exec.ts b/packages/eslint-plugin/src/rules/prefer-regexp-exec.ts
index 13452d48f86f..60bf310947fa 100644
--- a/packages/eslint-plugin/src/rules/prefer-regexp-exec.ts
+++ b/packages/eslint-plugin/src/rules/prefer-regexp-exec.ts
@@ -123,7 +123,7 @@ export default createRule({
if (
argumentNode.type === AST_NODE_TYPES.Literal &&
- typeof argumentNode.value == 'string'
+ typeof argumentNode.value === 'string'
) {
const regExp = RegExp(argumentNode.value);
return context.report({
diff --git a/packages/eslint-plugin/src/rules/prefer-string-starts-ends-with.ts b/packages/eslint-plugin/src/rules/prefer-string-starts-ends-with.ts
index 31a570652dcb..104637062bb6 100644
--- a/packages/eslint-plugin/src/rules/prefer-string-starts-ends-with.ts
+++ b/packages/eslint-plugin/src/rules/prefer-string-starts-ends-with.ts
@@ -60,7 +60,7 @@ export default createRule({
*/
function isNull(node: TSESTree.Node): node is TSESTree.Literal {
const evaluated = getStaticValue(node, globalScope);
- return evaluated != null && evaluated.value === null;
+ return evaluated != null && evaluated.value == null;
}
/**
diff --git a/packages/eslint-plugin/src/rules/space-before-function-paren.ts b/packages/eslint-plugin/src/rules/space-before-function-paren.ts
index 4a3f9042e784..5ff33aeb4fed 100644
--- a/packages/eslint-plugin/src/rules/space-before-function-paren.ts
+++ b/packages/eslint-plugin/src/rules/space-before-function-paren.ts
@@ -140,7 +140,8 @@ export default util.createRule({
return;
}
- let leftToken: TSESTree.Token, rightToken: TSESTree.Token;
+ let leftToken: TSESTree.Token;
+ let rightToken: TSESTree.Token;
if (node.typeParameters) {
leftToken = sourceCode.getLastToken(node.typeParameters)!;
rightToken = sourceCode.getTokenAfter(leftToken)!;
diff --git a/packages/eslint-plugin/src/rules/strict-boolean-expressions.ts b/packages/eslint-plugin/src/rules/strict-boolean-expressions.ts
index 1e327a8a4b83..bf58727df296 100644
--- a/packages/eslint-plugin/src/rules/strict-boolean-expressions.ts
+++ b/packages/eslint-plugin/src/rules/strict-boolean-expressions.ts
@@ -13,6 +13,7 @@ export type Options = [
allowNullableBoolean?: boolean;
allowNullableString?: boolean;
allowNullableNumber?: boolean;
+ allowNullableEnum?: boolean;
allowAny?: boolean;
allowRuleToRunWithoutStrictNullChecksIKnowWhatIAmDoing?: boolean;
},
@@ -29,6 +30,7 @@ export type MessageId =
| 'conditionErrorNullableNumber'
| 'conditionErrorObject'
| 'conditionErrorNullableObject'
+ | 'conditionErrorNullableEnum'
| 'noStrictNullCheck'
| 'conditionFixDefaultFalse'
| 'conditionFixDefaultEmptyString'
@@ -63,6 +65,7 @@ export default util.createRule({
allowNullableBoolean: { type: 'boolean' },
allowNullableString: { type: 'boolean' },
allowNullableNumber: { type: 'boolean' },
+ allowNullableEnum: { type: 'boolean' },
allowAny: { type: 'boolean' },
allowRuleToRunWithoutStrictNullChecksIKnowWhatIAmDoing: {
type: 'boolean',
@@ -102,6 +105,9 @@ export default util.createRule({
conditionErrorNullableObject:
'Unexpected nullable object value in conditional. ' +
'An explicit null check is required.',
+ conditionErrorNullableEnum:
+ 'Unexpected nullable enum value in conditional. ' +
+ 'Please handle the nullish/zero/NaN cases explicitly.',
noStrictNullCheck:
'This rule requires the `strictNullChecks` compiler option to be turned on to function correctly.',
@@ -137,6 +143,7 @@ export default util.createRule({
allowNullableBoolean: false,
allowNullableString: false,
allowNullableNumber: false,
+ allowNullableEnum: true,
allowAny: false,
allowRuleToRunWithoutStrictNullChecksIKnowWhatIAmDoing: false,
},
@@ -718,6 +725,35 @@ export default util.createRule({
return;
}
+ // nullable enum
+ if (is('nullish', 'number', 'enum') || is('nullish', 'string', 'enum')) {
+ if (!options.allowNullableEnum) {
+ if (isLogicalNegationExpression(node.parent!)) {
+ context.report({
+ node,
+ messageId: 'conditionErrorNullableEnum',
+ fix: util.getWrappingFixer({
+ sourceCode,
+ node: node.parent,
+ innerNode: node,
+ wrap: code => `${code} == null`,
+ }),
+ });
+ } else {
+ context.report({
+ node,
+ messageId: 'conditionErrorNullableEnum',
+ fix: util.getWrappingFixer({
+ sourceCode,
+ node,
+ wrap: code => `${code} != null`,
+ }),
+ });
+ }
+ }
+ return;
+ }
+
// any
if (is('any')) {
if (!options.allowAny) {
@@ -753,6 +789,7 @@ export default util.createRule({
| 'number'
| 'truthy number'
| 'object'
+ | 'enum'
| 'any'
| 'never';
@@ -814,6 +851,12 @@ export default util.createRule({
}
}
+ if (
+ types.some(type => tsutils.isTypeFlagSet(type, ts.TypeFlags.EnumLike))
+ ) {
+ variantTypes.add('enum');
+ }
+
if (
types.some(
type =>
diff --git a/packages/eslint-plugin/src/rules/switch-exhaustiveness-check.ts b/packages/eslint-plugin/src/rules/switch-exhaustiveness-check.ts
index cff8960dac84..43d4913b4ca3 100644
--- a/packages/eslint-plugin/src/rules/switch-exhaustiveness-check.ts
+++ b/packages/eslint-plugin/src/rules/switch-exhaustiveness-check.ts
@@ -121,7 +121,7 @@ export default createRule({
const unionTypes = unionTypeParts(discriminantType);
const caseTypes: Set = new Set();
for (const switchCase of node.cases) {
- if (switchCase.test === null) {
+ if (switchCase.test == null) {
// Switch has 'default' branch - do nothing.
return;
}
diff --git a/packages/eslint-plugin/src/rules/triple-slash-reference.ts b/packages/eslint-plugin/src/rules/triple-slash-reference.ts
index 5780d55cb5c2..4425e666338d 100644
--- a/packages/eslint-plugin/src/rules/triple-slash-reference.ts
+++ b/packages/eslint-plugin/src/rules/triple-slash-reference.ts
@@ -87,7 +87,7 @@ export default util.createRule({
}
},
Program(node): void {
- if (lib === 'always' && path === 'always' && types == 'always') {
+ if (lib === 'always' && path === 'always' && types === 'always') {
return;
}
programNode = node;
diff --git a/packages/eslint-plugin/src/util/isNullLiteral.ts b/packages/eslint-plugin/src/util/isNullLiteral.ts
index f8695f260924..85bf45882123 100644
--- a/packages/eslint-plugin/src/util/isNullLiteral.ts
+++ b/packages/eslint-plugin/src/util/isNullLiteral.ts
@@ -2,5 +2,5 @@ import type { TSESTree } from '@typescript-eslint/utils';
import { AST_NODE_TYPES } from '@typescript-eslint/utils';
export function isNullLiteral(i: TSESTree.Node): boolean {
- return i.type === AST_NODE_TYPES.Literal && i.value === null;
+ return i.type === AST_NODE_TYPES.Literal && i.value == null;
}
diff --git a/packages/eslint-plugin/src/util/misc.ts b/packages/eslint-plugin/src/util/misc.ts
index fa9c5ccf5287..8362736bd628 100644
--- a/packages/eslint-plugin/src/util/misc.ts
+++ b/packages/eslint-plugin/src/util/misc.ts
@@ -210,6 +210,7 @@ function typeNodeRequiresParentheses(
return (
node.type === AST_NODE_TYPES.TSFunctionType ||
node.type === AST_NODE_TYPES.TSConstructorType ||
+ node.type === AST_NODE_TYPES.TSConditionalType ||
(node.type === AST_NODE_TYPES.TSUnionType && text.startsWith('|')) ||
(node.type === AST_NODE_TYPES.TSIntersectionType && text.startsWith('&'))
);
diff --git a/packages/eslint-plugin/tests/rules/indent/indent.test.ts b/packages/eslint-plugin/tests/rules/indent/indent.test.ts
index 65fe9240377b..f9191d3ef105 100644
--- a/packages/eslint-plugin/tests/rules/indent/indent.test.ts
+++ b/packages/eslint-plugin/tests/rules/indent/indent.test.ts
@@ -638,7 +638,7 @@ type Foo = string | {
})
.filter(
(error): error is TSESLint.TestCaseError =>
- error !== null,
+ error != null,
),
};
if (invalid.errors.length > 0) {
diff --git a/packages/eslint-plugin/tests/rules/no-floating-promises.test.ts b/packages/eslint-plugin/tests/rules/no-floating-promises.test.ts
index a80acab989ae..070cef91e9a6 100644
--- a/packages/eslint-plugin/tests/rules/no-floating-promises.test.ts
+++ b/packages/eslint-plugin/tests/rules/no-floating-promises.test.ts
@@ -405,6 +405,57 @@ void doSomething();
`,
options: [{ ignoreIIFE: true }],
},
+ {
+ code: `
+async function foo() {
+ const myPromise = async () => void 0;
+ const condition = true;
+ void (condition && myPromise());
+}
+ `,
+ },
+ {
+ code: `
+async function foo() {
+ const myPromise = async () => void 0;
+ const condition = true;
+ await (condition && myPromise());
+}
+ `,
+ options: [{ ignoreVoid: false }],
+ },
+ {
+ code: `
+async function foo() {
+ const myPromise = async () => void 0;
+ const condition = true;
+ condition && void myPromise();
+}
+ `,
+ },
+ {
+ code: `
+async function foo() {
+ const myPromise = async () => void 0;
+ const condition = true;
+ condition && (await myPromise());
+}
+ `,
+ options: [{ ignoreVoid: false }],
+ },
+ {
+ code: `
+async function foo() {
+ const myPromise = async () => void 0;
+ let condition = false;
+ condition && myPromise();
+ condition = true;
+ condition || myPromise();
+ condition ?? myPromise();
+}
+ `,
+ options: [{ ignoreVoid: false }],
+ },
],
invalid: [
@@ -1117,5 +1168,264 @@ async function test() {
},
],
},
+ {
+ code: `
+async function foo() {
+ const myPromise = async () => void 0;
+ const condition = true;
+
+ void condition || myPromise();
+}
+ `,
+ errors: [
+ {
+ line: 6,
+ messageId: 'floatingVoid',
+ suggestions: [
+ {
+ messageId: 'floatingFixVoid',
+ output: `
+async function foo() {
+ const myPromise = async () => void 0;
+ const condition = true;
+
+ void (void condition || myPromise());
+}
+ `,
+ },
+ ],
+ },
+ ],
+ },
+ {
+ code: `
+async function foo() {
+ const myPromise = async () => void 0;
+ const condition = true;
+
+ (await condition) && myPromise();
+}
+ `,
+ options: [{ ignoreVoid: false }],
+ errors: [
+ {
+ line: 6,
+ messageId: 'floating',
+ suggestions: [
+ {
+ messageId: 'floatingFixAwait',
+ output: `
+async function foo() {
+ const myPromise = async () => void 0;
+ const condition = true;
+
+ await ((await condition) && myPromise());
+}
+ `,
+ },
+ ],
+ },
+ ],
+ },
+ {
+ code: `
+async function foo() {
+ const myPromise = async () => void 0;
+ const condition = true;
+
+ condition && myPromise();
+}
+ `,
+ errors: [
+ {
+ line: 6,
+ messageId: 'floatingVoid',
+ suggestions: [
+ {
+ messageId: 'floatingFixVoid',
+ output: `
+async function foo() {
+ const myPromise = async () => void 0;
+ const condition = true;
+
+ void (condition && myPromise());
+}
+ `,
+ },
+ ],
+ },
+ ],
+ },
+ {
+ code: `
+async function foo() {
+ const myPromise = async () => void 0;
+ const condition = false;
+
+ condition || myPromise();
+}
+ `,
+ errors: [
+ {
+ line: 6,
+ messageId: 'floatingVoid',
+ suggestions: [
+ {
+ messageId: 'floatingFixVoid',
+ output: `
+async function foo() {
+ const myPromise = async () => void 0;
+ const condition = false;
+
+ void (condition || myPromise());
+}
+ `,
+ },
+ ],
+ },
+ ],
+ },
+ {
+ code: `
+async function foo() {
+ const myPromise = async () => void 0;
+ const condition = null;
+
+ condition ?? myPromise();
+}
+ `,
+ errors: [
+ {
+ line: 6,
+ messageId: 'floatingVoid',
+ suggestions: [
+ {
+ messageId: 'floatingFixVoid',
+ output: `
+async function foo() {
+ const myPromise = async () => void 0;
+ const condition = null;
+
+ void (condition ?? myPromise());
+}
+ `,
+ },
+ ],
+ },
+ ],
+ },
+ {
+ code: `
+async function foo() {
+ const myPromise = Promise.resolve(true);
+ let condition = true;
+ condition && myPromise;
+}
+ `,
+ options: [{ ignoreVoid: false }],
+ errors: [
+ {
+ line: 5,
+ messageId: 'floating',
+ suggestions: [
+ {
+ messageId: 'floatingFixAwait',
+ output: `
+async function foo() {
+ const myPromise = Promise.resolve(true);
+ let condition = true;
+ await (condition && myPromise);
+}
+ `,
+ },
+ ],
+ },
+ ],
+ },
+ {
+ code: `
+async function foo() {
+ const myPromise = Promise.resolve(true);
+ let condition = false;
+ condition || myPromise;
+}
+ `,
+ options: [{ ignoreVoid: false }],
+ errors: [
+ {
+ line: 5,
+ messageId: 'floating',
+ suggestions: [
+ {
+ messageId: 'floatingFixAwait',
+ output: `
+async function foo() {
+ const myPromise = Promise.resolve(true);
+ let condition = false;
+ await (condition || myPromise);
+}
+ `,
+ },
+ ],
+ },
+ ],
+ },
+ {
+ code: `
+async function foo() {
+ const myPromise = Promise.resolve(true);
+ let condition = null;
+ condition ?? myPromise;
+}
+ `,
+ options: [{ ignoreVoid: false }],
+ errors: [
+ {
+ line: 5,
+ messageId: 'floating',
+ suggestions: [
+ {
+ messageId: 'floatingFixAwait',
+ output: `
+async function foo() {
+ const myPromise = Promise.resolve(true);
+ let condition = null;
+ await (condition ?? myPromise);
+}
+ `,
+ },
+ ],
+ },
+ ],
+ },
+ {
+ code: `
+async function foo() {
+ const myPromise = async () => void 0;
+ const condition = false;
+
+ condition || condition || myPromise();
+}
+ `,
+ errors: [
+ {
+ line: 6,
+ messageId: 'floatingVoid',
+ suggestions: [
+ {
+ messageId: 'floatingFixVoid',
+ output: `
+async function foo() {
+ const myPromise = async () => void 0;
+ const condition = false;
+
+ void (condition || condition || myPromise());
+}
+ `,
+ },
+ ],
+ },
+ ],
+ },
],
});
diff --git a/packages/eslint-plugin/tests/rules/no-import-type-side-effects.test.ts b/packages/eslint-plugin/tests/rules/no-import-type-side-effects.test.ts
new file mode 100644
index 000000000000..9dade06a9432
--- /dev/null
+++ b/packages/eslint-plugin/tests/rules/no-import-type-side-effects.test.ts
@@ -0,0 +1,44 @@
+import rule from '../../src/rules/no-import-type-side-effects';
+import { RuleTester } from '../RuleTester';
+
+const ruleTester = new RuleTester({
+ parser: '@typescript-eslint/parser',
+});
+
+ruleTester.run('no-import-type-side-effects', rule, {
+ valid: [
+ "import T from 'mod';",
+ "import * as T from 'mod';",
+ "import { T } from 'mod';",
+ "import type { T } from 'mod';",
+ "import type { T, U } from 'mod';",
+ "import { type T, U } from 'mod';",
+ "import { T, type U } from 'mod';",
+ "import type T from 'mod';",
+ "import type T, { U } from 'mod';",
+ "import T, { type U } from 'mod';",
+ "import type * as T from 'mod';",
+ ],
+ invalid: [
+ {
+ code: "import { type A } from 'mod';",
+ output: "import type { A } from 'mod';",
+ errors: [{ messageId: 'useTopLevelQualifier' }],
+ },
+ {
+ code: "import { type A as AA } from 'mod';",
+ output: "import type { A as AA } from 'mod';",
+ errors: [{ messageId: 'useTopLevelQualifier' }],
+ },
+ {
+ code: "import { type A, type B } from 'mod';",
+ output: "import type { A, B } from 'mod';",
+ errors: [{ messageId: 'useTopLevelQualifier' }],
+ },
+ {
+ code: "import { type A as AA, type B as BB } from 'mod';",
+ output: "import type { A as AA, B as BB } from 'mod';",
+ errors: [{ messageId: 'useTopLevelQualifier' }],
+ },
+ ],
+});
diff --git a/packages/eslint-plugin/tests/rules/prefer-string-starts-ends-with.test.ts b/packages/eslint-plugin/tests/rules/prefer-string-starts-ends-with.test.ts
index 82a25e2f472e..c9fe331c99d7 100644
--- a/packages/eslint-plugin/tests/rules/prefer-string-starts-ends-with.test.ts
+++ b/packages/eslint-plugin/tests/rules/prefer-string-starts-ends-with.test.ts
@@ -1083,7 +1083,7 @@ function addOptional<
function makeOptional(code: string): string;
function makeOptional(code: string | null | undefined): string | null;
function makeOptional(code: string | null | undefined): string | null {
- if (code === null || code === undefined) {
+ if (code == null) {
return null;
}
return (
diff --git a/packages/eslint-plugin/tests/rules/sort-type-constituents.test.ts b/packages/eslint-plugin/tests/rules/sort-type-constituents.test.ts
index 1aa6f8a6c9a3..42f9ab8153a3 100644
--- a/packages/eslint-plugin/tests/rules/sort-type-constituents.test.ts
+++ b/packages/eslint-plugin/tests/rules/sort-type-constituents.test.ts
@@ -359,6 +359,7 @@ type T = 1 | string | {} | A;
},
],
},
+ "type A = string | (T extends number ? 'hi' : 'there');",
],
invalid: [
...invalid('|'),
@@ -376,5 +377,18 @@ type T = 1 | string | {} | A;
},
],
},
+ {
+ output: "type A = string | (T extends number ? 'hi' : 'there');",
+ code: "type A = (T extends number ? 'hi' : 'there') | string;",
+ errors: [
+ {
+ messageId: 'notSortedNamed',
+ data: {
+ type: 'Union',
+ name: 'A',
+ },
+ },
+ ],
+ },
],
});
diff --git a/packages/eslint-plugin/tests/rules/strict-boolean-expressions.test.ts b/packages/eslint-plugin/tests/rules/strict-boolean-expressions.test.ts
index 9261acda41b7..f3b6deef1bf5 100644
--- a/packages/eslint-plugin/tests/rules/strict-boolean-expressions.test.ts
+++ b/packages/eslint-plugin/tests/rules/strict-boolean-expressions.test.ts
@@ -133,6 +133,39 @@ ruleTester.run('strict-boolean-expressions', rule, {
`,
}),
+ // nullable enum in boolean context
+ {
+ code: `
+ enum ExampleEnum {
+ This = 0,
+ That = 1,
+ }
+ const rand = Math.random();
+ let theEnum: ExampleEnum | null = null;
+ if (rand < 0.3) {
+ theEnum = ExampleEnum.This;
+ }
+ if (theEnum) {
+ }
+ `,
+ options: [{ allowNullableEnum: true }],
+ },
+ {
+ code: `
+ enum ExampleEnum {
+ This = 0,
+ That = 1,
+ }
+ const rand = Math.random();
+ let theEnum: ExampleEnum | null = null;
+ if (rand < 0.3) {
+ theEnum = ExampleEnum.This;
+ }
+ if (!theEnum) {
+ }
+ `,
+ options: [{ allowNullableEnum: true }],
+ },
{
code: `
declare const x: string[] | null;
@@ -965,6 +998,157 @@ if (y) {
],
}),
+ // nullable enum in boolean context
+ {
+ options: [{ allowNullableEnum: false }],
+ code: `
+ enum ExampleEnum {
+ This = 0,
+ That = 1,
+ }
+ const theEnum = Math.random() < 0.3 ? ExampleEnum.This : null;
+ if (theEnum) {
+ }
+ `,
+ errors: [
+ {
+ line: 7,
+ column: 13,
+ messageId: 'conditionErrorNullableEnum',
+ endLine: 7,
+ endColumn: 20,
+ },
+ ],
+ output: `
+ enum ExampleEnum {
+ This = 0,
+ That = 1,
+ }
+ const theEnum = Math.random() < 0.3 ? ExampleEnum.This : null;
+ if (theEnum != null) {
+ }
+ `,
+ },
+ {
+ options: [{ allowNullableEnum: false }],
+ code: `
+ enum ExampleEnum {
+ This = 0,
+ That = 1,
+ }
+ const theEnum = Math.random() < 0.3 ? ExampleEnum.This : null;
+ if (!theEnum) {
+ }
+ `,
+ errors: [
+ {
+ line: 7,
+ column: 14,
+ messageId: 'conditionErrorNullableEnum',
+ endLine: 7,
+ endColumn: 21,
+ },
+ ],
+ output: `
+ enum ExampleEnum {
+ This = 0,
+ That = 1,
+ }
+ const theEnum = Math.random() < 0.3 ? ExampleEnum.This : null;
+ if (theEnum == null) {
+ }
+ `,
+ },
+ {
+ options: [{ allowNullableEnum: false }],
+ code: `
+ enum ExampleEnum {
+ This,
+ That,
+ }
+ const theEnum = Math.random() < 0.3 ? ExampleEnum.This : null;
+ if (!theEnum) {
+ }
+ `,
+ errors: [
+ {
+ line: 7,
+ column: 14,
+ messageId: 'conditionErrorNullableEnum',
+ endLine: 7,
+ endColumn: 21,
+ },
+ ],
+ output: `
+ enum ExampleEnum {
+ This,
+ That,
+ }
+ const theEnum = Math.random() < 0.3 ? ExampleEnum.This : null;
+ if (theEnum == null) {
+ }
+ `,
+ },
+ {
+ options: [{ allowNullableEnum: false }],
+ code: `
+ enum ExampleEnum {
+ This = '',
+ That = 'a',
+ }
+ const theEnum = Math.random() < 0.3 ? ExampleEnum.This : null;
+ if (!theEnum) {
+ }
+ `,
+ errors: [
+ {
+ line: 7,
+ column: 14,
+ messageId: 'conditionErrorNullableEnum',
+ endLine: 7,
+ endColumn: 21,
+ },
+ ],
+ output: `
+ enum ExampleEnum {
+ This = '',
+ That = 'a',
+ }
+ const theEnum = Math.random() < 0.3 ? ExampleEnum.This : null;
+ if (theEnum == null) {
+ }
+ `,
+ },
+ {
+ options: [{ allowNullableEnum: false }],
+ code: `
+ enum ExampleEnum {
+ This = '',
+ That = 0,
+ }
+ const theEnum = Math.random() < 0.3 ? ExampleEnum.This : null;
+ if (!theEnum) {
+ }
+ `,
+ errors: [
+ {
+ line: 7,
+ column: 14,
+ messageId: 'conditionErrorNullableEnum',
+ endLine: 7,
+ endColumn: 21,
+ },
+ ],
+ output: `
+ enum ExampleEnum {
+ This = '',
+ That = 0,
+ }
+ const theEnum = Math.random() < 0.3 ? ExampleEnum.This : null;
+ if (theEnum == null) {
+ }
+ `,
+ },
// any in boolean context
...batchedSingleLineTests({
code: noFormat`
diff --git a/packages/experimental-utils/CHANGELOG.md b/packages/experimental-utils/CHANGELOG.md
index a11333dc15c7..ddc32b003c5b 100644
--- a/packages/experimental-utils/CHANGELOG.md
+++ b/packages/experimental-utils/CHANGELOG.md
@@ -3,6 +3,14 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
+# [5.51.0](https://github.com/typescript-eslint/typescript-eslint/compare/v5.50.0...v5.51.0) (2023-02-06)
+
+**Note:** Version bump only for package @typescript-eslint/experimental-utils
+
+
+
+
+
# [5.50.0](https://github.com/typescript-eslint/typescript-eslint/compare/v5.49.0...v5.50.0) (2023-01-31)
**Note:** Version bump only for package @typescript-eslint/experimental-utils
diff --git a/packages/experimental-utils/README.md b/packages/experimental-utils/README.md
index d1468f928b15..a285229f6389 100644
--- a/packages/experimental-utils/README.md
+++ b/packages/experimental-utils/README.md
@@ -19,4 +19,4 @@ You should switch to importing from that non-experimental package instead.
## Contributing
-[See the contributing guide here](../../CONTRIBUTING.md)
+[See the contributing guide here](https://typescript-eslint.io).
diff --git a/packages/experimental-utils/package.json b/packages/experimental-utils/package.json
index e3d44a14dfec..361143560976 100644
--- a/packages/experimental-utils/package.json
+++ b/packages/experimental-utils/package.json
@@ -1,6 +1,6 @@
{
"name": "@typescript-eslint/experimental-utils",
- "version": "5.50.0",
+ "version": "5.51.0",
"description": "(Experimental) Utilities for working with TypeScript + ESLint together",
"keywords": [
"eslint",
@@ -38,7 +38,7 @@
"typecheck": "tsc -p tsconfig.json --noEmit"
},
"dependencies": {
- "@typescript-eslint/utils": "5.50.0"
+ "@typescript-eslint/utils": "5.51.0"
},
"peerDependencies": {
"eslint": "^6.0.0 || ^7.0.0 || ^8.0.0"
diff --git a/packages/parser/CHANGELOG.md b/packages/parser/CHANGELOG.md
index 8dd79a4e7d19..b6d0b1016f3b 100644
--- a/packages/parser/CHANGELOG.md
+++ b/packages/parser/CHANGELOG.md
@@ -3,6 +3,14 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
+# [5.51.0](https://github.com/typescript-eslint/typescript-eslint/compare/v5.50.0...v5.51.0) (2023-02-06)
+
+**Note:** Version bump only for package @typescript-eslint/parser
+
+
+
+
+
# [5.50.0](https://github.com/typescript-eslint/typescript-eslint/compare/v5.49.0...v5.50.0) (2023-01-31)
**Note:** Version bump only for package @typescript-eslint/parser
diff --git a/packages/parser/package.json b/packages/parser/package.json
index 72b9df7a210c..e2bddc8fca28 100644
--- a/packages/parser/package.json
+++ b/packages/parser/package.json
@@ -1,6 +1,6 @@
{
"name": "@typescript-eslint/parser",
- "version": "5.50.0",
+ "version": "5.51.0",
"description": "An ESLint custom parser which leverages TypeScript ESTree",
"main": "dist/index.js",
"types": "dist/index.d.ts",
@@ -45,9 +45,9 @@
"eslint": "^6.0.0 || ^7.0.0 || ^8.0.0"
},
"dependencies": {
- "@typescript-eslint/scope-manager": "5.50.0",
- "@typescript-eslint/types": "5.50.0",
- "@typescript-eslint/typescript-estree": "5.50.0",
+ "@typescript-eslint/scope-manager": "5.51.0",
+ "@typescript-eslint/types": "5.51.0",
+ "@typescript-eslint/typescript-estree": "5.51.0",
"debug": "^4.3.4"
},
"devDependencies": {
diff --git a/packages/scope-manager/CHANGELOG.md b/packages/scope-manager/CHANGELOG.md
index 4243c56c73e5..c19e0b075295 100644
--- a/packages/scope-manager/CHANGELOG.md
+++ b/packages/scope-manager/CHANGELOG.md
@@ -3,6 +3,14 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
+# [5.51.0](https://github.com/typescript-eslint/typescript-eslint/compare/v5.50.0...v5.51.0) (2023-02-06)
+
+**Note:** Version bump only for package @typescript-eslint/scope-manager
+
+
+
+
+
# [5.50.0](https://github.com/typescript-eslint/typescript-eslint/compare/v5.49.0...v5.50.0) (2023-01-31)
**Note:** Version bump only for package @typescript-eslint/scope-manager
diff --git a/packages/scope-manager/package.json b/packages/scope-manager/package.json
index afa72f7662a5..c600e4ff2fd7 100644
--- a/packages/scope-manager/package.json
+++ b/packages/scope-manager/package.json
@@ -1,6 +1,6 @@
{
"name": "@typescript-eslint/scope-manager",
- "version": "5.50.0",
+ "version": "5.51.0",
"description": "TypeScript scope analyser for ESLint",
"keywords": [
"eslint",
@@ -38,12 +38,12 @@
"typecheck": "nx typecheck"
},
"dependencies": {
- "@typescript-eslint/types": "5.50.0",
- "@typescript-eslint/visitor-keys": "5.50.0"
+ "@typescript-eslint/types": "5.51.0",
+ "@typescript-eslint/visitor-keys": "5.51.0"
},
"devDependencies": {
"@types/glob": "*",
- "@typescript-eslint/typescript-estree": "5.50.0",
+ "@typescript-eslint/typescript-estree": "5.51.0",
"glob": "*",
"jest-specific-snapshot": "*",
"make-dir": "*",
diff --git a/packages/scope-manager/src/ScopeManager.ts b/packages/scope-manager/src/ScopeManager.ts
index 5368cca1dc3b..7f4b2a5f7052 100644
--- a/packages/scope-manager/src/ScopeManager.ts
+++ b/packages/scope-manager/src/ScopeManager.ts
@@ -140,7 +140,7 @@ class ScopeManager {
protected nestScope(scope: T): T;
protected nestScope(scope: Scope): Scope {
if (scope instanceof GlobalScope) {
- assert(this.currentScope === null);
+ assert(this.currentScope == null);
this.globalScope = scope;
}
this.currentScope = scope;
diff --git a/packages/scope-manager/src/referencer/ClassVisitor.ts b/packages/scope-manager/src/referencer/ClassVisitor.ts
index 5f84e37404f3..662b70813c83 100644
--- a/packages/scope-manager/src/referencer/ClassVisitor.ts
+++ b/packages/scope-manager/src/referencer/ClassVisitor.ts
@@ -163,7 +163,7 @@ class ClassVisitor extends Visitor {
* }
*/
if (
- keyName !== null &&
+ keyName != null &&
this.#classNode.body.body.find(
(node): node is TSESTree.MethodDefinition =>
node !== methodNode &&
diff --git a/packages/scope-manager/src/referencer/PatternVisitor.ts b/packages/scope-manager/src/referencer/PatternVisitor.ts
index 308c4c29208a..53de28469e85 100644
--- a/packages/scope-manager/src/referencer/PatternVisitor.ts
+++ b/packages/scope-manager/src/referencer/PatternVisitor.ts
@@ -97,10 +97,7 @@ class PatternVisitor extends VisitorBase {
this.#callback(pattern, {
topLevel: pattern === this.#rootPattern,
- rest:
- lastRestElement !== null &&
- lastRestElement !== undefined &&
- lastRestElement.argument === pattern,
+ rest: lastRestElement != null && lastRestElement.argument === pattern,
assignments: this.#assignments,
});
}
diff --git a/packages/scope-manager/src/referencer/Referencer.ts b/packages/scope-manager/src/referencer/Referencer.ts
index e7b41127ba46..7a14de51df15 100644
--- a/packages/scope-manager/src/referencer/Referencer.ts
+++ b/packages/scope-manager/src/referencer/Referencer.ts
@@ -124,7 +124,7 @@ class Referencer extends Visitor {
}
private referenceJsxPragma(): void {
- if (this.#jsxPragma === null || this.#hasReferencedJsxFactory) {
+ if (this.#jsxPragma == null || this.#hasReferencedJsxFactory) {
return;
}
this.#hasReferencedJsxFactory = this.referenceInSomeUpperScope(
@@ -134,7 +134,7 @@ class Referencer extends Visitor {
private referenceJsxFragment(): void {
if (
- this.#jsxFragmentName === null ||
+ this.#jsxFragmentName == null ||
this.#hasReferencedJsxFragmentFactory
) {
return;
diff --git a/packages/shared-fixtures/CHANGELOG.md b/packages/shared-fixtures/CHANGELOG.md
index 3548d5254c6d..62cf64f412a2 100644
--- a/packages/shared-fixtures/CHANGELOG.md
+++ b/packages/shared-fixtures/CHANGELOG.md
@@ -3,6 +3,14 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
+# [5.51.0](https://github.com/typescript-eslint/typescript-eslint/compare/v5.50.0...v5.51.0) (2023-02-06)
+
+**Note:** Version bump only for package @typescript-eslint/shared-fixtures
+
+
+
+
+
# [5.50.0](https://github.com/typescript-eslint/typescript-eslint/compare/v5.49.0...v5.50.0) (2023-01-31)
**Note:** Version bump only for package @typescript-eslint/shared-fixtures
diff --git a/packages/shared-fixtures/package.json b/packages/shared-fixtures/package.json
index 0e3d35976d45..c6bd1ccd6645 100644
--- a/packages/shared-fixtures/package.json
+++ b/packages/shared-fixtures/package.json
@@ -1,6 +1,6 @@
{
"description": "Code fixtures used to test the typescript-estree parser.",
"name": "@typescript-eslint/shared-fixtures",
- "version": "5.50.0",
+ "version": "5.51.0",
"private": true
}
diff --git a/packages/type-utils/CHANGELOG.md b/packages/type-utils/CHANGELOG.md
index 26f418c1436c..1c6a2a15d959 100644
--- a/packages/type-utils/CHANGELOG.md
+++ b/packages/type-utils/CHANGELOG.md
@@ -3,6 +3,14 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
+# [5.51.0](https://github.com/typescript-eslint/typescript-eslint/compare/v5.50.0...v5.51.0) (2023-02-06)
+
+**Note:** Version bump only for package @typescript-eslint/type-utils
+
+
+
+
+
# [5.50.0](https://github.com/typescript-eslint/typescript-eslint/compare/v5.49.0...v5.50.0) (2023-01-31)
**Note:** Version bump only for package @typescript-eslint/type-utils
diff --git a/packages/type-utils/package.json b/packages/type-utils/package.json
index 2b44585a6821..f4354a5342b6 100644
--- a/packages/type-utils/package.json
+++ b/packages/type-utils/package.json
@@ -1,6 +1,6 @@
{
"name": "@typescript-eslint/type-utils",
- "version": "5.50.0",
+ "version": "5.51.0",
"description": "Type utilities for working with TypeScript + ESLint together",
"keywords": [
"eslint",
@@ -39,13 +39,13 @@
"typecheck": "tsc -p tsconfig.json --noEmit"
},
"dependencies": {
- "@typescript-eslint/typescript-estree": "5.50.0",
- "@typescript-eslint/utils": "5.50.0",
+ "@typescript-eslint/typescript-estree": "5.51.0",
+ "@typescript-eslint/utils": "5.51.0",
"debug": "^4.3.4",
"tsutils": "^3.21.0"
},
"devDependencies": {
- "@typescript-eslint/parser": "5.50.0",
+ "@typescript-eslint/parser": "5.51.0",
"typescript": "*"
},
"peerDependencies": {
diff --git a/packages/types/CHANGELOG.md b/packages/types/CHANGELOG.md
index 0cd6d24aa095..497e08356510 100644
--- a/packages/types/CHANGELOG.md
+++ b/packages/types/CHANGELOG.md
@@ -3,6 +3,17 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
+# [5.51.0](https://github.com/typescript-eslint/typescript-eslint/compare/v5.50.0...v5.51.0) (2023-02-06)
+
+
+### Features
+
+* **typescript-estree:** cache project glob resolution ([#6367](https://github.com/typescript-eslint/typescript-eslint/issues/6367)) ([afae837](https://github.com/typescript-eslint/typescript-eslint/commit/afae8374df64101627808ccfeb5b715c865e910f))
+
+
+
+
+
# [5.50.0](https://github.com/typescript-eslint/typescript-eslint/compare/v5.49.0...v5.50.0) (2023-01-31)
**Note:** Version bump only for package @typescript-eslint/types
diff --git a/packages/types/package.json b/packages/types/package.json
index ccc72bcd6986..fd48c6e4466a 100644
--- a/packages/types/package.json
+++ b/packages/types/package.json
@@ -1,6 +1,6 @@
{
"name": "@typescript-eslint/types",
- "version": "5.50.0",
+ "version": "5.51.0",
"description": "Types for the TypeScript-ESTree AST spec",
"keywords": [
"eslint",
diff --git a/packages/types/src/parser-options.ts b/packages/types/src/parser-options.ts
index b3149231215e..a7fe3ce3ab36 100644
--- a/packages/types/src/parser-options.ts
+++ b/packages/types/src/parser-options.ts
@@ -3,6 +3,7 @@ import type { Program } from 'typescript';
import type { Lib } from './lib';
type DebugLevel = boolean | ('typescript-eslint' | 'eslint' | 'typescript')[];
+type CacheDurationSeconds = number | 'Infinity';
type EcmaVersion =
| 3
@@ -59,7 +60,17 @@ interface ParserOptions {
tsconfigRootDir?: string;
warnOnUnsupportedTypeScriptVersion?: boolean;
moduleResolver?: string;
+ cacheLifetime?: {
+ glob?: CacheDurationSeconds;
+ };
+
[additionalProperties: string]: unknown;
}
-export { DebugLevel, EcmaVersion, ParserOptions, SourceType };
+export {
+ CacheDurationSeconds,
+ DebugLevel,
+ EcmaVersion,
+ ParserOptions,
+ SourceType,
+};
diff --git a/packages/typescript-estree/CHANGELOG.md b/packages/typescript-estree/CHANGELOG.md
index b74f9d0a82db..8d1eb8e9a854 100644
--- a/packages/typescript-estree/CHANGELOG.md
+++ b/packages/typescript-estree/CHANGELOG.md
@@ -3,6 +3,17 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
+# [5.51.0](https://github.com/typescript-eslint/typescript-eslint/compare/v5.50.0...v5.51.0) (2023-02-06)
+
+
+### Features
+
+* **typescript-estree:** cache project glob resolution ([#6367](https://github.com/typescript-eslint/typescript-eslint/issues/6367)) ([afae837](https://github.com/typescript-eslint/typescript-eslint/commit/afae8374df64101627808ccfeb5b715c865e910f))
+
+
+
+
+
# [5.50.0](https://github.com/typescript-eslint/typescript-eslint/compare/v5.49.0...v5.50.0) (2023-01-31)
**Note:** Version bump only for package @typescript-eslint/typescript-estree
diff --git a/packages/typescript-estree/package.json b/packages/typescript-estree/package.json
index 728c8d417e96..22afddb5fc2c 100644
--- a/packages/typescript-estree/package.json
+++ b/packages/typescript-estree/package.json
@@ -1,6 +1,6 @@
{
"name": "@typescript-eslint/typescript-estree",
- "version": "5.50.0",
+ "version": "5.51.0",
"description": "A parser that converts TypeScript source code into an ESTree compatible form",
"main": "dist/index.js",
"types": "dist/index.d.ts",
@@ -42,8 +42,8 @@
"typecheck": "tsc -p tsconfig.json --noEmit"
},
"dependencies": {
- "@typescript-eslint/types": "5.50.0",
- "@typescript-eslint/visitor-keys": "5.50.0",
+ "@typescript-eslint/types": "5.51.0",
+ "@typescript-eslint/visitor-keys": "5.51.0",
"debug": "^4.3.4",
"globby": "^11.1.0",
"is-glob": "^4.0.3",
@@ -59,7 +59,7 @@
"@types/is-glob": "*",
"@types/semver": "*",
"@types/tmp": "*",
- "@typescript-eslint/shared-fixtures": "5.50.0",
+ "@typescript-eslint/shared-fixtures": "5.51.0",
"glob": "*",
"jest-specific-snapshot": "*",
"make-dir": "*",
diff --git a/packages/typescript-estree/src/convert-comments.ts b/packages/typescript-estree/src/convert-comments.ts
index ea02be412768..d4dd9f124a79 100644
--- a/packages/typescript-estree/src/convert-comments.ts
+++ b/packages/typescript-estree/src/convert-comments.ts
@@ -22,7 +22,7 @@ export function convertComments(
ast,
(_, comment) => {
const type =
- comment.kind == ts.SyntaxKind.SingleLineCommentTrivia
+ comment.kind === ts.SyntaxKind.SingleLineCommentTrivia
? AST_TOKEN_TYPES.Line
: AST_TOKEN_TYPES.Block;
const range: TSESTree.Range = [comment.pos, comment.end];
diff --git a/packages/typescript-estree/src/convert.ts b/packages/typescript-estree/src/convert.ts
index b21a42614dae..70ee9da4e0ce 100644
--- a/packages/typescript-estree/src/convert.ts
+++ b/packages/typescript-estree/src/convert.ts
@@ -2183,7 +2183,7 @@ export class Converter {
type: AST_NODE_TYPES.Literal,
raw: rawValue,
value: value,
- bigint: value === null ? bigint : String(value),
+ bigint: value == null ? bigint : String(value),
range,
});
}
diff --git a/packages/typescript-estree/src/create-program/getWatchProgramsForProjects.ts b/packages/typescript-estree/src/create-program/getWatchProgramsForProjects.ts
index 15d88e5f4540..d9d4de9c833f 100644
--- a/packages/typescript-estree/src/create-program/getWatchProgramsForProjects.ts
+++ b/packages/typescript-estree/src/create-program/getWatchProgramsForProjects.ts
@@ -8,6 +8,7 @@ import type { CanonicalPath } from './shared';
import {
canonicalDirname,
createDefaultCompilerOptionsFromExtra,
+ createHash,
getCanonicalFileName,
getModuleResolver,
} from './shared';
@@ -105,19 +106,6 @@ function diagnosticReporter(diagnostic: ts.Diagnostic): void {
);
}
-/**
- * Hash content for compare content.
- * @param content hashed contend
- * @returns hashed result
- */
-function createHash(content: string): string {
- // No ts.sys in browser environments.
- if (ts.sys?.createHash) {
- return ts.sys.createHash(content);
- }
- return content;
-}
-
function updateCachedFileList(
tsconfigPath: CanonicalPath,
program: ts.Program,
diff --git a/packages/typescript-estree/src/create-program/shared.ts b/packages/typescript-estree/src/create-program/shared.ts
index dd50f757dce1..e8de97969283 100644
--- a/packages/typescript-estree/src/create-program/shared.ts
+++ b/packages/typescript-estree/src/create-program/shared.ts
@@ -124,12 +124,26 @@ function getModuleResolver(moduleResolverPath: string): ModuleResolver {
return moduleResolver;
}
+/**
+ * Hash content for compare content.
+ * @param content hashed contend
+ * @returns hashed result
+ */
+function createHash(content: string): string {
+ // No ts.sys in browser environments.
+ if (ts.sys?.createHash) {
+ return ts.sys.createHash(content);
+ }
+ return content;
+}
+
export {
ASTAndProgram,
CORE_COMPILER_OPTIONS,
canonicalDirname,
CanonicalPath,
createDefaultCompilerOptionsFromExtra,
+ createHash,
ensureAbsolutePath,
getCanonicalFileName,
getAstFromProgram,
diff --git a/packages/typescript-estree/src/getModifiers.ts b/packages/typescript-estree/src/getModifiers.ts
index d8f8e716f9e2..a584a7659a77 100644
--- a/packages/typescript-estree/src/getModifiers.ts
+++ b/packages/typescript-estree/src/getModifiers.ts
@@ -31,7 +31,7 @@ export function getModifiers(
export function getDecorators(
node: ts.Node | null | undefined,
): undefined | ts.Decorator[] {
- if (node == undefined) {
+ if (node == null) {
return undefined;
}
diff --git a/packages/typescript-estree/src/parseSettings/ExpiringCache.ts b/packages/typescript-estree/src/parseSettings/ExpiringCache.ts
new file mode 100644
index 000000000000..f296c9f5f590
--- /dev/null
+++ b/packages/typescript-estree/src/parseSettings/ExpiringCache.ts
@@ -0,0 +1,69 @@
+import type { CacheDurationSeconds } from '@typescript-eslint/types';
+
+export const DEFAULT_TSCONFIG_CACHE_DURATION_SECONDS = 30;
+const ZERO_HR_TIME: [number, number] = [0, 0];
+
+/**
+ * A map with key-level expiration.
+ */
+export class ExpiringCache {
+ readonly #cacheDurationSeconds: CacheDurationSeconds;
+ /**
+ * The mapping of path-like string to the resolved TSConfig(s)
+ */
+ protected readonly map = new Map<
+ TKey,
+ Readonly<{
+ value: TValue;
+ lastSeen: [number, number];
+ }>
+ >();
+
+ constructor(cacheDurationSeconds: CacheDurationSeconds) {
+ this.#cacheDurationSeconds = cacheDurationSeconds;
+ }
+
+ set(key: TKey, value: TValue): this {
+ this.map.set(key, {
+ value,
+ lastSeen:
+ this.#cacheDurationSeconds === 'Infinity'
+ ? // no need to waste time calculating the hrtime in infinity mode as there's no expiry
+ ZERO_HR_TIME
+ : process.hrtime(),
+ });
+ return this;
+ }
+
+ get(key: TKey): TValue | undefined {
+ const entry = this.map.get(key);
+ if (entry?.value != null) {
+ if (this.#cacheDurationSeconds === 'Infinity') {
+ return entry.value;
+ }
+
+ const ageSeconds = process.hrtime(entry.lastSeen)[0];
+ if (ageSeconds < this.#cacheDurationSeconds) {
+ // cache hit woo!
+ return entry.value;
+ } else {
+ // key has expired - clean it up to free up memory
+ this.cleanupKey(key);
+ }
+ }
+ // no hit :'(
+ return undefined;
+ }
+
+ protected cleanupKey(key: TKey): void {
+ this.map.delete(key);
+ }
+
+ get size(): number {
+ return this.map.size;
+ }
+
+ clear(): void {
+ this.map.clear();
+ }
+}
diff --git a/packages/typescript-estree/src/parseSettings/createParseSettings.ts b/packages/typescript-estree/src/parseSettings/createParseSettings.ts
index b1cde9d4c9ad..e7267a852686 100644
--- a/packages/typescript-estree/src/parseSettings/createParseSettings.ts
+++ b/packages/typescript-estree/src/parseSettings/createParseSettings.ts
@@ -1,15 +1,10 @@
import debug from 'debug';
-import { sync as globSync } from 'globby';
-import isGlob from 'is-glob';
-import type { CanonicalPath } from '../create-program/shared';
-import {
- ensureAbsolutePath,
- getCanonicalFileName,
-} from '../create-program/shared';
+import { ensureAbsolutePath } from '../create-program/shared';
import type { TSESTreeOptions } from '../parser-options';
import type { MutableParseSettings } from './index';
import { inferSingleRun } from './inferSingleRun';
+import { resolveProjectList } from './resolveProjectList';
import { warnAboutTSVersion } from './warnAboutTSVersion';
const log = debug(
@@ -98,23 +93,13 @@ export function createParseSettings(
// Providing a program overrides project resolution
if (!parseSettings.programs) {
- const projectFolderIgnoreList = (
- options.projectFolderIgnoreList ?? ['**/node_modules/**']
- )
- .reduce((acc, folder) => {
- if (typeof folder === 'string') {
- acc.push(folder);
- }
- return acc;
- }, [])
- // prefix with a ! for not match glob
- .map(folder => (folder.startsWith('!') ? folder : `!${folder}`));
-
- parseSettings.projects = prepareAndTransformProjects(
- tsconfigRootDir,
- options.project,
- projectFolderIgnoreList,
- );
+ parseSettings.projects = resolveProjectList({
+ cacheLifetime: options.cacheLifetime,
+ project: options.project,
+ projectFolderIgnoreList: options.projectFolderIgnoreList,
+ singleRun: parseSettings.singleRun,
+ tsconfigRootDir: tsconfigRootDir,
+ });
}
warnAboutTSVersion(parseSettings);
@@ -144,58 +129,3 @@ function enforceString(code: unknown): string {
function getFileName(jsx?: boolean): string {
return jsx ? 'estree.tsx' : 'estree.ts';
}
-
-function getTsconfigPath(
- tsconfigPath: string,
- tsconfigRootDir: string,
-): CanonicalPath {
- return getCanonicalFileName(
- ensureAbsolutePath(tsconfigPath, tsconfigRootDir),
- );
-}
-
-/**
- * Normalizes, sanitizes, resolves and filters the provided project paths
- */
-function prepareAndTransformProjects(
- tsconfigRootDir: string,
- projectsInput: string | string[] | undefined,
- ignoreListInput: string[],
-): CanonicalPath[] {
- const sanitizedProjects: string[] = [];
-
- // Normalize and sanitize the project paths
- if (typeof projectsInput === 'string') {
- sanitizedProjects.push(projectsInput);
- } else if (Array.isArray(projectsInput)) {
- for (const project of projectsInput) {
- if (typeof project === 'string') {
- sanitizedProjects.push(project);
- }
- }
- }
-
- if (sanitizedProjects.length === 0) {
- return [];
- }
-
- // Transform glob patterns into paths
- const nonGlobProjects = sanitizedProjects.filter(project => !isGlob(project));
- const globProjects = sanitizedProjects.filter(project => isGlob(project));
- const uniqueCanonicalProjectPaths = new Set(
- nonGlobProjects
- .concat(
- globSync([...globProjects, ...ignoreListInput], {
- cwd: tsconfigRootDir,
- }),
- )
- .map(project => getTsconfigPath(project, tsconfigRootDir)),
- );
-
- log(
- 'parserOptions.project (excluding ignored) matched projects: %s',
- uniqueCanonicalProjectPaths,
- );
-
- return Array.from(uniqueCanonicalProjectPaths);
-}
diff --git a/packages/typescript-estree/src/parseSettings/index.ts b/packages/typescript-estree/src/parseSettings/index.ts
index 0a9734d1b241..53b1acf4a6fb 100644
--- a/packages/typescript-estree/src/parseSettings/index.ts
+++ b/packages/typescript-estree/src/parseSettings/index.ts
@@ -98,7 +98,7 @@ export interface MutableParseSettings {
/**
* Normalized paths to provided project paths.
*/
- projects: CanonicalPath[];
+ projects: readonly CanonicalPath[];
/**
* Whether to add the `range` property to AST nodes.
diff --git a/packages/typescript-estree/src/parseSettings/resolveProjectList.ts b/packages/typescript-estree/src/parseSettings/resolveProjectList.ts
new file mode 100644
index 000000000000..72e9539d2b9b
--- /dev/null
+++ b/packages/typescript-estree/src/parseSettings/resolveProjectList.ts
@@ -0,0 +1,146 @@
+import debug from 'debug';
+import { sync as globSync } from 'globby';
+import isGlob from 'is-glob';
+
+import type { CanonicalPath } from '../create-program/shared';
+import {
+ createHash,
+ ensureAbsolutePath,
+ getCanonicalFileName,
+} from '../create-program/shared';
+import type { TSESTreeOptions } from '../parser-options';
+import {
+ DEFAULT_TSCONFIG_CACHE_DURATION_SECONDS,
+ ExpiringCache,
+} from './ExpiringCache';
+
+const log = debug(
+ 'typescript-eslint:typescript-estree:parser:parseSettings:resolveProjectList',
+);
+
+let RESOLUTION_CACHE: ExpiringCache | null =
+ null;
+
+/**
+ * Normalizes, sanitizes, resolves and filters the provided project paths
+ */
+export function resolveProjectList(
+ options: Readonly<{
+ cacheLifetime?: TSESTreeOptions['cacheLifetime'];
+ project: TSESTreeOptions['project'];
+ projectFolderIgnoreList: TSESTreeOptions['projectFolderIgnoreList'];
+ singleRun: boolean;
+ tsconfigRootDir: string;
+ }>,
+): readonly CanonicalPath[] {
+ const sanitizedProjects: string[] = [];
+
+ // Normalize and sanitize the project paths
+ if (typeof options.project === 'string') {
+ sanitizedProjects.push(options.project);
+ } else if (Array.isArray(options.project)) {
+ for (const project of options.project) {
+ if (typeof project === 'string') {
+ sanitizedProjects.push(project);
+ }
+ }
+ }
+
+ if (sanitizedProjects.length === 0) {
+ return [];
+ }
+
+ const projectFolderIgnoreList = (
+ options.projectFolderIgnoreList ?? ['**/node_modules/**']
+ )
+ .reduce((acc, folder) => {
+ if (typeof folder === 'string') {
+ acc.push(folder);
+ }
+ return acc;
+ }, [])
+ // prefix with a ! for not match glob
+ .map(folder => (folder.startsWith('!') ? folder : `!${folder}`));
+
+ const cacheKey = getHash({
+ project: sanitizedProjects,
+ projectFolderIgnoreList,
+ tsconfigRootDir: options.tsconfigRootDir,
+ });
+ if (RESOLUTION_CACHE == null) {
+ // note - we initialize the global cache based on the first config we encounter.
+ // this does mean that you can't have multiple lifetimes set per folder
+ // I doubt that anyone will really bother reconfiguring this, let alone
+ // try to do complicated setups, so we'll deal with this later if ever.
+ RESOLUTION_CACHE = new ExpiringCache(
+ options.singleRun
+ ? 'Infinity'
+ : options.cacheLifetime?.glob ??
+ DEFAULT_TSCONFIG_CACHE_DURATION_SECONDS,
+ );
+ } else {
+ const cached = RESOLUTION_CACHE.get(cacheKey);
+ if (cached) {
+ return cached;
+ }
+ }
+
+ // Transform glob patterns into paths
+ const nonGlobProjects = sanitizedProjects.filter(project => !isGlob(project));
+ const globProjects = sanitizedProjects.filter(project => isGlob(project));
+
+ const uniqueCanonicalProjectPaths = new Set(
+ nonGlobProjects
+ .concat(
+ globProjects.length === 0
+ ? []
+ : globSync([...globProjects, ...projectFolderIgnoreList], {
+ cwd: options.tsconfigRootDir,
+ }),
+ )
+ .map(project =>
+ getCanonicalFileName(
+ ensureAbsolutePath(project, options.tsconfigRootDir),
+ ),
+ ),
+ );
+
+ log(
+ 'parserOptions.project (excluding ignored) matched projects: %s',
+ uniqueCanonicalProjectPaths,
+ );
+
+ const returnValue = Array.from(uniqueCanonicalProjectPaths);
+ RESOLUTION_CACHE.set(cacheKey, returnValue);
+ return returnValue;
+}
+
+function getHash({
+ project,
+ projectFolderIgnoreList,
+ tsconfigRootDir,
+}: Readonly<{
+ project: readonly string[];
+ projectFolderIgnoreList: readonly string[];
+ tsconfigRootDir: string;
+}>): string {
+ // create a stable representation of the config
+ const hashObject = {
+ tsconfigRootDir,
+ // the project order does matter and can impact the resolved globs
+ project,
+ // the ignore order won't doesn't ever matter
+ projectFolderIgnoreList: [...projectFolderIgnoreList].sort(),
+ };
+
+ return createHash(JSON.stringify(hashObject));
+}
+
+/**
+ * Exported for testing purposes only
+ * @internal
+ */
+export function clearGlobResolutionCache(): void {
+ RESOLUTION_CACHE?.clear();
+ RESOLUTION_CACHE = null;
+}
diff --git a/packages/typescript-estree/src/parser-options.ts b/packages/typescript-estree/src/parser-options.ts
index 632d9e6ae883..8cfe3c934c2c 100644
--- a/packages/typescript-estree/src/parser-options.ts
+++ b/packages/typescript-estree/src/parser-options.ts
@@ -1,4 +1,7 @@
-import type { DebugLevel } from '@typescript-eslint/types';
+import type {
+ CacheDurationSeconds,
+ DebugLevel,
+} from '@typescript-eslint/types';
import type * as ts from 'typescript';
import type { TSESTree, TSESTreeToTSNode, TSNode, TSToken } from './ts-estree';
@@ -168,6 +171,25 @@ interface ParseAndGenerateServicesOptions extends ParseOptions {
*/
allowAutomaticSingleRunInference?: boolean;
+ /**
+ * Granular control of the expiry lifetime of our internal caches.
+ * You can specify the number of seconds as an integer number, or the string
+ * 'Infinity' if you never want the cache to expire.
+ *
+ * By default cache entries will be evicted after 30 seconds, or will persist
+ * indefinitely if `allowAutomaticSingleRunInference = true` AND the parser
+ * infers that it is a single run.
+ */
+ cacheLifetime?: {
+ /**
+ * Glob resolution for `parserOptions.project` values.
+ */
+ glob?: CacheDurationSeconds;
+ };
+
+ /**
+ * Path to a file exporting a custom `ModuleResolver`.
+ */
moduleResolver?: string;
}
diff --git a/packages/typescript-estree/src/simple-traverse.ts b/packages/typescript-estree/src/simple-traverse.ts
index 13c763ec228e..2d51cdbe4fa1 100644
--- a/packages/typescript-estree/src/simple-traverse.ts
+++ b/packages/typescript-estree/src/simple-traverse.ts
@@ -5,7 +5,7 @@ import type { TSESTree } from './ts-estree';
// eslint-disable-next-line @typescript-eslint/no-explicit-any
function isValidNode(x: any): x is TSESTree.Node {
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
- return x !== null && typeof x === 'object' && typeof x.type === 'string';
+ return x != null && typeof x === 'object' && typeof x.type === 'string';
}
function getVisitorKeysForNode(
diff --git a/packages/typescript-estree/tests/ast-alignment/fixtures-to-test.ts b/packages/typescript-estree/tests/ast-alignment/fixtures-to-test.ts
index 9c4d8a44bcc9..6abab590b77c 100644
--- a/packages/typescript-estree/tests/ast-alignment/fixtures-to-test.ts
+++ b/packages/typescript-estree/tests/ast-alignment/fixtures-to-test.ts
@@ -477,11 +477,6 @@ tester.addFixturePatternConfig('typescript/expressions', {
* @see https://github.com/babel/babel/issues/14613
*/
'instantiation-expression',
- /**
- * TS 4.9 `satisfies` operator has not been implemented in Babel yet.
- * @see https://github.com/babel/babel/pull/14211
- */
- 'satisfies-expression',
],
});
diff --git a/packages/typescript-estree/tests/lib/parse.test.ts b/packages/typescript-estree/tests/lib/parse.test.ts
index 3e84dd3e069f..0f286efdb191 100644
--- a/packages/typescript-estree/tests/lib/parse.test.ts
+++ b/packages/typescript-estree/tests/lib/parse.test.ts
@@ -1,10 +1,14 @@
+import type { CacheDurationSeconds } from '@typescript-eslint/types';
import debug from 'debug';
+import * as globbyModule from 'globby';
import { join, resolve } from 'path';
+import type * as typescriptModule from 'typescript';
import * as parser from '../../src';
import * as astConverterModule from '../../src/ast-converter';
import * as sharedParserUtilsModule from '../../src/create-program/shared';
import type { TSESTreeOptions } from '../../src/parser-options';
+import { clearGlobResolutionCache } from '../../src/parseSettings/resolveProjectList';
import { createSnapshotTestBlock } from '../../tools/test-utils';
const FIXTURES_DIR = join(__dirname, '../fixtures/simpleProject');
@@ -38,7 +42,7 @@ jest.mock('../../src/create-program/shared', () => {
// Tests in CI by default run with lowercase program file names,
// resulting in path.relative results starting with many "../"s
jest.mock('typescript', () => {
- const ts = jest.requireActual('typescript');
+ const ts = jest.requireActual('typescript');
return {
...ts,
sys: {
@@ -48,10 +52,21 @@ jest.mock('typescript', () => {
};
});
+jest.mock('globby', () => {
+ const globby = jest.requireActual('globby');
+ return {
+ ...globby,
+ sync: jest.fn(globby.sync),
+ };
+});
+
+const hrtimeSpy = jest.spyOn(process, 'hrtime');
+
const astConverterMock = jest.mocked(astConverterModule.astConverter);
const createDefaultCompilerOptionsFromExtra = jest.mocked(
sharedParserUtilsModule.createDefaultCompilerOptionsFromExtra,
);
+const globbySyncMock = jest.mocked(globbyModule.sync);
/**
* Aligns paths between environments, node for windows uses `\`, for linux and mac uses `/`
@@ -63,6 +78,7 @@ function alignErrorPath(error: Error): never {
beforeEach(() => {
jest.clearAllMocks();
+ clearGlobResolutionCache();
});
describe('parseWithNodeMaps()', () => {
@@ -749,8 +765,69 @@ describe('parseAndGenerateServices', () => {
it('ignores a folder when given a string glob', () => {
const ignore = ['**/ignoreme/**'];
+ // cspell:disable-next-line
expect(testParse('ignoreme', ignore)).toThrow();
+ // cspell:disable-next-line
expect(testParse('includeme', ignore)).not.toThrow();
});
});
+
+ describe('cacheLifetime', () => {
+ describe('glob', () => {
+ function doParse(lifetime: CacheDurationSeconds): void {
+ parser.parseAndGenerateServices('const x = 1', {
+ cacheLifetime: {
+ glob: lifetime,
+ },
+ filePath: join(FIXTURES_DIR, 'file.ts'),
+ tsconfigRootDir: FIXTURES_DIR,
+ project: ['./**/tsconfig.json', './**/tsconfig.extra.json'],
+ });
+ }
+
+ it('should cache globs if the lifetime is non-zero', () => {
+ doParse(30);
+ expect(globbySyncMock).toHaveBeenCalledTimes(1);
+ doParse(30);
+ // shouldn't call globby again due to the caching
+ expect(globbySyncMock).toHaveBeenCalledTimes(1);
+ });
+
+ it('should not cache globs if the lifetime is zero', () => {
+ doParse(0);
+ expect(globbySyncMock).toHaveBeenCalledTimes(1);
+ doParse(0);
+ // should call globby again because we specified immediate cache expiry
+ expect(globbySyncMock).toHaveBeenCalledTimes(2);
+ });
+
+ it('should evict the cache if the entry expires', () => {
+ hrtimeSpy.mockReturnValueOnce([1, 0]);
+
+ doParse(30);
+ expect(globbySyncMock).toHaveBeenCalledTimes(1);
+
+ // wow so much time has passed
+ hrtimeSpy.mockReturnValueOnce([Number.MAX_VALUE, 0]);
+
+ doParse(30);
+ // shouldn't call globby again due to the caching
+ expect(globbySyncMock).toHaveBeenCalledTimes(2);
+ });
+
+ it('should infinitely cache if passed Infinity', () => {
+ hrtimeSpy.mockReturnValueOnce([1, 0]);
+
+ doParse('Infinity');
+ expect(globbySyncMock).toHaveBeenCalledTimes(1);
+
+ // wow so much time has passed
+ hrtimeSpy.mockReturnValueOnce([Number.MAX_VALUE, 0]);
+
+ doParse('Infinity');
+ // shouldn't call globby again due to the caching
+ expect(globbySyncMock).toHaveBeenCalledTimes(1);
+ });
+ });
+ });
});
diff --git a/packages/typescript-estree/tests/lib/semanticInfo-singleRun.test.ts b/packages/typescript-estree/tests/lib/semanticInfo-singleRun.test.ts
index 33daa30a13f0..b605dd6ea5f3 100644
--- a/packages/typescript-estree/tests/lib/semanticInfo-singleRun.test.ts
+++ b/packages/typescript-estree/tests/lib/semanticInfo-singleRun.test.ts
@@ -2,6 +2,7 @@ import glob from 'glob';
import * as path from 'path';
import { getCanonicalFileName } from '../../src/create-program/shared';
+import { createProgramFromConfigFile as createProgramFromConfigFileOriginal } from '../../src/create-program/useProvidedPrograms';
import {
clearParseAndGenerateServicesCalls,
clearProgramCache,
@@ -69,9 +70,9 @@ jest.mock('../../src/create-program/getWatchProgramsForProjects', () => {
};
});
-const {
- createProgramFromConfigFile,
-} = require('../../src/create-program/useProvidedPrograms');
+const createProgramFromConfigFile = jest.mocked(
+ createProgramFromConfigFileOriginal,
+);
const FIXTURES_DIR = './tests/fixtures/semanticInfo';
const testFiles = glob.sync(`**/*.src.ts`, {
@@ -97,7 +98,7 @@ describe('semanticInfo - singleRun', () => {
// ensure caches are clean for each test
clearProgramCache();
// ensure invocations of mock are clean for each test
- (createProgramFromConfigFile as jest.Mock).mockClear();
+ createProgramFromConfigFile.mockClear();
// Do not track invocations per file across tests
clearParseAndGenerateServicesCalls();
});
@@ -232,7 +233,7 @@ describe('semanticInfo - singleRun', () => {
const optionsWithReversedTsconfigs = {
...options,
// Now the matching tsconfig comes first
- project: options.project.reverse(),
+ project: [...options.project].reverse(),
};
const resultProgram = parseAndGenerateServices(
@@ -248,7 +249,7 @@ describe('semanticInfo - singleRun', () => {
expect(createProgramFromConfigFile).toHaveBeenNthCalledWith(
1,
- resolvedProject(tsconfigs[0]),
+ resolvedProject(tsconfigs[1]),
);
// Restore process data
diff --git a/packages/typescript-estree/tools/test-utils.ts b/packages/typescript-estree/tools/test-utils.ts
index 6f40b597ca73..aef5601ebe2f 100644
--- a/packages/typescript-estree/tools/test-utils.ts
+++ b/packages/typescript-estree/tools/test-utils.ts
@@ -92,7 +92,7 @@ type UnknownObject = Record;
function isObjectLike(value: unknown | null): value is UnknownObject {
return (
- typeof value === 'object' && !(value instanceof RegExp) && value !== null
+ typeof value === 'object' && !(value instanceof RegExp) && value != null
);
}
diff --git a/packages/utils/CHANGELOG.md b/packages/utils/CHANGELOG.md
index ec49364f3e77..f15c36d7d73f 100644
--- a/packages/utils/CHANGELOG.md
+++ b/packages/utils/CHANGELOG.md
@@ -3,6 +3,17 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
+# [5.51.0](https://github.com/typescript-eslint/typescript-eslint/compare/v5.50.0...v5.51.0) (2023-02-06)
+
+
+### Features
+
+* **eslint-plugin:** [no-import-type-side-effects] add rule to warn against runtime side effects with `verbatimModuleSyntax` ([#6394](https://github.com/typescript-eslint/typescript-eslint/issues/6394)) ([b14d3be](https://github.com/typescript-eslint/typescript-eslint/commit/b14d3be0f305d71e0adfc9381e9de993898b2b43))
+
+
+
+
+
# [5.50.0](https://github.com/typescript-eslint/typescript-eslint/compare/v5.49.0...v5.50.0) (2023-01-31)
**Note:** Version bump only for package @typescript-eslint/utils
diff --git a/packages/utils/package.json b/packages/utils/package.json
index 13e83257c675..bc80a009477f 100644
--- a/packages/utils/package.json
+++ b/packages/utils/package.json
@@ -1,6 +1,6 @@
{
"name": "@typescript-eslint/utils",
- "version": "5.50.0",
+ "version": "5.51.0",
"description": "Utilities for working with TypeScript + ESLint together",
"keywords": [
"eslint",
@@ -41,9 +41,9 @@
"dependencies": {
"@types/json-schema": "^7.0.9",
"@types/semver": "^7.3.12",
- "@typescript-eslint/scope-manager": "5.50.0",
- "@typescript-eslint/types": "5.50.0",
- "@typescript-eslint/typescript-estree": "5.50.0",
+ "@typescript-eslint/scope-manager": "5.51.0",
+ "@typescript-eslint/types": "5.51.0",
+ "@typescript-eslint/typescript-estree": "5.51.0",
"eslint-scope": "^5.1.1",
"eslint-utils": "^3.0.0",
"semver": "^7.3.7"
@@ -52,7 +52,7 @@
"eslint": "^6.0.0 || ^7.0.0 || ^8.0.0"
},
"devDependencies": {
- "@typescript-eslint/parser": "5.50.0",
+ "@typescript-eslint/parser": "5.51.0",
"typescript": "*"
},
"funding": {
diff --git a/packages/utils/src/ast-utils/predicates.ts b/packages/utils/src/ast-utils/predicates.ts
index 6a65cab7625b..36e08ee5c42f 100644
--- a/packages/utils/src/ast-utils/predicates.ts
+++ b/packages/utils/src/ast-utils/predicates.ts
@@ -139,6 +139,20 @@ const isAwaitKeyword = isTokenOfTypeWithConditions(AST_TOKEN_TYPES.Identifier, {
value: 'await',
});
+/**
+ * Checks if a possible token is the `type` keyword.
+ */
+const isTypeKeyword = isTokenOfTypeWithConditions(AST_TOKEN_TYPES.Identifier, {
+ value: 'type',
+});
+
+/**
+ * Checks if a possible token is the `import` keyword.
+ */
+const isImportKeyword = isTokenOfTypeWithConditions(AST_TOKEN_TYPES.Keyword, {
+ value: 'import',
+});
+
const isLoop = isNodeOfTypes([
AST_NODE_TYPES.DoWhileStatement,
AST_NODE_TYPES.ForStatement,
@@ -156,6 +170,7 @@ export {
isFunctionOrFunctionType,
isFunctionType,
isIdentifier,
+ isImportKeyword,
isLoop,
isLogicalOrOperator,
isNonNullAssertionPunctuator,
@@ -167,5 +182,6 @@ export {
isTSConstructorType,
isTSFunctionType,
isTypeAssertion,
+ isTypeKeyword,
isVariableDeclarator,
};
diff --git a/packages/utils/src/eslint-utils/applyDefault.ts b/packages/utils/src/eslint-utils/applyDefault.ts
index 85cd3b3653be..f16ae798715f 100644
--- a/packages/utils/src/eslint-utils/applyDefault.ts
+++ b/packages/utils/src/eslint-utils/applyDefault.ts
@@ -16,7 +16,7 @@ function applyDefault(
JSON.stringify(defaultOptions),
) as AsMutable;
- if (userOptions === null || userOptions === undefined) {
+ if (userOptions == null) {
return options;
}
diff --git a/packages/utils/src/eslint-utils/nullThrows.ts b/packages/utils/src/eslint-utils/nullThrows.ts
index df644c2befb0..1a79b2e09d43 100644
--- a/packages/utils/src/eslint-utils/nullThrows.ts
+++ b/packages/utils/src/eslint-utils/nullThrows.ts
@@ -18,7 +18,7 @@ function nullThrows(value: T | null | undefined, message: string): T {
// so ignore it in coverage metrics.
/* istanbul ignore if */
- if (value === null || value === undefined) {
+ if (value == null) {
throw new Error(`Non-null Assertion Failed: ${message}`);
}
diff --git a/packages/utils/src/ts-eslint/SourceCode.ts b/packages/utils/src/ts-eslint/SourceCode.ts
index 447c9debedbe..a44cdee3676b 100644
--- a/packages/utils/src/ts-eslint/SourceCode.ts
+++ b/packages/utils/src/ts-eslint/SourceCode.ts
@@ -389,10 +389,25 @@ namespace SourceCode {
}
export type FilterPredicate = (token: TSESTree.Token) => boolean;
+ export type GetFilterPredicate =
+ // https://github.com/prettier/prettier/issues/14275
+ // prettier-ignore
+ TFilter extends ((
+ token: TSESTree.Token,
+ ) => token is infer U extends TSESTree.Token)
+ ? U
+ : TDefault;
+ export type GetFilterPredicateFromOptions =
+ TOptions extends { filter?: FilterPredicate }
+ ? GetFilterPredicate
+ : GetFilterPredicate;
export type ReturnTypeFromOptions = T extends { includeComments: true }
- ? TSESTree.Token
- : Exclude;
+ ? GetFilterPredicateFromOptions
+ : GetFilterPredicateFromOptions<
+ T,
+ Exclude
+ >;
export type CursorWithSkipOptions =
| number
diff --git a/packages/utils/tests/eslint-utils/nullThrows.test.ts b/packages/utils/tests/eslint-utils/nullThrows.test.ts
new file mode 100644
index 000000000000..7997fecaa6df
--- /dev/null
+++ b/packages/utils/tests/eslint-utils/nullThrows.test.ts
@@ -0,0 +1,31 @@
+import { nullThrows, NullThrowsReasons } from '../../src/eslint-utils';
+
+describe('nullThrows', () => {
+ it('returns a falsy value when it exists', () => {
+ const value = 0;
+
+ const actual = nullThrows(value, NullThrowsReasons.MissingParent);
+
+ expect(actual).toBe(value);
+ });
+
+ it('returns a truthy value when it exists', () => {
+ const value = { abc: 'def' };
+
+ const actual = nullThrows(value, NullThrowsReasons.MissingParent);
+
+ expect(actual).toBe(value);
+ });
+
+ it('throws an error when the value is null', () => {
+ expect(() => nullThrows(null, NullThrowsReasons.MissingParent)).toThrow(
+ NullThrowsReasons.MissingParent,
+ );
+ });
+
+ it('throws an error when the value is undefined', () => {
+ expect(() =>
+ nullThrows(undefined, NullThrowsReasons.MissingParent),
+ ).toThrow(NullThrowsReasons.MissingParent);
+ });
+});
diff --git a/packages/visitor-keys/CHANGELOG.md b/packages/visitor-keys/CHANGELOG.md
index a7c260ce870f..3fa63a830424 100644
--- a/packages/visitor-keys/CHANGELOG.md
+++ b/packages/visitor-keys/CHANGELOG.md
@@ -3,6 +3,14 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
+# [5.51.0](https://github.com/typescript-eslint/typescript-eslint/compare/v5.50.0...v5.51.0) (2023-02-06)
+
+**Note:** Version bump only for package @typescript-eslint/visitor-keys
+
+
+
+
+
# [5.50.0](https://github.com/typescript-eslint/typescript-eslint/compare/v5.49.0...v5.50.0) (2023-01-31)
**Note:** Version bump only for package @typescript-eslint/visitor-keys
diff --git a/packages/visitor-keys/package.json b/packages/visitor-keys/package.json
index 14527dc3f80c..b984c406ee51 100644
--- a/packages/visitor-keys/package.json
+++ b/packages/visitor-keys/package.json
@@ -1,6 +1,6 @@
{
"name": "@typescript-eslint/visitor-keys",
- "version": "5.50.0",
+ "version": "5.51.0",
"description": "Visitor keys used to help traverse the TypeScript-ESTree AST",
"keywords": [
"eslint",
@@ -39,7 +39,7 @@
"typecheck": "tsc -p tsconfig.json --noEmit"
},
"dependencies": {
- "@typescript-eslint/types": "5.50.0",
+ "@typescript-eslint/types": "5.51.0",
"eslint-visitor-keys": "^3.3.0"
},
"devDependencies": {
diff --git a/packages/website-eslint/CHANGELOG.md b/packages/website-eslint/CHANGELOG.md
index ea36b42047a0..33686f6b038c 100644
--- a/packages/website-eslint/CHANGELOG.md
+++ b/packages/website-eslint/CHANGELOG.md
@@ -3,6 +3,14 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
+# [5.51.0](https://github.com/typescript-eslint/typescript-eslint/compare/v5.50.0...v5.51.0) (2023-02-06)
+
+**Note:** Version bump only for package @typescript-eslint/website-eslint
+
+
+
+
+
# [5.50.0](https://github.com/typescript-eslint/typescript-eslint/compare/v5.49.0...v5.50.0) (2023-01-31)
**Note:** Version bump only for package @typescript-eslint/website-eslint
diff --git a/packages/website-eslint/package.json b/packages/website-eslint/package.json
index 6bb1f6d3f902..df5766f2ebe3 100644
--- a/packages/website-eslint/package.json
+++ b/packages/website-eslint/package.json
@@ -1,6 +1,6 @@
{
"name": "@typescript-eslint/website-eslint",
- "version": "5.50.0",
+ "version": "5.51.0",
"private": true,
"description": "ESLint which works in browsers.",
"engines": {
@@ -16,19 +16,19 @@
"format": "prettier --write \"./**/*.{ts,mts,cts,tsx,js,mjs,cjs,jsx,json,md,css}\" --ignore-path ../../.prettierignore"
},
"dependencies": {
- "@typescript-eslint/types": "5.50.0",
- "@typescript-eslint/utils": "5.50.0"
+ "@typescript-eslint/types": "5.51.0",
+ "@typescript-eslint/utils": "5.51.0"
},
"devDependencies": {
"@rollup/plugin-commonjs": "^23.0.0",
"@rollup/plugin-json": "^5.0.0",
"@rollup/plugin-node-resolve": "^15.0.0",
"@rollup/pluginutils": "^5.0.0",
- "@typescript-eslint/eslint-plugin": "5.50.0",
- "@typescript-eslint/parser": "5.50.0",
- "@typescript-eslint/scope-manager": "5.50.0",
- "@typescript-eslint/typescript-estree": "5.50.0",
- "@typescript-eslint/visitor-keys": "5.50.0",
+ "@typescript-eslint/eslint-plugin": "5.51.0",
+ "@typescript-eslint/parser": "5.51.0",
+ "@typescript-eslint/scope-manager": "5.51.0",
+ "@typescript-eslint/typescript-estree": "5.51.0",
+ "@typescript-eslint/visitor-keys": "5.51.0",
"eslint": "*",
"rollup": "^2.75.4",
"rollup-plugin-terser": "^7.0.2",
diff --git a/packages/website/CHANGELOG.md b/packages/website/CHANGELOG.md
index 547367d081fd..2a28e8cdda0b 100644
--- a/packages/website/CHANGELOG.md
+++ b/packages/website/CHANGELOG.md
@@ -3,6 +3,14 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
+# [5.51.0](https://github.com/typescript-eslint/typescript-eslint/compare/v5.50.0...v5.51.0) (2023-02-06)
+
+**Note:** Version bump only for package website
+
+
+
+
+
# [5.50.0](https://github.com/typescript-eslint/typescript-eslint/compare/v5.49.0...v5.50.0) (2023-01-31)
**Note:** Version bump only for package website
diff --git a/packages/website/data/sponsors.json b/packages/website/data/sponsors.json
index 0ad6496d8453..f7c3f749e9dc 100644
--- a/packages/website/data/sponsors.json
+++ b/packages/website/data/sponsors.json
@@ -10,37 +10,44 @@
"id": "Nx (by Nrwl)",
"image": "https://images.opencollective.com/nx/0efbe42/logo.png",
"name": "Nx (by Nrwl)",
- "totalDonations": 600000,
+ "totalDonations": 625000,
"website": "https://nx.dev"
},
{
"id": "ESLint",
"image": "https://images.opencollective.com/eslint/96b09dc/logo.png",
"name": "ESLint",
- "totalDonations": 245000,
+ "totalDonations": 260000,
"website": "https://eslint.org/"
},
{
"id": "Hugging Face",
"image": "https://images.opencollective.com/huggingface/5c934ee/logo.png",
"name": "Hugging Face",
- "totalDonations": 180000,
+ "totalDonations": 200000,
"website": "https://huggingface.co"
},
{
"id": "Airbnb",
"image": "https://images.opencollective.com/airbnb/d327d66/logo.png",
"name": "Airbnb",
- "totalDonations": 150800,
+ "totalDonations": 155800,
"website": "https://www.airbnb.com/"
},
{
"id": "GitBook",
"image": "https://images.opencollective.com/gitbook/d35a8e7/logo.png",
"name": "GitBook",
- "totalDonations": 130000,
+ "totalDonations": 140000,
"website": "https://www.gitbook.com"
},
+ {
+ "id": "Codecademy",
+ "image": "https://images.opencollective.com/codecademy/d56a48d/logo.png",
+ "name": "Codecademy",
+ "totalDonations": 130000,
+ "website": "https://codecademy.com"
+ },
{
"id": "n8n.io - n8n GmbH",
"image": "https://images.opencollective.com/n8n/dca2f0c/logo.png",
@@ -55,13 +62,6 @@
"totalDonations": 120000,
"website": "https://blog.coinbase.com/engineering-and-security/home"
},
- {
- "id": "Codecademy",
- "image": "https://images.opencollective.com/codecademy/d56a48d/logo.png",
- "name": "Codecademy",
- "totalDonations": 120000,
- "website": "https://codecademy.com"
- },
{
"id": "Sentry",
"image": "https://images.opencollective.com/sentry/9620d33/logo.png",
@@ -80,9 +80,16 @@
"id": "Sourcegraph",
"image": "https://images.opencollective.com/sourcegraph/67e40ff/logo.png",
"name": "Sourcegraph",
- "totalDonations": 70000,
+ "totalDonations": 80000,
"website": "https://about.sourcegraph.com"
},
+ {
+ "id": "Codiga",
+ "image": "https://images.opencollective.com/codiga/1065f9f/logo.png",
+ "name": "Codiga",
+ "totalDonations": 60000,
+ "website": "https://www.codiga.io"
+ },
{
"id": "Future Processing",
"image": "https://images.opencollective.com/future-processing/1410d26/logo.png",
@@ -90,13 +97,6 @@
"totalDonations": 54000,
"website": "https://www.future-processing.com/"
},
- {
- "id": "Codiga",
- "image": "https://images.opencollective.com/codiga/1065f9f/logo.png",
- "name": "Codiga",
- "totalDonations": 50000,
- "website": "https://www.codiga.io"
- },
{
"id": "Whitebox",
"image": "https://images.opencollective.com/whiteboxinc/ef0d11d/logo.png",
@@ -108,7 +108,7 @@
"id": "STORIS",
"image": "https://images.opencollective.com/storis/dfb0e13/logo.png",
"name": "STORIS",
- "totalDonations": 30000,
+ "totalDonations": 31500,
"website": "https://www.storis.com/"
},
{
@@ -118,6 +118,13 @@
"totalDonations": 30000,
"website": "https://www.monito.com"
},
+ {
+ "id": "DeepSource",
+ "image": "https://images.opencollective.com/deepsource/0f18cea/logo.png",
+ "name": "DeepSource",
+ "totalDonations": 30000,
+ "website": "https://deepsource.io/"
+ },
{
"id": "revo.js",
"image": "https://images.opencollective.com/revojsro/82623a7/logo.png",
@@ -133,17 +140,17 @@
"website": "https://twitter.com/nevir"
},
{
- "id": "DeepSource",
- "image": "https://images.opencollective.com/deepsource/0f18cea/logo.png",
- "name": "DeepSource",
+ "id": "tRPC",
+ "image": "https://images.opencollective.com/trpc/82704a8/logo.png",
+ "name": "tRPC",
"totalDonations": 20000,
- "website": "https://deepsource.io/"
+ "website": "https://trpc.io"
},
{
"id": "David Johnston",
"image": "https://images.opencollective.com/blacksheepcode/976d69a/avatar.png",
"name": "David Johnston",
- "totalDonations": 16000,
+ "totalDonations": 16500,
"website": "https://blacksheepcode.com"
},
{
@@ -164,14 +171,14 @@
"id": "Evil Martians",
"image": "https://images.opencollective.com/evilmartians/707ab4d/logo.png",
"name": "Evil Martians",
- "totalDonations": 11000,
+ "totalDonations": 11500,
"website": "https://evilmartians.com/"
},
{
"id": "Balsa",
"image": "https://images.opencollective.com/balsa/77de498/logo.png",
"name": "Balsa",
- "totalDonations": 11000,
+ "totalDonations": 11500,
"website": "https://balsa.com"
},
{
@@ -194,12 +201,5 @@
"name": "Laserhub",
"totalDonations": 10000,
"website": "https://laserhub.com/"
- },
- {
- "id": "tRPC",
- "image": "https://images.opencollective.com/trpc/82704a8/logo.png",
- "name": "tRPC",
- "totalDonations": 10000,
- "website": "https://trpc.io"
}
]
diff --git a/packages/website/package.json b/packages/website/package.json
index 8f4ee2b74aa0..30e2fdf8fba9 100644
--- a/packages/website/package.json
+++ b/packages/website/package.json
@@ -1,6 +1,6 @@
{
"name": "website",
- "version": "5.50.0",
+ "version": "5.51.0",
"private": true,
"scripts": {
"build": "docusaurus build",
@@ -21,8 +21,8 @@
"@docusaurus/remark-plugin-npm2yarn": "~2.2.0",
"@docusaurus/theme-common": "~2.2.0",
"@mdx-js/react": "1.6.22",
- "@typescript-eslint/parser": "5.50.0",
- "@typescript-eslint/website-eslint": "5.50.0",
+ "@typescript-eslint/parser": "5.51.0",
+ "@typescript-eslint/website-eslint": "5.51.0",
"clsx": "^1.1.1",
"eslint": "*",
"json-schema": "^0.4.0",
@@ -48,7 +48,7 @@
"@types/react": "^18.0.9",
"@types/react-helmet": "^6.1.5",
"@types/react-router-dom": "^5.3.3",
- "@typescript-eslint/eslint-plugin": "5.50.0",
+ "@typescript-eslint/eslint-plugin": "5.51.0",
"copy-webpack-plugin": "^11.0.0",
"eslint-plugin-jsx-a11y": "^6.5.1",
"eslint-plugin-react": "^7.29.4",
diff --git a/packages/website/src/components/ast/serializer/serializer.ts b/packages/website/src/components/ast/serializer/serializer.ts
index 74a94de9a479..f6c75be3302b 100644
--- a/packages/website/src/components/ast/serializer/serializer.ts
+++ b/packages/website/src/components/ast/serializer/serializer.ts
@@ -26,7 +26,7 @@ function getSimpleModel(data: unknown): ASTViewerModelSimple {
value: String(data),
type: 'regexp',
};
- } else if (typeof data === 'undefined' || data === null) {
+ } else if (data == null) {
return {
value: String(data),
type: 'undefined',
diff --git a/tests/jest-resolver.js b/tests/jest-resolver.js
deleted file mode 100644
index 0636e2feee7e..000000000000
--- a/tests/jest-resolver.js
+++ /dev/null
@@ -1,20 +0,0 @@
-/* @ts-check */
-
-// temporary workaround - https://github.com/facebook/jest/issues/9771#issuecomment-871585234
-const resolver = require('enhanced-resolve').create.sync({
- conditionNames: ['require', 'node', 'default'],
- extensions: ['.js', '.json', '.node', '.ts', '.tsx'],
-});
-
-/**
- * @param request {unknown}
- * @param options {{ defaultResolver(...args: unknown[]): unknown, basedir: unknown }}
- * @returns {unknown}
- */
-module.exports = function (request, options) {
- // list global module that must be resolved by defaultResolver here
- if (['fs', 'http', 'path'].includes(request)) {
- return options.defaultResolver(request, options);
- }
- return resolver(options.basedir, request);
-};
diff --git a/tsconfig.eslint.json b/tsconfig.eslint.json
index 70fafc555473..36b912142b9a 100644
--- a/tsconfig.eslint.json
+++ b/tsconfig.eslint.json
@@ -10,7 +10,6 @@
".eslintrc.js",
"jest.config.base.js",
"jest.config.js",
- "jest.preset.js",
- "tests/jest-resolver.js"
+ "jest.preset.js"
]
}
diff --git a/yarn.lock b/yarn.lock
index bbe5aba913b6..228df7635b85 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -3246,6 +3246,13 @@
read-package-json-fast "^2.0.3"
which "^2.0.2"
+"@nrwl/cli@15.5.3":
+ version "15.5.3"
+ resolved "https://registry.yarnpkg.com/@nrwl/cli/-/cli-15.5.3.tgz#13277e5a0e8ba713850bcf13fa76717ea747a2bb"
+ integrity sha512-NWf9CWswvdYM6YzXuweaZPAZ2erMtQrrHZdgFbUGeojZBZ+b4TCGzLWNodZj4yQOa/eTwlyPMYO2LEw9CoapDQ==
+ dependencies:
+ nx "15.5.3"
+
"@nrwl/cli@15.6.3":
version "15.6.3"
resolved "https://registry.npmjs.org/@nrwl/cli/-/cli-15.6.3.tgz#999531d6efb30afc39373bdcbd7e78254a3a3fd3"
@@ -3317,6 +3324,13 @@
tar "6.1.11"
yargs-parser ">=21.0.1"
+"@nrwl/tao@15.5.3":
+ version "15.5.3"
+ resolved "https://registry.yarnpkg.com/@nrwl/tao/-/tao-15.5.3.tgz#08c05715d2ecb108ed8b2c5381b9017cf1448b4a"
+ integrity sha512-vgPLIW9IoBfQ4IkHRT5RC4LqNwFBK5jmHYmFIRgbIeFRudFBbnpmOaKRME0OwN7qJ6964PVVbzahAPvYVD02xw==
+ dependencies:
+ nx "15.5.3"
+
"@nrwl/tao@15.6.3":
version "15.6.3"
resolved "https://registry.npmjs.org/@nrwl/tao/-/tao-15.6.3.tgz#b24e11345375dea96bc386c60b9b1102a7584932"
@@ -6714,7 +6728,7 @@ end-of-stream@^1.1.0, end-of-stream@^1.4.1:
dependencies:
once "^1.4.0"
-enhanced-resolve@^5.10.0, enhanced-resolve@^5.9.3:
+enhanced-resolve@^5.10.0:
version "5.10.0"
resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.10.0.tgz#0dc579c3bb2a1032e357ac45b8f3a6f3ad4fb1e6"
integrity sha512-T0yTFjdpldGY8PmuXXR0PyQ1ufZpEGiHVrp7zHKB7jdR4qlmZHhONVM5AQOAWXuF/w3dnHbEQVrNptJgt7F+cQ==
@@ -10780,13 +10794,13 @@ nth-check@^2.0.0, nth-check@^2.0.1:
dependencies:
boolbase "^1.0.0"
-nx@15.6.3:
- version "15.6.3"
- resolved "https://registry.npmjs.org/nx/-/nx-15.6.3.tgz#900087bce38c6e5975660c23ebd41ead1bf54f98"
- integrity sha512-3t0A0GPLNen1yPAyE+VGZ3nkAzZYb5nfXtAcx8SHBlKq4u42yBY3khBmP1y4Og3jhIwFIj7J7Npeh8ZKrthmYQ==
+nx@15.5.3, "nx@>=15.4.2 < 16":
+ version "15.5.3"
+ resolved "https://registry.npmjs.org/nx/-/nx-15.5.3.tgz#bf6252e7d9e17121dd82dec4f6fce319b9e005fa"
+ integrity sha512-PHB8VbiBLP108xb+yR8IGEsYWr7OcmDDOjHL+73oP4lVjyPgT8wdTMe6tI5LdBgv+KZ+0kiThK3ckvcPsfgvLQ==
dependencies:
- "@nrwl/cli" "15.6.3"
- "@nrwl/tao" "15.6.3"
+ "@nrwl/cli" "15.5.3"
+ "@nrwl/tao" "15.5.3"
"@parcel/watcher" "2.0.4"
"@yarnpkg/lockfile" "^1.1.0"
"@yarnpkg/parsers" "^3.0.0-rc.18"
@@ -10821,13 +10835,13 @@ nx@15.6.3:
yargs "^17.6.2"
yargs-parser "21.1.1"
-"nx@>=15.4.2 < 16":
- version "15.5.3"
- resolved "https://registry.npmjs.org/nx/-/nx-15.5.3.tgz#bf6252e7d9e17121dd82dec4f6fce319b9e005fa"
- integrity sha512-PHB8VbiBLP108xb+yR8IGEsYWr7OcmDDOjHL+73oP4lVjyPgT8wdTMe6tI5LdBgv+KZ+0kiThK3ckvcPsfgvLQ==
+nx@15.6.3:
+ version "15.6.3"
+ resolved "https://registry.npmjs.org/nx/-/nx-15.6.3.tgz#900087bce38c6e5975660c23ebd41ead1bf54f98"
+ integrity sha512-3t0A0GPLNen1yPAyE+VGZ3nkAzZYb5nfXtAcx8SHBlKq4u42yBY3khBmP1y4Og3jhIwFIj7J7Npeh8ZKrthmYQ==
dependencies:
- "@nrwl/cli" "15.5.3"
- "@nrwl/tao" "15.5.3"
+ "@nrwl/cli" "15.6.3"
+ "@nrwl/tao" "15.6.3"
"@parcel/watcher" "2.0.4"
"@yarnpkg/lockfile" "^1.1.0"
"@yarnpkg/parsers" "^3.0.0-rc.18"