Skip to content

Commit

Permalink
feat: use eslint-compat-utils to support eslint v9 (#62)
Browse files Browse the repository at this point in the history
not flat config yet
  • Loading branch information
JounQin committed Mar 24, 2024
1 parent e6774db commit 1dbb323
Show file tree
Hide file tree
Showing 39 changed files with 198 additions and 107 deletions.
5 changes: 5 additions & 0 deletions .changeset/nine-pans-press.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"eslint-plugin-import-x": patch
---

feat: use eslint-compat-utils to support eslint v9 (not flat config yet)
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,12 +45,13 @@
"watch": "yarn test --watch"
},
"peerDependencies": {
"eslint": "^7.2.0 || ^8"
"eslint": "^7.2.0 || ^8 || ^9.0.0-0"
},
"dependencies": {
"@typescript-eslint/utils": "^5.62.0",
"debug": "^4.3.4",
"doctrine": "^3.0.0",
"eslint-compat-utils": "^0.5.0",
"eslint-import-resolver-node": "^0.3.9",
"get-tsconfig": "^4.7.3",
"is-glob": "^4.0.3",
Expand Down
40 changes: 40 additions & 0 deletions patches/eslint-compat-utils+0.5.0.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
diff --git a/node_modules/eslint-compat-utils/dist/index.d.ts b/node_modules/eslint-compat-utils/dist/index.d.ts
index 993098a..ccb61ad 100644
--- a/node_modules/eslint-compat-utils/dist/index.d.ts
+++ b/node_modules/eslint-compat-utils/dist/index.d.ts
@@ -1,21 +1,23 @@
-import { Rule, SourceCode } from 'eslint';
+import { TSESLint } from '@typescript-eslint/utils'
+
+type RuleContext = TSESLint.RuleContext<string, readonly unknown[]>

/**
* Returns an extended instance of `context.sourceCode` or the result of `context.getSourceCode()`.
* Extended instances can use new APIs such as `getScope(node)` even with old ESLint.
*/
-declare function getSourceCode(context: Rule.RuleContext): SourceCode;
+declare function getSourceCode(context: RuleContext): TSESLint.SourceCode;

/**
* Gets the value of `context.cwd`, but for older ESLint it returns the result of `context.getCwd()`.
* Versions older than v6.6.0 return a value from the result of `process.cwd()`.
*/
-declare function getCwd(context: Rule.RuleContext): string;
+declare function getCwd(context: RuleContext): string;

/**
* Gets the value of `context.filename`, but for older ESLint it returns the result of `context.getFilename()`.
*/
-declare function getFilename(context: Rule.RuleContext): string;
+declare function getFilename(context: RuleContext): string;

/**
* Gets the value of `context.physicalFilename`,
@@ -23,6 +25,6 @@ declare function getFilename(context: Rule.RuleContext): string;
* Versions older than v7.28.0 return a value guessed from the result of `context.getFilename()`,
* but it may be incorrect.
*/
-declare function getPhysicalFilename(context: Rule.RuleContext): string;
+declare function getPhysicalFilename(context: RuleContext): string;

export { getCwd, getFilename, getPhysicalFilename, getSourceCode };
3 changes: 2 additions & 1 deletion src/rules/consistent-type-specifier-style.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type { TSESLint, TSESTree } from '@typescript-eslint/utils'
import { getSourceCode } from 'eslint-compat-utils'

import { createRule } from '../utils'

Expand Down Expand Up @@ -73,7 +74,7 @@ export = createRule<[Options?], MessageId>({
},
defaultOptions: [],
create(context) {
const sourceCode = context.getSourceCode()
const sourceCode = getSourceCode(context)

if (context.options[0] === 'prefer-inline') {
return {
Expand Down
3 changes: 2 additions & 1 deletion src/rules/dynamic-import-chunkname.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import vm from 'node:vm'

import type { TSESTree } from '@typescript-eslint/utils'
import { getSourceCode } from 'eslint-compat-utils'

import { createRule } from '../utils'

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

function run(node: TSESTree.Node, arg: TSESTree.Node) {
const sourceCode = context.getSourceCode()
const sourceCode = getSourceCode(context)
const leadingComments = sourceCode.getCommentsBefore(arg)

if ((!leadingComments || leadingComments.length === 0) && !allowEmpty) {
Expand Down
11 changes: 9 additions & 2 deletions src/rules/first.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import type { TSESLint, TSESTree } from '@typescript-eslint/utils'
import { getSourceCode } from 'eslint-compat-utils'

import type { RuleContext } from '../types'
import { createRule } from '../utils'

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

const absoluteFirst = context.options[0] === 'absolute-first'
const sourceCode = context.getSourceCode()
const sourceCode = getSourceCode(context)
const originSourceCode = sourceCode.getText()

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

if (nonImportCount > 0) {
for (const variable of context.getDeclaredVariables(node)) {
/**
* @see https://eslint.org/docs/next/use/migrate-to-9.0.0#-removed-multiple-context-methods
*/
for (const variable of (
sourceCode as unknown as RuleContext
).getDeclaredVariables(node)) {
if (!shouldSort) {
break
}
Expand Down
9 changes: 3 additions & 6 deletions src/rules/named.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import path from 'node:path'

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

import { ExportMap, createRule } from '../utils'
import type { ModuleOptions } from '../utils'
Expand Down Expand Up @@ -97,11 +98,7 @@ export = createRule<[ModuleOptions?], MessageId>({
const deepPath = deepLookup.path
.map(i =>
path.relative(
path.dirname(
context.getPhysicalFilename
? context.getPhysicalFilename()
: context.getFilename(),
),
path.dirname(getPhysicalFilename(context)),
i.path,
),
)
Expand Down Expand Up @@ -200,7 +197,7 @@ export = createRule<[ModuleOptions?], MessageId>({
if (deepLookup.path.length > 1) {
const deepPath = deepLookup.path
.map(i =>
path.relative(path.dirname(context.getFilename()), i.path),
path.relative(path.dirname(getFilename(context)), i.path),
)
.join(' -> ')

Expand Down
13 changes: 9 additions & 4 deletions src/rules/namespace.ts
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ export = createRule<[Options], MessageId>({

// same as above, but does not add names to local map
ExportNamespaceSpecifier(namespace) {
const declaration = importDeclaration(context)
const declaration = importDeclaration(context, namespace)

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

if (declaredScope(context, dereference.object.name) !== 'module') {
if (
declaredScope(context, dereference, dereference.object.name) !==
'module'
) {
return
}

Expand Down Expand Up @@ -249,7 +252,9 @@ export = createRule<[Options], MessageId>({
}
},

VariableDeclarator({ id, init }) {
VariableDeclarator(node) {
const { id, init } = node

if (init == null) {
return
}
Expand All @@ -261,7 +266,7 @@ export = createRule<[Options], MessageId>({
}

// check for redefinition in intermediate scopes
if (declaredScope(context, init.name) !== 'module') {
if (declaredScope(context, node, init.name) !== 'module') {
return
}

Expand Down
14 changes: 5 additions & 9 deletions src/rules/newline-after-import.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@

import type { TSESLint, TSESTree } from '@typescript-eslint/utils'
import debug from 'debug'
import { getPhysicalFilename } from 'eslint-compat-utils'

import { isStaticRequire, createRule } from '../utils'
import { isStaticRequire, createRule, getScope } from '../utils'

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

Expand Down Expand Up @@ -267,14 +268,9 @@ export = createRule<[Options?], MessageId>({
requireCalls.push(node)
}
},
'Program:exit'() {
log(
'exit processing for',
context.getPhysicalFilename
? context.getPhysicalFilename()
: context.getFilename(),
)
const scopeBody = getScopeBody(context.getScope())
'Program:exit'(node) {
log('exit processing for', getPhysicalFilename(context))
const scopeBody = getScopeBody(getScope(context, node))

log('got scope:', scopeBody)

Expand Down
6 changes: 3 additions & 3 deletions src/rules/no-absolute-path.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import path from 'node:path'

import { getPhysicalFilename } from 'eslint-compat-utils'

import {
isAbsolute,
createRule,
Expand Down Expand Up @@ -35,9 +37,7 @@ export = createRule<[ModuleOptions?], MessageId>({
node: source,
messageId: 'absolute',
fix(fixer) {
const resolvedContext = context.getPhysicalFilename
? context.getPhysicalFilename()
: context.getFilename()
const resolvedContext = getPhysicalFilename(context)
// node.js and web imports work with posix style paths ("/")
let relativePath = path.posix.relative(
path.dirname(resolvedContext),
Expand Down
4 changes: 2 additions & 2 deletions src/rules/no-amd.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
* Rule to prefer imports to AMD
*/

import { createRule } from '../utils'
import { createRule, getScope } from '../utils'

type MessageId = 'amd'

Expand All @@ -23,7 +23,7 @@ export = createRule<[], MessageId>({
create(context) {
return {
CallExpression(node) {
if (context.getScope().type !== 'module') {
if (getScope(context, node).type !== 'module') {
return
}

Expand Down
10 changes: 5 additions & 5 deletions src/rules/no-commonjs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

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

import { createRule } from '../utils'
import { createRule, getScope } from '../utils'

type NormalizedOptions = {
allowPrimitiveModules?: boolean
Expand Down Expand Up @@ -122,16 +122,16 @@ export = createRule<[Options?], MessageId>({

// exports.
if ('name' in node.object && node.object.name === 'exports') {
const isInScope = context
.getScope()
.variables.some(variable => variable.name === 'exports')
const isInScope = getScope(context, node).variables.some(
variable => variable.name === 'exports',
)
if (!isInScope) {
context.report({ node, messageId: 'export' })
}
}
},
CallExpression(call) {
if (!validateScope(context.getScope())) {
if (!validateScope(getScope(context, call))) {
return
}

Expand Down
6 changes: 3 additions & 3 deletions src/rules/no-cycle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
* Ensures that no imported module imports the linted module.
*/

import { getPhysicalFilename } from 'eslint-compat-utils'

import type { DeclarationMetadata, ModuleOptions } from '../utils'
import {
ExportMap,
Expand Down Expand Up @@ -70,9 +72,7 @@ export = createRule<[Options?], MessageId>({
},
defaultOptions: [],
create(context) {
const filename = context.getPhysicalFilename
? context.getPhysicalFilename()
: context.getFilename()
const filename = getPhysicalFilename(context)

if (filename === '<text>') {
return {}
Expand Down
6 changes: 4 additions & 2 deletions src/rules/no-default-export.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { getSourceCode } from 'eslint-compat-utils'

import { createRule } from '../utils'

export = createRule({
Expand All @@ -24,7 +26,7 @@ export = createRule({

return {
ExportDefaultDeclaration(node) {
const { loc } = context.getSourceCode().getFirstTokens(node)[1] || {}
const { loc } = getSourceCode(context).getFirstTokens(node)[1] || {}
context.report({
node,
messageId: 'preferNamed',
Expand All @@ -39,7 +41,7 @@ export = createRule({
('value' in specifier.exported && specifier.exported.value)) ===
'default',
)) {
const { loc } = context.getSourceCode().getFirstTokens(node)[1] || {}
const { loc } = getSourceCode(context).getFirstTokens(node)[1] || {}
// @ts-expect-error - legacy parser type
if (specifier.type === 'ExportDefaultSpecifier') {
context.report({
Expand Down
7 changes: 5 additions & 2 deletions src/rules/no-deprecated.ts
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ export = createRule({
return
}

if (declaredScope(context, node.name) !== 'module') {
if (declaredScope(context, node, node.name) !== 'module') {
return
}
context.report({
Expand All @@ -165,7 +165,10 @@ export = createRule({
return
}

if (declaredScope(context, dereference.object.name) !== 'module') {
if (
declaredScope(context, dereference, dereference.object.name) !==
'module'
) {
return
}

Expand Down
3 changes: 2 additions & 1 deletion src/rules/no-duplicates.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type { TSESLint, TSESTree } from '@typescript-eslint/utils'
import { getSourceCode } from 'eslint-compat-utils'
import semver from 'semver'
import type { PackageJson } from 'type-fest'

Expand Down Expand Up @@ -28,7 +29,7 @@ function checkImports(
for (const [module, nodes] of imported.entries()) {
if (nodes.length > 1) {
const [first, ...rest] = nodes
const sourceCode = context.getSourceCode()
const sourceCode = getSourceCode(context)
const fix = getFix(first, rest, sourceCode, context)

context.report({
Expand Down
3 changes: 2 additions & 1 deletion src/rules/no-empty-named-blocks.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type { TSESTree } from '@typescript-eslint/utils'
import { getSourceCode } from 'eslint-compat-utils'

import { createRule } from '../utils'

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

0 comments on commit 1dbb323

Please sign in to comment.