Skip to content

Commit 1dbb323

Browse files
authored
feat: use eslint-compat-utils to support eslint v9 (#62)
not flat config yet
1 parent e6774db commit 1dbb323

39 files changed

+198
-107
lines changed

.changeset/nine-pans-press.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"eslint-plugin-import-x": patch
3+
---
4+
5+
feat: use eslint-compat-utils to support eslint v9 (not flat config yet)

package.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -45,12 +45,13 @@
4545
"watch": "yarn test --watch"
4646
},
4747
"peerDependencies": {
48-
"eslint": "^7.2.0 || ^8"
48+
"eslint": "^7.2.0 || ^8 || ^9.0.0-0"
4949
},
5050
"dependencies": {
5151
"@typescript-eslint/utils": "^5.62.0",
5252
"debug": "^4.3.4",
5353
"doctrine": "^3.0.0",
54+
"eslint-compat-utils": "^0.5.0",
5455
"eslint-import-resolver-node": "^0.3.9",
5556
"get-tsconfig": "^4.7.3",
5657
"is-glob": "^4.0.3",
+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
diff --git a/node_modules/eslint-compat-utils/dist/index.d.ts b/node_modules/eslint-compat-utils/dist/index.d.ts
2+
index 993098a..ccb61ad 100644
3+
--- a/node_modules/eslint-compat-utils/dist/index.d.ts
4+
+++ b/node_modules/eslint-compat-utils/dist/index.d.ts
5+
@@ -1,21 +1,23 @@
6+
-import { Rule, SourceCode } from 'eslint';
7+
+import { TSESLint } from '@typescript-eslint/utils'
8+
+
9+
+type RuleContext = TSESLint.RuleContext<string, readonly unknown[]>
10+
11+
/**
12+
* Returns an extended instance of `context.sourceCode` or the result of `context.getSourceCode()`.
13+
* Extended instances can use new APIs such as `getScope(node)` even with old ESLint.
14+
*/
15+
-declare function getSourceCode(context: Rule.RuleContext): SourceCode;
16+
+declare function getSourceCode(context: RuleContext): TSESLint.SourceCode;
17+
18+
/**
19+
* Gets the value of `context.cwd`, but for older ESLint it returns the result of `context.getCwd()`.
20+
* Versions older than v6.6.0 return a value from the result of `process.cwd()`.
21+
*/
22+
-declare function getCwd(context: Rule.RuleContext): string;
23+
+declare function getCwd(context: RuleContext): string;
24+
25+
/**
26+
* Gets the value of `context.filename`, but for older ESLint it returns the result of `context.getFilename()`.
27+
*/
28+
-declare function getFilename(context: Rule.RuleContext): string;
29+
+declare function getFilename(context: RuleContext): string;
30+
31+
/**
32+
* Gets the value of `context.physicalFilename`,
33+
@@ -23,6 +25,6 @@ declare function getFilename(context: Rule.RuleContext): string;
34+
* Versions older than v7.28.0 return a value guessed from the result of `context.getFilename()`,
35+
* but it may be incorrect.
36+
*/
37+
-declare function getPhysicalFilename(context: Rule.RuleContext): string;
38+
+declare function getPhysicalFilename(context: RuleContext): string;
39+
40+
export { getCwd, getFilename, getPhysicalFilename, getSourceCode };

src/rules/consistent-type-specifier-style.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import type { TSESLint, TSESTree } from '@typescript-eslint/utils'
2+
import { getSourceCode } from 'eslint-compat-utils'
23

34
import { createRule } from '../utils'
45

@@ -73,7 +74,7 @@ export = createRule<[Options?], MessageId>({
7374
},
7475
defaultOptions: [],
7576
create(context) {
76-
const sourceCode = context.getSourceCode()
77+
const sourceCode = getSourceCode(context)
7778

7879
if (context.options[0] === 'prefer-inline') {
7980
return {

src/rules/dynamic-import-chunkname.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import vm from 'node:vm'
22

33
import type { TSESTree } from '@typescript-eslint/utils'
4+
import { getSourceCode } from 'eslint-compat-utils'
45

56
import { createRule } from '../utils'
67

@@ -74,7 +75,7 @@ export = createRule<[Options?], MessageId>({
7475
const chunkSubstrRegex = new RegExp(chunkSubstrFormat)
7576

7677
function run(node: TSESTree.Node, arg: TSESTree.Node) {
77-
const sourceCode = context.getSourceCode()
78+
const sourceCode = getSourceCode(context)
7879
const leadingComments = sourceCode.getCommentsBefore(arg)
7980

8081
if ((!leadingComments || leadingComments.length === 0) && !allowEmpty) {

src/rules/first.ts

+9-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import type { TSESLint, TSESTree } from '@typescript-eslint/utils'
2+
import { getSourceCode } from 'eslint-compat-utils'
23

4+
import type { RuleContext } from '../types'
35
import { createRule } from '../utils'
46

57
function getImportValue(node: TSESTree.ProgramStatement) {
@@ -51,7 +53,7 @@ export = createRule<['absolute-first'?], MessageId>({
5153
}
5254

5355
const absoluteFirst = context.options[0] === 'absolute-first'
54-
const sourceCode = context.getSourceCode()
56+
const sourceCode = getSourceCode(context)
5557
const originSourceCode = sourceCode.getText()
5658

5759
let nonImportCount = 0
@@ -95,7 +97,12 @@ export = createRule<['absolute-first'?], MessageId>({
9597
}
9698

9799
if (nonImportCount > 0) {
98-
for (const variable of context.getDeclaredVariables(node)) {
100+
/**
101+
* @see https://eslint.org/docs/next/use/migrate-to-9.0.0#-removed-multiple-context-methods
102+
*/
103+
for (const variable of (
104+
sourceCode as unknown as RuleContext
105+
).getDeclaredVariables(node)) {
99106
if (!shouldSort) {
100107
break
101108
}

src/rules/named.ts

+3-6
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import path from 'node:path'
22

33
import type { TSESTree } from '@typescript-eslint/utils'
4+
import { getFilename, getPhysicalFilename } from 'eslint-compat-utils'
45

56
import { ExportMap, createRule } from '../utils'
67
import type { ModuleOptions } from '../utils'
@@ -97,11 +98,7 @@ export = createRule<[ModuleOptions?], MessageId>({
9798
const deepPath = deepLookup.path
9899
.map(i =>
99100
path.relative(
100-
path.dirname(
101-
context.getPhysicalFilename
102-
? context.getPhysicalFilename()
103-
: context.getFilename(),
104-
),
101+
path.dirname(getPhysicalFilename(context)),
105102
i.path,
106103
),
107104
)
@@ -200,7 +197,7 @@ export = createRule<[ModuleOptions?], MessageId>({
200197
if (deepLookup.path.length > 1) {
201198
const deepPath = deepLookup.path
202199
.map(i =>
203-
path.relative(path.dirname(context.getFilename()), i.path),
200+
path.relative(path.dirname(getFilename(context)), i.path),
204201
)
205202
.join(' -> ')
206203

src/rules/namespace.ts

+9-4
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,7 @@ export = createRule<[Options], MessageId>({
152152

153153
// same as above, but does not add names to local map
154154
ExportNamespaceSpecifier(namespace) {
155-
const declaration = importDeclaration(context)
155+
const declaration = importDeclaration(context, namespace)
156156

157157
const imports = ExportMap.get(declaration.source.value, context)
158158
if (imports == null) {
@@ -186,7 +186,10 @@ export = createRule<[Options], MessageId>({
186186
return
187187
}
188188

189-
if (declaredScope(context, dereference.object.name) !== 'module') {
189+
if (
190+
declaredScope(context, dereference, dereference.object.name) !==
191+
'module'
192+
) {
190193
return
191194
}
192195

@@ -249,7 +252,9 @@ export = createRule<[Options], MessageId>({
249252
}
250253
},
251254

252-
VariableDeclarator({ id, init }) {
255+
VariableDeclarator(node) {
256+
const { id, init } = node
257+
253258
if (init == null) {
254259
return
255260
}
@@ -261,7 +266,7 @@ export = createRule<[Options], MessageId>({
261266
}
262267

263268
// check for redefinition in intermediate scopes
264-
if (declaredScope(context, init.name) !== 'module') {
269+
if (declaredScope(context, node, init.name) !== 'module') {
265270
return
266271
}
267272

src/rules/newline-after-import.ts

+5-9
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,9 @@
44

55
import type { TSESLint, TSESTree } from '@typescript-eslint/utils'
66
import debug from 'debug'
7+
import { getPhysicalFilename } from 'eslint-compat-utils'
78

8-
import { isStaticRequire, createRule } from '../utils'
9+
import { isStaticRequire, createRule, getScope } from '../utils'
910

1011
const log = debug('eslint-plugin-import-x:rules:newline-after-import')
1112

@@ -267,14 +268,9 @@ export = createRule<[Options?], MessageId>({
267268
requireCalls.push(node)
268269
}
269270
},
270-
'Program:exit'() {
271-
log(
272-
'exit processing for',
273-
context.getPhysicalFilename
274-
? context.getPhysicalFilename()
275-
: context.getFilename(),
276-
)
277-
const scopeBody = getScopeBody(context.getScope())
271+
'Program:exit'(node) {
272+
log('exit processing for', getPhysicalFilename(context))
273+
const scopeBody = getScopeBody(getScope(context, node))
278274

279275
log('got scope:', scopeBody)
280276

src/rules/no-absolute-path.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import path from 'node:path'
22

3+
import { getPhysicalFilename } from 'eslint-compat-utils'
4+
35
import {
46
isAbsolute,
57
createRule,
@@ -35,9 +37,7 @@ export = createRule<[ModuleOptions?], MessageId>({
3537
node: source,
3638
messageId: 'absolute',
3739
fix(fixer) {
38-
const resolvedContext = context.getPhysicalFilename
39-
? context.getPhysicalFilename()
40-
: context.getFilename()
40+
const resolvedContext = getPhysicalFilename(context)
4141
// node.js and web imports work with posix style paths ("/")
4242
let relativePath = path.posix.relative(
4343
path.dirname(resolvedContext),

src/rules/no-amd.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
* Rule to prefer imports to AMD
33
*/
44

5-
import { createRule } from '../utils'
5+
import { createRule, getScope } from '../utils'
66

77
type MessageId = 'amd'
88

@@ -23,7 +23,7 @@ export = createRule<[], MessageId>({
2323
create(context) {
2424
return {
2525
CallExpression(node) {
26-
if (context.getScope().type !== 'module') {
26+
if (getScope(context, node).type !== 'module') {
2727
return
2828
}
2929

src/rules/no-commonjs.ts

+5-5
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
import type { TSESLint, TSESTree } from '@typescript-eslint/utils'
66

7-
import { createRule } from '../utils'
7+
import { createRule, getScope } from '../utils'
88

99
type NormalizedOptions = {
1010
allowPrimitiveModules?: boolean
@@ -122,16 +122,16 @@ export = createRule<[Options?], MessageId>({
122122

123123
// exports.
124124
if ('name' in node.object && node.object.name === 'exports') {
125-
const isInScope = context
126-
.getScope()
127-
.variables.some(variable => variable.name === 'exports')
125+
const isInScope = getScope(context, node).variables.some(
126+
variable => variable.name === 'exports',
127+
)
128128
if (!isInScope) {
129129
context.report({ node, messageId: 'export' })
130130
}
131131
}
132132
},
133133
CallExpression(call) {
134-
if (!validateScope(context.getScope())) {
134+
if (!validateScope(getScope(context, call))) {
135135
return
136136
}
137137

src/rules/no-cycle.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
* Ensures that no imported module imports the linted module.
33
*/
44

5+
import { getPhysicalFilename } from 'eslint-compat-utils'
6+
57
import type { DeclarationMetadata, ModuleOptions } from '../utils'
68
import {
79
ExportMap,
@@ -70,9 +72,7 @@ export = createRule<[Options?], MessageId>({
7072
},
7173
defaultOptions: [],
7274
create(context) {
73-
const filename = context.getPhysicalFilename
74-
? context.getPhysicalFilename()
75-
: context.getFilename()
75+
const filename = getPhysicalFilename(context)
7676

7777
if (filename === '<text>') {
7878
return {}

src/rules/no-default-export.ts

+4-2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import { getSourceCode } from 'eslint-compat-utils'
2+
13
import { createRule } from '../utils'
24

35
export = createRule({
@@ -24,7 +26,7 @@ export = createRule({
2426

2527
return {
2628
ExportDefaultDeclaration(node) {
27-
const { loc } = context.getSourceCode().getFirstTokens(node)[1] || {}
29+
const { loc } = getSourceCode(context).getFirstTokens(node)[1] || {}
2830
context.report({
2931
node,
3032
messageId: 'preferNamed',
@@ -39,7 +41,7 @@ export = createRule({
3941
('value' in specifier.exported && specifier.exported.value)) ===
4042
'default',
4143
)) {
42-
const { loc } = context.getSourceCode().getFirstTokens(node)[1] || {}
44+
const { loc } = getSourceCode(context).getFirstTokens(node)[1] || {}
4345
// @ts-expect-error - legacy parser type
4446
if (specifier.type === 'ExportDefaultSpecifier') {
4547
context.report({

src/rules/no-deprecated.ts

+5-2
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@ export = createRule({
148148
return
149149
}
150150

151-
if (declaredScope(context, node.name) !== 'module') {
151+
if (declaredScope(context, node, node.name) !== 'module') {
152152
return
153153
}
154154
context.report({
@@ -165,7 +165,10 @@ export = createRule({
165165
return
166166
}
167167

168-
if (declaredScope(context, dereference.object.name) !== 'module') {
168+
if (
169+
declaredScope(context, dereference, dereference.object.name) !==
170+
'module'
171+
) {
169172
return
170173
}
171174

src/rules/no-duplicates.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import type { TSESLint, TSESTree } from '@typescript-eslint/utils'
2+
import { getSourceCode } from 'eslint-compat-utils'
23
import semver from 'semver'
34
import type { PackageJson } from 'type-fest'
45

@@ -28,7 +29,7 @@ function checkImports(
2829
for (const [module, nodes] of imported.entries()) {
2930
if (nodes.length > 1) {
3031
const [first, ...rest] = nodes
31-
const sourceCode = context.getSourceCode()
32+
const sourceCode = getSourceCode(context)
3233
const fix = getFix(first, rest, sourceCode, context)
3334

3435
context.report({

src/rules/no-empty-named-blocks.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import type { TSESTree } from '@typescript-eslint/utils'
2+
import { getSourceCode } from 'eslint-compat-utils'
23

34
import { createRule } from '../utils'
45

@@ -103,7 +104,7 @@ export = createRule({
103104
fix(fixer) {
104105
// Remove the empty block and the 'from' token, leaving the import only for its side
105106
// effects, e.g. `import 'mod'`
106-
const sourceCode = context.getSourceCode()
107+
const sourceCode = getSourceCode(context)
107108
const fromToken = pTokens.find(t => t.value === 'from')!
108109
const importToken = pTokens.find(
109110
t => t.value === 'import',

0 commit comments

Comments
 (0)