From 28dcd297db0a1945ae85338a26552d98f3716bd8 Mon Sep 17 00:00:00 2001 From: Jeff Date: Thu, 18 Jan 2024 13:44:01 -0700 Subject: [PATCH] Improve typing --- src/index.ts | 2 +- src/lib/rules/order.ts | 84 +++++++++++-------- src/lib/utils/alphabetize-ranks.ts | 17 ++-- src/lib/utils/find-comment.ts | 18 ++-- src/lib/utils/find-root-node.ts | 7 +- src/lib/utils/make-newlines-between-report.ts | 38 +++++---- src/lib/utils/make-out-of-order-report.ts | 59 +++++++------ src/lib/utils/resolve-import-group.ts | 8 +- src/lib/utils/take-tokens.ts | 36 ++++---- tests/lib/rules/order.ts | 22 +++-- 10 files changed, 167 insertions(+), 124 deletions(-) diff --git a/src/index.ts b/src/index.ts index 6524c2f..d9a6f8f 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,4 +1,4 @@ -import { orderRule } from './lib/rules/order' +import { orderRule } from './lib/rules/order.js' const plugin = { rules: { diff --git a/src/lib/rules/order.ts b/src/lib/rules/order.ts index 8d513b2..9f781ef 100644 --- a/src/lib/rules/order.ts +++ b/src/lib/rules/order.ts @@ -1,10 +1,11 @@ +import { AST_NODE_TYPES, ESLintUtils, type TSESLint, type TSESTree } from '@typescript-eslint/utils' import type { Rule } from 'eslint' import type { ImportDeclaration } from 'estree' -import { mutateRanksToAlphabetize } from '../utils/alphabetize-ranks' -import { makeNewlinesBetweenReport } from '../utils/make-newlines-between-report' -import { makeOutOfOrderReport } from '../utils/make-out-of-order-report' -import { resolveImportGroup } from '../utils/resolve-import-group' +import { mutateRanksToAlphabetize } from '../utils/alphabetize-ranks.js' +import { makeNewlinesBetweenReport } from '../utils/make-newlines-between-report.js' +import { makeOutOfOrderReport } from '../utils/make-out-of-order-report.js' +import { resolveImportGroup } from '../utils/resolve-import-group.js' const IMPORT_GROUPS = [ 'builtin', @@ -19,15 +20,17 @@ const IMPORT_GROUPS = [ type ImportGroup = (typeof IMPORT_GROUPS)[number] type GroupRankMap = Record<(typeof IMPORT_GROUPS)[number], number> -export type ImportNode = ImportDeclaration & Rule.NodeParentExtension +export type ImportNode = (TSESTree.ImportDeclaration | TSESTree.TSImportEqualsDeclaration) & { + parent: TSESTree.Node +} type ImportName = ImportDeclaration['source']['value'] export interface ImportNodeObject { - node: Rule.Node & { importKind?: string } + node: ImportNode // & { importKind?: string } value: ImportName displayName: ImportName - type: 'import' | 'import:' + type: 'import' | 'import:object' rank: number } @@ -37,11 +40,11 @@ interface RankObject { } function computeRank( - context: Rule.RuleContext, - importEntry: Omit, + settings: TSESLint.SharedConfigurationSettings, + importEntry: ImportNodeObject, ranks: RankObject, ) { - let kind: ImportGroup = resolveImportGroup(importEntry.value as string, context) + let kind: ImportGroup = resolveImportGroup(importEntry.value as string, settings) let rank: number /** @@ -65,14 +68,14 @@ function computeRank( } function registerNode( - context: Rule.RuleContext, - importEntry: Omit, + settings: TSESLint.SharedConfigurationSettings, + importEntry: ImportNodeObject, ranks: RankObject, - imported, + imported?: unknown[], ) { - let rank = computeRank(context, importEntry, ranks) + let rank = computeRank(settings, importEntry, ranks) if (rank !== -1) { - imported.push({ ...importEntry, rank }) + imported?.push({ ...importEntry, rank }) } } @@ -109,29 +112,38 @@ function convertGroupsToRanks(groups: typeof IMPORT_GROUPS) { return { groups: ranks, omittedTypes } } -export const orderRule: Rule.RuleModule = { +// eslint-disable-next-line new-cap +const createRule = ESLintUtils.RuleCreator( + (name) => + `https://github.com/stormwarning/eslint-plugin-import-sorting/blob/main/docs/rules/${name}.md`, +) + +export const orderRule = createRule({ + name: 'order', meta: { type: 'layout', fixable: 'code', docs: { description: 'Enforce a convention in the order of `import` statements.', - url: 'https://github.com/stormwarning/eslint-plugin-import-sorting/blob/main/docs/rules/order.md', }, - schema: [ - { - type: 'object', - }, - ], + messages: { + 'needs-newline': 'There should be at least one empty line between import groups', + 'extra-newline': 'There should be no empty line between import groups', + 'extra-newline-in-group': 'There should be no empty line within import group', + 'out-of-order': '{{secondImport}} should occur {{order}} {{firstImport}}', + }, + schema: [], }, + defaultOptions: [], create(context) { - let importMap: Map> = new Map() + let importMap = new Map() let { groups, omittedTypes } = convertGroupsToRanks(IMPORT_GROUPS) let ranks: RankObject = { groups, omittedTypes, } - function getBlockImports(node: Rule.Node) { + function getBlockImports(node: ImportNode) { if (!importMap.has(node)) { importMap.set(node, []) } @@ -145,32 +157,36 @@ export const orderRule: Rule.RuleModule = { if (node.specifiers.length > 0) { let name = node.source.value registerNode( - context, + context.settings, { node, value: name, displayName: name, type: 'import', + /** @todo Check that setting this to a value doesn't cause problems. */ + rank: 0, }, ranks, getBlockImports(node.parent), ) } }, - TSImportEqualsDeclaration(node: ImportNode) { + TSImportEqualsDeclaration(node) { + // @ts-expect-error -- Probably don't need this check. if (node.isExport) return - let displayName - let value - let type + let displayName: string + let value: string + let type: 'import' | 'import:object' - if (node.moduleReference.type === 'TSExternalModuleReference') { - value = node.moduleReference.expression.value + if (node.moduleReference.type === AST_NODE_TYPES.TSExternalModuleReference) { + /** @todo Not sure how to properly type this property. */ + value = node.moduleReference.expression.value as string displayName = value type = 'import' } else { value = '' - displayName = context.getSourceCode().getText(node.moduleReference) + displayName = context.sourceCode.getText(node.moduleReference) type = 'import:object' } @@ -181,6 +197,8 @@ export const orderRule: Rule.RuleModule = { value, displayName, type, + /** @todo Check that setting this to a value doesn't cause problems. */ + rank: 0, }, ranks, getBlockImports(node.parent), @@ -199,4 +217,4 @@ export const orderRule: Rule.RuleModule = { }, } }, -} +}) diff --git a/src/lib/utils/alphabetize-ranks.ts b/src/lib/utils/alphabetize-ranks.ts index 2576011..07fb649 100644 --- a/src/lib/utils/alphabetize-ranks.ts +++ b/src/lib/utils/alphabetize-ranks.ts @@ -2,7 +2,7 @@ import path from 'node:path' import groupBy from 'object.groupby' -import type { ImportNodeObject } from '../rules/order' +import type { ImportNodeObject } from '../rules/order.js' type OrderDirection = 'asc' | 'desc' @@ -21,10 +21,10 @@ function compareString(first: string, second: string) { function compareDotSegments(first: string, second: string) { let regex = /\.+(?=\/)/g - let firstCount = (first.match(regex) || []).join('').length - let secondCount = (second.match(regex) || []).join('').length + let firstCount = (first.match(regex) ?? []).join('').length + let secondCount = (second.match(regex) ?? []).join('').length - if (firstCount > secondCount) return -1 + if (secondCount < firstCount) return -1 if (firstCount < secondCount) return 1 // If segment length is the same, compare the basename alphabetically. @@ -69,8 +69,8 @@ function getSorter(order: OrderDirection) { result = multiplierImportKind * compareString( - nodeA.node.importKind || DEFAULT_IMPORT_KIND, - nodeB.node.importKind || DEFAULT_IMPORT_KIND, + nodeA.node.importKind ?? DEFAULT_IMPORT_KIND, + nodeB.node.importKind ?? DEFAULT_IMPORT_KIND, ) } @@ -78,7 +78,10 @@ function getSorter(order: OrderDirection) { } } -export function mutateRanksToAlphabetize(imported: ImportNodeObject[], alphabetizeOptions) { +export function mutateRanksToAlphabetize( + imported: ImportNodeObject[], + alphabetizeOptions: OrderDirection, +) { let groupedByRanks: Record = groupBy( imported, (item: ImportNodeObject) => item.rank, diff --git a/src/lib/utils/find-comment.ts b/src/lib/utils/find-comment.ts index ec9c1dd..76cc272 100644 --- a/src/lib/utils/find-comment.ts +++ b/src/lib/utils/find-comment.ts @@ -1,12 +1,12 @@ -import type { Rule, SourceCode } from 'eslint' +import type { TSESLint } from '@typescript-eslint/utils' -import { takeTokensAfterWhile, takeTokensBeforeWhile } from './take-tokens' +import type { ImportNode } from '../rules/order.js' +import { takeTokensAfterWhile, takeTokensBeforeWhile } from './take-tokens.js' -export function findStartOfLineWithComments(sourceCode: SourceCode, node: Rule.Node) { +export function findStartOfLineWithComments(sourceCode: TSESLint.SourceCode, node: ImportNode) { let tokensToEndOfLine = takeTokensBeforeWhile(sourceCode, node, commentOnSameLineAs(node)) - let startOfTokens = ( + let startOfTokens = tokensToEndOfLine.length > 0 ? tokensToEndOfLine[0].range?.[0] : node.range?.[0] - ) as number let result = startOfTokens for (let index = startOfTokens - 1; index > 0; index--) { @@ -20,11 +20,11 @@ export function findStartOfLineWithComments(sourceCode: SourceCode, node: Rule.N return result } -export function findEndOfLineWithComments(sourceCode: SourceCode, node: Rule.Node) { +export function findEndOfLineWithComments(sourceCode: TSESLint.SourceCode, node: ImportNode) { let tokensToEndOfLine = takeTokensAfterWhile(sourceCode, node, commentOnSameLineAs(node)) let endOfTokens = ( tokensToEndOfLine.length > 0 ? tokensToEndOfLine.at(-1)?.range?.[1] : node.range?.[1] - ) as number + )! let result = endOfTokens for (let index = endOfTokens; index < sourceCode.text.length; index++) { @@ -48,9 +48,9 @@ export function findEndOfLineWithComments(sourceCode: SourceCode, node: Rule.Nod } /** @todo rename to has-comment-on-same-line */ -export function commentOnSameLineAs(node: Rule.Node) { +export function commentOnSameLineAs(node: ImportNode) { return (token: any) => (token.type === 'Block' || token.type === 'Line') && token.loc.start.line === token.loc.end.line && - token.loc.end.line === node.loc?.end.line + token.loc.end.line === node.loc.end.line } diff --git a/src/lib/utils/find-root-node.ts b/src/lib/utils/find-root-node.ts index 4b5e06f..36f02b5 100644 --- a/src/lib/utils/find-root-node.ts +++ b/src/lib/utils/find-root-node.ts @@ -1,11 +1,12 @@ -import type { Rule } from 'eslint' +import type { ImportNode } from '../rules/order.js' -export function findRootNode(node: Rule.Node) { +export function findRootNode(node: ImportNode) { let parent = node + /** @todo Not sure how to properly type `parent` property. */ // eslint-disable-next-line no-eq-null, eqeqeq while (parent.parent != null && parent.parent.body == null) { - parent = parent.parent + parent = parent.parent as ImportNode } return parent diff --git a/src/lib/utils/make-newlines-between-report.ts b/src/lib/utils/make-newlines-between-report.ts index a4d5848..d303a72 100644 --- a/src/lib/utils/make-newlines-between-report.ts +++ b/src/lib/utils/make-newlines-between-report.ts @@ -1,15 +1,21 @@ -import type { AST, Rule } from 'eslint' +/* eslint-disable @typescript-eslint/ban-types */ -import type { ImportNodeObject } from '../rules/order' +import type { TSESLint } from '@typescript-eslint/utils' +import type { AST } from 'eslint' + +import type { ImportNodeObject } from '../rules/order.js' import { commentOnSameLineAs, findEndOfLineWithComments, findStartOfLineWithComments, -} from './find-comment' -import { findRootNode } from './find-root-node' -import { takeTokensAfterWhile } from './take-tokens' +} from './find-comment.js' +import { findRootNode } from './find-root-node.js' +import { takeTokensAfterWhile } from './take-tokens.js' -function fixNewLineAfterImport(context: Rule.RuleContext, previousImport: ImportNodeObject) { +function fixNewLineAfterImport( + context: TSESLint.RuleContext, + previousImport: ImportNodeObject, +) { let previousRoot = findRootNode(previousImport.node) let tokensToEndOfLine = takeTokensAfterWhile( context.sourceCode, @@ -17,17 +23,17 @@ function fixNewLineAfterImport(context: Rule.RuleContext, previousImport: Import commentOnSameLineAs(previousRoot), ) - let endOfLine = previousRoot.range?.[1] as number + let endOfLine = previousRoot.range[1] if (tokensToEndOfLine.length > 0) { - endOfLine = tokensToEndOfLine.at(-1)?.range?.[1] as number + endOfLine = tokensToEndOfLine.at(-1)!.range[1] } - return (fixer: Rule.RuleFixer) => - fixer.insertTextAfterRange([previousRoot.range?.[0] as number, endOfLine], '\n') + return (fixer: TSESLint.RuleFixer) => + fixer.insertTextAfterRange([previousRoot.range[0], endOfLine], '\n') } function removeNewLineAfterImport( - context: Rule.RuleContext, + context: TSESLint.RuleContext, currentImport: ImportNodeObject, previousImport: ImportNodeObject, ) { @@ -40,14 +46,14 @@ function removeNewLineAfterImport( ] if (/^\s*$/.test(sourceCode.text.slice(rangeToRemove[0], rangeToRemove[1]))) { - return (fixer: Rule.RuleFixer) => fixer.removeRange(rangeToRemove) + return (fixer: TSESLint.RuleFixer) => fixer.removeRange(rangeToRemove) } return undefined } export function makeNewlinesBetweenReport( - context: Rule.RuleContext, + context: TSESLint.RuleContext, imported: ImportNodeObject[], newlinesBetweenImports = 'always', ) { @@ -79,7 +85,7 @@ export function makeNewlinesBetweenReport( if (isStartOfDistinctGroup) { context.report({ node: previousImport.node, - message: 'There should be at least one empty line between import groups', + messageId: 'needs-newline', fix: fixNewLineAfterImport(context, previousImport), }) } @@ -89,14 +95,14 @@ export function makeNewlinesBetweenReport( ) { context.report({ node: previousImport.node, - message: 'There should be no empty line within import group', + messageId: 'extra-newline', fix: removeNewLineAfterImport(context, currentImport, previousImport), }) } } else if (emptyLinesBetween > 0) { context.report({ node: previousImport.node, - message: 'There should be no empty line between import groups', + messageId: 'extra-newline-in-group', fix: removeNewLineAfterImport(context, currentImport, previousImport), }) } diff --git a/src/lib/utils/make-out-of-order-report.ts b/src/lib/utils/make-out-of-order-report.ts index 7fda7d8..16047a2 100644 --- a/src/lib/utils/make-out-of-order-report.ts +++ b/src/lib/utils/make-out-of-order-report.ts @@ -1,8 +1,10 @@ -import type { Rule } from 'eslint' +/* eslint-disable @typescript-eslint/ban-types */ -import type { ImportNodeObject } from '../rules/order' -import { findEndOfLineWithComments, findStartOfLineWithComments } from './find-comment' -import { findRootNode } from './find-root-node' +import { AST_NODE_TYPES, type TSESLint } from '@typescript-eslint/utils' + +import type { ImportNode, ImportNodeObject } from '../rules/order.js' +import { findEndOfLineWithComments, findStartOfLineWithComments } from './find-comment.js' +import { findRootNode } from './find-root-node.js' type OrderTerm = 'before' | 'after' @@ -11,12 +13,12 @@ function findOutOfOrder(imported: ImportNodeObject[]) { let maxSeenRankNode = imported[0] return imported.filter((importedModule) => { - let result = importedModule.rank < maxSeenRankNode.rank + let isLessThanPrevious = importedModule.rank < maxSeenRankNode.rank if (maxSeenRankNode.rank < importedModule.rank) { maxSeenRankNode = importedModule } - return result + return isLessThanPrevious }) } @@ -24,18 +26,20 @@ function reverse(array: ImportNodeObject[]) { return array.map((v) => ({ ...v, rank: -v.rank })).reverse() } -function isPlainImportModule(node: Rule.Node) { +function isPlainImportModule(node: ImportNode) { return ( + node.type === AST_NODE_TYPES.ImportDeclaration && // eslint-disable-next-line no-eq-null, eqeqeq - node.type === 'ImportDeclaration' && node.specifiers != null && node.specifiers.length > 0 + node.specifiers != null && + node.specifiers.length > 0 ) } -function isPlainImportEquals(node) { - return node.type === 'TSImportEqualsDeclaration' && node.moduleReference.expression +function isPlainImportEquals(node: ImportNode): boolean { + return node.type === AST_NODE_TYPES.TSImportEqualsDeclaration && node.moduleReference.expression } -function canCrossNodeWhileReorder(node: Rule.Node) { +function canCrossNodeWhileReorder(node: ImportNode) { return isPlainImportModule(node) || isPlainImportEquals(node) } @@ -43,12 +47,12 @@ function canCrossNodeWhileReorder(node: Rule.Node) { * The `parent` key should have a type of `ESTree.Program` but then the `body` * key is incompatible with the `Rule.Node` type. */ -function canReorderItems(firstNode: Rule.Node, secondNode: Rule.Node) { +function canReorderItems(firstNode: ImportNode, secondNode: ImportNode) { let { parent } = firstNode - let [firstIndex, secondIndex] = [ - parent.body.indexOf(firstNode), - parent.body.indexOf(secondNode), - ].sort() + + let [firstIndex, secondIndex] = ( + [parent.body.indexOf(firstNode), parent.body.indexOf(secondNode)] as [number, number] + ).sort() let nodesBetween = parent.body.slice(firstIndex, secondIndex + 1) for (let nodeBetween of nodesBetween) { @@ -62,12 +66,13 @@ function canReorderItems(firstNode: Rule.Node, secondNode: Rule.Node) { function makeImportDescription(node: ImportNodeObject) { if (node.node.importKind === 'type') return 'type import' - if (node.node.importKind === 'typeof') return 'typeof import' + // Only needed for Flow syntax. + // if (node.node.importKind === 'typeof') return 'typeof import' return 'import' } function fixOutOfOrder( - context: Rule.RuleContext, + context: TSESLint.RuleContext, firstNode: ImportNodeObject, secondNode: ImportNodeObject, order: OrderTerm, @@ -90,14 +95,14 @@ function fixOutOfOrder( let firstImport = `${makeImportDescription(firstNode)} of \`${firstNode.displayName}\`` let secondImport = `\`${secondNode.displayName}\` ${makeImportDescription(secondNode)}` - let message = `${secondImport} should occur ${order} ${firstImport}` if (order === 'before') { context.report({ node: secondNode.node, - message, + messageId: 'out-of-order', + data: { firstImport, secondImport, order }, fix: canFix - ? (fixer: Rule.RuleFixer) => + ? (fixer: TSESLint.RuleFixer) => fixer.replaceTextRange( [firstRootStart, secondRootEnd], newCode + sourceCode.text.slice(firstRootStart, secondRootStart), @@ -107,9 +112,10 @@ function fixOutOfOrder( } else if (order === 'after') { context.report({ node: secondNode.node, - message, + messageId: 'out-of-order', + data: { firstImport, secondImport, order }, fix: canFix - ? (fixer: Rule.RuleFixer) => + ? (fixer: TSESLint.RuleFixer) => fixer.replaceTextRange( [secondRootStart, firstRootEnd], sourceCode.text.slice(secondRootEnd, firstRootEnd) + newCode, @@ -120,7 +126,7 @@ function fixOutOfOrder( } function reportOutOfOrder( - context: Rule.RuleContext, + context: TSESLint.RuleContext, imported: ImportNodeObject[], outOfOrder: ImportNodeObject[], order: OrderTerm, @@ -131,7 +137,10 @@ function reportOutOfOrder( } } -export function makeOutOfOrderReport(context: Rule.RuleContext, imported: ImportNodeObject[]) { +export function makeOutOfOrderReport( + context: TSESLint.RuleContext, + imported: ImportNodeObject[], +) { let outOfOrder = findOutOfOrder(imported) if (outOfOrder.length === 0) return diff --git a/src/lib/utils/resolve-import-group.ts b/src/lib/utils/resolve-import-group.ts index 3d824c0..b3f5384 100644 --- a/src/lib/utils/resolve-import-group.ts +++ b/src/lib/utils/resolve-import-group.ts @@ -1,6 +1,6 @@ import { isBuiltin } from 'node:module' -import type { Rule } from 'eslint' +import type { TSESLint } from '@typescript-eslint/utils' const moduleRegExp = /^\w/ function isModule(name: string) { @@ -35,9 +35,9 @@ function isStyle(name: string) { return name.endsWith('.css') } -export function resolveImportGroup(name: string, context: Rule.RuleContext) { - let knownFramework = context.settings['import-sorting/known-framework'] as string - let knownFirstParty = context.settings['import-sorting/known-first-party'] as string +export function resolveImportGroup(name: string, settings: TSESLint.SharedConfigurationSettings) { + let knownFramework = settings['import-sorting/known-framework'] as string + let knownFirstParty = settings['import-sorting/known-first-party'] as string if (isBuiltin(name)) return 'builtin' if (isStyle(name)) return 'style' diff --git a/src/lib/utils/take-tokens.ts b/src/lib/utils/take-tokens.ts index dbcba6a..a905e12 100644 --- a/src/lib/utils/take-tokens.ts +++ b/src/lib/utils/take-tokens.ts @@ -1,11 +1,14 @@ -import type { SourceCode, AST, Rule } from 'eslint' -import * as ESTree from 'estree' +import type { TSESLint } from '@typescript-eslint/utils' +import type { AST } from 'eslint' -function getTokensOrCommentsBefore(sourceCode: SourceCode, node: Rule.Node, count = 100) { +import type { ImportNode } from '../rules/order.js' + +function getTokensOrCommentsBefore(sourceCode: TSESLint.SourceCode, node: ImportNode, count = 100) { let currentNodeOrToken = node - let result: Array = [] + let result: Array = [] for (let index = 0; index < count; index++) { + // @ts-expect-error -- Not sure where this method comes from. currentNodeOrToken = sourceCode.getTokenOrCommentBefore(currentNodeOrToken) // eslint-disable-next-line no-eq-null, eqeqeq if (currentNodeOrToken == null) { @@ -19,12 +22,12 @@ function getTokensOrCommentsBefore(sourceCode: SourceCode, node: Rule.Node, coun } export function takeTokensBeforeWhile( - sourceCode: SourceCode, - node: Rule.Node, - condition: (_token: ESTree.Node | AST.Token) => boolean, + sourceCode: TSESLint.SourceCode, + node: ImportNode, + condition: (_token: ImportNode | AST.Token) => boolean, ) { let tokens = getTokensOrCommentsBefore(sourceCode, node, 100) - let result: Array = [] + let result: Array = [] for (let index = tokens.length - 1; index >= 0; index--) { if (condition(tokens[index])) { @@ -37,11 +40,16 @@ export function takeTokensBeforeWhile( return result.reverse() } -function getTokensOrCommentsAfter(sourceCode: SourceCode, node: Rule.Node, count: number) { +function getTokensOrCommentsAfter( + sourceCode: TSESLint.SourceCode, + node: ImportNode, + count: number, +) { let currentNodeOrToken = node - let result: Array = [] + let result: Array = [] for (let index = 0; index < count; index++) { + // @ts-expect-error -- Not sure where this method comes from. currentNodeOrToken = sourceCode.getTokenOrCommentAfter(currentNodeOrToken) // eslint-disable-next-line no-eq-null, eqeqeq if (currentNodeOrToken == null) { @@ -55,12 +63,12 @@ function getTokensOrCommentsAfter(sourceCode: SourceCode, node: Rule.Node, count } export function takeTokensAfterWhile( - sourceCode: SourceCode, - node: Rule.Node, - condition: (_token: ESTree.Node | AST.Token) => boolean, + sourceCode: TSESLint.SourceCode, + node: ImportNode, + condition: (_token: ImportNode | AST.Token) => boolean, ) { let tokens = getTokensOrCommentsAfter(sourceCode, node, 100) - let result: Array = [] + let result: Array = [] for (let token of tokens) { if (condition(token)) { diff --git a/tests/lib/rules/order.ts b/tests/lib/rules/order.ts index 91d49f3..0c226fe 100644 --- a/tests/lib/rules/order.ts +++ b/tests/lib/rules/order.ts @@ -1,15 +1,16 @@ -import { RuleTester } from 'eslint' +import { RuleTester } from '@typescript-eslint/rule-tester' -import { orderRule } from '../../../src/lib/rules/order' +import { orderRule } from '../../../src/lib/rules/order.js' const ruleTester = new RuleTester({ + parser: '@typescript-eslint/parser', parserOptions: { sourceType: 'module', ecmaVersion: 6, }, settings: { - 'import-sorting/known-framework': /^react(\/|-dom|-router|$)/, - 'import-sorting/known-first-party': /^~/, + 'import-sorting/known-framework': /^react(\/|-dom|-router|$)/.source, + 'import-sorting/known-first-party': /^~/.source, }, }) @@ -53,7 +54,7 @@ ruleTester.run('order', orderRule, { import { x, y, z } from 'package' import { a, b, c } from 'package/path' `, - errors: [{ message: '`package` import should occur before import of `package/path`' }], + errors: [{ messageId: 'out-of-order' }], }, // It groups built-in modules separately. @@ -69,10 +70,7 @@ ruleTester.run('order', orderRule, { import { x, y, z } from '@scope/package' import { a, b, c } from 'package' `, - errors: [ - { message: 'There should be at least one empty line between import groups' }, - { message: '`node:fs` import should occur before import of `@scope/package`' }, - ], + errors: [{ messageId: 'needs-newline' }, { messageId: 'out-of-order' }], }, // It groups framework modules separately. @@ -87,7 +85,7 @@ ruleTester.run('order', orderRule, { import flatten from 'react-keyed-flatten-children' `, - errors: [{ message: 'There should be at least one empty line between import groups' }], + errors: [{ messageId: 'needs-newline' }], }, // It groups first-party modules separately. @@ -102,7 +100,7 @@ ruleTester.run('order', orderRule, { import B from '~/components' `, - errors: [{ message: 'There should be at least one empty line between import groups' }], + errors: [{ messageId: 'needs-newline' }], }, /** @@ -125,7 +123,7 @@ ruleTester.run('order', orderRule, { `, errors: [ { - message: '`../module-a.js` import should occur before import of `./index.js`', + messageId: 'out-of-order', }, ], },