From d66cde00ee15c49951071636ccf0b3c4ed8ba831 Mon Sep 17 00:00:00 2001 From: Gareth Jones Date: Sun, 7 Apr 2024 12:31:53 +1200 Subject: [PATCH] [New] support eslint v9 Co-authored-by: Gareth Jones Co-authored-by: michael faith --- .github/workflows/node-4+.yml | 27 ++++++++++ CHANGELOG.md | 1 + package.json | 4 +- tests/files/issue210.config.flat.js | 3 ++ tests/files/just-json-files/eslint.config.js | 28 +++++++++++ tests/src/cli.js | 53 +++++++++++++------- tests/src/rule-tester.js | 46 ++++++++++++++++- tests/src/rules/named.js | 4 +- tests/src/rules/namespace.js | 2 +- tests/src/rules/no-unused-modules.js | 16 +++--- 10 files changed, 152 insertions(+), 32 deletions(-) create mode 100644 tests/files/issue210.config.flat.js create mode 100644 tests/files/just-json-files/eslint.config.js diff --git a/.github/workflows/node-4+.yml b/.github/workflows/node-4+.yml index f2dad098c..323c2ad54 100644 --- a/.github/workflows/node-4+.yml +++ b/.github/workflows/node-4+.yml @@ -36,6 +36,7 @@ jobs: - macos-latest node-version: ${{ fromJson(needs.matrix.outputs.latest) }} eslint: + - 9 - 8 - 7 - 6 @@ -63,34 +64,58 @@ jobs: env: TS_PARSER: 2 exclude: + - node-version: 16 + eslint: 9 + - node-version: 15 + eslint: 9 - node-version: 15 eslint: 8 + - node-version: 14 + eslint: 9 + - node-version: 13 + eslint: 9 - node-version: 13 eslint: 8 + - node-version: 12 + eslint: 9 + - node-version: 11 + eslint: 9 - node-version: 11 eslint: 8 + - node-version: 10 + eslint: 9 - node-version: 10 eslint: 8 + - node-version: 9 + eslint: 9 - node-version: 9 eslint: 8 - node-version: 9 eslint: 7 + - node-version: 8 + eslint: 9 - node-version: 8 eslint: 8 - node-version: 8 eslint: 7 + - node-version: 7 + eslint: 9 - node-version: 7 eslint: 8 - node-version: 7 eslint: 7 - node-version: 7 eslint: 6 + - node-version: 6 + eslint: 9 - node-version: 6 eslint: 8 - node-version: 6 eslint: 7 - node-version: 6 eslint: 6 + - node-version: 5 + eslint: 9 - node-version: 5 eslint: 8 - node-version: 5 @@ -99,6 +124,8 @@ jobs: eslint: 6 - node-version: 5 eslint: 5 + - node-version: 4 + eslint: 9 - node-version: 4 eslint: 8 - node-version: 4 diff --git a/CHANGELOG.md b/CHANGELOG.md index 9865e3b2b..795fa5bc4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ This change log adheres to standards from [Keep a CHANGELOG](https://keepachange ## [Unreleased] ### Added +- support eslint v9 ([#2996], thanks [@G-Rath] [@michaelfaith]) - [`order`]: allow validating named imports ([#3043], thanks [@manuth]) ### Fixed diff --git a/package.json b/package.json index c0a6b56e4..53997893a 100644 --- a/package.json +++ b/package.json @@ -76,7 +76,7 @@ "chai": "^4.3.10", "cross-env": "^4.0.0", "escope": "^3.6.0", - "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8", + "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9", "eslint-doc-generator": "^1.6.1", "eslint-import-resolver-node": "file:./resolvers/node", "eslint-import-resolver-typescript": "^1.0.2 || ^1.1.1", @@ -106,7 +106,7 @@ "typescript-eslint-parser": "^15 || ^20 || ^22" }, "peerDependencies": { - "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8" + "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9" }, "dependencies": { "@rtsao/scc": "^1.1.0", diff --git a/tests/files/issue210.config.flat.js b/tests/files/issue210.config.flat.js new file mode 100644 index 000000000..c894376f4 --- /dev/null +++ b/tests/files/issue210.config.flat.js @@ -0,0 +1,3 @@ +exports.languageOptions = { + sourceType: 'module', +} diff --git a/tests/files/just-json-files/eslint.config.js b/tests/files/just-json-files/eslint.config.js new file mode 100644 index 000000000..b1bf2070b --- /dev/null +++ b/tests/files/just-json-files/eslint.config.js @@ -0,0 +1,28 @@ +var jsonPlugin = require('eslint-plugin-json'); + +if (!jsonPlugin.processors.json) { + jsonPlugin.processors.json = jsonPlugin.processors['.json']; +} + +module.exports = [ + { + files: ['tests/files/just-json-files/*.json'], + plugins:{ + json: jsonPlugin, + }, + processor: 'json/json', + rules: Object.assign( + {}, + { + 'import/no-unused-modules': [ + 'error', + { + 'missingExports': false, + 'unusedExports': true, + }, + ], + }, + jsonPlugin.configs.recommended.rules + ) + }, +]; diff --git a/tests/src/cli.js b/tests/src/cli.js index 8a7345487..60b8382d0 100644 --- a/tests/src/cli.js +++ b/tests/src/cli.js @@ -15,17 +15,29 @@ describe('CLI regression tests', function () { let cli; before(function () { if (ESLint) { - eslint = new ESLint({ - useEslintrc: false, - overrideConfigFile: './tests/files/issue210.config.js', - rulePaths: ['./src/rules'], - overrideConfig: { - rules: { - named: 2, + if (semver.satisfies(eslintPkg.version, '>= 9')) { + eslint = new ESLint({ + overrideConfigFile: './tests/files/issue210.config.flat.js', + overrideConfig: { + rules: { + 'import/named': 2, + }, }, - }, - plugins: { 'eslint-plugin-import': importPlugin }, - }); + plugins: { 'eslint-plugin-import': importPlugin }, + }); + } else { + eslint = new ESLint({ + useEslintrc: false, + overrideConfigFile: './tests/files/issue210.config.js', + rulePaths: ['./src/rules'], + overrideConfig: { + rules: { + named: 2, + }, + }, + plugins: { 'eslint-plugin-import': importPlugin }, + }); + } } else { cli = new CLIEngine({ useEslintrc: false, @@ -56,13 +68,20 @@ describe('CLI regression tests', function () { this.skip(); } else { if (ESLint) { - eslint = new ESLint({ - useEslintrc: false, - overrideConfigFile: './tests/files/just-json-files/.eslintrc.json', - rulePaths: ['./src/rules'], - ignore: false, - plugins: { 'eslint-plugin-import': importPlugin }, - }); + if (semver.satisfies(eslintPkg.version, '>= 9')) { + eslint = new ESLint({ + overrideConfigFile: './tests/files/just-json-files/eslint.config.js', + plugins: { 'eslint-plugin-import': importPlugin }, + }); + } else { + eslint = new ESLint({ + useEslintrc: false, + overrideConfigFile: './tests/files/just-json-files/.eslintrc.json', + rulePaths: ['./src/rules'], + ignore: false, + plugins: { 'eslint-plugin-import': importPlugin }, + }); + } } else { cli = new CLIEngine({ useEslintrc: false, diff --git a/tests/src/rule-tester.js b/tests/src/rule-tester.js index f00b520d0..390c6cd7f 100644 --- a/tests/src/rule-tester.js +++ b/tests/src/rule-tester.js @@ -1,5 +1,47 @@ +import { RuleTester } from 'eslint'; +import { version as eslintVersion } from 'eslint/package.json'; +import semver from 'semver'; + +export const usingFlatConfig = semver.major(eslintVersion) >= 9; + export function withoutAutofixOutput(test) { - return { ...test, output: test.code }; + return { ...test, ...usingFlatConfig || { output: test.code } }; +} + +class FlatCompatRuleTester extends RuleTester { + constructor(testerConfig = { parserOptions: { sourceType: 'script' } }) { + super(FlatCompatRuleTester._flatCompat(testerConfig)); + } + + run(ruleName, rule, tests) { + super.run(ruleName, rule, { + valid: tests.valid.map((t) => FlatCompatRuleTester._flatCompat(t)), + invalid: tests.invalid.map((t) => FlatCompatRuleTester._flatCompat(t)), + }); + } + + static _flatCompat(config) { + if (!config || !usingFlatConfig || typeof config !== 'object') { + return config; + } + + const { parser, parserOptions = {}, languageOptions = {}, ...remainingConfig } = config; + const { ecmaVersion, sourceType, ...remainingParserOptions } = parserOptions; + const parserObj = typeof parser === 'string' ? require(parser) : parser; + + return { + ...remainingConfig, + languageOptions: { + ...languageOptions, + ...parserObj ? { parser: parserObj } : {}, + ...ecmaVersion ? { ecmaVersion } : {}, + ...sourceType ? { sourceType } : {}, + parserOptions: { + ...remainingParserOptions, + }, + }, + }; + } } -export { RuleTester } from 'eslint'; +export { FlatCompatRuleTester as RuleTester }; diff --git a/tests/src/rules/named.js b/tests/src/rules/named.js index f506caeb6..51a76c129 100644 --- a/tests/src/rules/named.js +++ b/tests/src/rules/named.js @@ -1,5 +1,5 @@ import { test, SYNTAX_CASES, getTSParsers, testFilePath, testVersion, parsers } from '../utils'; -import { RuleTester } from '../rule-tester'; +import { RuleTester, usingFlatConfig } from '../rule-tester'; import path from 'path'; import { CASE_SENSITIVE_FS } from 'eslint-module-utils/resolve'; @@ -32,7 +32,7 @@ ruleTester.run('named', rule, { settings: { 'import/resolve': { extensions: ['.js', '.jsx'] } } }), // validate that eslint-disable-line silences this properly - test({ code: 'import {a, b, d} from "./common"; // eslint-disable-line named' }), + test({ code: `import {a, b, d} from "./common"; // eslint-disable-line ${usingFlatConfig ? 'rule-to-test/' : ''}named` }), test({ code: 'import { foo, bar } from "./re-export-names"' }), diff --git a/tests/src/rules/namespace.js b/tests/src/rules/namespace.js index 60fcb93f6..2a31d57e1 100644 --- a/tests/src/rules/namespace.js +++ b/tests/src/rules/namespace.js @@ -2,7 +2,7 @@ import { test, SYNTAX_CASES, getTSParsers, testVersion, testFilePath, parsers } import { RuleTester } from '../rule-tester'; import flatMap from 'array.prototype.flatmap'; -const ruleTester = new RuleTester({ env: { es6: true } }); +const ruleTester = new RuleTester({ parserOptions: { ecmaVersion: 6 } }); const rule = require('rules/namespace'); function error(name, namespace) { diff --git a/tests/src/rules/no-unused-modules.js b/tests/src/rules/no-unused-modules.js index 22b54ce6f..d86f40622 100644 --- a/tests/src/rules/no-unused-modules.js +++ b/tests/src/rules/no-unused-modules.js @@ -288,8 +288,8 @@ describe('dynamic imports', function () { // test for unused exports with `import()` ruleTester.run('no-unused-modules', rule, { - valid: [ - test({ + valid: [].concat( + testVersion('< 9', () => ({ options: unusedExportsOptions, code: ` export const a = 10 @@ -300,10 +300,10 @@ describe('dynamic imports', function () { `, parser: parsers.BABEL_OLD, filename: testFilePath('./no-unused-modules/exports-for-dynamic-js.js'), - }), - ], - invalid: [ - test({ + })), + ), + invalid: [].concat( + testVersion('< 9', () => ({ options: unusedExportsOptions, code: ` export const a = 10 @@ -319,8 +319,8 @@ describe('dynamic imports', function () { error(`exported declaration 'b' not used within other modules`), error(`exported declaration 'c' not used within other modules`), error(`exported declaration 'default' not used within other modules`), - ] }), - ], + ] })), + ), }); typescriptRuleTester.run('no-unused-modules', rule, { valid: [