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

[New] support eslint 9 #2996

Merged
merged 4 commits into from
Sep 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions .github/workflows/node-4+.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ jobs:
- macos-latest
node-version: ${{ fromJson(needs.matrix.outputs.latest) }}
eslint:
- 9
- 8
- 7
- 6
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand Down
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -15,6 +16,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])
Expand Down Expand Up @@ -1165,6 +1167,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
Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -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",
Expand Down
12 changes: 12 additions & 0 deletions src/core/sourceType.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/**
* @param {import('eslint').Rule.RuleContext} context
* @returns 'module' | 'script' | 'commonjs' | undefined
*/
export default function sourceType(context) {
if ('sourceType' in context.parserOptions) {
return context.parserOptions.sourceType;
}
if ('languageOptions' in context && context.languageOptions) {
return context.languageOptions.sourceType;
}
}
3 changes: 2 additions & 1 deletion src/rules/no-default-export.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { getSourceCode } from 'eslint-module-utils/contextCompat';

import docsUrl from '../docsUrl';
import sourceType from '../core/sourceType';

module.exports = {
meta: {
Expand All @@ -15,7 +16,7 @@ module.exports = {

create(context) {
// ignore non-modules
if (context.parserOptions.sourceType !== 'module') {
if (sourceType(context) !== 'module') {
return {};
}

Expand Down
3 changes: 2 additions & 1 deletion src/rules/no-named-export.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import sourceType from '../core/sourceType';
import docsUrl from '../docsUrl';

module.exports = {
Expand All @@ -13,7 +14,7 @@ module.exports = {

create(context) {
// ignore non-modules
if (context.parserOptions.sourceType !== 'module') {
if (sourceType(context) !== 'module') {
return {};
}

Expand Down
3 changes: 2 additions & 1 deletion src/rules/unambiguous.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

import { isModule } from 'eslint-module-utils/unambiguous';
import docsUrl from '../docsUrl';
import sourceType from '../core/sourceType';

module.exports = {
meta: {
Expand All @@ -19,7 +20,7 @@ module.exports = {

create(context) {
// ignore non-modules
if (context.parserOptions.sourceType !== 'module') {
if (sourceType(context) !== 'module') {
return {};
}

Expand Down
3 changes: 3 additions & 0 deletions tests/files/issue210.config.flat.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
exports.languageOptions = {
sourceType: 'module',
}
28 changes: 28 additions & 0 deletions tests/files/just-json-files/eslint.config.js
Original file line number Diff line number Diff line change
@@ -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
)
},
];
53 changes: 36 additions & 17 deletions tests/src/cli.js
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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')) {
G-Rath marked this conversation as resolved.
Show resolved Hide resolved
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,
Expand Down
46 changes: 44 additions & 2 deletions tests/src/rule-tester.js
Original file line number Diff line number Diff line change
@@ -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 {
constructor(testerConfig = { parserOptions: { sourceType: 'script' } }) {
this._tester = new RuleTester(FlatCompatRuleTester._flatCompat(testerConfig));
}

run(ruleName, rule, tests) {
this._tester.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 };
4 changes: 2 additions & 2 deletions tests/src/rules/named.js
Original file line number Diff line number Diff line change
@@ -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';
Expand Down Expand Up @@ -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"' }),

Expand Down
2 changes: 1 addition & 1 deletion tests/src/rules/namespace.js
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
16 changes: 8 additions & 8 deletions tests/src/rules/no-unused-modules.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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: [
Expand Down