Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

chore: v7 #901

Merged
merged 11 commits into from
Nov 23, 2024
15 changes: 2 additions & 13 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,6 @@ module.exports = {
rules: {
// Base
'max-lines-per-function': 'off',
'no-restricted-imports': [
'error',
{
patterns: [
{
group: ['@typescript-eslint/utils/dist/*'],
message: 'Import from `@typescript-eslint/utils` instead.',
},
],
},
],

// Import
'import/order': [
Expand All @@ -51,12 +40,12 @@ module.exports = {
files: ['**/*.ts?(x)'],
parser: '@typescript-eslint/parser',
parserOptions: {
project: './tsconfig.eslint.json',
tsconfigRootDir: __dirname,
project: ['./tsconfig.eslint.json'],
},
extends: [
'plugin:@typescript-eslint/recommended',
'plugin:@typescript-eslint/recommended-requiring-type-checking',
'plugin:@typescript-eslint/recommended-type-checked',
'plugin:import/typescript',
],
rules: {
Expand Down
18 changes: 2 additions & 16 deletions .github/workflows/verifications.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,22 +35,8 @@ jobs:
strategy:
fail-fast: false
matrix:
node: [12.22.0, 12, 14.17.0, 14, 16, 17, 18, 19, 20, 22]
eslint: [7.5, 7, 8, 9]
exclude:
# eslint@9 doesn't support < Node v18
- node: 17
eslint: 9
- node: 16
eslint: 9
- node: 14
eslint: 9
- node: 14.17.0
eslint: 9
- node: 12
eslint: 9
- node: 12.22.0
eslint: 9
node: [18.18.0, 18, 20.9.0, 20, 21.1.0, 21, 22, 23]
eslint: [8.57.0, 8, 9]
steps:
- name: Checkout
uses: actions/checkout@v4
Expand Down
15 changes: 8 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,19 +22,19 @@
[![PRs Welcome][pr-badge]][pr-url]
[![All Contributors][all-contributors-badge]](#contributors-)

## Installation
## Prerequisites

You'll first need to install [ESLint](https://eslint.org):
To use this plugin, you must have [Node.js](https://nodejs.org/en/) (`^18.18.0`, `^20.9.0`, or `>=21.1.0`) installed.

```shell
$ npm install --save-dev eslint
# or
$ yarn add --dev eslint
```
## Installation

You'll first need to [install ESLint](https://eslint.org/docs/latest/use/getting-started).

Next, install `eslint-plugin-testing-library`:

```shell
$ pnpm add --save-dev eslint-plugin-testing-library
# or
$ npm install --save-dev eslint-plugin-testing-library
# or
$ yarn add --dev eslint-plugin-testing-library
Expand All @@ -49,6 +49,7 @@ You can find detailed guides for migrating `eslint-plugin-testing-library` in th
- [Migration guide for v4](docs/migration-guides/v4.md)
- [Migration guide for v5](docs/migration-guides/v5.md)
- [Migration guide for v6](docs/migration-guides/v6.md)
- [Migration guide for v7](docs/migration-guides/v7.md)

## Usage

Expand Down
14 changes: 14 additions & 0 deletions docs/migration-guides/v7.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Guide: migrating to v7

If you are not on v6 yet, we recommend first following the [v6 migration guide](docs/migration-guides/v6.md).

## Overview

- **(Breaking)** Supported versions of Node.js have been updated to `^18.18.0`, `^20.9.0`, or `>=21.1.0`, matching ESLint.
- **(Breaking)** Supported versions of ESLint have been updated to `^8.57.0`, or `^9.0.0`.
- Full support for ESLint v9 (v8 still compatible) and typescript-eslint v8

## Steps to upgrade

1. Make sure you are using a supported version of Node.js, and upgrade if not.
2. Make sure you are using a supported version of ESLint, and upgrade if not.
8 changes: 3 additions & 5 deletions lib/configs/index.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,22 @@
import { join } from 'path';

import { type TSESLint } from '@typescript-eslint/utils';
import type { TSESLint } from '@typescript-eslint/utils';

import {
importDefault,
SUPPORTED_TESTING_FRAMEWORKS,
SupportedTestingFramework,
} from '../utils';

export type LinterConfigRules = Pick<Required<TSESLint.Linter.Config>, 'rules'>;

const configsDir = __dirname;

const getConfigForFramework = (framework: SupportedTestingFramework) =>
importDefault<LinterConfigRules>(join(configsDir, framework));
importDefault<TSESLint.SharedConfig.RulesRecord>(join(configsDir, framework));

export default SUPPORTED_TESTING_FRAMEWORKS.reduce(
(allConfigs, framework) => ({
...allConfigs,
[framework]: getConfigForFramework(framework),
}),
{}
) as Record<SupportedTestingFramework, LinterConfigRules>;
) as Record<SupportedTestingFramework, TSESLint.SharedConfig.RulesRecord>;
23 changes: 12 additions & 11 deletions lib/create-testing-library-rule/detect-testing-library-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,32 +25,33 @@ import {
PRESENCE_MATCHERS,
} from '../utils';

const SETTING_OPTION_OFF = 'off' as const;
const SETTING_OPTION_OFF = 'off';

export type TestingLibrarySettings = {
'testing-library/utils-module'?: string | typeof SETTING_OPTION_OFF;
'testing-library/utils-module'?:
| typeof SETTING_OPTION_OFF
| (string & NonNullable<unknown>);
'testing-library/custom-renders'?: string[] | typeof SETTING_OPTION_OFF;
'testing-library/custom-queries'?: string[] | typeof SETTING_OPTION_OFF;
};

export type TestingLibraryContext<
TOptions extends readonly unknown[],
TMessageIds extends string,
TOptions extends readonly unknown[],
> = Readonly<
TSESLint.RuleContext<TMessageIds, TOptions> & {
settings: TestingLibrarySettings;
}
>;

export type EnhancedRuleCreate<
TOptions extends readonly unknown[],
TMessageIds extends string,
TRuleListener extends TSESLint.RuleListener = TSESLint.RuleListener,
TOptions extends readonly unknown[],
> = (
context: TestingLibraryContext<TOptions, TMessageIds>,
context: TestingLibraryContext<TMessageIds, TOptions>,
optionsWithDefault: Readonly<TOptions>,
detectionHelpers: Readonly<DetectionHelpers>
) => TRuleListener;
) => TSESLint.RuleListener;

// Helpers methods
type GetTestingLibraryImportNodeFn = () => ImportModuleNode | null;
Expand Down Expand Up @@ -154,15 +155,14 @@ export type DetectionOptions = {
* Enhances a given rule `create` with helpers to detect Testing Library utils.
*/
export function detectTestingLibraryUtils<
TOptions extends readonly unknown[],
TMessageIds extends string,
TRuleListener extends TSESLint.RuleListener = TSESLint.RuleListener,
TOptions extends readonly unknown[],
>(
ruleCreate: EnhancedRuleCreate<TOptions, TMessageIds, TRuleListener>,
ruleCreate: EnhancedRuleCreate<TMessageIds, TOptions>,
{ skipRuleReportingCheck = false }: Partial<DetectionOptions> = {}
) {
return (
context: TestingLibraryContext<TOptions, TMessageIds>,
context: TestingLibraryContext<TMessageIds, TOptions>,
optionsWithDefault: Readonly<TOptions>
): TSESLint.RuleListener => {
const importedTestingLibraryNodes: ImportModuleNode[] = [];
Expand Down Expand Up @@ -212,6 +212,7 @@ export function detectTestingLibraryUtils<

const originalNodeName =
isImportSpecifier(importedUtilSpecifier) &&
ASTUtils.isIdentifier(importedUtilSpecifier.imported) &&
importedUtilSpecifier.local.name !== importedUtilSpecifier.imported.name
? importedUtilSpecifier.imported.name
: undefined;
Expand Down
43 changes: 18 additions & 25 deletions lib/create-testing-library-rule/index.ts
Original file line number Diff line number Diff line change
@@ -1,44 +1,37 @@
import { ESLintUtils, TSESLint } from '@typescript-eslint/utils';
import { ESLintUtils } from '@typescript-eslint/utils';

import { getDocsUrl, TestingLibraryRuleMeta } from '../utils';
import { getDocsUrl, TestingLibraryPluginDocs } from '../utils';

import {
DetectionOptions,
detectTestingLibraryUtils,
EnhancedRuleCreate,
} from './detect-testing-library-utils';

export function createTestingLibraryRule<
export const createTestingLibraryRule = <
TOptions extends readonly unknown[],
TMessageIds extends string,
TRuleListener extends TSESLint.RuleListener = TSESLint.RuleListener,
>({
create,
detectionOptions = {},
meta,
...remainingConfig
}: Readonly<{
name: string;
meta: TestingLibraryRuleMeta<TMessageIds, TOptions>;
defaultOptions: Readonly<TOptions>;
detectionOptions?: Partial<DetectionOptions>;
create: EnhancedRuleCreate<TOptions, TMessageIds, TRuleListener>;
}>): TSESLint.RuleModule<TMessageIds, TOptions> {
// eslint-disable-next-line new-cap
return ESLintUtils.RuleCreator(getDocsUrl)({
}: Readonly<
Omit<
ESLintUtils.RuleWithMetaAndName<
TOptions,
TMessageIds,
TestingLibraryPluginDocs<TOptions>
>,
'create'
> & {
create: EnhancedRuleCreate<TMessageIds, TOptions>;
detectionOptions?: Partial<DetectionOptions>;
}
>) =>
ESLintUtils.RuleCreator<TestingLibraryPluginDocs<TOptions>>(getDocsUrl)({
...remainingConfig,
create: detectTestingLibraryUtils<TOptions, TMessageIds, TRuleListener>(
create: detectTestingLibraryUtils<TMessageIds, TOptions>(
create,
detectionOptions
),
meta: {
...meta,
docs: {
...meta.docs,
// We're using our own recommendedConfig meta to tell our build tools
// if the rule is recommended on a config basis
recommended: false,
},
},
});
}
8 changes: 4 additions & 4 deletions lib/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { SupportedTestingFramework } from './utils';
const {
name: packageName,
version: packageVersion,
// eslint-disable-next-line @typescript-eslint/no-var-requires
// eslint-disable-next-line @typescript-eslint/no-require-imports
} = require('../package.json') as { name: string; version: string };

const plugin = {
Expand All @@ -20,7 +20,7 @@ const plugin = {
// we don't have types for flat config yet
configs: {} as Record<
SupportedTestingFramework | `flat/${SupportedTestingFramework}`,
Pick<Required<TSESLint.Linter.Config>, 'rules'>
TSESLint.SharedConfig.RulesRecord
>,
rules,
};
Expand All @@ -35,9 +35,9 @@ plugin.configs = {
rules: config.rules,
},
])
) as Record<
) as unknown as Record<
`flat/${SupportedTestingFramework}`,
Pick<Required<TSESLint.Linter.Config>, 'rules'> & { plugins: unknown }
TSESLint.SharedConfig.RulesRecord & { plugins: unknown }
>),
};

Expand Down
9 changes: 5 additions & 4 deletions lib/node-utils/index.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { FunctionScope, ScopeType } from '@typescript-eslint/scope-manager';
import {
AST_NODE_TYPES,
ASTUtils,
TSESLint,
TSESLintScope,
TSESTree,
} from '@typescript-eslint/utils';

Expand Down Expand Up @@ -188,7 +188,7 @@ export function isPromiseAllSettled(node: TSESTree.CallExpression): boolean {
}

/**
* Determines whether a given node belongs to handled Promise.all or Promise.allSettled
* Determines whether a given node belongs to handled `Promise.all` or `Promise.allSettled`
* array expression.
*/
export function isPromisesArrayResolved(node: TSESTree.Node): boolean {
Expand Down Expand Up @@ -295,7 +295,7 @@ export function getVariableReferences(
return [];
}

interface InnermostFunctionScope extends TSESLintScope.FunctionScope {
interface InnermostFunctionScope extends FunctionScope {
block:
| TSESTree.ArrowFunctionExpression
| TSESTree.FunctionDeclaration
Expand All @@ -312,7 +312,7 @@ export function getInnermostFunctionScope(
);

if (
innermostScope.type === 'function' &&
innermostScope.type === ScopeType.function &&
ASTUtils.isFunction(innermostScope.block)
) {
return innermostScope as unknown as InnermostFunctionScope;
Expand Down Expand Up @@ -665,6 +665,7 @@ export function findImportSpecifier(
const namedExport = node.specifiers.find((n) => {
return (
isImportSpecifier(n) &&
ASTUtils.isIdentifier(n.imported) &&
[n.imported.name, n.local.name].includes(specifierName)
);
});
Expand Down
6 changes: 3 additions & 3 deletions lib/rules/await-async-events.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,15 +55,15 @@ export default createTestingLibraryRule<Options, MessageIds>({
default: USER_EVENT_NAME,
oneOf: [
{
enum: EVENTS_SIMULATORS.concat(),
type: 'string',
enum: EVENTS_SIMULATORS,
},
{
type: 'array',
items: {
type: 'string',
enum: EVENTS_SIMULATORS,
enum: EVENTS_SIMULATORS.concat(),
},
type: 'array',
},
],
},
Expand Down
16 changes: 5 additions & 11 deletions lib/rules/index.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,20 @@
import { readdirSync } from 'fs';
import { join, parse } from 'path';

import { TSESLint } from '@typescript-eslint/utils';

import { importDefault, TestingLibraryRuleMeta } from '../utils';

type RuleModule = TSESLint.RuleModule<string, unknown[]> & {
meta: TestingLibraryRuleMeta<string, unknown[]> & {
recommended: false;
};
};
import { importDefault, TestingLibraryPluginRuleModule } from '../utils';

const rulesDir = __dirname;
const excludedFiles = ['index'];

export default readdirSync(rulesDir)
.map((rule) => parse(rule).name)
.filter((ruleName) => !excludedFiles.includes(ruleName))
.reduce<Record<string, RuleModule>>(
.reduce<Record<string, TestingLibraryPluginRuleModule<string, unknown[]>>>(
(allRules, ruleName) => ({
...allRules,
[ruleName]: importDefault<RuleModule>(join(rulesDir, ruleName)),
[ruleName]: importDefault<
TestingLibraryPluginRuleModule<string, unknown[]>
>(join(rulesDir, ruleName)),
}),
{}
);
Loading
Loading