From 1fa8a07a791b2497c8003e63e45f359eb65b1508 Mon Sep 17 00:00:00 2001 From: Jordan Harband Date: Mon, 30 Sep 2024 13:59:51 +0700 Subject: [PATCH 1/4] [Refactor] create `sourceType` helper --- src/core/sourceType.js | 7 +++++++ src/rules/no-default-export.js | 3 ++- src/rules/no-named-export.js | 3 ++- src/rules/unambiguous.js | 3 ++- 4 files changed, 13 insertions(+), 3 deletions(-) create mode 100644 src/core/sourceType.js diff --git a/src/core/sourceType.js b/src/core/sourceType.js new file mode 100644 index 0000000000..4243be4cf9 --- /dev/null +++ b/src/core/sourceType.js @@ -0,0 +1,7 @@ +/** + * @param {import('eslint').Rule.RuleContext} context + * @returns 'module' | 'script' | undefined + */ +export default function sourceType(context) { + return context.parserOptions.sourceType; +} diff --git a/src/rules/no-default-export.js b/src/rules/no-default-export.js index fcb4f1b2fd..d18f0c48f6 100644 --- a/src/rules/no-default-export.js +++ b/src/rules/no-default-export.js @@ -1,6 +1,7 @@ import { getSourceCode } from 'eslint-module-utils/contextCompat'; import docsUrl from '../docsUrl'; +import sourceType from '../core/sourceType'; module.exports = { meta: { @@ -15,7 +16,7 @@ module.exports = { create(context) { // ignore non-modules - if (context.parserOptions.sourceType !== 'module') { + if (sourceType(context) !== 'module') { return {}; } diff --git a/src/rules/no-named-export.js b/src/rules/no-named-export.js index efaf9dc4c8..fc9b2c48d6 100644 --- a/src/rules/no-named-export.js +++ b/src/rules/no-named-export.js @@ -1,3 +1,4 @@ +import sourceType from '../core/sourceType'; import docsUrl from '../docsUrl'; module.exports = { @@ -13,7 +14,7 @@ module.exports = { create(context) { // ignore non-modules - if (context.parserOptions.sourceType !== 'module') { + if (sourceType(context) !== 'module') { return {}; } diff --git a/src/rules/unambiguous.js b/src/rules/unambiguous.js index 91152ea2af..2491fad3eb 100644 --- a/src/rules/unambiguous.js +++ b/src/rules/unambiguous.js @@ -5,6 +5,7 @@ import { isModule } from 'eslint-module-utils/unambiguous'; import docsUrl from '../docsUrl'; +import sourceType from '../core/sourceType'; module.exports = { meta: { @@ -19,7 +20,7 @@ module.exports = { create(context) { // ignore non-modules - if (context.parserOptions.sourceType !== 'module') { + if (sourceType(context) !== 'module') { return {}; } From d27a639f338a77a6a0de87cb40c17d7b81a65b41 Mon Sep 17 00:00:00 2001 From: Gareth Jones Date: Mon, 16 Sep 2024 08:07:30 +1200 Subject: [PATCH 2/4] [Fix] adjust "is source type module" checks for flat config Also see https://github.com/jest-community/eslint-plugin-jest/pull/1639 --- CHANGELOG.md | 2 ++ src/core/sourceType.js | 9 +++++++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8c249f1dff..9865e3b2b6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ This change log adheres to standards from [Keep a CHANGELOG](https://keepachange - [`export`]: False positive for exported overloaded functions in TS ([#3065], thanks [@liuxingbaoyu]) - `exportMap`: export map cache is tainted by unreliable parse results ([#3062], thanks [@michaelfaith]) - `exportMap`: improve cacheKey when using flat config ([#3072], thanks [@michaelfaith]) +- adjust "is source type module" checks for flat config ([#2996], thanks [@G-Rath]) ### Changed - [Docs] [`no-relative-packages`]: fix typo ([#3066], thanks [@joshuaobrien]) @@ -1165,6 +1166,7 @@ for info on changes for earlier releases. [#3011]: https://github.com/import-js/eslint-plugin-import/pull/3011 [#3004]: https://github.com/import-js/eslint-plugin-import/pull/3004 [#2998]: https://github.com/import-js/eslint-plugin-import/pull/2998 +[#2996]: https://github.com/import-js/eslint-plugin-import/pull/2996 [#2993]: https://github.com/import-js/eslint-plugin-import/pull/2993 [#2991]: https://github.com/import-js/eslint-plugin-import/pull/2991 [#2989]: https://github.com/import-js/eslint-plugin-import/pull/2989 diff --git a/src/core/sourceType.js b/src/core/sourceType.js index 4243be4cf9..5ff92edc97 100644 --- a/src/core/sourceType.js +++ b/src/core/sourceType.js @@ -1,7 +1,12 @@ /** * @param {import('eslint').Rule.RuleContext} context - * @returns 'module' | 'script' | undefined + * @returns 'module' | 'script' | 'commonjs' | undefined */ export default function sourceType(context) { - return context.parserOptions.sourceType; + if ('sourceType' in context.parserOptions) { + return context.parserOptions.sourceType; + } + if ('languageOptions' in context && context.languageOptions) { + return context.languageOptions.sourceType; + } } From d66cde00ee15c49951071636ccf0b3c4ed8ba831 Mon Sep 17 00:00:00 2001 From: Gareth Jones Date: Sun, 7 Apr 2024 12:31:53 +1200 Subject: [PATCH 3/4] [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 f2dad098ca..323c2ad540 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 9865e3b2b6..795fa5bc48 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 c0a6b56e4a..53997893aa 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 0000000000..c894376f48 --- /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 0000000000..b1bf2070bb --- /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 8a73454878..60b8382d09 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 f00b520d07..390c6cd7f7 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 f506caeb68..51a76c1290 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 60fcb93f6d..2a31d57e19 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 22b54ce6f4..d86f406220 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: [ From 5a51b9a4ca13cb5fa5cfa349a99999826b5e2aed Mon Sep 17 00:00:00 2001 From: Gareth Jones Date: Mon, 8 Apr 2024 17:11:39 +1200 Subject: [PATCH 4/4] [Tests] `rule-tester`: try this babel class workaround --- tests/src/rule-tester.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/src/rule-tester.js b/tests/src/rule-tester.js index 390c6cd7f7..103f2fd6fe 100644 --- a/tests/src/rule-tester.js +++ b/tests/src/rule-tester.js @@ -8,13 +8,13 @@ export function withoutAutofixOutput(test) { return { ...test, ...usingFlatConfig || { output: test.code } }; } -class FlatCompatRuleTester extends RuleTester { +class FlatCompatRuleTester { constructor(testerConfig = { parserOptions: { sourceType: 'script' } }) { - super(FlatCompatRuleTester._flatCompat(testerConfig)); + this._tester = new RuleTester(FlatCompatRuleTester._flatCompat(testerConfig)); } run(ruleName, rule, tests) { - super.run(ruleName, rule, { + this._tester.run(ruleName, rule, { valid: tests.valid.map((t) => FlatCompatRuleTester._flatCompat(t)), invalid: tests.invalid.map((t) => FlatCompatRuleTester._flatCompat(t)), });