Skip to content

Commit

Permalink
chore: v7 (#901)
Browse files Browse the repository at this point in the history
BREAKING CHANGE: Requires Node@^18.18.0 || ^20.9.0 || >=21.1.0

BREAKING CHANGE: Requires ESLint  `^8.57.0`, or `^9.0.0`

Co-authored-by: Michaël De Boey <[email protected]>
  • Loading branch information
Belco90 and MichaelDeBoey authored Nov 23, 2024
1 parent 3615223 commit 13645b7
Show file tree
Hide file tree
Showing 30 changed files with 665 additions and 587 deletions.
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

0 comments on commit 13645b7

Please sign in to comment.