From f2a3dba635b9eb29df61ebf3265654c3b66f5740 Mon Sep 17 00:00:00 2001 From: Marco Pasqualetti Date: Thu, 14 Nov 2024 17:07:27 +0100 Subject: [PATCH 01/49] test(default): migrate to `createRuleTestCaseFunction` --- .../consistent-type-specifier-style.spec.ts | 11 ++++--- test/utils.ts | 30 ++++++++++++++++++- 2 files changed, 36 insertions(+), 5 deletions(-) diff --git a/test/rules/consistent-type-specifier-style.spec.ts b/test/rules/consistent-type-specifier-style.spec.ts index 4f6d8ad2b..3927fe549 100644 --- a/test/rules/consistent-type-specifier-style.spec.ts +++ b/test/rules/consistent-type-specifier-style.spec.ts @@ -1,11 +1,14 @@ import { RuleTester as TSESLintRuleTester } from '@typescript-eslint/rule-tester' import { TSESTree } from '@typescript-eslint/utils' -import { parsers, test } from '../utils' +import { parsers, createRuleTestCaseFunction } from '../utils' +import type { RunTests } from '../utils' import rule from 'eslint-plugin-import-x/rules/consistent-type-specifier-style' -const COMMON_TESTS = { +const test = createRuleTestCaseFunction() + +const COMMON_TESTS: RunTests = { valid: [ // // prefer-top-level @@ -248,7 +251,7 @@ import { ], } as const -const TS_ONLY = { +const TS_ONLY: RunTests = { valid: [ // // always valid @@ -258,7 +261,7 @@ const TS_ONLY = { invalid: [], } -const FLOW_ONLY = { +const FLOW_ONLY: RunTests = { valid: [ // // prefer-top-level diff --git a/test/utils.ts b/test/utils.ts index 64f38939c..26ef7c411 100644 --- a/test/utils.ts +++ b/test/utils.ts @@ -3,6 +3,7 @@ import path from 'node:path' import type { ValidTestCase as TSESLintValidTestCase, InvalidTestCase as TSESLintInvalidTestCase, + RunTests as TSESLintRunTests, } from '@typescript-eslint/rule-tester' import type { TSESTree } from '@typescript-eslint/utils' import type { RuleModule } from '@typescript-eslint/utils/ts-eslint' @@ -91,7 +92,7 @@ export function test( return createRuleTestCase(t) } -type GetRuleModuleTypes = +export type GetRuleModuleTypes = TRule extends RuleModule ? { messageIds: MessageIds @@ -99,6 +100,33 @@ type GetRuleModuleTypes = } : never +/** + * Type helper to build {@link TSESLintRuleTester.run} test parameters + * from a given {@link RuleModule} + * + * @example + * ```ts + * const COMMON_TESTS: RunTests = { + * valid: [ + * { + * code: "import Foo from 'Foo';", + * options: [], + * }, + * ], + * invalid: [ + * { + * code: "import Foo from 'Foo';", + * options: ['prefer-top-level'], + * errors: [] + * }, + * ] + * ``` + */ +export type RunTests< + TRule extends RuleModule, + TRuleType extends GetRuleModuleTypes = GetRuleModuleTypes, +> = TSESLintRunTests + /** * Create a function that can be used to create both valid and invalid test case * to be provided to {@link TSESLintRuleTester}. From 4614f7a33af35aaacd51d8d619e6e4615ec24b19 Mon Sep 17 00:00:00 2001 From: Marco Pasqualetti Date: Thu, 14 Nov 2024 17:26:18 +0100 Subject: [PATCH 02/49] test(consistent-type-specifier-style): migrate to `createRuleTestCaseFunction` --- test/rules/default.spec.ts | 97 ++++++++++++++++--- test/rules/dynamic-import-chunkname.spec.ts | 4 +- test/rules/export.spec.ts | 4 +- test/rules/named.spec.ts | 4 +- test/rules/namespace.spec.ts | 4 +- .../rules/no-anonymous-default-export.spec.ts | 4 +- test/rules/no-deprecated.spec.ts | 4 +- test/rules/no-named-as-default-member.spec.ts | 4 +- test/rules/no-named-as-default.spec.ts | 4 +- test/rules/no-named-default.spec.ts | 4 +- test/rules/no-unresolved.spec.ts | 4 +- test/utils.ts | 2 +- 12 files changed, 102 insertions(+), 37 deletions(-) diff --git a/test/rules/default.spec.ts b/test/rules/default.spec.ts index 0dd155d5a..9b3fb2e76 100644 --- a/test/rules/default.spec.ts +++ b/test/rules/default.spec.ts @@ -2,13 +2,20 @@ import path from 'node:path' import { RuleTester as TSESLintRuleTester } from '@typescript-eslint/rule-tester' -import { test, SYNTAX_CASES, parsers } from '../utils' +import { + createRuleTestCaseFunction, + SYNTAX_VALID_CASES, + parsers, + RunTests, +} from '../utils' import rule from 'eslint-plugin-import-x/rules/default' import { CASE_SENSITIVE_FS } from 'eslint-plugin-import-x/utils' const ruleTester = new TSESLintRuleTester() +const test = createRuleTestCaseFunction() + ruleTester.run('default', rule, { valid: [ test({ @@ -123,7 +130,7 @@ ruleTester.run('default', rule, { }, }), - ...SYNTAX_CASES, + ...(SYNTAX_VALID_CASES as RunTests['valid']), ], invalid: [ @@ -138,9 +145,10 @@ ruleTester.run('default', rule, { code: 'import baz from "./named-exports";', errors: [ { - message: - 'No default export found in imported module "./named-exports".', - type: 'ImportDefaultSpecifier', + messageId: 'noDefaultExport', + data: { + module: './named-exports', + }, }, ], }), @@ -149,31 +157,64 @@ ruleTester.run('default', rule, { test({ code: 'export baz from "./named-exports"', languageOptions: { parser: require(parsers.BABEL) }, - errors: ['No default export found in imported module "./named-exports".'], + errors: [ + { + messageId: 'noDefaultExport', + data: { + module: './named-exports', + }, + }, + ], }), test({ code: 'export baz, { bar } from "./named-exports"', languageOptions: { parser: require(parsers.BABEL) }, - errors: ['No default export found in imported module "./named-exports".'], + errors: [ + { + messageId: 'noDefaultExport', + data: { + module: './named-exports', + }, + }, + ], }), test({ code: 'export baz, * as names from "./named-exports"', languageOptions: { parser: require(parsers.BABEL) }, - errors: ['No default export found in imported module "./named-exports".'], + errors: [ + { + messageId: 'noDefaultExport', + data: { + module: './named-exports', + }, + }, + ], }), // exports default from a module with no default test({ code: 'import twofer from "./broken-trampoline"', languageOptions: { parser: require(parsers.BABEL) }, errors: [ - 'No default export found in imported module "./broken-trampoline".', + { + messageId: 'noDefaultExport', + data: { + module: './broken-trampoline', + }, + }, ], }), // #328: * exports do not include default test({ code: 'import barDefault from "./re-export"', - errors: ['No default export found in imported module "./re-export".'], + errors: [ + { + messageId: 'noDefaultExport', + data: { + module: './re-export', + }, + }, + ], }), ], }) @@ -190,7 +231,12 @@ if (!CASE_SENSITIVE_FS) { test({ code: 'import bar from "./Named-Exports"', errors: [ - 'No default export found in imported module "./Named-Exports".', + { + messageId: 'noDefaultExport', + data: { + module: './Named-Exports', + }, + }, ], }), ], @@ -321,7 +367,14 @@ describe('TypeScript', () => { 'import-x/parsers': { [parsers.TS]: ['.ts'] }, 'import-x/resolver': { 'eslint-import-resolver-typescript': true }, }, - errors: ['No default export found in imported module "./typescript".'], + errors: [ + { + messageId: 'noDefaultExport', + data: { + module: './typescript', + }, + }, + ], }), test({ code: `import React from "./typescript-export-assign-default-namespace"`, @@ -331,7 +384,12 @@ describe('TypeScript', () => { 'import-x/resolver': { 'eslint-import-resolver-typescript': true }, }, errors: [ - 'No default export found in imported module "./typescript-export-assign-default-namespace".', + { + messageId: 'noDefaultExport', + data: { + module: './typescript-export-assign-default-namespace', + }, + }, ], }), test({ @@ -342,7 +400,12 @@ describe('TypeScript', () => { 'import-x/resolver': { 'eslint-import-resolver-typescript': true }, }, errors: [ - 'No default export found in imported module "./typescript-export-as-default-namespace".', + { + messageId: 'noDefaultExport', + data: { + module: './typescript-export-as-default-namespace', + }, + }, ], }), test({ @@ -362,8 +425,10 @@ describe('TypeScript', () => { }, errors: [ { - message: - 'No default export found in imported module "./typescript-export-as-default-namespace".', + messageId: 'noDefaultExport', + data: { + module: './typescript-export-as-default-namespace', + }, line: 1, column: 8, endLine: 1, diff --git a/test/rules/dynamic-import-chunkname.spec.ts b/test/rules/dynamic-import-chunkname.spec.ts index 3f7b60677..dd740048e 100644 --- a/test/rules/dynamic-import-chunkname.spec.ts +++ b/test/rules/dynamic-import-chunkname.spec.ts @@ -1,7 +1,7 @@ import { RuleTester as TSESLintRuleTester } from '@typescript-eslint/rule-tester' import { TSESTree } from '@typescript-eslint/utils' -import { SYNTAX_CASES, parsers } from '../utils' +import { SYNTAX_VALID_CASES, parsers } from '../utils' import rule from 'eslint-plugin-import-x/rules/dynamic-import-chunkname' @@ -425,7 +425,7 @@ ruleTester.run('dynamic-import-chunkname', rule, { options, languageOptions: { parser: babelParser }, }, - ...SYNTAX_CASES, + ...SYNTAX_VALID_CASES, ], invalid: [ diff --git a/test/rules/export.spec.ts b/test/rules/export.spec.ts index f9ce211e7..de5d11061 100644 --- a/test/rules/export.spec.ts +++ b/test/rules/export.spec.ts @@ -1,6 +1,6 @@ import { RuleTester as TSESLintRuleTester } from '@typescript-eslint/rule-tester' -import { test, testFilePath, SYNTAX_CASES, parsers } from '../utils' +import { test, testFilePath, SYNTAX_VALID_CASES, parsers } from '../utils' import rule from 'eslint-plugin-import-x/rules/export' @@ -27,7 +27,7 @@ ruleTester.run('export', rule, { // #328: "export * from" does not export a default test({ code: 'export default foo; export * from "./bar"' }), - ...SYNTAX_CASES, + ...SYNTAX_VALID_CASES, test({ code: ` diff --git a/test/rules/named.spec.ts b/test/rules/named.spec.ts index 967bb92e6..c7fa7c43c 100644 --- a/test/rules/named.spec.ts +++ b/test/rules/named.spec.ts @@ -3,7 +3,7 @@ import path from 'node:path' import { RuleTester as TSESLintRuleTester } from '@typescript-eslint/rule-tester' import type { TSESTree } from '@typescript-eslint/utils' -import { test, SYNTAX_CASES, testFilePath, parsers } from '../utils' +import { test, SYNTAX_VALID_CASES, testFilePath, parsers } from '../utils' import rule from 'eslint-plugin-import-x/rules/named' import { CASE_SENSITIVE_FS } from 'eslint-plugin-import-x/utils' @@ -195,7 +195,7 @@ ruleTester.run('named', rule, { options: [{ commonjs: true }], }), - ...SYNTAX_CASES, + ...SYNTAX_VALID_CASES, test({ code: `import { ExtfieldModel, Extfield2Model } from './models';`, diff --git a/test/rules/namespace.spec.ts b/test/rules/namespace.spec.ts index 69bad45ef..802d157c7 100644 --- a/test/rules/namespace.spec.ts +++ b/test/rules/namespace.spec.ts @@ -1,6 +1,6 @@ import { RuleTester as TSESLintRuleTester } from '@typescript-eslint/rule-tester' -import { test, SYNTAX_CASES, testFilePath, parsers } from '../utils' +import { test, SYNTAX_VALID_CASES, testFilePath, parsers } from '../utils' import rule from 'eslint-plugin-import-x/rules/namespace' @@ -226,7 +226,7 @@ const valid = [ }, }), - ...SYNTAX_CASES, + ...SYNTAX_VALID_CASES, test({ code: ` diff --git a/test/rules/no-anonymous-default-export.spec.ts b/test/rules/no-anonymous-default-export.spec.ts index 340f09024..1326b7571 100644 --- a/test/rules/no-anonymous-default-export.spec.ts +++ b/test/rules/no-anonymous-default-export.spec.ts @@ -1,6 +1,6 @@ import { RuleTester as TSESLintRuleTester } from '@typescript-eslint/rule-tester' -import { test, SYNTAX_CASES, parsers } from '../utils' +import { test, SYNTAX_VALID_CASES, parsers } from '../utils' import rule from 'eslint-plugin-import-x/rules/no-anonymous-default-export' @@ -62,7 +62,7 @@ ruleTester.run('no-anonymous-default-export', rule, { // Allow call expressions by default for backwards compatibility test({ code: 'export default foo(bar)' }), - ...SYNTAX_CASES, + ...SYNTAX_VALID_CASES, ], invalid: [ diff --git a/test/rules/no-deprecated.spec.ts b/test/rules/no-deprecated.spec.ts index eebc33bc8..5b73d8517 100644 --- a/test/rules/no-deprecated.spec.ts +++ b/test/rules/no-deprecated.spec.ts @@ -1,6 +1,6 @@ import { RuleTester as TSESLintRuleTester } from '@typescript-eslint/rule-tester' -import { test, SYNTAX_CASES, parsers } from '../utils' +import { test, SYNTAX_VALID_CASES, parsers } from '../utils' import rule from 'eslint-plugin-import-x/rules/no-deprecated' @@ -43,7 +43,7 @@ ruleTester.run('no-deprecated', rule, { code: "import { deepDep } from './deep-deprecated'; function x(deepDep) { console.log(deepDep.MY_TERRIBLE_ACTION) }", }), - ...SYNTAX_CASES, + ...SYNTAX_VALID_CASES, ], invalid: [ // reports on parse errors even without specifiers diff --git a/test/rules/no-named-as-default-member.spec.ts b/test/rules/no-named-as-default-member.spec.ts index da25bcf8a..2c539bb25 100644 --- a/test/rules/no-named-as-default-member.spec.ts +++ b/test/rules/no-named-as-default-member.spec.ts @@ -1,6 +1,6 @@ import { RuleTester as TSESLintRuleTester } from '@typescript-eslint/rule-tester' -import { test, SYNTAX_CASES, parsers } from '../utils' +import { test, SYNTAX_VALID_CASES, parsers } from '../utils' import rule from 'eslint-plugin-import-x/rules/no-named-as-default-member' @@ -25,7 +25,7 @@ ruleTester.run('no-named-as-default-member', rule, { }, }), - ...SYNTAX_CASES, + ...SYNTAX_VALID_CASES, ], invalid: [ diff --git a/test/rules/no-named-as-default.spec.ts b/test/rules/no-named-as-default.spec.ts index ec84fa1c1..d393695ff 100644 --- a/test/rules/no-named-as-default.spec.ts +++ b/test/rules/no-named-as-default.spec.ts @@ -1,6 +1,6 @@ import { RuleTester as TSESLintRuleTester } from '@typescript-eslint/rule-tester' -import { test, SYNTAX_CASES, parsers } from '../utils' +import { test, SYNTAX_VALID_CASES, parsers } from '../utils' import rule from 'eslint-plugin-import-x/rules/no-named-as-default' @@ -79,7 +79,7 @@ ruleTester.run('no-named-as-default', rule, { }, }), - ...SYNTAX_CASES, + ...SYNTAX_VALID_CASES, ], invalid: [ diff --git a/test/rules/no-named-default.spec.ts b/test/rules/no-named-default.spec.ts index 63f9a3624..5185eb27a 100644 --- a/test/rules/no-named-default.spec.ts +++ b/test/rules/no-named-default.spec.ts @@ -1,6 +1,6 @@ import { RuleTester as TSESLintRuleTester } from '@typescript-eslint/rule-tester' -import { test, SYNTAX_CASES, parsers } from '../utils' +import { test, SYNTAX_VALID_CASES, parsers } from '../utils' import rule from 'eslint-plugin-import-x/rules/no-named-default' @@ -21,7 +21,7 @@ ruleTester.run('no-named-default', rule, { languageOptions: { parser: require(parsers.BABEL) }, }), - ...SYNTAX_CASES, + ...SYNTAX_VALID_CASES, ], invalid: [ diff --git a/test/rules/no-unresolved.spec.ts b/test/rules/no-unresolved.spec.ts index d5cc31cff..a562c2a7d 100644 --- a/test/rules/no-unresolved.spec.ts +++ b/test/rules/no-unresolved.spec.ts @@ -2,7 +2,7 @@ import path from 'node:path' import { RuleTester as TSESLintRuleTester } from '@typescript-eslint/rule-tester' -import { test, SYNTAX_CASES, parsers, testFilePath } from '../utils' +import { test, SYNTAX_VALID_CASES, parsers, testFilePath } from '../utils' import type { ValidTestCase } from '../utils' import rule from 'eslint-plugin-import-x/rules/no-unresolved' @@ -512,7 +512,7 @@ ruleTester.run('no-unresolved electron', rule, { }) ruleTester.run('no-unresolved syntax verification', rule, { - valid: SYNTAX_CASES, + valid: SYNTAX_VALID_CASES, invalid: [], }) diff --git a/test/utils.ts b/test/utils.ts index 26ef7c411..816f3a58f 100644 --- a/test/utils.ts +++ b/test/utils.ts @@ -182,7 +182,7 @@ export function testContext(settings?: PluginSettings) { * to be added as valid cases just to ensure no nullable fields are going * to crash at runtime */ -export const SYNTAX_CASES = [ +export const SYNTAX_VALID_CASES: TSESLintRunTests['valid'] = [ 'for (let { foo, bar } of baz) {}', 'for (let [ foo, bar ] of baz) {}', From 3a28a503d2a42b462ff610872481a38f53df019e Mon Sep 17 00:00:00 2001 From: Marco Pasqualetti Date: Thu, 14 Nov 2024 17:37:57 +0100 Subject: [PATCH 03/49] test(dynamic-import-chunkname): migrate to `createRuleTestCaseFunction` --- test/rules/default.spec.ts | 2 +- test/rules/dynamic-import-chunkname.spec.ts | 11 +- test/utils.ts | 130 ++++++++++---------- 3 files changed, 75 insertions(+), 68 deletions(-) diff --git a/test/rules/default.spec.ts b/test/rules/default.spec.ts index 9b3fb2e76..6fd9707dc 100644 --- a/test/rules/default.spec.ts +++ b/test/rules/default.spec.ts @@ -6,8 +6,8 @@ import { createRuleTestCaseFunction, SYNTAX_VALID_CASES, parsers, - RunTests, } from '../utils' +import type { RunTests } from '../utils' import rule from 'eslint-plugin-import-x/rules/default' import { CASE_SENSITIVE_FS } from 'eslint-plugin-import-x/utils' diff --git a/test/rules/dynamic-import-chunkname.spec.ts b/test/rules/dynamic-import-chunkname.spec.ts index dd740048e..bb333d49c 100644 --- a/test/rules/dynamic-import-chunkname.spec.ts +++ b/test/rules/dynamic-import-chunkname.spec.ts @@ -2,38 +2,41 @@ import { RuleTester as TSESLintRuleTester } from '@typescript-eslint/rule-tester import { TSESTree } from '@typescript-eslint/utils' import { SYNTAX_VALID_CASES, parsers } from '../utils' +import type { GetRuleModuleOptions } from '../utils' import rule from 'eslint-plugin-import-x/rules/dynamic-import-chunkname' const ruleTester = new TSESLintRuleTester() +type RuleOptions = GetRuleModuleOptions + const pickyCommentFormat = '[a-zA-Z-_/.]+' const options = [ { importFunctions: ['dynamicImport'], }, -] as const +] as const satisfies RuleOptions const pickyCommentOptions = [ { importFunctions: ['dynamicImport'], webpackChunknameFormat: pickyCommentFormat, }, -] as const +] as const satisfies RuleOptions const allowEmptyOptions = [ { importFunctions: ['dynamicImport'], allowEmpty: true, }, -] as const +] as const satisfies RuleOptions const multipleImportFunctionOptions = [ { importFunctions: ['dynamicImport', 'definitelyNotStaticImport'], }, -] as const +] as const satisfies RuleOptions const babelParser = require(parsers.BABEL) diff --git a/test/utils.ts b/test/utils.ts index 816f3a58f..2a4b21973 100644 --- a/test/utils.ts +++ b/test/utils.ts @@ -92,7 +92,7 @@ export function test( return createRuleTestCase(t) } -export type GetRuleModuleTypes = +type GetRuleModuleTypes = TRule extends RuleModule ? { messageIds: MessageIds @@ -100,6 +100,9 @@ export type GetRuleModuleTypes = } : never +export type GetRuleModuleOptions = + TRule extends RuleModule ? Options : never + /** * Type helper to build {@link TSESLintRuleTester.run} test parameters * from a given {@link RuleModule} @@ -182,68 +185,69 @@ export function testContext(settings?: PluginSettings) { * to be added as valid cases just to ensure no nullable fields are going * to crash at runtime */ -export const SYNTAX_VALID_CASES: TSESLintRunTests['valid'] = [ - 'for (let { foo, bar } of baz) {}', - 'for (let [ foo, bar ] of baz) {}', - - 'const { x, y } = bar', - test({ - code: 'const { x, y, ...z } = bar', - languageOptions: { parser: require(parsers.BABEL) }, - }), - - // all the exports - 'let x; export { x }', - 'let x; export { x as y }', - - // not sure about these since they reference a file - // 'export { x } from "./y.js"'}), - // 'export * as y from "./y.js"', languageOptions: { parser: require(parsers.BABEL) } }), - - 'export const x = null', - 'export var x = null', - 'export let x = null', - - 'export default x', - 'export default class x {}', - - // issue #267: parser opt-in extension list - test({ - code: 'import json from "./data.json"', - settings: { 'import-x/extensions': ['.js'] }, // breaking: remove for v2 - }), - - // JSON - test({ - code: 'import foo from "./foobar.json";', - settings: { 'import-x/extensions': ['.js'] }, // breaking: remove for v2 - }), - test({ - code: 'import foo from "./foobar";', - settings: { 'import-x/extensions': ['.js'] }, // breaking: remove for v2 - }), - - // issue #370: deep commonjs import - test({ - code: 'import { foo } from "./issue-370-commonjs-namespace/bar"', - settings: { 'import-x/ignore': ['foo'] }, - }), - - // issue #348: deep commonjs re-export - test({ - code: 'export * from "./issue-370-commonjs-namespace/bar"', - settings: { 'import-x/ignore': ['foo'] }, - }), - - test({ - code: 'import * as a from "./commonjs-namespace/a"; a.b', - }), - - // ignore invalid extensions - test({ - code: 'import { foo } from "./ignore.invalid.extension"', - }), -] +export const SYNTAX_VALID_CASES: TSESLintRunTests['valid'] = + [ + 'for (let { foo, bar } of baz) {}', + 'for (let [ foo, bar ] of baz) {}', + + 'const { x, y } = bar', + createRuleTestCase({ + code: 'const { x, y, ...z } = bar', + languageOptions: { parser: require(parsers.BABEL) }, + }), + + // all the exports + 'let x; export { x }', + 'let x; export { x as y }', + + // not sure about these since they reference a file + // 'export { x } from "./y.js"'}), + // 'export * as y from "./y.js"', languageOptions: { parser: require(parsers.BABEL) } }), + + 'export const x = null', + 'export var x = null', + 'export let x = null', + + 'export default x', + 'export default class x {}', + + // issue #267: parser opt-in extension list + createRuleTestCase({ + code: 'import json from "./data.json"', + settings: { 'import-x/extensions': ['.js'] }, // breaking: remove for v2 + }), + + // JSON + createRuleTestCase({ + code: 'import foo from "./foobar.json";', + settings: { 'import-x/extensions': ['.js'] }, // breaking: remove for v2 + }), + createRuleTestCase({ + code: 'import foo from "./foobar";', + settings: { 'import-x/extensions': ['.js'] }, // breaking: remove for v2 + }), + + // issue #370: deep commonjs import + createRuleTestCase({ + code: 'import { foo } from "./issue-370-commonjs-namespace/bar"', + settings: { 'import-x/ignore': ['foo'] }, + }), + + // issue #348: deep commonjs re-export + createRuleTestCase({ + code: 'export * from "./issue-370-commonjs-namespace/bar"', + settings: { 'import-x/ignore': ['foo'] }, + }), + + createRuleTestCase({ + code: 'import * as a from "./commonjs-namespace/a"; a.b', + }), + + // ignore invalid extensions + createRuleTestCase({ + code: 'import { foo } from "./ignore.invalid.extension"', + }), + ] const testCompiled = process.env.TEST_COMPILED === '1' From bf706903888db5ae1f58abf369943bc43d5fcda7 Mon Sep 17 00:00:00 2001 From: Marco Pasqualetti Date: Thu, 14 Nov 2024 17:54:49 +0100 Subject: [PATCH 04/49] test(export): migrate to `createRuleTestCaseFunction` --- test/rules/export.spec.ts | 97 +++++++++++++++++++++++++-------------- 1 file changed, 63 insertions(+), 34 deletions(-) diff --git a/test/rules/export.spec.ts b/test/rules/export.spec.ts index de5d11061..9d573dd28 100644 --- a/test/rules/export.spec.ts +++ b/test/rules/export.spec.ts @@ -1,11 +1,18 @@ import { RuleTester as TSESLintRuleTester } from '@typescript-eslint/rule-tester' -import { test, testFilePath, SYNTAX_VALID_CASES, parsers } from '../utils' +import { + testFilePath, + SYNTAX_VALID_CASES, + parsers, + createRuleTestCaseFunction, +} from '../utils' import rule from 'eslint-plugin-import-x/rules/export' const ruleTester = new TSESLintRuleTester() +const test = createRuleTestCaseFunction() + ruleTester.run('export', rule, { valid: [ test({ @@ -104,8 +111,8 @@ ruleTester.run('export', rule, { test({ code: 'let foo; export { foo }; export * from "./export-all"', errors: [ - "Multiple exports of name 'foo'.", - "Multiple exports of name 'foo'.", + { messageId: 'multiNamed', data: { name: 'foo' } }, + { messageId: 'multiNamed', data: { name: 'foo' } }, ], }), // test({ @@ -125,9 +132,9 @@ ruleTester.run('export', rule, { languageOptions: { parser: require(parsers.ESPREE) }, errors: [ { + // @ts-expect-error parse error here so can'use rule types message: "Parse errors in imported module './malformed.js': 'return' outside of function (1:1)", - type: 'Literal', }, ], }), @@ -152,14 +159,14 @@ ruleTester.run('export', rule, { // #328: "export * from" does not export a default test({ code: 'export * from "./default-export"', - errors: [`No named exports found in module './default-export'.`], + errors: [{ messageId: 'noNamed', data: { module: './default-export' } }], }), test({ code: 'let foo; export { foo as "foo" }; export * from "./export-all"', errors: [ - "Multiple exports of name 'foo'.", - "Multiple exports of name 'foo'.", + { messageId: 'multiNamed', data: { name: 'foo' } }, + { messageId: 'multiNamed', data: { name: 'foo' } }, ], languageOptions: { parser: require(parsers.ESPREE), @@ -175,7 +182,7 @@ ruleTester.run('export', rule, { export default function a() {} export { x as default }; `, - errors: ['Multiple default exports.', 'Multiple default exports.'], + errors: [{ messageId: 'multiDefault' }, { messageId: 'multiDefault' }], }), ], }) @@ -367,11 +374,13 @@ describe('TypeScript', () => { `, errors: [ { - message: `Multiple exports of name 'Foo'.`, + messageId: 'multiNamed', + data: { name: 'Foo' }, line: 2, }, { - message: `Multiple exports of name 'Foo'.`, + messageId: 'multiNamed', + data: { name: 'Foo' }, line: 3, }, ], @@ -389,11 +398,13 @@ describe('TypeScript', () => { `, errors: [ { - message: `Multiple exports of name 'a'.`, + messageId: 'multiNamed', + data: { name: 'a' }, line: 4, }, { - message: `Multiple exports of name 'a'.`, + messageId: 'multiNamed', + data: { name: 'a' }, line: 5, }, ], @@ -409,11 +420,11 @@ describe('TypeScript', () => { `, errors: [ { - message: 'Multiple default exports.', + messageId: 'multiDefault', line: 4, }, { - message: 'Multiple default exports.', + messageId: 'multiDefault', line: 5, }, ], @@ -434,19 +445,23 @@ describe('TypeScript', () => { `, errors: [ { - message: `Multiple exports of name 'Foo'.`, + messageId: 'multiNamed', + data: { name: 'Foo' }, line: 4, }, { - message: `Multiple exports of name 'Foo'.`, + messageId: 'multiNamed', + data: { name: 'Foo' }, line: 5, }, { - message: `Multiple exports of name 'Bar'.`, + messageId: 'multiNamed', + data: { name: 'Bar' }, line: 8, }, { - message: `Multiple exports of name 'Bar'.`, + messageId: 'multiNamed', + data: { name: 'Bar' }, line: 9, }, ], @@ -461,11 +476,13 @@ describe('TypeScript', () => { `, errors: [ { - message: `Multiple exports of name 'Foo'.`, + messageId: 'multiNamed', + data: { name: 'Foo' }, line: 2, }, { - message: `Multiple exports of name 'Foo'.`, + messageId: 'multiNamed', + data: { name: 'Foo' }, line: 3, }, ], @@ -479,11 +496,13 @@ describe('TypeScript', () => { `, errors: [ { - message: `Multiple exports of name 'Foo'.`, + messageId: 'multiNamed', + data: { name: 'Foo' }, line: 2, }, { - message: `Multiple exports of name 'Foo'.`, + messageId: 'multiNamed', + data: { name: 'Foo' }, line: 3, }, ], @@ -497,11 +516,13 @@ describe('TypeScript', () => { `, errors: [ { - message: `Multiple exports of name 'Foo'.`, + messageId: 'multiNamed', + data: { name: 'Foo' }, line: 2, }, { - message: `Multiple exports of name 'Foo'.`, + messageId: 'multiNamed', + data: { name: 'Foo' }, line: 3, }, ], @@ -515,11 +536,13 @@ describe('TypeScript', () => { `, errors: [ { - message: `Multiple exports of name 'Foo'.`, + messageId: 'multiNamed', + data: { name: 'Foo' }, line: 2, }, { - message: `Multiple exports of name 'Foo'.`, + messageId: 'multiNamed', + data: { name: 'Foo' }, line: 3, }, ], @@ -533,11 +556,13 @@ describe('TypeScript', () => { `, errors: [ { - message: `Multiple exports of name 'Foo'.`, + messageId: 'multiNamed', + data: { name: 'Foo' }, line: 2, }, { - message: `Multiple exports of name 'Foo'.`, + messageId: 'multiNamed', + data: { name: 'Foo' }, line: 3, }, ], @@ -551,11 +576,13 @@ describe('TypeScript', () => { `, errors: [ { - message: `Multiple exports of name 'Foo'.`, + messageId: 'multiNamed', + data: { name: 'Foo' }, line: 2, }, { - message: `Multiple exports of name 'Foo'.`, + messageId: 'multiNamed', + data: { name: 'Foo' }, line: 3, }, ], @@ -568,11 +595,13 @@ describe('TypeScript', () => { `, errors: [ { - message: `Multiple exports of name 'Foo'.`, + messageId: 'multiNamed', + data: { name: 'Foo' }, line: 2, }, { - message: `Multiple exports of name 'Foo'.`, + messageId: 'multiNamed', + data: { name: 'Foo' }, line: 3, }, ], @@ -593,11 +622,11 @@ describe('TypeScript', () => { `, errors: [ { - message: 'Multiple default exports.', + messageId: 'multiDefault', line: 7, }, { - message: 'Multiple default exports.', + messageId: 'multiDefault', line: 9, }, ], From aca0f8d56aa6937d44e2064ab403c0ac74da2fca Mon Sep 17 00:00:00 2001 From: Marco Pasqualetti Date: Thu, 14 Nov 2024 18:12:45 +0100 Subject: [PATCH 05/49] test(exports-last): migrate to `createRuleTestCaseFunction` --- test/rules/exports-last.spec.ts | 29 ++++++++++++++++++----------- test/utils.ts | 3 +++ 2 files changed, 21 insertions(+), 11 deletions(-) diff --git a/test/rules/exports-last.spec.ts b/test/rules/exports-last.spec.ts index 11d41a948..99891f9f7 100644 --- a/test/rules/exports-last.spec.ts +++ b/test/rules/exports-last.spec.ts @@ -1,17 +1,24 @@ import { RuleTester as TSESLintRuleTester } from '@typescript-eslint/rule-tester' +import type { TestCaseError as TSESLintTestCaseError } from '@typescript-eslint/rule-tester' import type { TSESTree } from '@typescript-eslint/utils' -import { test } from '../utils' +import { createRuleTestCaseFunction } from '../utils' +import type { GetRuleModuleMessageIds } from '../utils' import rule from 'eslint-plugin-import-x/rules/exports-last' const ruleTester = new TSESLintRuleTester() -const error = (type: `${TSESTree.AST_NODE_TYPES}`) => - ({ +const test = createRuleTestCaseFunction() + +function createInvalidCaseError( + type: `${TSESTree.AST_NODE_TYPES}`, +): TSESLintTestCaseError> { + return { messageId: 'end', - type, - }) as const + type: type as TSESTree.AST_NODE_TYPES, + } +} ruleTester.run('exports-last', rule, { valid: [ @@ -88,7 +95,7 @@ ruleTester.run('exports-last', rule, { export default 'bar' const bar = true `, - errors: [error('ExportDefaultDeclaration')], + errors: [createInvalidCaseError('ExportDefaultDeclaration')], }), // Named export before variable declaration test({ @@ -96,7 +103,7 @@ ruleTester.run('exports-last', rule, { export const foo = 'bar' const bar = true `, - errors: [error('ExportNamedDeclaration')], + errors: [createInvalidCaseError('ExportNamedDeclaration')], }), // Export all before variable declaration test({ @@ -104,9 +111,9 @@ ruleTester.run('exports-last', rule, { export * from './foo' const bar = true `, - errors: [error('ExportAllDeclaration')], + errors: [createInvalidCaseError('ExportAllDeclaration')], }), - // Many exports arround variable declaration + // Many exports around variable declaration test({ code: ` export default 'such foo many bar' @@ -118,8 +125,8 @@ ruleTester.run('exports-last', rule, { export const how = 'many' `, errors: [ - error('ExportDefaultDeclaration'), - error('ExportNamedDeclaration'), + createInvalidCaseError('ExportDefaultDeclaration'), + createInvalidCaseError('ExportNamedDeclaration'), ], }), ], diff --git a/test/utils.ts b/test/utils.ts index 2a4b21973..08e078762 100644 --- a/test/utils.ts +++ b/test/utils.ts @@ -100,6 +100,9 @@ type GetRuleModuleTypes = } : never +export type GetRuleModuleMessageIds = + TRule extends RuleModule ? MessageIds : never + export type GetRuleModuleOptions = TRule extends RuleModule ? Options : never From f34ec1add04d17def84dc3c0eae716941eb78228 Mon Sep 17 00:00:00 2001 From: Marco Pasqualetti Date: Fri, 15 Nov 2024 18:22:40 +0100 Subject: [PATCH 06/49] fix(first): add missing value in options type --- .changeset/fluffy-dolls-pump.md | 5 +++++ src/rules/first.ts | 4 +++- test/rules/first.spec.ts | 26 ++++++++++++++------------ 3 files changed, 22 insertions(+), 13 deletions(-) create mode 100644 .changeset/fluffy-dolls-pump.md diff --git a/.changeset/fluffy-dolls-pump.md b/.changeset/fluffy-dolls-pump.md new file mode 100644 index 000000000..47f059f30 --- /dev/null +++ b/.changeset/fluffy-dolls-pump.md @@ -0,0 +1,5 @@ +--- +"eslint-plugin-import-x": patch +--- + +fix(first): add missing value in options type diff --git a/src/rules/first.ts b/src/rules/first.ts index 05e2f75a7..edb352735 100644 --- a/src/rules/first.ts +++ b/src/rules/first.ts @@ -20,9 +20,11 @@ function isPossibleDirective(node: TSESTree.ProgramStatement) { ) } +type Options = 'absolute-first' | 'disable-absolute-first' + type MessageId = 'absolute' | 'order' -export = createRule<['absolute-first'?], MessageId>({ +export = createRule<[Options?], MessageId>({ name: 'first', meta: { type: 'suggestion', diff --git a/test/rules/first.spec.ts b/test/rules/first.spec.ts index 7e5aad5b1..e967eb9ee 100644 --- a/test/rules/first.spec.ts +++ b/test/rules/first.spec.ts @@ -2,12 +2,14 @@ import fs from 'node:fs' import { RuleTester as TSESLintRuleTester } from '@typescript-eslint/rule-tester' -import { test, parsers, testFilePath } from '../utils' +import { createRuleTestCaseFunction, parsers, testFilePath } from '../utils' import rule from 'eslint-plugin-import-x/rules/first' const ruleTester = new TSESLintRuleTester() +const test = createRuleTestCaseFunction() + ruleTester.run('first', rule, { valid: [ test({ @@ -37,7 +39,7 @@ ruleTester.run('first', rule, { code: "import { x } from './foo';\ export { x };\ import { y } from './bar';", - errors: 1, + errors: [{ messageId: 'order' }], output: "import { x } from './foo';\ import { y } from './bar';\ @@ -48,7 +50,7 @@ ruleTester.run('first', rule, { export { x };\ import { y } from './bar';\ import { z } from './baz';", - errors: 2, + errors: [{ messageId: 'order' }, { messageId: 'order' }], output: "import { x } from './foo';\ import { y } from './bar';\ @@ -58,13 +60,13 @@ ruleTester.run('first', rule, { test({ code: "import { x } from './foo'; import { y } from 'bar'", options: ['absolute-first'], - errors: 1, + errors: [{ messageId: 'absolute' }], }), test({ code: "import { x } from 'foo';\ 'use directive';\ import { y } from 'bar';", - errors: 1, + errors: [{ messageId: 'order' }], output: "import { x } from 'foo';\ import { y } from 'bar';\ @@ -76,7 +78,11 @@ ruleTester.run('first', rule, { if (true) { x() };\ import { x } from './foo';\ import { z } from './baz';", - errors: 3, + errors: [ + { messageId: 'order' }, + { messageId: 'order' }, + { messageId: 'order' }, + ], output: [ "import { y } from './bar';\ var a = 1;\ @@ -97,7 +103,7 @@ ruleTester.run('first', rule, { }), test({ code: "if (true) { console.log(1) }import a from 'b'", - errors: 1, + errors: [{ messageId: 'order' }], output: "import a from 'b'\nif (true) { console.log(1) }", }), ], @@ -130,11 +136,7 @@ describe('TypeScript', () => { `, options: ['absolute-first'], ...parserConfig, - errors: [ - { - message: 'Absolute imports should come before relative imports.', - }, - ], + errors: [{ messageId: 'absolute' }], }), ], }) From ccd3ade201f689fd98debc8d29832023fa110577 Mon Sep 17 00:00:00 2001 From: Marco Pasqualetti Date: Fri, 15 Nov 2024 18:28:14 +0100 Subject: [PATCH 07/49] test(group-exports): migrate to `createRuleTestCaseFunction` --- src/rules/group-exports.ts | 4 +- test/rules/group-exports.spec.ts | 84 +++++++++++++++++++++++--------- 2 files changed, 65 insertions(+), 23 deletions(-) diff --git a/src/rules/group-exports.ts b/src/rules/group-exports.ts index 377712f6b..d3eaf4f36 100644 --- a/src/rules/group-exports.ts +++ b/src/rules/group-exports.ts @@ -35,7 +35,9 @@ function accessorChain(node: TSESTree.MemberExpression) { return chain } -export = createRule<[], 'ExportNamedDeclaration' | 'AssignmentExpression'>({ +type MessageId = 'ExportNamedDeclaration' | 'AssignmentExpression' + +export = createRule<[], MessageId>({ name: 'group-exports', meta: { type: 'suggestion', diff --git a/test/rules/group-exports.spec.ts b/test/rules/group-exports.spec.ts index 31e04752a..207e37698 100644 --- a/test/rules/group-exports.spec.ts +++ b/test/rules/group-exports.spec.ts @@ -1,16 +1,9 @@ import { RuleTester as TSESLintRuleTester } from '@typescript-eslint/rule-tester' -import { parsers, test } from '../utils' +import { createRuleTestCaseFunction, parsers } from '../utils' import rule from 'eslint-plugin-import-x/rules/group-exports' -const errors = { - named: - 'Multiple named export declarations; consolidate all named exports into a single export declaration', - commonjs: - 'Multiple CommonJS exports; consolidate all exports into a single assignment to `module.exports`', -} - const ruleTester = new TSESLintRuleTester({ languageOptions: { parser: require(parsers.BABEL), @@ -25,6 +18,8 @@ const ruleTester = new TSESLintRuleTester({ }, }) +const test = createRuleTestCaseFunction() + ruleTester.run('group-exports', rule, { valid: [ test({ code: 'export const test = true' }), @@ -190,14 +185,20 @@ ruleTester.run('group-exports', rule, { export const test = true export const another = true `, - errors: [errors.named, errors.named], + errors: [ + { messageId: 'ExportNamedDeclaration' }, + { messageId: 'ExportNamedDeclaration' }, + ], }), test({ code: ` export { method1 } from './module-1' export { method2 } from './module-1' `, - errors: [errors.named, errors.named], + errors: [ + { messageId: 'ExportNamedDeclaration' }, + { messageId: 'ExportNamedDeclaration' }, + ], }), test({ code: ` @@ -205,49 +206,71 @@ ruleTester.run('group-exports', rule, { module.exports.test = true module.exports.another = true `, - errors: [errors.commonjs, errors.commonjs, errors.commonjs], + errors: [ + { messageId: 'AssignmentExpression' }, + { messageId: 'AssignmentExpression' }, + { messageId: 'AssignmentExpression' }, + ], }), test({ code: ` module.exports = {} module.exports.test = true `, - errors: [errors.commonjs, errors.commonjs], + errors: [ + { messageId: 'AssignmentExpression' }, + { messageId: 'AssignmentExpression' }, + ], }), test({ code: ` module.exports = { test: true } module.exports.another = true `, - errors: [errors.commonjs, errors.commonjs], + errors: [ + { messageId: 'AssignmentExpression' }, + { messageId: 'AssignmentExpression' }, + ], }), test({ code: ` module.exports.test = true module.exports.another = true `, - errors: [errors.commonjs, errors.commonjs], + errors: [ + { messageId: 'AssignmentExpression' }, + { messageId: 'AssignmentExpression' }, + ], }), test({ code: ` exports.test = true module.exports.another = true `, - errors: [errors.commonjs, errors.commonjs], + errors: [ + { messageId: 'AssignmentExpression' }, + { messageId: 'AssignmentExpression' }, + ], }), test({ code: ` module.exports = () => {} module.exports.attached = true `, - errors: [errors.commonjs, errors.commonjs], + errors: [ + { messageId: 'AssignmentExpression' }, + { messageId: 'AssignmentExpression' }, + ], }), test({ code: ` module.exports = function test() {} module.exports.attached = true `, - errors: [errors.commonjs, errors.commonjs], + errors: [ + { messageId: 'AssignmentExpression' }, + { messageId: 'AssignmentExpression' }, + ], }), test({ code: ` @@ -255,14 +278,21 @@ ruleTester.run('group-exports', rule, { exports.test = true exports.another = true `, - errors: [errors.commonjs, errors.commonjs, errors.commonjs], + errors: [ + { messageId: 'AssignmentExpression' }, + { messageId: 'AssignmentExpression' }, + { messageId: 'AssignmentExpression' }, + ], }), test({ code: ` module.exports = "non-object" module.exports.attached = true `, - errors: [errors.commonjs, errors.commonjs], + errors: [ + { messageId: 'AssignmentExpression' }, + { messageId: 'AssignmentExpression' }, + ], }), test({ code: ` @@ -270,7 +300,11 @@ ruleTester.run('group-exports', rule, { module.exports.attached = true module.exports.another = true `, - errors: [errors.commonjs, errors.commonjs, errors.commonjs], + errors: [ + { messageId: 'AssignmentExpression' }, + { messageId: 'AssignmentExpression' }, + { messageId: 'AssignmentExpression' }, + ], }), test({ code: ` @@ -285,14 +319,20 @@ ruleTester.run('group-exports', rule, { export type { secondType }; export { first }; `, - errors: [errors.named, errors.named], + errors: [ + { messageId: 'ExportNamedDeclaration' }, + { messageId: 'ExportNamedDeclaration' }, + ], }), test({ code: ` export type { type1 } from './module-1' export type { type2 } from './module-1' `, - errors: [errors.named, errors.named], + errors: [ + { messageId: 'ExportNamedDeclaration' }, + { messageId: 'ExportNamedDeclaration' }, + ], }), ], }) From 4400f8b3164fca768f042444f1dd1b068aa01a5d Mon Sep 17 00:00:00 2001 From: Marco Pasqualetti Date: Fri, 15 Nov 2024 18:31:29 +0100 Subject: [PATCH 08/49] test(max-dependencies): migrate to `createRuleTestCaseFunction` --- test/rules/max-dependencies.spec.ts | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/test/rules/max-dependencies.spec.ts b/test/rules/max-dependencies.spec.ts index 34143eca8..27cfd89a9 100644 --- a/test/rules/max-dependencies.spec.ts +++ b/test/rules/max-dependencies.spec.ts @@ -1,11 +1,13 @@ import { RuleTester as TSESLintRuleTester } from '@typescript-eslint/rule-tester' -import { test, parsers } from '../utils' +import { parsers, createRuleTestCaseFunction } from '../utils' import rule from 'eslint-plugin-import-x/rules/max-dependencies' const ruleTester = new TSESLintRuleTester() +const test = createRuleTestCaseFunction() + ruleTester.run('max-dependencies', rule, { valid: [ test({ code: 'import "./foo.js"' }), @@ -38,7 +40,7 @@ ruleTester.run('max-dependencies', rule, { max: 1, }, ], - errors: ['Maximum number of dependencies (1) exceeded.'], + errors: [{ messageId: 'max', data: { max: 1 } }], }), test({ @@ -48,7 +50,7 @@ ruleTester.run('max-dependencies', rule, { max: 2, }, ], - errors: ['Maximum number of dependencies (2) exceeded.'], + errors: [{ messageId: 'max', data: { max: 2 } }], }), test({ @@ -58,7 +60,7 @@ ruleTester.run('max-dependencies', rule, { max: 2, }, ], - errors: ['Maximum number of dependencies (2) exceeded.'], + errors: [{ messageId: 'max', data: { max: 2 } }], }), test({ @@ -68,7 +70,7 @@ ruleTester.run('max-dependencies', rule, { max: 2, }, ], - errors: ['Maximum number of dependencies (2) exceeded.'], + errors: [{ messageId: 'max', data: { max: 2 } }], }), test({ @@ -79,7 +81,7 @@ ruleTester.run('max-dependencies', rule, { max: 1, }, ], - errors: ['Maximum number of dependencies (1) exceeded.'], + errors: [{ messageId: 'max', data: { max: 1 } }], }), test({ @@ -91,7 +93,7 @@ ruleTester.run('max-dependencies', rule, { ignoreTypeImports: false, }, ], - errors: ['Maximum number of dependencies (2) exceeded.'], + errors: [{ messageId: 'max', data: { max: 2 } }], }), ], }) @@ -119,7 +121,7 @@ describe('TypeScript', () => { max: 1, }, ], - errors: ['Maximum number of dependencies (1) exceeded.'], + errors: [{ messageId: 'max', data: { max: 1 } }], }), test({ @@ -131,7 +133,7 @@ describe('TypeScript', () => { ignoreTypeImports: false, }, ], - errors: ['Maximum number of dependencies (2) exceeded.'], + errors: [{ messageId: 'max', data: { max: 2 } }], }), ], }) From 4c514f8426daa96438a4eb518c2ab73e24fa838d Mon Sep 17 00:00:00 2001 From: Marco Pasqualetti Date: Fri, 15 Nov 2024 18:56:16 +0100 Subject: [PATCH 09/49] test(named): migrate to `createRuleTestCaseFunction` --- test/rules/named.spec.ts | 140 ++++++++++++++++++++++----------------- 1 file changed, 80 insertions(+), 60 deletions(-) diff --git a/test/rules/named.spec.ts b/test/rules/named.spec.ts index c7fa7c43c..81109a25c 100644 --- a/test/rules/named.spec.ts +++ b/test/rules/named.spec.ts @@ -1,21 +1,46 @@ import path from 'node:path' import { RuleTester as TSESLintRuleTester } from '@typescript-eslint/rule-tester' +import type { TestCaseError as TSESLintTestCaseError } from '@typescript-eslint/rule-tester' import type { TSESTree } from '@typescript-eslint/utils' -import { test, SYNTAX_VALID_CASES, testFilePath, parsers } from '../utils' +import { + createRuleTestCaseFunction, + SYNTAX_VALID_CASES, + testFilePath, + parsers, +} from '../utils' +import type { RunTests, GetRuleModuleMessageIds } from '../utils' import rule from 'eslint-plugin-import-x/rules/named' import { CASE_SENSITIVE_FS } from 'eslint-plugin-import-x/utils' const ruleTester = new TSESLintRuleTester() -function error( +const test = createRuleTestCaseFunction() + +function createNotFoundError( name: string, module: string, type: `${TSESTree.AST_NODE_TYPES}` = 'Identifier', -) { - return { message: `${name} not found in '${module}'`, type } +): TSESLintTestCaseError> { + return { + messageId: 'notFound', + data: { name, path: module }, + type: type as TSESTree.AST_NODE_TYPES, + } +} + +function createNotFoundDeepError( + name: string, + deepPath: string, + type: `${TSESTree.AST_NODE_TYPES}` = 'Identifier', +): TSESLintTestCaseError> { + return { + messageId: 'notFoundDeep', + data: { name, deepPath }, + type: type as TSESTree.AST_NODE_TYPES, + } } ruleTester.run('named', rule, { @@ -181,12 +206,12 @@ ruleTester.run('named', rule, { test({ code: 'const { baz } = require("./bar")', - errors: [error('baz', './bar')], + errors: [createNotFoundError('baz', './bar')], }), test({ code: 'const { baz } = require("./bar")', - errors: [error('baz', './bar')], + errors: [createNotFoundError('baz', './bar')], options: [{ commonjs: false }], }), @@ -238,93 +263,101 @@ ruleTester.run('named', rule, { invalid: [ test({ code: 'import { somethingElse } from "./test-module"', - errors: [error('somethingElse', './test-module')], + errors: [createNotFoundError('somethingElse', './test-module')], }), test({ code: 'import { baz } from "./bar"', - errors: [error('baz', './bar')], + errors: [createNotFoundError('baz', './bar')], }), // test multiple test({ code: 'import { baz, bop } from "./bar"', - errors: [error('baz', './bar'), error('bop', './bar')], + errors: [ + createNotFoundError('baz', './bar'), + createNotFoundError('bop', './bar'), + ], }), test({ code: 'import {a, b, c} from "./named-exports"', - errors: [error('c', './named-exports')], + errors: [createNotFoundError('c', './named-exports')], }), test({ code: 'import { a } from "./default-export"', - errors: [error('a', './default-export')], + errors: [createNotFoundError('a', './default-export')], }), test({ code: 'import { ActionTypess } from "./qc"', - errors: [error('ActionTypess', './qc')], + errors: [createNotFoundError('ActionTypess', './qc')], }), test({ code: 'import {a, b, c, d, e} from "./re-export"', - errors: [error('e', './re-export')], + errors: [createNotFoundError('e', './re-export')], }), test({ code: 'import { a } from "./re-export-names"', - errors: [error('a', './re-export-names')], + errors: [createNotFoundError('a', './re-export-names')], }), // export tests test({ code: 'export { bar } from "./bar"', - errors: ["bar not found in './bar'"], + errors: [createNotFoundError('bar', './bar')], }), // es7 test({ code: 'export bar2, { bar } from "./bar"', languageOptions: { parser: require(parsers.BABEL) }, - errors: ["bar not found in './bar'"], + errors: [createNotFoundError('bar', './bar')], }), test({ code: 'import { foo, bar, baz } from "./named-trampoline"', languageOptions: { parser: require(parsers.BABEL) }, - errors: ["baz not found in './named-trampoline'"], + errors: [createNotFoundError('baz', './named-trampoline')], }), test({ code: 'import { baz } from "./broken-trampoline"', languageOptions: { parser: require(parsers.BABEL) }, - errors: ['baz not found via broken-trampoline.js -> named-exports.js'], + errors: [ + createNotFoundDeepError( + 'baz', + 'broken-trampoline.js -> named-exports.js', + ), + ], }), test({ code: 'const { baz } = require("./bar")', - errors: [error('baz', './bar')], + errors: [createNotFoundError('baz', './bar')], options: [{ commonjs: true }], }), test({ code: 'let { baz } = require("./bar")', - errors: [error('baz', './bar')], + errors: [createNotFoundError('baz', './bar')], options: [{ commonjs: true }], }), test({ code: 'const { baz: bar, bop } = require("./bar"), { a } = require("./re-export-names")', errors: [ - error('baz', './bar'), - error('bop', './bar'), - error('a', './re-export-names'), + createNotFoundError('baz', './bar'), + createNotFoundError('bop', './bar'), + createNotFoundError('a', './re-export-names'), ], options: [{ commonjs: true }], }), test({ code: 'const { default: defExport } = require("./named-exports")', - errors: [error('default', './named-exports')], + errors: [createNotFoundError('default', './named-exports')], options: [{ commonjs: true }], }), @@ -341,42 +374,44 @@ ruleTester.run('named', rule, { test({ code: 'import { type MyOpaqueType, MyMissingClass } from "./flowtypes"', languageOptions: { parser: require(parsers.BABEL) }, - errors: ["MyMissingClass not found in './flowtypes'"], + errors: [createNotFoundError('MyMissingClass', './flowtypes')], }), // jsnext test({ code: '/*jsnext*/ import { createSnorlax } from "redux"', settings: { 'import-x/ignore': [] }, - errors: ["createSnorlax not found in 'redux'"], + errors: [createNotFoundError('createSnorlax', 'redux')], }), // should work without ignore test({ code: '/*jsnext*/ import { createSnorlax } from "redux"', - errors: ["createSnorlax not found in 'redux'"], + errors: [createNotFoundError('createSnorlax', 'redux')], }), // ignore is ignored if exports are found test({ code: 'import { baz } from "es6-module"', - errors: ["baz not found in 'es6-module'"], + errors: [createNotFoundError('baz', 'es6-module')], }), // issue #251 test({ code: 'import { foo, bar, bap } from "./re-export-default"', - errors: ["bap not found in './re-export-default'"], + errors: [createNotFoundError('bap', './re-export-default')], }), // #328: * exports do not include default test({ code: 'import { default as barDefault } from "./re-export"', - errors: [`default not found in './re-export'`], + errors: [createNotFoundError('default', './re-export')], }), test({ code: 'import { "somethingElse" as somethingElse } from "./test-module"', - errors: [error('somethingElse', './test-module', 'Literal')], + errors: [ + createNotFoundError('somethingElse', './test-module', 'Literal'), + ], languageOptions: { parser: require(parsers.ESPREE), parserOptions: { ecmaVersion: 2022 }, @@ -385,8 +420,8 @@ ruleTester.run('named', rule, { test({ code: 'import { "baz" as baz, "bop" as bop } from "./bar"', errors: [ - error('baz', './bar', 'Literal'), - error('bop', './bar', 'Literal'), + createNotFoundError('baz', './bar', 'Literal'), + createNotFoundError('bop', './bar', 'Literal'), ], languageOptions: { parser: require(parsers.ESPREE), @@ -395,7 +430,7 @@ ruleTester.run('named', rule, { }), test({ code: 'import { "default" as barDefault } from "./re-export"', - errors: [`default not found in './re-export'`], + errors: [createNotFoundError('default', './re-export', 'Literal')], languageOptions: { parser: require(parsers.ESPREE), parserOptions: { ecmaVersion: 2022 }, @@ -416,7 +451,7 @@ if (!CASE_SENSITIVE_FS) { invalid: [ test({ code: 'import { foo } from "./Named-Exports"', - errors: [`foo not found in './Named-Exports'`], + errors: [createNotFoundError('foo', './Named-Exports')], }), ], }) @@ -434,7 +469,7 @@ describe('export *', () => { invalid: [ test({ code: 'import { bar } from "./export-all"', - errors: [`bar not found in './export-all'`], + errors: [createNotFoundError('bar', './export-all')], }), ], }) @@ -446,7 +481,7 @@ describe('TypeScript', () => { 'import-x/resolver': { 'eslint-import-resolver-typescript': true }, } - let valid = [ + let valid: RunTests['valid'] = [ test({ code: `import x from './typescript-export-assign-object'`, languageOptions: { @@ -461,7 +496,7 @@ describe('TypeScript', () => { }), ] - const invalid = [ + let invalid: RunTests['invalid'] = [ // TODO: uncomment this test // test({ // code: `import {a} from './export-star-3/b';`, @@ -484,10 +519,7 @@ describe('TypeScript', () => { }, settings, errors: [ - { - message: `NotExported not found in './typescript-export-assign-object'`, - type: 'Identifier', - }, + createNotFoundError('NotExported', './typescript-export-assign-object'), ], }), test({ @@ -503,10 +535,7 @@ describe('TypeScript', () => { }, settings, errors: [ - { - message: `FooBar not found in './typescript-export-assign-object'`, - type: 'Identifier', - }, + createNotFoundError('FooBar', './typescript-export-assign-object'), ], }), ] @@ -562,30 +591,21 @@ describe('TypeScript', () => { }), ] - invalid.push( + invalid = [ + ...invalid, test({ code: `import { MissingType } from "./${source}"`, settings, - errors: [ - { - message: `MissingType not found in './${source}'`, - type: 'Identifier', - }, - ], + errors: [createNotFoundError('MissingType', `./${source}`)], }), test({ code: `import { NotExported } from "./${source}"`, settings, - errors: [ - { - message: `NotExported not found in './${source}'`, - type: 'Identifier', - }, - ], + errors: [createNotFoundError('NotExported', `./${source}`)], }), - ) + ] } ruleTester.run(`named`, rule, { From 1a25c6c62caab69f5e663ea789fa0dc7555fa39f Mon Sep 17 00:00:00 2001 From: Marco Pasqualetti Date: Fri, 15 Nov 2024 22:29:58 +0100 Subject: [PATCH 10/49] ci: check if the issue is with the last eslint version --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 11bd9b54f..e2ccad36f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -24,7 +24,7 @@ jobs: eslint: - '8.56' - '8' - - '9' + - '9.14' include: - executeLint: true From 9df615051e1f03821ef4ece0ee945f6933328f92 Mon Sep 17 00:00:00 2001 From: Marco Pasqualetti Date: Sat, 16 Nov 2024 19:54:47 +0100 Subject: [PATCH 11/49] docs: update changeset entry --- .changeset/fluffy-dolls-pump.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/fluffy-dolls-pump.md b/.changeset/fluffy-dolls-pump.md index 47f059f30..960f91979 100644 --- a/.changeset/fluffy-dolls-pump.md +++ b/.changeset/fluffy-dolls-pump.md @@ -2,4 +2,4 @@ "eslint-plugin-import-x": patch --- -fix(first): add missing value in options type +fix(first): improves the type declaration of the rule `first`'s option From aff792210aa4d6e497870fc794ed0b836fdfa5de Mon Sep 17 00:00:00 2001 From: Marco Pasqualetti Date: Sat, 16 Nov 2024 20:12:26 +0100 Subject: [PATCH 12/49] test(namespace): migrate to `createRuleTestCaseFunction` --- src/rules/namespace.ts | 12 ++-- test/rules/namespace.spec.ts | 119 ++++++++++++++++++----------------- 2 files changed, 71 insertions(+), 60 deletions(-) diff --git a/src/rules/namespace.ts b/src/rules/namespace.ts index c1e29c6b4..0b6156cf4 100644 --- a/src/rules/namespace.ts +++ b/src/rules/namespace.ts @@ -15,6 +15,7 @@ type MessageId = | 'namespaceMember' | 'topLevelNames' | 'notFoundInNamespace' + | 'notFoundInNamespaceDeep' type Options = { allowComputed?: boolean @@ -86,15 +87,16 @@ function makeMessage( namepath: string[], node: TSESTree.Node = last, ) { + const messageId = + namepath.length > 1 ? 'notFoundInNamespaceDeep' : 'notFoundInNamespace' return { node, - messageId: 'notFoundInNamespace' as const, + messageId, data: { name: last.name, - depth: namepath.length > 1 ? 'deeply ' : '', namepath: namepath.join('.'), }, - } + } as const } export = createRule<[Options], MessageId>({ @@ -127,7 +129,9 @@ export = createRule<[Options], MessageId>({ namespaceMember: "Assignment to member of namespace '{{namespace}}'.", topLevelNames: 'Only destructure top-level names.', notFoundInNamespace: - "'{{name}}' not found in {{depth}}imported namespace '{{namepath}}'.", + "'{{name}}' not found in imported namespace '{{namepath}}'.", + notFoundInNamespaceDeep: + "'{{name}}' not found in deeply imported namespace '{{namepath}}'.", }, }, defaultOptions: [ diff --git a/test/rules/namespace.spec.ts b/test/rules/namespace.spec.ts index 802d157c7..cdbc3a8a1 100644 --- a/test/rules/namespace.spec.ts +++ b/test/rules/namespace.spec.ts @@ -1,6 +1,14 @@ import { RuleTester as TSESLintRuleTester } from '@typescript-eslint/rule-tester' +import type { TestCaseError as TSESLintTestCaseError } from '@typescript-eslint/rule-tester' +import type { TSESTree } from '@typescript-eslint/utils' -import { test, SYNTAX_VALID_CASES, testFilePath, parsers } from '../utils' +import { + createRuleTestCaseFunction, + SYNTAX_VALID_CASES, + testFilePath, + parsers, +} from '../utils' +import type { GetRuleModuleMessageIds, RunTests } from '../utils' import rule from 'eslint-plugin-import-x/rules/namespace' @@ -10,13 +18,31 @@ const ruleTester = new TSESLintRuleTester({ }, }) -function error(name: string, namespace: string) { +const test = createRuleTestCaseFunction() + +function createNotFoundInNamespaceError( + name: string, + namespace: string, + type?: `${TSESTree.AST_NODE_TYPES}`, +): TSESLintTestCaseError> { + return { + messageId: 'notFoundInNamespace', + data: { name, namepath: namespace }, + type: type as TSESTree.AST_NODE_TYPES, + } +} + +function createNotFoundInNamespaceDeepError( + name: string, + namespace: string, +): TSESLintTestCaseError> { return { - message: `'${name}' not found in imported namespace '${namespace}'.`, + messageId: 'notFoundInNamespaceDeep', + data: { name, namepath: namespace }, } } -const valid = [ +let valid: RunTests['valid'] = [ test({ code: 'import "./malformed.js"', languageOptions: { parser: require(parsers.ESPREE) }, @@ -226,7 +252,7 @@ const valid = [ }, }), - ...SYNTAX_VALID_CASES, + ...(SYNTAX_VALID_CASES as RunTests['valid']), test({ code: ` @@ -294,68 +320,46 @@ const valid = [ }), ] -const invalid = [ +let invalid: RunTests['invalid'] = [ test({ code: "import * as names from './named-exports'; console.log(names.c)", - errors: [error('c', 'names')], + errors: [createNotFoundInNamespaceError('c', 'names')], }), test({ code: "import * as names from './named-exports'; console.log(names['a']);", - errors: [ - "Unable to validate computed reference to imported namespace 'names'.", - ], + errors: [{ messageId: 'computedReference', data: { namespace: 'names' } }], }), // assignment warning (from no-reassign) test({ code: "import * as foo from './bar'; foo.foo = 'y';", - errors: [{ message: "Assignment to member of namespace 'foo'." }], + errors: [{ messageId: 'namespaceMember', data: { namespace: 'foo' } }], }), test({ code: "import * as foo from './bar'; foo.x = 'y';", errors: [ - "Assignment to member of namespace 'foo'.", - "'x' not found in imported namespace 'foo'.", + { messageId: 'namespaceMember', data: { namespace: 'foo' } }, + createNotFoundInNamespaceError('x', 'foo'), ], }), // invalid destructuring test({ code: 'import * as names from "./named-exports"; const { c } = names', - errors: [ - { - type: 'Property', - message: "'c' not found in imported namespace 'names'.", - }, - ], + errors: [createNotFoundInNamespaceError('c', 'names', 'Property')], }), test({ code: 'import * as names from "./named-exports"; function b() { const { c } = names }', - errors: [ - { - type: 'Property', - message: "'c' not found in imported namespace 'names'.", - }, - ], + errors: [createNotFoundInNamespaceError('c', 'names', 'Property')], }), test({ code: 'import * as names from "./named-exports"; const { c: d } = names', - errors: [ - { - type: 'Property', - message: "'c' not found in imported namespace 'names'.", - }, - ], + errors: [createNotFoundInNamespaceError('c', 'names', 'Property')], }), test({ code: 'import * as names from "./named-exports"; const { c: { d } } = names', - errors: [ - { - type: 'Property', - message: "'c' not found in imported namespace 'names'.", - }, - ], + errors: [createNotFoundInNamespaceError('c', 'names', 'Property')], }), ///////// @@ -365,7 +369,7 @@ const invalid = [ test({ code: 'import * as Endpoints from "./issue-195/Endpoints"; console.log(Endpoints.Foo)', languageOptions: { parser: require(parsers.BABEL) }, - errors: ["'Foo' not found in imported namespace 'Endpoints'."], + errors: [createNotFoundInNamespaceError('Foo', 'Endpoints')], }), // parse errors @@ -376,38 +380,39 @@ const invalid = [ }, errors: [ { + // @ts-expect-error testing parsing error message: "Parse errors in imported module './malformed.js': 'return' outside of function (1:1)", - type: 'Literal', + type: 'Literal' as TSESTree.AST_NODE_TYPES, }, ], }), test({ code: "import b from './deep/default'; console.log(b.e)", - errors: ["'e' not found in imported namespace 'b'."], + errors: [createNotFoundInNamespaceError('e', 'b')], }), // respect hoisting test({ code: `console.log(names.c); import * as names from './named-exports';`, - errors: [error('c', 'names')], + errors: [createNotFoundInNamespaceError('c', 'names')], }), test({ code: `function x() { console.log(names.c) } import * as names from './named-exports';`, - errors: [error('c', 'names')], + errors: [createNotFoundInNamespaceError('c', 'names')], }), // #328: * exports do not include default test({ code: 'import * as ree from "./re-export"; console.log(ree.default)', - errors: [`'default' not found in imported namespace 'ree'.`], + errors: [createNotFoundInNamespaceError('default', 'ree')], }), // JSX test({ code: 'import * as Names from "./named-exports"; const Foo = ', - errors: [error('e', 'Names')], + errors: [createNotFoundInNamespaceError('e', 'Names')], languageOptions: { parserOptions: { ecmaFeatures: { @@ -419,7 +424,7 @@ const invalid = [ test({ code: `import { "b" as b } from "./deep/a"; console.log(b.e)`, - errors: ["'e' not found in imported namespace 'b'."], + errors: [createNotFoundInNamespaceError('e', 'b')], languageOptions: { parser: require(parsers.ESPREE), parserOptions: { ecmaVersion: 2022 }, @@ -427,7 +432,7 @@ const invalid = [ }), test({ code: `import { "b" as b } from "./deep/a"; console.log(b.c.e)`, - errors: ["'e' not found in deeply imported namespace 'b.c'."], + errors: [createNotFoundInNamespaceDeepError('e', 'b.c')], languageOptions: { parser: require(parsers.ESPREE), parserOptions: { ecmaVersion: 2022 }, @@ -440,7 +445,8 @@ const invalid = [ ////////////////////// for (const [folder, parser] of [['deep'], ['deep-es7', parsers.BABEL]]) { // close over params - valid.push( + valid = [ + ...valid, test({ languageOptions: { ...(parser && { parser: require(parser) }) }, code: `import * as a from "./${folder}/a"; console.log(a.b.c.d.e)`, @@ -466,40 +472,41 @@ for (const [folder, parser] of [['deep'], ['deep-es7', parsers.BABEL]]) { languageOptions: { ...(parser && { parser: require(parser) }) }, code: `import * as a from "./${folder}/a"; console.log(a.b.default)`, }), - ) + ] - invalid.push( + invalid = [ + ...invalid, test({ languageOptions: { ...(parser && { parser: require(parser) }) }, code: `import * as a from "./${folder}/a"; console.log(a.b.e)`, - errors: ["'e' not found in deeply imported namespace 'a.b'."], + errors: [createNotFoundInNamespaceDeepError('e', 'a.b')], }), test({ languageOptions: { ...(parser && { parser: require(parser) }) }, code: `import { b } from "./${folder}/a"; console.log(b.e)`, - errors: ["'e' not found in imported namespace 'b'."], + errors: [createNotFoundInNamespaceError('e', 'b')], }), test({ languageOptions: { ...(parser && { parser: require(parser) }) }, code: `import * as a from "./${folder}/a"; console.log(a.b.c.e)`, - errors: ["'e' not found in deeply imported namespace 'a.b.c'."], + errors: [createNotFoundInNamespaceDeepError('e', 'a.b.c')], }), test({ languageOptions: { ...(parser && { parser: require(parser) }) }, code: `import { b } from "./${folder}/a"; console.log(b.c.e)`, - errors: ["'e' not found in deeply imported namespace 'b.c'."], + errors: [createNotFoundInNamespaceDeepError('e', 'b.c')], }), test({ languageOptions: { ...(parser && { parser: require(parser) }) }, code: `import * as a from "./${folder}/a"; var {b:{ e }} = a`, - errors: ["'e' not found in deeply imported namespace 'a.b'."], + errors: [createNotFoundInNamespaceDeepError('e', 'a.b')], }), test({ languageOptions: { ...(parser && { parser: require(parser) }) }, code: `import * as a from "./${folder}/a"; var {b:{c:{ e }}} = a`, - errors: ["'e' not found in deeply imported namespace 'a.b.c'."], + errors: [createNotFoundInNamespaceDeepError('e', 'a.b.c')], }), - ) + ] } ruleTester.run('namespace', rule, { valid, invalid }) From 4b649ce87cbf59f9b1980f1bb50ad846628e160b Mon Sep 17 00:00:00 2001 From: Marco Pasqualetti Date: Sat, 16 Nov 2024 20:18:02 +0100 Subject: [PATCH 13/49] test(newline-after-import): add types to helper functions --- test/rules/newline-after-import.spec.ts | 38 ++++++++++++++----------- 1 file changed, 22 insertions(+), 16 deletions(-) diff --git a/test/rules/newline-after-import.spec.ts b/test/rules/newline-after-import.spec.ts index a56f27a13..e86774e11 100644 --- a/test/rules/newline-after-import.spec.ts +++ b/test/rules/newline-after-import.spec.ts @@ -1,12 +1,16 @@ import { RuleTester as TSESLintRuleTester } from '@typescript-eslint/rule-tester' +import type { TestCaseError as TSESLintTestCaseError } from '@typescript-eslint/rule-tester' import { parsers } from '../utils' +import type { GetRuleModuleMessageIds } from '../utils' import rule from 'eslint-plugin-import-x/rules/newline-after-import' const ruleTester = new TSESLintRuleTester() -const getImportError = (count: number) => ({ +const createImportError = ( + count: number, +): TSESLintTestCaseError> => ({ messageId: 'newline' as const, data: { count, @@ -15,9 +19,11 @@ const getImportError = (count: number) => ({ }, }) -const IMPORT_ERROR = getImportError(1) +const IMPORT_ERROR = createImportError(1) -const getRequireError = (count: number) => ({ +const createRequireError = ( + count: number, +): TSESLintTestCaseError> => ({ messageId: 'newline' as const, data: { count, @@ -26,7 +32,7 @@ const getRequireError = (count: number) => ({ }, }) -const REQUIRE_ERROR = getRequireError(1) +const REQUIRE_ERROR = createRequireError(1) ruleTester.run('newline-after-import', rule, { valid: [ @@ -601,7 +607,7 @@ ruleTester.run('newline-after-import', rule, { { line: 1, column: 1, - ...getImportError(2), + ...createImportError(2), }, ], languageOptions: { @@ -860,7 +866,7 @@ ruleTester.run('newline-after-import', rule, { { line: 1, column: 1, - ...getImportError(2), + ...createImportError(2), }, ], languageOptions: { @@ -875,7 +881,7 @@ ruleTester.run('newline-after-import', rule, { { line: 1, column: 1, - ...getImportError(2), + ...createImportError(2), }, ], languageOptions: { @@ -890,7 +896,7 @@ ruleTester.run('newline-after-import', rule, { { line: 1, column: 1, - ...getImportError(2), + ...createImportError(2), }, ], languageOptions: { @@ -905,7 +911,7 @@ ruleTester.run('newline-after-import', rule, { { line: 1, column: 1, - ...getImportError(2), + ...createImportError(2), }, ], languageOptions: { @@ -920,7 +926,7 @@ ruleTester.run('newline-after-import', rule, { { line: 1, column: 1, - ...getImportError(2), + ...createImportError(2), }, ], languageOptions: { @@ -935,7 +941,7 @@ ruleTester.run('newline-after-import', rule, { { line: 1, column: 1, - ...getImportError(2), + ...createImportError(2), }, ], languageOptions: { @@ -950,7 +956,7 @@ ruleTester.run('newline-after-import', rule, { { line: 1, column: 1, - ...getImportError(2), + ...createImportError(2), }, ], languageOptions: { @@ -965,7 +971,7 @@ ruleTester.run('newline-after-import', rule, { { line: 1, column: 1, - ...getImportError(2), + ...createImportError(2), }, ], languageOptions: { @@ -980,7 +986,7 @@ ruleTester.run('newline-after-import', rule, { { line: 1, column: 1, - ...getImportError(2), + ...createImportError(2), }, ], languageOptions: { @@ -1031,7 +1037,7 @@ ruleTester.run('newline-after-import', rule, { { line: 1, column: 1, - ...getRequireError(2), + ...createRequireError(2), }, ], languageOptions: { parserOptions: { ecmaVersion: 2015 } }, @@ -1044,7 +1050,7 @@ ruleTester.run('newline-after-import', rule, { { line: 1, column: 1, - ...getRequireError(2), + ...createRequireError(2), }, ], languageOptions: { parserOptions: { ecmaVersion: 2015 } }, From 0a7a1f0d94e3c064836b67a40bb9325dbedebd0b Mon Sep 17 00:00:00 2001 From: Marco Pasqualetti Date: Sat, 16 Nov 2024 20:20:40 +0100 Subject: [PATCH 14/49] test(no-absolute-path): add types to helper functions --- test/rules/no-absolute-path.spec.ts | 36 ++++++++++++++++------------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/test/rules/no-absolute-path.spec.ts b/test/rules/no-absolute-path.spec.ts index 651ee0698..7c60568c6 100644 --- a/test/rules/no-absolute-path.spec.ts +++ b/test/rules/no-absolute-path.spec.ts @@ -1,20 +1,24 @@ import path from 'node:path' import { RuleTester as TSESLintRuleTester } from '@typescript-eslint/rule-tester' +import type { TestCaseError as TSESLintTestCaseError } from '@typescript-eslint/rule-tester' -import { parsers, test } from '../utils' +import { createRuleTestCaseFunction, parsers } from '../utils' +import type { GetRuleModuleMessageIds } from '../utils' import rule from 'eslint-plugin-import-x/rules/no-absolute-path' const ruleTester = new TSESLintRuleTester() -const error = { +const test = createRuleTestCaseFunction() + +const ABSOLUTE_ERROR: TSESLintTestCaseError< + GetRuleModuleMessageIds +> = { messageId: 'absolute', } -function absolutePath(testPath: string) { - return path.join(__dirname, testPath) -} +const absolutePath = (testPath: string) => path.join(__dirname, testPath) ruleTester.run('no-absolute-path', rule, { valid: [ @@ -64,57 +68,57 @@ ruleTester.run('no-absolute-path', rule, { test({ code: `import f from "${absolutePath('/foo')}"`, filename: absolutePath('/foo/bar/index.js'), - errors: [error], + errors: [ABSOLUTE_ERROR], output: 'import f from ".."', }), test({ code: `import f from "${absolutePath('/foo/bar/baz.js')}"`, filename: absolutePath('/foo/bar/index.js'), - errors: [error], + errors: [ABSOLUTE_ERROR], output: 'import f from "./baz.js"', }), test({ code: `import f from "${absolutePath('/foo/path')}"`, filename: absolutePath('/foo/bar/index.js'), - errors: [error], + errors: [ABSOLUTE_ERROR], output: 'import f from "../path"', }), test({ code: `import f from "${absolutePath('/some/path')}"`, filename: absolutePath('/foo/bar/index.js'), - errors: [error], + errors: [ABSOLUTE_ERROR], output: 'import f from "../../some/path"', }), test({ code: `import f from "${absolutePath('/some/path')}"`, filename: absolutePath('/foo/bar/index.js'), options: [{ amd: true }], - errors: [error], + errors: [ABSOLUTE_ERROR], output: 'import f from "../../some/path"', }), test({ code: `var f = require("${absolutePath('/foo')}")`, filename: absolutePath('/foo/bar/index.js'), - errors: [error], + errors: [ABSOLUTE_ERROR], output: 'var f = require("..")', }), test({ code: `var f = require("${absolutePath('/foo/path')}")`, filename: absolutePath('/foo/bar/index.js'), - errors: [error], + errors: [ABSOLUTE_ERROR], output: 'var f = require("../path")', }), test({ code: `var f = require("${absolutePath('/some/path')}")`, filename: absolutePath('/foo/bar/index.js'), - errors: [error], + errors: [ABSOLUTE_ERROR], output: 'var f = require("../../some/path")', }), test({ code: `var f = require("${absolutePath('/some/path')}")`, filename: absolutePath('/foo/bar/index.js'), options: [{ amd: true }], - errors: [error], + errors: [ABSOLUTE_ERROR], output: 'var f = require("../../some/path")', }), // validate amd @@ -122,7 +126,7 @@ ruleTester.run('no-absolute-path', rule, { code: `require(["${absolutePath('/some/path')}"], function (f) { /* ... */ })`, filename: absolutePath('/foo/bar/index.js'), options: [{ amd: true }], - errors: [error], + errors: [ABSOLUTE_ERROR], output: 'require(["../../some/path"], function (f) { /* ... */ })', }), test({ @@ -130,7 +134,7 @@ ruleTester.run('no-absolute-path', rule, { filename: absolutePath('/foo/bar/index.js'), languageOptions: { parser: require(parsers.ESPREE) }, options: [{ amd: true }], - errors: [error], + errors: [ABSOLUTE_ERROR], output: 'define(["../../some/path"], function (f) { /* ... */ })', }), ], From c94504586991350a0459e0acb118ddc2b905df2b Mon Sep 17 00:00:00 2001 From: Marco Pasqualetti Date: Sat, 16 Nov 2024 20:28:56 +0100 Subject: [PATCH 15/49] test(no-anonymous-default-export): migrate to `createRuleTestCaseFunction` --- .../rules/no-anonymous-default-export.spec.ts | 86 +++++++------------ 1 file changed, 29 insertions(+), 57 deletions(-) diff --git a/test/rules/no-anonymous-default-export.spec.ts b/test/rules/no-anonymous-default-export.spec.ts index 1326b7571..b5f7c9998 100644 --- a/test/rules/no-anonymous-default-export.spec.ts +++ b/test/rules/no-anonymous-default-export.spec.ts @@ -1,11 +1,28 @@ import { RuleTester as TSESLintRuleTester } from '@typescript-eslint/rule-tester' +import type { TestCaseError as TSESLintTestCaseError } from '@typescript-eslint/rule-tester' -import { test, SYNTAX_VALID_CASES, parsers } from '../utils' +import { + SYNTAX_VALID_CASES, + parsers, + createRuleTestCaseFunction, +} from '../utils' +import type { GetRuleModuleMessageIds } from '../utils' import rule from 'eslint-plugin-import-x/rules/no-anonymous-default-export' const ruleTester = new TSESLintRuleTester() +const test = createRuleTestCaseFunction() + +function createAssignError( + type: string, +): TSESLintTestCaseError> { + return { + messageId: 'assign', + data: { type }, + } +} + ruleTester.run('no-anonymous-default-export', rule, { valid: [ // Exports with identifiers are valid @@ -68,96 +85,51 @@ ruleTester.run('no-anonymous-default-export', rule, { invalid: [ test({ code: 'export default []', - errors: [ - { - message: - 'Assign array to a variable before exporting as module default', - }, - ], + errors: [createAssignError('array')], }), test({ code: 'export default () => {}', - errors: [ - { - message: - 'Assign arrow function to a variable before exporting as module default', - }, - ], + errors: [createAssignError('arrow function')], }), test({ code: 'export default class {}', - errors: [{ message: 'Unexpected default export of anonymous class' }], + errors: [{ messageId: 'anonymous', data: { type: 'class' } }], }), test({ code: 'export default function() {}', - errors: [{ message: 'Unexpected default export of anonymous function' }], + errors: [{ messageId: 'anonymous', data: { type: 'function' } }], }), test({ code: 'export default 123', - errors: [ - { - message: - 'Assign literal to a variable before exporting as module default', - }, - ], + errors: [createAssignError('literal')], }), test({ code: "export default 'foo'", - errors: [ - { - message: - 'Assign literal to a variable before exporting as module default', - }, - ], + errors: [createAssignError('literal')], }), test({ code: 'export default `foo`', - errors: [ - { - message: - 'Assign literal to a variable before exporting as module default', - }, - ], + errors: [createAssignError('literal')], }), test({ code: 'export default {}', - errors: [ - { - message: - 'Assign object to a variable before exporting as module default', - }, - ], + errors: [createAssignError('object')], }), test({ code: 'export default foo(bar)', options: [{ allowCallExpression: false }], - errors: [ - { - message: - 'Assign call result to a variable before exporting as module default', - }, - ], + errors: [createAssignError('call result')], }), test({ code: 'export default new Foo()', - errors: [ - { - message: - 'Assign instance to a variable before exporting as module default', - }, - ], + errors: [createAssignError('instance')], }), // Test failure with non-covering exception test({ code: 'export default 123', options: [{ allowObject: true }], - errors: [ - { - message: - 'Assign literal to a variable before exporting as module default', - }, - ], + errors: [createAssignError('literal')], }), ], }) From 2b0e3fb8b137b604a42833e45ae91603d02ad9ad Mon Sep 17 00:00:00 2001 From: Marco Pasqualetti Date: Sun, 17 Nov 2024 18:15:48 +0100 Subject: [PATCH 16/49] =?UTF-8?q?fix(no-cycle):=20improves=20the=20type=20?= =?UTF-8?q?declaration=20of=20the=20rule=20`no-cycle`=E2=80=99s=20`maxDept?= =?UTF-8?q?h`=20option?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .changeset/cuddly-ligers-wonder.md | 5 ++ src/rules/no-cycle.ts | 31 +++++++----- test/rules/no-cycle.spec.ts | 80 ++++++++++++++++-------------- 3 files changed, 66 insertions(+), 50 deletions(-) create mode 100644 .changeset/cuddly-ligers-wonder.md diff --git a/.changeset/cuddly-ligers-wonder.md b/.changeset/cuddly-ligers-wonder.md new file mode 100644 index 000000000..17c5f4740 --- /dev/null +++ b/.changeset/cuddly-ligers-wonder.md @@ -0,0 +1,5 @@ +--- +"eslint-plugin-import-x": patch +--- + +fix(no-cycle): improves the type declaration of the rule `no-cycle`’s `maxDepth` option diff --git a/src/rules/no-cycle.ts b/src/rules/no-cycle.ts index 8090fe45b..82a310ee7 100644 --- a/src/rules/no-cycle.ts +++ b/src/rules/no-cycle.ts @@ -15,10 +15,10 @@ import { type Options = { allowUnsafeDynamicCyclicDependency?: boolean ignoreExternal?: boolean - maxDepth?: number + maxDepth?: number | '∞' } & ModuleOptions -type MessageId = 'cycle' +type MessageId = 'cycle' | 'cycleSource' type Traverser = { mget(): ExportMap | null @@ -65,7 +65,8 @@ export = createRule<[Options?], MessageId>({ }), ], messages: { - cycle: 'Dependency cycle {{source}}', + cycle: 'Dependency cycle detected', + cycleSource: 'Dependency cycle via "{{source}}"', }, }, defaultOptions: [], @@ -187,16 +188,20 @@ export = createRule<[Options?], MessageId>({ while (untraversed.length > 0) { const next = untraversed.shift()! // bfs! if (detectCycle(next)) { - context.report({ - node: importer, - messageId: 'cycle', - data: { - source: - next.route.length > 0 - ? `via ${routeString(next.route)}` - : 'detected.', - }, - }) + if (next.route.length > 0) { + context.report({ + node: importer, + messageId: 'cycleSource', + data: { + source: routeString(next.route), + }, + }) + } else { + context.report({ + node: importer, + messageId: 'cycle', + }) + } return } } diff --git a/test/rules/no-cycle.spec.ts b/test/rules/no-cycle.spec.ts index 3727c94f5..09861bd3f 100644 --- a/test/rules/no-cycle.spec.ts +++ b/test/rules/no-cycle.spec.ts @@ -1,19 +1,27 @@ import { RuleTester as TSESLintRuleTester } from '@typescript-eslint/rule-tester' +import type { TestCaseError as TSESLintTestCaseError } from '@typescript-eslint/rule-tester' -import { parsers, test as _test, testFilePath } from '../utils' -import type { ValidTestCase } from '../utils' +import { createRuleTestCaseFunction, parsers, testFilePath } from '../utils' +import type { GetRuleModuleMessageIds } from '../utils' import rule from 'eslint-plugin-import-x/rules/no-cycle' const ruleTester = new TSESLintRuleTester() -const error = (message: string) => ({ message }) +const _test = createRuleTestCaseFunction() -const test = (def: T) => +const test = (testCase => _test({ filename: testFilePath('./cycles/depth-zero.js'), - ...def, - }) + ...testCase, + })) as typeof _test + +const createCycleSourceError = ( + source: string, +): TSESLintTestCaseError> => ({ + messageId: 'cycleSource', + data: { source }, +}) ruleTester.run('no-cycle', rule, { valid: [ @@ -110,11 +118,11 @@ ruleTester.run('no-cycle', rule, { test({ code: 'import { bar } from "./flow-types-some-type-imports"', languageOptions: { parser: require(parsers.BABEL) }, - errors: [error(`Dependency cycle detected.`)], + errors: [{ messageId: 'cycle' }], }), test({ code: 'import { foo } from "cycles/external/depth-one"', - errors: [error(`Dependency cycle detected.`)], + errors: [{ messageId: 'cycle' }], settings: { 'import-x/resolver': 'webpack', 'import-x/external-module-folders': ['cycles/external'], @@ -122,7 +130,7 @@ ruleTester.run('no-cycle', rule, { }), test({ code: 'import { foo } from "./external-depth-two"', - errors: [error(`Dependency cycle via cycles/external/depth-one:1`)], + errors: [createCycleSourceError('cycles/external/depth-one:1')], settings: { 'import-x/resolver': 'webpack', 'import-x/external-module-folders': ['cycles/external'], @@ -134,135 +142,133 @@ ruleTester.run('no-cycle', rule, { test({ code: `import { foo } from "./es6/depth-one"`, options: [{ ...opts }], - errors: [error(`Dependency cycle detected.`)], + errors: [{ messageId: 'cycle' }], }), test({ code: `import { foo } from "./es6/depth-one"`, options: [{ ...opts, maxDepth: 1 }], - errors: [error(`Dependency cycle detected.`)], + errors: [{ messageId: 'cycle' }], }), test({ code: `const { foo } = require("./es6/depth-one")`, - errors: [error(`Dependency cycle detected.`)], options: [{ ...opts, commonjs: true }], + errors: [{ messageId: 'cycle' }], }), test({ code: `require(["./es6/depth-one"], d1 => {})`, - errors: [error(`Dependency cycle detected.`)], options: [{ ...opts, amd: true }], + errors: [{ messageId: 'cycle' }], }), test({ code: `define(["./es6/depth-one"], d1 => {})`, - errors: [error(`Dependency cycle detected.`)], options: [{ ...opts, amd: true }], + errors: [{ messageId: 'cycle' }], }), test({ code: `import { foo } from "./es6/depth-one-reexport"`, options: [{ ...opts }], - errors: [error(`Dependency cycle detected.`)], + errors: [{ messageId: 'cycle' }], }), test({ code: `import { foo } from "./es6/depth-two"`, options: [{ ...opts }], - errors: [error(`Dependency cycle via ./depth-one:1`)], + errors: [createCycleSourceError('./depth-one:1')], }), test({ code: `import { foo } from "./es6/depth-two"`, options: [{ ...opts, maxDepth: 2 }], - errors: [error(`Dependency cycle via ./depth-one:1`)], + errors: [createCycleSourceError('./depth-one:1')], }), test({ code: `const { foo } = require("./es6/depth-two")`, - errors: [error(`Dependency cycle via ./depth-one:1`)], + errors: [createCycleSourceError('./depth-one:1')], options: [{ ...opts, commonjs: true }], }), test({ code: `import { two } from "./es6/depth-three-star"`, options: [{ ...opts }], - errors: [error(`Dependency cycle via ./depth-two:1=>./depth-one:1`)], + errors: [createCycleSourceError('./depth-two:1=>./depth-one:1')], }), test({ code: `import one, { two, three } from "./es6/depth-three-star"`, options: [{ ...opts }], - errors: [error(`Dependency cycle via ./depth-two:1=>./depth-one:1`)], + errors: [createCycleSourceError('./depth-two:1=>./depth-one:1')], }), test({ code: `import { bar } from "./es6/depth-three-indirect"`, options: [{ ...opts }], - errors: [error(`Dependency cycle via ./depth-two:1=>./depth-one:1`)], + errors: [createCycleSourceError('./depth-two:1=>./depth-one:1')], }), test({ code: `import { bar } from "./es6/depth-three-indirect"`, options: [{ ...opts }], - errors: [error(`Dependency cycle via ./depth-two:1=>./depth-one:1`)], + errors: [createCycleSourceError('./depth-two:1=>./depth-one:1')], languageOptions: { parser: require(parsers.BABEL) }, }), test({ code: `import { foo } from "./es6/depth-two"`, options: [{ ...opts, maxDepth: Number.POSITIVE_INFINITY }], - errors: [error(`Dependency cycle via ./depth-one:1`)], + errors: [createCycleSourceError('./depth-one:1')], }), test({ code: `import { foo } from "./es6/depth-two"`, options: [{ ...opts, maxDepth: '∞' }], - errors: [error(`Dependency cycle via ./depth-one:1`)], + errors: [createCycleSourceError('./depth-one:1')], }), ]), test({ code: `import("./es6/depth-three-star")`, - errors: [error(`Dependency cycle via ./depth-two:1=>./depth-one:1`)], + errors: [createCycleSourceError('./depth-two:1=>./depth-one:1')], languageOptions: { parser: require(parsers.BABEL) }, }), test({ code: `import("./es6/depth-three-indirect")`, - errors: [error(`Dependency cycle via ./depth-two:1=>./depth-one:1`)], + errors: [createCycleSourceError('./depth-two:1=>./depth-one:1')], languageOptions: { parser: require(parsers.BABEL) }, }), test({ code: `import("./es6/depth-two")`, options: [{ maxDepth: Number.POSITIVE_INFINITY }], - errors: [error(`Dependency cycle via ./depth-one:1`)], + errors: [createCycleSourceError('./depth-one:1')], }), test({ code: `import("./es6/depth-two")`, options: [{ maxDepth: '∞' }], - errors: [error(`Dependency cycle via ./depth-one:1`)], + errors: [createCycleSourceError('./depth-one:1')], }), test({ code: `function bar(){ return import("./es6/depth-one"); } // #2265 5`, - errors: [error(`Dependency cycle detected.`)], + errors: [{ messageId: 'cycle' }], languageOptions: { parser: require(parsers.BABEL) }, }), test({ // Dynamic import is not properly caracterized with eslint < 4 code: `import { foo } from "./es6/depth-one-dynamic"; // #2265 6`, - errors: [error(`Dependency cycle detected.`)], + errors: [{ messageId: 'cycle' }], languageOptions: { parser: require(parsers.BABEL) }, }), test({ code: `function bar(){ return import("./es6/depth-one"); } // #2265 7`, - errors: [error(`Dependency cycle detected.`)], + errors: [{ messageId: 'cycle' }], }), test({ code: `import { foo } from "./es6/depth-one-dynamic"; // #2265 8`, - errors: [error(`Dependency cycle detected.`)], + errors: [{ messageId: 'cycle' }], }), test({ code: 'import { bar } from "./flow-types-depth-one"', languageOptions: { parser: require(parsers.BABEL) }, errors: [ - error( - `Dependency cycle via ./flow-types-depth-two:4=>./es6/depth-one:1`, - ), + createCycleSourceError('./flow-types-depth-two:4=>./es6/depth-one:1'), ], }), test({ code: 'import { foo } from "./intermediate-ignore"', errors: [ { - message: 'Dependency cycle via ./ignore:1', + ...createCycleSourceError('./ignore:1'), line: 1, }, ], @@ -271,7 +277,7 @@ ruleTester.run('no-cycle', rule, { code: 'import { foo } from "./ignore"', errors: [ { - message: 'Dependency cycle detected.', + messageId: 'cycle', line: 1, }, ], From 3fa282048660eb466f54179d1bc909ab8c54b8fe Mon Sep 17 00:00:00 2001 From: Marco Pasqualetti Date: Sun, 17 Nov 2024 23:25:56 +0100 Subject: [PATCH 17/49] test(utils): rewrite `createRuleTestCaseFunctions` to be more type check friendly --- .../consistent-type-specifier-style.spec.ts | 50 ++--- test/rules/default.spec.ts | 110 +++++---- test/rules/export.spec.ts | 124 +++++----- test/rules/exports-last.spec.ts | 30 +-- test/rules/extensions.spec.ts | 138 ++++++------ test/rules/first.spec.ts | 32 +-- test/rules/group-exports.spec.ts | 84 +++---- test/rules/max-dependencies.spec.ts | 30 +-- test/rules/named.spec.ts | 212 +++++++++--------- test/rules/namespace.spec.ts | 146 ++++++------ test/rules/no-absolute-path.spec.ts | 74 +++--- .../rules/no-anonymous-default-export.spec.ts | 70 +++--- test/rules/no-cycle.spec.ts | 143 ++++++------ test/rules/no-useless-path-segments.spec.ts | 115 +++++----- test/rules/no-webpack-loader-syntax.spec.ts | 42 ++-- test/utils.ts | 40 ++-- 16 files changed, 715 insertions(+), 725 deletions(-) diff --git a/test/rules/consistent-type-specifier-style.spec.ts b/test/rules/consistent-type-specifier-style.spec.ts index 3927fe549..2cdfa856c 100644 --- a/test/rules/consistent-type-specifier-style.spec.ts +++ b/test/rules/consistent-type-specifier-style.spec.ts @@ -1,59 +1,59 @@ import { RuleTester as TSESLintRuleTester } from '@typescript-eslint/rule-tester' import { TSESTree } from '@typescript-eslint/utils' -import { parsers, createRuleTestCaseFunction } from '../utils' +import { parsers, createRuleTestCaseFunctions } from '../utils' import type { RunTests } from '../utils' import rule from 'eslint-plugin-import-x/rules/consistent-type-specifier-style' -const test = createRuleTestCaseFunction() +const { tValid } = createRuleTestCaseFunctions() const COMMON_TESTS: RunTests = { valid: [ // // prefer-top-level // - test({ + tValid({ code: "import Foo from 'Foo';", options: ['prefer-top-level'], }), - test({ + tValid({ code: "import type Foo from 'Foo';", options: ['prefer-top-level'], }), - test({ + tValid({ code: "import { Foo } from 'Foo';", options: ['prefer-top-level'], }), - test({ + tValid({ code: "import { Foo as Bar } from 'Foo';", options: ['prefer-top-level'], }), - test({ + tValid({ code: "import * as Foo from 'Foo';", options: ['prefer-top-level'], }), - test({ + tValid({ code: "import 'Foo';", options: ['prefer-top-level'], }), - test({ + tValid({ code: "import {} from 'Foo';", options: ['prefer-top-level'], }), - test({ + tValid({ code: "import type {} from 'Foo';", options: ['prefer-top-level'], }), - test({ + tValid({ code: "import type { Foo } from 'Foo';", options: ['prefer-top-level'], }), - test({ + tValid({ code: "import type { Foo as Bar } from 'Foo';", options: ['prefer-top-level'], }), - test({ + tValid({ code: "import type { Foo, Bar, Baz, Bam } from 'Foo';", options: ['prefer-top-level'], }), @@ -61,47 +61,47 @@ const COMMON_TESTS: RunTests = { // // prefer-inline // - test({ + tValid({ code: "import Foo from 'Foo';", options: ['prefer-inline'], }), - test({ + tValid({ code: "import type Foo from 'Foo';", options: ['prefer-inline'], }), - test({ + tValid({ code: "import { Foo } from 'Foo';", options: ['prefer-inline'], }), - test({ + tValid({ code: "import { Foo as Bar } from 'Foo';", options: ['prefer-inline'], }), - test({ + tValid({ code: "import * as Foo from 'Foo';", options: ['prefer-inline'], }), - test({ + tValid({ code: "import 'Foo';", options: ['prefer-inline'], }), - test({ + tValid({ code: "import {} from 'Foo';", options: ['prefer-inline'], }), - test({ + tValid({ code: "import type {} from 'Foo';", options: ['prefer-inline'], }), - test({ + tValid({ code: "import { type Foo } from 'Foo';", options: ['prefer-inline'], }), - test({ + tValid({ code: "import { type Foo as Bar } from 'Foo';", options: ['prefer-inline'], }), - test({ + tValid({ code: "import { type Foo, type Bar, Baz, Bam } from 'Foo';", options: ['prefer-inline'], }), @@ -256,7 +256,7 @@ const TS_ONLY: RunTests = { // // always valid // - test({ code: "import type * as Foo from 'Foo';" }), + tValid({ code: "import type * as Foo from 'Foo';" }), ], invalid: [], } diff --git a/test/rules/default.spec.ts b/test/rules/default.spec.ts index 6fd9707dc..b33742d12 100644 --- a/test/rules/default.spec.ts +++ b/test/rules/default.spec.ts @@ -3,7 +3,7 @@ import path from 'node:path' import { RuleTester as TSESLintRuleTester } from '@typescript-eslint/rule-tester' import { - createRuleTestCaseFunction, + createRuleTestCaseFunctions, SYNTAX_VALID_CASES, parsers, } from '../utils' @@ -14,62 +14,56 @@ import { CASE_SENSITIVE_FS } from 'eslint-plugin-import-x/utils' const ruleTester = new TSESLintRuleTester() -const test = createRuleTestCaseFunction() +const { tValid, tInvalid } = createRuleTestCaseFunctions() ruleTester.run('default', rule, { valid: [ - test({ + tValid({ code: 'import "./malformed.js"', languageOptions: { parser: require(parsers.ESPREE) }, }), - test({ code: 'import foo from "./empty-folder";' }), - test({ code: 'import { foo } from "./default-export";' }), - test({ code: 'import foo from "./default-export";' }), - test({ code: 'import foo from "./mixed-exports";' }), - test({ - code: 'import bar from "./default-export";', - }), - test({ - code: 'import CoolClass from "./default-class";', - }), - test({ - code: 'import bar, { baz } from "./default-export";', - }), + tValid({ code: 'import foo from "./empty-folder";' }), + tValid({ code: 'import { foo } from "./default-export";' }), + tValid({ code: 'import foo from "./default-export";' }), + tValid({ code: 'import foo from "./mixed-exports";' }), + tValid({ code: 'import bar from "./default-export";' }), + tValid({ code: 'import CoolClass from "./default-class";' }), + tValid({ code: 'import bar, { baz } from "./default-export";' }), // core modules always have a default - test({ code: 'import crypto from "crypto";' }), + tValid({ code: 'import crypto from "crypto";' }), - test({ + tValid({ code: 'import common from "./common";', languageOptions: { parser: require(parsers.BABEL) }, }), // es7 export syntax - test({ + tValid({ code: 'export bar from "./bar"', languageOptions: { parser: require(parsers.BABEL) }, }), - test({ code: 'export { default as bar } from "./bar"' }), - test({ + tValid({ code: 'export { default as bar } from "./bar"' }), + tValid({ code: 'export bar, { foo } from "./bar"', languageOptions: { parser: require(parsers.BABEL) }, }), - test({ code: 'export { default as bar, foo } from "./bar"' }), - test({ + tValid({ code: 'export { default as bar, foo } from "./bar"' }), + tValid({ code: 'export bar, * as names from "./bar"', languageOptions: { parser: require(parsers.BABEL) }, }), // sanity check - test({ code: 'export {a} from "./named-exports"' }), - test({ + tValid({ code: 'export {a} from "./named-exports"' }), + tValid({ code: 'import twofer from "./trampoline"', languageOptions: { parser: require(parsers.BABEL) }, }), // jsx - test({ + tValid({ code: 'import MyCoolComponent from "./jsx/MyCoolComponent.jsx"', languageOptions: { parserOptions: { @@ -81,11 +75,11 @@ ruleTester.run('default', rule, { }), // #54: import of named export default - test({ code: 'import foo from "./named-default-export"' }), + tValid({ code: 'import foo from "./named-default-export"' }), // #94: redux export of execution result, - test({ code: 'import connectedApp from "./redux"' }), - test({ + tValid({ code: 'import connectedApp from "./redux"' }), + tValid({ code: 'import App from "./jsx/App"', languageOptions: { parserOptions: { @@ -95,32 +89,32 @@ ruleTester.run('default', rule, { }), // from no-errors - test({ + tValid({ code: "import Foo from './jsx/FooES7.js';", languageOptions: { parser: require(parsers.BABEL) }, }), // #545: more ES7 cases - test({ + tValid({ code: "import bar from './default-export-from.js';", languageOptions: { parser: require(parsers.BABEL) }, }), - test({ + tValid({ code: "import bar from './default-export-from-named.js';", languageOptions: { parser: require(parsers.BABEL) }, }), - test({ + tValid({ code: "import bar from './default-export-from-ignored.js';", settings: { 'import-x/ignore': ['common'] }, languageOptions: { parser: require(parsers.BABEL) }, }), - test({ + tValid({ code: "export bar from './default-export-from-ignored.js';", settings: { 'import-x/ignore': ['common'] }, languageOptions: { parser: require(parsers.BABEL) }, }), - test({ + tValid({ code: 'export { "default" as bar } from "./bar"', languageOptions: { parser: require(parsers.ESPREE), @@ -134,14 +128,14 @@ ruleTester.run('default', rule, { ], invalid: [ - // test({ + // tInvalid({ // code: "import Foo from './jsx/FooES7.js';", // errors: [ // "Parse errors in imported module './jsx/FooES7.js': Unexpected token = (6:14)", // ], // }), - test({ + tInvalid({ code: 'import baz from "./named-exports";', errors: [ { @@ -154,7 +148,7 @@ ruleTester.run('default', rule, { }), // es7 export syntax - test({ + tInvalid({ code: 'export baz from "./named-exports"', languageOptions: { parser: require(parsers.BABEL) }, errors: [ @@ -166,7 +160,7 @@ ruleTester.run('default', rule, { }, ], }), - test({ + tInvalid({ code: 'export baz, { bar } from "./named-exports"', languageOptions: { parser: require(parsers.BABEL) }, errors: [ @@ -178,7 +172,7 @@ ruleTester.run('default', rule, { }, ], }), - test({ + tInvalid({ code: 'export baz, * as names from "./named-exports"', languageOptions: { parser: require(parsers.BABEL) }, errors: [ @@ -191,7 +185,7 @@ ruleTester.run('default', rule, { ], }), // exports default from a module with no default - test({ + tInvalid({ code: 'import twofer from "./broken-trampoline"', languageOptions: { parser: require(parsers.BABEL) }, errors: [ @@ -205,7 +199,7 @@ ruleTester.run('default', rule, { }), // #328: * exports do not include default - test({ + tInvalid({ code: 'import barDefault from "./re-export"', errors: [ { @@ -223,12 +217,12 @@ ruleTester.run('default', rule, { if (!CASE_SENSITIVE_FS) { ruleTester.run('default (path case-insensitivity)', rule, { valid: [ - test({ + tValid({ code: 'import foo from "./jsx/MyUncoolComponent.jsx"', }), ], invalid: [ - test({ + tInvalid({ code: 'import bar from "./Named-Exports"', errors: [ { @@ -246,21 +240,21 @@ if (!CASE_SENSITIVE_FS) { describe('TypeScript', () => { ruleTester.run(`default`, rule, { valid: [ - test({ + tValid({ code: `import foobar from "./typescript-default"`, settings: { 'import-x/parsers': { [parsers.TS]: ['.ts'] }, 'import-x/resolver': { 'eslint-import-resolver-typescript': true }, }, }), - test({ + tValid({ code: `import foobar from "./typescript-export-assign-default"`, settings: { 'import-x/parsers': { [parsers.TS]: ['.ts'] }, 'import-x/resolver': { 'eslint-import-resolver-typescript': true }, }, }), - test({ + tValid({ code: `import foobar from "./typescript-export-assign-function"`, settings: { @@ -268,7 +262,7 @@ describe('TypeScript', () => { 'import-x/resolver': { 'eslint-import-resolver-typescript': true }, }, }), - test({ + tValid({ code: `import foobar from "./typescript-export-assign-mixed"`, settings: { @@ -276,7 +270,7 @@ describe('TypeScript', () => { 'import-x/resolver': { 'eslint-import-resolver-typescript': true }, }, }), - test({ + tValid({ code: `import foobar from "./typescript-export-assign-default-reexport"`, settings: { @@ -284,7 +278,7 @@ describe('TypeScript', () => { 'import-x/resolver': { 'eslint-import-resolver-typescript': true }, }, }), - test({ + tValid({ code: `import React from "./typescript-export-assign-default-namespace"`, settings: { @@ -300,7 +294,7 @@ describe('TypeScript', () => { }, }, }), - test({ + tValid({ code: `import Foo from "./typescript-export-as-default-namespace"`, settings: { @@ -317,7 +311,7 @@ describe('TypeScript', () => { }, }, }), - test({ + tValid({ code: `import Foo from "./typescript-export-react-test-renderer"`, settings: { @@ -333,7 +327,7 @@ describe('TypeScript', () => { }, }, }), - test({ + tValid({ code: `import Foo from "./typescript-extended-config"`, settings: { @@ -349,7 +343,7 @@ describe('TypeScript', () => { }, }, }), - test({ + tValid({ code: `import foobar from "./typescript-export-assign-property"`, settings: { @@ -360,7 +354,7 @@ describe('TypeScript', () => { ], invalid: [ - test({ + tInvalid({ code: `import foobar from "./typescript"`, settings: { @@ -376,7 +370,7 @@ describe('TypeScript', () => { }, ], }), - test({ + tInvalid({ code: `import React from "./typescript-export-assign-default-namespace"`, settings: { @@ -392,7 +386,7 @@ describe('TypeScript', () => { }, ], }), - test({ + tInvalid({ code: `import FooBar from "./typescript-export-as-default-namespace"`, settings: { @@ -408,7 +402,7 @@ describe('TypeScript', () => { }, ], }), - test({ + tInvalid({ code: `import Foo from "./typescript-export-as-default-namespace"`, settings: { diff --git a/test/rules/export.spec.ts b/test/rules/export.spec.ts index 9d573dd28..770128035 100644 --- a/test/rules/export.spec.ts +++ b/test/rules/export.spec.ts @@ -4,39 +4,39 @@ import { testFilePath, SYNTAX_VALID_CASES, parsers, - createRuleTestCaseFunction, + createRuleTestCaseFunctions, } from '../utils' import rule from 'eslint-plugin-import-x/rules/export' const ruleTester = new TSESLintRuleTester() -const test = createRuleTestCaseFunction() +const { tValid, tInvalid } = createRuleTestCaseFunctions() ruleTester.run('export', rule, { valid: [ - test({ + tValid({ code: 'import "./malformed.js"', languageOptions: { parser: require(parsers.ESPREE) }, }), // default - test({ code: 'var foo = "foo"; export default foo;' }), - test({ code: 'export var foo = "foo"; export var bar = "bar";' }), - test({ code: 'export var foo = "foo", bar = "bar";' }), - test({ code: 'export var { foo, bar } = object;' }), - test({ code: 'export var [ foo, bar ] = array;' }), - test({ code: 'let foo; export { foo, foo as bar }' }), - test({ code: 'let bar; export { bar }; export * from "./export-all"' }), - test({ code: 'export * from "./export-all"' }), - test({ code: 'export * from "./does-not-exist"' }), + tValid({ code: 'var foo = "foo"; export default foo;' }), + tValid({ code: 'export var foo = "foo"; export var bar = "bar";' }), + tValid({ code: 'export var foo = "foo", bar = "bar";' }), + tValid({ code: 'export var { foo, bar } = object;' }), + tValid({ code: 'export var [ foo, bar ] = array;' }), + tValid({ code: 'let foo; export { foo, foo as bar }' }), + tValid({ code: 'let bar; export { bar }; export * from "./export-all"' }), + tValid({ code: 'export * from "./export-all"' }), + tValid({ code: 'export * from "./does-not-exist"' }), // #328: "export * from" does not export a default - test({ code: 'export default foo; export * from "./bar"' }), + tValid({ code: 'export default foo; export * from "./bar"' }), ...SYNTAX_VALID_CASES, - test({ + tValid({ code: ` import * as A from './named-export-collision/a'; import * as B from './named-export-collision/b'; @@ -44,7 +44,7 @@ ruleTester.run('export', rule, { export { A, B }; `, }), - test({ + tValid({ code: ` export * as A from './named-export-collision/a'; export * as B from './named-export-collision/b'; @@ -77,45 +77,45 @@ ruleTester.run('export', rule, { invalid: [ // multiple defaults - // test({ + // tInvalid({ // code: 'export default foo; export default bar', // errors: ['Multiple default exports.', 'Multiple default exports.'], // }), - // test({ + // tInvalid({ // code: 'export default function foo() {}; ' + // 'export default function bar() {}', // errors: ['Multiple default exports.', 'Multiple default exports.'], // }), - // test({ + // tInvalid({ // code: 'export function foo() {}; ' + // 'export { bar as foo }', // errors: ['Parsing error: Duplicate export \'foo\''], // }), - // test({ + // tInvalid({ // code: 'export {foo}; export {foo};', // errors: ['Parsing error: Duplicate export \'foo\''], // }), - // test({ + // tInvalid({ // code: 'export {foo}; export {bar as foo};', // errors: ['Parsing error: Duplicate export \'foo\''], // }), - // test({ + // tInvalid({ // code: 'export var foo = "foo"; export var foo = "bar";', // errors: ['Parsing error: Duplicate export \'foo\''], // }), - // test({ + // tInvalid({ // code: 'export var foo = "foo", foo = "bar";', // errors: ['Parsing error: Duplicate export \'foo\''], // }), - test({ + tInvalid({ code: 'let foo; export { foo }; export * from "./export-all"', errors: [ { messageId: 'multiNamed', data: { name: 'foo' } }, { messageId: 'multiNamed', data: { name: 'foo' } }, ], }), - // test({ + // tInvalid({ // code: 'export * from "./default-export"', // errors: [ // { @@ -127,7 +127,7 @@ ruleTester.run('export', rule, { // note: Espree bump to Acorn 4+ changed this test's error message. // `npm up` first if it's failing. - test({ + tInvalid({ code: 'export * from "./malformed.js"', languageOptions: { parser: require(parsers.ESPREE) }, errors: [ @@ -139,30 +139,30 @@ ruleTester.run('export', rule, { ], }), - // test({ + // tInvalid({ // code: 'export var { foo, bar } = object; export var foo = "bar"', // errors: ['Parsing error: Duplicate export \'foo\''], // }), - // test({ + // tInvalid({ // code: 'export var { bar: { foo } } = object; export var foo = "bar"', // errors: ['Parsing error: Duplicate export \'foo\''], // }), - // test({ + // tInvalid({ // code: 'export var [ foo, bar ] = array; export var bar = "baz"', // errors: ['Parsing error: Duplicate export \'bar\''], // }), - // test({ + // tInvalid({ // code: 'export var [ foo, /*sparse*/, { bar } ] = array; export var bar = "baz"', // errors: ['Parsing error: Duplicate export \'bar\''], // }), // #328: "export * from" does not export a default - test({ + tInvalid({ code: 'export * from "./default-export"', errors: [{ messageId: 'noNamed', data: { module: './default-export' } }], }), - test({ + tInvalid({ code: 'let foo; export { foo as "foo" }; export * from "./export-all"', errors: [ { messageId: 'multiNamed', data: { name: 'foo' } }, @@ -176,7 +176,7 @@ ruleTester.run('export', rule, { }, }), - test({ + tInvalid({ code: ` export default function a(): void; export default function a() {} @@ -198,14 +198,14 @@ describe('TypeScript', () => { ruleTester.run('export', rule, { valid: [ // type/value name clash - test({ + tValid({ code: ` export const Foo = 1; export type Foo = number; `, ...parserConfig, }), - test({ + tValid({ code: ` export const Foo = 1; export interface Foo {} @@ -213,7 +213,7 @@ describe('TypeScript', () => { ...parserConfig, }), - test({ + tValid({ code: ` export function fff(a: string); export function fff(a: number); @@ -221,7 +221,7 @@ describe('TypeScript', () => { ...parserConfig, }), - test({ + tValid({ code: ` export function fff(a: string); export function fff(a: number); @@ -231,7 +231,7 @@ describe('TypeScript', () => { }), // namespace - test({ + tValid({ code: ` export const Bar = 1; export namespace Foo { @@ -240,7 +240,7 @@ describe('TypeScript', () => { `, ...parserConfig, }), - test({ + tValid({ code: ` export type Bar = string; export namespace Foo { @@ -249,7 +249,7 @@ describe('TypeScript', () => { `, ...parserConfig, }), - test({ + tValid({ code: ` export const Bar = 1; export type Bar = string; @@ -260,7 +260,7 @@ describe('TypeScript', () => { `, ...parserConfig, }), - test({ + tValid({ code: ` export namespace Foo { export const Foo = 1; @@ -275,7 +275,7 @@ describe('TypeScript', () => { ...parserConfig, }), - test({ + tValid({ code: ` export class Foo { } export namespace Foo { } @@ -285,21 +285,21 @@ describe('TypeScript', () => { `, ...parserConfig, }), - test({ + tValid({ code: ` export function Foo(); export namespace Foo { } `, ...parserConfig, }), - test({ + tValid({ code: ` export function Foo(a: string); export namespace Foo { } `, ...parserConfig, }), - test({ + tValid({ code: ` export function Foo(a: string); export function Foo(a: number); @@ -307,20 +307,20 @@ describe('TypeScript', () => { `, ...parserConfig, }), - test({ + tValid({ code: ` export enum Foo { } export namespace Foo { } `, ...parserConfig, }), - test({ + tValid({ code: 'export * from "./file1.ts"', filename: testFilePath('typescript-d-ts/file-2.ts'), ...parserConfig, }), - test({ + tValid({ code: ` export * as A from './named-export-collision/a'; export * as B from './named-export-collision/b'; @@ -328,7 +328,7 @@ describe('TypeScript', () => { }), // Exports in ambient modules - test({ + tValid({ code: ` declare module "a" { const Foo = 1; @@ -341,7 +341,7 @@ describe('TypeScript', () => { `, ...parserConfig, }), - test({ + tValid({ code: ` declare module "a" { const Foo = 1; @@ -353,7 +353,7 @@ describe('TypeScript', () => { ...parserConfig, }), - test({ + tValid({ ...parserConfig, code: ` export * from './module'; @@ -367,7 +367,7 @@ describe('TypeScript', () => { ], invalid: [ // type/value name clash - test({ + tInvalid({ code: ` export type Foo = string; export type Foo = number; @@ -388,7 +388,7 @@ describe('TypeScript', () => { }), // namespace - test({ + tInvalid({ code: ` export const a = 1 export namespace Foo { @@ -410,7 +410,7 @@ describe('TypeScript', () => { ], ...parserConfig, }), - test({ + tInvalid({ code: ` declare module 'foo' { const Foo = 1; @@ -430,7 +430,7 @@ describe('TypeScript', () => { ], ...parserConfig, }), - test({ + tInvalid({ code: ` export namespace Foo { export namespace Bar { @@ -468,7 +468,7 @@ describe('TypeScript', () => { ...parserConfig, }), - test({ + tInvalid({ code: ` export class Foo { } export class Foo { } @@ -488,7 +488,7 @@ describe('TypeScript', () => { ], ...parserConfig, }), - test({ + tInvalid({ code: ` export enum Foo { } export enum Foo { } @@ -508,7 +508,7 @@ describe('TypeScript', () => { ], ...parserConfig, }), - test({ + tInvalid({ code: ` export enum Foo { } export class Foo { } @@ -528,7 +528,7 @@ describe('TypeScript', () => { ], ...parserConfig, }), - test({ + tInvalid({ code: ` export const Foo = 'bar'; export class Foo { } @@ -548,7 +548,7 @@ describe('TypeScript', () => { ], ...parserConfig, }), - test({ + tInvalid({ code: ` export function Foo() { }; export class Foo { } @@ -568,7 +568,7 @@ describe('TypeScript', () => { ], ...parserConfig, }), - test({ + tInvalid({ code: ` export const Foo = 'bar'; export function Foo() { }; @@ -588,7 +588,7 @@ describe('TypeScript', () => { ], ...parserConfig, }), - test({ + tInvalid({ code: ` export const Foo = 'bar'; export namespace Foo { } @@ -609,7 +609,7 @@ describe('TypeScript', () => { }), // Exports in ambient modules - test({ + tInvalid({ code: ` declare module "a" { const Foo = 1; diff --git a/test/rules/exports-last.spec.ts b/test/rules/exports-last.spec.ts index 99891f9f7..89f8e9066 100644 --- a/test/rules/exports-last.spec.ts +++ b/test/rules/exports-last.spec.ts @@ -2,14 +2,14 @@ import { RuleTester as TSESLintRuleTester } from '@typescript-eslint/rule-tester import type { TestCaseError as TSESLintTestCaseError } from '@typescript-eslint/rule-tester' import type { TSESTree } from '@typescript-eslint/utils' -import { createRuleTestCaseFunction } from '../utils' +import { createRuleTestCaseFunctions } from '../utils' import type { GetRuleModuleMessageIds } from '../utils' import rule from 'eslint-plugin-import-x/rules/exports-last' const ruleTester = new TSESLintRuleTester() -const test = createRuleTestCaseFunction() +const { tValid, tInvalid } = createRuleTestCaseFunctions() function createInvalidCaseError( type: `${TSESTree.AST_NODE_TYPES}`, @@ -23,36 +23,36 @@ function createInvalidCaseError( ruleTester.run('exports-last', rule, { valid: [ // Empty file - test({ + tValid({ code: '// comment', }), - test({ + tValid({ // No exports code: ` const foo = 'bar' const bar = 'baz' `, }), - test({ + tValid({ code: ` const foo = 'bar' export {foo} `, }), - test({ + tValid({ code: ` const foo = 'bar' export default foo `, }), // Only exports - test({ + tValid({ code: ` export default foo export const bar = true `, }), - test({ + tValid({ code: ` const foo = 'bar' export default foo @@ -60,7 +60,7 @@ ruleTester.run('exports-last', rule, { `, }), // Multiline export - test({ + tValid({ code: ` const foo = 'bar' export default function bar () { @@ -70,7 +70,7 @@ ruleTester.run('exports-last', rule, { `, }), // Many exports - test({ + tValid({ code: ` const foo = 'bar' export default foo @@ -82,7 +82,7 @@ ruleTester.run('exports-last', rule, { `, }), // Export all - test({ + tValid({ code: ` export * from './foo' `, @@ -90,7 +90,7 @@ ruleTester.run('exports-last', rule, { ], invalid: [ // Default export before variable declaration - test({ + tInvalid({ code: ` export default 'bar' const bar = true @@ -98,7 +98,7 @@ ruleTester.run('exports-last', rule, { errors: [createInvalidCaseError('ExportDefaultDeclaration')], }), // Named export before variable declaration - test({ + tInvalid({ code: ` export const foo = 'bar' const bar = true @@ -106,7 +106,7 @@ ruleTester.run('exports-last', rule, { errors: [createInvalidCaseError('ExportNamedDeclaration')], }), // Export all before variable declaration - test({ + tInvalid({ code: ` export * from './foo' const bar = true @@ -114,7 +114,7 @@ ruleTester.run('exports-last', rule, { errors: [createInvalidCaseError('ExportAllDeclaration')], }), // Many exports around variable declaration - test({ + tInvalid({ code: ` export default 'such foo many bar' export const so = 'many' diff --git a/test/rules/extensions.spec.ts b/test/rules/extensions.spec.ts index a7d8d438d..e9aa26bb0 100644 --- a/test/rules/extensions.spec.ts +++ b/test/rules/extensions.spec.ts @@ -1,6 +1,6 @@ import { RuleTester as TSESLintRuleTester } from '@typescript-eslint/rule-tester' -import { createRuleTestCaseFunction, testFilePath } from '../utils' +import { createRuleTestCaseFunctions, testFilePath } from '../utils' import rule from 'eslint-plugin-import-x/rules/extensions' @@ -15,29 +15,29 @@ const ruleTesterWithTypeScriptImports = new TSESLintRuleTester({ }, }) -const test = createRuleTestCaseFunction() +const { tValid, tInvalid } = createRuleTestCaseFunctions() ruleTester.run('extensions', rule, { valid: [ - test({ code: 'import a from "@/a"' }), - test({ code: 'import a from "a"' }), - test({ code: 'import dot from "./file.with.dot"' }), - test({ + tValid({ code: 'import a from "@/a"' }), + tValid({ code: 'import a from "a"' }), + tValid({ code: 'import dot from "./file.with.dot"' }), + tValid({ code: 'import a from "a/index.js"', options: ['always'], }), - test({ + tValid({ code: 'import dot from "./file.with.dot.js"', options: ['always'], }), - test({ + tValid({ code: [ 'import a from "a"', 'import packageConfig from "./package.json"', ].join('\n'), options: [{ json: 'always', js: 'never' }], }), - test({ + tValid({ code: [ 'import lib from "./bar"', 'import component from "./bar.jsx"', @@ -49,7 +49,7 @@ ruleTester.run('extensions', rule, { }, }), - test({ + tValid({ code: [ 'import bar from "./bar"', 'import barjson from "./bar.json"', @@ -61,7 +61,7 @@ ruleTester.run('extensions', rule, { }, }), - test({ + tValid({ code: ['import bar from "./bar.js"', 'import pack from "./package"'].join( '\n', ), @@ -70,13 +70,13 @@ ruleTester.run('extensions', rule, { }), // unresolved (#271/#295) - test({ code: 'import path from "path"' }), - test({ code: 'import path from "path"', options: ['never'] }), - test({ code: 'import path from "path"', options: ['always'] }), - test({ code: 'import thing from "./fake-file.js"', options: ['always'] }), - test({ code: 'import thing from "non-package"', options: ['never'] }), + tValid({ code: 'import path from "path"' }), + tValid({ code: 'import path from "path"', options: ['never'] }), + tValid({ code: 'import path from "path"', options: ['always'] }), + tValid({ code: 'import thing from "./fake-file.js"', options: ['always'] }), + tValid({ code: 'import thing from "non-package"', options: ['never'] }), - test({ + tValid({ code: ` import foo from './foo.js' import bar from './bar.json' @@ -86,7 +86,7 @@ ruleTester.run('extensions', rule, { options: ['ignorePackages'], }), - test({ + tValid({ code: ` import foo from './foo.js' import bar from './bar.json' @@ -96,7 +96,7 @@ ruleTester.run('extensions', rule, { options: ['always', { ignorePackages: true }], }), - test({ + tValid({ code: ` import foo from './foo' import bar from './bar' @@ -106,7 +106,7 @@ ruleTester.run('extensions', rule, { options: ['never', { ignorePackages: true }], }), - test({ + tValid({ code: 'import exceljs from "exceljs"', options: ['always', { js: 'never', jsx: 'never' }], filename: testFilePath('./internal-modules/plugins/plugin.js'), @@ -119,13 +119,13 @@ ruleTester.run('extensions', rule, { }), // export (#964) - test({ + tValid({ code: ['export { foo } from "./foo.js"', 'let bar; export { bar }'].join( '\n', ), options: ['always'], }), - test({ + tValid({ code: ['export { foo } from "./foo"', 'let bar; export { bar }'].join( '\n', ), @@ -133,7 +133,7 @@ ruleTester.run('extensions', rule, { }), // Root packages should be ignored and they are names not files - test({ + tValid({ code: [ 'import lib from "pkg.js"', 'import lib2 from "pgk/package"', @@ -143,16 +143,16 @@ ruleTester.run('extensions', rule, { }), // Query strings. - test({ + tValid({ code: 'import bare from "./foo?a=True.ext"', options: ['never'], }), - test({ + tValid({ code: 'import bare from "./foo.js?a=True"', options: ['always'], }), - test({ + tValid({ code: [ 'import lib from "pkg"', 'import lib2 from "pgk/package.js"', @@ -163,7 +163,7 @@ ruleTester.run('extensions', rule, { ], invalid: [ - test({ + tInvalid({ code: 'import a from "a/index.js"', errors: [ { @@ -174,7 +174,7 @@ ruleTester.run('extensions', rule, { }, ], }), - test({ + tInvalid({ code: 'import dot from "./file.with.dot"', options: ['always'], errors: [ @@ -186,7 +186,7 @@ ruleTester.run('extensions', rule, { }, ], }), - test({ + tInvalid({ code: [ 'import a from "a/index.js"', 'import packageConfig from "./package"', @@ -208,7 +208,7 @@ ruleTester.run('extensions', rule, { }, ], }), - test({ + tInvalid({ code: [ 'import lib from "./bar.js"', 'import component from "./bar.jsx"', @@ -227,7 +227,7 @@ ruleTester.run('extensions', rule, { }, ], }), - test({ + tInvalid({ code: [ 'import lib from "./bar.js"', 'import component from "./bar.jsx"', @@ -247,7 +247,7 @@ ruleTester.run('extensions', rule, { ], }), // extension resolve order (#583/#965) - test({ + tInvalid({ code: [ 'import component from "./bar.jsx"', 'import data from "./bar.json"', @@ -265,7 +265,7 @@ ruleTester.run('extensions', rule, { }, ], }), - test({ + tInvalid({ code: 'import "./bar.coffee"', errors: [ { @@ -279,7 +279,7 @@ ruleTester.run('extensions', rule, { settings: { 'import-x/resolve': { extensions: ['.coffee', '.js'] } }, }), - test({ + tInvalid({ code: [ 'import barjs from "./bar.js"', 'import barjson from "./bar.json"', @@ -299,7 +299,7 @@ ruleTester.run('extensions', rule, { ], }), - test({ + tInvalid({ code: ['import barjs from "."', 'import barjs2 from ".."'].join('\n'), options: ['always'], errors: [ @@ -318,7 +318,7 @@ ruleTester.run('extensions', rule, { ], }), - test({ + tInvalid({ code: [ 'import barjs from "./bar.js"', 'import barjson from "./bar.json"', @@ -339,7 +339,7 @@ ruleTester.run('extensions', rule, { }), // unresolved (#271/#295) - test({ + tInvalid({ code: 'import thing from "./fake-file.js"', options: ['never'], errors: [ @@ -351,7 +351,7 @@ ruleTester.run('extensions', rule, { }, ], }), - test({ + tInvalid({ code: 'import thing from "non-package/test"', options: ['always'], errors: [ @@ -364,7 +364,7 @@ ruleTester.run('extensions', rule, { ], }), - test({ + tInvalid({ code: 'import thing from "@name/pkg/test"', options: ['always'], errors: [ @@ -377,7 +377,7 @@ ruleTester.run('extensions', rule, { ], }), - test({ + tInvalid({ code: 'import thing from "@name/pkg/test.js"', options: ['never'], errors: [ @@ -390,7 +390,7 @@ ruleTester.run('extensions', rule, { ], }), - test({ + tInvalid({ code: ` import foo from './foo.js' import bar from './bar.json' @@ -417,7 +417,7 @@ ruleTester.run('extensions', rule, { ], }), - test({ + tInvalid({ code: ` import foo from './foo.js' import bar from './bar.json' @@ -444,7 +444,7 @@ ruleTester.run('extensions', rule, { ], }), - test({ + tInvalid({ code: ` import foo from './foo.js' import bar from './bar.json' @@ -469,7 +469,7 @@ ruleTester.run('extensions', rule, { options: ['never', { ignorePackages: true }], }), - test({ + tInvalid({ code: ` import foo from './foo.js' import bar from './bar.json' @@ -487,7 +487,7 @@ ruleTester.run('extensions', rule, { }), // export (#964) - test({ + tInvalid({ code: ['export { foo } from "./foo"', 'let bar; export { bar }'].join( '\n', ), @@ -501,7 +501,7 @@ ruleTester.run('extensions', rule, { }, ], }), - test({ + tInvalid({ code: ['export { foo } from "./foo.js"', 'let bar; export { bar }'].join( '\n', ), @@ -517,7 +517,7 @@ ruleTester.run('extensions', rule, { }), // Query strings. - test({ + tInvalid({ code: 'import withExtension from "./foo.js?a=True"', options: ['never'], errors: [ @@ -529,7 +529,7 @@ ruleTester.run('extensions', rule, { }, ], }), - test({ + tInvalid({ code: 'import withoutExtension from "./foo?a=True.ext"', options: ['always'], errors: [ @@ -543,7 +543,7 @@ ruleTester.run('extensions', rule, { }), // require (#1230) - test({ + tInvalid({ code: ['const { foo } = require("./foo")', 'export { foo }'].join('\n'), options: ['always'], errors: [ @@ -555,7 +555,7 @@ ruleTester.run('extensions', rule, { }, ], }), - test({ + tInvalid({ code: ['const { foo } = require("./foo.js")', 'export { foo }'].join( '\n', ), @@ -571,7 +571,7 @@ ruleTester.run('extensions', rule, { }), // export { } from - test({ + tInvalid({ code: 'export { foo } from "./foo"', options: ['always'], errors: [ @@ -583,7 +583,7 @@ ruleTester.run('extensions', rule, { }, ], }), - test({ + tInvalid({ code: ` import foo from "@/ImNotAScopedModule"; import chart from '@/configs/chart'; @@ -602,7 +602,7 @@ ruleTester.run('extensions', rule, { }, ], }), - test({ + tInvalid({ code: 'export { foo } from "./foo.js"', options: ['never'], errors: [ @@ -616,7 +616,7 @@ ruleTester.run('extensions', rule, { }), // export * from - test({ + tInvalid({ code: 'export * from "./foo"', options: ['always'], errors: [ @@ -628,7 +628,7 @@ ruleTester.run('extensions', rule, { }, ], }), - test({ + tInvalid({ code: 'export * from "./foo.js"', options: ['never'], errors: [ @@ -641,7 +641,7 @@ ruleTester.run('extensions', rule, { }, ], }), - test({ + tInvalid({ code: 'import foo from "@/ImNotAScopedModule.js"', options: ['never'], errors: [ @@ -653,7 +653,7 @@ ruleTester.run('extensions', rule, { }, ], }), - test({ + tInvalid({ code: ` import _ from 'lodash'; import m from '@test-scope/some-module/index.js'; @@ -681,7 +681,7 @@ ruleTester.run('extensions', rule, { }), // TODO: properly ignore packages resolved via relative imports - test({ + tInvalid({ code: ['import * as test from "."'].join('\n'), filename: testFilePath('./internal-modules/test.js'), options: ['ignorePackages'], @@ -694,7 +694,7 @@ ruleTester.run('extensions', rule, { ], }), // TODO: properly ignore packages resolved via relative imports - test({ + tInvalid({ code: ['import * as test from ".."'].join('\n'), filename: testFilePath('./internal-modules/plugins/plugin.js'), options: ['ignorePackages'], @@ -712,14 +712,14 @@ ruleTester.run('extensions', rule, { describe('TypeScript', () => { ruleTester.run(`typescript - extensions ignore type-only`, rule, { valid: [ - test({ + tValid({ code: 'import type T from "./typescript-declare";', options: [ 'always', { ts: 'never', tsx: 'never', js: 'never', jsx: 'never' }, ], }), - test({ + tValid({ code: 'export type { MyType } from "./typescript-declare";', options: [ 'always', @@ -728,7 +728,7 @@ describe('TypeScript', () => { }), ], invalid: [ - test({ + tInvalid({ code: 'import T from "./typescript-declare";', errors: [ { @@ -741,7 +741,7 @@ describe('TypeScript', () => { { ts: 'never', tsx: 'never', js: 'never', jsx: 'never' }, ], }), - test({ + tInvalid({ code: 'export { MyType } from "./typescript-declare";', errors: [ { @@ -754,7 +754,7 @@ describe('TypeScript', () => { { ts: 'never', tsx: 'never', js: 'never', jsx: 'never' }, ], }), - test({ + tInvalid({ code: 'import type T from "./typescript-declare";', errors: [ { @@ -775,7 +775,7 @@ describe('TypeScript', () => { }, ], }), - test({ + tInvalid({ code: 'export type { MyType } from "./typescript-declare";', errors: [ { @@ -801,17 +801,17 @@ describe('TypeScript', () => { ruleTesterWithTypeScriptImports.run('extensions', rule, { valid: [ - test({ + tValid({ code: 'import type { MyType } from "./typescript-declare.ts";', options: ['always', { checkTypeImports: true }], }), - test({ + tValid({ code: 'export type { MyType } from "./typescript-declare.ts";', options: ['always', { checkTypeImports: true }], }), ], invalid: [ - test({ + tInvalid({ code: 'import type { MyType } from "./typescript-declare";', errors: [ { @@ -821,7 +821,7 @@ describe('TypeScript', () => { ], options: ['always', { checkTypeImports: true }], }), - test({ + tInvalid({ code: 'export type { MyType } from "./typescript-declare";', errors: [ { diff --git a/test/rules/first.spec.ts b/test/rules/first.spec.ts index e967eb9ee..dda55394e 100644 --- a/test/rules/first.spec.ts +++ b/test/rules/first.spec.ts @@ -2,31 +2,31 @@ import fs from 'node:fs' import { RuleTester as TSESLintRuleTester } from '@typescript-eslint/rule-tester' -import { createRuleTestCaseFunction, parsers, testFilePath } from '../utils' +import { createRuleTestCaseFunctions, parsers, testFilePath } from '../utils' import rule from 'eslint-plugin-import-x/rules/first' const ruleTester = new TSESLintRuleTester() -const test = createRuleTestCaseFunction() +const { tValid, tInvalid } = createRuleTestCaseFunctions() ruleTester.run('first', rule, { valid: [ - test({ + tValid({ code: "import { x } from './foo'; import { y } from './bar';\ export { x, y }", }), - test({ code: "import { x } from 'foo'; import { y } from './bar'" }), - test({ code: "import { x } from './foo'; import { y } from 'bar'" }), - test({ + tValid({ code: "import { x } from 'foo'; import { y } from './bar'" }), + tValid({ code: "import { x } from './foo'; import { y } from 'bar'" }), + tValid({ code: "import { x } from './foo'; import { y } from 'bar'", options: ['disable-absolute-first'], }), - test({ + tValid({ code: "'use directive';\ import { x } from 'foo';", }), - test({ + tValid({ name: '...component.html (issue #2210)', code: fs.readFileSync(testFilePath('component.html'), 'utf8'), languageOptions: { @@ -35,7 +35,7 @@ ruleTester.run('first', rule, { }), ], invalid: [ - test({ + tInvalid({ code: "import { x } from './foo';\ export { x };\ import { y } from './bar';", @@ -45,7 +45,7 @@ ruleTester.run('first', rule, { import { y } from './bar';\ export { x };", }), - test({ + tInvalid({ code: "import { x } from './foo';\ export { x };\ import { y } from './bar';\ @@ -57,12 +57,12 @@ ruleTester.run('first', rule, { import { z } from './baz';\ export { x };", }), - test({ + tInvalid({ code: "import { x } from './foo'; import { y } from 'bar'", options: ['absolute-first'], errors: [{ messageId: 'absolute' }], }), - test({ + tInvalid({ code: "import { x } from 'foo';\ 'use directive';\ import { y } from 'bar';", @@ -72,7 +72,7 @@ ruleTester.run('first', rule, { import { y } from 'bar';\ 'use directive';", }), - test({ + tInvalid({ code: "var a = 1;\ import { y } from './bar';\ if (true) { x() };\ @@ -101,7 +101,7 @@ ruleTester.run('first', rule, { if (true) { x() };", ], }), - test({ + tInvalid({ code: "if (true) { console.log(1) }import a from 'b'", errors: [{ messageId: 'order' }], output: "import a from 'b'\nif (true) { console.log(1) }", @@ -119,7 +119,7 @@ describe('TypeScript', () => { ruleTester.run('order', rule, { valid: [ - test({ + tValid({ code: ` import y = require('bar'); import { x } from 'foo'; @@ -129,7 +129,7 @@ describe('TypeScript', () => { }), ], invalid: [ - test({ + tInvalid({ code: ` import { x } from './foo'; import y = require('bar'); diff --git a/test/rules/group-exports.spec.ts b/test/rules/group-exports.spec.ts index 207e37698..42f4e862e 100644 --- a/test/rules/group-exports.spec.ts +++ b/test/rules/group-exports.spec.ts @@ -1,6 +1,6 @@ import { RuleTester as TSESLintRuleTester } from '@typescript-eslint/rule-tester' -import { createRuleTestCaseFunction, parsers } from '../utils' +import { createRuleTestCaseFunctions, parsers } from '../utils' import rule from 'eslint-plugin-import-x/rules/group-exports' @@ -18,18 +18,18 @@ const ruleTester = new TSESLintRuleTester({ }, }) -const test = createRuleTestCaseFunction() +const { tValid, tInvalid } = createRuleTestCaseFunctions() ruleTester.run('group-exports', rule, { valid: [ - test({ code: 'export const test = true' }), - test({ + tValid({ code: 'export const test = true' }), + tValid({ code: ` export default {} export const test = true `, }), - test({ + tValid({ code: ` const first = true const second = true @@ -39,87 +39,87 @@ ruleTester.run('group-exports', rule, { } `, }), - test({ + tValid({ code: ` export default {} /* test */ export const test = true `, }), - test({ + tValid({ code: ` export default {} // test export const test = true `, }), - test({ + tValid({ code: ` export const test = true /* test */ export default {} `, }), - test({ + tValid({ code: ` export const test = true // test export default {} `, }), - test({ + tValid({ code: ` export { default as module1 } from './module-1' export { default as module2 } from './module-2' `, }), - test({ code: 'module.exports = {} ' }), - test({ + tValid({ code: 'module.exports = {} ' }), + tValid({ code: ` module.exports = { test: true, another: false } `, }), - test({ code: 'exports.test = true' }), + tValid({ code: 'exports.test = true' }), - test({ + tValid({ code: ` module.exports = {} const test = module.exports `, }), - test({ + tValid({ code: ` exports.test = true const test = exports.test `, }), - test({ + tValid({ code: ` module.exports = {} module.exports.too.deep = true `, }), - test({ + tValid({ code: ` module.exports.deep.first = true module.exports.deep.second = true `, }), - test({ + tValid({ code: ` module.exports = {} exports.too.deep = true `, }), - test({ + tValid({ code: ` export default {} const test = true export { test } `, }), - test({ + tValid({ code: ` const test = true export { test } @@ -127,31 +127,31 @@ ruleTester.run('group-exports', rule, { export default {} `, }), - test({ + tValid({ code: ` module.something.else = true module.something.different = true `, }), - test({ + tValid({ code: ` module.exports.test = true module.something.different = true `, }), - test({ + tValid({ code: ` exports.test = true module.something.different = true `, }), - test({ + tValid({ code: ` unrelated = 'assignment' module.exports.test = true `, }), - test({ + tValid({ code: ` type firstType = { propType: string @@ -161,7 +161,7 @@ ruleTester.run('group-exports', rule, { export { first }; `, }), - test({ + tValid({ code: ` type firstType = { propType: string @@ -172,7 +172,7 @@ ruleTester.run('group-exports', rule, { export type { firstType, secondType }; `, }), - test({ + tValid({ code: ` export type { type1A, type1B } from './module-1' export { method1 } from './module-1' @@ -180,7 +180,7 @@ ruleTester.run('group-exports', rule, { }), ], invalid: [ - test({ + tInvalid({ code: ` export const test = true export const another = true @@ -190,7 +190,7 @@ ruleTester.run('group-exports', rule, { { messageId: 'ExportNamedDeclaration' }, ], }), - test({ + tInvalid({ code: ` export { method1 } from './module-1' export { method2 } from './module-1' @@ -200,7 +200,7 @@ ruleTester.run('group-exports', rule, { { messageId: 'ExportNamedDeclaration' }, ], }), - test({ + tInvalid({ code: ` module.exports = {} module.exports.test = true @@ -212,7 +212,7 @@ ruleTester.run('group-exports', rule, { { messageId: 'AssignmentExpression' }, ], }), - test({ + tInvalid({ code: ` module.exports = {} module.exports.test = true @@ -222,7 +222,7 @@ ruleTester.run('group-exports', rule, { { messageId: 'AssignmentExpression' }, ], }), - test({ + tInvalid({ code: ` module.exports = { test: true } module.exports.another = true @@ -232,7 +232,7 @@ ruleTester.run('group-exports', rule, { { messageId: 'AssignmentExpression' }, ], }), - test({ + tInvalid({ code: ` module.exports.test = true module.exports.another = true @@ -242,7 +242,7 @@ ruleTester.run('group-exports', rule, { { messageId: 'AssignmentExpression' }, ], }), - test({ + tInvalid({ code: ` exports.test = true module.exports.another = true @@ -252,7 +252,7 @@ ruleTester.run('group-exports', rule, { { messageId: 'AssignmentExpression' }, ], }), - test({ + tInvalid({ code: ` module.exports = () => {} module.exports.attached = true @@ -262,9 +262,9 @@ ruleTester.run('group-exports', rule, { { messageId: 'AssignmentExpression' }, ], }), - test({ + tInvalid({ code: ` - module.exports = function test() {} + module.exports = function tInvalid() {} module.exports.attached = true `, errors: [ @@ -272,7 +272,7 @@ ruleTester.run('group-exports', rule, { { messageId: 'AssignmentExpression' }, ], }), - test({ + tInvalid({ code: ` module.exports = () => {} exports.test = true @@ -284,7 +284,7 @@ ruleTester.run('group-exports', rule, { { messageId: 'AssignmentExpression' }, ], }), - test({ + tInvalid({ code: ` module.exports = "non-object" module.exports.attached = true @@ -294,7 +294,7 @@ ruleTester.run('group-exports', rule, { { messageId: 'AssignmentExpression' }, ], }), - test({ + tInvalid({ code: ` module.exports = "non-object" module.exports.attached = true @@ -306,7 +306,7 @@ ruleTester.run('group-exports', rule, { { messageId: 'AssignmentExpression' }, ], }), - test({ + tInvalid({ code: ` type firstType = { propType: string @@ -324,7 +324,7 @@ ruleTester.run('group-exports', rule, { { messageId: 'ExportNamedDeclaration' }, ], }), - test({ + tInvalid({ code: ` export type { type1 } from './module-1' export type { type2 } from './module-1' diff --git a/test/rules/max-dependencies.spec.ts b/test/rules/max-dependencies.spec.ts index 27cfd89a9..a5f679c40 100644 --- a/test/rules/max-dependencies.spec.ts +++ b/test/rules/max-dependencies.spec.ts @@ -1,18 +1,18 @@ import { RuleTester as TSESLintRuleTester } from '@typescript-eslint/rule-tester' -import { parsers, createRuleTestCaseFunction } from '../utils' +import { parsers, createRuleTestCaseFunctions } from '../utils' import rule from 'eslint-plugin-import-x/rules/max-dependencies' const ruleTester = new TSESLintRuleTester() -const test = createRuleTestCaseFunction() +const { tValid, tInvalid } = createRuleTestCaseFunctions() ruleTester.run('max-dependencies', rule, { valid: [ - test({ code: 'import "./foo.js"' }), + tValid({ code: 'import "./foo.js"' }), - test({ + tValid({ code: 'import "./foo.js"; import "./bar.js";', options: [ { @@ -21,7 +21,7 @@ ruleTester.run('max-dependencies', rule, { ], }), - test({ + tValid({ code: 'import "./foo.js"; import "./bar.js"; const a = require("./foo.js"); const b = require("./bar.js");', options: [ { @@ -30,10 +30,10 @@ ruleTester.run('max-dependencies', rule, { ], }), - test({ code: 'import {x, y, z} from "./foo"' }), + tValid({ code: 'import {x, y, z} from "./foo"' }), ], invalid: [ - test({ + tInvalid({ code: "import { x } from './foo'; import { y } from './foo'; import {z} from './bar';", options: [ { @@ -43,7 +43,7 @@ ruleTester.run('max-dependencies', rule, { errors: [{ messageId: 'max', data: { max: 1 } }], }), - test({ + tInvalid({ code: "import { x } from './foo'; import { y } from './bar'; import { z } from './baz';", options: [ { @@ -53,7 +53,7 @@ ruleTester.run('max-dependencies', rule, { errors: [{ messageId: 'max', data: { max: 2 } }], }), - test({ + tInvalid({ code: "import { x } from './foo'; require(\"./bar\"); import { z } from './baz';", options: [ { @@ -63,7 +63,7 @@ ruleTester.run('max-dependencies', rule, { errors: [{ messageId: 'max', data: { max: 2 } }], }), - test({ + tInvalid({ code: 'import { x } from \'./foo\'; import { z } from \'./foo\'; require("./bar"); const path = require("path");', options: [ { @@ -73,7 +73,7 @@ ruleTester.run('max-dependencies', rule, { errors: [{ messageId: 'max', data: { max: 2 } }], }), - test({ + tInvalid({ code: "import type { x } from './foo'; import type { y } from './bar'", languageOptions: { parser: require(parsers.BABEL) }, options: [ @@ -84,7 +84,7 @@ ruleTester.run('max-dependencies', rule, { errors: [{ messageId: 'max', data: { max: 1 } }], }), - test({ + tInvalid({ code: "import type { x } from './foo'; import type { y } from './bar'; import type { z } from './baz'", languageOptions: { parser: require(parsers.BABEL) }, options: [ @@ -101,7 +101,7 @@ ruleTester.run('max-dependencies', rule, { describe('TypeScript', () => { ruleTester.run('max-dependencies', rule, { valid: [ - test({ + tValid({ code: "import type { x } from './foo'; import { y } from './bar';", options: [ @@ -113,7 +113,7 @@ describe('TypeScript', () => { }), ], invalid: [ - test({ + tInvalid({ code: "import type { x } from './foo'; import type { y } from './bar'", options: [ @@ -124,7 +124,7 @@ describe('TypeScript', () => { errors: [{ messageId: 'max', data: { max: 1 } }], }), - test({ + tInvalid({ code: "import type { x } from './foo'; import type { y } from './bar'; import type { z } from './baz'", options: [ diff --git a/test/rules/named.spec.ts b/test/rules/named.spec.ts index 81109a25c..4ffdaff70 100644 --- a/test/rules/named.spec.ts +++ b/test/rules/named.spec.ts @@ -5,7 +5,7 @@ import type { TestCaseError as TSESLintTestCaseError } from '@typescript-eslint/ import type { TSESTree } from '@typescript-eslint/utils' import { - createRuleTestCaseFunction, + createRuleTestCaseFunctions, SYNTAX_VALID_CASES, testFilePath, parsers, @@ -17,7 +17,7 @@ import { CASE_SENSITIVE_FS } from 'eslint-plugin-import-x/utils' const ruleTester = new TSESLintRuleTester() -const test = createRuleTestCaseFunction() +const { tValid, tInvalid } = createRuleTestCaseFunctions() function createNotFoundError( name: string, @@ -45,184 +45,182 @@ function createNotFoundDeepError( ruleTester.run('named', rule, { valid: [ - test({ + tValid({ code: 'import "./malformed.js"', languageOptions: { parser: require(parsers.ESPREE) }, }), - test({ code: 'import { foo } from "./bar"' }), - test({ code: 'import { foo } from "./empty-module"' }), - test({ code: 'import bar from "./bar.js"' }), - test({ code: 'import bar, { foo } from "./bar.js"' }), - test({ code: 'import {a, b, d} from "./named-exports"' }), - test({ code: 'import {ExportedClass} from "./named-exports"' }), - test({ code: 'import { destructingAssign } from "./named-exports"' }), - test({ + tValid({ code: 'import { foo } from "./bar"' }), + tValid({ code: 'import { foo } from "./empty-module"' }), + tValid({ code: 'import bar from "./bar.js"' }), + tValid({ code: 'import bar, { foo } from "./bar.js"' }), + tValid({ code: 'import {a, b, d} from "./named-exports"' }), + tValid({ code: 'import {ExportedClass} from "./named-exports"' }), + tValid({ code: 'import { destructingAssign } from "./named-exports"' }), + tValid({ code: 'import { destructingRenamedAssign } from "./named-exports"', }), - test({ code: 'import { ActionTypes } from "./qc"' }), - test({ code: 'import {a, b, c, d} from "./re-export"' }), - test({ code: 'import {a, b, c} from "./re-export-common-star"' }), - test({ code: 'import {RuleTester} from "./re-export-node_modules"' }), + tValid({ code: 'import { ActionTypes } from "./qc"' }), + tValid({ code: 'import {a, b, c, d} from "./re-export"' }), + tValid({ code: 'import {a, b, c} from "./re-export-common-star"' }), + tValid({ code: 'import {RuleTester} from "./re-export-node_modules"' }), - test({ + tValid({ code: 'import { jsxFoo } from "./jsx/AnotherComponent"', settings: { 'import-x/resolve': { extensions: ['.js', '.jsx'] } }, }), - test({ code: 'import { foo, bar } from "./re-export-names"' }), + tValid({ code: 'import { foo, bar } from "./re-export-names"' }), - test({ + tValid({ code: 'import { foo, bar } from "./common"', settings: { 'import-x/ignore': ['common'] }, }), // ignore core modules by default - test({ code: 'import { foo } from "crypto"' }), - test({ code: 'import { zoob } from "a"' }), + tValid({ code: 'import { foo } from "crypto"' }), + tValid({ code: 'import { zoob } from "a"' }), - test({ code: 'import { someThing } from "./test-module"' }), + tValid({ code: 'import { someThing } from "./test-module"' }), // export tests - test({ code: 'export { foo } from "./bar"' }), - test({ code: 'export { foo as bar } from "./bar"' }), - test({ code: 'export { foo } from "./does-not-exist"' }), + tValid({ code: 'export { foo } from "./bar"' }), + tValid({ code: 'export { foo as bar } from "./bar"' }), + tValid({ code: 'export { foo } from "./does-not-exist"' }), // es7 - test({ + tValid({ code: 'export bar, { foo } from "./bar"', languageOptions: { parser: require(parsers.BABEL) }, }), - test({ + tValid({ code: 'import { foo, bar } from "./named-trampoline"', languageOptions: { parser: require(parsers.BABEL) }, }), // regression tests - test({ code: 'let foo; export { foo as bar }' }), + tValid({ code: 'let foo; export { foo as bar }' }), // destructured exports - test({ code: 'import { destructuredProp } from "./named-exports"' }), - test({ code: 'import { arrayKeyProp } from "./named-exports"' }), - test({ code: 'import { deepProp } from "./named-exports"' }), - test({ code: 'import { deepSparseElement } from "./named-exports"' }), + tValid({ code: 'import { destructuredProp } from "./named-exports"' }), + tValid({ code: 'import { arrayKeyProp } from "./named-exports"' }), + tValid({ code: 'import { deepProp } from "./named-exports"' }), + tValid({ code: 'import { deepSparseElement } from "./named-exports"' }), // should ignore imported/exported flow types, even if they don’t exist - test({ + tValid({ code: 'import type { MissingType } from "./flowtypes"', languageOptions: { parser: require(parsers.BABEL) }, }), - test({ + tValid({ code: 'import typeof { MissingType } from "./flowtypes"', languageOptions: { parser: require(parsers.BABEL) }, }), - test({ + tValid({ code: 'import type { MyOpaqueType } from "./flowtypes"', languageOptions: { parser: require(parsers.BABEL) }, }), - test({ + tValid({ code: 'import typeof { MyOpaqueType } from "./flowtypes"', languageOptions: { parser: require(parsers.BABEL) }, }), - test({ + tValid({ code: 'import { type MyOpaqueType, MyClass } from "./flowtypes"', languageOptions: { parser: require(parsers.BABEL) }, }), - test({ + tValid({ code: 'import { typeof MyOpaqueType, MyClass } from "./flowtypes"', languageOptions: { parser: require(parsers.BABEL) }, }), - test({ + tValid({ code: 'import typeof MissingType from "./flowtypes"', languageOptions: { parser: require(parsers.BABEL) }, }), - test({ + tValid({ code: 'import typeof * as MissingType from "./flowtypes"', languageOptions: { parser: require(parsers.BABEL) }, }), - test({ + tValid({ code: 'export type { MissingType } from "./flowtypes"', languageOptions: { parser: require(parsers.BABEL) }, }), - test({ + tValid({ code: 'export type { MyOpaqueType } from "./flowtypes"', languageOptions: { parser: require(parsers.BABEL) }, }), // jsnext - test({ + tValid({ code: '/*jsnext*/ import { createStore } from "redux"', settings: { 'import-x/ignore': [] }, }), // should work without ignore - test({ + tValid({ code: '/*jsnext*/ import { createStore } from "redux"', }), // ignore is ignored if exports are found - test({ code: 'import { foo } from "es6-module"' }), + tValid({ code: 'import { foo } from "es6-module"' }), // issue #210: shameless self-reference - test({ code: 'import { me, soGreat } from "./narcissist"' }), + tValid({ code: 'import { me, soGreat } from "./narcissist"' }), // issue #251: re-export default as named - test({ code: 'import { foo, bar, baz } from "./re-export-default"' }), - test({ + tValid({ code: 'import { foo, bar, baz } from "./re-export-default"' }), + tValid({ code: 'import { common } from "./re-export-default"', settings: { 'import-x/ignore': ['common'] }, }), // ignore CJS by default. always ignore ignore list - test({ + tValid({ code: 'import {a, b, d} from "./common"', languageOptions: { parser: require(parsers.BABEL) }, }), - test({ + tValid({ code: 'import { baz } from "./bar"', settings: { 'import-x/ignore': ['bar'] }, }), - test({ + tValid({ code: 'import { common } from "./re-export-default"', }), // destructured requires with commonjs option - test({ + tValid({ code: 'const { destructuredProp } = require("./named-exports")', options: [{ commonjs: true }], }), - test({ + tValid({ code: 'let { arrayKeyProp } = require("./named-exports")', options: [{ commonjs: true }], }), - test({ + tValid({ code: 'const { deepProp } = require("./named-exports")', options: [{ commonjs: true }], }), - test({ + tValid({ code: 'const { foo, bar } = require("./re-export-names")', options: [{ commonjs: true }], }), - test({ + tValid({ code: 'const { baz } = require("./bar")', - errors: [createNotFoundError('baz', './bar')], }), - test({ + tValid({ code: 'const { baz } = require("./bar")', - errors: [createNotFoundError('baz', './bar')], options: [{ commonjs: false }], }), - test({ + tValid({ code: 'const { default: defExport } = require("./bar")', options: [{ commonjs: true }], }), ...SYNTAX_VALID_CASES, - test({ + tValid({ code: `import { ExtfieldModel, Extfield2Model } from './models';`, filename: testFilePath('./export-star/downstream.js'), languageOptions: { @@ -233,25 +231,25 @@ ruleTester.run('named', rule, { }, }), - test({ + tValid({ code: 'const { something } = require("./dynamic-import-in-commonjs")', languageOptions: { parserOptions: { ecmaVersion: 2021 } }, options: [{ commonjs: true }], }), - test({ + tValid({ code: 'import { something } from "./dynamic-import-in-commonjs"', languageOptions: { parserOptions: { ecmaVersion: 2021 } }, }), - test({ + tValid({ code: 'import { "foo" as foo } from "./bar"', languageOptions: { parser: require(parsers.ESPREE), parserOptions: { ecmaVersion: 2022 }, }, }), - test({ + tValid({ code: 'import { "foo" as foo } from "./empty-module"', languageOptions: { parser: require(parsers.ESPREE), @@ -261,18 +259,18 @@ ruleTester.run('named', rule, { ], invalid: [ - test({ + tInvalid({ code: 'import { somethingElse } from "./test-module"', errors: [createNotFoundError('somethingElse', './test-module')], }), - test({ + tInvalid({ code: 'import { baz } from "./bar"', errors: [createNotFoundError('baz', './bar')], }), // test multiple - test({ + tInvalid({ code: 'import { baz, bop } from "./bar"', errors: [ createNotFoundError('baz', './bar'), @@ -280,49 +278,49 @@ ruleTester.run('named', rule, { ], }), - test({ + tInvalid({ code: 'import {a, b, c} from "./named-exports"', errors: [createNotFoundError('c', './named-exports')], }), - test({ + tInvalid({ code: 'import { a } from "./default-export"', errors: [createNotFoundError('a', './default-export')], }), - test({ + tInvalid({ code: 'import { ActionTypess } from "./qc"', errors: [createNotFoundError('ActionTypess', './qc')], }), - test({ + tInvalid({ code: 'import {a, b, c, d, e} from "./re-export"', errors: [createNotFoundError('e', './re-export')], }), - test({ + tInvalid({ code: 'import { a } from "./re-export-names"', errors: [createNotFoundError('a', './re-export-names')], }), // export tests - test({ + tInvalid({ code: 'export { bar } from "./bar"', errors: [createNotFoundError('bar', './bar')], }), // es7 - test({ + tInvalid({ code: 'export bar2, { bar } from "./bar"', languageOptions: { parser: require(parsers.BABEL) }, errors: [createNotFoundError('bar', './bar')], }), - test({ + tInvalid({ code: 'import { foo, bar, baz } from "./named-trampoline"', languageOptions: { parser: require(parsers.BABEL) }, errors: [createNotFoundError('baz', './named-trampoline')], }), - test({ + tInvalid({ code: 'import { baz } from "./broken-trampoline"', languageOptions: { parser: require(parsers.BABEL) }, errors: [ @@ -333,19 +331,19 @@ ruleTester.run('named', rule, { ], }), - test({ + tInvalid({ code: 'const { baz } = require("./bar")', errors: [createNotFoundError('baz', './bar')], options: [{ commonjs: true }], }), - test({ + tInvalid({ code: 'let { baz } = require("./bar")', errors: [createNotFoundError('baz', './bar')], options: [{ commonjs: true }], }), - test({ + tInvalid({ code: 'const { baz: bar, bop } = require("./bar"), { a } = require("./re-export-names")', errors: [ createNotFoundError('baz', './bar'), @@ -355,14 +353,14 @@ ruleTester.run('named', rule, { options: [{ commonjs: true }], }), - test({ + tInvalid({ code: 'const { default: defExport } = require("./named-exports")', errors: [createNotFoundError('default', './named-exports')], options: [{ commonjs: true }], }), // parse errors - // test({ + // tInvalid({ // code: "import { a } from './test.coffee';", // settings: { 'import-x/extensions': ['.js', '.coffee'] }, // errors: [{ @@ -371,43 +369,43 @@ ruleTester.run('named', rule, { // }], // }), - test({ + tInvalid({ code: 'import { type MyOpaqueType, MyMissingClass } from "./flowtypes"', languageOptions: { parser: require(parsers.BABEL) }, errors: [createNotFoundError('MyMissingClass', './flowtypes')], }), // jsnext - test({ + tInvalid({ code: '/*jsnext*/ import { createSnorlax } from "redux"', settings: { 'import-x/ignore': [] }, errors: [createNotFoundError('createSnorlax', 'redux')], }), // should work without ignore - test({ + tInvalid({ code: '/*jsnext*/ import { createSnorlax } from "redux"', errors: [createNotFoundError('createSnorlax', 'redux')], }), // ignore is ignored if exports are found - test({ + tInvalid({ code: 'import { baz } from "es6-module"', errors: [createNotFoundError('baz', 'es6-module')], }), // issue #251 - test({ + tInvalid({ code: 'import { foo, bar, bap } from "./re-export-default"', errors: [createNotFoundError('bap', './re-export-default')], }), // #328: * exports do not include default - test({ + tInvalid({ code: 'import { default as barDefault } from "./re-export"', errors: [createNotFoundError('default', './re-export')], }), - test({ + tInvalid({ code: 'import { "somethingElse" as somethingElse } from "./test-module"', errors: [ createNotFoundError('somethingElse', './test-module', 'Literal'), @@ -417,7 +415,7 @@ ruleTester.run('named', rule, { parserOptions: { ecmaVersion: 2022 }, }, }), - test({ + tInvalid({ code: 'import { "baz" as baz, "bop" as bop } from "./bar"', errors: [ createNotFoundError('baz', './bar', 'Literal'), @@ -428,7 +426,7 @@ ruleTester.run('named', rule, { parserOptions: { ecmaVersion: 2022 }, }, }), - test({ + tInvalid({ code: 'import { "default" as barDefault } from "./re-export"', errors: [createNotFoundError('default', './re-export', 'Literal')], languageOptions: { @@ -444,12 +442,12 @@ if (!CASE_SENSITIVE_FS) { describe('path case-insensitivity', () => { ruleTester.run('named', rule, { valid: [ - test({ + tValid({ code: 'import { b } from "./Named-Exports"', }), ], invalid: [ - test({ + tInvalid({ code: 'import { foo } from "./Named-Exports"', errors: [createNotFoundError('foo', './Named-Exports')], }), @@ -462,12 +460,12 @@ if (!CASE_SENSITIVE_FS) { describe('export *', () => { ruleTester.run('named', rule, { valid: [ - test({ + tValid({ code: 'import { foo } from "./export-all"', }), ], invalid: [ - test({ + tInvalid({ code: 'import { bar } from "./export-all"', errors: [createNotFoundError('bar', './export-all')], }), @@ -482,7 +480,7 @@ describe('TypeScript', () => { } let valid: RunTests['valid'] = [ - test({ + tValid({ code: `import x from './typescript-export-assign-object'`, languageOptions: { parserOptions: { @@ -498,7 +496,7 @@ describe('TypeScript', () => { let invalid: RunTests['invalid'] = [ // TODO: uncomment this test - // test({ + // tInvalid({ // code: `import {a} from './export-star-3/b';`, // filename: testFilePath('./export-star-3/a.js'), // parser, @@ -507,7 +505,7 @@ describe('TypeScript', () => { // { message: 'a not found in ./export-star-3/b' }, // ], // }), - test({ + tInvalid({ code: `import { NotExported } from './typescript-export-assign-object'`, languageOptions: { parserOptions: { @@ -522,7 +520,7 @@ describe('TypeScript', () => { createNotFoundError('NotExported', './typescript-export-assign-object'), ], }), - test({ + tInvalid({ // `export =` syntax creates a default export only code: `import { FooBar } from './typescript-export-assign-object'`, languageOptions: { @@ -548,32 +546,32 @@ describe('TypeScript', () => { ]) { valid = [ ...valid, - test({ + tValid({ code: `import { MyType } from "./${source}"`, settings, }), - test({ + tValid({ code: `import { Foo } from "./${source}"`, settings, }), - test({ + tValid({ code: `import { Bar } from "./${source}"`, settings, }), - test({ + tValid({ code: `import { getFoo } from "./${source}"`, settings, }), - test({ + tValid({ code: `import { MyEnum } from "./${source}"`, settings, }), - test({ + tValid({ code: ` import { MyModule } from "./${source}" MyModule.ModuleFunction() @@ -581,7 +579,7 @@ describe('TypeScript', () => { settings, }), - test({ + tValid({ code: ` import { MyNamespace } from "./${source}" MyNamespace.NSModule.NSModuleFunction() @@ -593,13 +591,13 @@ describe('TypeScript', () => { invalid = [ ...invalid, - test({ + tInvalid({ code: `import { MissingType } from "./${source}"`, settings, errors: [createNotFoundError('MissingType', `./${source}`)], }), - test({ + tInvalid({ code: `import { NotExported } from "./${source}"`, settings, diff --git a/test/rules/namespace.spec.ts b/test/rules/namespace.spec.ts index cdbc3a8a1..115646e97 100644 --- a/test/rules/namespace.spec.ts +++ b/test/rules/namespace.spec.ts @@ -3,7 +3,7 @@ import type { TestCaseError as TSESLintTestCaseError } from '@typescript-eslint/ import type { TSESTree } from '@typescript-eslint/utils' import { - createRuleTestCaseFunction, + createRuleTestCaseFunctions, SYNTAX_VALID_CASES, testFilePath, parsers, @@ -18,7 +18,7 @@ const ruleTester = new TSESLintRuleTester({ }, }) -const test = createRuleTestCaseFunction() +const { tValid, tInvalid } = createRuleTestCaseFunctions() function createNotFoundInNamespaceError( name: string, @@ -43,23 +43,23 @@ function createNotFoundInNamespaceDeepError( } let valid: RunTests['valid'] = [ - test({ + tValid({ code: 'import "./malformed.js"', languageOptions: { parser: require(parsers.ESPREE) }, }), - test({ code: "import * as foo from './empty-folder';" }), - test({ + tValid({ code: "import * as foo from './empty-folder';" }), + tValid({ code: 'import * as names from "./named-exports"; console.log((names.b).c); ', }), - test({ + tValid({ code: 'import * as names from "./named-exports"; console.log(names.a);', }), - test({ + tValid({ code: 'import * as names from "./re-export-names"; console.log(names.foo);', }), - test({ + tValid({ code: "import * as elements from './jsx';", languageOptions: { parserOptions: { @@ -70,7 +70,7 @@ let valid: RunTests['valid'] = [ }, }), // import re-exported jsx files, where jsx file exports a string - test({ + tValid({ code: ` import * as foo from "./jsx/re-export.js"; console.log(foo.jsxFoo); @@ -80,7 +80,7 @@ let valid: RunTests['valid'] = [ }, }), // import re-exported jsx files, where jsx files export functions that return html tags - test({ + tValid({ code: ` import * as foo from "./jsx/bar/index.js"; console.log(foo.Baz1); @@ -100,16 +100,16 @@ let valid: RunTests['valid'] = [ }, }), - test({ code: "import * as foo from './common';" }), + tValid({ code: "import * as foo from './common';" }), // destructuring namespaces - test({ + tValid({ code: 'import * as names from "./named-exports"; const { a } = names', }), - test({ + tValid({ code: 'import * as names from "./named-exports"; const { d: c } = names', }), - test({ + tValid({ code: ` import * as names from "./named-exports"; const { c } = foo, @@ -118,71 +118,71 @@ let valid: RunTests['valid'] = [ `, }), // deep destructuring only cares about top level - test({ + tValid({ code: 'import * as names from "./named-exports"; const { ExportedClass: { length } } = names', }), // detect scope redefinition - test({ + tValid({ code: 'import * as names from "./named-exports"; function b(names) { const { c } = names }', }), - test({ + tValid({ code: 'import * as names from "./named-exports"; function b() { let names = null; const { c } = names }', }), - test({ + tValid({ code: 'import * as names from "./named-exports"; const x = function names() { const { c } = names }', }), ///////// // es7 // ///////// - test({ + tValid({ code: 'export * as names from "./named-exports"', languageOptions: { parser: require(parsers.BABEL) }, }), - test({ + tValid({ code: 'export defport, * as names from "./named-exports"', languageOptions: { parser: require(parsers.BABEL) }, }), // non-existent is handled by no-unresolved - test({ + tValid({ code: 'export * as names from "./does-not-exist"', languageOptions: { parser: require(parsers.BABEL) }, }), - test({ + tValid({ code: 'import * as Endpoints from "./issue-195/Endpoints"; console.log(Endpoints.Users)', languageOptions: { parser: require(parsers.BABEL) }, }), // respect hoisting - test({ + tValid({ code: 'function x() { console.log((names.b).c); } import * as names from "./named-exports"; ', }), // names.default is valid export - test({ code: "import * as names from './default-export';" }), - test({ + tValid({ code: "import * as names from './default-export';" }), + tValid({ code: "import * as names from './default-export'; console.log(names.default)", languageOptions: { parser: require(parsers.ESPREE) }, }), - test({ + tValid({ code: 'export * as names from "./default-export"', languageOptions: { parser: require(parsers.BABEL) }, }), - test({ + tValid({ code: 'export defport, * as names from "./default-export"', languageOptions: { parser: require(parsers.BABEL) }, }), // #456: optionally ignore computed references - test({ + tValid({ code: `import * as names from './named-exports'; console.log(names['a']);`, options: [{ allowComputed: true }], }), // #656: should handle object-rest properties - test({ + tValid({ code: `import * as names from './named-exports'; const {a, b, ...rest} = names;`, languageOptions: { parserOptions: { @@ -190,18 +190,18 @@ let valid: RunTests['valid'] = [ }, }, }), - test({ + tValid({ code: `import * as names from './named-exports'; const {a, b, ...rest} = names;`, languageOptions: { parser: require(parsers.BABEL) }, }), // #1144: should handle re-export CommonJS as namespace - test({ + tValid({ code: `import * as ns from './re-export-common'; const {foo} = ns;`, }), // JSX - test({ + tValid({ code: 'import * as Names from "./named-exports"; const Foo = ', languageOptions: { parserOptions: { @@ -213,7 +213,7 @@ let valid: RunTests['valid'] = [ }), // Typescript - test({ + tValid({ code: ` import * as foo from "./typescript-declare-nested" foo.bar.MyFunction() @@ -225,7 +225,7 @@ let valid: RunTests['valid'] = [ }, }), - test({ + tValid({ code: `import { foobar } from "./typescript-declare-interface"`, settings: { @@ -234,7 +234,7 @@ let valid: RunTests['valid'] = [ }, }), - test({ + tValid({ code: 'export * from "typescript/lib/typescript.d"', settings: { @@ -243,7 +243,7 @@ let valid: RunTests['valid'] = [ }, }), - test({ + tValid({ code: 'export = function name() {}', settings: { @@ -254,7 +254,7 @@ let valid: RunTests['valid'] = [ ...(SYNTAX_VALID_CASES as RunTests['valid']), - test({ + tValid({ code: ` import * as color from './color'; export const getBackgroundFromColor = (color) => color.bg; @@ -262,7 +262,7 @@ let valid: RunTests['valid'] = [ `, }), - test({ + tValid({ code: ` import * as middle from './middle'; @@ -276,42 +276,42 @@ let valid: RunTests['valid'] = [ }, }), - test({ + tValid({ code: "import * as names from './default-export-string';", languageOptions: { parser: require(parsers.BABEL), parserOptions: { ecmaVersion: 2022 }, }, }), - test({ + tValid({ code: "import * as names from './default-export-string'; console.log(names.default)", languageOptions: { parser: require(parsers.BABEL), parserOptions: { ecmaVersion: 2022 }, }, }), - test({ + tValid({ code: "import * as names from './default-export-namespace-string';", languageOptions: { parser: require(parsers.ESPREE), parserOptions: { ecmaVersion: 2022 }, }, }), - test({ + tValid({ code: "import * as names from './default-export-namespace-string'; console.log(names.default)", languageOptions: { parser: require(parsers.ESPREE), parserOptions: { ecmaVersion: 2022 }, }, }), - test({ + tValid({ code: `import { "b" as b } from "./deep/a"; console.log(b.c.d.e)`, languageOptions: { parser: require(parsers.ESPREE), parserOptions: { ecmaVersion: 2022 }, }, }), - test({ + tValid({ code: `import { "b" as b } from "./deep/a"; var {c:{d:{e}}} = b`, languageOptions: { parser: require(parsers.ESPREE), @@ -321,22 +321,22 @@ let valid: RunTests['valid'] = [ ] let invalid: RunTests['invalid'] = [ - test({ + tInvalid({ code: "import * as names from './named-exports'; console.log(names.c)", errors: [createNotFoundInNamespaceError('c', 'names')], }), - test({ + tInvalid({ code: "import * as names from './named-exports'; console.log(names['a']);", errors: [{ messageId: 'computedReference', data: { namespace: 'names' } }], }), // assignment warning (from no-reassign) - test({ + tInvalid({ code: "import * as foo from './bar'; foo.foo = 'y';", errors: [{ messageId: 'namespaceMember', data: { namespace: 'foo' } }], }), - test({ + tInvalid({ code: "import * as foo from './bar'; foo.x = 'y';", errors: [ { messageId: 'namespaceMember', data: { namespace: 'foo' } }, @@ -345,19 +345,19 @@ let invalid: RunTests['invalid'] = [ }), // invalid destructuring - test({ + tInvalid({ code: 'import * as names from "./named-exports"; const { c } = names', errors: [createNotFoundInNamespaceError('c', 'names', 'Property')], }), - test({ + tInvalid({ code: 'import * as names from "./named-exports"; function b() { const { c } = names }', errors: [createNotFoundInNamespaceError('c', 'names', 'Property')], }), - test({ + tInvalid({ code: 'import * as names from "./named-exports"; const { c: d } = names', errors: [createNotFoundInNamespaceError('c', 'names', 'Property')], }), - test({ + tInvalid({ code: 'import * as names from "./named-exports"; const { c: { d } } = names', errors: [createNotFoundInNamespaceError('c', 'names', 'Property')], }), @@ -366,14 +366,14 @@ let invalid: RunTests['invalid'] = [ // es7 // ///////// - test({ + tInvalid({ code: 'import * as Endpoints from "./issue-195/Endpoints"; console.log(Endpoints.Foo)', languageOptions: { parser: require(parsers.BABEL) }, errors: [createNotFoundInNamespaceError('Foo', 'Endpoints')], }), // parse errors - test({ + tInvalid({ code: "import * as namespace from './malformed.js';", languageOptions: { parser: require(parsers.ESPREE), @@ -388,29 +388,29 @@ let invalid: RunTests['invalid'] = [ ], }), - test({ + tInvalid({ code: "import b from './deep/default'; console.log(b.e)", errors: [createNotFoundInNamespaceError('e', 'b')], }), // respect hoisting - test({ + tInvalid({ code: `console.log(names.c); import * as names from './named-exports';`, errors: [createNotFoundInNamespaceError('c', 'names')], }), - test({ + tInvalid({ code: `function x() { console.log(names.c) } import * as names from './named-exports';`, errors: [createNotFoundInNamespaceError('c', 'names')], }), // #328: * exports do not include default - test({ + tInvalid({ code: 'import * as ree from "./re-export"; console.log(ree.default)', errors: [createNotFoundInNamespaceError('default', 'ree')], }), // JSX - test({ + tInvalid({ code: 'import * as Names from "./named-exports"; const Foo = ', errors: [createNotFoundInNamespaceError('e', 'Names')], languageOptions: { @@ -422,7 +422,7 @@ let invalid: RunTests['invalid'] = [ }, }), - test({ + tInvalid({ code: `import { "b" as b } from "./deep/a"; console.log(b.e)`, errors: [createNotFoundInNamespaceError('e', 'b')], languageOptions: { @@ -430,7 +430,7 @@ let invalid: RunTests['invalid'] = [ parserOptions: { ecmaVersion: 2022 }, }, }), - test({ + tInvalid({ code: `import { "b" as b } from "./deep/a"; console.log(b.c.e)`, errors: [createNotFoundInNamespaceDeepError('e', 'b.c')], languageOptions: { @@ -447,28 +447,28 @@ for (const [folder, parser] of [['deep'], ['deep-es7', parsers.BABEL]]) { // close over params valid = [ ...valid, - test({ + tValid({ languageOptions: { ...(parser && { parser: require(parser) }) }, code: `import * as a from "./${folder}/a"; console.log(a.b.c.d.e)`, }), - test({ + tValid({ languageOptions: { ...(parser && { parser: require(parser) }) }, code: `import { b } from "./${folder}/a"; console.log(b.c.d.e)`, }), - test({ + tValid({ languageOptions: { ...(parser && { parser: require(parser) }) }, code: `import * as a from "./${folder}/a"; console.log(a.b.c.d.e.f)`, }), - test({ + tValid({ languageOptions: { ...(parser && { parser: require(parser) }) }, code: `import * as a from "./${folder}/a"; var {b:{c:{d:{e}}}} = a`, }), - test({ + tValid({ languageOptions: { ...(parser && { parser: require(parser) }) }, code: `import { b } from "./${folder}/a"; var {c:{d:{e}}} = b`, }), // deep namespaces should include explicitly exported defaults - test({ + tValid({ languageOptions: { ...(parser && { parser: require(parser) }) }, code: `import * as a from "./${folder}/a"; console.log(a.b.default)`, }), @@ -476,32 +476,32 @@ for (const [folder, parser] of [['deep'], ['deep-es7', parsers.BABEL]]) { invalid = [ ...invalid, - test({ + tInvalid({ languageOptions: { ...(parser && { parser: require(parser) }) }, code: `import * as a from "./${folder}/a"; console.log(a.b.e)`, errors: [createNotFoundInNamespaceDeepError('e', 'a.b')], }), - test({ + tInvalid({ languageOptions: { ...(parser && { parser: require(parser) }) }, code: `import { b } from "./${folder}/a"; console.log(b.e)`, errors: [createNotFoundInNamespaceError('e', 'b')], }), - test({ + tInvalid({ languageOptions: { ...(parser && { parser: require(parser) }) }, code: `import * as a from "./${folder}/a"; console.log(a.b.c.e)`, errors: [createNotFoundInNamespaceDeepError('e', 'a.b.c')], }), - test({ + tInvalid({ languageOptions: { ...(parser && { parser: require(parser) }) }, code: `import { b } from "./${folder}/a"; console.log(b.c.e)`, errors: [createNotFoundInNamespaceDeepError('e', 'b.c')], }), - test({ + tInvalid({ languageOptions: { ...(parser && { parser: require(parser) }) }, code: `import * as a from "./${folder}/a"; var {b:{ e }} = a`, errors: [createNotFoundInNamespaceDeepError('e', 'a.b')], }), - test({ + tInvalid({ languageOptions: { ...(parser && { parser: require(parser) }) }, code: `import * as a from "./${folder}/a"; var {b:{c:{ e }}} = a`, errors: [createNotFoundInNamespaceDeepError('e', 'a.b.c')], diff --git a/test/rules/no-absolute-path.spec.ts b/test/rules/no-absolute-path.spec.ts index 7c60568c6..9bb7449fe 100644 --- a/test/rules/no-absolute-path.spec.ts +++ b/test/rules/no-absolute-path.spec.ts @@ -3,14 +3,14 @@ import path from 'node:path' import { RuleTester as TSESLintRuleTester } from '@typescript-eslint/rule-tester' import type { TestCaseError as TSESLintTestCaseError } from '@typescript-eslint/rule-tester' -import { createRuleTestCaseFunction, parsers } from '../utils' +import { createRuleTestCaseFunctions, parsers } from '../utils' import type { GetRuleModuleMessageIds } from '../utils' import rule from 'eslint-plugin-import-x/rules/no-absolute-path' const ruleTester = new TSESLintRuleTester() -const test = createRuleTestCaseFunction() +const { tValid, tInvalid } = createRuleTestCaseFunctions() const ABSOLUTE_ERROR: TSESLintTestCaseError< GetRuleModuleMessageIds @@ -22,99 +22,99 @@ const absolutePath = (testPath: string) => path.join(__dirname, testPath) ruleTester.run('no-absolute-path', rule, { valid: [ - test({ code: 'import _ from "lodash"' }), - test({ code: 'import find from "lodash.find"' }), - test({ code: 'import foo from "./foo"' }), - test({ code: 'import foo from "../foo"' }), - test({ code: 'import foo from "foo"' }), - test({ code: 'import foo from "./"' }), - test({ code: 'import foo from "@scope/foo"' }), - test({ code: 'var _ = require("lodash")' }), - test({ code: 'var find = require("lodash.find")' }), - test({ code: 'var foo = require("./foo")' }), - test({ code: 'var foo = require("../foo")' }), - test({ code: 'var foo = require("foo")' }), - test({ code: 'var foo = require("./")' }), - test({ code: 'var foo = require("@scope/foo")' }), + tValid({ code: 'import _ from "lodash"' }), + tValid({ code: 'import find from "lodash.find"' }), + tValid({ code: 'import foo from "./foo"' }), + tValid({ code: 'import foo from "../foo"' }), + tValid({ code: 'import foo from "foo"' }), + tValid({ code: 'import foo from "./"' }), + tValid({ code: 'import foo from "@scope/foo"' }), + tValid({ code: 'var _ = require("lodash")' }), + tValid({ code: 'var find = require("lodash.find")' }), + tValid({ code: 'var foo = require("./foo")' }), + tValid({ code: 'var foo = require("../foo")' }), + tValid({ code: 'var foo = require("foo")' }), + tValid({ code: 'var foo = require("./")' }), + tValid({ code: 'var foo = require("@scope/foo")' }), - test({ code: 'import events from "events"' }), - test({ code: 'import path from "path"' }), - test({ code: 'var events = require("events")' }), - test({ code: 'var path = require("path")' }), - test({ code: 'import path from "path";import events from "events"' }), + tValid({ code: 'import events from "events"' }), + tValid({ code: 'import path from "path"' }), + tValid({ code: 'var events = require("events")' }), + tValid({ code: 'var path = require("path")' }), + tValid({ code: 'import path from "path";import events from "events"' }), // still works if only `amd: true` is provided - test({ + tValid({ code: 'import path from "path"', options: [{ amd: true }], }), // amd not enabled by default - test({ code: 'require(["/some/path"], function (f) { /* ... */ })' }), - test({ + tValid({ code: 'require(["/some/path"], function (f) { /* ... */ })' }), + tValid({ code: 'define(["/some/path"], function (f) { /* ... */ })', languageOptions: { parser: require(parsers.ESPREE) }, }), - test({ + tValid({ code: 'require(["./some/path"], function (f) { /* ... */ })', options: [{ amd: true }], }), - test({ + tValid({ code: 'define(["./some/path"], function (f) { /* ... */ })', options: [{ amd: true }], }), ], invalid: [ - test({ + tInvalid({ code: `import f from "${absolutePath('/foo')}"`, filename: absolutePath('/foo/bar/index.js'), errors: [ABSOLUTE_ERROR], output: 'import f from ".."', }), - test({ + tInvalid({ code: `import f from "${absolutePath('/foo/bar/baz.js')}"`, filename: absolutePath('/foo/bar/index.js'), errors: [ABSOLUTE_ERROR], output: 'import f from "./baz.js"', }), - test({ + tInvalid({ code: `import f from "${absolutePath('/foo/path')}"`, filename: absolutePath('/foo/bar/index.js'), errors: [ABSOLUTE_ERROR], output: 'import f from "../path"', }), - test({ + tInvalid({ code: `import f from "${absolutePath('/some/path')}"`, filename: absolutePath('/foo/bar/index.js'), errors: [ABSOLUTE_ERROR], output: 'import f from "../../some/path"', }), - test({ + tInvalid({ code: `import f from "${absolutePath('/some/path')}"`, filename: absolutePath('/foo/bar/index.js'), options: [{ amd: true }], errors: [ABSOLUTE_ERROR], output: 'import f from "../../some/path"', }), - test({ + tInvalid({ code: `var f = require("${absolutePath('/foo')}")`, filename: absolutePath('/foo/bar/index.js'), errors: [ABSOLUTE_ERROR], output: 'var f = require("..")', }), - test({ + tInvalid({ code: `var f = require("${absolutePath('/foo/path')}")`, filename: absolutePath('/foo/bar/index.js'), errors: [ABSOLUTE_ERROR], output: 'var f = require("../path")', }), - test({ + tInvalid({ code: `var f = require("${absolutePath('/some/path')}")`, filename: absolutePath('/foo/bar/index.js'), errors: [ABSOLUTE_ERROR], output: 'var f = require("../../some/path")', }), - test({ + tInvalid({ code: `var f = require("${absolutePath('/some/path')}")`, filename: absolutePath('/foo/bar/index.js'), options: [{ amd: true }], @@ -122,14 +122,14 @@ ruleTester.run('no-absolute-path', rule, { output: 'var f = require("../../some/path")', }), // validate amd - test({ + tInvalid({ code: `require(["${absolutePath('/some/path')}"], function (f) { /* ... */ })`, filename: absolutePath('/foo/bar/index.js'), options: [{ amd: true }], errors: [ABSOLUTE_ERROR], output: 'require(["../../some/path"], function (f) { /* ... */ })', }), - test({ + tInvalid({ code: `define(["${absolutePath('/some/path')}"], function (f) { /* ... */ })`, filename: absolutePath('/foo/bar/index.js'), languageOptions: { parser: require(parsers.ESPREE) }, diff --git a/test/rules/no-anonymous-default-export.spec.ts b/test/rules/no-anonymous-default-export.spec.ts index b5f7c9998..74b0e0f34 100644 --- a/test/rules/no-anonymous-default-export.spec.ts +++ b/test/rules/no-anonymous-default-export.spec.ts @@ -4,15 +4,15 @@ import type { TestCaseError as TSESLintTestCaseError } from '@typescript-eslint/ import { SYNTAX_VALID_CASES, parsers, - createRuleTestCaseFunction, + createRuleTestCaseFunctions, } from '../utils' -import type { GetRuleModuleMessageIds } from '../utils' +import type { GetRuleModuleMessageIds, RunTests } from '../utils' import rule from 'eslint-plugin-import-x/rules/no-anonymous-default-export' const ruleTester = new TSESLintRuleTester() -const test = createRuleTestCaseFunction() +const { tValid, tInvalid } = createRuleTestCaseFunctions() function createAssignError( type: string, @@ -26,49 +26,49 @@ function createAssignError( ruleTester.run('no-anonymous-default-export', rule, { valid: [ // Exports with identifiers are valid - test({ code: 'const foo = 123\nexport default foo' }), - test({ code: 'export default function foo() {}' }), - test({ code: 'export default class MyClass {}' }), + tValid({ code: 'const foo = 123\nexport default foo' }), + tValid({ code: 'export default function foo() {}' }), + tValid({ code: 'export default class MyClass {}' }), // Allow each forbidden type with appropriate option - test({ code: 'export default []', options: [{ allowArray: true }] }), - test({ + tValid({ code: 'export default []', options: [{ allowArray: true }] }), + tValid({ code: 'export default () => {}', options: [{ allowArrowFunction: true }], }), - test({ + tValid({ code: 'export default class {}', options: [{ allowAnonymousClass: true }], }), - test({ + tValid({ code: 'export default function() {}', options: [{ allowAnonymousFunction: true }], }), - test({ code: 'export default 123', options: [{ allowLiteral: true }] }), - test({ code: "export default 'foo'", options: [{ allowLiteral: true }] }), - test({ code: 'export default `foo`', options: [{ allowLiteral: true }] }), - test({ code: 'export default {}', options: [{ allowObject: true }] }), - test({ + tValid({ code: 'export default 123', options: [{ allowLiteral: true }] }), + tValid({ code: "export default 'foo'", options: [{ allowLiteral: true }] }), + tValid({ code: 'export default `foo`', options: [{ allowLiteral: true }] }), + tValid({ code: 'export default {}', options: [{ allowObject: true }] }), + tValid({ code: 'export default foo(bar)', options: [{ allowCallExpression: true }], }), - test({ code: 'export default new Foo()', options: [{ allowNew: true }] }), + tValid({ code: 'export default new Foo()', options: [{ allowNew: true }] }), // Allow forbidden types with multiple options - test({ + tValid({ code: 'export default 123', options: [{ allowLiteral: true, allowObject: true }], }), - test({ + tValid({ code: 'export default {}', options: [{ allowLiteral: true, allowObject: true }], }), // Sanity check unrelated export syntaxes - test({ code: "export * from 'foo'" }), - test({ code: 'const foo = 123\nexport { foo }' }), - test({ code: 'const foo = 123\nexport { foo as default }' }), - test({ + tValid({ code: "export * from 'foo'" }), + tValid({ code: 'const foo = 123\nexport { foo }' }), + tValid({ code: 'const foo = 123\nexport { foo as default }' }), + tValid({ code: 'const foo = 123\nexport { foo as "default" }', languageOptions: { parser: require(parsers.ESPREE), @@ -77,56 +77,56 @@ ruleTester.run('no-anonymous-default-export', rule, { }), // Allow call expressions by default for backwards compatibility - test({ code: 'export default foo(bar)' }), + tValid({ code: 'export default foo(bar)' }), - ...SYNTAX_VALID_CASES, + ...(SYNTAX_VALID_CASES as RunTests['valid']), ], invalid: [ - test({ + tInvalid({ code: 'export default []', errors: [createAssignError('array')], }), - test({ + tInvalid({ code: 'export default () => {}', errors: [createAssignError('arrow function')], }), - test({ + tInvalid({ code: 'export default class {}', errors: [{ messageId: 'anonymous', data: { type: 'class' } }], }), - test({ + tInvalid({ code: 'export default function() {}', errors: [{ messageId: 'anonymous', data: { type: 'function' } }], }), - test({ + tInvalid({ code: 'export default 123', errors: [createAssignError('literal')], }), - test({ + tInvalid({ code: "export default 'foo'", errors: [createAssignError('literal')], }), - test({ + tInvalid({ code: 'export default `foo`', errors: [createAssignError('literal')], }), - test({ + tInvalid({ code: 'export default {}', errors: [createAssignError('object')], }), - test({ + tInvalid({ code: 'export default foo(bar)', options: [{ allowCallExpression: false }], errors: [createAssignError('call result')], }), - test({ + tInvalid({ code: 'export default new Foo()', errors: [createAssignError('instance')], }), // Test failure with non-covering exception - test({ + tInvalid({ code: 'export default 123', options: [{ allowObject: true }], errors: [createAssignError('literal')], diff --git a/test/rules/no-cycle.spec.ts b/test/rules/no-cycle.spec.ts index 09861bd3f..8dcb263ae 100644 --- a/test/rules/no-cycle.spec.ts +++ b/test/rules/no-cycle.spec.ts @@ -1,20 +1,27 @@ import { RuleTester as TSESLintRuleTester } from '@typescript-eslint/rule-tester' import type { TestCaseError as TSESLintTestCaseError } from '@typescript-eslint/rule-tester' -import { createRuleTestCaseFunction, parsers, testFilePath } from '../utils' +import { createRuleTestCaseFunctions, parsers, testFilePath } from '../utils' import type { GetRuleModuleMessageIds } from '../utils' import rule from 'eslint-plugin-import-x/rules/no-cycle' const ruleTester = new TSESLintRuleTester() -const _test = createRuleTestCaseFunction() +const { tValid: _tValid, tInvalid: _tInvalid } = + createRuleTestCaseFunctions() -const test = (testCase => - _test({ +const tValid = (testCase => + _tValid({ filename: testFilePath('./cycles/depth-zero.js'), ...testCase, - })) as typeof _test + })) as typeof _tValid + +const tInvalid = (testCase => + _tInvalid({ + filename: testFilePath('./cycles/depth-zero.js'), + ...testCase, + })) as typeof _tInvalid const createCycleSourceError = ( source: string, @@ -26,20 +33,20 @@ const createCycleSourceError = ( ruleTester.run('no-cycle', rule, { valid: [ // this rule doesn't care if the cycle length is 0 - test({ code: 'import foo from "./foo.js"' }), + tValid({ code: 'import foo from "./foo.js"' }), - test({ code: 'import _ from "lodash"' }), - test({ code: 'import foo from "@scope/foo"' }), - test({ code: 'var _ = require("lodash")' }), - test({ code: 'var find = require("lodash.find")' }), - test({ code: 'var foo = require("./foo")' }), - test({ code: 'var foo = require("../foo")' }), - test({ code: 'var foo = require("foo")' }), - test({ code: 'var foo = require("./")' }), - test({ code: 'var foo = require("@scope/foo")' }), - test({ code: 'var bar = require("./bar/index")' }), - test({ code: 'var bar = require("./bar")' }), - test({ + tValid({ code: 'import _ from "lodash"' }), + tValid({ code: 'import foo from "@scope/foo"' }), + tValid({ code: 'var _ = require("lodash")' }), + tValid({ code: 'var find = require("lodash.find")' }), + tValid({ code: 'var foo = require("./foo")' }), + tValid({ code: 'var foo = require("../foo")' }), + tValid({ code: 'var foo = require("foo")' }), + tValid({ code: 'var foo = require("./")' }), + tValid({ code: 'var foo = require("@scope/foo")' }), + tValid({ code: 'var bar = require("./bar/index")' }), + tValid({ code: 'var bar = require("./bar")' }), + tValid({ code: 'import { foo } from "cycles/external/depth-one"', options: [{ ignoreExternal: true }], settings: { @@ -47,7 +54,7 @@ ruleTester.run('no-cycle', rule, { 'import-x/external-module-folders': ['cycles/external'], }, }), - test({ + tValid({ code: 'import { foo } from "./external-depth-two"', options: [{ ignoreExternal: true }], settings: { @@ -56,71 +63,71 @@ ruleTester.run('no-cycle', rule, { }, }), - test({ + tValid({ code: `import { foo } from "./es6/depth-two"`, options: [{ maxDepth: 1 }], }), - test({ + tValid({ code: `import { foo, bar } from "./es6/depth-two"`, options: [{ maxDepth: 1 }], }), - test({ + tValid({ code: `import("./es6/depth-two").then(function({ foo }) {})`, options: [{ maxDepth: 1 }], languageOptions: { parser: require(parsers.BABEL) }, }), - test({ + tValid({ code: `import type { FooType } from "./es6/depth-one"`, languageOptions: { parser: require(parsers.BABEL) }, }), - test({ + tValid({ code: `import type { FooType, BarType } from "./es6/depth-one"`, languageOptions: { parser: require(parsers.BABEL) }, }), - test({ + tValid({ code: `function bar(){ return import("./es6/depth-one"); } // #2265 1`, options: [{ allowUnsafeDynamicCyclicDependency: true }], languageOptions: { parser: require(parsers.BABEL) }, }), - test({ + tValid({ code: `import { foo } from "./es6/depth-one-dynamic"; // #2265 2`, options: [{ allowUnsafeDynamicCyclicDependency: true }], languageOptions: { parser: require(parsers.BABEL) }, }), - test({ + tValid({ code: `function bar(){ return import("./es6/depth-one"); } // #2265 3`, options: [{ allowUnsafeDynamicCyclicDependency: true }], }), - test({ + tValid({ code: `import { foo } from "./es6/depth-one-dynamic"; // #2265 4`, options: [{ allowUnsafeDynamicCyclicDependency: true }], }), - test({ + tValid({ code: 'import { bar } from "./flow-types"', languageOptions: { parser: require(parsers.BABEL) }, }), - test({ + tValid({ code: 'import { bar } from "./flow-types-only-importing-type"', languageOptions: { parser: require(parsers.BABEL) }, }), - test({ + tValid({ code: 'import { bar } from "./flow-types-only-importing-multiple-types"', languageOptions: { parser: require(parsers.BABEL) }, }), - test({ + tValid({ code: 'import { bar } from "./flow-typeof"', languageOptions: { parser: require(parsers.BABEL) }, }), ], invalid: [ - test({ + tInvalid({ code: 'import { bar } from "./flow-types-some-type-imports"', languageOptions: { parser: require(parsers.BABEL) }, errors: [{ messageId: 'cycle' }], }), - test({ + tInvalid({ code: 'import { foo } from "cycles/external/depth-one"', errors: [{ messageId: 'cycle' }], settings: { @@ -128,7 +135,7 @@ ruleTester.run('no-cycle', rule, { 'import-x/external-module-folders': ['cycles/external'], }, }), - test({ + tInvalid({ code: 'import { foo } from "./external-depth-two"', errors: [createCycleSourceError('cycles/external/depth-one:1')], settings: { @@ -139,148 +146,138 @@ ruleTester.run('no-cycle', rule, { // Ensure behavior does not change for those tests, with or without ...[{}, { allowUnsafeDynamicCyclicDependency: true }].flatMap(opts => [ - test({ + tInvalid({ code: `import { foo } from "./es6/depth-one"`, options: [{ ...opts }], errors: [{ messageId: 'cycle' }], }), - test({ + tInvalid({ code: `import { foo } from "./es6/depth-one"`, options: [{ ...opts, maxDepth: 1 }], errors: [{ messageId: 'cycle' }], }), - test({ + tInvalid({ code: `const { foo } = require("./es6/depth-one")`, options: [{ ...opts, commonjs: true }], errors: [{ messageId: 'cycle' }], }), - test({ + tInvalid({ code: `require(["./es6/depth-one"], d1 => {})`, options: [{ ...opts, amd: true }], errors: [{ messageId: 'cycle' }], }), - test({ + tInvalid({ code: `define(["./es6/depth-one"], d1 => {})`, options: [{ ...opts, amd: true }], errors: [{ messageId: 'cycle' }], }), - test({ + tInvalid({ code: `import { foo } from "./es6/depth-one-reexport"`, options: [{ ...opts }], errors: [{ messageId: 'cycle' }], }), - test({ + tInvalid({ code: `import { foo } from "./es6/depth-two"`, options: [{ ...opts }], errors: [createCycleSourceError('./depth-one:1')], }), - test({ + tInvalid({ code: `import { foo } from "./es6/depth-two"`, options: [{ ...opts, maxDepth: 2 }], errors: [createCycleSourceError('./depth-one:1')], }), - test({ + tInvalid({ code: `const { foo } = require("./es6/depth-two")`, errors: [createCycleSourceError('./depth-one:1')], options: [{ ...opts, commonjs: true }], }), - test({ + tInvalid({ code: `import { two } from "./es6/depth-three-star"`, options: [{ ...opts }], errors: [createCycleSourceError('./depth-two:1=>./depth-one:1')], }), - test({ + tInvalid({ code: `import one, { two, three } from "./es6/depth-three-star"`, options: [{ ...opts }], errors: [createCycleSourceError('./depth-two:1=>./depth-one:1')], }), - test({ + tInvalid({ code: `import { bar } from "./es6/depth-three-indirect"`, options: [{ ...opts }], errors: [createCycleSourceError('./depth-two:1=>./depth-one:1')], }), - test({ + tInvalid({ code: `import { bar } from "./es6/depth-three-indirect"`, options: [{ ...opts }], errors: [createCycleSourceError('./depth-two:1=>./depth-one:1')], languageOptions: { parser: require(parsers.BABEL) }, }), - test({ + tInvalid({ code: `import { foo } from "./es6/depth-two"`, options: [{ ...opts, maxDepth: Number.POSITIVE_INFINITY }], errors: [createCycleSourceError('./depth-one:1')], }), - test({ + tInvalid({ code: `import { foo } from "./es6/depth-two"`, options: [{ ...opts, maxDepth: '∞' }], errors: [createCycleSourceError('./depth-one:1')], }), ]), - test({ + tInvalid({ code: `import("./es6/depth-three-star")`, errors: [createCycleSourceError('./depth-two:1=>./depth-one:1')], languageOptions: { parser: require(parsers.BABEL) }, }), - test({ + tInvalid({ code: `import("./es6/depth-three-indirect")`, errors: [createCycleSourceError('./depth-two:1=>./depth-one:1')], languageOptions: { parser: require(parsers.BABEL) }, }), - test({ + tInvalid({ code: `import("./es6/depth-two")`, options: [{ maxDepth: Number.POSITIVE_INFINITY }], errors: [createCycleSourceError('./depth-one:1')], }), - test({ + tInvalid({ code: `import("./es6/depth-two")`, options: [{ maxDepth: '∞' }], errors: [createCycleSourceError('./depth-one:1')], }), - test({ + tInvalid({ code: `function bar(){ return import("./es6/depth-one"); } // #2265 5`, errors: [{ messageId: 'cycle' }], languageOptions: { parser: require(parsers.BABEL) }, }), - test({ + tInvalid({ // Dynamic import is not properly caracterized with eslint < 4 code: `import { foo } from "./es6/depth-one-dynamic"; // #2265 6`, errors: [{ messageId: 'cycle' }], languageOptions: { parser: require(parsers.BABEL) }, }), - test({ + tInvalid({ code: `function bar(){ return import("./es6/depth-one"); } // #2265 7`, errors: [{ messageId: 'cycle' }], }), - test({ + tInvalid({ code: `import { foo } from "./es6/depth-one-dynamic"; // #2265 8`, errors: [{ messageId: 'cycle' }], }), - test({ + tInvalid({ code: 'import { bar } from "./flow-types-depth-one"', languageOptions: { parser: require(parsers.BABEL) }, errors: [ createCycleSourceError('./flow-types-depth-two:4=>./es6/depth-one:1'), ], }), - test({ + tInvalid({ code: 'import { foo } from "./intermediate-ignore"', - errors: [ - { - ...createCycleSourceError('./ignore:1'), - line: 1, - }, - ], + errors: [{ ...createCycleSourceError('./ignore:1'), line: 1 }], }), - test({ + tInvalid({ code: 'import { foo } from "./ignore"', - errors: [ - { - messageId: 'cycle', - line: 1, - }, - ], + errors: [{ messageId: 'cycle', line: 1 }], }), ], }) diff --git a/test/rules/no-useless-path-segments.spec.ts b/test/rules/no-useless-path-segments.spec.ts index 66515776a..9a8b44c6e 100644 --- a/test/rules/no-useless-path-segments.spec.ts +++ b/test/rules/no-useless-path-segments.spec.ts @@ -1,65 +1,68 @@ import { RuleTester as TSESLintRuleTester } from '@typescript-eslint/rule-tester' -import { parsers, createRuleTestCaseFunction } from '../utils' +import { parsers, createRuleTestCaseFunctions } from '../utils' import rule from 'eslint-plugin-import-x/rules/no-useless-path-segments' -const test = createRuleTestCaseFunction() - const ruleTester = new TSESLintRuleTester() +const { tValid, tInvalid } = createRuleTestCaseFunctions() + function runResolverTests(resolver: 'node' | 'webpack') { ruleTester.run(`no-useless-path-segments (${resolver})`, rule, { valid: [ // CommonJS modules with default options - test({ + tValid({ code: 'require("./../fixtures/malformed.js")', languageOptions: { parser: require(parsers.ESPREE) }, }), // ES modules with default options - test({ + tValid({ code: 'import "./malformed.js"', languageOptions: { parser: require(parsers.ESPREE) }, }), - test({ code: 'import "./test-module"' }), - test({ code: 'import "./bar/"' }), - test({ code: 'import "."' }), - test({ code: 'import ".."' }), - test({ code: 'import fs from "fs"' }), + tValid({ code: 'import "./test-module"' }), + tValid({ code: 'import "./bar/"' }), + tValid({ code: 'import "."' }), + tValid({ code: 'import ".."' }), + tValid({ code: 'import fs from "fs"' }), // ES modules + noUselessIndex - test({ code: 'import "../index"' }), // noUselessIndex is false by default - test({ + tValid({ code: 'import "../index"' }), // noUselessIndex is false by default + tValid({ code: 'import "../my-custom-index"', options: [{ noUselessIndex: true }], }), - test({ code: 'import "./bar.js"', options: [{ noUselessIndex: true }] }), // ./bar/index.js exists - test({ code: 'import "./bar"', options: [{ noUselessIndex: true }] }), - test({ code: 'import "./bar/"', options: [{ noUselessIndex: true }] }), // ./bar.js exists - test({ + tValid({ + code: 'import "./bar.js"', + options: [{ noUselessIndex: true }], + }), // ./bar/index.js exists + tValid({ code: 'import "./bar"', options: [{ noUselessIndex: true }] }), + tValid({ code: 'import "./bar/"', options: [{ noUselessIndex: true }] }), // ./bar.js exists + tValid({ code: 'import "./malformed.js"', languageOptions: { parser: require(parsers.BABEL) }, options: [{ noUselessIndex: true }], }), // ./malformed directory does not exist - test({ + tValid({ code: 'import "./malformed"', options: [{ noUselessIndex: true }], }), // ./malformed directory does not exist - test({ + tValid({ code: 'import "./importType"', options: [{ noUselessIndex: true }], }), // ./importType.js does not exist - test({ + tValid({ code: 'import(".")', languageOptions: { parser: require(parsers.BABEL) }, }), - test({ + tValid({ code: 'import("..")', languageOptions: { parser: require(parsers.BABEL) }, }), - test({ + tValid({ code: 'import("fs").then(function(fs) {})', languageOptions: { parser: require(parsers.BABEL) }, }), @@ -67,7 +70,7 @@ function runResolverTests(resolver: 'node' | 'webpack') { invalid: [ // CommonJS modules - test({ + tInvalid({ code: 'require("./../fixtures/malformed.js")', output: [ 'require("../fixtures/malformed.js")', @@ -85,7 +88,7 @@ function runResolverTests(resolver: 'node' | 'webpack') { }, ], }), - test({ + tInvalid({ code: 'require("./../fixtures/malformed")', output: ['require("../fixtures/malformed")', 'require("./malformed")'], options: [{ commonjs: true }], @@ -100,7 +103,7 @@ function runResolverTests(resolver: 'node' | 'webpack') { }, ], }), - test({ + tInvalid({ code: 'require("../fixtures/malformed.js")', output: 'require("./malformed.js")', options: [{ commonjs: true }], @@ -115,7 +118,7 @@ function runResolverTests(resolver: 'node' | 'webpack') { }, ], }), - test({ + tInvalid({ code: 'require("../fixtures/malformed")', output: 'require("./malformed")', options: [{ commonjs: true }], @@ -130,7 +133,7 @@ function runResolverTests(resolver: 'node' | 'webpack') { }, ], }), - test({ + tInvalid({ code: 'require("./test-module/")', output: 'require("./test-module")', options: [{ commonjs: true }], @@ -145,7 +148,7 @@ function runResolverTests(resolver: 'node' | 'webpack') { }, ], }), - test({ + tInvalid({ code: 'require("./")', output: 'require(".")', options: [{ commonjs: true }], @@ -156,7 +159,7 @@ function runResolverTests(resolver: 'node' | 'webpack') { }, ], }), - test({ + tInvalid({ code: 'require("../")', output: 'require("..")', options: [{ commonjs: true }], @@ -167,7 +170,7 @@ function runResolverTests(resolver: 'node' | 'webpack') { }, ], }), - test({ + tInvalid({ code: 'require("./deep//a")', output: 'require("./deep/a")', options: [{ commonjs: true }], @@ -180,7 +183,7 @@ function runResolverTests(resolver: 'node' | 'webpack') { }), // CommonJS modules + noUselessIndex - test({ + tInvalid({ code: 'require("./bar/index.js")', output: 'require("./bar/")', options: [{ commonjs: true, noUselessIndex: true }], @@ -191,7 +194,7 @@ function runResolverTests(resolver: 'node' | 'webpack') { }, ], // ./bar.js exists }), - test({ + tInvalid({ code: 'require("./bar/index")', output: 'require("./bar/")', options: [{ commonjs: true, noUselessIndex: true }], @@ -202,7 +205,7 @@ function runResolverTests(resolver: 'node' | 'webpack') { }, ], // ./bar.js exists }), - test({ + tInvalid({ code: 'require("./importPath/")', output: 'require("./importPath")', options: [{ commonjs: true, noUselessIndex: true }], @@ -213,7 +216,7 @@ function runResolverTests(resolver: 'node' | 'webpack') { }, ], // ./importPath.js does not exist }), - test({ + tInvalid({ code: 'require("./importPath/index.js")', output: 'require("./importPath")', options: [{ commonjs: true, noUselessIndex: true }], @@ -227,7 +230,7 @@ function runResolverTests(resolver: 'node' | 'webpack') { }, ], // ./importPath.js does not exist }), - test({ + tInvalid({ code: 'require("./importType/index")', output: 'require("./importType")', options: [{ commonjs: true, noUselessIndex: true }], @@ -241,7 +244,7 @@ function runResolverTests(resolver: 'node' | 'webpack') { }, ], // ./importPath.js does not exist }), - test({ + tInvalid({ code: 'require("./index")', output: 'require(".")', options: [{ commonjs: true, noUselessIndex: true }], @@ -252,7 +255,7 @@ function runResolverTests(resolver: 'node' | 'webpack') { }, ], }), - test({ + tInvalid({ code: 'require("../index")', output: 'require("..")', options: [{ commonjs: true, noUselessIndex: true }], @@ -263,7 +266,7 @@ function runResolverTests(resolver: 'node' | 'webpack') { }, ], }), - test({ + tInvalid({ code: 'require("../index.js")', output: 'require("..")', options: [{ commonjs: true, noUselessIndex: true }], @@ -276,7 +279,7 @@ function runResolverTests(resolver: 'node' | 'webpack') { }), // ES modules - test({ + tInvalid({ code: 'import "./../fixtures/malformed.js"', output: [ 'import "../fixtures/malformed.js"', @@ -293,7 +296,7 @@ function runResolverTests(resolver: 'node' | 'webpack') { }, ], }), - test({ + tInvalid({ code: 'import "./../fixtures/malformed"', output: ['import "../fixtures/malformed"', 'import "./malformed"'], languageOptions: { parser: require(parsers.ESPREE) }, @@ -307,7 +310,7 @@ function runResolverTests(resolver: 'node' | 'webpack') { }, ], }), - test({ + tInvalid({ code: 'import "../fixtures/malformed.js"', output: 'import "./malformed.js"', languageOptions: { parser: require(parsers.BABEL) }, @@ -321,7 +324,7 @@ function runResolverTests(resolver: 'node' | 'webpack') { }, ], }), - test({ + tInvalid({ code: 'import "../fixtures/malformed"', output: 'import "./malformed"', languageOptions: { parser: require(parsers.ESPREE) }, @@ -335,7 +338,7 @@ function runResolverTests(resolver: 'node' | 'webpack') { }, ], }), - test({ + tInvalid({ code: 'import "./test-module/"', output: 'import "./test-module"', errors: [ @@ -348,7 +351,7 @@ function runResolverTests(resolver: 'node' | 'webpack') { }, ], }), - test({ + tInvalid({ code: 'import "./"', output: 'import "."', errors: [ @@ -358,7 +361,7 @@ function runResolverTests(resolver: 'node' | 'webpack') { }, ], }), - test({ + tInvalid({ code: 'import "../"', output: 'import ".."', errors: [ @@ -368,7 +371,7 @@ function runResolverTests(resolver: 'node' | 'webpack') { }, ], }), - test({ + tInvalid({ code: 'import "./deep//a"', output: 'import "./deep/a"', errors: [ @@ -380,7 +383,7 @@ function runResolverTests(resolver: 'node' | 'webpack') { }), // ES modules + noUselessIndex - test({ + tInvalid({ code: 'import "./bar/index.js"', output: 'import "./bar/"', options: [{ noUselessIndex: true }], @@ -391,7 +394,7 @@ function runResolverTests(resolver: 'node' | 'webpack') { }, ], // ./bar.js exists }), - test({ + tInvalid({ code: 'import "./bar/index"', output: 'import "./bar/"', options: [{ noUselessIndex: true }], @@ -402,7 +405,7 @@ function runResolverTests(resolver: 'node' | 'webpack') { }, ], // ./bar.js exists }), - test({ + tInvalid({ code: 'import "./importPath/"', output: 'import "./importPath"', options: [{ noUselessIndex: true }], @@ -413,7 +416,7 @@ function runResolverTests(resolver: 'node' | 'webpack') { }, ], // ./importPath.js does not exist }), - test({ + tInvalid({ code: 'import "./importPath/index.js"', output: 'import "./importPath"', options: [{ noUselessIndex: true }], @@ -427,7 +430,7 @@ function runResolverTests(resolver: 'node' | 'webpack') { }, ], // ./importPath.js does not exist }), - test({ + tInvalid({ code: 'import "./importPath/index"', output: 'import "./importPath"', options: [{ noUselessIndex: true }], @@ -441,7 +444,7 @@ function runResolverTests(resolver: 'node' | 'webpack') { }, ], // ./importPath.js does not exist }), - test({ + tInvalid({ code: 'import "./index"', output: 'import "."', options: [{ noUselessIndex: true }], @@ -452,7 +455,7 @@ function runResolverTests(resolver: 'node' | 'webpack') { }, ], }), - test({ + tInvalid({ code: 'import "../index"', output: 'import ".."', options: [{ noUselessIndex: true }], @@ -463,7 +466,7 @@ function runResolverTests(resolver: 'node' | 'webpack') { }, ], }), - test({ + tInvalid({ code: 'import "../index.js"', output: 'import ".."', options: [{ noUselessIndex: true }], @@ -474,7 +477,7 @@ function runResolverTests(resolver: 'node' | 'webpack') { }, ], }), - test({ + tInvalid({ code: 'import("./")', output: 'import(".")', languageOptions: { parser: require(parsers.BABEL) }, @@ -485,7 +488,7 @@ function runResolverTests(resolver: 'node' | 'webpack') { }, ], }), - test({ + tInvalid({ code: 'import("../")', output: 'import("..")', languageOptions: { parser: require(parsers.BABEL) }, @@ -496,7 +499,7 @@ function runResolverTests(resolver: 'node' | 'webpack') { }, ], }), - test({ + tInvalid({ code: 'import("./deep//a")', output: 'import("./deep/a")', languageOptions: { parser: require(parsers.BABEL) }, diff --git a/test/rules/no-webpack-loader-syntax.spec.ts b/test/rules/no-webpack-loader-syntax.spec.ts index 8eec072ed..47fa9050c 100644 --- a/test/rules/no-webpack-loader-syntax.spec.ts +++ b/test/rules/no-webpack-loader-syntax.spec.ts @@ -1,33 +1,33 @@ import { RuleTester as TSESLintRuleTester } from '@typescript-eslint/rule-tester' -import { createRuleTestCaseFunction } from '../utils' +import { createRuleTestCaseFunctions } from '../utils' import rule from 'eslint-plugin-import-x/rules/no-webpack-loader-syntax' -const test = createRuleTestCaseFunction() +const { tValid, tInvalid } = createRuleTestCaseFunctions() const ruleTester = new TSESLintRuleTester() ruleTester.run('no-webpack-loader-syntax', rule, { valid: [ - test({ code: 'import _ from "lodash"' }), - test({ code: 'import find from "lodash.find"' }), - test({ code: 'import foo from "./foo.css"' }), - test({ code: 'import data from "@scope/my-package/data.json"' }), - test({ code: 'var _ = require("lodash")' }), - test({ code: 'var find = require("lodash.find")' }), - test({ code: 'var foo = require("./foo")' }), - test({ code: 'var foo = require("../foo")' }), - test({ code: 'var foo = require("foo")' }), - test({ code: 'var foo = require("./")' }), - test({ code: 'var foo = require("@scope/foo")' }), + tValid({ code: 'import _ from "lodash"' }), + tValid({ code: 'import find from "lodash.find"' }), + tValid({ code: 'import foo from "./foo.css"' }), + tValid({ code: 'import data from "@scope/my-package/data.json"' }), + tValid({ code: 'var _ = require("lodash")' }), + tValid({ code: 'var find = require("lodash.find")' }), + tValid({ code: 'var foo = require("./foo")' }), + tValid({ code: 'var foo = require("../foo")' }), + tValid({ code: 'var foo = require("foo")' }), + tValid({ code: 'var foo = require("./")' }), + tValid({ code: 'var foo = require("@scope/foo")' }), ], invalid: [ - test({ + tInvalid({ code: 'import _ from "babel!lodash"', errors: [{ messageId: 'unexpected', data: { name: 'babel!lodash' } }], }), - test({ + tInvalid({ code: 'import find from "-babel-loader!lodash.find"', errors: [ { @@ -36,13 +36,13 @@ ruleTester.run('no-webpack-loader-syntax', rule, { }, ], }), - test({ + tInvalid({ code: 'import foo from "style!css!./foo.css"', errors: [ { messageId: 'unexpected', data: { name: 'style!css!./foo.css' } }, ], }), - test({ + tInvalid({ code: 'import data from "json!@scope/my-package/data.json"', errors: [ { @@ -51,11 +51,11 @@ ruleTester.run('no-webpack-loader-syntax', rule, { }, ], }), - test({ + tInvalid({ code: 'var _ = require("babel!lodash")', errors: [{ messageId: 'unexpected', data: { name: 'babel!lodash' } }], }), - test({ + tInvalid({ code: 'var find = require("-babel-loader!lodash.find")', errors: [ { @@ -64,13 +64,13 @@ ruleTester.run('no-webpack-loader-syntax', rule, { }, ], }), - test({ + tInvalid({ code: 'var foo = require("style!css!./foo.css")', errors: [ { messageId: 'unexpected', data: { name: 'style!css!./foo.css' } }, ], }), - test({ + tInvalid({ code: 'var data = require("json!@scope/my-package/data.json")', errors: [ { diff --git a/test/utils.ts b/test/utils.ts index 08e078762..a501605e6 100644 --- a/test/utils.ts +++ b/test/utils.ts @@ -81,7 +81,7 @@ export type InvalidTestCaseError = type?: `${TSESTree.AST_NODE_TYPES}` }) -/** @deprecated use {@link createRuleTestCaseFunction} */ +/** @deprecated use {@link createRuleTestCaseFunctions} */ // eslint-disable-next-line eslint-plugin/require-meta-docs-description, eslint-plugin/require-meta-type, eslint-plugin/prefer-message-ids, eslint-plugin/prefer-object-rule, eslint-plugin/require-meta-schema export function test( t: T, @@ -134,22 +134,27 @@ export type RunTests< > = TSESLintRunTests /** - * Create a function that can be used to create both valid and invalid test case + * Create two functions that can be used to create both valid and invalid test case * to be provided to {@link TSESLintRuleTester}. * This function accepts one type parameter that should extend a {@link RuleModule} - * to be able to provide a function with typed `MessageIds` and `Options` properties + * to be able to provide the result with typed `MessageIds` and `Options` properties * * @example * ```ts * import { createRuleTestCaseFunction } from '../utils' * - * const test = createRuleTestCaseFunction() - * * const ruleTester = new TSESLintRuleTester() * + * const { tValid, tInvalid } = createRuleTestCaseFunction() + * * ruleTester.run(`no-useless-path-segments (${resolver})`, rule, { * valid: [ - * test({ + * tValid({ + * code: '...', + * }), + * ], + * invalid: [ + * tInvalid({ * code: '...', * }), * ] @@ -158,23 +163,16 @@ export type RunTests< * * If the `TRule` parameter is omitted default types are used. */ -export function createRuleTestCaseFunction< +export function createRuleTestCaseFunctions< TRule extends RuleModule, TData extends GetRuleModuleTypes = GetRuleModuleTypes, - TTestCase extends - | TSESLintValidTestCase - | TSESLintInvalidTestCase = - | TSESLintValidTestCase - | TSESLintInvalidTestCase, ->(): < - TReturn = TTestCase extends { errors: InvalidTestCaseError[] | number } - ? TSESLintInvalidTestCase - : TSESLintValidTestCase, ->( - t: TTestCase, -) => TReturn { - // @ts-expect-error simplify testing - return createRuleTestCase + Valid = TSESLintValidTestCase, + Invalid = TSESLintInvalidTestCase, +>(): { tValid: (t: Valid) => Valid; tInvalid: (t: Invalid) => Invalid } { + return { + tValid: createRuleTestCase as never, + tInvalid: createRuleTestCase as never, + } } export function testContext(settings?: PluginSettings) { From 43eb6efb0a8a49efb15eefdb4412680e8a01c229 Mon Sep 17 00:00:00 2001 From: Marco Pasqualetti Date: Mon, 18 Nov 2024 08:34:41 +0100 Subject: [PATCH 18/49] test(no-default-export): migrate to `createRuleTestCaseFunctions` --- test/rules/no-default-export.spec.ts | 120 ++++++++++++++------------- 1 file changed, 62 insertions(+), 58 deletions(-) diff --git a/test/rules/no-default-export.spec.ts b/test/rules/no-default-export.spec.ts index 92ef91f0e..e15feb719 100644 --- a/test/rules/no-default-export.spec.ts +++ b/test/rules/no-default-export.spec.ts @@ -1,14 +1,30 @@ import { RuleTester as TSESLintRuleTester } from '@typescript-eslint/rule-tester' +import type { TestCaseError as TSESLintTestCaseError } from '@typescript-eslint/rule-tester' +import type { TSESTree, AST_NODE_TYPES } from '@typescript-eslint/utils' -import { parsers, test } from '../utils' +import { parsers, createRuleTestCaseFunctions } from '../utils' +import type { GetRuleModuleMessageIds } from '../utils' import rule from 'eslint-plugin-import-x/rules/no-default-export' const ruleTester = new TSESLintRuleTester() +const { tValid, tInvalid } = createRuleTestCaseFunctions() + +function createNoAliasDefaultError( + local: string, + type?: `${TSESTree.AST_NODE_TYPES}`, +): TSESLintTestCaseError> { + return { + messageId: 'noAliasDefault', + data: { local }, + type: type as TSESTree.AST_NODE_TYPES, + } +} + ruleTester.run('no-default-export', rule, { valid: [ - test({ + tValid({ code: 'module.exports = function foo() {}', languageOptions: { parserOptions: { @@ -16,161 +32,155 @@ ruleTester.run('no-default-export', rule, { }, }, }), - test({ + tValid({ code: 'module.exports = function foo() {}', }), - test({ + tValid({ code: ` export const foo = 'foo'; export const bar = 'bar'; `, }), - test({ + tValid({ code: ` export const foo = 'foo'; export function bar() {}; `, }), - test({ + tValid({ code: `export const foo = 'foo';`, }), - test({ + tValid({ code: ` const foo = 'foo'; export { foo }; `, }), - test({ + tValid({ code: `let foo, bar; export { foo, bar }`, }), - test({ + tValid({ code: `export const { foo, bar } = item;`, }), - test({ + tValid({ code: `export const { foo, bar: baz } = item;`, }), - test({ + tValid({ code: `export const { foo: { bar, baz } } = item;`, }), - test({ + tValid({ code: ` let item; export const foo = item; export { item }; `, }), - test({ + tValid({ code: `export * from './foo';`, }), - test({ + tValid({ code: `export const { foo } = { foo: "bar" };`, }), - test({ + tValid({ code: `export const { foo: { bar } } = { foo: { bar: "baz" } };`, }), - test({ + tValid({ code: 'export { a, b } from "foo.js"', languageOptions: { parser: require(parsers.BABEL) }, }), // no exports at all - test({ + tValid({ code: `import * as foo from './foo';`, }), - test({ + tValid({ code: `import foo from './foo';`, }), - test({ + tValid({ code: `import {default as foo} from './foo';`, }), - test({ + tValid({ code: `export type UserId = number;`, languageOptions: { parser: require(parsers.BABEL) }, }), - test({ + tValid({ code: 'export foo from "foo.js"', languageOptions: { parser: require(parsers.BABEL) }, }), - test({ + tValid({ code: `export Memory, { MemoryValue } from './Memory'`, languageOptions: { parser: require(parsers.BABEL) }, }), ], invalid: [ - test({ + tInvalid({ code: 'export default function bar() {};', errors: [ { - type: 'ExportDefaultDeclaration', - message: 'Prefer named exports.', + type: 'ExportDefaultDeclaration' as AST_NODE_TYPES, + messageId: 'preferNamed', line: 1, column: 8, }, ], }), - test({ + tInvalid({ code: ` export const foo = 'foo'; export default bar;`, errors: [ { - type: 'ExportDefaultDeclaration', - message: 'Prefer named exports.', + type: 'ExportDefaultDeclaration' as AST_NODE_TYPES, + messageId: 'preferNamed', line: 3, column: 16, }, ], }), - test({ + tInvalid({ code: 'export default class Bar {};', errors: [ { - type: 'ExportDefaultDeclaration', - message: 'Prefer named exports.', + type: 'ExportDefaultDeclaration' as AST_NODE_TYPES, + messageId: 'preferNamed', line: 1, column: 8, }, ], }), - test({ + tInvalid({ code: 'export default function() {};', errors: [ { - type: 'ExportDefaultDeclaration', - message: 'Prefer named exports.', + type: 'ExportDefaultDeclaration' as AST_NODE_TYPES, + messageId: 'preferNamed', line: 1, column: 8, }, ], }), - test({ + tInvalid({ code: 'export default class {};', errors: [ { - type: 'ExportDefaultDeclaration', - message: 'Prefer named exports.', + type: 'ExportDefaultDeclaration' as AST_NODE_TYPES, + messageId: 'preferNamed', line: 1, column: 8, }, ], }), - test({ + tInvalid({ code: 'let foo; export { foo as default }', - errors: [ - { - type: 'ExportNamedDeclaration', - message: - 'Do not alias `foo` as `default`. Just export `foo` itself instead.', - }, - ], + errors: [createNoAliasDefaultError('foo', 'ExportNamedDeclaration')], }), - test({ + tInvalid({ code: "function foo() { return 'foo'; }\nexport default foo;", filename: 'foo.ts', errors: [ { - type: 'ExportDefaultDeclaration', + type: 'ExportDefaultDeclaration' as AST_NODE_TYPES, messageId: 'preferNamed', }, ], @@ -184,25 +194,19 @@ ruleTester.run('no-default-export', rule, { 'import-x/resolver': { typescript: true }, }, }), - test({ + tInvalid({ code: 'export default from "foo.js"', languageOptions: { parser: require(parsers.BABEL) }, errors: [ { - type: 'ExportNamedDeclaration', - message: 'Prefer named exports.', + type: 'ExportNamedDeclaration' as AST_NODE_TYPES, + messageId: 'preferNamed', }, ], }), - test({ + tInvalid({ code: 'let foo; export { foo as "default" }', - errors: [ - { - type: 'ExportNamedDeclaration', - message: - 'Do not alias `foo` as `default`. Just export `foo` itself instead.', - }, - ], + errors: [createNoAliasDefaultError('foo', 'ExportNamedDeclaration')], languageOptions: { parser: require(parsers.ESPREE), parserOptions: { ecmaVersion: 2022 }, From 04780632d39ce55bd122042e9aa4629ea0139eaf Mon Sep 17 00:00:00 2001 From: Marco Pasqualetti Date: Mon, 18 Nov 2024 09:01:32 +0100 Subject: [PATCH 19/49] fix(no-deprecated): improve error message when no description is available --- .changeset/nine-wolves-repeat.md | 5 + src/rules/no-deprecated.ts | 19 +-- test/fixtures/deprecated.js | 5 + test/rules/no-deprecated.spec.ts | 264 ++++++++++++++++++------------- 4 files changed, 176 insertions(+), 117 deletions(-) create mode 100644 .changeset/nine-wolves-repeat.md diff --git a/.changeset/nine-wolves-repeat.md b/.changeset/nine-wolves-repeat.md new file mode 100644 index 000000000..218d7f33e --- /dev/null +++ b/.changeset/nine-wolves-repeat.md @@ -0,0 +1,5 @@ +--- +"eslint-plugin-import-x": patch +--- + +fix(no-deprecated): improve error message when no description is available diff --git a/src/rules/no-deprecated.ts b/src/rules/no-deprecated.ts index a64cc012d..9c77705e6 100644 --- a/src/rules/no-deprecated.ts +++ b/src/rules/no-deprecated.ts @@ -5,14 +5,14 @@ import type { ModuleNamespace } from '../utils' import { ExportMap, createRule, declaredScope, getValue } from '../utils' function message(deprecation: Tag) { - return { - messageId: 'deprecated', - data: { - description: deprecation.description - ? `: ${deprecation.description}` - : '.', - }, - } as const + if (deprecation.description) { + return { + messageId: 'deprecatedDesc', + data: { description: deprecation.description }, + } as const + } + + return { messageId: 'deprecated' } as const } function getDeprecation(metadata?: ModuleNamespace | null) { @@ -34,7 +34,8 @@ export = createRule({ }, schema: [], messages: { - deprecated: 'Deprecated{{description}}', + deprecatedDesc: 'Deprecated: {{description}}', + deprecated: 'Deprecated: consider to find an alternative.', }, }, defaultOptions: [], diff --git a/test/fixtures/deprecated.js b/test/fixtures/deprecated.js index 78ce78c04..48adc58c0 100644 --- a/test/fixtures/deprecated.js +++ b/test/fixtures/deprecated.js @@ -50,3 +50,8 @@ export function fine() { export function _undocumented() { return 'sneaky!' } + +/** @deprecated */ +export function _deprecatedNoDescription() { + return '_deprecatedNoDescription' +} diff --git a/test/rules/no-deprecated.spec.ts b/test/rules/no-deprecated.spec.ts index 5b73d8517..f102f8adc 100644 --- a/test/rules/no-deprecated.spec.ts +++ b/test/rules/no-deprecated.spec.ts @@ -1,45 +1,65 @@ import { RuleTester as TSESLintRuleTester } from '@typescript-eslint/rule-tester' +import type { TestCaseError as TSESLintTestCaseError } from '@typescript-eslint/rule-tester' +import type { TSESTree } from '@typescript-eslint/utils' -import { test, SYNTAX_VALID_CASES, parsers } from '../utils' +import { + createRuleTestCaseFunctions, + SYNTAX_VALID_CASES, + parsers, +} from '../utils' +import type { GetRuleModuleMessageIds } from '../utils' import rule from 'eslint-plugin-import-x/rules/no-deprecated' const ruleTester = new TSESLintRuleTester() +const { tValid, tInvalid } = createRuleTestCaseFunctions() + +function createDeprecatedDescError( + description: string, + type?: `${TSESTree.AST_NODE_TYPES}`, +): TSESLintTestCaseError> { + return { + messageId: 'deprecatedDesc', + data: { description }, + type: type as TSESTree.AST_NODE_TYPES, + } +} + ruleTester.run('no-deprecated', rule, { valid: [ - test({ code: "import { x } from './fake' " }), - test({ code: "import bar from './bar'" }), + tValid({ code: "import { x } from './fake' " }), + tValid({ code: "import bar from './bar'" }), - test({ code: "import { fine } from './deprecated'" }), - test({ code: "import { _undocumented } from './deprecated'" }), + tValid({ code: "import { fine } from './deprecated'" }), + tValid({ code: "import { _undocumented } from './deprecated'" }), - test({ + tValid({ code: "import { fn } from './deprecated'", settings: { 'import-x/docstyle': ['tomdoc'] }, }), - test({ + tValid({ code: "import { fine } from './tomdoc-deprecated'", settings: { 'import-x/docstyle': ['tomdoc'] }, }), - test({ + tValid({ code: "import { _undocumented } from './tomdoc-deprecated'", settings: { 'import-x/docstyle': ['tomdoc'] }, }), // naked namespace is fine - test({ code: "import * as depd from './deprecated'" }), - test({ + tValid({ code: "import * as depd from './deprecated'" }), + tValid({ code: "import * as depd from './deprecated'; console.log(depd.fine())", }), - test({ code: "import { deepDep } from './deep-deprecated'" }), - test({ + tValid({ code: "import { deepDep } from './deep-deprecated'" }), + tValid({ code: "import { deepDep } from './deep-deprecated'; console.log(deepDep.fine())", }), // redefined - test({ + tValid({ code: "import { deepDep } from './deep-deprecated'; function x(deepDep) { console.log(deepDep.MY_TERRIBLE_ACTION) }", }), @@ -47,172 +67,200 @@ ruleTester.run('no-deprecated', rule, { ], invalid: [ // reports on parse errors even without specifiers - test({ code: "import './malformed.js'", errors: 1 }), + tInvalid({ + code: "import './malformed.js'", + // @ts-expect-error parsing error + errors: 1, + }), - test({ + tInvalid({ + code: "import { _deprecatedNoDescription } from './deprecated'", + errors: [{ messageId: 'deprecated' }], + }), + + tInvalid({ code: "import { fn } from './deprecated'", - errors: ["Deprecated: please use 'x' instead."], + errors: [createDeprecatedDescError("please use 'x' instead.")], }), - test({ + tInvalid({ code: "import TerribleClass from './deprecated'", - errors: ['Deprecated: this is awful, use NotAsBadClass.'], + errors: [createDeprecatedDescError('this is awful, use NotAsBadClass.')], }), - test({ + tInvalid({ code: "import { MY_TERRIBLE_ACTION } from './deprecated'", - errors: ['Deprecated: please stop sending/handling this action type.'], + errors: [ + createDeprecatedDescError( + 'please stop sending/handling this action type.', + ), + ], }), - test({ + tInvalid({ code: "import { fn } from './deprecated'", settings: { 'import-x/docstyle': ['jsdoc', 'tomdoc'] }, - errors: ["Deprecated: please use 'x' instead."], + errors: [createDeprecatedDescError("please use 'x' instead.")], }), - test({ + tInvalid({ code: "import { fn } from './tomdoc-deprecated'", settings: { 'import-x/docstyle': ['tomdoc'] }, - errors: ['Deprecated: This function is terrible.'], + errors: [createDeprecatedDescError('This function is terrible.')], }), - test({ + tInvalid({ code: "import TerribleClass from './tomdoc-deprecated'", settings: { 'import-x/docstyle': ['tomdoc'] }, - errors: ['Deprecated: this is awful, use NotAsBadClass.'], + errors: [createDeprecatedDescError('this is awful, use NotAsBadClass.')], }), - test({ + tInvalid({ code: "import { MY_TERRIBLE_ACTION } from './tomdoc-deprecated'", settings: { 'import-x/docstyle': ['tomdoc'] }, - errors: ['Deprecated: Please stop sending/handling this action type.'], + errors: [ + createDeprecatedDescError( + 'Please stop sending/handling this action type.', + ), + ], }), // ignore redeclares - test({ + tInvalid({ code: "import { MY_TERRIBLE_ACTION } from './deprecated'; function shadow(MY_TERRIBLE_ACTION) { console.log(MY_TERRIBLE_ACTION); }", - errors: ['Deprecated: please stop sending/handling this action type.'], + errors: [ + createDeprecatedDescError( + 'please stop sending/handling this action type.', + ), + ], }), // ignore non-deprecateds - test({ + tInvalid({ code: "import { MY_TERRIBLE_ACTION, fine } from './deprecated'; console.log(fine)", - errors: ['Deprecated: please stop sending/handling this action type.'], + errors: [ + createDeprecatedDescError( + 'please stop sending/handling this action type.', + ), + ], }), // reflag on subsequent usages - test({ + tInvalid({ code: "import { MY_TERRIBLE_ACTION } from './deprecated'; console.log(MY_TERRIBLE_ACTION)", errors: [ - { - type: 'ImportSpecifier', - message: 'Deprecated: please stop sending/handling this action type.', - }, - { - type: 'Identifier', - message: 'Deprecated: please stop sending/handling this action type.', - }, + createDeprecatedDescError( + 'please stop sending/handling this action type.', + 'ImportSpecifier', + ), + + createDeprecatedDescError( + 'please stop sending/handling this action type.', + 'Identifier', + ), ], }), // don't flag other members - test({ + tInvalid({ code: "import { MY_TERRIBLE_ACTION } from './deprecated'; console.log(someOther.MY_TERRIBLE_ACTION)", errors: [ - { - type: 'ImportSpecifier', - message: 'Deprecated: please stop sending/handling this action type.', - }, + createDeprecatedDescError( + 'please stop sending/handling this action type.', + 'ImportSpecifier', + ), ], }), // flag it even with members - test({ + tInvalid({ code: "import { MY_TERRIBLE_ACTION } from './deprecated'; console.log(MY_TERRIBLE_ACTION.whatever())", errors: [ - { - type: 'ImportSpecifier', - message: 'Deprecated: please stop sending/handling this action type.', - }, - { - type: 'Identifier', - message: 'Deprecated: please stop sending/handling this action type.', - }, + createDeprecatedDescError( + 'please stop sending/handling this action type.', + 'ImportSpecifier', + ), + + createDeprecatedDescError( + 'please stop sending/handling this action type.', + 'Identifier', + ), ], }), // works for function calls too - test({ + tInvalid({ code: "import { MY_TERRIBLE_ACTION } from './deprecated'; console.log(MY_TERRIBLE_ACTION(this, is, the, worst))", errors: [ - { - type: 'ImportSpecifier', - message: 'Deprecated: please stop sending/handling this action type.', - }, - { - type: 'Identifier', - message: 'Deprecated: please stop sending/handling this action type.', - }, + createDeprecatedDescError( + 'please stop sending/handling this action type.', + 'ImportSpecifier', + ), + + createDeprecatedDescError( + 'please stop sending/handling this action type.', + 'Identifier', + ), ], }), // deprecated full module - test({ + tInvalid({ code: "import Thing from './deprecated-file'", errors: [ - { - type: 'ImportDeclaration', - message: 'Deprecated: this module is the worst.', - }, + createDeprecatedDescError( + 'this module is the worst.', + 'ImportDeclaration', + ), ], }), // don't flag as part of other member expressions - test({ + tInvalid({ code: "import Thing from './deprecated-file'; console.log(other.Thing)", errors: [ - { - type: 'ImportDeclaration', - message: 'Deprecated: this module is the worst.', - }, + createDeprecatedDescError( + 'this module is the worst.', + 'ImportDeclaration', + ), ], }), // namespace following - test({ + tInvalid({ code: "import * as depd from './deprecated'; console.log(depd.MY_TERRIBLE_ACTION)", errors: [ - { - type: 'Identifier', - message: 'Deprecated: please stop sending/handling this action type.', - }, + createDeprecatedDescError( + 'please stop sending/handling this action type.', + 'Identifier', + ), ], }), - test({ + tInvalid({ code: "import * as deep from './deep-deprecated'; console.log(deep.deepDep.MY_TERRIBLE_ACTION)", errors: [ - { - type: 'Identifier', - message: 'Deprecated: please stop sending/handling this action type.', - }, + createDeprecatedDescError( + 'please stop sending/handling this action type.', + 'Identifier', + ), ], }), - test({ + tInvalid({ code: "import { deepDep } from './deep-deprecated'; console.log(deepDep.MY_TERRIBLE_ACTION)", errors: [ - { - type: 'Identifier', - message: 'Deprecated: please stop sending/handling this action type.', - }, + createDeprecatedDescError( + 'please stop sending/handling this action type.', + 'Identifier', + ), ], }), - test({ + tInvalid({ code: "import { deepDep } from './deep-deprecated'; function x(deepNDep) { console.log(deepDep.MY_TERRIBLE_ACTION) }", errors: [ - { - type: 'Identifier', - message: 'Deprecated: please stop sending/handling this action type.', - }, + createDeprecatedDescError( + 'please stop sending/handling this action type.', + 'Identifier', + ), ], }), ], @@ -220,23 +268,23 @@ ruleTester.run('no-deprecated', rule, { ruleTester.run('no-deprecated: hoisting', rule, { valid: [ - test({ + tValid({ code: "function x(deepDep) { console.log(deepDep.MY_TERRIBLE_ACTION) } import { deepDep } from './deep-deprecated'", }), ], invalid: [ - test({ + tInvalid({ code: "console.log(MY_TERRIBLE_ACTION); import { MY_TERRIBLE_ACTION } from './deprecated'", errors: [ - { - type: 'Identifier', - message: 'Deprecated: please stop sending/handling this action type.', - }, - { - type: 'ImportSpecifier', - message: 'Deprecated: please stop sending/handling this action type.', - }, + createDeprecatedDescError( + 'please stop sending/handling this action type.', + 'Identifier', + ), + createDeprecatedDescError( + 'please stop sending/handling this action type.', + 'ImportSpecifier', + ), ], }), ], @@ -252,17 +300,17 @@ describe('TypeScript', () => { ruleTester.run('no-deprecated', rule, { valid: [ - test({ + tValid({ code: "import * as hasDeprecated from './ts-deprecated.ts'", ...parserConfig, }), ], invalid: [ - test({ + tInvalid({ code: "import { foo } from './ts-deprecated.ts'; console.log(foo())", errors: [ - { type: 'ImportSpecifier', message: "Deprecated: don't use this!" }, - { type: 'Identifier', message: "Deprecated: don't use this!" }, + createDeprecatedDescError("don't use this!", 'ImportSpecifier'), + createDeprecatedDescError("don't use this!", 'Identifier'), ], ...parserConfig, }), From 876a6c7e26ef37fe7d9bfe6ab2e9c0277d9777fd Mon Sep 17 00:00:00 2001 From: Marco Pasqualetti Date: Mon, 18 Nov 2024 09:17:02 +0100 Subject: [PATCH 20/49] test(no-duplicates): migrate to `createRuleTestCaseFunctions` --- test/rules/no-duplicates.spec.ts | 448 +++++++++++++------------------ 1 file changed, 183 insertions(+), 265 deletions(-) diff --git a/test/rules/no-duplicates.spec.ts b/test/rules/no-duplicates.spec.ts index 7aecacb1f..910923e9a 100644 --- a/test/rules/no-duplicates.spec.ts +++ b/test/rules/no-duplicates.spec.ts @@ -1,81 +1,91 @@ import path from 'node:path' import { RuleTester as TSESLintRuleTester } from '@typescript-eslint/rule-tester' +import type { TestCaseError as TSESLintTestCaseError } from '@typescript-eslint/rule-tester' import { - test, + createRuleTestCaseFunctions, parsers, tsVersionSatisfies, typescriptEslintParserSatisfies, } from '../utils' +import type { GetRuleModuleMessageIds, RunTests } from '../utils' import jsxConfig from 'eslint-plugin-import-x/config/react' import rule from 'eslint-plugin-import-x/rules/no-duplicates' const ruleTester = new TSESLintRuleTester() +const { tValid, tInvalid } = createRuleTestCaseFunctions() + +function createDuplicatedError( + module: string, +): TSESLintTestCaseError> { + return { + messageId: 'duplicate', + data: { module }, + } +} + ruleTester.run('no-duplicates', rule, { valid: [ - test({ + tValid({ code: 'import "./malformed.js"', languageOptions: { parser: require(parsers.ESPREE) }, }), - test({ code: "import { x } from './foo'; import { y } from './bar'" }), + tValid({ code: "import { x } from './foo'; import { y } from './bar'" }), // #86: every unresolved module should not show up as 'null' and duplicate - test({ + tValid({ code: 'import foo from "234artaf"; import { shoop } from "234q25ad"', }), // #225: ignore duplicate if is a flow type import - test({ + tValid({ code: "import { x } from './foo'; import type { y } from './foo'", languageOptions: { parser: require(parsers.BABEL) }, }), // #1107: Using different query strings that trigger different webpack loaders. - test({ + tValid({ code: "import x from './bar?optionX'; import y from './bar?optionY';", options: [{ considerQueryString: true }], settings: { 'import-x/resolver': 'webpack' }, }), - test({ + tValid({ code: "import x from './foo'; import y from './bar';", options: [{ considerQueryString: true }], settings: { 'import-x/resolver': 'webpack' }, }), // #1538: It is impossible to import namespace and other in one line, so allow this. - test({ + tValid({ code: "import * as ns from './foo'; import {y} from './foo'", }), - test({ + tValid({ code: "import {y} from './foo'; import * as ns from './foo'", }), ], invalid: [ - test({ + tInvalid({ code: "import { x } from './foo'; import { y } from './foo'", output: "import { x , y } from './foo'; ", - errors: [ - "'./foo' imported multiple times.", - "'./foo' imported multiple times.", - ], + errors: [createDuplicatedError('./foo'), createDuplicatedError('./foo')], }), - test({ + tInvalid({ code: "import {x} from './foo'; import {y} from './foo'; import { z } from './foo'", output: "import {x,y, z } from './foo'; ", errors: [ - "'./foo' imported multiple times.", - "'./foo' imported multiple times.", - "'./foo' imported multiple times.", + createDuplicatedError('./foo'), + createDuplicatedError('./foo'), + createDuplicatedError('./foo'), ], }), // ensure resolved path results in warnings - test({ + tInvalid({ code: "import { x } from './bar'; import { y } from 'bar';", output: "import { x , y } from './bar'; ", settings: { @@ -83,318 +93,253 @@ ruleTester.run('no-duplicates', rule, { paths: [path.resolve('test/fixtures')], }, }, - errors: 2, // path ends up hardcoded + // @ts-expect-error path ends up hardcoded + errors: 2, }), // #1107: Using different query strings that trigger different webpack loaders. - test({ + tInvalid({ code: "import x from './bar.js?optionX'; import y from './bar?optionX';", settings: { 'import-x/resolver': 'webpack' }, - errors: 2, // path ends up hardcoded + // @ts-expect-error path ends up hardcoded + errors: 2, }), - test({ + tInvalid({ code: "import x from './bar?optionX'; import y from './bar?optionY';", settings: { 'import-x/resolver': 'webpack' }, - errors: 2, // path ends up hardcoded + // @ts-expect-error path ends up hardcoded + errors: 2, }), // #1107: Using same query strings that trigger the same loader. - test({ + tInvalid({ code: "import x from './bar?optionX'; import y from './bar.js?optionX';", options: [{ considerQueryString: true }], settings: { 'import-x/resolver': 'webpack' }, - errors: 2, // path ends up hardcoded + // @ts-expect-error path ends up hardcoded + errors: 2, }), // #86: duplicate unresolved modules should be flagged - test({ + tInvalid({ // Autofix bail because of different default import names. code: "import foo from 'non-existent'; import bar from 'non-existent';", languageOptions: { parser: require(parsers.ESPREE) }, errors: [ - "'non-existent' imported multiple times.", - "'non-existent' imported multiple times.", + createDuplicatedError('non-existent'), + createDuplicatedError('non-existent'), ], }), - test({ + tInvalid({ code: "import type { x } from './foo'; import type { y } from './foo'", output: "import type { x , y } from './foo'; ", languageOptions: { parser: require(parsers.BABEL) }, - errors: [ - "'./foo' imported multiple times.", - "'./foo' imported multiple times.", - ], + errors: [createDuplicatedError('./foo'), createDuplicatedError('./foo')], }), - test({ + tInvalid({ code: "import './foo'; import './foo'", output: "import './foo'; ", - errors: [ - "'./foo' imported multiple times.", - "'./foo' imported multiple times.", - ], + errors: [createDuplicatedError('./foo'), createDuplicatedError('./foo')], }), - test({ + tInvalid({ code: "import { x, /* x */ } from './foo'; import {//y\ny//y2\n} from './foo'", output: "import { x, /* x */ //y\ny//y2\n} from './foo'; ", languageOptions: { parser: require(parsers.ESPREE) }, - errors: [ - "'./foo' imported multiple times.", - "'./foo' imported multiple times.", - ], + errors: [createDuplicatedError('./foo'), createDuplicatedError('./foo')], }), - test({ + tInvalid({ code: "import {x} from './foo'; import {} from './foo'", output: "import {x} from './foo'; ", - errors: [ - "'./foo' imported multiple times.", - "'./foo' imported multiple times.", - ], + errors: [createDuplicatedError('./foo'), createDuplicatedError('./foo')], }), // #2347: duplicate identifiers should be removed - test({ + tInvalid({ code: "import {a} from './foo'; import { a } from './foo'", output: "import {a} from './foo'; ", - errors: [ - "'./foo' imported multiple times.", - "'./foo' imported multiple times.", - ], + errors: [createDuplicatedError('./foo'), createDuplicatedError('./foo')], }), // #2347: duplicate identifiers should be removed - test({ + tInvalid({ code: "import {a,b} from './foo'; import { b, c } from './foo'; import {b,c,d} from './foo'", output: "import {a,b, c ,d} from './foo'; ", errors: [ - "'./foo' imported multiple times.", - "'./foo' imported multiple times.", - "'./foo' imported multiple times.", + createDuplicatedError('./foo'), + createDuplicatedError('./foo'), + createDuplicatedError('./foo'), ], }), // #2347: duplicate identifiers should be removed, but not if they are adjacent to comments - test({ + tInvalid({ code: "import {a} from './foo'; import { a/*,b*/ } from './foo'", output: "import {a, a/*,b*/ } from './foo'; ", - errors: [ - "'./foo' imported multiple times.", - "'./foo' imported multiple times.", - ], + errors: [createDuplicatedError('./foo'), createDuplicatedError('./foo')], }), - test({ + tInvalid({ code: "import {x} from './foo'; import {} from './foo'; import {/*c*/} from './foo'; import {y} from './foo'", output: "import {x/*c*/,y} from './foo'; ", errors: [ - "'./foo' imported multiple times.", - "'./foo' imported multiple times.", - "'./foo' imported multiple times.", - "'./foo' imported multiple times.", + createDuplicatedError('./foo'), + createDuplicatedError('./foo'), + createDuplicatedError('./foo'), + createDuplicatedError('./foo'), ], }), - test({ + tInvalid({ code: "import { } from './foo'; import {x} from './foo'", output: "import { x} from './foo'; ", - errors: [ - "'./foo' imported multiple times.", - "'./foo' imported multiple times.", - ], + errors: [createDuplicatedError('./foo'), createDuplicatedError('./foo')], }), - test({ + tInvalid({ code: "import './foo'; import {x} from './foo'", output: "import {x} from './foo'; ", - errors: [ - "'./foo' imported multiple times.", - "'./foo' imported multiple times.", - ], + errors: [createDuplicatedError('./foo'), createDuplicatedError('./foo')], }), - test({ + tInvalid({ code: "import'./foo'; import {x} from './foo'", output: "import {x} from'./foo'; ", - errors: [ - "'./foo' imported multiple times.", - "'./foo' imported multiple times.", - ], + errors: [createDuplicatedError('./foo'), createDuplicatedError('./foo')], }), - test({ + tInvalid({ code: "import './foo'; import { /*x*/} from './foo'; import {//y\n} from './foo'; import {z} from './foo'", output: "import { /*x*///y\nz} from './foo'; ", errors: [ - "'./foo' imported multiple times.", - "'./foo' imported multiple times.", - "'./foo' imported multiple times.", - "'./foo' imported multiple times.", + createDuplicatedError('./foo'), + createDuplicatedError('./foo'), + createDuplicatedError('./foo'), + createDuplicatedError('./foo'), ], }), - test({ + tInvalid({ code: "import './foo'; import def, {x} from './foo'", output: "import def, {x} from './foo'; ", - errors: [ - "'./foo' imported multiple times.", - "'./foo' imported multiple times.", - ], + errors: [createDuplicatedError('./foo'), createDuplicatedError('./foo')], }), - test({ + tInvalid({ code: "import './foo'; import def from './foo'", output: "import def from './foo'; ", - errors: [ - "'./foo' imported multiple times.", - "'./foo' imported multiple times.", - ], + errors: [createDuplicatedError('./foo'), createDuplicatedError('./foo')], }), - test({ + tInvalid({ code: "import def from './foo'; import {x} from './foo'", output: "import def, {x} from './foo'; ", - errors: [ - "'./foo' imported multiple times.", - "'./foo' imported multiple times.", - ], + errors: [createDuplicatedError('./foo'), createDuplicatedError('./foo')], }), - test({ + tInvalid({ code: "import {x} from './foo'; import def from './foo'", output: "import def, {x} from './foo'; ", - errors: [ - "'./foo' imported multiple times.", - "'./foo' imported multiple times.", - ], + errors: [createDuplicatedError('./foo'), createDuplicatedError('./foo')], }), - test({ + tInvalid({ code: "import{x} from './foo'; import def from './foo'", output: "import def,{x} from './foo'; ", - errors: [ - "'./foo' imported multiple times.", - "'./foo' imported multiple times.", - ], + errors: [createDuplicatedError('./foo'), createDuplicatedError('./foo')], }), - test({ + tInvalid({ code: "import {x} from './foo'; import def, {y} from './foo'", output: "import def, {x,y} from './foo'; ", - errors: [ - "'./foo' imported multiple times.", - "'./foo' imported multiple times.", - ], + errors: [createDuplicatedError('./foo'), createDuplicatedError('./foo')], }), - test({ + tInvalid({ // Autofix bail because cannot merge namespace imports. code: "import * as ns1 from './foo'; import * as ns2 from './foo'", - errors: [ - "'./foo' imported multiple times.", - "'./foo' imported multiple times.", - ], + errors: [createDuplicatedError('./foo'), createDuplicatedError('./foo')], }), - test({ + tInvalid({ code: "import * as ns from './foo'; import {x} from './foo'; import {y} from './foo'", // Autofix could merge some imports, but not the namespace import. output: "import * as ns from './foo'; import {x,y} from './foo'; ", - errors: [ - "'./foo' imported multiple times.", - "'./foo' imported multiple times.", - ], + errors: [createDuplicatedError('./foo'), createDuplicatedError('./foo')], }), - test({ + tInvalid({ code: "import {x} from './foo'; import * as ns from './foo'; import {y} from './foo'; import './foo'", // Autofix could merge some imports, but not the namespace import. output: "import {x,y} from './foo'; import * as ns from './foo'; ", errors: [ - "'./foo' imported multiple times.", - "'./foo' imported multiple times.", - "'./foo' imported multiple times.", + createDuplicatedError('./foo'), + createDuplicatedError('./foo'), + createDuplicatedError('./foo'), ], }), - test({ + tInvalid({ // Autofix bail because of comment. code: ` // some-tool-disable-next-line import {x} from './foo' import {//y\ny} from './foo' `, - errors: [ - "'./foo' imported multiple times.", - "'./foo' imported multiple times.", - ], + errors: [createDuplicatedError('./foo'), createDuplicatedError('./foo')], }), - test({ + tInvalid({ // Autofix bail because of comment. code: ` import {x} from './foo' // some-tool-disable-next-line import {y} from './foo' `, - errors: [ - "'./foo' imported multiple times.", - "'./foo' imported multiple times.", - ], + errors: [createDuplicatedError('./foo'), createDuplicatedError('./foo')], }), - test({ + tInvalid({ // Autofix bail because of comment. code: ` import {x} from './foo' // some-tool-disable-line import {y} from './foo' `, - errors: [ - "'./foo' imported multiple times.", - "'./foo' imported multiple times.", - ], + errors: [createDuplicatedError('./foo'), createDuplicatedError('./foo')], }), - test({ + tInvalid({ // Autofix bail because of comment. code: ` import {x} from './foo' import {y} from './foo' // some-tool-disable-line `, - errors: [ - "'./foo' imported multiple times.", - "'./foo' imported multiple times.", - ], + errors: [createDuplicatedError('./foo'), createDuplicatedError('./foo')], }), - test({ + tInvalid({ // Autofix bail because of comment. code: ` import {x} from './foo' /* comment */ import {y} from './foo' `, - errors: [ - "'./foo' imported multiple times.", - "'./foo' imported multiple times.", - ], + errors: [createDuplicatedError('./foo'), createDuplicatedError('./foo')], }), - test({ + tInvalid({ // Autofix bail because of comment. code: ` import {x} from './foo' import {y} from './foo' /* comment multiline */ `, - errors: [ - "'./foo' imported multiple times.", - "'./foo' imported multiple times.", - ], + errors: [createDuplicatedError('./foo'), createDuplicatedError('./foo')], }), - test({ + tInvalid({ code: ` import {x} from './foo' import {y} from './foo' @@ -405,13 +350,10 @@ import {y} from './foo' import {x,y} from './foo' // some-tool-disable-next-line `, - errors: [ - "'./foo' imported multiple times.", - "'./foo' imported multiple times.", - ], + errors: [createDuplicatedError('./foo'), createDuplicatedError('./foo')], }), - test({ + tInvalid({ code: ` import {x} from './foo' // comment @@ -424,61 +366,46 @@ import {x,y} from './foo' // comment `, - errors: [ - "'./foo' imported multiple times.", - "'./foo' imported multiple times.", - ], + errors: [createDuplicatedError('./foo'), createDuplicatedError('./foo')], }), - test({ + tInvalid({ // Autofix bail because of comment. code: ` import {x} from './foo' import/* comment */{y} from './foo' `, - errors: [ - "'./foo' imported multiple times.", - "'./foo' imported multiple times.", - ], + errors: [createDuplicatedError('./foo'), createDuplicatedError('./foo')], }), - test({ + tInvalid({ // Autofix bail because of comment. code: ` import {x} from './foo' import/* comment */'./foo' `, - errors: [ - "'./foo' imported multiple times.", - "'./foo' imported multiple times.", - ], + errors: [createDuplicatedError('./foo'), createDuplicatedError('./foo')], }), - test({ + tInvalid({ // Autofix bail because of comment. code: ` import {x} from './foo' import{y}/* comment */from './foo' `, - errors: [ - "'./foo' imported multiple times.", - "'./foo' imported multiple times.", - ], + errors: [createDuplicatedError('./foo'), createDuplicatedError('./foo')], }), - test({ + tInvalid({ // Autofix bail because of comment. code: ` import {x} from './foo' import{y}from/* comment */'./foo' `, - errors: [ - "'./foo' imported multiple times.", - "'./foo' imported multiple times.", - ], + errors: [createDuplicatedError('./foo'), createDuplicatedError('./foo')], }), - test({ + tInvalid({ // Autofix bail because of comment. code: ` import {x} from @@ -486,33 +413,24 @@ import {x,y} from './foo' './foo' import {y} from './foo' `, - errors: [ - "'./foo' imported multiple times.", - "'./foo' imported multiple times.", - ], + errors: [createDuplicatedError('./foo'), createDuplicatedError('./foo')], }), // #2027 long import list generate empty lines - test({ + tInvalid({ code: "import { Foo } from './foo';\nimport { Bar } from './foo';\nexport const value = {}", output: "import { Foo , Bar } from './foo';\nexport const value = {}", - errors: [ - "'./foo' imported multiple times.", - "'./foo' imported multiple times.", - ], + errors: [createDuplicatedError('./foo'), createDuplicatedError('./foo')], }), // #2027 long import list generate empty lines - test({ + tInvalid({ code: "import { Foo } from './foo';\nimport Bar from './foo';\nexport const value = {}", output: "import Bar, { Foo } from './foo';\nexport const value = {}", - errors: [ - "'./foo' imported multiple times.", - "'./foo' imported multiple times.", - ], + errors: [createDuplicatedError('./foo'), createDuplicatedError('./foo')], }), - test({ + tInvalid({ code: ` import { DEFAULT_FILTER_KEYS, @@ -547,13 +465,13 @@ const TestComponent = () => { export default TestComponent; `, errors: [ - "'../constants' imported multiple times.", - "'../constants' imported multiple times.", + createDuplicatedError('../constants'), + createDuplicatedError('../constants'), ], ...jsxConfig, }), - test({ + tInvalid({ code: ` import {A1,} from 'foo'; import {B1,} from 'foo'; @@ -582,32 +500,32 @@ export default TestComponent; `, errors: [ { - message: "'foo' imported multiple times.", + ...createDuplicatedError('foo'), line: 2, column: 27, }, { - message: "'foo' imported multiple times.", + ...createDuplicatedError('foo'), line: 3, column: 27, }, { - message: "'foo' imported multiple times.", + ...createDuplicatedError('foo'), line: 4, column: 27, }, { - message: "'bar' imported multiple times.", + ...createDuplicatedError('bar'), line: 8, column: 16, }, { - message: "'bar' imported multiple times.", + ...createDuplicatedError('bar'), line: 11, column: 16, }, { - message: "'bar' imported multiple times.", + ...createDuplicatedError('bar'), line: 14, column: 16, }, @@ -625,36 +543,36 @@ describe('TypeScript', () => { }, } - const valid = [ + const valid: RunTests['valid'] = [ // #1667: ignore duplicate if is a typescript type import - test({ + tValid({ code: "import type { x } from './foo'; import y from './foo'", ...parserConfig, }), - test({ + tValid({ code: "import type { x } from './foo'; import type * as y from './foo'", ...parserConfig, }), - test({ + tValid({ code: "import type x from './foo'; import type y from './bar'", ...parserConfig, }), - test({ + tValid({ code: "import type {x} from './foo'; import type {y} from './bar'", ...parserConfig, }), - test({ + tValid({ code: "import type x from './foo'; import type {y} from './foo'", ...parserConfig, }), - test({ + tValid({ code: ` import type {} from './module'; import {} from './module2'; `, ...parserConfig, }), - test({ + tValid({ code: ` import type { Identifier } from 'module'; @@ -673,84 +591,84 @@ describe('TypeScript', () => { ? [] : [ // #2470: ignore duplicate if is a typescript inline type import - test({ + tValid({ code: "import { type x } from './foo'; import y from './foo'", ...parserConfig, }), - test({ + tValid({ code: "import { type x } from './foo'; import { y } from './foo'", ...parserConfig, }), - test({ + tValid({ code: "import { type x } from './foo'; import type y from 'foo'", ...parserConfig, }), ]), - test({ + tValid({ code: "import type { A } from 'foo';import type B from 'foo';", ...parserConfig, options: [{ 'prefer-inline': true }], }), - test({ + tValid({ code: "import { type A } from 'foo';import type B from 'foo';", ...parserConfig, options: [{ 'prefer-inline': true }], }), - test({ + tValid({ code: "import type A from 'foo';import { B } from 'foo';", ...parserConfig, options: [{ 'prefer-inline': true }], }), ] - const invalid = [ - test({ + const invalid: RunTests['invalid'] = [ + tInvalid({ code: "import type x from './foo'; import type y from './foo'", ...parserConfig, errors: [ { + ...createDuplicatedError('./foo'), line: 1, column: 20, - message: "'./foo' imported multiple times.", }, { + ...createDuplicatedError('./foo'), line: 1, column: 48, - message: "'./foo' imported multiple times.", }, ], }), - test({ + tInvalid({ code: "import type x from './foo'; import type x from './foo'", output: "import type x from './foo'; ", ...parserConfig, errors: [ { + ...createDuplicatedError('./foo'), line: 1, column: 20, - message: "'./foo' imported multiple times.", }, { + ...createDuplicatedError('./foo'), line: 1, column: 48, - message: "'./foo' imported multiple times.", }, ], }), - test({ + tInvalid({ code: "import type {x} from './foo'; import type {y} from './foo'", ...parserConfig, output: `import type {x,y} from './foo'; `, errors: [ { + ...createDuplicatedError('./foo'), line: 1, column: 22, - message: "'./foo' imported multiple times.", }, { + ...createDuplicatedError('./foo'), line: 1, column: 52, - message: "'./foo' imported multiple times.", }, ], }), @@ -758,145 +676,145 @@ describe('TypeScript', () => { !typescriptEslintParserSatisfies('>= 5.7.0') ? [] : [ - test({ + tInvalid({ code: "import {type x} from './foo'; import type {y} from './foo'", ...parserConfig, options: [{ 'prefer-inline': false }], output: `import {type x,y} from './foo'; `, errors: [ { + ...createDuplicatedError('./foo'), line: 1, column: 22, - message: "'./foo' imported multiple times.", }, { + ...createDuplicatedError('./foo'), line: 1, column: 52, - message: "'./foo' imported multiple times.", }, ], }), - test({ + tInvalid({ code: "import type {x} from 'foo'; import {type y} from 'foo'", ...parserConfig, options: [{ 'prefer-inline': true }], output: `import {type x,type y} from 'foo'; `, errors: [ { + ...createDuplicatedError('foo'), line: 1, column: 22, - message: "'foo' imported multiple times.", }, { + ...createDuplicatedError('foo'), line: 1, column: 50, - message: "'foo' imported multiple times.", }, ], }), - test({ + tInvalid({ code: "import {type x} from 'foo'; import type {y} from 'foo'", ...parserConfig, options: [{ 'prefer-inline': true }], output: `import {type x,type y} from 'foo'; `, errors: [ { + ...createDuplicatedError('foo'), line: 1, column: 22, - message: "'foo' imported multiple times.", }, { + ...createDuplicatedError('foo'), line: 1, column: 50, - message: "'foo' imported multiple times.", }, ], }), - test({ + tInvalid({ code: "import {type x} from 'foo'; import type {y} from 'foo'", ...parserConfig, output: `import {type x,y} from 'foo'; `, errors: [ { + ...createDuplicatedError('foo'), line: 1, column: 22, - message: "'foo' imported multiple times.", }, { + ...createDuplicatedError('foo'), line: 1, column: 50, - message: "'foo' imported multiple times.", }, ], }), - test({ + tInvalid({ code: "import {type x} from './foo'; import {type y} from './foo'", ...parserConfig, options: [{ 'prefer-inline': true }], output: `import {type x,type y} from './foo'; `, errors: [ { + ...createDuplicatedError('./foo'), line: 1, column: 22, - message: "'./foo' imported multiple times.", }, { + ...createDuplicatedError('./foo'), line: 1, column: 52, - message: "'./foo' imported multiple times.", }, ], }), - test({ + tInvalid({ code: "import {type x} from './foo'; import {type y} from './foo'", ...parserConfig, output: `import {type x,type y} from './foo'; `, errors: [ { + ...createDuplicatedError('./foo'), line: 1, column: 22, - message: "'./foo' imported multiple times.", }, { + ...createDuplicatedError('./foo'), line: 1, column: 52, - message: "'./foo' imported multiple times.", }, ], }), - test({ + tInvalid({ code: "import {AValue, type x, BValue} from './foo'; import {type y} from './foo'", ...parserConfig, output: `import {AValue, type x, BValue,type y} from './foo'; `, errors: [ { + ...createDuplicatedError('./foo'), line: 1, column: 38, - message: "'./foo' imported multiple times.", }, { + ...createDuplicatedError('./foo'), line: 1, column: 68, - message: "'./foo' imported multiple times.", }, ], }), // #2834 Detect duplicates across type and regular imports - test({ + tInvalid({ code: "import {AValue} from './foo'; import type {AType} from './foo'", ...parserConfig, options: [{ 'prefer-inline': true }], output: `import {AValue,type AType} from './foo'; `, errors: [ { + ...createDuplicatedError('./foo'), line: 1, column: 22, - message: "'./foo' imported multiple times.", }, { + ...createDuplicatedError('./foo'), line: 1, column: 56, - message: "'./foo' imported multiple times.", }, ], }), From 967c38ab60d5c2297ab5b6f2ef2ecaf7fc6b7efe Mon Sep 17 00:00:00 2001 From: Marco Pasqualetti Date: Mon, 18 Nov 2024 09:31:20 +0100 Subject: [PATCH 21/49] test(no-dynamic-require): migrate to `createRuleTestCaseFunctions` --- .../consistent-type-specifier-style.spec.ts | 8 +- test/rules/default.spec.ts | 4 +- test/rules/named.spec.ts | 6 +- test/rules/namespace.spec.ts | 8 +- .../rules/no-anonymous-default-export.spec.ts | 4 +- test/rules/no-duplicates.spec.ts | 6 +- test/rules/no-dynamic-require.spec.ts | 80 +++++++++---------- test/utils.ts | 2 +- 8 files changed, 56 insertions(+), 62 deletions(-) diff --git a/test/rules/consistent-type-specifier-style.spec.ts b/test/rules/consistent-type-specifier-style.spec.ts index 2cdfa856c..b9e5d7628 100644 --- a/test/rules/consistent-type-specifier-style.spec.ts +++ b/test/rules/consistent-type-specifier-style.spec.ts @@ -2,13 +2,13 @@ import { RuleTester as TSESLintRuleTester } from '@typescript-eslint/rule-tester import { TSESTree } from '@typescript-eslint/utils' import { parsers, createRuleTestCaseFunctions } from '../utils' -import type { RunTests } from '../utils' +import type { RuleRunTests } from '../utils' import rule from 'eslint-plugin-import-x/rules/consistent-type-specifier-style' const { tValid } = createRuleTestCaseFunctions() -const COMMON_TESTS: RunTests = { +const COMMON_TESTS: RuleRunTests = { valid: [ // // prefer-top-level @@ -251,7 +251,7 @@ import { ], } as const -const TS_ONLY: RunTests = { +const TS_ONLY: RuleRunTests = { valid: [ // // always valid @@ -261,7 +261,7 @@ const TS_ONLY: RunTests = { invalid: [], } -const FLOW_ONLY: RunTests = { +const FLOW_ONLY: RuleRunTests = { valid: [ // // prefer-top-level diff --git a/test/rules/default.spec.ts b/test/rules/default.spec.ts index b33742d12..54a4688d0 100644 --- a/test/rules/default.spec.ts +++ b/test/rules/default.spec.ts @@ -7,7 +7,7 @@ import { SYNTAX_VALID_CASES, parsers, } from '../utils' -import type { RunTests } from '../utils' +import type { RuleRunTests } from '../utils' import rule from 'eslint-plugin-import-x/rules/default' import { CASE_SENSITIVE_FS } from 'eslint-plugin-import-x/utils' @@ -124,7 +124,7 @@ ruleTester.run('default', rule, { }, }), - ...(SYNTAX_VALID_CASES as RunTests['valid']), + ...(SYNTAX_VALID_CASES as RuleRunTests['valid']), ], invalid: [ diff --git a/test/rules/named.spec.ts b/test/rules/named.spec.ts index 4ffdaff70..798e2687f 100644 --- a/test/rules/named.spec.ts +++ b/test/rules/named.spec.ts @@ -10,7 +10,7 @@ import { testFilePath, parsers, } from '../utils' -import type { RunTests, GetRuleModuleMessageIds } from '../utils' +import type { RuleRunTests, GetRuleModuleMessageIds } from '../utils' import rule from 'eslint-plugin-import-x/rules/named' import { CASE_SENSITIVE_FS } from 'eslint-plugin-import-x/utils' @@ -479,7 +479,7 @@ describe('TypeScript', () => { 'import-x/resolver': { 'eslint-import-resolver-typescript': true }, } - let valid: RunTests['valid'] = [ + let valid: RuleRunTests['valid'] = [ tValid({ code: `import x from './typescript-export-assign-object'`, languageOptions: { @@ -494,7 +494,7 @@ describe('TypeScript', () => { }), ] - let invalid: RunTests['invalid'] = [ + let invalid: RuleRunTests['invalid'] = [ // TODO: uncomment this test // tInvalid({ // code: `import {a} from './export-star-3/b';`, diff --git a/test/rules/namespace.spec.ts b/test/rules/namespace.spec.ts index 115646e97..eab032d56 100644 --- a/test/rules/namespace.spec.ts +++ b/test/rules/namespace.spec.ts @@ -8,7 +8,7 @@ import { testFilePath, parsers, } from '../utils' -import type { GetRuleModuleMessageIds, RunTests } from '../utils' +import type { GetRuleModuleMessageIds, RuleRunTests } from '../utils' import rule from 'eslint-plugin-import-x/rules/namespace' @@ -42,7 +42,7 @@ function createNotFoundInNamespaceDeepError( } } -let valid: RunTests['valid'] = [ +let valid: RuleRunTests['valid'] = [ tValid({ code: 'import "./malformed.js"', languageOptions: { parser: require(parsers.ESPREE) }, @@ -252,7 +252,7 @@ let valid: RunTests['valid'] = [ }, }), - ...(SYNTAX_VALID_CASES as RunTests['valid']), + ...(SYNTAX_VALID_CASES as RuleRunTests['valid']), tValid({ code: ` @@ -320,7 +320,7 @@ let valid: RunTests['valid'] = [ }), ] -let invalid: RunTests['invalid'] = [ +let invalid: RuleRunTests['invalid'] = [ tInvalid({ code: "import * as names from './named-exports'; console.log(names.c)", errors: [createNotFoundInNamespaceError('c', 'names')], diff --git a/test/rules/no-anonymous-default-export.spec.ts b/test/rules/no-anonymous-default-export.spec.ts index 74b0e0f34..c83d745da 100644 --- a/test/rules/no-anonymous-default-export.spec.ts +++ b/test/rules/no-anonymous-default-export.spec.ts @@ -6,7 +6,7 @@ import { parsers, createRuleTestCaseFunctions, } from '../utils' -import type { GetRuleModuleMessageIds, RunTests } from '../utils' +import type { GetRuleModuleMessageIds, RuleRunTests } from '../utils' import rule from 'eslint-plugin-import-x/rules/no-anonymous-default-export' @@ -79,7 +79,7 @@ ruleTester.run('no-anonymous-default-export', rule, { // Allow call expressions by default for backwards compatibility tValid({ code: 'export default foo(bar)' }), - ...(SYNTAX_VALID_CASES as RunTests['valid']), + ...(SYNTAX_VALID_CASES as RuleRunTests['valid']), ], invalid: [ diff --git a/test/rules/no-duplicates.spec.ts b/test/rules/no-duplicates.spec.ts index 910923e9a..b941d1ec8 100644 --- a/test/rules/no-duplicates.spec.ts +++ b/test/rules/no-duplicates.spec.ts @@ -9,7 +9,7 @@ import { tsVersionSatisfies, typescriptEslintParserSatisfies, } from '../utils' -import type { GetRuleModuleMessageIds, RunTests } from '../utils' +import type { GetRuleModuleMessageIds, RuleRunTests } from '../utils' import jsxConfig from 'eslint-plugin-import-x/config/react' import rule from 'eslint-plugin-import-x/rules/no-duplicates' @@ -543,7 +543,7 @@ describe('TypeScript', () => { }, } - const valid: RunTests['valid'] = [ + const valid: RuleRunTests['valid'] = [ // #1667: ignore duplicate if is a typescript type import tValid({ code: "import type { x } from './foo'; import y from './foo'", @@ -621,7 +621,7 @@ describe('TypeScript', () => { }), ] - const invalid: RunTests['invalid'] = [ + const invalid: RuleRunTests['invalid'] = [ tInvalid({ code: "import type x from './foo'; import type y from './foo'", ...parserConfig, diff --git a/test/rules/no-dynamic-require.spec.ts b/test/rules/no-dynamic-require.spec.ts index 93b8f373e..d834ac3ee 100644 --- a/test/rules/no-dynamic-require.spec.ts +++ b/test/rules/no-dynamic-require.spec.ts @@ -1,38 +1,34 @@ import { RuleTester as TSESLintRuleTester } from '@typescript-eslint/rule-tester' -import { parsers, test } from '../utils' -import type { ValidTestCase } from '../utils' +import { parsers, createRuleTestCaseFunctions } from '../utils' import rule from 'eslint-plugin-import-x/rules/no-dynamic-require' const ruleTester = new TSESLintRuleTester() -const error = { - messageId: 'require', -} as const +const { tValid, tInvalid } = createRuleTestCaseFunctions() -const dynamicImportError = { - messageId: 'import', -} as const +const REQUIRE_ERROR = { messageId: 'require' } as const +const DYNAMIC_IMPORT_ERROR = { messageId: 'import' } as const ruleTester.run('no-dynamic-require', rule, { valid: [ - test({ code: 'import _ from "lodash"' }), - test({ code: 'require("foo")' }), - test({ code: 'require(`foo`)' }), - test({ code: 'require("./foo")' }), - test({ code: 'require("@scope/foo")' }), - test({ code: 'require()' }), - test({ code: 'require("./foo", "bar" + "okay")' }), - test({ code: 'var foo = require("foo")' }), - test({ code: 'var foo = require(`foo`)' }), - test({ code: 'var foo = require("./foo")' }), - test({ code: 'var foo = require("@scope/foo")' }), + tValid({ code: 'import _ from "lodash"' }), + tValid({ code: 'require("foo")' }), + tValid({ code: 'require(`foo`)' }), + tValid({ code: 'require("./foo")' }), + tValid({ code: 'require("@scope/foo")' }), + tValid({ code: 'require()' }), + tValid({ code: 'require("./foo", "bar" + "okay")' }), + tValid({ code: 'var foo = require("foo")' }), + tValid({ code: 'var foo = require(`foo`)' }), + tValid({ code: 'var foo = require("./foo")' }), + tValid({ code: 'var foo = require("@scope/foo")' }), //dynamic import ...[parsers.ESPREE, parsers.BABEL].flatMap($parser => { - const _test = (testObj: T) => - $parser === parsers.ESPREE ? testObj : test(testObj) + const _test: typeof tValid = testObj => + $parser === parsers.ESPREE ? testObj : tValid(testObj) const parser = require($parser) @@ -119,7 +115,6 @@ ruleTester.run('no-dynamic-require', rule, { }), _test({ code: 'import("../" + name)', - errors: [dynamicImportError], languageOptions: { parser, parserOptions: { @@ -129,7 +124,6 @@ ruleTester.run('no-dynamic-require', rule, { }), _test({ code: 'import(`../${name}`)', - errors: [dynamicImportError], languageOptions: { parser, parserOptions: { @@ -141,39 +135,39 @@ ruleTester.run('no-dynamic-require', rule, { }), ], invalid: [ - test({ + tInvalid({ code: 'require("../" + name)', - errors: [error], + errors: [REQUIRE_ERROR], }), - test({ + tInvalid({ code: 'require(`../${name}`)', - errors: [error], + errors: [REQUIRE_ERROR], }), - test({ + tInvalid({ code: 'require(name)', - errors: [error], + errors: [REQUIRE_ERROR], }), - test({ + tInvalid({ code: 'require(name())', - errors: [error], + errors: [REQUIRE_ERROR], }), - test({ + tInvalid({ code: 'require(name + "foo", "bar")', - errors: [error], + errors: [REQUIRE_ERROR], options: [{ esmodule: true }], }), // dynamic import ...[parsers.ESPREE, parsers.BABEL].flatMap($parser => { - const _test = (testObj: T) => - $parser === parsers.ESPREE ? testObj : test(testObj) + const _test: typeof tInvalid = testObj => + $parser === parsers.ESPREE ? testObj : tInvalid(testObj) const parser = require($parser) return [ _test({ code: 'import("../" + name)', - errors: [dynamicImportError], + errors: [DYNAMIC_IMPORT_ERROR], options: [{ esmodule: true }], languageOptions: { parser, @@ -184,7 +178,7 @@ ruleTester.run('no-dynamic-require', rule, { }), _test({ code: 'import(`../${name}`)', - errors: [dynamicImportError], + errors: [DYNAMIC_IMPORT_ERROR], options: [{ esmodule: true }], languageOptions: { parser, @@ -195,7 +189,7 @@ ruleTester.run('no-dynamic-require', rule, { }), _test({ code: 'import(name)', - errors: [dynamicImportError], + errors: [DYNAMIC_IMPORT_ERROR], options: [{ esmodule: true }], languageOptions: { parser, @@ -206,7 +200,7 @@ ruleTester.run('no-dynamic-require', rule, { }), _test({ code: 'import(name())', - errors: [dynamicImportError], + errors: [DYNAMIC_IMPORT_ERROR], options: [{ esmodule: true }], languageOptions: { parser, @@ -217,13 +211,13 @@ ruleTester.run('no-dynamic-require', rule, { }), ] }), - test({ + tInvalid({ code: 'require(`foo${x}`)', - errors: [error], + errors: [REQUIRE_ERROR], }), - test({ + tInvalid({ code: 'var foo = require(`foo${x}`)', - errors: [error], + errors: [REQUIRE_ERROR], }), ], }) diff --git a/test/utils.ts b/test/utils.ts index a501605e6..31cd7885b 100644 --- a/test/utils.ts +++ b/test/utils.ts @@ -128,7 +128,7 @@ export type GetRuleModuleOptions = * ] * ``` */ -export type RunTests< +export type RuleRunTests< TRule extends RuleModule, TRuleType extends GetRuleModuleTypes = GetRuleModuleTypes, > = TSESLintRunTests From 7741c150959b22532d9335eac267d0382c41b107 Mon Sep 17 00:00:00 2001 From: Marco Pasqualetti Date: Tue, 19 Nov 2024 06:55:52 +0100 Subject: [PATCH 22/49] test(no-empty-named-blocks): migrate to `createRuleTestCaseFunctions` --- test/rules/no-empty-named-blocks.spec.ts | 57 +++++++++++++----------- test/utils.ts | 1 - 2 files changed, 30 insertions(+), 28 deletions(-) diff --git a/test/rules/no-empty-named-blocks.spec.ts b/test/rules/no-empty-named-blocks.spec.ts index cabdeae81..166e5604b 100644 --- a/test/rules/no-empty-named-blocks.spec.ts +++ b/test/rules/no-empty-named-blocks.spec.ts @@ -1,19 +1,22 @@ import { RuleTester as TSESLintRuleTester } from '@typescript-eslint/rule-tester' +import type { Parser as TSESLintParser } from '@typescript-eslint/utils/ts-eslint' -import { parsers, test } from '../utils' +import { parsers, createRuleTestCaseFunctions } from '../utils' import rule from 'eslint-plugin-import-x/rules/no-empty-named-blocks' const ruleTester = new TSESLintRuleTester() -// eslint-disable-next-line @typescript-eslint/no-explicit-any -function generateSuggestionsTestCases(cases: string[], parser?: any) { +const { tValid, tInvalid } = createRuleTestCaseFunctions() + +function generateSuggestionsTestCases( + cases: string[], + parser?: TSESLintParser.LooseParserModule, +): Array> { return cases.map(code => - test({ + tInvalid({ code, - languageOptions: { - ...(parser && { parser }), - }, + languageOptions: { parser }, errors: [ { messageId: 'emptyNamed', @@ -35,38 +38,38 @@ function generateSuggestionsTestCases(cases: string[], parser?: any) { ruleTester.run('no-empty-named-blocks', rule, { valid: [ - test({ code: `import 'mod';` }), - test({ code: `import Default from 'mod';` }), - test({ code: `import { Named } from 'mod';` }), - test({ code: `import Default, { Named } from 'mod';` }), - test({ code: `import * as Namespace from 'mod';` }), + tValid({ code: `import 'mod';` }), + tValid({ code: `import Default from 'mod';` }), + tValid({ code: `import { Named } from 'mod';` }), + tValid({ code: `import Default, { Named } from 'mod';` }), + tValid({ code: `import * as Namespace from 'mod';` }), // Typescript - test({ code: `import type Default from 'mod';` }), - test({ + tValid({ code: `import type Default from 'mod';` }), + tValid({ code: `import type { Named } from 'mod';`, }), - test({ + tValid({ code: `import type Default, { Named } from 'mod';`, }), - test({ + tValid({ code: `import type * as Namespace from 'mod';`, }), // Flow - test({ + tValid({ code: `import typeof Default from 'mod'; // babel old`, languageOptions: { parser: require(parsers.BABEL) }, }), - test({ + tValid({ code: `import typeof { Named } from 'mod'; // babel old`, languageOptions: { parser: require(parsers.BABEL) }, }), - test({ + tValid({ code: `import typeof Default, { Named } from 'mod'; // babel old`, languageOptions: { parser: require(parsers.BABEL) }, }), - test({ + tValid({ code: ` module.exports = { rules: { @@ -75,7 +78,7 @@ ruleTester.run('no-empty-named-blocks', rule, { }; `, }), - test({ + tValid({ code: ` import { DESCRIPTORS, NODE } from '../helpers/constants'; // ... @@ -86,10 +89,10 @@ ruleTester.run('no-empty-named-blocks', rule, { }), ], invalid: [ - test({ + tInvalid({ code: `import Default, {} from 'mod';`, output: `import Default from 'mod';`, - errors: ['Unexpected empty named import block'], + errors: [{ messageId: 'emptyNamed' }], }), ...generateSuggestionsTestCases([ `import {} from 'mod';`, @@ -105,11 +108,11 @@ ruleTester.run('no-empty-named-blocks', rule, { `import type{}from 'mod';`, `import type {}from'mod';`, ]), - test({ + tInvalid({ code: `import type Default, {} from 'mod';`, output: `import type Default from 'mod';`, - errors: ['Unexpected empty named import block'], + errors: [{ messageId: 'emptyNamed' }], }), // Flow @@ -122,11 +125,11 @@ ruleTester.run('no-empty-named-blocks', rule, { ], require(parsers.BABEL), ), - test({ + tInvalid({ code: `import typeof Default, {} from 'mod';`, output: `import typeof Default from 'mod';`, languageOptions: { parser: require(parsers.BABEL) }, - errors: ['Unexpected empty named import block'], + errors: [{ messageId: 'emptyNamed' }], }), ], }) diff --git a/test/utils.ts b/test/utils.ts index 31cd7885b..65064296e 100644 --- a/test/utils.ts +++ b/test/utils.ts @@ -82,7 +82,6 @@ export type InvalidTestCaseError = }) /** @deprecated use {@link createRuleTestCaseFunctions} */ -// eslint-disable-next-line eslint-plugin/require-meta-docs-description, eslint-plugin/require-meta-type, eslint-plugin/prefer-message-ids, eslint-plugin/prefer-object-rule, eslint-plugin/require-meta-schema export function test( t: T, ): T extends { errors: InvalidTestCaseError[] | number } From cdf91f92d3019dae2fea0cf3ddeef721be84863c Mon Sep 17 00:00:00 2001 From: Marco Pasqualetti Date: Tue, 19 Nov 2024 07:06:28 +0100 Subject: [PATCH 23/49] chore(eslint): enable `reportUnusedDisableDirectives` --- .eslintrc.js | 1 + src/utils/pkg-up.ts | 1 - src/utils/read-pkg-up.ts | 1 - 3 files changed, 1 insertion(+), 2 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index 54a81f13f..93dfb07dc 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -3,6 +3,7 @@ */ module.exports = { root: true, + reportUnusedDisableDirectives: true, extends: [ 'eslint:recommended', 'plugin:@typescript-eslint/recommended', diff --git a/src/utils/pkg-up.ts b/src/utils/pkg-up.ts index b6fe51252..83b44eeec 100644 --- a/src/utils/pkg-up.ts +++ b/src/utils/pkg-up.ts @@ -7,7 +7,6 @@ function findUp(filename: string | string[], cwd?: string): string | null { const filenames = [filename].flat() - // eslint-disable-next-line no-constant-condition while (true) { const file = filenames.find(el => fs.existsSync(path.resolve(dir, el))) diff --git a/src/utils/read-pkg-up.ts b/src/utils/read-pkg-up.ts index c600fcdef..aceaaee32 100644 --- a/src/utils/read-pkg-up.ts +++ b/src/utils/read-pkg-up.ts @@ -8,7 +8,6 @@ function stripBOM(str: string) { return str.replace(/^\uFEFF/, '') } -// eslint-disable-next-line eslint-plugin/require-meta-docs-description, eslint-plugin/require-meta-type, eslint-plugin/prefer-message-ids, eslint-plugin/prefer-object-rule, eslint-plugin/require-meta-schema export function readPkgUp(opts?: { cwd?: string }) { const fp = pkgUp(opts) From 4f9e82b3262e3dad2b856c14b625fdb2c10b3044 Mon Sep 17 00:00:00 2001 From: Marco Pasqualetti Date: Tue, 19 Nov 2024 07:23:20 +0100 Subject: [PATCH 24/49] test(no-extraneous-dependencies): migrate to `createRuleTestCaseFunctions` --- test/rules/no-extraneous-dependencies.spec.ts | 368 +++++++----------- 1 file changed, 130 insertions(+), 238 deletions(-) diff --git a/test/rules/no-extraneous-dependencies.spec.ts b/test/rules/no-extraneous-dependencies.spec.ts index aa0f0ee73..d2cc5ce00 100644 --- a/test/rules/no-extraneous-dependencies.spec.ts +++ b/test/rules/no-extraneous-dependencies.spec.ts @@ -7,7 +7,7 @@ import { dependencies as deps, devDependencies as devDeps, } from '../fixtures/package.json' -import { parsers, test, testFilePath } from '../utils' +import { parsers, createRuleTestCaseFunctions, testFilePath } from '../utils' import typescriptConfig from 'eslint-plugin-import-x/config/typescript' import rule from 'eslint-plugin-import-x/rules/no-extraneous-dependencies' @@ -15,6 +15,8 @@ import rule from 'eslint-plugin-import-x/rules/no-extraneous-dependencies' const ruleTester = new TSESLintRuleTester() const typescriptRuleTester = new TSESLintRuleTester(typescriptConfig) +const { tValid, tInvalid } = createRuleTestCaseFunctions() + const packageDirWithSyntaxError = testFilePath('with-syntax-error') const packageFileWithSyntaxErrorMessage = (() => { @@ -53,28 +55,28 @@ const emptyPackageDir = testFilePath('empty-folder') ruleTester.run('no-extraneous-dependencies', rule, { valid: [ ...[...Object.keys(deps), ...Object.keys(devDeps)].flatMap(pkg => [ - test({ code: `import "${pkg}"` }), - test({ code: `import foo, { bar } from "${pkg}"` }), - test({ code: `require("${pkg}")` }), - test({ code: `var foo = require("${pkg}")` }), - test({ code: `export { foo } from "${pkg}"` }), - test({ code: `export * from "${pkg}"` }), + tValid({ code: `import "${pkg}"` }), + tValid({ code: `import foo, { bar } from "${pkg}"` }), + tValid({ code: `require("${pkg}")` }), + tValid({ code: `var foo = require("${pkg}")` }), + tValid({ code: `export { foo } from "${pkg}"` }), + tValid({ code: `export * from "${pkg}"` }), ]), - test({ code: 'import "eslint/lib/api"' }), - test({ code: 'import "fs"' }), - test({ code: 'import "./foo"' }), + tValid({ code: 'import "eslint/lib/api"' }), + tValid({ code: 'import "fs"' }), + tValid({ code: 'import "./foo"' }), - test({ + tValid({ code: 'import "electron"', settings: { 'import-x/core-modules': ['electron'] }, }), - test({ + tValid({ code: 'import "eslint"', options: [{ peerDependencies: true }], }), // 'project' type - test({ + tValid({ code: 'import "importType"', settings: { 'import-x/resolver': { @@ -82,32 +84,32 @@ ruleTester.run('no-extraneous-dependencies', rule, { }, }, }), - test({ + tValid({ code: 'import jest from "jest"', options: [{ devDependencies: ['*.spec.js'] }], filename: 'foo.spec.js', }), - test({ + tValid({ code: 'import jest from "jest"', options: [{ devDependencies: ['*.spec.js'] }], filename: path.resolve('foo.spec.js'), }), - test({ + tValid({ code: 'import jest from "jest"', options: [{ devDependencies: ['*.test.js', '*.spec.js'] }], filename: path.resolve('foo.spec.js'), }), - test({ code: 'require(6)' }), - test({ + tValid({ code: 'require(6)' }), + tValid({ code: 'import "doctrine"', options: [{ packageDir: path.join(__dirname, '../../') }], }), - test({ + tValid({ code: 'import type MyType from "myflowtyped";', options: [{ packageDir: packageDirWithFlowTyped }], languageOptions: { parser: require(parsers.BABEL) }, }), - test({ + tValid({ code: ` // @flow import typeof TypeScriptModule from 'typescript'; @@ -115,88 +117,88 @@ ruleTester.run('no-extraneous-dependencies', rule, { options: [{ packageDir: packageDirWithFlowTyped }], languageOptions: { parser: require(parsers.BABEL) }, }), - test({ + tValid({ code: 'import react from "react";', options: [{ packageDir: packageDirMonoRepoWithNested }], }), - test({ + tValid({ code: 'import leftpad from "left-pad";', options: [ { packageDir: [packageDirMonoRepoWithNested, packageDirMonoRepoRoot] }, ], }), - test({ + tValid({ code: 'import leftpad from "left-pad";', options: [{ packageDir: packageDirMonoRepoRoot }], }), - test({ + tValid({ code: 'import leftpad from "left-pad";', options: [{ packageDir: [emptyPackageDir, packageDirMonoRepoRoot] }], }), - test({ + tValid({ code: 'import leftpad from "left-pad";', options: [{ packageDir: [packageDirMonoRepoRoot, emptyPackageDir] }], }), - test({ + tValid({ code: 'import react from "react";', options: [ { packageDir: [packageDirMonoRepoRoot, packageDirMonoRepoWithNested] }, ], }), - test({ + tValid({ code: 'import leftpad from "left-pad";', options: [ { packageDir: [packageDirMonoRepoRoot, packageDirMonoRepoWithNested] }, ], }), - test({ + tValid({ code: 'import rightpad from "right-pad";', options: [ { packageDir: [packageDirMonoRepoRoot, packageDirMonoRepoWithNested] }, ], }), - test({ code: 'import foo from "@generated/foo"' }), - test({ + tValid({ code: 'import foo from "@generated/foo"' }), + tValid({ code: 'import foo from "@generated/foo"', options: [{ packageDir: packageDirBundleDeps }], }), - test({ + tValid({ code: 'import foo from "@generated/foo"', options: [{ packageDir: packageDirBundledDepsAsObject }], }), - test({ + tValid({ code: 'import foo from "@generated/foo"', options: [{ packageDir: packageDirBundledDepsRaceCondition }], }), - test({ code: 'export function getToken() {}' }), - test({ code: 'export class Component extends React.Component {}' }), - test({ code: 'export function Component() {}' }), - test({ code: 'export const Component = () => {}' }), + tValid({ code: 'export function getToken() {}' }), + tValid({ code: 'export class Component extends React.Component {}' }), + tValid({ code: 'export function Component() {}' }), + tValid({ code: 'export const Component = () => {}' }), - test({ + tValid({ code: 'import "not-a-dependency"', filename: path.join(packageDirMonoRepoRoot, 'foo.js'), options: [{ packageDir: packageDirMonoRepoRoot }], settings: { 'import-x/core-modules': ['not-a-dependency'] }, }), - test({ + tValid({ code: 'import "@generated/bar/module"', settings: { 'import-x/core-modules': ['@generated/bar'] }, }), - test({ + tValid({ code: 'import "@generated/bar/and/sub/path"', settings: { 'import-x/core-modules': ['@generated/bar'] }, }), // check if "rxjs" dependency declaration fix the "rxjs/operators subpackage - test({ + tValid({ code: 'import "rxjs/operators"', }), - test({ + tValid({ code: 'import "esm-package/esm-module";', }), - test({ + tValid({ code: ` import "alias/esm-package/esm-module"; import 'expose-loader?exposes[]=$&exposes[]=jQuery!jquery'; @@ -204,7 +206,7 @@ ruleTester.run('no-extraneous-dependencies', rule, { settings: { 'import-x/resolver': 'webpack' }, }), - test({ + tValid({ code: 'import "@custom-internal-alias/api/service";', settings: { 'import-x/resolver': { @@ -221,7 +223,7 @@ ruleTester.run('no-extraneous-dependencies', rule, { }, }), - test({ + tValid({ code: 'import "not-a-dependency"', filename: path.join(packageDirMonoRepoRoot, 'foo.js'), options: [ @@ -230,289 +232,200 @@ ruleTester.run('no-extraneous-dependencies', rule, { }), ], invalid: [ - test({ + tInvalid({ code: 'import "not-a-dependency"', filename: path.join(packageDirMonoRepoRoot, 'foo.js'), options: [{ packageDir: packageDirMonoRepoRoot }], errors: [ - { - message: - "'not-a-dependency' should be listed in the project's dependencies. Run 'npm i -S not-a-dependency' to add it", - }, + { messageId: 'missing', data: { packageName: 'not-a-dependency' } }, ], }), - test({ + tInvalid({ code: 'import "not-a-dependency"', filename: path.join(packageDirMonoRepoWithNested, 'foo.js'), options: [{ packageDir: packageDirMonoRepoRoot }], errors: [ - { - message: - "'not-a-dependency' should be listed in the project's dependencies. Run 'npm i -S not-a-dependency' to add it", - }, + { messageId: 'missing', data: { packageName: 'not-a-dependency' } }, ], }), - test({ + tInvalid({ code: 'import "not-a-dependency"', options: [{ packageDir: packageDirMonoRepoRoot }], errors: [ - { - message: - "'not-a-dependency' should be listed in the project's dependencies. Run 'npm i -S not-a-dependency' to add it", - }, + { messageId: 'missing', data: { packageName: 'not-a-dependency' } }, ], }), - test({ + tInvalid({ code: 'import "not-a-dependency"', errors: [ - { - message: - "'not-a-dependency' should be listed in the project's dependencies. Run 'npm i -S not-a-dependency' to add it", - }, + { messageId: 'missing', data: { packageName: 'not-a-dependency' } }, ], }), - test({ + tInvalid({ code: 'var donthaveit = require("@org/not-a-dependency")', errors: [ { - message: - "'@org/not-a-dependency' should be listed in the project's dependencies. Run 'npm i -S @org/not-a-dependency' to add it", + messageId: 'missing', + data: { packageName: '@org/not-a-dependency' }, }, ], }), - test({ + tInvalid({ code: 'var donthaveit = require("@org/not-a-dependency/foo")', errors: [ { - message: - "'@org/not-a-dependency' should be listed in the project's dependencies. Run 'npm i -S @org/not-a-dependency' to add it", + messageId: 'missing', + data: { packageName: '@org/not-a-dependency' }, }, ], }), - test({ + tInvalid({ code: 'import "eslint"', options: [{ devDependencies: false, peerDependencies: false }], - errors: [ - { - message: - "'eslint' should be listed in the project's dependencies, not devDependencies.", - }, - ], + errors: [{ messageId: 'devDep', data: { packageName: 'eslint' } }], }), - test({ + tInvalid({ code: 'import "lodash"', options: [{ optionalDependencies: false }], - errors: [ - { - message: - "'lodash' should be listed in the project's dependencies, not optionalDependencies.", - }, - ], + errors: [{ messageId: 'optDep', data: { packageName: 'lodash' } }], }), - test({ + tInvalid({ code: 'var foo = require("not-a-dependency")', errors: [ - { - message: - "'not-a-dependency' should be listed in the project's dependencies. Run 'npm i -S not-a-dependency' to add it", - }, + { messageId: 'missing', data: { packageName: 'not-a-dependency' } }, ], }), - test({ + tInvalid({ code: 'var glob = require("glob")', options: [{ devDependencies: false }], - errors: [ - { - message: - "'glob' should be listed in the project's dependencies, not devDependencies.", - }, - ], + errors: [{ messageId: 'devDep', data: { packageName: 'glob' } }], }), - test({ + tInvalid({ code: 'import jest from "jest"', options: [{ devDependencies: ['*.test.js'] }], filename: 'foo.tes.js', - errors: [ - { - message: - "'jest' should be listed in the project's dependencies, not devDependencies.", - }, - ], + errors: [{ messageId: 'devDep', data: { packageName: 'jest' } }], }), - test({ + tInvalid({ code: 'import jest from "jest"', options: [{ devDependencies: ['*.test.js'] }], filename: path.resolve('foo.tes.js'), - errors: [ - { - message: - "'jest' should be listed in the project's dependencies, not devDependencies.", - }, - ], + errors: [{ messageId: 'devDep', data: { packageName: 'jest' } }], }), - test({ + tInvalid({ code: 'import jest from "jest"', options: [{ devDependencies: ['*.test.js', '*.spec.js'] }], filename: 'foo.tes.js', - errors: [ - { - message: - "'jest' should be listed in the project's dependencies, not devDependencies.", - }, - ], + errors: [{ messageId: 'devDep', data: { packageName: 'jest' } }], }), - test({ + tInvalid({ code: 'import jest from "jest"', options: [{ devDependencies: ['*.test.js', '*.spec.js'] }], filename: path.resolve('foo.tes.js'), - errors: [ - { - message: - "'jest' should be listed in the project's dependencies, not devDependencies.", - }, - ], + errors: [{ messageId: 'devDep', data: { packageName: 'jest' } }], }), - test({ + tInvalid({ code: 'var eslint = require("lodash")', options: [{ optionalDependencies: false }], - errors: [ - { - message: - "'lodash' should be listed in the project's dependencies, not optionalDependencies.", - }, - ], + errors: [{ messageId: 'optDep', data: { packageName: 'lodash' } }], }), - test({ + tInvalid({ code: 'import "not-a-dependency"', options: [{ packageDir: path.join(__dirname, '../../') }], errors: [ - { - message: - "'not-a-dependency' should be listed in the project's dependencies. Run 'npm i -S not-a-dependency' to add it", - }, + { messageId: 'missing', data: { packageName: 'not-a-dependency' } }, ], }), - test({ + tInvalid({ code: 'import "bar"', options: [{ packageDir: path.join(__dirname, './doesn-exist/') }], - errors: [ - { - message: 'The package.json file could not be found.', - }, - ], + errors: [{ messageId: 'pkgNotFound' }], }), - test({ + tInvalid({ code: 'import foo from "foo"', options: [{ packageDir: packageDirWithSyntaxError }], errors: [ { - message: `The package.json file could not be parsed: ${packageFileWithSyntaxErrorMessage}`, + messageId: 'pkgUnparsable', + data: { error: packageFileWithSyntaxErrorMessage }, }, ], }), - test({ + tInvalid({ code: 'import leftpad from "left-pad";', filename: path.join(packageDirMonoRepoWithNested, 'foo.js'), options: [{ packageDir: packageDirMonoRepoWithNested }], - errors: [ - { - message: - "'left-pad' should be listed in the project's dependencies. Run 'npm i -S left-pad' to add it", - }, - ], + errors: [{ messageId: 'missing', data: { packageName: 'left-pad' } }], }), - test({ + tInvalid({ code: 'import react from "react";', filename: path.join(packageDirMonoRepoRoot, 'foo.js'), - errors: [ - { - message: - "'react' should be listed in the project's dependencies. Run 'npm i -S react' to add it", - }, - ], + errors: [{ messageId: 'missing', data: { packageName: 'react' } }], }), - test({ + tInvalid({ code: 'import react from "react";', filename: path.join(packageDirMonoRepoWithNested, 'foo.js'), options: [{ packageDir: packageDirMonoRepoRoot }], - errors: [ - { - message: - "'react' should be listed in the project's dependencies. Run 'npm i -S react' to add it", - }, - ], + errors: [{ messageId: 'missing', data: { packageName: 'react' } }], }), - test({ + tInvalid({ code: 'import "react";', filename: path.join(packageDirWithEmpty, 'index.js'), options: [{ packageDir: packageDirWithEmpty }], - errors: [ - { - message: - "'react' should be listed in the project's dependencies. Run 'npm i -S react' to add it", - }, - ], + errors: [{ messageId: 'missing', data: { packageName: 'react' } }], }), - test({ + tInvalid({ code: 'import bar from "@generated/bar"', errors: [ - "'@generated/bar' should be listed in the project's dependencies. Run 'npm i -S @generated/bar' to add it", + { messageId: 'missing', data: { packageName: '@generated/bar' } }, ], }), - test({ + tInvalid({ code: 'import foo from "@generated/foo"', options: [{ bundledDependencies: false }], errors: [ - "'@generated/foo' should be listed in the project's dependencies. Run 'npm i -S @generated/foo' to add it", + { messageId: 'missing', data: { packageName: '@generated/foo' } }, ], }), - test({ + tInvalid({ code: 'import bar from "@generated/bar"', options: [{ packageDir: packageDirBundledDepsRaceCondition }], errors: [ - "'@generated/bar' should be listed in the project's dependencies. Run 'npm i -S @generated/bar' to add it", + { messageId: 'missing', data: { packageName: '@generated/bar' } }, ], }), - test({ + tInvalid({ code: 'export { foo } from "not-a-dependency";', errors: [ - { - message: - "'not-a-dependency' should be listed in the project's dependencies. Run 'npm i -S not-a-dependency' to add it", - }, + { messageId: 'missing', data: { packageName: 'not-a-dependency' } }, ], }), - test({ + tInvalid({ code: 'export * from "not-a-dependency";', errors: [ - { - message: - "'not-a-dependency' should be listed in the project's dependencies. Run 'npm i -S not-a-dependency' to add it", - }, + { messageId: 'missing', data: { packageName: 'not-a-dependency' } }, ], }), - test({ + tInvalid({ code: 'import jest from "alias/jest";', settings: { 'import-x/resolver': 'webpack' }, errors: [ - { - // missing dependency is jest not alias - message: - "'jest' should be listed in the project's dependencies. Run 'npm i -S jest' to add it", - }, + // missing dependency is jest not alias + { messageId: 'missing', data: { packageName: 'jest' } }, ], }), - test({ + tInvalid({ code: 'import "esm-package-not-in-pkg-json/esm-module";', errors: [ { - message: `'esm-package-not-in-pkg-json' should be listed in the project's dependencies. Run 'npm i -S esm-package-not-in-pkg-json' to add it`, + messageId: 'missing', + data: { packageName: 'esm-package-not-in-pkg-json' }, }, ], }), - test({ + tInvalid({ code: 'import "not-a-dependency"', settings: { 'import-x/resolver': { @@ -522,10 +435,7 @@ ruleTester.run('no-extraneous-dependencies', rule, { }, options: [{ includeInternal: true }], errors: [ - { - message: - "'not-a-dependency' should be listed in the project's dependencies. Run 'npm i -S not-a-dependency' to add it", - }, + { messageId: 'missing', data: { packageName: 'not-a-dependency' } }, ], }), ], @@ -541,7 +451,7 @@ describe('TypeScript', () => { ruleTester.run('no-extraneous-dependencies', rule, { valid: [ - test({ + tValid({ code: 'import type T from "a";', options: [ { @@ -552,7 +462,7 @@ describe('TypeScript', () => { ...parserConfig, }), - test({ + tValid({ code: 'import type { T } from "a"; export type { T };', options: [ { @@ -563,7 +473,7 @@ describe('TypeScript', () => { ...parserConfig, }), - test({ + tValid({ code: 'export type { T } from "a";', options: [ { @@ -575,7 +485,7 @@ describe('TypeScript', () => { }), ], invalid: [ - test({ + tInvalid({ code: 'import T from "a";', options: [ { @@ -583,16 +493,11 @@ describe('TypeScript', () => { devDependencies: false, }, ], - errors: [ - { - message: - "'a' should be listed in the project's dependencies, not devDependencies.", - }, - ], + errors: [{ messageId: 'devDep', data: { packageName: 'a' } }], ...parserConfig, }), - test({ + tInvalid({ code: 'import type T from "a";', options: [ { @@ -601,12 +506,7 @@ describe('TypeScript', () => { includeTypes: true, }, ], - errors: [ - { - message: - "'a' should be listed in the project's dependencies, not devDependencies.", - }, - ], + errors: [{ messageId: 'devDep', data: { packageName: 'a' } }], ...parserConfig, }), ], @@ -618,68 +518,60 @@ typescriptRuleTester.run( rule, { valid: [ - test({ + tValid({ code: 'import type MyType from "not-a-dependency";', filename: testFilePath('./no-unused-modules/typescript/file-ts-a.ts'), languageOptions: { parser: require(parsers.BABEL) }, }), - test({ + tValid({ code: 'import type { MyType } from "not-a-dependency";', filename: testFilePath('./no-unused-modules/typescript/file-ts-a.ts'), languageOptions: { parser: require(parsers.BABEL) }, }), - test({ + tValid({ code: 'import { type MyType } from "not-a-dependency";', filename: testFilePath('./no-unused-modules/typescript/file-ts-a.ts'), languageOptions: { parser: require(parsers.BABEL) }, }), - test({ + tValid({ code: 'import { type MyType, type OtherType } from "not-a-dependency";', filename: testFilePath('./no-unused-modules/typescript/file-ts-a.ts'), languageOptions: { parser: require(parsers.BABEL) }, }), ], invalid: [ - test({ + tInvalid({ code: 'import type { MyType } from "not-a-dependency";', options: [{ includeTypes: true }], filename: testFilePath('./no-unused-modules/typescript/file-ts-a.ts'), languageOptions: { parser: require(parsers.BABEL) }, errors: [ - { - message: `'not-a-dependency' should be listed in the project's dependencies. Run 'npm i -S not-a-dependency' to add it`, - }, + { messageId: 'missing', data: { packageName: 'not-a-dependency' } }, ], }), - test({ + tInvalid({ code: `import type { Foo } from 'not-a-dependency';`, options: [{ includeTypes: true }], filename: testFilePath('./no-unused-modules/typescript/file-ts-a.ts'), languageOptions: { parser: require(parsers.BABEL) }, errors: [ - { - message: `'not-a-dependency' should be listed in the project's dependencies. Run 'npm i -S not-a-dependency' to add it`, - }, + { messageId: 'missing', data: { packageName: 'not-a-dependency' } }, ], }), - test({ + tInvalid({ code: 'import Foo, { type MyType } from "not-a-dependency";', filename: testFilePath('./no-unused-modules/typescript/file-ts-a.ts'), languageOptions: { parser: require(parsers.BABEL) }, errors: [ - { - message: `'not-a-dependency' should be listed in the project's dependencies. Run 'npm i -S not-a-dependency' to add it`, - }, + { messageId: 'missing', data: { packageName: 'not-a-dependency' } }, ], }), - test({ + tInvalid({ code: 'import { type MyType, Foo } from "not-a-dependency";', filename: testFilePath('./no-unused-modules/typescript/file-ts-a.ts'), languageOptions: { parser: require(parsers.BABEL) }, errors: [ - { - message: `'not-a-dependency' should be listed in the project's dependencies. Run 'npm i -S not-a-dependency' to add it`, - }, + { messageId: 'missing', data: { packageName: 'not-a-dependency' } }, ], }), ], From 1c5add5ecf52d0858056163085ae84419ce53575 Mon Sep 17 00:00:00 2001 From: Marco Pasqualetti Date: Tue, 19 Nov 2024 07:26:08 +0100 Subject: [PATCH 25/49] test(no-import-module-exports): migrate to `createRuleTestCaseFunctions` --- test/rules/no-import-module-exports.spec.ts | 47 +++++++++++---------- 1 file changed, 25 insertions(+), 22 deletions(-) diff --git a/test/rules/no-import-module-exports.spec.ts b/test/rules/no-import-module-exports.spec.ts index f3096dc4c..6ce41a665 100644 --- a/test/rules/no-import-module-exports.spec.ts +++ b/test/rules/no-import-module-exports.spec.ts @@ -1,8 +1,9 @@ import path from 'node:path' import { RuleTester as TSESLintRuleTester } from '@typescript-eslint/rule-tester' +import { AST_NODE_TYPES } from '@typescript-eslint/utils' -import { test } from '../utils' +import { createRuleTestCaseFunctions } from '../utils' import rule from 'eslint-plugin-import-x/rules/no-import-module-exports' @@ -12,44 +13,46 @@ const ruleTester = new TSESLintRuleTester({ }, }) -const error = { +const { tValid, tInvalid } = createRuleTestCaseFunctions() + +const NOT_ALLOWED_ERROR = { messageId: 'notAllowed', - type: 'ImportDeclaration', + type: AST_NODE_TYPES.ImportDeclaration, } as const ruleTester.run('no-import-module-exports', rule, { valid: [ - test({ + tValid({ code: ` const thing = require('thing') module.exports = thing `, }), - test({ + tValid({ code: ` import thing from 'otherthing' console.log(thing.module.exports) `, }), - test({ + tValid({ code: ` import thing from 'other-thing' export default thing `, }), - test({ + tValid({ code: ` const thing = require('thing') exports.foo = bar `, }), - test({ + tValid({ code: ` import { module } from 'qunit' module.skip('A test', function () {}) `, }), - test({ + tValid({ code: ` import foo from 'path'; module.exports = foo; @@ -58,7 +61,7 @@ ruleTester.run('no-import-module-exports', rule, { // See test/fixtures/package.json filename: path.resolve('test/fixtures/index.js'), }), - test({ + tValid({ code: ` import foo from 'path'; module.exports = foo; @@ -69,7 +72,7 @@ ruleTester.run('no-import-module-exports', rule, { ), options: [{ exceptions: ['**/*/other/entry-point.js'] }], }), - test({ + tValid({ code: ` import * as process from 'process'; console.log(process.env); @@ -79,7 +82,7 @@ ruleTester.run('no-import-module-exports', rule, { 'test/fixtures/missing-entrypoint/cli.js', ), }), - test({ + tValid({ code: ` import fs from 'fs/promises'; @@ -133,43 +136,43 @@ ruleTester.run('no-import-module-exports', rule, { }), ], invalid: [ - test({ + tInvalid({ code: ` import { stuff } from 'starwars' module.exports = thing `, - errors: [error], + errors: [NOT_ALLOWED_ERROR], }), - test({ + tInvalid({ code: ` import thing from 'starwars' const baz = module.exports = thing console.log(baz) `, - errors: [error], + errors: [NOT_ALLOWED_ERROR], }), - test({ + tInvalid({ code: ` import * as allThings from 'starwars' exports.bar = thing `, - errors: [error], + errors: [NOT_ALLOWED_ERROR], }), - test({ + tInvalid({ code: ` import thing from 'other-thing' exports.foo = bar `, - errors: [error], + errors: [NOT_ALLOWED_ERROR], }), - test({ + tInvalid({ code: ` import foo from 'path'; module.exports = foo; `, filename: path.resolve('test/fixtures/some/other/entry-point.js'), options: [{ exceptions: ['**/*/other/file.js'] }], - errors: [error], + errors: [NOT_ALLOWED_ERROR], }), ], }) From 2ca670d0290e84919177314f86431e8658d9913e Mon Sep 17 00:00:00 2001 From: Marco Pasqualetti Date: Tue, 19 Nov 2024 07:52:17 +0100 Subject: [PATCH 26/49] test(no-internal-modules): migrate to `createRuleTestCaseFunctions` --- test/rules/no-internal-modules.spec.ts | 249 ++++++++++--------------- 1 file changed, 99 insertions(+), 150 deletions(-) diff --git a/test/rules/no-internal-modules.spec.ts b/test/rules/no-internal-modules.spec.ts index c683c2fdc..5767bde60 100644 --- a/test/rules/no-internal-modules.spec.ts +++ b/test/rules/no-internal-modules.spec.ts @@ -1,44 +1,57 @@ import { RuleTester as TSESLintRuleTester } from '@typescript-eslint/rule-tester' +import type { TestCaseError as TSESLintTestCaseError } from '@typescript-eslint/rule-tester' -import { test, testFilePath } from '../utils' +import { createRuleTestCaseFunctions, testFilePath } from '../utils' +import type { GetRuleModuleMessageIds } from '../utils' import rule from 'eslint-plugin-import-x/rules/no-internal-modules' const ruleTester = new TSESLintRuleTester() +const { tValid, tInvalid } = createRuleTestCaseFunctions() + +function createNotAllowedError( + importPath: string, +): TSESLintTestCaseError> { + return { + messageId: 'noAllowed', + data: { importPath }, + } +} + ruleTester.run('no-internal-modules', rule, { valid: [ // imports - test({ + tValid({ code: 'import a from "./plugin2"', filename: testFilePath('./internal-modules/plugins/plugin.js'), options: [], }), - test({ + tValid({ code: 'const a = require("./plugin2")', filename: testFilePath('./internal-modules/plugins/plugin.js'), }), - test({ + tValid({ code: 'const a = require("./plugin2/")', filename: testFilePath('./internal-modules/plugins/plugin.js'), }), - test({ + tValid({ code: 'const dynamic = "./plugin2/"; const a = require(dynamic)', filename: testFilePath('./internal-modules/plugins/plugin.js'), }), - test({ + tValid({ code: 'import b from "./internal.js"', filename: testFilePath('./internal-modules/plugins/plugin2/index.js'), }), - test({ + tValid({ code: 'import get from "lodash.get"', filename: testFilePath('./internal-modules/plugins/plugin2/index.js'), }), - test({ + tValid({ code: 'import b from "@org/package"', filename: testFilePath('./internal-modules/plugins/plugin2/internal.js'), }), - test({ + tValid({ code: 'import b from "../../api/service"', filename: testFilePath('./internal-modules/plugins/plugin2/internal.js'), options: [ @@ -47,7 +60,7 @@ ruleTester.run('no-internal-modules', rule, { }, ], }), - test({ + tValid({ code: 'import "jquery/dist/jquery"', filename: testFilePath('./internal-modules/plugins/plugin2/internal.js'), options: [ @@ -56,7 +69,7 @@ ruleTester.run('no-internal-modules', rule, { }, ], }), - test({ + tValid({ code: 'import "./app/index.js";\nimport "./app/index"', filename: testFilePath('./internal-modules/plugins/plugin2/internal.js'), options: [ @@ -65,7 +78,7 @@ ruleTester.run('no-internal-modules', rule, { }, ], }), - test({ + tValid({ code: 'import a from "./plugin2/thing"', filename: testFilePath('./internal-modules/plugins/plugin.js'), options: [ @@ -74,7 +87,7 @@ ruleTester.run('no-internal-modules', rule, { }, ], }), - test({ + tValid({ code: 'const a = require("./plugin2/thing")', filename: testFilePath('./internal-modules/plugins/plugin.js'), options: [ @@ -83,7 +96,7 @@ ruleTester.run('no-internal-modules', rule, { }, ], }), - test({ + tValid({ code: 'import b from "@org/package"', filename: testFilePath('./internal-modules/plugins/plugin2/internal.js'), options: [ @@ -93,19 +106,19 @@ ruleTester.run('no-internal-modules', rule, { ], }), // exports - test({ + tValid({ code: 'export {a} from "./internal.js"', filename: testFilePath('./internal-modules/plugins/plugin2/index.js'), }), - test({ + tValid({ code: 'export * from "lodash.get"', filename: testFilePath('./internal-modules/plugins/plugin2/index.js'), }), - test({ + tValid({ code: 'export {b} from "@org/package"', filename: testFilePath('./internal-modules/plugins/plugin2/internal.js'), }), - test({ + tValid({ code: 'export {b} from "../../api/service"', filename: testFilePath('./internal-modules/plugins/plugin2/internal.js'), options: [ @@ -114,7 +127,7 @@ ruleTester.run('no-internal-modules', rule, { }, ], }), - test({ + tValid({ code: 'export * from "jquery/dist/jquery"', filename: testFilePath('./internal-modules/plugins/plugin2/internal.js'), options: [ @@ -123,7 +136,7 @@ ruleTester.run('no-internal-modules', rule, { }, ], }), - test({ + tValid({ code: 'export * from "./app/index.js";\nexport * from "./app/index"', filename: testFilePath('./internal-modules/plugins/plugin2/internal.js'), options: [ @@ -132,7 +145,7 @@ ruleTester.run('no-internal-modules', rule, { }, ], }), - test({ + tValid({ code: ` export class AuthHelper { @@ -141,7 +154,7 @@ ruleTester.run('no-internal-modules', rule, { } `, }), - test({ + tValid({ code: ` export class AuthHelper { @@ -150,92 +163,72 @@ ruleTester.run('no-internal-modules', rule, { } `, }), - test({ + tValid({ code: 'export * from "./plugin2/thing"', filename: testFilePath('./internal-modules/plugins/plugin.js'), - options: [ - { - forbid: ['**/api/*'], - }, - ], + options: [{ forbid: ['**/api/*'] }], }), - test({ + tValid({ code: 'export { b } from "@org/package"', filename: testFilePath('./internal-modules/plugins/plugin2/internal.js'), - options: [ - { - forbid: ['@org/package/*'], - }, - ], + options: [{ forbid: ['@org/package/*'] }], }), - test({ + tValid({ code: 'export * from "./app/index.js";\nexport * from "./app/index"', filename: testFilePath('./internal-modules/plugins/plugin2/internal.js'), - options: [ - { - forbid: ['**/index.ts'], - }, - ], + options: [{ forbid: ['**/index.ts'] }], }), ], invalid: [ // imports - test({ + tInvalid({ code: 'import "./plugin2/index.js";\nimport "./plugin2/app/index"', filename: testFilePath('./internal-modules/plugins/plugin.js'), - options: [ - { - allow: ['*/index.js'], - }, - ], + options: [{ allow: ['*/index.js'] }], errors: [ { - message: 'Reaching to "./plugin2/app/index" is not allowed.', + ...createNotAllowedError('./plugin2/app/index'), line: 2, column: 8, }, ], }), - test({ + tInvalid({ code: 'import b from "app/a"', filename: testFilePath('./internal-modules/plugins/plugin2/internal.js'), - options: [ - { - forbid: ['app/**/**'], - }, - ], + options: [{ forbid: ['app/**/**'] }], errors: [ { - message: 'Reaching to "app/a" is not allowed.', + ...createNotAllowedError('app/a'), line: 1, column: 15, }, ], }), - test({ + tInvalid({ code: 'import "./app/index.js"', filename: testFilePath('./internal-modules/plugins/plugin2/internal.js'), errors: [ { - message: 'Reaching to "./app/index.js" is not allowed.', + ...createNotAllowedError('./app/index.js'), line: 1, column: 8, }, ], }), - test({ + tInvalid({ code: 'import b from "./plugin2/internal"', filename: testFilePath('./internal-modules/plugins/plugin.js'), errors: [ { - message: 'Reaching to "./plugin2/internal" is not allowed.', + ...createNotAllowedError('./plugin2/internal'), line: 1, column: 15, }, ], }), - test({ + tInvalid({ code: 'import a from "../api/service/index"', filename: testFilePath('./internal-modules/plugins/plugin.js'), options: [ @@ -245,129 +238,105 @@ ruleTester.run('no-internal-modules', rule, { ], errors: [ { - message: 'Reaching to "../api/service/index" is not allowed.', + ...createNotAllowedError('../api/service/index'), line: 1, column: 15, }, ], }), - test({ + tInvalid({ code: 'import b from "@org/package/internal"', filename: testFilePath('./internal-modules/plugins/plugin2/internal.js'), errors: [ { - message: 'Reaching to "@org/package/internal" is not allowed.', + ...createNotAllowedError('@org/package/internal'), line: 1, column: 15, }, ], }), - test({ + tInvalid({ code: 'import get from "debug/src/node"', filename: testFilePath('./internal-modules/plugins/plugin.js'), errors: [ { - message: 'Reaching to "debug/src/node" is not allowed.', + ...createNotAllowedError('debug/src/node'), line: 1, column: 17, }, ], }), - test({ + tInvalid({ code: 'import "./app/index.js"', filename: testFilePath('./internal-modules/plugins/plugin2/internal.js'), - options: [ - { - forbid: ['**/app/*'], - }, - ], + options: [{ forbid: ['**/app/*'] }], errors: [ { - message: 'Reaching to "./app/index.js" is not allowed.', + ...createNotAllowedError('./app/index.js'), line: 1, column: 8, }, ], }), - test({ + tInvalid({ code: 'import b from "@org/package"', filename: testFilePath('./internal-modules/plugins/plugin2/internal.js'), - options: [ - { - forbid: ['@org/**'], - }, - ], + options: [{ forbid: ['@org/**'] }], errors: [ { - message: 'Reaching to "@org/package" is not allowed.', + ...createNotAllowedError('@org/package'), line: 1, column: 15, }, ], }), - test({ + tInvalid({ code: 'import b from "app/a/b"', filename: testFilePath('./internal-modules/plugins/plugin2/internal.js'), - options: [ - { - forbid: ['app/**/**'], - }, - ], + options: [{ forbid: ['app/**/**'] }], errors: [ { - message: 'Reaching to "app/a/b" is not allowed.', + ...createNotAllowedError('app/a/b'), line: 1, column: 15, }, ], }), - test({ + tInvalid({ code: 'import get from "lodash.get"', filename: testFilePath('./internal-modules/plugins/plugin2/index.js'), - options: [ - { - forbid: ['lodash.*'], - }, - ], + options: [{ forbid: ['lodash.*'] }], errors: [ { - message: 'Reaching to "lodash.get" is not allowed.', + ...createNotAllowedError('lodash.get'), line: 1, column: 17, }, ], }), - test({ + tInvalid({ code: 'import "./app/index.js";\nimport "./app/index"', filename: testFilePath('./internal-modules/plugins/plugin2/internal.js'), - options: [ - { - forbid: ['**/index{.js,}'], - }, - ], + options: [{ forbid: ['**/index{.js,}'] }], errors: [ { - message: 'Reaching to "./app/index.js" is not allowed.', + ...createNotAllowedError('./app/index.js'), line: 1, column: 8, }, { - message: 'Reaching to "./app/index" is not allowed.', + ...createNotAllowedError('./app/index'), line: 2, column: 8, }, ], }), - test({ + tInvalid({ code: 'import "@/api/service";', - options: [ - { - forbid: ['**/api/*'], - }, - ], + options: [{ forbid: ['**/api/*'] }], errors: [ { - message: 'Reaching to "@/api/service" is not allowed.', + ...createNotAllowedError('@/api/service'), line: 1, column: 8, }, @@ -387,125 +356,105 @@ ruleTester.run('no-internal-modules', rule, { }, }), // exports - test({ + tInvalid({ code: 'export * from "./plugin2/index.js";\nexport * from "./plugin2/app/index"', filename: testFilePath('./internal-modules/plugins/plugin.js'), - options: [ - { - allow: ['*/index.js'], - }, - ], + options: [{ allow: ['*/index.js'] }], errors: [ { - message: 'Reaching to "./plugin2/app/index" is not allowed.', + ...createNotAllowedError('./plugin2/app/index'), line: 2, column: 15, }, ], }), - test({ + tInvalid({ code: 'export * from "app/a"', filename: testFilePath('./internal-modules/plugins/plugin2/internal.js'), - options: [ - { - forbid: ['app/**/**'], - }, - ], + options: [{ forbid: ['app/**/**'] }], errors: [ { - message: 'Reaching to "app/a" is not allowed.', + ...createNotAllowedError('app/a'), line: 1, column: 15, }, ], }), - test({ + tInvalid({ code: 'export * from "./app/index.js"', filename: testFilePath('./internal-modules/plugins/plugin2/internal.js'), errors: [ { - message: 'Reaching to "./app/index.js" is not allowed.', + ...createNotAllowedError('./app/index.js'), line: 1, column: 15, }, ], }), - test({ + tInvalid({ code: 'export {b} from "./plugin2/internal"', filename: testFilePath('./internal-modules/plugins/plugin.js'), errors: [ { - message: 'Reaching to "./plugin2/internal" is not allowed.', + ...createNotAllowedError('./plugin2/internal'), line: 1, column: 17, }, ], }), - test({ + tInvalid({ code: 'export {a} from "../api/service/index"', filename: testFilePath('./internal-modules/plugins/plugin.js'), - options: [ - { - allow: ['**/internal-modules/*'], - }, - ], + options: [{ allow: ['**/internal-modules/*'] }], errors: [ { - message: 'Reaching to "../api/service/index" is not allowed.', + ...createNotAllowedError('../api/service/index'), line: 1, column: 17, }, ], }), - test({ + tInvalid({ code: 'export {b} from "@org/package/internal"', filename: testFilePath('./internal-modules/plugins/plugin2/internal.js'), errors: [ { - message: 'Reaching to "@org/package/internal" is not allowed.', + ...createNotAllowedError('@org/package/internal'), line: 1, column: 17, }, ], }), - test({ + tInvalid({ code: 'export {get} from "debug/src/node"', filename: testFilePath('./internal-modules/plugins/plugin.js'), errors: [ { - message: 'Reaching to "debug/src/node" is not allowed.', + ...createNotAllowedError('debug/src/node'), line: 1, column: 19, }, ], }), - test({ + tInvalid({ code: 'export * from "./plugin2/thing"', filename: testFilePath('./internal-modules/plugins/plugin.js'), - options: [ - { - forbid: ['**/plugin2/*'], - }, - ], + options: [{ forbid: ['**/plugin2/*'] }], errors: [ { - message: 'Reaching to "./plugin2/thing" is not allowed.', + ...createNotAllowedError('./plugin2/thing'), line: 1, column: 15, }, ], }), - test({ + tInvalid({ code: 'export * from "app/a"', filename: testFilePath('./internal-modules/plugins/plugin2/internal.js'), - options: [ - { - forbid: ['**'], - }, - ], + options: [{ forbid: ['**'] }], errors: [ { - message: 'Reaching to "app/a" is not allowed.', + ...createNotAllowedError('app/a'), line: 1, column: 15, }, From 9f33bd6395f6a2cfbc8a65f6dcd2802fd3ad7938 Mon Sep 17 00:00:00 2001 From: Marco Pasqualetti Date: Tue, 19 Nov 2024 07:58:22 +0100 Subject: [PATCH 27/49] test(no-mutable-exports): migrate to `createRuleTestCaseFunctions` --- test/rules/no-mutable-exports.spec.ts | 104 ++++++++++++++------------ 1 file changed, 58 insertions(+), 46 deletions(-) diff --git a/test/rules/no-mutable-exports.spec.ts b/test/rules/no-mutable-exports.spec.ts index 2d4120a69..baa66defb 100644 --- a/test/rules/no-mutable-exports.spec.ts +++ b/test/rules/no-mutable-exports.spec.ts @@ -1,40 +1,52 @@ import { RuleTester as TSESLintRuleTester } from '@typescript-eslint/rule-tester' +import type { TestCaseError as TSESLintTestCaseError } from '@typescript-eslint/rule-tester' -import { parsers, test } from '../utils' +import { parsers, createRuleTestCaseFunctions } from '../utils' +import type { GetRuleModuleMessageIds } from '../utils' import rule from 'eslint-plugin-import-x/rules/no-mutable-exports' const ruleTester = new TSESLintRuleTester() +const { tValid, tInvalid } = createRuleTestCaseFunctions() + +function createNoMutableError( + kind: string, +): TSESLintTestCaseError> { + return { messageId: 'noMutable', data: { kind } } +} + ruleTester.run('no-mutable-exports', rule, { valid: [ - test({ code: 'export const count = 1' }), - test({ code: 'export function getCount() {}' }), - test({ code: 'export class Counter {}' }), - test({ code: 'export default count = 1' }), - test({ code: 'export default function getCount() {}' }), - test({ code: 'export default class Counter {}' }), - test({ code: 'const count = 1\nexport { count }' }), - test({ code: 'const count = 1\nexport { count as counter }' }), - test({ code: 'const count = 1\nexport default count' }), - test({ code: 'const count = 1\nexport { count as default }' }), - test({ code: 'function getCount() {}\nexport { getCount }' }), - test({ code: 'function getCount() {}\nexport { getCount as getCounter }' }), - test({ code: 'function getCount() {}\nexport default getCount' }), - test({ code: 'function getCount() {}\nexport { getCount as default }' }), - test({ code: 'class Counter {}\nexport { Counter }' }), - test({ code: 'class Counter {}\nexport { Counter as Count }' }), - test({ code: 'class Counter {}\nexport default Counter' }), - test({ code: 'class Counter {}\nexport { Counter as default }' }), - test({ - languageOptions: { parser: require(parsers.BABEL) }, - code: 'export Something from "./something";', + tValid({ code: 'export const count = 1' }), + tValid({ code: 'export function getCount() {}' }), + tValid({ code: 'export class Counter {}' }), + tValid({ code: 'export default count = 1' }), + tValid({ code: 'export default function getCount() {}' }), + tValid({ code: 'export default class Counter {}' }), + tValid({ code: 'const count = 1\nexport { count }' }), + tValid({ code: 'const count = 1\nexport { count as counter }' }), + tValid({ code: 'const count = 1\nexport default count' }), + tValid({ code: 'const count = 1\nexport { count as default }' }), + tValid({ code: 'function getCount() {}\nexport { getCount }' }), + tValid({ + code: 'function getCount() {}\nexport { getCount as getCounter }', }), - test({ + tValid({ code: 'function getCount() {}\nexport default getCount' }), + tValid({ code: 'function getCount() {}\nexport { getCount as default }' }), + tValid({ code: 'class Counter {}\nexport { Counter }' }), + tValid({ code: 'class Counter {}\nexport { Counter as Count }' }), + tValid({ code: 'class Counter {}\nexport default Counter' }), + tValid({ code: 'class Counter {}\nexport { Counter as default }' }), + tValid({ + code: 'export Something from "./something";', languageOptions: { parser: require(parsers.BABEL) }, + }), + tValid({ code: 'type Foo = {}\nexport type {Foo}', + languageOptions: { parser: require(parsers.BABEL) }, }), - test({ + tValid({ code: 'const count = 1\nexport { count as "counter" }', languageOptions: { parser: require(parsers.ESPREE), @@ -43,41 +55,41 @@ ruleTester.run('no-mutable-exports', rule, { }), ], invalid: [ - test({ + tInvalid({ code: 'export let count = 1', - errors: ["Exporting mutable 'let' binding, use 'const' instead."], + errors: [createNoMutableError('let')], }), - test({ + tInvalid({ code: 'export var count = 1', - errors: ["Exporting mutable 'var' binding, use 'const' instead."], + errors: [createNoMutableError('var')], }), - test({ + tInvalid({ code: 'let count = 1\nexport { count }', - errors: ["Exporting mutable 'let' binding, use 'const' instead."], + errors: [createNoMutableError('let')], }), - test({ + tInvalid({ code: 'var count = 1\nexport { count }', - errors: ["Exporting mutable 'var' binding, use 'const' instead."], + errors: [createNoMutableError('var')], }), - test({ + tInvalid({ code: 'let count = 1\nexport { count as counter }', - errors: ["Exporting mutable 'let' binding, use 'const' instead."], + errors: [createNoMutableError('let')], }), - test({ + tInvalid({ code: 'var count = 1\nexport { count as counter }', - errors: ["Exporting mutable 'var' binding, use 'const' instead."], + errors: [createNoMutableError('var')], }), - test({ + tInvalid({ code: 'let count = 1\nexport default count', - errors: ["Exporting mutable 'let' binding, use 'const' instead."], + errors: [createNoMutableError('let')], }), - test({ + tInvalid({ code: 'var count = 1\nexport default count', - errors: ["Exporting mutable 'var' binding, use 'const' instead."], + errors: [createNoMutableError('var')], }), - test({ + tInvalid({ code: 'let count = 1\nexport { count as "counter" }', - errors: ["Exporting mutable 'let' binding, use 'const' instead."], + errors: [createNoMutableError('let')], languageOptions: { parser: require(parsers.ESPREE), parserOptions: { ecmaVersion: 2022 }, @@ -85,13 +97,13 @@ ruleTester.run('no-mutable-exports', rule, { }), // todo: undeclared globals - // test({ + // tInvalid({ // code: 'count = 1\nexport { count }', - // errors: ['Exporting mutable global binding, use \'const\' instead.'], + // errors: [createNoMutableError('global binding')], // }), - // test({ + // tInvalid({ // code: 'count = 1\nexport default count', - // errors: ['Exporting mutable global binding, use \'const\' instead.'], + // errors: [createNoMutableError('global binding')], // }), ], }) From 9d238ced5d4ab5d5a5fd9f9ee6077a9475ca2c7c Mon Sep 17 00:00:00 2001 From: Marco Pasqualetti Date: Tue, 19 Nov 2024 08:05:27 +0100 Subject: [PATCH 28/49] test(no-named-as-default-member): migrate to `createRuleTestCaseFunctions` --- test/rules/no-named-as-default-member.spec.ts | 89 +++++++++++-------- 1 file changed, 52 insertions(+), 37 deletions(-) diff --git a/test/rules/no-named-as-default-member.spec.ts b/test/rules/no-named-as-default-member.spec.ts index 2c539bb25..08c696015 100644 --- a/test/rules/no-named-as-default-member.spec.ts +++ b/test/rules/no-named-as-default-member.spec.ts @@ -1,23 +1,39 @@ import { RuleTester as TSESLintRuleTester } from '@typescript-eslint/rule-tester' +import type { TestCaseError as TSESLintTestCaseError } from '@typescript-eslint/rule-tester' +import type { TSESTree } from '@typescript-eslint/utils' -import { test, SYNTAX_VALID_CASES, parsers } from '../utils' +import { + createRuleTestCaseFunctions, + SYNTAX_VALID_CASES, + parsers, +} from '../utils' +import type { GetRuleModuleMessageIds } from '../utils' import rule from 'eslint-plugin-import-x/rules/no-named-as-default-member' const ruleTester = new TSESLintRuleTester() +const { tValid, tInvalid } = createRuleTestCaseFunctions() + +function createMemberError( + data: { objectName: string; propName: string; sourcePath: string }, + type: `${TSESTree.AST_NODE_TYPES}`, +): TSESLintTestCaseError> { + return { messageId: 'member', data, type: type as TSESTree.AST_NODE_TYPES } +} + ruleTester.run('no-named-as-default-member', rule, { valid: [ - test({ code: 'import bar, {foo} from "./bar";' }), - test({ code: 'import bar from "./bar"; const baz = bar.baz' }), - test({ code: 'import {foo} from "./bar"; const baz = foo.baz;' }), - test({ + tValid({ code: 'import bar, {foo} from "./bar";' }), + tValid({ code: 'import bar from "./bar"; const baz = bar.baz' }), + tValid({ code: 'import {foo} from "./bar"; const baz = foo.baz;' }), + tValid({ code: 'import * as named from "./named-exports"; const a = named.a', }), - test({ + tValid({ code: 'import foo from "./default-export-default-property"; const a = foo.default', }), - test({ + tValid({ code: 'import bar, { foo } from "./export-default-string-and-named"', languageOptions: { parser: require(parsers.ESPREE), @@ -29,54 +45,53 @@ ruleTester.run('no-named-as-default-member', rule, { ], invalid: [ - test({ + tInvalid({ code: 'import bar from "./bar"; const foo = bar.foo;', errors: [ - { - message: - "Caution: `bar` also has a named export `foo`. Check if you meant to write `import {foo} from './bar'` instead.", - type: 'MemberExpression', - }, + createMemberError( + { objectName: 'bar', propName: 'foo', sourcePath: './bar' }, + 'MemberExpression', + ), ], }), - test({ + tInvalid({ code: 'import bar from "./bar"; bar.foo();', errors: [ - { - message: - "Caution: `bar` also has a named export `foo`. Check if you meant to write `import {foo} from './bar'` instead.", - type: 'MemberExpression', - }, + createMemberError( + { objectName: 'bar', propName: 'foo', sourcePath: './bar' }, + 'MemberExpression', + ), ], }), - test({ + tInvalid({ code: 'import bar from "./bar"; const {foo} = bar;', errors: [ - { - message: - "Caution: `bar` also has a named export `foo`. Check if you meant to write `import {foo} from './bar'` instead.", - type: 'Identifier', - }, + createMemberError( + { objectName: 'bar', propName: 'foo', sourcePath: './bar' }, + 'Identifier', + ), ], }), - test({ + tInvalid({ code: 'import bar from "./bar"; const {foo: foo2, baz} = bar;', errors: [ - { - message: - "Caution: `bar` also has a named export `foo`. Check if you meant to write `import {foo} from './bar'` instead.", - type: 'Identifier', - }, + createMemberError( + { objectName: 'bar', propName: 'foo', sourcePath: './bar' }, + 'Identifier', + ), ], }), - test({ + tInvalid({ code: 'import bar from "./export-default-string-and-named"; const foo = bar.foo;', errors: [ - { - message: - "Caution: `bar` also has a named export `foo`. Check if you meant to write `import {foo} from './export-default-string-and-named'` instead.", - type: 'MemberExpression', - }, + createMemberError( + { + objectName: 'bar', + propName: 'foo', + sourcePath: './export-default-string-and-named', + }, + 'MemberExpression', + ), ], languageOptions: { parser: require(parsers.ESPREE), From 8b15d24ba45bf451f5967af4e4478f4a0bc6b8dd Mon Sep 17 00:00:00 2001 From: Marco Pasqualetti Date: Tue, 19 Nov 2024 08:30:22 +0100 Subject: [PATCH 29/49] test(no-named-as-default): migrate to `createRuleTestCaseFunctions` --- test/rules/no-named-as-default.spec.ts | 108 +++++++++++-------------- 1 file changed, 46 insertions(+), 62 deletions(-) diff --git a/test/rules/no-named-as-default.spec.ts b/test/rules/no-named-as-default.spec.ts index d393695ff..51ee8c829 100644 --- a/test/rules/no-named-as-default.spec.ts +++ b/test/rules/no-named-as-default.spec.ts @@ -1,15 +1,35 @@ import { RuleTester as TSESLintRuleTester } from '@typescript-eslint/rule-tester' +import type { TestCaseError as TSESLintTestCaseError } from '@typescript-eslint/rule-tester' +import type { AST_NODE_TYPES, TSESTree } from '@typescript-eslint/utils' -import { test, SYNTAX_VALID_CASES, parsers } from '../utils' +import { + createRuleTestCaseFunctions, + SYNTAX_VALID_CASES, + parsers, +} from '../utils' +import type { GetRuleModuleMessageIds } from '../utils' import rule from 'eslint-plugin-import-x/rules/no-named-as-default' const ruleTester = new TSESLintRuleTester() +const { tValid, tInvalid } = createRuleTestCaseFunctions() + +function createDefaultError( + name: string, + type: `${TSESTree.AST_NODE_TYPES}`, +): TSESLintTestCaseError> { + return { + messageId: 'default', + data: { name }, + type: type as TSESTree.AST_NODE_TYPES, + } +} + ruleTester.run('no-named-as-default', rule, { valid: [ // https://github.com/un-ts/eslint-plugin-import-x/issues/123 - test({ + tValid({ code: `/** TypeScript */ import klawSync from "klaw-sync";`, settings: { 'import-x/extensions': [ @@ -47,7 +67,7 @@ ruleTester.run('no-named-as-default', rule, { }, }), - test({ + tValid({ code: 'import "./malformed.js"', languageOptions: { parser: require(parsers.ESPREE) }, }), @@ -56,22 +76,22 @@ ruleTester.run('no-named-as-default', rule, { 'import bar, { foo } from "./empty-folder";', // es7 - test({ + tValid({ code: 'export bar, { foo } from "./bar";', languageOptions: { parser: require(parsers.BABEL) }, }), - test({ + tValid({ code: 'export bar from "./bar";', languageOptions: { parser: require(parsers.BABEL) }, }), // #566: don't false-positive on `default` itself - test({ + tValid({ code: 'export default from "./bar";', languageOptions: { parser: require(parsers.BABEL) }, }), - test({ + tValid({ code: 'import bar, { foo } from "./export-default-string-and-named"', languageOptions: { parser: require(parsers.ESPREE), @@ -83,102 +103,66 @@ ruleTester.run('no-named-as-default', rule, { ], invalid: [ - test({ + tInvalid({ code: 'import foo from "./bar";', - errors: [ - { - message: - "Using exported name 'foo' as identifier for default export.", - type: 'ImportDefaultSpecifier', - }, - ], + errors: [createDefaultError('foo', 'ImportDefaultSpecifier')], }), - test({ + tInvalid({ code: 'import foo, { foo as bar } from "./bar";', - errors: [ - { - message: - "Using exported name 'foo' as identifier for default export.", - type: 'ImportDefaultSpecifier', - }, - ], + errors: [createDefaultError('foo', 'ImportDefaultSpecifier')], }), // es7 - test({ + tInvalid({ code: 'export foo from "./bar";', languageOptions: { parser: require(parsers.BABEL) }, errors: [ - { - message: - "Using exported name 'foo' as identifier for default export.", - // eslint-disable-next-line @typescript-eslint/no-explicit-any -- ExportDefaultSpecifier is unavailable yet - type: 'ExportDefaultSpecifier' as any, - }, + // @ts-expect-error ExportDefaultSpecifier is unavailable yet + createDefaultError('foo', 'ExportDefaultSpecifier'), ], }), - test({ + tInvalid({ code: 'export foo, { foo as bar } from "./bar";', languageOptions: { parser: require(parsers.BABEL) }, errors: [ - { - message: - "Using exported name 'foo' as identifier for default export.", - // eslint-disable-next-line @typescript-eslint/no-explicit-any -- ExportDefaultSpecifier is unavailable yet - type: 'ExportDefaultSpecifier' as any, - }, + // @ts-expect-error ExportDefaultSpecifier is unavailable yet + createDefaultError('foo', 'ExportDefaultSpecifier'), ], }), - test({ + tInvalid({ code: 'import foo from "./malformed.js"', languageOptions: { parser: require(parsers.ESPREE) }, errors: [ { + // @ts-expect-error parsing error message: "Parse errors in imported module './malformed.js': 'return' outside of function (1:1)", - type: 'Literal', + type: 'Literal' as AST_NODE_TYPES, }, ], }), - test({ + tInvalid({ code: 'import foo from "./export-default-string-and-named"', - errors: [ - { - message: - "Using exported name 'foo' as identifier for default export.", - type: 'ImportDefaultSpecifier', - }, - ], + errors: [createDefaultError('foo', 'ImportDefaultSpecifier')], languageOptions: { parser: require(parsers.ESPREE), parserOptions: { ecmaVersion: 2022 }, }, }), - test({ + tInvalid({ code: 'import foo, { foo as bar } from "./export-default-string-and-named"', - errors: [ - { - message: - "Using exported name 'foo' as identifier for default export.", - type: 'ImportDefaultSpecifier', - }, - ], + errors: [createDefaultError('foo', 'ImportDefaultSpecifier')], languageOptions: { parser: require(parsers.ESPREE), parserOptions: { ecmaVersion: 2022 }, }, }), - test({ + tInvalid({ code: `import z from 'zod';`, - errors: [ - { - message: "Using exported name 'z' as identifier for default export.", - type: 'ImportDefaultSpecifier', - }, - ], + errors: [createDefaultError('z', 'ImportDefaultSpecifier')], }), ], }) From f4e2db1ba1a81c6acdbd80d4c4bbac61e1d800e7 Mon Sep 17 00:00:00 2001 From: Marco Pasqualetti Date: Tue, 19 Nov 2024 08:35:32 +0100 Subject: [PATCH 30/49] test(no-named-default): migrate to `createRuleTestCaseFunctions` --- test/rules/no-named-default.spec.ts | 57 ++++++++++++++++++----------- 1 file changed, 35 insertions(+), 22 deletions(-) diff --git a/test/rules/no-named-default.spec.ts b/test/rules/no-named-default.spec.ts index 5185eb27a..fbee75d59 100644 --- a/test/rules/no-named-default.spec.ts +++ b/test/rules/no-named-default.spec.ts @@ -1,22 +1,29 @@ import { RuleTester as TSESLintRuleTester } from '@typescript-eslint/rule-tester' +import { AST_NODE_TYPES } from '@typescript-eslint/utils' -import { test, SYNTAX_VALID_CASES, parsers } from '../utils' +import { + createRuleTestCaseFunctions, + SYNTAX_VALID_CASES, + parsers, +} from '../utils' import rule from 'eslint-plugin-import-x/rules/no-named-default' const ruleTester = new TSESLintRuleTester() +const { tValid, tInvalid } = createRuleTestCaseFunctions() + ruleTester.run('no-named-default', rule, { valid: [ - test({ code: 'import bar from "./bar";' }), - test({ code: 'import bar, { foo } from "./bar";' }), + tValid({ code: 'import bar from "./bar";' }), + tValid({ code: 'import bar, { foo } from "./bar";' }), // Should ignore imported flow types - test({ + tValid({ code: 'import { type default as Foo } from "./bar";', languageOptions: { parser: require(parsers.BABEL) }, }), - test({ + tValid({ code: 'import { typeof default as Foo } from "./bar";', languageOptions: { parser: require(parsers.BABEL) }, }), @@ -25,39 +32,45 @@ ruleTester.run('no-named-default', rule, { ], invalid: [ - /*test({ - code: 'import { default } from "./bar";', - errors: [{ - message: 'Use default import syntax to import \'default\'.', - type: 'Identifier', - }], - languageOptions: { parser: require(parsers.BABEL) } , - }),*/ - test({ + // tInvalid({ + // code: 'import { default } from "./bar";', + // errors: [ + // { + // messageId: 'default', + // data: { importName: 'default' }, + // type: AST_NODE_TYPES.Identifier, + // }, + // ], + // languageOptions: { parser: require(parsers.BABEL) }, + // }), + tInvalid({ code: 'import { default as bar } from "./bar";', errors: [ { - message: "Use default import syntax to import 'bar'.", - type: 'Identifier', + messageId: 'default', + data: { importName: 'bar' }, + type: AST_NODE_TYPES.Identifier, }, ], }), - test({ + tInvalid({ code: 'import { foo, default as bar } from "./bar";', errors: [ { - message: "Use default import syntax to import 'bar'.", - type: 'Identifier', + messageId: 'default', + data: { importName: 'bar' }, + type: AST_NODE_TYPES.Identifier, }, ], }), - test({ + tInvalid({ code: 'import { "default" as bar } from "./bar";', errors: [ { - message: "Use default import syntax to import 'bar'.", - type: 'Identifier', + messageId: 'default', + data: { importName: 'bar' }, + type: AST_NODE_TYPES.Identifier, }, ], languageOptions: { From 47f5cffa5f127dde33ec28cc9583f9f15e89026b Mon Sep 17 00:00:00 2001 From: Marco Pasqualetti Date: Tue, 19 Nov 2024 08:43:38 +0100 Subject: [PATCH 31/49] test(no-named-export): migrate to `createRuleTestCaseFunctions` --- test/rules/no-named-export.spec.ts | 186 ++++++++--------------------- 1 file changed, 52 insertions(+), 134 deletions(-) diff --git a/test/rules/no-named-export.spec.ts b/test/rules/no-named-export.spec.ts index 55829764a..2f5916351 100644 --- a/test/rules/no-named-export.spec.ts +++ b/test/rules/no-named-export.spec.ts @@ -1,17 +1,25 @@ import { RuleTester as TSESLintRuleTester } from '@typescript-eslint/rule-tester' +import { AST_NODE_TYPES } from '@typescript-eslint/utils' -import { parsers, test } from '../utils' +import { parsers, createRuleTestCaseFunctions } from '../utils' import rule from 'eslint-plugin-import-x/rules/no-named-export' const ruleTester = new TSESLintRuleTester() +const { tValid, tInvalid } = createRuleTestCaseFunctions() + +const NO_ALLOWED_ERROR = { + messageId: 'noAllowed', + type: AST_NODE_TYPES.ExportNamedDeclaration, +} as const + ruleTester.run('no-named-export', rule, { valid: [ - test({ + tValid({ code: 'module.export.foo = function () {}', }), - test({ + tValid({ code: 'module.exports = function foo() {}', languageOptions: { parserOptions: { @@ -19,28 +27,28 @@ ruleTester.run('no-named-export', rule, { }, }, }), - test({ + tValid({ code: 'export default function bar() {};', }), - test({ + tValid({ code: 'let foo; export { foo as default }', }), - test({ + tValid({ code: 'export default from "foo.js"', languageOptions: { parser: require(parsers.BABEL) }, }), // no exports at all - test({ + tValid({ code: `import * as foo from './foo';`, }), - test({ + tValid({ code: `import foo from './foo';`, }), - test({ + tValid({ code: `import {default as foo} from './foo';`, }), - test({ + tValid({ code: 'let foo; export { foo as "default" }', languageOptions: { parser: require(parsers.ESPREE), @@ -49,189 +57,99 @@ ruleTester.run('no-named-export', rule, { }), ], invalid: [ - test({ + tInvalid({ code: ` export const foo = 'foo'; export const bar = 'bar'; `, - errors: [ - { - type: 'ExportNamedDeclaration', - messageId: 'noAllowed', - }, - { - type: 'ExportNamedDeclaration', - messageId: 'noAllowed', - }, - ], + errors: [NO_ALLOWED_ERROR, NO_ALLOWED_ERROR], }), - test({ + tInvalid({ code: ` export const foo = 'foo'; export default bar;`, - errors: [ - { - type: 'ExportNamedDeclaration', - messageId: 'noAllowed', - }, - ], + errors: [NO_ALLOWED_ERROR], }), - test({ + tInvalid({ code: ` export const foo = 'foo'; export function bar() {}; `, - errors: [ - { - type: 'ExportNamedDeclaration', - messageId: 'noAllowed', - }, - { - type: 'ExportNamedDeclaration', - messageId: 'noAllowed', - }, - ], + errors: [NO_ALLOWED_ERROR, NO_ALLOWED_ERROR], }), - test({ + tInvalid({ code: `export const foo = 'foo';`, - errors: [ - { - type: 'ExportNamedDeclaration', - messageId: 'noAllowed', - }, - ], + errors: [NO_ALLOWED_ERROR], }), - test({ + tInvalid({ code: ` const foo = 'foo'; export { foo }; `, - errors: [ - { - type: 'ExportNamedDeclaration', - messageId: 'noAllowed', - }, - ], + errors: [NO_ALLOWED_ERROR], }), - test({ + tInvalid({ code: `let foo, bar; export { foo, bar }`, - errors: [ - { - type: 'ExportNamedDeclaration', - messageId: 'noAllowed', - }, - ], + errors: [NO_ALLOWED_ERROR], }), - test({ + tInvalid({ code: `export const { foo, bar } = item;`, - errors: [ - { - type: 'ExportNamedDeclaration', - messageId: 'noAllowed', - }, - ], + errors: [NO_ALLOWED_ERROR], }), - test({ + tInvalid({ code: `export const { foo, bar: baz } = item;`, - errors: [ - { - type: 'ExportNamedDeclaration', - messageId: 'noAllowed', - }, - ], + errors: [NO_ALLOWED_ERROR], }), - test({ + tInvalid({ code: `export const { foo: { bar, baz } } = item;`, - errors: [ - { - type: 'ExportNamedDeclaration', - messageId: 'noAllowed', - }, - ], + errors: [NO_ALLOWED_ERROR], }), - test({ + tInvalid({ code: ` let item; export const foo = item; export { item }; `, - errors: [ - { - type: 'ExportNamedDeclaration', - messageId: 'noAllowed', - }, - { - type: 'ExportNamedDeclaration', - messageId: 'noAllowed', - }, - ], + errors: [NO_ALLOWED_ERROR, NO_ALLOWED_ERROR], }), - test({ + tInvalid({ code: `export * from './foo';`, errors: [ { - type: 'ExportAllDeclaration', messageId: 'noAllowed', + type: AST_NODE_TYPES.ExportAllDeclaration, }, ], }), - test({ + tInvalid({ code: `export const { foo } = { foo: "bar" };`, - errors: [ - { - type: 'ExportNamedDeclaration', - messageId: 'noAllowed', - }, - ], + errors: [NO_ALLOWED_ERROR], }), - test({ + tInvalid({ code: `export const { foo: { bar } } = { foo: { bar: "baz" } };`, errors: [ - { - type: 'ExportNamedDeclaration', - messageId: 'noAllowed', - }, + { messageId: 'noAllowed', type: AST_NODE_TYPES.ExportNamedDeclaration }, ], }), - test({ + tInvalid({ code: 'export { a, b } from "foo.js"', languageOptions: { parser: require(parsers.BABEL) }, - errors: [ - { - type: 'ExportNamedDeclaration', - messageId: 'noAllowed', - }, - ], + errors: [NO_ALLOWED_ERROR], }), - test({ + tInvalid({ code: `export type UserId = number;`, languageOptions: { parser: require(parsers.BABEL) }, - errors: [ - { - type: 'ExportNamedDeclaration', - messageId: 'noAllowed', - }, - ], + errors: [NO_ALLOWED_ERROR], }), - test({ + tInvalid({ code: 'export foo from "foo.js"', languageOptions: { parser: require(parsers.BABEL) }, - errors: [ - { - type: 'ExportNamedDeclaration', - messageId: 'noAllowed', - }, - ], + errors: [NO_ALLOWED_ERROR], }), - test({ + tInvalid({ code: `export Memory, { MemoryValue } from './Memory'`, languageOptions: { parser: require(parsers.BABEL) }, - errors: [ - { - type: 'ExportNamedDeclaration', - messageId: 'noAllowed', - }, - ], + errors: [NO_ALLOWED_ERROR], }), ], }) From 48b403432e1704a16d98c867557c47d99fe48068 Mon Sep 17 00:00:00 2001 From: Marco Pasqualetti Date: Tue, 19 Nov 2024 08:46:56 +0100 Subject: [PATCH 32/49] test(no-namespace): migrate to `createRuleTestCaseFunctions` --- test/rules/no-namespace.spec.ts | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/test/rules/no-namespace.spec.ts b/test/rules/no-namespace.spec.ts index 2c2efce8e..9456e2bc9 100644 --- a/test/rules/no-namespace.spec.ts +++ b/test/rules/no-namespace.spec.ts @@ -1,13 +1,15 @@ import { RuleTester as TSESLintRuleTester } from '@typescript-eslint/rule-tester' -import { test } from '../utils' +import { createRuleTestCaseFunctions } from '../utils' import rule from 'eslint-plugin-import-x/rules/no-namespace' -const ERROR_MESSAGE = 'Unexpected namespace import.' - const ruleTester = new TSESLintRuleTester() +const { tInvalid } = createRuleTestCaseFunctions() + +const NO_NAMESPACE_ERROR = { messageId: 'noNamespace' } as const + ruleTester.run('no-namespace', rule, { valid: [ { @@ -44,37 +46,37 @@ ruleTester.run('no-namespace', rule, { ], invalid: [ - test({ + tInvalid({ code: "import * as foo from 'foo';", errors: [ { + ...NO_NAMESPACE_ERROR, line: 1, column: 8, - message: ERROR_MESSAGE, }, ], }), - test({ + tInvalid({ code: "import defaultExport, * as foo from 'foo';", errors: [ { + ...NO_NAMESPACE_ERROR, line: 1, column: 23, - message: ERROR_MESSAGE, }, ], }), - test({ + tInvalid({ code: "import * as foo from './foo';", errors: [ { + ...NO_NAMESPACE_ERROR, line: 1, column: 8, - message: ERROR_MESSAGE, }, ], }), - test({ + tInvalid({ code: ` import * as foo from './foo'; florp(foo.bar); @@ -87,13 +89,13 @@ ruleTester.run('no-namespace', rule, { `.trim(), errors: [ { + ...NO_NAMESPACE_ERROR, line: 1, column: 8, - message: ERROR_MESSAGE, }, ], }), - test({ + tInvalid({ code: ` import * as foo from './foo'; const bar = 'name conflict'; @@ -112,13 +114,13 @@ ruleTester.run('no-namespace', rule, { `.trim(), errors: [ { + ...NO_NAMESPACE_ERROR, line: 1, column: 8, - message: ERROR_MESSAGE, }, ], }), - test({ + tInvalid({ code: ` import * as foo from './foo'; function func(arg) { @@ -135,9 +137,9 @@ ruleTester.run('no-namespace', rule, { `.trim(), errors: [ { + ...NO_NAMESPACE_ERROR, line: 1, column: 8, - message: ERROR_MESSAGE, }, ], }), From 92a0d488fc6ed2b92e085bf0bbf0b07d0bde6e6d Mon Sep 17 00:00:00 2001 From: Marco Pasqualetti Date: Tue, 19 Nov 2024 08:51:44 +0100 Subject: [PATCH 33/49] test(no-nodejs-modules): migrate to `createRuleTestCaseFunctions` --- test/rules/no-nodejs-modules.spec.ts | 176 ++++++++++----------------- 1 file changed, 67 insertions(+), 109 deletions(-) diff --git a/test/rules/no-nodejs-modules.spec.ts b/test/rules/no-nodejs-modules.spec.ts index 8d999f2ed..39b9b8295 100644 --- a/test/rules/no-nodejs-modules.spec.ts +++ b/test/rules/no-nodejs-modules.spec.ts @@ -1,162 +1,120 @@ import { RuleTester as TSESLintRuleTester } from '@typescript-eslint/rule-tester' +import type { TestCaseError as TSESLintTestCaseError } from '@typescript-eslint/rule-tester' -import { test } from '../utils' +import { createRuleTestCaseFunctions } from '../utils' +import type { GetRuleModuleMessageIds } from '../utils' import rule from 'eslint-plugin-import-x/rules/no-nodejs-modules' const ruleTester = new TSESLintRuleTester() -const error = (message: string) => ({ - message, -}) +const { tValid, tInvalid } = createRuleTestCaseFunctions() + +function createBuilinError( + moduleName: string, +): TSESLintTestCaseError> { + return { messageId: 'builtin', data: { moduleName } } +} ruleTester.run('no-nodejs-modules', rule, { valid: [ - test({ code: 'import _ from "lodash"' }), - test({ code: 'import find from "lodash.find"' }), - test({ code: 'import foo from "./foo"' }), - test({ code: 'import foo from "../foo"' }), - test({ code: 'import foo from "foo"' }), - test({ code: 'import foo from "./"' }), - test({ code: 'import foo from "@scope/foo"' }), - test({ code: 'var _ = require("lodash")' }), - test({ code: 'var find = require("lodash.find")' }), - test({ code: 'var foo = require("./foo")' }), - test({ code: 'var foo = require("../foo")' }), - test({ code: 'var foo = require("foo")' }), - test({ code: 'var foo = require("./")' }), - test({ code: 'var foo = require("@scope/foo")' }), - test({ + tValid({ code: 'import _ from "lodash"' }), + tValid({ code: 'import find from "lodash.find"' }), + tValid({ code: 'import foo from "./foo"' }), + tValid({ code: 'import foo from "../foo"' }), + tValid({ code: 'import foo from "foo"' }), + tValid({ code: 'import foo from "./"' }), + tValid({ code: 'import foo from "@scope/foo"' }), + tValid({ code: 'var _ = require("lodash")' }), + tValid({ code: 'var find = require("lodash.find")' }), + tValid({ code: 'var foo = require("./foo")' }), + tValid({ code: 'var foo = require("../foo")' }), + tValid({ code: 'var foo = require("foo")' }), + tValid({ code: 'var foo = require("./")' }), + tValid({ code: 'var foo = require("@scope/foo")' }), + tValid({ code: 'import events from "events"', - options: [ - { - allow: ['events'], - }, - ], + options: [{ allow: ['events'] }], }), - test({ + tValid({ code: 'import path from "path"', - options: [ - { - allow: ['path'], - }, - ], + options: [{ allow: ['path'] }], }), - test({ + tValid({ code: 'var events = require("events")', - options: [ - { - allow: ['events'], - }, - ], + options: [{ allow: ['events'] }], }), - test({ + tValid({ code: 'var path = require("path")', - options: [ - { - allow: ['path'], - }, - ], + options: [{ allow: ['path'] }], }), - test({ + tValid({ code: 'import path from "path";import events from "events"', - options: [ - { - allow: ['path', 'events'], - }, - ], + options: [{ allow: ['path', 'events'] }], }), - test({ + tValid({ code: 'import events from "node:events"', - options: [ - { - allow: ['node:events'], - }, - ], + options: [{ allow: ['node:events'] }], }), - test({ + tValid({ code: 'var events = require("node:events")', - options: [ - { - allow: ['node:events'], - }, - ], + options: [{ allow: ['node:events'] }], }), - test({ + tValid({ code: 'import path from "node:path"', - options: [ - { - allow: ['node:path'], - }, - ], + options: [{ allow: ['node:path'] }], }), - test({ + tValid({ code: 'var path = require("node:path")', - options: [ - { - allow: ['node:path'], - }, - ], + options: [{ allow: ['node:path'] }], }), - test({ + tValid({ code: 'import path from "node:path";import events from "node:events"', - options: [ - { - allow: ['node:path', 'node:events'], - }, - ], + options: [{ allow: ['node:path', 'node:events'] }], }), ], invalid: [ - test({ + tInvalid({ code: 'import path from "path"', - errors: [error('Do not import Node.js builtin module "path"')], + errors: [createBuilinError('path')], }), - test({ + tInvalid({ code: 'import fs from "fs"', - errors: [error('Do not import Node.js builtin module "fs"')], + errors: [createBuilinError('fs')], }), - test({ + tInvalid({ code: 'var path = require("path")', - errors: [error('Do not import Node.js builtin module "path"')], + errors: [createBuilinError('path')], }), - test({ + tInvalid({ code: 'var fs = require("fs")', - errors: [error('Do not import Node.js builtin module "fs"')], + errors: [createBuilinError('fs')], }), - test({ + tInvalid({ code: 'import fs from "fs"', - options: [ - { - allow: ['path'], - }, - ], - errors: [error('Do not import Node.js builtin module "fs"')], - }), - test({ + options: [{ allow: ['path'] }], + errors: [createBuilinError('fs')], + }), + tInvalid({ code: 'import path from "node:path"', - errors: [error('Do not import Node.js builtin module "node:path"')], + errors: [createBuilinError('node:path')], }), - test({ + tInvalid({ code: 'var path = require("node:path")', - errors: [error('Do not import Node.js builtin module "node:path"')], + errors: [createBuilinError('node:path')], }), - test({ + tInvalid({ code: 'import fs from "node:fs"', - errors: [error('Do not import Node.js builtin module "node:fs"')], + errors: [createBuilinError('node:fs')], }), - test({ + tInvalid({ code: 'var fs = require("node:fs")', - errors: [error('Do not import Node.js builtin module "node:fs"')], + errors: [createBuilinError('node:fs')], }), - test({ + tInvalid({ code: 'import fs from "node:fs"', - options: [ - { - allow: ['node:path'], - }, - ], - errors: [error('Do not import Node.js builtin module "node:fs"')], + options: [{ allow: ['node:path'] }], + errors: [createBuilinError('node:fs')], }), ], }) From 99900ffc2b1a3b64aa28a23709540e4b5614725c Mon Sep 17 00:00:00 2001 From: Marco Pasqualetti Date: Tue, 19 Nov 2024 08:57:17 +0100 Subject: [PATCH 34/49] test(no-relative-packages): migrate to `createRuleTestCaseFunctions` --- test/rules/no-relative-packages.spec.ts | 54 ++++++++++++++++--------- 1 file changed, 36 insertions(+), 18 deletions(-) diff --git a/test/rules/no-relative-packages.spec.ts b/test/rules/no-relative-packages.spec.ts index 2b664b3fb..487469f78 100644 --- a/test/rules/no-relative-packages.spec.ts +++ b/test/rules/no-relative-packages.spec.ts @@ -1,90 +1,108 @@ import path from 'node:path' import { RuleTester as TSESLintRuleTester } from '@typescript-eslint/rule-tester' +import type { TestCaseError as TSESLintTestCaseError } from '@typescript-eslint/rule-tester' -import { test, testFilePath } from '../utils' +import { createRuleTestCaseFunctions, testFilePath } from '../utils' +import type { GetRuleModuleMessageIds } from '../utils' import rule from 'eslint-plugin-import-x/rules/no-relative-packages' const ruleTester = new TSESLintRuleTester() +const { tValid, tInvalid } = createRuleTestCaseFunctions() + +function createNotFoundError( + importPath: string, + properImport: string, +): TSESLintTestCaseError> { + return { + messageId: 'noAllowed', + data: { properImport, importPath }, + } +} + ruleTester.run('no-relative-packages', rule, { valid: [ - test({ + tValid({ code: 'import foo from "./index.js"', filename: testFilePath('./package/index.js'), }), - test({ + tValid({ code: 'import bar from "../bar"', filename: testFilePath('./package/index.js'), }), - test({ + tValid({ code: 'import {foo} from "a"', filename: testFilePath('./package-named/index.js'), }), - test({ + tValid({ code: 'const bar = require("../bar.js")', filename: testFilePath('./package/index.js'), }), - test({ + tValid({ code: 'const bar = require("../not/a/file/path.js")', filename: testFilePath('./package/index.js'), }), - test({ + tValid({ code: 'import "package"', filename: testFilePath('./package/index.js'), }), - test({ + tValid({ code: 'require("../bar.js")', filename: testFilePath('./package/index.js'), }), ], invalid: [ - test({ + tInvalid({ code: 'import foo from "./package-named"', filename: testFilePath('./bar.js'), errors: [ { - message: - 'Relative import from another package is not allowed. Use `package-named` instead of `./package-named`', + ...createNotFoundError('./package-named', 'package-named'), line: 1, column: 17, }, ], output: 'import foo from "package-named"', }), - test({ + tInvalid({ code: 'import foo from "../package-named"', filename: testFilePath('./package/index.js'), errors: [ { - message: - 'Relative import from another package is not allowed. Use `package-named` instead of `../package-named`', + ...createNotFoundError('../package-named', 'package-named'), line: 1, column: 17, }, ], output: 'import foo from "package-named"', }), - test({ + tInvalid({ code: 'import foo from "../package-scoped"', filename: testFilePath('./package/index.js'), errors: [ { - message: `Relative import from another package is not allowed. Use \`${path.normalize('@scope/package-named')}\` instead of \`../package-scoped\``, + ...createNotFoundError( + '../package-scoped', + path.normalize('@scope/package-named'), + ), line: 1, column: 17, }, ], output: `import foo from "@scope/package-named"`, }), - test({ + tInvalid({ code: 'import bar from "../bar"', filename: testFilePath('./package-named/index.js'), errors: [ { - message: `Relative import from another package is not allowed. Use \`${path.normalize('eslint-plugin-import-x/test/fixtures/bar')}\` instead of \`../bar\``, + ...createNotFoundError( + '../bar', + path.normalize('eslint-plugin-import-x/test/fixtures/bar'), + ), line: 1, column: 17, }, From 75a66a335c077704a937fc899bc6a5a8ff3abb62 Mon Sep 17 00:00:00 2001 From: Marco Pasqualetti Date: Tue, 19 Nov 2024 09:06:16 +0100 Subject: [PATCH 35/49] test(no-relative-parent-imports): migrate to `createRuleTestCaseFunctions` --- test/rules/no-relative-parent-imports.spec.ts | 86 +++++++++++-------- 1 file changed, 49 insertions(+), 37 deletions(-) diff --git a/test/rules/no-relative-parent-imports.spec.ts b/test/rules/no-relative-parent-imports.spec.ts index d11193917..21698125e 100644 --- a/test/rules/no-relative-parent-imports.spec.ts +++ b/test/rules/no-relative-parent-imports.spec.ts @@ -1,125 +1,137 @@ import { RuleTester as TSESLintRuleTester } from '@typescript-eslint/rule-tester' +import type { TestCaseError as TSESLintTestCaseError } from '@typescript-eslint/rule-tester' -import { parsers, test as _test, testFilePath } from '../utils' -import type { ValidTestCase } from '../utils' +import { parsers, createRuleTestCaseFunctions, testFilePath } from '../utils' +import type { GetRuleModuleMessageIds } from '../utils' import rule from 'eslint-plugin-import-x/rules/no-relative-parent-imports' -const test = (def: T) => - _test({ - ...def, +const ruleTester = new TSESLintRuleTester() + +const { tValid: _tValid, tInvalid: _tInvalid } = + createRuleTestCaseFunctions() + +const tValid = (testCase => + _tValid({ + ...testCase, filename: testFilePath('./internal-modules/plugins/plugin2/index.js'), languageOptions: { parser: require(parsers.BABEL) }, - }) + })) as typeof _tValid -const ruleTester = new TSESLintRuleTester() +const tInvalid = (testCase => + _tInvalid({ + ...testCase, + filename: testFilePath('./internal-modules/plugins/plugin2/index.js'), + languageOptions: { parser: require(parsers.BABEL) }, + })) as typeof _tInvalid + +function createNoAllowedError( + filename: string, + depPath: string, +): TSESLintTestCaseError> { + return { messageId: 'noAllowed', data: { filename, depPath } } +} ruleTester.run('no-relative-parent-imports', rule, { valid: [ - test({ + tValid({ code: 'import foo from "./internal.js"', }), - test({ + tValid({ code: 'import foo from "./app/index.js"', }), - test({ + tValid({ code: 'import foo from "package"', }), - test({ + tValid({ code: 'require("./internal.js")', options: [{ commonjs: true }], }), - test({ + tValid({ code: 'require("./app/index.js")', options: [{ commonjs: true }], }), - test({ + tValid({ code: 'require("package")', options: [{ commonjs: true }], }), - test({ + tValid({ code: 'import("./internal.js")', }), - test({ + tValid({ code: 'import("./app/index.js")', }), - test({ + tValid({ code: 'import(".")', }), - test({ + tValid({ code: 'import("path")', }), - test({ + tValid({ code: 'import("package")', }), - test({ + tValid({ code: 'import("@scope/package")', }), ], invalid: [ - test({ + tInvalid({ code: 'import foo from "../plugin.js"', errors: [ { - message: - "Relative imports from parent directories are not allowed. Please either pass what you're importing through at runtime (dependency injection), move `index.js` to same directory as `../plugin.js` or consider making `../plugin.js` a package.", + ...createNoAllowedError('index.js', '../plugin.js'), line: 1, column: 17, }, ], }), - test({ + tInvalid({ code: 'require("../plugin.js")', options: [{ commonjs: true }], errors: [ { - message: - "Relative imports from parent directories are not allowed. Please either pass what you're importing through at runtime (dependency injection), move `index.js` to same directory as `../plugin.js` or consider making `../plugin.js` a package.", + ...createNoAllowedError('index.js', '../plugin.js'), line: 1, column: 9, }, ], }), - test({ + tInvalid({ code: 'import("../plugin.js")', errors: [ { - message: - "Relative imports from parent directories are not allowed. Please either pass what you're importing through at runtime (dependency injection), move `index.js` to same directory as `../plugin.js` or consider making `../plugin.js` a package.", + ...createNoAllowedError('index.js', '../plugin.js'), line: 1, column: 8, }, ], }), - test({ + tInvalid({ code: 'import foo from "./../plugin.js"', errors: [ { - message: - "Relative imports from parent directories are not allowed. Please either pass what you're importing through at runtime (dependency injection), move `index.js` to same directory as `./../plugin.js` or consider making `./../plugin.js` a package.", + ...createNoAllowedError('index.js', './../plugin.js'), line: 1, column: 17, }, ], }), - test({ + tInvalid({ code: 'import foo from "../../api/service"', errors: [ { - message: - "Relative imports from parent directories are not allowed. Please either pass what you're importing through at runtime (dependency injection), move `index.js` to same directory as `../../api/service` or consider making `../../api/service` a package.", + ...createNoAllowedError('index.js', '../../api/service'), line: 1, column: 17, }, ], }), - test({ + tInvalid({ code: 'import("../../api/service")', errors: [ { - message: - "Relative imports from parent directories are not allowed. Please either pass what you're importing through at runtime (dependency injection), move `index.js` to same directory as `../../api/service` or consider making `../../api/service` a package.", + ...createNoAllowedError('index.js', '../../api/service'), line: 1, column: 8, }, From c24546ceb2c829b5377be76443612ffa8ef108e7 Mon Sep 17 00:00:00 2001 From: Marco Pasqualetti Date: Tue, 19 Nov 2024 09:17:24 +0100 Subject: [PATCH 36/49] test(utils): add defaultOptions param to `createRuleTestCaseFunctions` --- test/rules/no-cycle.spec.ts | 17 +++------------- test/rules/no-relative-parent-imports.spec.ts | 20 ++++--------------- test/utils.ts | 15 +++++++++++--- 3 files changed, 19 insertions(+), 33 deletions(-) diff --git a/test/rules/no-cycle.spec.ts b/test/rules/no-cycle.spec.ts index 8dcb263ae..cfc01377f 100644 --- a/test/rules/no-cycle.spec.ts +++ b/test/rules/no-cycle.spec.ts @@ -8,20 +8,9 @@ import rule from 'eslint-plugin-import-x/rules/no-cycle' const ruleTester = new TSESLintRuleTester() -const { tValid: _tValid, tInvalid: _tInvalid } = - createRuleTestCaseFunctions() - -const tValid = (testCase => - _tValid({ - filename: testFilePath('./cycles/depth-zero.js'), - ...testCase, - })) as typeof _tValid - -const tInvalid = (testCase => - _tInvalid({ - filename: testFilePath('./cycles/depth-zero.js'), - ...testCase, - })) as typeof _tInvalid +const { tValid, tInvalid } = createRuleTestCaseFunctions({ + filename: testFilePath('./cycles/depth-zero.js'), +}) const createCycleSourceError = ( source: string, diff --git a/test/rules/no-relative-parent-imports.spec.ts b/test/rules/no-relative-parent-imports.spec.ts index 21698125e..36acdf3c5 100644 --- a/test/rules/no-relative-parent-imports.spec.ts +++ b/test/rules/no-relative-parent-imports.spec.ts @@ -8,22 +8,10 @@ import rule from 'eslint-plugin-import-x/rules/no-relative-parent-imports' const ruleTester = new TSESLintRuleTester() -const { tValid: _tValid, tInvalid: _tInvalid } = - createRuleTestCaseFunctions() - -const tValid = (testCase => - _tValid({ - ...testCase, - filename: testFilePath('./internal-modules/plugins/plugin2/index.js'), - languageOptions: { parser: require(parsers.BABEL) }, - })) as typeof _tValid - -const tInvalid = (testCase => - _tInvalid({ - ...testCase, - filename: testFilePath('./internal-modules/plugins/plugin2/index.js'), - languageOptions: { parser: require(parsers.BABEL) }, - })) as typeof _tInvalid +const { tValid, tInvalid } = createRuleTestCaseFunctions({ + filename: testFilePath('./internal-modules/plugins/plugin2/index.js'), + languageOptions: { parser: require(parsers.BABEL) }, +}) function createNoAllowedError( filename: string, diff --git a/test/utils.ts b/test/utils.ts index 65064296e..601383e23 100644 --- a/test/utils.ts +++ b/test/utils.ts @@ -160,6 +160,10 @@ export type RuleRunTests< * }) * ``` * + * @param defaultOptions If you have a specific set of options + * that need to be passed to each test case you + * can supply them directly to this function. + * * If the `TRule` parameter is omitted default types are used. */ export function createRuleTestCaseFunctions< @@ -167,10 +171,15 @@ export function createRuleTestCaseFunctions< TData extends GetRuleModuleTypes = GetRuleModuleTypes, Valid = TSESLintValidTestCase, Invalid = TSESLintInvalidTestCase, ->(): { tValid: (t: Valid) => Valid; tInvalid: (t: Invalid) => Invalid } { +>( + defaultOptions: Pick< + TSESLintValidTestCase, + 'filename' | 'languageOptions' | 'settings' + > = {}, +): { tValid: (t: Valid) => Valid; tInvalid: (t: Invalid) => Invalid } { return { - tValid: createRuleTestCase as never, - tInvalid: createRuleTestCase as never, + tValid: t => createRuleTestCase({ ...defaultOptions, ...t } as never), + tInvalid: t => createRuleTestCase({ ...defaultOptions, ...t } as never), } } From ae3b808abeaf62670f70e65b7a71765278bc8bda Mon Sep 17 00:00:00 2001 From: Marco Pasqualetti Date: Tue, 19 Nov 2024 09:59:15 +0100 Subject: [PATCH 37/49] test(no-rename-default): migrate to `createRuleTestCaseFunctions` --- test/rules/no-rename-default.spec.ts | 852 +++++++++++++++++---------- 1 file changed, 554 insertions(+), 298 deletions(-) diff --git a/test/rules/no-rename-default.spec.ts b/test/rules/no-rename-default.spec.ts index c7e040979..be4a61761 100644 --- a/test/rules/no-rename-default.spec.ts +++ b/test/rules/no-rename-default.spec.ts @@ -1,11 +1,33 @@ import { RuleTester } from '@typescript-eslint/rule-tester' +import type { TestCaseError as TSESLintTestCaseError } from '@typescript-eslint/rule-tester' +import type { TSESTree } from '@typescript-eslint/utils' -import { parsers, test } from '../utils' +import { parsers, createRuleTestCaseFunctions } from '../utils' +import type { GetRuleModuleMessageIds } from '../utils' import rule from 'eslint-plugin-import-x/rules/no-rename-default' const ruleTester = new RuleTester() +const { tValid, tInvalid } = createRuleTestCaseFunctions() + +function createRenameDefaultError( + data: { + importBasename: string + defaultExportName: string + requiresOrImports: string + importName: string + suggestion: string + }, + type: `${TSESTree.AST_NODE_TYPES}`, +): TSESLintTestCaseError> { + return { + messageId: 'renameDefault', + data, + type: type as TSESTree.AST_NODE_TYPES, + } +} + // IMPORT // anonymous-arrow.js // anonymous-arrow-async.js @@ -31,23 +53,23 @@ ruleTester.run('no-rename-default', rule, { // anonymous-primitive.js ruleTester.run('no-rename-default', rule, { valid: [ - test({ + tValid({ code: `const _ = require('./no-rename-default/anonymous-arrow')`, options: [{ commonjs: true }], }), - test({ + tValid({ code: `const _ = require('./no-rename-default/anonymous-arrow-async')`, options: [{ commonjs: true }], }), - test({ + tValid({ code: `const _ = require('./no-rename-default/anonymous-class')`, options: [{ commonjs: true }], }), - test({ + tValid({ code: `const _ = require('./no-rename-default/anonymous-object')`, options: [{ commonjs: true }], }), - test({ + tValid({ code: `const _ = require('./no-rename-default/anonymous-primitive')`, options: [{ commonjs: true }], }), @@ -76,84 +98,128 @@ ruleTester.run('no-rename-default', rule, { `import generator from './no-rename-default/assign-generator-named'`, ], invalid: [ - test({ + tInvalid({ code: `import myArrow from './no-rename-default/assign-arrow'`, errors: [ - { - message: - "Caution: `assign-arrow.js` has a default export `arrow`. This imports `arrow` as `myArrow`. Check if you meant to write `import arrow from './no-rename-default/assign-arrow'` instead.", - type: 'ImportDefaultSpecifier', - }, + createRenameDefaultError( + { + importBasename: 'assign-arrow.js', + defaultExportName: 'arrow', + requiresOrImports: 'imports', + importName: 'myArrow', + suggestion: "import arrow from './no-rename-default/assign-arrow'", + }, + 'ImportDefaultSpecifier', + ), ], }), - test({ + tInvalid({ code: `import myArrowAsync from './no-rename-default/assign-arrow-async'`, errors: [ - { - message: - "Caution: `assign-arrow-async.js` has a default export `arrowAsync`. This imports `arrowAsync` as `myArrowAsync`. Check if you meant to write `import arrowAsync from './no-rename-default/assign-arrow-async'` instead.", - type: 'ImportDefaultSpecifier', - }, + createRenameDefaultError( + { + importBasename: 'assign-arrow-async.js', + defaultExportName: 'arrowAsync', + requiresOrImports: 'imports', + importName: 'myArrowAsync', + suggestion: + "import arrowAsync from './no-rename-default/assign-arrow-async'", + }, + 'ImportDefaultSpecifier', + ), ], }), - test({ + tInvalid({ code: `import MyUser from './no-rename-default/assign-class'`, errors: [ - { - message: - "Caution: `assign-class.js` has a default export `User`. This imports `User` as `MyUser`. Check if you meant to write `import User from './no-rename-default/assign-class'` instead.", - type: 'ImportDefaultSpecifier', - }, + createRenameDefaultError( + { + importBasename: 'assign-class.js', + defaultExportName: 'User', + requiresOrImports: 'imports', + importName: 'MyUser', + suggestion: "import User from './no-rename-default/assign-class'", + }, + 'ImportDefaultSpecifier', + ), ], }), - test({ + tInvalid({ code: `import MyUser from './no-rename-default/assign-class-named'`, errors: [ - { - message: - "Caution: `assign-class-named.js` has a default export `User`. This imports `User` as `MyUser`. Check if you meant to write `import User from './no-rename-default/assign-class-named'` instead.", - type: 'ImportDefaultSpecifier', - }, + createRenameDefaultError( + { + importBasename: 'assign-class-named.js', + defaultExportName: 'User', + requiresOrImports: 'imports', + importName: 'MyUser', + suggestion: + "import User from './no-rename-default/assign-class-named'", + }, + 'ImportDefaultSpecifier', + ), ], }), - test({ + tInvalid({ code: `import myFn from './no-rename-default/assign-fn'`, errors: [ - { - message: - "Caution: `assign-fn.js` has a default export `fn`. This imports `fn` as `myFn`. Check if you meant to write `import fn from './no-rename-default/assign-fn'` instead.", - type: 'ImportDefaultSpecifier', - }, + createRenameDefaultError( + { + importBasename: 'assign-fn.js', + defaultExportName: 'fn', + requiresOrImports: 'imports', + importName: 'myFn', + suggestion: "import fn from './no-rename-default/assign-fn'", + }, + 'ImportDefaultSpecifier', + ), ], }), - test({ + tInvalid({ code: `import myFn from './no-rename-default/assign-fn-named'`, errors: [ - { - message: - "Caution: `assign-fn-named.js` has a default export `fn`. This imports `fn` as `myFn`. Check if you meant to write `import fn from './no-rename-default/assign-fn-named'` instead.", - type: 'ImportDefaultSpecifier', - }, + createRenameDefaultError( + { + importBasename: 'assign-fn-named.js', + defaultExportName: 'fn', + requiresOrImports: 'imports', + importName: 'myFn', + suggestion: "import fn from './no-rename-default/assign-fn-named'", + }, + 'ImportDefaultSpecifier', + ), ], }), - test({ + tInvalid({ code: `import myGenerator from './no-rename-default/assign-generator'`, errors: [ - { - message: - "Caution: `assign-generator.js` has a default export `generator`. This imports `generator` as `myGenerator`. Check if you meant to write `import generator from './no-rename-default/assign-generator'` instead.", - type: 'ImportDefaultSpecifier', - }, + createRenameDefaultError( + { + importBasename: 'assign-generator.js', + defaultExportName: 'generator', + requiresOrImports: 'imports', + importName: 'myGenerator', + suggestion: + "import generator from './no-rename-default/assign-generator'", + }, + 'ImportDefaultSpecifier', + ), ], }), - test({ + tInvalid({ code: `import myGenerator from './no-rename-default/assign-generator-named'`, errors: [ - { - message: - "Caution: `assign-generator-named.js` has a default export `generator`. This imports `generator` as `myGenerator`. Check if you meant to write `import generator from './no-rename-default/assign-generator-named'` instead.", - type: 'ImportDefaultSpecifier', - }, + createRenameDefaultError( + { + importBasename: 'assign-generator-named.js', + defaultExportName: 'generator', + requiresOrImports: 'imports', + importName: 'myGenerator', + suggestion: + "import generator from './no-rename-default/assign-generator-named'", + }, + 'ImportDefaultSpecifier', + ), ], }), ], @@ -170,35 +236,35 @@ ruleTester.run('no-rename-default', rule, { // assign-generator-named.js ruleTester.run('no-rename-default', rule, { valid: [ - test({ + tValid({ code: `import myArrow from './no-rename-default/assign-arrow'`, options: [{ preventRenamingBindings: false }], }), - test({ + tValid({ code: `import myArrowAsync from './no-rename-default/assign-arrow-async'`, options: [{ preventRenamingBindings: false }], }), - test({ + tValid({ code: `import MyUser from './no-rename-default/assign-class'`, options: [{ preventRenamingBindings: false }], }), - test({ + tValid({ code: `import MyUser from './no-rename-default/assign-class-named'`, options: [{ preventRenamingBindings: false }], }), - test({ + tValid({ code: `import myFn from './no-rename-default/assign-fn'`, options: [{ preventRenamingBindings: false }], }), - test({ + tValid({ code: `import myFn from './no-rename-default/assign-fn-named'`, options: [{ preventRenamingBindings: false }], }), - test({ + tValid({ code: `import myGenerator from './no-rename-default/assign-generator'`, options: [{ preventRenamingBindings: false }], }), - test({ + tValid({ code: `import myGenerator from './no-rename-default/assign-generator-named'`, options: [{ preventRenamingBindings: false }], }), @@ -217,126 +283,173 @@ ruleTester.run('no-rename-default', rule, { // assign-generator-named.js ruleTester.run('no-rename-default', rule, { valid: [ - test({ + tValid({ code: `const arrow = require('./no-rename-default/assign-arrow')`, options: [{ commonjs: true }], }), - test({ + tValid({ code: `const arrowAsync = require('./no-rename-default/assign-arrow-async')`, options: [{ commonjs: true }], }), - test({ + tValid({ code: `const User = require('./no-rename-default/assign-class')`, options: [{ commonjs: true }], }), - test({ + tValid({ code: `const User = require('./no-rename-default/assign-class-named')`, options: [{ commonjs: true }], }), - test({ + tValid({ code: `const fn = require('./no-rename-default/assign-fn')`, options: [{ commonjs: true }], }), - test({ + tValid({ code: `const fn = require('./no-rename-default/assign-fn-named')`, options: [{ commonjs: true }], }), - test({ + tValid({ code: `const generator = require('./no-rename-default/assign-generator')`, options: [{ commonjs: true }], }), - test({ + tValid({ code: `const generator = require('./no-rename-default/assign-generator-named')`, options: [{ commonjs: true }], }), ], invalid: [ - test({ + tInvalid({ code: `const myArrow = require('./no-rename-default/assign-arrow')`, options: [{ commonjs: true }], errors: [ - { - message: - "Caution: `assign-arrow.js` has a default export `arrow`. This requires `arrow` as `myArrow`. Check if you meant to write `const arrow = require('./no-rename-default/assign-arrow')` instead.", - type: 'VariableDeclarator', - }, + createRenameDefaultError( + { + importBasename: 'assign-arrow.js', + defaultExportName: 'arrow', + requiresOrImports: 'requires', + importName: 'myArrow', + suggestion: + "const arrow = require('./no-rename-default/assign-arrow')", + }, + 'VariableDeclarator', + ), ], }), - test({ + tInvalid({ code: `const myArrowAsync = require('./no-rename-default/assign-arrow-async')`, options: [{ commonjs: true }], errors: [ - { - message: - "Caution: `assign-arrow-async.js` has a default export `arrowAsync`. This requires `arrowAsync` as `myArrowAsync`. Check if you meant to write `const arrowAsync = require('./no-rename-default/assign-arrow-async')` instead.", - type: 'VariableDeclarator', - }, + createRenameDefaultError( + { + importBasename: 'assign-arrow-async.js', + defaultExportName: 'arrowAsync', + requiresOrImports: 'requires', + importName: 'myArrowAsync', + suggestion: + "const arrowAsync = require('./no-rename-default/assign-arrow-async')", + }, + 'VariableDeclarator', + ), ], }), - test({ + tInvalid({ code: `const MyUser = require('./no-rename-default/assign-class')`, options: [{ commonjs: true }], errors: [ - { - message: - "Caution: `assign-class.js` has a default export `User`. This requires `User` as `MyUser`. Check if you meant to write `const User = require('./no-rename-default/assign-class')` instead.", - type: 'VariableDeclarator', - }, + createRenameDefaultError( + { + importBasename: 'assign-class.js', + defaultExportName: 'User', + requiresOrImports: 'requires', + importName: 'MyUser', + suggestion: + "const User = require('./no-rename-default/assign-class')", + }, + 'VariableDeclarator', + ), ], }), - test({ + tInvalid({ code: `const MyUser = require('./no-rename-default/assign-class-named')`, options: [{ commonjs: true }], errors: [ - { - message: - "Caution: `assign-class-named.js` has a default export `User`. This requires `User` as `MyUser`. Check if you meant to write `const User = require('./no-rename-default/assign-class-named')` instead.", - type: 'VariableDeclarator', - }, + createRenameDefaultError( + { + importBasename: 'assign-class-named.js', + defaultExportName: 'User', + requiresOrImports: 'requires', + importName: 'MyUser', + suggestion: + "const User = require('./no-rename-default/assign-class-named')", + }, + 'VariableDeclarator', + ), ], }), - test({ + tInvalid({ code: `const myFn = require('./no-rename-default/assign-fn')`, options: [{ commonjs: true }], errors: [ - { - message: - "Caution: `assign-fn.js` has a default export `fn`. This requires `fn` as `myFn`. Check if you meant to write `const fn = require('./no-rename-default/assign-fn')` instead.", - type: 'VariableDeclarator', - }, + createRenameDefaultError( + { + importBasename: 'assign-fn.js', + defaultExportName: 'fn', + requiresOrImports: 'requires', + importName: 'myFn', + suggestion: "const fn = require('./no-rename-default/assign-fn')", + }, + 'VariableDeclarator', + ), ], }), - test({ + tInvalid({ code: `const myFn = require('./no-rename-default/assign-fn-named')`, options: [{ commonjs: true }], errors: [ - { - message: - "Caution: `assign-fn-named.js` has a default export `fn`. This requires `fn` as `myFn`. Check if you meant to write `const fn = require('./no-rename-default/assign-fn-named')` instead.", - type: 'VariableDeclarator', - }, + createRenameDefaultError( + { + importBasename: 'assign-fn-named.js', + defaultExportName: 'fn', + requiresOrImports: 'requires', + importName: 'myFn', + suggestion: + "const fn = require('./no-rename-default/assign-fn-named')", + }, + 'VariableDeclarator', + ), ], }), - test({ + tInvalid({ code: `const myGenerator = require('./no-rename-default/assign-generator')`, options: [{ commonjs: true }], errors: [ - { - message: - "Caution: `assign-generator.js` has a default export `generator`. This requires `generator` as `myGenerator`. Check if you meant to write `const generator = require('./no-rename-default/assign-generator')` instead.", - type: 'VariableDeclarator', - }, + createRenameDefaultError( + { + importBasename: 'assign-generator.js', + defaultExportName: 'generator', + requiresOrImports: 'requires', + importName: 'myGenerator', + suggestion: + "const generator = require('./no-rename-default/assign-generator')", + }, + 'VariableDeclarator', + ), ], }), - test({ + tInvalid({ code: `const myGenerator = require('./no-rename-default/assign-generator-named')`, options: [{ commonjs: true }], errors: [ - { - message: - "Caution: `assign-generator-named.js` has a default export `generator`. This requires `generator` as `myGenerator`. Check if you meant to write `const generator = require('./no-rename-default/assign-generator-named')` instead.", - type: 'VariableDeclarator', - }, + createRenameDefaultError( + { + importBasename: 'assign-generator-named.js', + defaultExportName: 'generator', + requiresOrImports: 'requires', + importName: 'myGenerator', + suggestion: + "const generator = require('./no-rename-default/assign-generator-named')", + }, + 'VariableDeclarator', + ), ], }), ], @@ -353,35 +466,35 @@ ruleTester.run('no-rename-default', rule, { // assign-generator-named.js ruleTester.run('no-renamed-default', rule, { valid: [ - test({ + tValid({ code: `const myArrow = require('./no-rename-default/assign-arrow')`, options: [{ commonjs: true, preventRenamingBindings: false }], }), - test({ + tValid({ code: `const myArrowAsync = require('./no-rename-default/assign-arrow-async')`, options: [{ commonjs: true, preventRenamingBindings: false }], }), - test({ + tValid({ code: `const MyUser = require('./no-rename-default/assign-class')`, options: [{ commonjs: true, preventRenamingBindings: false }], }), - test({ + tValid({ code: `const MyUser = require('./no-rename-default/assign-class-named')`, options: [{ commonjs: true, preventRenamingBindings: false }], }), - test({ + tValid({ code: `const myFn = require('./no-rename-default/assign-fn')`, options: [{ commonjs: true, preventRenamingBindings: false }], }), - test({ + tValid({ code: `const myFn = require('./no-rename-default/assign-fn-named')`, options: [{ commonjs: true, preventRenamingBindings: false }], }), - test({ + tValid({ code: `const myGenerator = require('./no-rename-default/assign-generator')`, options: [{ commonjs: true, preventRenamingBindings: false }], }), - test({ + tValid({ code: `const myGenerator = require('./no-rename-default/assign-generator-named')`, options: [{ commonjs: true, preventRenamingBindings: false }], }), @@ -394,14 +507,19 @@ ruleTester.run('no-renamed-default', rule, { ruleTester.run('no-rename-default', rule, { valid: [`import User from './no-rename-default/class-user'`], invalid: [ - test({ + tInvalid({ code: `import MyUser from './no-rename-default/class-user'`, errors: [ - { - message: - "Caution: `class-user.js` has a default export `User`. This imports `User` as `MyUser`. Check if you meant to write `import User from './no-rename-default/class-user'` instead.", - type: 'ImportDefaultSpecifier', - }, + createRenameDefaultError( + { + importBasename: 'class-user.js', + defaultExportName: 'User', + requiresOrImports: 'imports', + importName: 'MyUser', + suggestion: "import User from './no-rename-default/class-user'", + }, + 'ImportDefaultSpecifier', + ), ], }), ], @@ -411,21 +529,27 @@ ruleTester.run('no-rename-default', rule, { // class-user.js ruleTester.run('no-rename-default', rule, { valid: [ - test({ + tValid({ code: `const User = require('./no-rename-default/class-user')`, options: [{ commonjs: true }], }), ], invalid: [ - test({ + tInvalid({ code: `const MyUser = require('./no-rename-default/class-user')`, options: [{ commonjs: true }], errors: [ - { - message: - "Caution: `class-user.js` has a default export `User`. This requires `User` as `MyUser`. Check if you meant to write `const User = require('./no-rename-default/class-user')` instead.", - type: 'VariableDeclarator', - }, + createRenameDefaultError( + { + importBasename: 'class-user.js', + defaultExportName: 'User', + requiresOrImports: 'requires', + importName: 'MyUser', + suggestion: + "const User = require('./no-rename-default/class-user')", + }, + 'VariableDeclarator', + ), ], }), ], @@ -452,96 +576,150 @@ ruleTester.run('no-rename-default', rule, { `, ], invalid: [ - test({ + tInvalid({ code: `import bar from './no-rename-default/const-foo'`, errors: [ - { - message: - "Caution: `const-foo.js` has a default export `foo`. This imports `foo` as `bar`. Check if you meant to write `import foo from './no-rename-default/const-foo'` instead.", - type: 'ImportDefaultSpecifier', - }, + createRenameDefaultError( + { + importBasename: 'const-foo.js', + defaultExportName: 'foo', + requiresOrImports: 'imports', + importName: 'bar', + suggestion: "import foo from './no-rename-default/const-foo'", + }, + 'ImportDefaultSpecifier', + ), ], }), - test({ + tInvalid({ code: `import { default as bar } from './no-rename-default/const-foo'`, errors: [ - { - message: - "Caution: `const-foo.js` has a default export `foo`. This imports `foo` as `bar`. Check if you meant to write `import { default as foo } from './no-rename-default/const-foo'` instead.", - type: 'ImportSpecifier', - }, + createRenameDefaultError( + { + importBasename: 'const-foo.js', + defaultExportName: 'foo', + requiresOrImports: 'imports', + importName: 'bar', + suggestion: + "import { default as foo } from './no-rename-default/const-foo'", + }, + 'ImportSpecifier', + ), ], }), - test({ + tInvalid({ code: `import { default as bar, fooNamed1 } from './no-rename-default/const-foo'`, errors: [ - { - message: - "Caution: `const-foo.js` has a default export `foo`. This imports `foo` as `bar`. Check if you meant to write `import { default as foo } from './no-rename-default/const-foo'` instead.", - type: 'ImportSpecifier', - }, + createRenameDefaultError( + { + importBasename: 'const-foo.js', + defaultExportName: 'foo', + requiresOrImports: 'imports', + importName: 'bar', + suggestion: + "import { default as foo } from './no-rename-default/const-foo'", + }, + 'ImportSpecifier', + ), ], }), - test({ + tInvalid({ code: `import bar, { fooNamed1 } from './no-rename-default/const-foo'`, errors: [ - { - message: - "Caution: `const-foo.js` has a default export `foo`. This imports `foo` as `bar`. Check if you meant to write `import foo from './no-rename-default/const-foo'` instead.", - type: 'ImportDefaultSpecifier', - }, + createRenameDefaultError( + { + importBasename: 'const-foo.js', + defaultExportName: 'foo', + requiresOrImports: 'imports', + importName: 'bar', + suggestion: "import foo from './no-rename-default/const-foo'", + }, + 'ImportDefaultSpecifier', + ), ], }), - test({ + tInvalid({ code: `import foo from './no-rename-default/const-bar' import bar from './no-rename-default/const-foo'`, errors: [ - { - message: - "Caution: `const-bar.js` has a default export `bar`. This imports `bar` as `foo`. Check if you meant to write `import bar from './no-rename-default/const-bar'` instead.", - type: 'ImportDefaultSpecifier', - }, - { - message: - "Caution: `const-foo.js` has a default export `foo`. This imports `foo` as `bar`. Check if you meant to write `import foo from './no-rename-default/const-foo'` instead.", - type: 'ImportDefaultSpecifier', - }, + createRenameDefaultError( + { + importBasename: 'const-bar.js', + defaultExportName: 'bar', + requiresOrImports: 'imports', + importName: 'foo', + suggestion: "import bar from './no-rename-default/const-bar'", + }, + 'ImportDefaultSpecifier', + ), + createRenameDefaultError( + { + importBasename: 'const-foo.js', + defaultExportName: 'foo', + requiresOrImports: 'imports', + importName: 'bar', + suggestion: "import foo from './no-rename-default/const-foo'", + }, + 'ImportDefaultSpecifier', + ), ], }), - test({ + tInvalid({ code: `import findUsers from './no-rename-default/fn-get-users'`, errors: [ - { - message: - "Caution: `fn-get-users.js` has a default export `getUsers`. This imports `getUsers` as `findUsers`. Check if you meant to write `import getUsers from './no-rename-default/fn-get-users'` instead.", - type: 'ImportDefaultSpecifier', - }, + createRenameDefaultError( + { + importBasename: 'fn-get-users.js', + defaultExportName: 'getUsers', + requiresOrImports: 'imports', + importName: 'findUsers', + suggestion: + "import getUsers from './no-rename-default/fn-get-users'", + }, + 'ImportDefaultSpecifier', + ), ], }), - test({ + tInvalid({ code: `import findUsersSync from './no-rename-default/fn-get-users-sync'`, errors: [ - { - message: - "Caution: `fn-get-users-sync.js` has a default export `getUsersSync`. This imports `getUsersSync` as `findUsersSync`. Check if you meant to write `import getUsersSync from './no-rename-default/fn-get-users-sync'` instead.", - type: 'ImportDefaultSpecifier', - }, + createRenameDefaultError( + { + importBasename: 'fn-get-users-sync.js', + defaultExportName: 'getUsersSync', + requiresOrImports: 'imports', + importName: 'findUsersSync', + suggestion: + "import getUsersSync from './no-rename-default/fn-get-users-sync'", + }, + 'ImportDefaultSpecifier', + ), ], }), - test({ + tInvalid({ code: `import foo, { barNamed1 } from './no-rename-default/const-bar' import bar, { fooNamed1 } from './no-rename-default/const-foo'`, errors: [ - { - message: - "Caution: `const-bar.js` has a default export `bar`. This imports `bar` as `foo`. Check if you meant to write `import bar from './no-rename-default/const-bar'` instead.", - type: 'ImportDefaultSpecifier', - }, - { - message: - "Caution: `const-foo.js` has a default export `foo`. This imports `foo` as `bar`. Check if you meant to write `import foo from './no-rename-default/const-foo'` instead.", - type: 'ImportDefaultSpecifier', - }, + createRenameDefaultError( + { + importBasename: 'const-bar.js', + defaultExportName: 'bar', + requiresOrImports: 'imports', + importName: 'foo', + suggestion: "import bar from './no-rename-default/const-bar'", + }, + 'ImportDefaultSpecifier', + ), + createRenameDefaultError( + { + importBasename: 'const-foo.js', + defaultExportName: 'foo', + requiresOrImports: 'imports', + importName: 'bar', + suggestion: "import foo from './no-rename-default/const-foo'", + }, + 'ImportDefaultSpecifier', + ), ], }), ], @@ -552,40 +730,40 @@ ruleTester.run('no-rename-default', rule, { // const-foo.js ruleTester.run('no-rename-default', rule, { valid: [ - test({ + tValid({ code: `const foo = require('./no-rename-default/const-foo')`, options: [{ commonjs: true }], }), - test({ + tValid({ code: `const { fooNamed1 } = require('./no-rename-default/const-foo')`, options: [{ commonjs: true }], }), - test({ + tValid({ code: `const { fooNamed1, fooNamed2 } = require('./no-rename-default/const-foo')`, options: [{ commonjs: true }], }), - test({ + tValid({ code: `const { default: foo } = require('./no-rename-default/const-foo')`, options: [{ commonjs: true }], }), - test({ + tValid({ code: `const { default: foo, fooNamed1 } = require('./no-rename-default/const-foo')`, options: [{ commonjs: true }], }), - test({ + tValid({ code: `const foo = require('./no-rename-default/const-foo') const { fooNamed1 } = require('./no-rename-default/const-foo')`, options: [{ commonjs: true }], }), - test({ + tValid({ code: `const getUsers = require('./no-rename-default/fn-get-users')`, options: [{ commonjs: true }], }), - test({ + tValid({ code: `const getUsersSync = require('./no-rename-default/fn-get-users-sync')`, options: [{ commonjs: true }], }), - test({ + tValid({ code: ` const bar = require('./no-rename-default/const-bar') const { barNamed1 } = require('./no-rename-default/const-bar') @@ -596,71 +774,103 @@ ruleTester.run('no-rename-default', rule, { }), ], invalid: [ - test({ + tInvalid({ code: `const bar = require('./no-rename-default/const-foo')`, options: [{ commonjs: true }], errors: [ - { - message: - "Caution: `const-foo.js` has a default export `foo`. This requires `foo` as `bar`. Check if you meant to write `const foo = require('./no-rename-default/const-foo')` instead.", - type: 'VariableDeclarator', - }, + createRenameDefaultError( + { + importBasename: 'const-foo.js', + defaultExportName: 'foo', + requiresOrImports: 'requires', + importName: 'bar', + suggestion: "const foo = require('./no-rename-default/const-foo')", + }, + 'VariableDeclarator', + ), ], }), - test({ + tInvalid({ code: `const bar = require('./no-rename-default/const-foo') const { fooNamed1 } = require('./no-rename-default/const-foo')`, options: [{ commonjs: true }], errors: [ - { - message: - "Caution: `const-foo.js` has a default export `foo`. This requires `foo` as `bar`. Check if you meant to write `const foo = require('./no-rename-default/const-foo')` instead.", - type: 'VariableDeclarator', - }, + createRenameDefaultError( + { + importBasename: 'const-foo.js', + defaultExportName: 'foo', + requiresOrImports: 'requires', + importName: 'bar', + suggestion: "const foo = require('./no-rename-default/const-foo')", + }, + 'VariableDeclarator', + ), ], }), - test({ + tInvalid({ code: `const { default: bar } = require('./no-rename-default/const-foo')`, options: [{ commonjs: true }], errors: [ - { - message: - "Caution: `const-foo.js` has a default export `foo`. This requires `foo` as `bar`. Check if you meant to write `const { default: foo } = require('./no-rename-default/const-foo')` instead.", - type: 'VariableDeclarator', - }, + createRenameDefaultError( + { + importBasename: 'const-foo.js', + defaultExportName: 'foo', + requiresOrImports: 'requires', + importName: 'bar', + suggestion: + "const { default: foo } = require('./no-rename-default/const-foo')", + }, + 'VariableDeclarator', + ), ], }), - test({ + tInvalid({ code: `const { default: bar, fooNamed1 } = require('./no-rename-default/const-foo')`, options: [{ commonjs: true }], errors: [ - { - message: - "Caution: `const-foo.js` has a default export `foo`. This requires `foo` as `bar`. Check if you meant to write `const { default: foo } = require('./no-rename-default/const-foo')` instead.", - type: 'VariableDeclarator', - }, + createRenameDefaultError( + { + importBasename: 'const-foo.js', + defaultExportName: 'foo', + requiresOrImports: 'requires', + importName: 'bar', + suggestion: + "const { default: foo } = require('./no-rename-default/const-foo')", + }, + 'VariableDeclarator', + ), ], }), - test({ + tInvalid({ code: ` const foo = require('./no-rename-default/const-bar') const bar = require('./no-rename-default/const-foo') `, options: [{ commonjs: true }], errors: [ - { - message: - "Caution: `const-bar.js` has a default export `bar`. This requires `bar` as `foo`. Check if you meant to write `const bar = require('./no-rename-default/const-bar')` instead.", - type: 'VariableDeclarator', - }, - { - message: - "Caution: `const-foo.js` has a default export `foo`. This requires `foo` as `bar`. Check if you meant to write `const foo = require('./no-rename-default/const-foo')` instead.", - type: 'VariableDeclarator', - }, + createRenameDefaultError( + { + importBasename: 'const-bar.js', + defaultExportName: 'bar', + requiresOrImports: 'requires', + importName: 'foo', + suggestion: "const bar = require('./no-rename-default/const-bar')", + }, + 'VariableDeclarator', + ), + createRenameDefaultError( + { + importBasename: 'const-foo.js', + defaultExportName: 'foo', + requiresOrImports: 'requires', + importName: 'bar', + suggestion: "const foo = require('./no-rename-default/const-foo')", + }, + 'VariableDeclarator', + ), ], }), - test({ + tInvalid({ code: ` const foo = require('./no-rename-default/const-bar') const { barNamed1 } = require('./no-rename-default/const-bar') @@ -669,16 +879,26 @@ ruleTester.run('no-rename-default', rule, { `, options: [{ commonjs: true }], errors: [ - { - message: - "Caution: `const-bar.js` has a default export `bar`. This requires `bar` as `foo`. Check if you meant to write `const bar = require('./no-rename-default/const-bar')` instead.", - type: 'VariableDeclarator', - }, - { - message: - "Caution: `const-foo.js` has a default export `foo`. This requires `foo` as `bar`. Check if you meant to write `const foo = require('./no-rename-default/const-foo')` instead.", - type: 'VariableDeclarator', - }, + createRenameDefaultError( + { + importBasename: 'const-bar.js', + defaultExportName: 'bar', + requiresOrImports: 'requires', + importName: 'foo', + suggestion: "const bar = require('./no-rename-default/const-bar')", + }, + 'VariableDeclarator', + ), + createRenameDefaultError( + { + importBasename: 'const-foo.js', + defaultExportName: 'foo', + requiresOrImports: 'requires', + importName: 'bar', + suggestion: "const foo = require('./no-rename-default/const-foo')", + }, + 'VariableDeclarator', + ), ], }), ], @@ -693,24 +913,36 @@ ruleTester.run('no-rename-default', rule, { `import getUsersSync from './no-rename-default/fn-get-users-sync'`, ], invalid: [ - test({ + tInvalid({ code: `import findUsers from './no-rename-default/fn-get-users'`, errors: [ - { - message: - "Caution: `fn-get-users.js` has a default export `getUsers`. This imports `getUsers` as `findUsers`. Check if you meant to write `import getUsers from './no-rename-default/fn-get-users'` instead.", - type: 'ImportDefaultSpecifier', - }, + createRenameDefaultError( + { + importBasename: 'fn-get-users.js', + defaultExportName: 'getUsers', + requiresOrImports: 'imports', + importName: 'findUsers', + suggestion: + "import getUsers from './no-rename-default/fn-get-users'", + }, + 'ImportDefaultSpecifier', + ), ], }), - test({ + tInvalid({ code: `import findUsersSync from './no-rename-default/fn-get-users-sync'`, errors: [ - { - message: - "Caution: `fn-get-users-sync.js` has a default export `getUsersSync`. This imports `getUsersSync` as `findUsersSync`. Check if you meant to write `import getUsersSync from './no-rename-default/fn-get-users-sync'` instead.", - type: 'ImportDefaultSpecifier', - }, + createRenameDefaultError( + { + importBasename: 'fn-get-users-sync.js', + defaultExportName: 'getUsersSync', + requiresOrImports: 'imports', + importName: 'findUsersSync', + suggestion: + "import getUsersSync from './no-rename-default/fn-get-users-sync'", + }, + 'ImportDefaultSpecifier', + ), ], }), ], @@ -721,36 +953,48 @@ ruleTester.run('no-rename-default', rule, { // fn-get-users-sync.js ruleTester.run('no-rename-default', rule, { valid: [ - test({ + tValid({ code: `const getUsers = require('./no-rename-default/fn-get-users')`, options: [{ commonjs: true }], }), - test({ + tValid({ code: `const getUsersSync = require('./no-rename-default/fn-get-users-sync')`, options: [{ commonjs: true }], }), ], invalid: [ - test({ + tInvalid({ code: `const findUsers = require('./no-rename-default/fn-get-users')`, options: [{ commonjs: true }], errors: [ - { - message: - "Caution: `fn-get-users.js` has a default export `getUsers`. This requires `getUsers` as `findUsers`. Check if you meant to write `const getUsers = require('./no-rename-default/fn-get-users')` instead.", - type: 'VariableDeclarator', - }, + createRenameDefaultError( + { + importBasename: 'fn-get-users.js', + defaultExportName: 'getUsers', + requiresOrImports: 'requires', + importName: 'findUsers', + suggestion: + "const getUsers = require('./no-rename-default/fn-get-users')", + }, + 'VariableDeclarator', + ), ], }), - test({ + tInvalid({ code: `const findUsersSync = require('./no-rename-default/fn-get-users-sync')`, options: [{ commonjs: true }], errors: [ - { - message: - "Caution: `fn-get-users-sync.js` has a default export `getUsersSync`. This requires `getUsersSync` as `findUsersSync`. Check if you meant to write `const getUsersSync = require('./no-rename-default/fn-get-users-sync')` instead.", - type: 'VariableDeclarator', - }, + createRenameDefaultError( + { + importBasename: 'fn-get-users-sync.js', + defaultExportName: 'getUsersSync', + requiresOrImports: 'requires', + importName: 'findUsersSync', + suggestion: + "const getUsersSync = require('./no-rename-default/fn-get-users-sync')", + }, + 'VariableDeclarator', + ), ], }), ], @@ -761,14 +1005,20 @@ ruleTester.run('no-rename-default', rule, { ruleTester.run('no-rename-default', rule, { valid: [`import reader from './no-rename-default/generator-reader'`], invalid: [ - test({ + tInvalid({ code: `import myReader from './no-rename-default/generator-reader'`, errors: [ - { - message: - "Caution: `generator-reader.js` has a default export `reader`. This imports `reader` as `myReader`. Check if you meant to write `import reader from './no-rename-default/generator-reader'` instead.", - type: 'ImportDefaultSpecifier', - }, + createRenameDefaultError( + { + importBasename: 'generator-reader.js', + defaultExportName: 'reader', + requiresOrImports: 'imports', + importName: 'myReader', + suggestion: + "import reader from './no-rename-default/generator-reader'", + }, + 'ImportDefaultSpecifier', + ), ], }), ], @@ -778,21 +1028,27 @@ ruleTester.run('no-rename-default', rule, { // generator-reader.js ruleTester.run('no-rename-default', rule, { valid: [ - test({ + tValid({ code: `const reader = require('./no-rename-default/generator-reader')`, options: [{ commonjs: true }], }), ], invalid: [ - test({ + tInvalid({ code: `const myReader = require('./no-rename-default/generator-reader')`, options: [{ commonjs: true }], errors: [ - { - message: - "Caution: `generator-reader.js` has a default export `reader`. This requires `reader` as `myReader`. Check if you meant to write `const reader = require('./no-rename-default/generator-reader')` instead.", - type: 'VariableDeclarator', - }, + createRenameDefaultError( + { + importBasename: 'generator-reader.js', + defaultExportName: 'reader', + requiresOrImports: 'requires', + importName: 'myReader', + suggestion: + "const reader = require('./no-rename-default/generator-reader')", + }, + 'VariableDeclarator', + ), ], }), ], @@ -815,7 +1071,7 @@ ruleTester.run('no-rename-default', rule, { // binding-const-rename-fn.js ruleTester.run('no-rename-default', rule, { valid: [ - test({ + tValid({ code: `const foo = require('./no-rename-default/pr-3006-feedback/binding-const-rename-fn')`, options: [{ commonjs: true, preventRenamingBindings: false }], }), @@ -842,15 +1098,15 @@ ruleTester.run('no-rename-default', rule, { // binding-hoc-with-logger-with-auth-for-get-users.js ruleTester.run('no-rename-default', rule, { valid: [ - test({ + tValid({ code: `const foo = require('./no-rename-default/pr-3006-feedback/binding-hoc-with-logger-for-foo')`, options: [{ commonjs: true }], }), - test({ + tValid({ code: `const getUsers = require('./no-rename-default/pr-3006-feedback/binding-hoc-with-logger-for-get-users')`, options: [{ commonjs: true }], }), - test({ + tValid({ code: `const getUsers = require('./no-rename-default/pr-3006-feedback/binding-hoc-with-logger-with-auth-for-get-users')`, options: [{ commonjs: true }], }), @@ -862,7 +1118,7 @@ ruleTester.run('no-rename-default', rule, { // binding-fn-rename.js ruleTester.run('no-rename-default', rule, { valid: [ - test({ + tValid({ code: `import _ from './no-rename-default/pr-3006-feedback/binding-fn-rename'`, options: [{ preventRenamingBindings: false }], }), @@ -874,7 +1130,7 @@ ruleTester.run('no-rename-default', rule, { // binding-fn-rename.js ruleTester.run('no-rename-default', rule, { valid: [ - test({ + tValid({ code: `const _ = require('./no-rename-default/pr-3006-feedback/binding-fn-rename')`, options: [{ commonjs: true, preventRenamingBindings: false }], }), @@ -885,7 +1141,7 @@ ruleTester.run('no-rename-default', rule, { describe('TypeScript', function () { ruleTester.run('no-rename-default', rule, { valid: [ - test({ + tValid({ code: `import foo from './no-rename-default/typescript-default'`, settings: { 'import-x/parsers': { [parsers.TS]: ['.ts'] }, From 09260740ff6e405776929606a7806f6aba5afb1f Mon Sep 17 00:00:00 2001 From: Marco Pasqualetti Date: Wed, 20 Nov 2024 09:14:18 +0100 Subject: [PATCH 38/49] test(no-restricted-paths): migrate to `createRuleTestCaseFunctions` --- test/rules/no-restricted-paths.spec.ts | 309 ++++++++++--------------- 1 file changed, 123 insertions(+), 186 deletions(-) diff --git a/test/rules/no-restricted-paths.spec.ts b/test/rules/no-restricted-paths.spec.ts index 1e9bac579..b5d8d0f0e 100644 --- a/test/rules/no-restricted-paths.spec.ts +++ b/test/rules/no-restricted-paths.spec.ts @@ -1,14 +1,25 @@ import { RuleTester as TSESLintRuleTester } from '@typescript-eslint/rule-tester' +import type { TestCaseError as TSESLintTestCaseError } from '@typescript-eslint/rule-tester' -import { parsers, test, testFilePath } from '../utils' +import { parsers, createRuleTestCaseFunctions, testFilePath } from '../utils' +import type { GetRuleModuleMessageIds } from '../utils' import rule from 'eslint-plugin-import-x/rules/no-restricted-paths' const ruleTester = new TSESLintRuleTester() +const { tValid, tInvalid } = createRuleTestCaseFunctions() + +function createZoneError( + importPath: string, + extra: string = '', +): TSESLintTestCaseError> { + return { messageId: 'zone', data: { importPath, extra } } +} + ruleTester.run('no-restricted-paths', rule, { valid: [ - test({ + tValid({ code: 'import a from "../client/a.js"', filename: testFilePath('./restricted-paths/server/b.js'), options: [ @@ -22,7 +33,7 @@ ruleTester.run('no-restricted-paths', rule, { }, ], }), - test({ + tValid({ code: 'import a from "../client/a.js"', filename: testFilePath('./restricted-paths/server/b.js'), options: [ @@ -36,7 +47,7 @@ ruleTester.run('no-restricted-paths', rule, { }, ], }), - test({ + tValid({ code: 'import a from "../client/a.js"', filename: testFilePath('./restricted-paths/client/b.js'), options: [ @@ -50,7 +61,7 @@ ruleTester.run('no-restricted-paths', rule, { }, ], }), - test({ + tValid({ code: 'const a = require("../client/a.js")', filename: testFilePath('./restricted-paths/server/b.js'), options: [ @@ -64,7 +75,7 @@ ruleTester.run('no-restricted-paths', rule, { }, ], }), - test({ + tValid({ code: 'import b from "../server/b.js"', filename: testFilePath('./restricted-paths/client/a.js'), options: [ @@ -78,7 +89,7 @@ ruleTester.run('no-restricted-paths', rule, { }, ], }), - test({ + tValid({ code: 'import a from "./a.js"', filename: testFilePath('./restricted-paths/server/one/a.js'), options: [ @@ -93,7 +104,7 @@ ruleTester.run('no-restricted-paths', rule, { }, ], }), - test({ + tValid({ code: 'import a from "../two/a.js"', filename: testFilePath('./restricted-paths/server/one/a.js'), options: [ @@ -108,7 +119,7 @@ ruleTester.run('no-restricted-paths', rule, { }, ], }), - test({ + tValid({ code: 'import a from "../one/a.js"', filename: testFilePath('./restricted-paths/server/two-new/a.js'), options: [ @@ -123,7 +134,7 @@ ruleTester.run('no-restricted-paths', rule, { }, ], }), - test({ + tValid({ code: 'import A from "../two/a.js"', filename: testFilePath('./restricted-paths/server/one/a.js'), options: [ @@ -141,7 +152,7 @@ ruleTester.run('no-restricted-paths', rule, { // support of arrays for from and target // array with single element - test({ + tValid({ code: 'import a from "../client/a.js"', filename: testFilePath('./restricted-paths/server/b.js'), options: [ @@ -155,7 +166,7 @@ ruleTester.run('no-restricted-paths', rule, { }, ], }), - test({ + tValid({ code: 'import a from "../client/a.js"', filename: testFilePath('./restricted-paths/server/b.js'), options: [ @@ -170,7 +181,7 @@ ruleTester.run('no-restricted-paths', rule, { ], }), // array with multiple elements - test({ + tValid({ code: 'import a from "../one/a.js"', filename: testFilePath('./restricted-paths/server/two-new/a.js'), options: [ @@ -187,7 +198,7 @@ ruleTester.run('no-restricted-paths', rule, { }, ], }), - test({ + tValid({ code: 'import a from "../one/a.js"', filename: testFilePath('./restricted-paths/server/two-new/a.js'), options: [ @@ -206,7 +217,7 @@ ruleTester.run('no-restricted-paths', rule, { ], }), // array with multiple glob patterns in from - test({ + tValid({ code: 'import a from "../client/a.js"', filename: testFilePath('./restricted-paths/client/b.js'), options: [ @@ -224,7 +235,7 @@ ruleTester.run('no-restricted-paths', rule, { ], }), // array with mix of glob and non glob patterns in target - test({ + tValid({ code: 'import a from "../client/a.js"', filename: testFilePath('./restricted-paths/client/b.js'), options: [ @@ -243,8 +254,8 @@ ruleTester.run('no-restricted-paths', rule, { }), // irrelevant function calls - test({ code: 'notrequire("../server/b.js")' }), - test({ + tValid({ code: 'notrequire("../server/b.js")' }), + tValid({ code: 'notrequire("../server/b.js")', filename: testFilePath('./restricted-paths/client/a.js'), options: [ @@ -260,15 +271,15 @@ ruleTester.run('no-restricted-paths', rule, { }), // no config - test({ code: 'require("../server/b.js")' }), - test({ code: 'import b from "../server/b.js"' }), + tValid({ code: 'require("../server/b.js")' }), + tValid({ code: 'import b from "../server/b.js"' }), // builtin (ignore) - test({ code: 'require("os")' }), + tValid({ code: 'require("os")' }), ], invalid: [ - test({ + tInvalid({ code: 'import b from "../server/b.js"; // 1', filename: testFilePath('./restricted-paths/client/a.js'), options: [ @@ -283,14 +294,13 @@ ruleTester.run('no-restricted-paths', rule, { ], errors: [ { - message: - 'Unexpected path "../server/b.js" imported in restricted zone.', + ...createZoneError('../server/b.js'), line: 1, column: 15, }, ], }), - test({ + tInvalid({ code: 'import b from "../server/b.js"; // 2', filename: testFilePath('./restricted-paths/client/a.js'), options: [ @@ -305,8 +315,7 @@ ruleTester.run('no-restricted-paths', rule, { ], errors: [ { - message: - 'Unexpected path "../server/b.js" imported in restricted zone.', + ...createZoneError('../server/b.js'), line: 1, column: 15, }, @@ -316,7 +325,7 @@ ruleTester.run('no-restricted-paths', rule, { ...(process.platform === 'win32' ? [] : [ - test({ + tInvalid({ code: 'import b from "../server/b.js";', filename: testFilePath('./restricted-paths/client/a.js'), options: [ @@ -331,15 +340,14 @@ ruleTester.run('no-restricted-paths', rule, { ], errors: [ { - message: - 'Unexpected path "../server/b.js" imported in restricted zone.', + ...createZoneError('../server/b.js'), line: 1, column: 15, }, ], }), ]), - test({ + tInvalid({ code: 'import b from "../server/b.js"; // 2 ter', filename: testFilePath('./restricted-paths/client/a.js'), options: [ @@ -354,14 +362,13 @@ ruleTester.run('no-restricted-paths', rule, { ], errors: [ { - message: - 'Unexpected path "../server/b.js" imported in restricted zone.', + ...createZoneError('../server/b.js'), line: 1, column: 15, }, ], }), - test({ + tInvalid({ code: 'import a from "../client/a"\nimport c from "./c"', filename: testFilePath('./restricted-paths/server/b.js'), options: [ @@ -380,18 +387,18 @@ ruleTester.run('no-restricted-paths', rule, { ], errors: [ { - message: 'Unexpected path "../client/a" imported in restricted zone.', + ...createZoneError('../client/a'), line: 1, column: 15, }, { - message: 'Unexpected path "./c" imported in restricted zone.', + ...createZoneError('./c'), line: 2, column: 15, }, ], }), - test({ + tInvalid({ code: 'import b from "../server/b.js"; // 3', filename: testFilePath('./restricted-paths/client/a.js'), options: [ @@ -407,14 +414,13 @@ ruleTester.run('no-restricted-paths', rule, { ], errors: [ { - message: - 'Unexpected path "../server/b.js" imported in restricted zone.', + ...createZoneError('../server/b.js'), line: 1, column: 15, }, ], }), - test({ + tInvalid({ code: 'const b = require("../server/b.js")', filename: testFilePath('./restricted-paths/client/a.js'), options: [ @@ -429,14 +435,13 @@ ruleTester.run('no-restricted-paths', rule, { ], errors: [ { - message: - 'Unexpected path "../server/b.js" imported in restricted zone.', + ...createZoneError('../server/b.js'), line: 1, column: 19, }, ], }), - test({ + tInvalid({ code: 'import b from "../two/a.js"', filename: testFilePath('./restricted-paths/server/one/a.js'), options: [ @@ -452,13 +457,13 @@ ruleTester.run('no-restricted-paths', rule, { ], errors: [ { - message: 'Unexpected path "../two/a.js" imported in restricted zone.', + ...createZoneError('../two/a.js'), line: 1, column: 15, }, ], }), - test({ + tInvalid({ code: 'import b from "../two/a.js"', filename: testFilePath('./restricted-paths/server/one/a.js'), options: [ @@ -475,14 +480,13 @@ ruleTester.run('no-restricted-paths', rule, { ], errors: [ { - message: - 'Unexpected path "../two/a.js" imported in restricted zone. Custom message', + ...createZoneError('../two/a.js', ' Custom message'), line: 1, column: 15, }, ], }), - test({ + tInvalid({ code: 'import b from "../two/a.js"', filename: testFilePath('./restricted-paths/server/one/a.js'), options: [ @@ -496,16 +500,9 @@ ruleTester.run('no-restricted-paths', rule, { ], }, ], - errors: [ - { - message: - 'Restricted path exceptions must be descendants of the configured `from` path for that zone.', - line: 1, - column: 15, - }, - ], + errors: [{ messageId: 'path', line: 1, column: 15 }], }), - test({ + tInvalid({ code: 'import A from "../two/a.js"', filename: testFilePath('./restricted-paths/server/one/a.js'), options: [ @@ -520,13 +517,13 @@ ruleTester.run('no-restricted-paths', rule, { ], errors: [ { - message: 'Unexpected path "../two/a.js" imported in restricted zone.', + ...createZoneError('../two/a.js'), line: 1, column: 15, }, ], }), - test({ + tInvalid({ code: 'import A from "../two/a.js"', filename: testFilePath('./restricted-paths/server/one/a.js'), options: [ @@ -540,19 +537,12 @@ ruleTester.run('no-restricted-paths', rule, { ], }, ], - errors: [ - { - message: - 'Restricted path exceptions must be glob patterns when `from` contains glob patterns', - line: 1, - column: 15, - }, - ], + errors: [{ messageId: 'glob', line: 1, column: 15 }], }), // support of arrays for from and target // array with single element - test({ + tInvalid({ code: 'import b from "../server/b.js"; // 4', filename: testFilePath('./restricted-paths/client/a.js'), options: [ @@ -567,14 +557,13 @@ ruleTester.run('no-restricted-paths', rule, { ], errors: [ { - message: - 'Unexpected path "../server/b.js" imported in restricted zone.', + ...createZoneError('../server/b.js'), line: 1, column: 15, }, ], }), - test({ + tInvalid({ code: 'import b from "../server/b.js"; // 5', filename: testFilePath('./restricted-paths/client/a.js'), options: [ @@ -589,15 +578,14 @@ ruleTester.run('no-restricted-paths', rule, { ], errors: [ { - message: - 'Unexpected path "../server/b.js" imported in restricted zone.', + ...createZoneError('../server/b.js'), line: 1, column: 15, }, ], }), // array with multiple elements - test({ + tInvalid({ code: 'import b from "../server/b.js"; // 6', filename: testFilePath('./restricted-paths/client/a.js'), options: [ @@ -615,14 +603,13 @@ ruleTester.run('no-restricted-paths', rule, { ], errors: [ { - message: - 'Unexpected path "../server/b.js" imported in restricted zone.', + ...createZoneError('../server/b.js'), line: 1, column: 15, }, ], }), - test({ + tInvalid({ code: 'import b from "../server/one/b.js"\nimport a from "../server/two/a.js"', filename: testFilePath('./restricted-paths/client/a.js'), options: [ @@ -640,21 +627,19 @@ ruleTester.run('no-restricted-paths', rule, { ], errors: [ { - message: - 'Unexpected path "../server/one/b.js" imported in restricted zone.', + ...createZoneError('../server/one/b.js'), line: 1, column: 15, }, { - message: - 'Unexpected path "../server/two/a.js" imported in restricted zone.', + ...createZoneError('../server/two/a.js'), line: 2, column: 15, }, ], }), // array with multiple glob patterns in from - test({ + tInvalid({ code: 'import b from "../server/one/b.js"\nimport a from "../server/two/a.js"', filename: testFilePath('./restricted-paths/client/a.js'), options: [ @@ -672,21 +657,19 @@ ruleTester.run('no-restricted-paths', rule, { ], errors: [ { - message: - 'Unexpected path "../server/one/b.js" imported in restricted zone.', + ...createZoneError('../server/one/b.js'), line: 1, column: 15, }, { - message: - 'Unexpected path "../server/two/a.js" imported in restricted zone.', + ...createZoneError('../server/two/a.js'), line: 2, column: 15, }, ], }), // array with mix of glob and non glob patterns in target - test({ + tInvalid({ code: 'import b from "../server/b.js"; // 7', filename: testFilePath('./restricted-paths/client/a.js'), options: [ @@ -704,15 +687,14 @@ ruleTester.run('no-restricted-paths', rule, { ], errors: [ { - message: - 'Unexpected path "../server/b.js" imported in restricted zone.', + ...createZoneError('../server/b.js'), line: 1, column: 15, }, ], }), // configuration format - test({ + tInvalid({ code: 'import A from "../two/a.js"', filename: testFilePath('./restricted-paths/server/one/a.js'), options: [ @@ -726,16 +708,9 @@ ruleTester.run('no-restricted-paths', rule, { ], }, ], - errors: [ - { - message: - 'Restricted path exceptions must be glob patterns when `from` contains glob patterns', - line: 1, - column: 15, - }, - ], + errors: [{ messageId: 'glob', line: 1, column: 15 }], }), - test({ + tInvalid({ code: 'import b from "../server/one/b.js"', filename: testFilePath('./restricted-paths/client/a.js'), options: [ @@ -751,14 +726,7 @@ ruleTester.run('no-restricted-paths', rule, { ], }, ], - errors: [ - { - message: - 'Restricted path `from` must contain either only glob patterns or none', - line: 1, - column: 15, - }, - ], + errors: [{ messageId: 'mixedGlob', line: 1, column: 15 }], }), ], }) @@ -770,9 +738,10 @@ describe('Typescript', () => { } ruleTester.run('no-restricted-paths', rule, { valid: [ - test({ + tValid({ code: 'import type a from "../client/a.ts"', filename: testFilePath('./restricted-paths/server/b.ts'), + settings, options: [ { zones: [ @@ -783,10 +752,8 @@ describe('Typescript', () => { ], }, ], - - settings, }), - test({ + tValid({ code: 'import type a from "../client/a.ts"', filename: testFilePath('./restricted-paths/server/b.ts'), options: [ @@ -802,9 +769,10 @@ describe('Typescript', () => { settings, }), - test({ + tValid({ code: 'import type a from "../client/a.ts"', filename: testFilePath('./restricted-paths/client/b.ts'), + settings, options: [ { zones: [ @@ -815,10 +783,8 @@ describe('Typescript', () => { ], }, ], - - settings, }), - test({ + tValid({ code: 'import type b from "../server/b.ts"', filename: testFilePath('./restricted-paths/client/a.ts'), options: [ @@ -834,9 +800,10 @@ describe('Typescript', () => { settings, }), - test({ + tValid({ code: 'import type a from "./a.ts"', filename: testFilePath('./restricted-paths/server/one/a.ts'), + settings, options: [ { zones: [ @@ -848,12 +815,11 @@ describe('Typescript', () => { ], }, ], - - settings, }), - test({ + tValid({ code: 'import type a from "../two/a.ts"', filename: testFilePath('./restricted-paths/server/one/a.ts'), + settings, options: [ { zones: [ @@ -865,12 +831,11 @@ describe('Typescript', () => { ], }, ], - - settings, }), - test({ + tValid({ code: 'import type a from "../one/a.ts"', filename: testFilePath('./restricted-paths/server/two-new/a.ts'), + settings, options: [ { zones: [ @@ -882,12 +847,11 @@ describe('Typescript', () => { ], }, ], - - settings, }), - test({ + tValid({ code: 'import type A from "../two/a.ts"', filename: testFilePath('./restricted-paths/server/one/a.ts'), + settings, options: [ { zones: [ @@ -899,21 +863,22 @@ describe('Typescript', () => { ], }, ], - - settings, }), // no config - test({ code: 'import type b from "../server/b.js"', settings }), - test({ + tValid({ + code: 'import type b from "../server/b.js"', + settings, + }), + tValid({ code: 'import type * as b from "../server/b.js"', - settings, }), ], invalid: [ - test({ + tInvalid({ code: 'import type b from "../server/b"', filename: testFilePath('./restricted-paths/client/a.ts'), + settings, options: [ { zones: [ @@ -926,18 +891,16 @@ describe('Typescript', () => { ], errors: [ { - message: - 'Unexpected path "../server/b" imported in restricted zone.', + ...createZoneError('../server/b'), line: 1, column: 20, }, ], - - settings, }), - test({ + tInvalid({ code: 'import type b from "../server/b"', filename: testFilePath('./restricted-paths/client/a.ts'), + settings, options: [ { zones: [ @@ -950,18 +913,16 @@ describe('Typescript', () => { ], errors: [ { - message: - 'Unexpected path "../server/b" imported in restricted zone.', + ...createZoneError('../server/b'), line: 1, column: 20, }, ], - - settings, }), - test({ + tInvalid({ code: 'import type a from "../client/a"\nimport type c from "./c.ts"', filename: testFilePath('./restricted-paths/server/b.ts'), + settings, options: [ { zones: [ @@ -977,23 +938,21 @@ describe('Typescript', () => { ], errors: [ { - message: - 'Unexpected path "../client/a" imported in restricted zone.', + ...createZoneError('../client/a'), line: 1, column: 20, }, { - message: 'Unexpected path "./c.ts" imported in restricted zone.', + ...createZoneError('./c.ts'), line: 2, column: 20, }, ], - - settings, }), - test({ + tInvalid({ code: 'import type b from "../server/b"', filename: testFilePath('./restricted-paths/client/a'), + settings, options: [ { zones: [{ target: './client', from: './server' }], @@ -1002,18 +961,16 @@ describe('Typescript', () => { ], errors: [ { - message: - 'Unexpected path "../server/b" imported in restricted zone.', + ...createZoneError('../server/b'), line: 1, column: 20, }, ], - - settings, }), - test({ + tInvalid({ code: 'import type b from "../two/a"', filename: testFilePath('./restricted-paths/server/one/a.ts'), + settings, options: [ { zones: [ @@ -1027,17 +984,16 @@ describe('Typescript', () => { ], errors: [ { - message: 'Unexpected path "../two/a" imported in restricted zone.', + ...createZoneError('../two/a'), line: 1, column: 20, }, ], - - settings, }), - test({ + tInvalid({ code: 'import type b from "../two/a"', filename: testFilePath('./restricted-paths/server/one/a'), + settings, options: [ { zones: [ @@ -1052,18 +1008,16 @@ describe('Typescript', () => { ], errors: [ { - message: - 'Unexpected path "../two/a" imported in restricted zone. Custom message', + ...createZoneError('../two/a', ' Custom message'), line: 1, column: 20, }, ], - - settings, }), - test({ + tInvalid({ code: 'import type b from "../two/a"', filename: testFilePath('./restricted-paths/server/one/a.ts'), + settings, options: [ { zones: [ @@ -1075,20 +1029,12 @@ describe('Typescript', () => { ], }, ], - errors: [ - { - message: - 'Restricted path exceptions must be descendants of the configured `from` path for that zone.', - line: 1, - column: 20, - }, - ], - - settings, + errors: [{ messageId: 'path', line: 1, column: 20 }], }), - test({ + tInvalid({ code: 'import type A from "../two/a"', filename: testFilePath('./restricted-paths/server/one/a.ts'), + settings, options: [ { zones: [ @@ -1101,17 +1047,17 @@ describe('Typescript', () => { ], errors: [ { - message: 'Unexpected path "../two/a" imported in restricted zone.', + ...createZoneError('../two/a'), line: 1, column: 20, }, ], - - settings, }), - test({ + tInvalid({ code: 'import type A from "../two/a"', + filename: testFilePath('./restricted-paths/server/one/a.ts'), + settings, options: [ { zones: [ @@ -1123,16 +1069,7 @@ describe('Typescript', () => { ], }, ], - errors: [ - { - message: - 'Restricted path exceptions must be glob patterns when `from` contains glob patterns', - line: 1, - column: 20, - }, - ], - - settings, + errors: [{ messageId: 'glob', line: 1, column: 20 }], }), ], }) From 4fe20066e965563641b03942e8e00ec9f83b4d81 Mon Sep 17 00:00:00 2001 From: Marco Pasqualetti Date: Wed, 20 Nov 2024 18:32:14 +0100 Subject: [PATCH 39/49] test(no-self-import): migrate to `createRuleTestCaseFunctions` --- test/rules/no-self-import.spec.ts | 68 +++++++++++++++---------------- 1 file changed, 33 insertions(+), 35 deletions(-) diff --git a/test/rules/no-self-import.spec.ts b/test/rules/no-self-import.spec.ts index 8620ec987..1814ae394 100644 --- a/test/rules/no-self-import.spec.ts +++ b/test/rules/no-self-import.spec.ts @@ -1,121 +1,119 @@ import { RuleTester as TSESLintRuleTester } from '@typescript-eslint/rule-tester' -import { test, testFilePath } from '../utils' +import { createRuleTestCaseFunctions, testFilePath } from '../utils' import rule from 'eslint-plugin-import-x/rules/no-self-import' const ruleTester = new TSESLintRuleTester() -const error = { - message: 'Module imports itself.', -} +const { tValid, tInvalid } = createRuleTestCaseFunctions() ruleTester.run('no-self-import', rule, { valid: [ - test({ + tValid({ code: 'import _ from "lodash"', filename: testFilePath('./no-self-import.js'), }), - test({ + tValid({ code: 'import find from "lodash.find"', filename: testFilePath('./no-self-import.js'), }), - test({ + tValid({ code: 'import foo from "./foo"', filename: testFilePath('./no-self-import.js'), }), - test({ + tValid({ code: 'import foo from "../foo"', filename: testFilePath('./no-self-import.js'), }), - test({ + tValid({ code: 'import foo from "foo"', filename: testFilePath('./no-self-import.js'), }), - test({ + tValid({ code: 'import foo from "./"', filename: testFilePath('./no-self-import.js'), }), - test({ + tValid({ code: 'import foo from "@scope/foo"', filename: testFilePath('./no-self-import.js'), }), - test({ + tValid({ code: 'var _ = require("lodash")', filename: testFilePath('./no-self-import.js'), }), - test({ + tValid({ code: 'var find = require("lodash.find")', filename: testFilePath('./no-self-import.js'), }), - test({ + tValid({ code: 'var foo = require("./foo")', filename: testFilePath('./no-self-import.js'), }), - test({ + tValid({ code: 'var foo = require("../foo")', filename: testFilePath('./no-self-import.js'), }), - test({ + tValid({ code: 'var foo = require("foo")', filename: testFilePath('./no-self-import.js'), }), - test({ + tValid({ code: 'var foo = require("./")', filename: testFilePath('./no-self-import.js'), }), - test({ + tValid({ code: 'var foo = require("@scope/foo")', filename: testFilePath('./no-self-import.js'), }), - test({ + tValid({ code: 'var bar = require("./bar/index")', filename: testFilePath('./no-self-import.js'), }), - test({ + tValid({ code: 'var bar = require("./bar")', filename: testFilePath('./bar/index.js'), }), - test({ + tValid({ code: 'var bar = require("./bar")', filename: '', }), ], invalid: [ - test({ + tInvalid({ code: 'import bar from "./no-self-import"', - errors: [error], filename: testFilePath('./no-self-import.js'), + errors: [{ messageId: 'self' }], }), - test({ + tInvalid({ code: 'var bar = require("./no-self-import")', - errors: [error], filename: testFilePath('./no-self-import.js'), + errors: [{ messageId: 'self' }], }), - test({ + tInvalid({ code: 'var bar = require("./no-self-import.js")', - errors: [error], filename: testFilePath('./no-self-import.js'), + errors: [{ messageId: 'self' }], }), - test({ + tInvalid({ code: 'var bar = require(".")', - errors: [error], filename: testFilePath('./index.js'), + errors: [{ messageId: 'self' }], }), - test({ + tInvalid({ code: 'var bar = require("./")', - errors: [error], filename: testFilePath('./index.js'), + errors: [{ messageId: 'self' }], }), - test({ + tInvalid({ code: 'var bar = require("././././")', - errors: [error], filename: testFilePath('./index.js'), + errors: [{ messageId: 'self' }], }), - test({ + tInvalid({ code: 'var bar = require("../no-self-import-folder")', - errors: [error], filename: testFilePath('./no-self-import-folder/index.js'), + errors: [{ messageId: 'self' }], }), ], }) From 91705cc3bfc0a5ee4ebe82c77d0b28792b05c0a4 Mon Sep 17 00:00:00 2001 From: Marco Pasqualetti Date: Wed, 20 Nov 2024 18:34:45 +0100 Subject: [PATCH 40/49] test(no-unassigned-import): migrate to `createRuleTestCaseFunctions` --- test/rules/no-unassigned-import.spec.ts | 88 ++++++++++++------------- 1 file changed, 43 insertions(+), 45 deletions(-) diff --git a/test/rules/no-unassigned-import.spec.ts b/test/rules/no-unassigned-import.spec.ts index a78c1bf79..eebec70e1 100644 --- a/test/rules/no-unassigned-import.spec.ts +++ b/test/rules/no-unassigned-import.spec.ts @@ -2,109 +2,107 @@ import path from 'node:path' import { RuleTester as TSESLintRuleTester } from '@typescript-eslint/rule-tester' -import { test } from '../utils' +import { createRuleTestCaseFunctions } from '../utils' import rule from 'eslint-plugin-import-x/rules/no-unassigned-import' const ruleTester = new TSESLintRuleTester() -const error = { - messageId: 'unassigned', -} as const +const { tValid, tInvalid } = createRuleTestCaseFunctions() ruleTester.run('no-unassigned-import', rule, { valid: [ - test({ code: 'import _ from "lodash"' }), - test({ code: 'import _, {foo} from "lodash"' }), - test({ code: 'import _, {foo as bar} from "lodash"' }), - test({ code: 'import {foo as bar} from "lodash"' }), - test({ code: 'import * as _ from "lodash"' }), - test({ code: 'import _ from "./"' }), - test({ code: 'const _ = require("lodash")' }), - test({ code: 'const {foo} = require("lodash")' }), - test({ code: 'const {foo: bar} = require("lodash")' }), - test({ code: 'const [a, b] = require("lodash")' }), - test({ code: 'const _ = require("./")' }), - test({ code: 'foo(require("lodash"))' }), - test({ code: 'require("lodash").foo' }), - test({ code: 'require("lodash").foo()' }), - test({ code: 'require("lodash")()' }), - test({ + tValid({ code: 'import _, {foo} from "lodash"' }), + tValid({ code: 'import _ from "lodash"' }), + tValid({ code: 'import _, {foo as bar} from "lodash"' }), + tValid({ code: 'import {foo as bar} from "lodash"' }), + tValid({ code: 'import * as _ from "lodash"' }), + tValid({ code: 'import _ from "./"' }), + tValid({ code: 'const _ = require("lodash")' }), + tValid({ code: 'const {foo} = require("lodash")' }), + tValid({ code: 'const {foo: bar} = require("lodash")' }), + tValid({ code: 'const [a, b] = require("lodash")' }), + tValid({ code: 'const _ = require("./")' }), + tValid({ code: 'foo(require("lodash"))' }), + tValid({ code: 'require("lodash").foo' }), + tValid({ code: 'require("lodash").foo()' }), + tValid({ code: 'require("lodash")()' }), + tValid({ code: 'import "app.css"', options: [{ allow: ['**/*.css'] }], }), - test({ + tValid({ code: 'import "app.css";', options: [{ allow: ['*.css'] }], }), - test({ + tValid({ code: 'import "./app.css"', options: [{ allow: ['**/*.css'] }], }), - test({ + tValid({ code: 'import "foo/bar"', options: [{ allow: ['foo/**'] }], }), - test({ + tValid({ code: 'import "foo/bar"', options: [{ allow: ['foo/bar'] }], }), - test({ + tValid({ code: 'import "../dir/app.css"', options: [{ allow: ['**/*.css'] }], }), - test({ + tValid({ code: 'import "../dir/app.js"', options: [{ allow: ['**/dir/**'] }], }), - test({ + tValid({ code: 'require("./app.css")', options: [{ allow: ['**/*.css'] }], }), - test({ + tValid({ code: 'import "babel-register"', options: [{ allow: ['babel-register'] }], }), - test({ + tValid({ code: 'import "./styles/app.css"', - options: [{ allow: ['src/styles/**'] }], filename: path.resolve('src/app.js'), + options: [{ allow: ['src/styles/**'] }], }), - test({ + tValid({ code: 'import "../scripts/register.js"', - options: [{ allow: ['src/styles/**', '**/scripts/*.js'] }], filename: path.resolve('src/app.js'), + options: [{ allow: ['src/styles/**', '**/scripts/*.js'] }], }), ], invalid: [ - test({ + tInvalid({ code: 'import "lodash"', - errors: [error], + errors: [{ messageId: 'unassigned' }], }), - test({ + tInvalid({ code: 'require("lodash")', - errors: [error], + errors: [{ messageId: 'unassigned' }], }), - test({ + tInvalid({ code: 'import "./app.css"', options: [{ allow: ['**/*.js'] }], - errors: [error], + errors: [{ messageId: 'unassigned' }], }), - test({ + tInvalid({ code: 'import "./app.css"', options: [{ allow: ['**/dir/**'] }], - errors: [error], + errors: [{ messageId: 'unassigned' }], }), - test({ + tInvalid({ code: 'require("./app.css")', options: [{ allow: ['**/*.js'] }], - errors: [error], + errors: [{ messageId: 'unassigned' }], }), - test({ + tInvalid({ code: 'import "./styles/app.css"', - options: [{ allow: ['styles/*.css'] }], filename: path.resolve('src/app.js'), - errors: [error], + options: [{ allow: ['styles/*.css'] }], + errors: [{ messageId: 'unassigned' }], }), ], }) From 582d921ccec41491903a6e02f1875c0207f3c5b0 Mon Sep 17 00:00:00 2001 From: Marco Pasqualetti Date: Wed, 20 Nov 2024 22:20:43 +0100 Subject: [PATCH 41/49] test(no-unresolved): migrate to `createRuleTestCaseFunctions` --- test/rules/no-unresolved.spec.ts | 328 +++++++++++++------------------ 1 file changed, 142 insertions(+), 186 deletions(-) diff --git a/test/rules/no-unresolved.spec.ts b/test/rules/no-unresolved.spec.ts index a562c2a7d..34e310847 100644 --- a/test/rules/no-unresolved.spec.ts +++ b/test/rules/no-unresolved.spec.ts @@ -1,279 +1,237 @@ import path from 'node:path' import { RuleTester as TSESLintRuleTester } from '@typescript-eslint/rule-tester' +import type { TestCaseError as TSESLintTestCaseError } from '@typescript-eslint/rule-tester' +import type { TSESTree } from '@typescript-eslint/utils' -import { test, SYNTAX_VALID_CASES, parsers, testFilePath } from '../utils' -import type { ValidTestCase } from '../utils' +import { + createRuleTestCaseFunctions, + SYNTAX_VALID_CASES, + parsers, + testFilePath, +} from '../utils' +import type { GetRuleModuleMessageIds } from '../utils' import rule from 'eslint-plugin-import-x/rules/no-unresolved' import { CASE_SENSITIVE_FS } from 'eslint-plugin-import-x/utils' const ruleTester = new TSESLintRuleTester() -function runResolverTests(resolver: 'node' | 'webpack') { - // redefine 'test' to set a resolver - // thus 'rest'. needed something 4-chars-long for formatting simplicity - function rest(specs: T) { - return test({ - ...specs, - settings: { - ...specs.settings, - 'import-x/resolver': resolver, - 'import-x/cache': { lifetime: 0 }, - }, - }) +function createError( + messageId: GetRuleModuleMessageIds, + module: string, + type?: `${TSESTree.AST_NODE_TYPES}`, +): TSESLintTestCaseError> { + return { + messageId, + data: { module }, + type: type as TSESTree.AST_NODE_TYPES, } +} + +function runResolverTests(resolver: 'node' | 'webpack') { + const { tValid, tInvalid } = createRuleTestCaseFunctions({ + settings: { + 'import-x/resolver': resolver, + 'import-x/cache': { lifetime: 0 }, + }, + }) ruleTester.run(`no-unresolved (${resolver})`, rule, { valid: [ - test({ + tValid({ code: 'import "./malformed.js"', languageOptions: { parser: require(parsers.ESPREE) }, }), - rest({ code: 'import foo from "./bar";' }), - rest({ code: "import bar from './bar.js';" }), - rest({ code: "import {someThing} from './test-module';" }), - rest({ code: "import fs from 'fs';" }), - rest({ + tValid({ code: 'import foo from "./bar";' }), + tValid({ code: "import bar from './bar.js';" }), + tValid({ code: "import {someThing} from './test-module';" }), + tValid({ code: "import fs from 'fs';" }), + tValid({ code: "import('fs');", languageOptions: { parser: require(parsers.BABEL) }, }), // check with eslint parser - rest({ + tValid({ code: "import('fs');", languageOptions: { parserOptions: { ecmaVersion: 2021 }, }, }), - rest({ code: 'import * as foo from "a"' }), + tValid({ code: 'import * as foo from "a"' }), - rest({ code: 'export { foo } from "./bar"' }), - rest({ code: 'export * from "./bar"' }), - rest({ code: 'let foo; export { foo }' }), + tValid({ code: 'export { foo } from "./bar"' }), + tValid({ code: 'export * from "./bar"' }), + tValid({ code: 'let foo; export { foo }' }), // stage 1 proposal for export symmetry, - rest({ + tValid({ code: 'export * as bar from "./bar"', languageOptions: { parser: require(parsers.BABEL) }, }), - rest({ + tValid({ code: 'export bar from "./bar"', languageOptions: { parser: require(parsers.BABEL) }, }), - rest({ code: 'import foo from "./jsx/MyUnCoolComponent.jsx"' }), + tValid({ code: 'import foo from "./jsx/MyUnCoolComponent.jsx"' }), // commonjs setting - rest({ + tValid({ code: 'var foo = require("./bar")', options: [{ commonjs: true }], }), - rest({ + tValid({ code: 'require("./bar")', options: [{ commonjs: true }], }), - rest({ + tValid({ code: 'require("./does-not-exist")', options: [{ commonjs: false }], }), - rest({ code: 'require("./does-not-exist")' }), + tValid({ code: 'require("./does-not-exist")' }), // amd setting - rest({ + tValid({ code: 'require(["./bar"], function (bar) {})', options: [{ amd: true }], }), - rest({ + tValid({ code: 'define(["./bar"], function (bar) {})', options: [{ amd: true }], }), - rest({ + tValid({ code: 'require(["./does-not-exist"], function (bar) {})', options: [{ amd: false }], }), // magic modules: https://github.com/requirejs/requirejs/wiki/Differences-between-the-simplified-CommonJS-wrapper-and-standard-AMD-define#magic-modules - rest({ + tValid({ code: 'define(["require", "exports", "module"], function (r, e, m) { })', options: [{ amd: true }], }), // don't validate without callback param - rest({ + tValid({ code: 'require(["./does-not-exist"])', options: [{ amd: true }], }), - rest({ code: 'define(["./does-not-exist"], function (bar) {})' }), + tValid({ code: 'define(["./does-not-exist"], function (bar) {})' }), // stress tests - rest({ + tValid({ code: 'require("./does-not-exist", "another arg")', options: [{ commonjs: true, amd: true }], }), - rest({ + tValid({ code: 'proxyquire("./does-not-exist")', options: [{ commonjs: true, amd: true }], }), - rest({ + tValid({ code: '(function() {})("./does-not-exist")', options: [{ commonjs: true, amd: true }], }), - rest({ + tValid({ code: 'define([0, foo], function (bar) {})', options: [{ amd: true }], }), - rest({ + tValid({ code: 'require(0)', options: [{ commonjs: true }], }), - rest({ + tValid({ code: 'require(foo)', options: [{ commonjs: true }], }), ], invalid: [ - rest({ + tInvalid({ code: 'import reallyfake from "./reallyfake/module"', settings: { 'import-x/ignore': ['^\\./fake/'] }, - errors: [ - { - message: "Unable to resolve path to module './reallyfake/module'.", - }, - ], + errors: [createError('unresolved', './reallyfake/module')], }), - rest({ + tInvalid({ code: "import bar from './baz';", - errors: [ - { - message: "Unable to resolve path to module './baz'.", - type: 'Literal', - }, - ], + errors: [createError('unresolved', './baz', 'Literal')], }), - rest({ + tInvalid({ code: "import bar from './empty-folder';", - errors: [ - { - message: "Unable to resolve path to module './empty-folder'.", - type: 'Literal', - }, - ], + errors: [createError('unresolved', './empty-folder', 'Literal')], }), // sanity check that this module is _not_ found without proper settings - rest({ + tInvalid({ code: "import { DEEP } from 'in-alternate-root';", - errors: [ - { - message: "Unable to resolve path to module 'in-alternate-root'.", - type: 'Literal', - }, - ], + errors: [createError('unresolved', 'in-alternate-root', 'Literal')], }), - rest({ + tInvalid({ code: "import('in-alternate-root').then(function({DEEP}) {});", - errors: [ - { - message: "Unable to resolve path to module 'in-alternate-root'.", - type: 'Literal', - }, - ], + errors: [createError('unresolved', 'in-alternate-root', 'Literal')], languageOptions: { parser: require(parsers.BABEL) }, }), - rest({ + tInvalid({ code: 'export { foo } from "./does-not-exist"', - errors: ["Unable to resolve path to module './does-not-exist'."], + errors: [createError('unresolved', './does-not-exist')], }), - rest({ + tInvalid({ code: 'export * from "./does-not-exist"', - errors: ["Unable to resolve path to module './does-not-exist'."], + errors: [createError('unresolved', './does-not-exist')], }), // check with eslint parser - rest({ + tInvalid({ code: "import('in-alternate-root').then(function({DEEP}) {});", - errors: [ - { - message: "Unable to resolve path to module 'in-alternate-root'.", - type: 'Literal', - }, - ], + errors: [createError('unresolved', 'in-alternate-root', 'Literal')], languageOptions: { parserOptions: { ecmaVersion: 2021 }, }, }), // export symmetry proposal - rest({ + tInvalid({ code: 'export * as bar from "./does-not-exist"', languageOptions: { parser: require(parsers.BABEL) }, - errors: ["Unable to resolve path to module './does-not-exist'."], + errors: [createError('unresolved', './does-not-exist')], }), - rest({ + tInvalid({ code: 'export bar from "./does-not-exist"', languageOptions: { parser: require(parsers.BABEL) }, - errors: ["Unable to resolve path to module './does-not-exist'."], + errors: [createError('unresolved', './does-not-exist')], }), // commonjs setting - rest({ + tInvalid({ code: 'var bar = require("./baz")', options: [{ commonjs: true }], - errors: [ - { - message: "Unable to resolve path to module './baz'.", - type: 'Literal', - }, - ], + errors: [createError('unresolved', './baz', 'Literal')], }), - rest({ + tInvalid({ code: 'require("./baz")', options: [{ commonjs: true }], - errors: [ - { - message: "Unable to resolve path to module './baz'.", - type: 'Literal', - }, - ], + errors: [createError('unresolved', './baz', 'Literal')], }), // amd - rest({ + tInvalid({ code: 'require(["./baz"], function (bar) {})', options: [{ amd: true }], - errors: [ - { - message: "Unable to resolve path to module './baz'.", - type: 'Literal', - }, - ], + errors: [createError('unresolved', './baz', 'Literal')], }), - rest({ + tInvalid({ code: 'define(["./baz"], function (bar) {})', options: [{ amd: true }], - errors: [ - { - message: "Unable to resolve path to module './baz'.", - type: 'Literal', - }, - ], + errors: [createError('unresolved', './baz', 'Literal')], }), - rest({ + tInvalid({ code: 'define(["./baz", "./bar", "./does-not-exist"], function (bar) {})', options: [{ amd: true }], errors: [ - { - message: "Unable to resolve path to module './baz'.", - type: 'Literal', - }, - { - message: "Unable to resolve path to module './does-not-exist'.", - type: 'Literal', - }, + createError('unresolved', './baz', 'Literal'), + createError('unresolved', './does-not-exist', 'Literal'), ], }), ], @@ -281,21 +239,21 @@ function runResolverTests(resolver: 'node' | 'webpack') { ruleTester.run(`issue #333 (${resolver})`, rule, { valid: [ - rest({ code: 'import foo from "./bar.json"' }), - rest({ code: 'import foo from "./bar"' }), - rest({ + tValid({ code: 'import foo from "./bar.json"' }), + tValid({ code: 'import foo from "./bar"' }), + tValid({ code: 'import foo from "./bar.json"', settings: { 'import-x/extensions': ['.js'] }, }), - rest({ + tValid({ code: 'import foo from "./bar"', settings: { 'import-x/extensions': ['.js'] }, }), ], invalid: [ - rest({ + tInvalid({ code: 'import bar from "./foo.json"', - errors: ["Unable to resolve path to module './foo.json'."], + errors: [createError('unresolved', './foo.json', 'Literal')], }), ], }) @@ -309,7 +267,7 @@ function runResolverTests(resolver: 'node' | 'webpack') { ruleTester.run('case sensitivity', rule, { valid: [ - rest({ + tValid({ // test with explicit flag code: 'import foo from "./jsx/MyUncoolComponent.jsx"', options: [{ caseSensitive: false }], @@ -317,19 +275,19 @@ function runResolverTests(resolver: 'node' | 'webpack') { ], invalid: [ - rest({ + tInvalid({ // test default code: 'import foo from "./jsx/MyUncoolComponent.jsx"', errors: [ - `Casing of ./jsx/MyUncoolComponent.jsx does not match the underlying filesystem.`, + createError('casingMismatch', './jsx/MyUncoolComponent.jsx'), ], }), - rest({ + tInvalid({ // test with explicit flag code: 'import foo from "./jsx/MyUncoolComponent.jsx"', options: [{ caseSensitive: true }], errors: [ - `Casing of ./jsx/MyUncoolComponent.jsx does not match the underlying filesystem.`, + createError('casingMismatch', './jsx/MyUncoolComponent.jsx'), ], }), ], @@ -338,7 +296,7 @@ function runResolverTests(resolver: 'node' | 'webpack') { ruleTester.run('case sensitivity strict', rule, { valid: [ // #1259 issue - rest({ + tValid({ // caseSensitiveStrict is disabled by default code: `import foo from "${mismatchedPath}"`, }), @@ -346,21 +304,17 @@ function runResolverTests(resolver: 'node' | 'webpack') { invalid: [ // #1259 issue - rest({ + tInvalid({ // test with enabled caseSensitiveStrict option code: `import foo from "${mismatchedPath}"`, options: [{ caseSensitiveStrict: true }], - errors: [ - `Casing of ${mismatchedPath} does not match the underlying filesystem.`, - ], + errors: [createError('casingMismatch', mismatchedPath)], }), - rest({ + tInvalid({ // test with enabled caseSensitiveStrict option and disabled caseSensitive code: `import foo from "${mismatchedPath}"`, options: [{ caseSensitiveStrict: true, caseSensitive: false }], - errors: [ - `Casing of ${mismatchedPath} does not match the underlying filesystem.`, - ], + errors: [createError('casingMismatch', mismatchedPath)], }), ], }) @@ -371,9 +325,11 @@ for (const resolver of ['node', 'webpack'] as const) { runResolverTests(resolver) } +const { tValid, tInvalid } = createRuleTestCaseFunctions() + ruleTester.run('no-unresolved (import-x resolve legacy)', rule, { valid: [ - test({ + tValid({ code: "import { DEEP } from 'in-alternate-root';", settings: { 'import-x/resolve': { @@ -382,7 +338,7 @@ ruleTester.run('no-unresolved (import-x resolve legacy)', rule, { }, }), - test({ + tValid({ code: "import { DEEP } from 'in-alternate-root'; import { bar } from 'src-bar';", settings: { 'import-x/resolve': { @@ -391,79 +347,79 @@ ruleTester.run('no-unresolved (import-x resolve legacy)', rule, { }, }), - test({ + tValid({ code: 'import * as foo from "jsx-module/foo"', settings: { 'import-x/resolve': { extensions: ['.jsx'] } }, }), ], invalid: [ - test({ + tInvalid({ code: 'import * as foo from "jsx-module/foo"', - errors: ["Unable to resolve path to module 'jsx-module/foo'."], + errors: [createError('unresolved', 'jsx-module/foo')], }), ], }) ruleTester.run('no-unresolved (webpack-specific)', rule, { valid: [ - test({ + tValid({ // default webpack config in fixtures/webpack.config.js knows about jsx code: 'import * as foo from "jsx-module/foo"', settings: { 'import-x/resolver': 'webpack' }, }), - test({ + tValid({ // should ignore loaders code: 'import * as foo from "some-loader?with=args!jsx-module/foo"', settings: { 'import-x/resolver': 'webpack' }, }), ], invalid: [ - test({ + tInvalid({ // default webpack config in fixtures/webpack.config.js knows about jsx code: 'import * as foo from "jsx-module/foo"', settings: { 'import-x/resolver': { webpack: { config: 'webpack.empty.config.js' } }, }, - errors: ["Unable to resolve path to module 'jsx-module/foo'."], + errors: [createError('unresolved', 'jsx-module/foo')], }), ], }) ruleTester.run('no-unresolved ignore list', rule, { valid: [ - test({ + tValid({ code: 'import "./malformed.js"', languageOptions: { parser: require(parsers.BABEL) }, options: [{ ignore: ['.png$', '.gif$'] }], }), - test({ + tValid({ code: 'import "./test.giffy"', options: [{ ignore: ['.png$', '.gif$'] }], }), - test({ + tValid({ code: 'import "./test.gif"', options: [{ ignore: ['.png$', '.gif$'] }], }), - test({ + tValid({ code: 'import "./test.png"', options: [{ ignore: ['.png$', '.gif$'] }], }), ], invalid: [ - test({ + tInvalid({ code: 'import "./test.gif"', options: [{ ignore: ['.png$'] }], - errors: ["Unable to resolve path to module './test.gif'."], + errors: [createError('unresolved', './test.gif')], }), - test({ + tInvalid({ code: 'import "./test.png"', options: [{ ignore: ['.gif$'] }], - errors: ["Unable to resolve path to module './test.png'."], + errors: [createError('unresolved', './test.png')], }), ], }) @@ -473,24 +429,26 @@ ruleTester.run('no-unresolved unknown resolver', rule, { invalid: [ // logs resolver load error - test({ + tInvalid({ code: 'import "./malformed.js"', languageOptions: { parser: require(parsers.BABEL) }, settings: { 'import-x/resolver': 'doesnt-exist' }, errors: [ + // @ts-expect-error resolver error `Resolve error: unable to load resolver "doesnt-exist".`, - `Unable to resolve path to module './malformed.js'.`, + createError('unresolved', './malformed.js'), ], }), // only logs resolver message once - test({ + tInvalid({ code: 'import "./malformed.js"; import "./fake.js"', settings: { 'import-x/resolver': 'doesnt-exist' }, errors: [ + // @ts-expect-error resolver error `Resolve error: unable to load resolver "doesnt-exist".`, - `Unable to resolve path to module './malformed.js'.`, - `Unable to resolve path to module './fake.js'.`, + createError('unresolved', './malformed.js'), + createError('unresolved', './fake.js'), ], }), ], @@ -498,15 +456,15 @@ ruleTester.run('no-unresolved unknown resolver', rule, { ruleTester.run('no-unresolved electron', rule, { valid: [ - test({ + tValid({ code: 'import "electron"', settings: { 'import-x/core-modules': ['electron'] }, }), ], invalid: [ - test({ + tInvalid({ code: 'import "electron"', - errors: [`Unable to resolve path to module 'electron'.`], + errors: [createError('unresolved', 'electron')], }), ], }) @@ -519,18 +477,16 @@ ruleTester.run('no-unresolved syntax verification', rule, { // https://github.com/import-js/eslint-plugin-import-x/issues/2024 ruleTester.run('import() with built-in parser', rule, { valid: [ - test({ + tValid({ code: "import('fs');", languageOptions: { parserOptions: { ecmaVersion: 2021 } }, }), ], invalid: [ - test({ + tInvalid({ code: 'import("./does-not-exist-l0w9ssmcqy9").then(() => {})', languageOptions: { parserOptions: { ecmaVersion: 2021 } }, - errors: [ - "Unable to resolve path to module './does-not-exist-l0w9ssmcqy9'.", - ], + errors: [createError('unresolved', './does-not-exist-l0w9ssmcqy9')], }), ], }) @@ -539,21 +495,21 @@ describe('TypeScript', () => { // Type-only imports were added in TypeScript ESTree 2.23.0 ruleTester.run('no-unresolved (ignore type-only)', rule, { valid: [ - test({ + tValid({ code: 'import type { JSONSchema7Type } from "@types/json-schema";', }), - test({ + tValid({ code: 'export type { JSONSchema7Type } from "@types/json-schema";', }), ], invalid: [ - test({ + tInvalid({ code: 'import { JSONSchema7Type } from "@types/json-schema";', - errors: ["Unable to resolve path to module '@types/json-schema'."], + errors: [createError('unresolved', '@types/json-schema')], }), - test({ + tInvalid({ code: 'export { JSONSchema7Type } from "@types/json-schema";', - errors: ["Unable to resolve path to module '@types/json-schema'."], + errors: [createError('unresolved', '@types/json-schema')], }), ], }) From 0529088cc4a7bef65e7e651fcfc3d05f62d12790 Mon Sep 17 00:00:00 2001 From: Marco Pasqualetti Date: Wed, 20 Nov 2024 22:52:09 +0100 Subject: [PATCH 42/49] =?UTF-8?q?fix(no-unused-modules):=20improves=20the?= =?UTF-8?q?=20type=20declaration=20of=20the=20rule=20`no-unused-modules`?= =?UTF-8?q?=E2=80=99s=20`missingExports`=20option?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .changeset/nice-schools-drive.md | 5 + src/rules/no-unused-modules.ts | 2 +- test/rules/no-unused-modules.spec.ts | 857 ++++++++++++--------------- 3 files changed, 394 insertions(+), 470 deletions(-) create mode 100644 .changeset/nice-schools-drive.md diff --git a/.changeset/nice-schools-drive.md b/.changeset/nice-schools-drive.md new file mode 100644 index 000000000..61b2a1474 --- /dev/null +++ b/.changeset/nice-schools-drive.md @@ -0,0 +1,5 @@ +--- +"eslint-plugin-import-x": patch +--- + +fix(no-unused-modules): improves the type declaration of the rule `no-unused-modules`’s `missingExports` option diff --git a/src/rules/no-unused-modules.ts b/src/rules/no-unused-modules.ts index 6a2f4b81e..ee5e8193a 100644 --- a/src/rules/no-unused-modules.ts +++ b/src/rules/no-unused-modules.ts @@ -391,7 +391,7 @@ const fileIsInPkg = (file: string) => { type Options = { src?: string[] ignoreExports?: string[] - missingExports?: string[] + missingExports?: true unusedExports?: boolean ignoreUnusedTypeExports?: boolean } diff --git a/test/rules/no-unused-modules.spec.ts b/test/rules/no-unused-modules.spec.ts index aae74d1be..bbee8d3f9 100644 --- a/test/rules/no-unused-modules.spec.ts +++ b/test/rules/no-unused-modules.spec.ts @@ -1,12 +1,14 @@ import fs from 'node:fs' import { RuleTester as TSESLintRuleTester } from '@typescript-eslint/rule-tester' +import type { TestCaseError as TSESLintTestCaseError } from '@typescript-eslint/rule-tester' import type { TSESLint } from '@typescript-eslint/utils' // @ts-expect-error -- in correct types import { FlatRuleTester as ESLint8_56_FlatRuleTester } from 'eslint8.56/use-at-your-own-risk' import { RuleTester as ESLint9_FlatRuleTester } from 'eslint9' -import { test, testFilePath, parsers } from '../utils' +import { createRuleTestCaseFunctions, testFilePath, parsers } from '../utils' +import type { GetRuleModuleOptions, GetRuleModuleMessageIds } from '../utils' import jsxConfig from 'eslint-plugin-import-x/config/react' import typescriptConfig from 'eslint-plugin-import-x/config/typescript' @@ -16,15 +18,17 @@ const ruleTester = new TSESLintRuleTester() const typescriptRuleTester = new TSESLintRuleTester(typescriptConfig) const jsxRuleTester = new TSESLintRuleTester(jsxConfig) -const error = (message: string) => ({ message }) +const { tValid, tInvalid } = createRuleTestCaseFunctions() -const missingExportsOptions = [ +type RuleOptions = GetRuleModuleOptions + +const missingExportsOptions: RuleOptions = [ { missingExports: true, }, ] -const unusedExportsOptions = [ +const unusedExportsOptions: RuleOptions = [ { unusedExports: true, src: [testFilePath('./no-unused-modules/**/*.js')], @@ -32,7 +36,7 @@ const unusedExportsOptions = [ }, ] -const unusedExportsTypescriptOptions = [ +const unusedExportsTypescriptOptions: RuleOptions = [ { unusedExports: true, src: [testFilePath('./no-unused-modules/typescript')], @@ -40,7 +44,7 @@ const unusedExportsTypescriptOptions = [ }, ] -const unusedExportsTypescriptIgnoreUnusedTypesOptions = [ +const unusedExportsTypescriptIgnoreUnusedTypesOptions: RuleOptions = [ { unusedExports: true, ignoreUnusedTypeExports: true, @@ -49,7 +53,7 @@ const unusedExportsTypescriptIgnoreUnusedTypesOptions = [ }, ] -const unusedExportsJsxOptions = [ +const unusedExportsJsxOptions: RuleOptions = [ { unusedExports: true, src: [testFilePath('./no-unused-modules/jsx')], @@ -57,67 +61,76 @@ const unusedExportsJsxOptions = [ }, ] +function createUnusedError( + value: string, +): TSESLintTestCaseError> { + return { + messageId: 'unused', + data: { value }, + } +} + // tests for missing exports ruleTester.run('no-unused-modules', rule, { valid: [ - test({ + tValid({ code: 'export default function noOptions() {}', }), - test({ - options: missingExportsOptions, + tValid({ code: 'export default () => 1', - }), - test({ options: missingExportsOptions, + }), + tValid({ code: 'export const a = 1', + options: missingExportsOptions, }), - test({ + tValid({ options: missingExportsOptions, code: 'const a = 1; export { a }', }), - test({ - options: missingExportsOptions, + tValid({ code: 'function a() { return true }; export { a }', - }), - test({ options: missingExportsOptions, - code: 'const a = 1; const b = 2; export { a, b }', }), - test({ + tValid({ + code: 'const a = 1; const b = 2; export { a, b }', options: missingExportsOptions, - code: 'const a = 1; export default a', }), - test({ + tValid({ + code: 'const a = 1; export default a', options: missingExportsOptions, - code: 'export class Foo {}', }), - test({ + tValid({ + code: 'export class Foo {}', options: missingExportsOptions, - code: 'export const [foobar] = [];', }), - test({ + tValid({ + code: 'export const [foobar] = [];', options: missingExportsOptions, - code: 'export const [foobar] = foobarFactory();', }), - test({ + tValid({ + code: 'export const [foobar] = foobarFactory();', options: missingExportsOptions, + }), + tValid({ code: ` - export default function NewComponent () { - return 'I am new component' + export default function NewComponent () { + return 'I am new component' } - `, + `, + options: missingExportsOptions, }), ], invalid: [ - test({ - options: missingExportsOptions, + tInvalid({ code: 'const a = 1', - errors: [error(`No exports found`)], - }), - test({ options: missingExportsOptions, + errors: [{ messageId: 'notFound' }], + }), + tInvalid({ code: '/* const a = 1 */', - errors: [error(`No exports found`)], + options: missingExportsOptions, + errors: [{ messageId: 'notFound' }], }), ], }) @@ -125,66 +138,66 @@ ruleTester.run('no-unused-modules', rule, { // tests for exports ruleTester.run('no-unused-modules', rule, { valid: [ - test({ - options: unusedExportsOptions, + tValid({ code: 'import { o2 } from "./file-o";export default () => 12', + options: unusedExportsOptions, filename: testFilePath('./no-unused-modules/file-a.js'), languageOptions: { parser: require(parsers.BABEL) }, }), - test({ - options: unusedExportsOptions, + tValid({ code: 'export const b = 2', + options: unusedExportsOptions, filename: testFilePath('./no-unused-modules/file-b.js'), languageOptions: { parser: require(parsers.BABEL) }, }), - test({ - options: unusedExportsOptions, + tValid({ code: 'const c1 = 3; function c2() { return 3 }; export { c1, c2 }', + options: unusedExportsOptions, filename: testFilePath('./no-unused-modules/file-c.js'), languageOptions: { parser: require(parsers.BABEL) }, }), - test({ - options: unusedExportsOptions, + tValid({ code: 'export function d() { return 4 }', + options: unusedExportsOptions, filename: testFilePath('./no-unused-modules/file-d.js'), languageOptions: { parser: require(parsers.BABEL) }, }), - test({ - options: unusedExportsOptions, + tValid({ code: 'export class q { q0() {} }', + options: unusedExportsOptions, filename: testFilePath('./no-unused-modules/file-q.js'), languageOptions: { parser: require(parsers.BABEL) }, }), - test({ - options: unusedExportsOptions, + tValid({ code: 'const e0 = 5; export { e0 as e }', + options: unusedExportsOptions, filename: testFilePath('./no-unused-modules/file-e.js'), languageOptions: { parser: require(parsers.BABEL) }, }), - test({ - options: unusedExportsOptions, + tValid({ code: 'const l0 = 5; const l = 10; export { l0 as l1, l }; export default () => {}', + options: unusedExportsOptions, filename: testFilePath('./no-unused-modules/file-l.js'), languageOptions: { parser: require(parsers.BABEL) }, }), - test({ - options: unusedExportsOptions, + tValid({ code: 'const o0 = 0; const o1 = 1; export { o0, o1 as o2 }; export default () => {}', + options: unusedExportsOptions, filename: testFilePath('./no-unused-modules/file-o.js'), languageOptions: { parser: require(parsers.BABEL) }, }), - test({ - options: unusedExportsOptions, + tValid({ code: ` - export const [o0, o2] = createLoadingAndErrorSelectors( - AUTH_USER + export const [o0, o2] = createLoadingAndErrorSelectors( + AUTH_USER ); - `, + `, + options: unusedExportsOptions, filename: testFilePath('./no-unused-modules/file-o.js'), }), ], invalid: [ - test({ + tInvalid({ options: unusedExportsOptions, code: ` import eslint from 'eslint' @@ -203,31 +216,17 @@ ruleTester.run('no-unused-modules', rule, { `, filename: testFilePath('./no-unused-modules/file-0.js'), errors: [ - { - message: `exported declaration 'default' not used within other modules`, - line: 12, - column: 18, - }, - { - message: `exported declaration 'o0' not used within other modules`, - line: 12, - column: 27, - }, - { - message: `exported declaration 'o3' not used within other modules`, - line: 12, - column: 31, - }, - error(`exported declaration 'p' not used within other modules`), + { ...createUnusedError('default'), line: 12, column: 18 }, + { ...createUnusedError('o0'), line: 12, column: 27 }, + { ...createUnusedError('o3'), line: 12, column: 31 }, + { ...createUnusedError('p'), line: 13, column: 18 }, ], }), - test({ + tInvalid({ options: unusedExportsOptions, code: `const n0 = 'n0'; const n1 = 42; export { n0, n1 }; export default () => {}`, filename: testFilePath('./no-unused-modules/file-n.js'), - errors: [ - error(`exported declaration 'default' not used within other modules`), - ], + errors: [createUnusedError('default')], }), ], }) @@ -236,54 +235,47 @@ ruleTester.run('no-unused-modules', rule, { ruleTester.run('no-unused-modules', rule, { valid: [], invalid: [ - test({ + tInvalid({ options: unusedExportsOptions, code: 'export default () => 13', filename: testFilePath('./no-unused-modules/file-f.js'), - errors: [ - error(`exported declaration 'default' not used within other modules`), - ], + errors: [createUnusedError('default')], }), - test({ + tInvalid({ options: unusedExportsOptions, code: 'export const g = 2', filename: testFilePath('./no-unused-modules/file-g.js'), - errors: [error(`exported declaration 'g' not used within other modules`)], + errors: [createUnusedError('g')], }), - test({ + tInvalid({ options: unusedExportsOptions, code: 'const h1 = 3; function h2() { return 3 }; const h3 = true; export { h1, h2, h3 }', filename: testFilePath('./no-unused-modules/file-h.js'), - errors: [ - error(`exported declaration 'h1' not used within other modules`), - ], + errors: [createUnusedError('h1')], }), - test({ + tInvalid({ options: unusedExportsOptions, code: 'const i1 = 3; function i2() { return 3 }; export { i1, i2 }', filename: testFilePath('./no-unused-modules/file-i.js'), - errors: [ - error(`exported declaration 'i1' not used within other modules`), - error(`exported declaration 'i2' not used within other modules`), - ], + errors: [createUnusedError('i1'), createUnusedError('i2')], }), - test({ + tInvalid({ options: unusedExportsOptions, code: 'export function j() { return 4 }', filename: testFilePath('./no-unused-modules/file-j.js'), - errors: [error(`exported declaration 'j' not used within other modules`)], + errors: [createUnusedError('j')], }), - test({ + tInvalid({ options: unusedExportsOptions, code: 'export class q { q0() {} }', filename: testFilePath('./no-unused-modules/file-q.js'), - errors: [error(`exported declaration 'q' not used within other modules`)], + errors: [createUnusedError('q')], }), - test({ + tInvalid({ options: unusedExportsOptions, code: 'const k0 = 5; export { k0 as k }', filename: testFilePath('./no-unused-modules/file-k.js'), - errors: [error(`exported declaration 'k' not used within other modules`)], + errors: [createUnusedError('k')], }), ], }) @@ -294,7 +286,7 @@ describe('dynamic imports', () => { // test for unused exports with `import()` ruleTester.run('no-unused-modules', rule, { valid: [ - test({ + tValid({ options: unusedExportsOptions, code: ` export const a = 10 @@ -308,7 +300,7 @@ describe('dynamic imports', () => { }), ], invalid: [ - test({ + tInvalid({ options: unusedExportsOptions, code: ` export const a = 10 @@ -322,17 +314,17 @@ describe('dynamic imports', () => { './no-unused-modules/exports-for-dynamic-js-2.js', ), errors: [ - error(`exported declaration 'a' not used within other modules`), - 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`), + createUnusedError('a'), + createUnusedError('b'), + createUnusedError('c'), + createUnusedError('default'), ], }), ], }) typescriptRuleTester.run('no-unused-modules', rule, { valid: [ - test({ + tValid({ options: unusedExportsTypescriptOptions, code: ` export const ts_a = 10 @@ -345,17 +337,12 @@ describe('dynamic imports', () => { './no-unused-modules/typescript/exports-for-dynamic-ts.ts', ), }), - test({ + tValid({ code: ` import App from './App'; `, filename: testFilePath('./unused-modules-reexport-crash/src/index.tsx'), - options: [ - { - unusedExports: true, - ignoreExports: ['**/magic/**'], - }, - ], + options: [{ unusedExports: true, ignoreExports: ['**/magic/**'] }], }), ], invalid: [], @@ -365,27 +352,27 @@ describe('dynamic imports', () => { // // test for export from ruleTester.run('no-unused-modules', rule, { valid: [ - test({ + tValid({ options: unusedExportsOptions, code: `export { default } from './file-o'`, filename: testFilePath('./no-unused-modules/file-s.js'), }), ], invalid: [ - test({ + tInvalid({ options: unusedExportsOptions, code: `export { k } from '${testFilePath( './no-unused-modules/file-k.js', )}'`, filename: testFilePath('./no-unused-modules/file-j.js'), - errors: [error(`exported declaration 'k' not used within other modules`)], + errors: [createUnusedError('k')], }), ], }) ruleTester.run('no-unused-modules', rule, { valid: [ - test({ + tValid({ options: unusedExportsOptions, code: 'const k0 = 5; export { k0 as k }', filename: testFilePath('./no-unused-modules/file-k.js'), @@ -397,34 +384,34 @@ ruleTester.run('no-unused-modules', rule, { // test for ignored files ruleTester.run('no-unused-modules', rule, { valid: [ - test({ - options: unusedExportsOptions, + tValid({ code: 'export default () => 14', + options: unusedExportsOptions, filename: testFilePath('./no-unused-modules/file-ignored-a.js'), }), - test({ - options: unusedExportsOptions, + tValid({ code: 'export const b = 2', + options: unusedExportsOptions, filename: testFilePath('./no-unused-modules/file-ignored-b.js'), }), - test({ - options: unusedExportsOptions, + tValid({ code: 'const c1 = 3; function c2() { return 3 }; export { c1, c2 }', + options: unusedExportsOptions, filename: testFilePath('./no-unused-modules/file-ignored-c.js'), }), - test({ - options: unusedExportsOptions, + tValid({ code: 'export function d() { return 4 }', + options: unusedExportsOptions, filename: testFilePath('./no-unused-modules/file-ignored-d.js'), }), - test({ - options: unusedExportsOptions, + tValid({ code: 'const f = 5; export { f as e }', + options: unusedExportsOptions, filename: testFilePath('./no-unused-modules/file-ignored-e.js'), }), - test({ - options: unusedExportsOptions, + tValid({ code: 'const l0 = 5; const l = 10; export { l0 as l1, l }; export default () => {}', + options: unusedExportsOptions, filename: testFilePath('./no-unused-modules/file-ignored-l.js'), }), ], @@ -434,22 +421,20 @@ ruleTester.run('no-unused-modules', rule, { // add named import for file with default export ruleTester.run('no-unused-modules', rule, { valid: [ - test({ - options: unusedExportsOptions, + tValid({ code: `import { f } from '${testFilePath( './no-unused-modules/file-f.js', )}'`, + options: unusedExportsOptions, filename: testFilePath('./no-unused-modules/file-0.js'), }), ], invalid: [ - test({ - options: unusedExportsOptions, + tInvalid({ code: 'export default () => 15', + options: unusedExportsOptions, filename: testFilePath('./no-unused-modules/file-f.js'), - errors: [ - error(`exported declaration 'default' not used within other modules`), - ], + errors: [createUnusedError('default')], }), ], }) @@ -457,14 +442,14 @@ ruleTester.run('no-unused-modules', rule, { // add default import for file with default export ruleTester.run('no-unused-modules', rule, { valid: [ - test({ - options: unusedExportsOptions, + tValid({ code: `import f from '${testFilePath('./no-unused-modules/file-f.js')}'`, + options: unusedExportsOptions, filename: testFilePath('./no-unused-modules/file-0.js'), }), - test({ - options: unusedExportsOptions, + tValid({ code: 'export default () => 16', + options: unusedExportsOptions, filename: testFilePath('./no-unused-modules/file-f.js'), }), ], @@ -474,20 +459,20 @@ ruleTester.run('no-unused-modules', rule, { // add default import for file with named export ruleTester.run('no-unused-modules', rule, { valid: [ - test({ - options: unusedExportsOptions, + tValid({ code: `import g from '${testFilePath( './no-unused-modules/file-g.js', )}';import {h} from '${testFilePath('./no-unused-modules/file-gg.js')}'`, + options: unusedExportsOptions, filename: testFilePath('./no-unused-modules/file-0.js'), }), ], invalid: [ - test({ - options: unusedExportsOptions, + tInvalid({ code: 'export const g = 2', + options: unusedExportsOptions, filename: testFilePath('./no-unused-modules/file-g.js'), - errors: [error(`exported declaration 'g' not used within other modules`)], + errors: [createUnusedError('g')], }), ], }) @@ -495,16 +480,16 @@ ruleTester.run('no-unused-modules', rule, { // add named import for file with named export ruleTester.run('no-unused-modules', rule, { valid: [ - test({ - options: unusedExportsOptions, + tValid({ code: `import { g } from '${testFilePath( './no-unused-modules/file-g.js', )}'; import eslint from 'eslint'`, filename: testFilePath('./no-unused-modules/file-0.js'), - }), - test({ options: unusedExportsOptions, + }), + tValid({ code: 'export const g = 2', + options: unusedExportsOptions, filename: testFilePath('./no-unused-modules/file-g.js'), }), ], @@ -514,20 +499,20 @@ ruleTester.run('no-unused-modules', rule, { // add different named import for file with named export ruleTester.run('no-unused-modules', rule, { valid: [ - test({ - options: unusedExportsOptions, + tValid({ code: `import { c } from '${testFilePath( './no-unused-modules/file-b.js', )}'`, filename: testFilePath('./no-unused-modules/file-0.js'), + options: unusedExportsOptions, }), ], invalid: [ - test({ - options: unusedExportsOptions, + tInvalid({ code: 'export const b = 2', filename: testFilePath('./no-unused-modules/file-b.js'), - errors: [error(`exported declaration 'b' not used within other modules`)], + options: unusedExportsOptions, + errors: [createUnusedError('b')], }), ], }) @@ -535,16 +520,16 @@ ruleTester.run('no-unused-modules', rule, { // add renamed named import for file with named export ruleTester.run('no-unused-modules', rule, { valid: [ - test({ - options: unusedExportsOptions, + tValid({ code: `import { g as g1 } from '${testFilePath( './no-unused-modules/file-g.js', )}'; import eslint from 'eslint'`, + options: unusedExportsOptions, filename: testFilePath('./no-unused-modules/file-0.js'), }), - test({ - options: unusedExportsOptions, + tValid({ code: 'export const g = 2', + options: unusedExportsOptions, filename: testFilePath('./no-unused-modules/file-g.js'), }), ], @@ -554,20 +539,20 @@ ruleTester.run('no-unused-modules', rule, { // add different renamed named import for file with named export ruleTester.run('no-unused-modules', rule, { valid: [ - test({ - options: unusedExportsOptions, + tValid({ code: `import { g1 as g } from '${testFilePath( './no-unused-modules/file-g.js', )}'`, + options: unusedExportsOptions, filename: testFilePath('./no-unused-modules/file-0.js'), }), ], invalid: [ - test({ - options: unusedExportsOptions, + tInvalid({ code: 'export const g = 2', + options: unusedExportsOptions, filename: testFilePath('./no-unused-modules/file-g.js'), - errors: [error(`exported declaration 'g' not used within other modules`)], + errors: [createUnusedError('g')], }), ], }) @@ -575,22 +560,20 @@ ruleTester.run('no-unused-modules', rule, { // remove default import for file with default export ruleTester.run('no-unused-modules', rule, { valid: [ - test({ - options: unusedExportsOptions, + tValid({ code: `import { a1, a2 } from '${testFilePath( './no-unused-modules/file-a.js', )}'`, filename: testFilePath('./no-unused-modules/file-0.js'), + options: unusedExportsOptions, }), ], invalid: [ - test({ - options: unusedExportsOptions, + tInvalid({ code: 'export default () => 17', filename: testFilePath('./no-unused-modules/file-a.js'), - errors: [ - error(`exported declaration 'default' not used within other modules`), - ], + options: unusedExportsOptions, + errors: [createUnusedError('default')], }), ], }) @@ -599,31 +582,31 @@ ruleTester.run('no-unused-modules', rule, { ruleTester.run('no-unused-modules', rule, { valid: [], invalid: [ - test({ - options: unusedExportsOptions, + tInvalid({ code: 'const m0 = 5; const m = 10; export { m0 as m1, m }; export default () => {}', filename: testFilePath('./no-unused-modules/file-m.js'), + options: unusedExportsOptions, errors: [ - error(`exported declaration 'm1' not used within other modules`), - error(`exported declaration 'm' not used within other modules`), - error(`exported declaration 'default' not used within other modules`), + createUnusedError('m1'), + createUnusedError('m'), + createUnusedError('default'), ], }), ], }) ruleTester.run('no-unused-modules', rule, { valid: [ - test({ - options: unusedExportsOptions, + tValid({ code: `import * as m from '${testFilePath( './no-unused-modules/file-m.js', )}'; import unknown from 'unknown-module'`, filename: testFilePath('./no-unused-modules/file-0.js'), - }), - test({ options: unusedExportsOptions, + }), + tValid({ code: 'const m0 = 5; const m = 10; export { m0 as m1, m }; export default () => {}', filename: testFilePath('./no-unused-modules/file-m.js'), + options: unusedExportsOptions, }), ], invalid: [], @@ -632,23 +615,23 @@ ruleTester.run('no-unused-modules', rule, { // remove all exports ruleTester.run('no-unused-modules', rule, { valid: [ - test({ - options: unusedExportsOptions, + tValid({ code: `/* import * as m from '${testFilePath( './no-unused-modules/file-m.js', )}' */`, filename: testFilePath('./no-unused-modules/file-0.js'), + options: unusedExportsOptions, }), ], invalid: [ - test({ - options: unusedExportsOptions, + tInvalid({ code: 'const m0 = 5; const m = 10; export { m0 as m1, m }; export default () => {}', filename: testFilePath('./no-unused-modules/file-m.js'), + options: unusedExportsOptions, errors: [ - error(`exported declaration 'm1' not used within other modules`), - error(`exported declaration 'm' not used within other modules`), - error(`exported declaration 'default' not used within other modules`), + createUnusedError('m1'), + createUnusedError('m'), + createUnusedError('default'), ], }), ], @@ -656,10 +639,10 @@ ruleTester.run('no-unused-modules', rule, { ruleTester.run('no-unused-modules', rule, { valid: [ - test({ - options: unusedExportsOptions, + tValid({ code: `export * from '${testFilePath('./no-unused-modules/file-m.js')}';`, filename: testFilePath('./no-unused-modules/file-0.js'), + options: unusedExportsOptions, }), ], invalid: [], @@ -667,13 +650,11 @@ ruleTester.run('no-unused-modules', rule, { ruleTester.run('no-unused-modules', rule, { valid: [], invalid: [ - test({ - options: unusedExportsOptions, + tInvalid({ code: 'const m0 = 5; const m = 10; export { m0 as m1, m }; export default () => {}', filename: testFilePath('./no-unused-modules/file-m.js'), - errors: [ - error(`exported declaration 'default' not used within other modules`), - ], + options: unusedExportsOptions, + errors: [createUnusedError('default')], }), ], }) @@ -681,24 +662,19 @@ ruleTester.run('no-unused-modules', rule, { ruleTester.run('no-unused-modules', rule, { valid: [], invalid: [ - test({ - options: unusedExportsOptions, + tInvalid({ code: `export { m1, m} from '${testFilePath( './no-unused-modules/file-m.js', )}';`, filename: testFilePath('./no-unused-modules/file-0.js'), - errors: [ - error(`exported declaration 'm1' not used within other modules`), - error(`exported declaration 'm' not used within other modules`), - ], - }), - test({ options: unusedExportsOptions, + errors: [createUnusedError('m1'), createUnusedError('m')], + }), + tInvalid({ code: 'const m0 = 5; const m = 10; export { m0 as m1, m }; export default () => {}', filename: testFilePath('./no-unused-modules/file-m.js'), - errors: [ - error(`exported declaration 'default' not used within other modules`), - ], + options: unusedExportsOptions, + errors: [createUnusedError('default')], }), ], }) @@ -714,22 +690,19 @@ ruleTester.run('no-unused-modules', rule, { */ ], invalid: [ - test({ - options: unusedExportsOptions, + tInvalid({ code: `export { default, m1 } from '${testFilePath( './no-unused-modules/file-m.js', )}';`, filename: testFilePath('./no-unused-modules/file-0.js'), - errors: [ - error(`exported declaration 'default' not used within other modules`), - error(`exported declaration 'm1' not used within other modules`), - ], - }), - test({ options: unusedExportsOptions, + errors: [createUnusedError('default'), createUnusedError('m1')], + }), + tInvalid({ code: 'const m0 = 5; const m = 10; export { m0 as m1, m }; export default () => {}', filename: testFilePath('./no-unused-modules/file-m.js'), - errors: [error(`exported declaration 'm' not used within other modules`)], + options: unusedExportsOptions, + errors: [createUnusedError('m')], }), ], }) @@ -737,10 +710,10 @@ ruleTester.run('no-unused-modules', rule, { // Test that import and export in the same file both counts as usage ruleTester.run('no-unused-modules', rule, { valid: [ - test({ - options: unusedExportsOptions, + tValid({ code: `export const a = 5;export const b = 't1'`, filename: testFilePath('./no-unused-modules/import-export-1.js'), + options: unusedExportsOptions, }), ], invalid: [], @@ -749,38 +722,38 @@ ruleTester.run('no-unused-modules', rule, { describe('renameDefault', () => { ruleTester.run('no-unused-modules', rule, { valid: [ - test({ - options: unusedExportsOptions, + tValid({ code: 'export { default as Component } from "./Component"', filename: testFilePath( './no-unused-modules/renameDefault/components.js', ), - }), - test({ options: unusedExportsOptions, + }), + tValid({ code: 'export default function Component() {}', filename: testFilePath( './no-unused-modules/renameDefault/Component.js', ), + options: unusedExportsOptions, }), ], invalid: [], }) ruleTester.run('no-unused-modules', rule, { valid: [ - test({ - options: unusedExportsOptions, + tValid({ code: 'export { default as ComponentA } from "./ComponentA";export { default as ComponentB } from "./ComponentB";', filename: testFilePath( './no-unused-modules/renameDefault-2/components.js', ), - }), - test({ options: unusedExportsOptions, + }), + tValid({ code: 'export default function ComponentA() {};', filename: testFilePath( './no-unused-modules/renameDefault-2/ComponentA.js', ), + options: unusedExportsOptions, }), ], invalid: [], @@ -798,17 +771,17 @@ describe('test behavior for new file', () => { // add import in newly created file ruleTester.run('no-unused-modules', rule, { valid: [ - test({ - options: unusedExportsOptions, + tValid({ code: `import * as m from '${testFilePath( './no-unused-modules/file-m.js', )}'`, filename: testFilePath('./no-unused-modules/file-added-0.js'), - }), - test({ options: unusedExportsOptions, + }), + tValid({ code: 'const m0 = 5; const m = 10; export { m0 as m1, m }; export default () => {}', filename: testFilePath('./no-unused-modules/file-m.js'), + options: unusedExportsOptions, }), ], invalid: [], @@ -818,30 +791,28 @@ describe('test behavior for new file', () => { ruleTester.run('no-unused-modules', rule, { valid: [], invalid: [ - test({ - options: unusedExportsOptions, + tInvalid({ code: `export default () => {2}`, filename: testFilePath('./no-unused-modules/file-added-0.js'), - errors: [ - error(`exported declaration 'default' not used within other modules`), - ], + options: unusedExportsOptions, + errors: [createUnusedError('default')], }), ], }) ruleTester.run('no-unused-modules', rule, { valid: [ - test({ - options: unusedExportsOptions, + tValid({ code: `import def from '${testFilePath( './no-unused-modules/file-added-0.js', )}'`, filename: testFilePath('./no-unused-modules/file-0.js'), - }), - test({ options: unusedExportsOptions, + }), + tValid({ code: `export default () => {}`, filename: testFilePath('./no-unused-modules/file-added-0.js'), + options: unusedExportsOptions, }), ], invalid: [], @@ -850,37 +821,35 @@ describe('test behavior for new file', () => { // export * only considers named imports. default imports still need to be reported ruleTester.run('no-unused-modules', rule, { valid: [ - test({ - options: unusedExportsOptions, + tValid({ code: `export * from '${testFilePath( './no-unused-modules/file-added-0.js', )}'`, filename: testFilePath('./no-unused-modules/file-0.js'), + options: unusedExportsOptions, }), // Test export * from 'external-compiled-library' - test({ - options: unusedExportsOptions, + tValid({ code: `export * from 'external-compiled-library'`, filename: testFilePath('./no-unused-modules/file-r.js'), + options: unusedExportsOptions, }), ], invalid: [ - test({ - options: unusedExportsOptions, + tInvalid({ code: `export const z = 'z';export default () => {}`, filename: testFilePath('./no-unused-modules/file-added-0.js'), - errors: [ - error(`exported declaration 'default' not used within other modules`), - ], + options: unusedExportsOptions, + errors: [createUnusedError('default')], }), ], }) ruleTester.run('no-unused-modules', rule, { valid: [ - test({ - options: unusedExportsOptions, + tValid({ code: `export const a = 2`, filename: testFilePath('./no-unused-modules/file-added-0.js'), + options: unusedExportsOptions, }), ], invalid: [], @@ -890,24 +859,19 @@ describe('test behavior for new file', () => { ruleTester.run('no-unused-modules', rule, { valid: [], invalid: [ - test({ - options: unusedExportsOptions, + tInvalid({ code: `export { a } from '${testFilePath( './no-unused-modules/file-added-0.js', )}'`, filename: testFilePath('./no-unused-modules/file-0.js'), - errors: [ - error(`exported declaration 'a' not used within other modules`), - ], - }), - test({ options: unusedExportsOptions, + errors: [createUnusedError('a')], + }), + tInvalid({ code: `export const z = 'z';export default () => {}`, filename: testFilePath('./no-unused-modules/file-added-0.js'), - errors: [ - error(`exported declaration 'z' not used within other modules`), - error(`exported declaration 'default' not used within other modules`), - ], + options: unusedExportsOptions, + errors: [createUnusedError('z'), createUnusedError('default')], }), ], }) @@ -920,26 +884,23 @@ describe('test behavior for new file', () => { { encoding: 'utf8', flag: 'w' }, ) }) + ruleTester.run('no-unused-modules', rule, { valid: [ - test({ - options: unusedExportsOptions, + tValid({ code: `export * from '${testFilePath( './no-unused-modules/file-added-1.js', )}'`, filename: testFilePath('./no-unused-modules/file-0.js'), + options: unusedExportsOptions, }), ], invalid: [ - test({ - options: unusedExportsOptions, + tInvalid({ code: `export const z = 'z';export default () => {}`, filename: testFilePath('./no-unused-modules/file-added-1.js'), - errors: [ - error( - `exported declaration 'default' not used within other modules`, - ), - ], + options: unusedExportsOptions, + errors: [createUnusedError('default')], }), ], }) @@ -966,17 +927,17 @@ describe('test behavior for new file', () => { }) ruleTester.run('no-unused-modules', rule, { valid: [ - test({ - options: unusedExportsOptions, + tValid({ code: `import added from '${testFilePath( './no-unused-modules/file-added-2.js', )}'`, filename: testFilePath('./no-unused-modules/file-added-1.js'), - }), - test({ options: unusedExportsOptions, + }), + tValid({ code: `export default () => {}`, filename: testFilePath('./no-unused-modules/file-added-2.js'), + options: unusedExportsOptions, }), ], invalid: [], @@ -997,17 +958,17 @@ describe('test behavior for new file', () => { }) ruleTester.run('no-unused-modules', rule, { valid: [ - test({ - options: unusedExportsOptions, + tValid({ code: `import { added } from '${testFilePath( './no-unused-modules/file-added-3.js', )}'`, filename: testFilePath('./no-unused-modules/file-added-1.js'), - }), - test({ options: unusedExportsOptions, + }), + tValid({ code: `export const added = () => {}`, filename: testFilePath('./no-unused-modules/file-added-3.js'), + options: unusedExportsOptions, }), ], invalid: [], @@ -1022,27 +983,25 @@ describe('test behavior for new file', () => { describe('test behavior for destructured exports', () => { ruleTester.run('no-unused-modules', rule, { valid: [ - test({ - options: unusedExportsOptions, + tValid({ code: `import { destructured } from '${testFilePath( './no-unused-modules/file-destructured-1.js', )}'`, filename: testFilePath('./no-unused-modules/file-destructured-2.js'), - }), - test({ options: unusedExportsOptions, + }), + tValid({ code: `export const { destructured } = {};`, filename: testFilePath('./no-unused-modules/file-destructured-1.js'), + options: unusedExportsOptions, }), ], invalid: [ - test({ - options: unusedExportsOptions, + tInvalid({ code: `export const { destructured2 } = {};`, filename: testFilePath('./no-unused-modules/file-destructured-1.js'), - errors: [ - `exported declaration 'destructured2' not used within other modules`, - ], + options: unusedExportsOptions, + errors: [createUnusedError('destructured2')], }), ], }) @@ -1058,17 +1017,17 @@ describe('test behavior for new file', () => { }) ruleTester.run('no-unused-modules', rule, { valid: [ - test({ - options: unusedExportsOptions, + tValid({ code: `import * as added from '${testFilePath( './no-unused-modules/file-added-4.js.js', )}'`, filename: testFilePath('./no-unused-modules/file-added-1.js'), - }), - test({ options: unusedExportsOptions, + }), + tValid({ code: `export const added = () => {}; export default () => {}`, filename: testFilePath('./no-unused-modules/file-added-4.js.js'), + options: unusedExportsOptions, }), ], invalid: [], @@ -1083,7 +1042,9 @@ describe('test behavior for new file', () => { describe('do not report missing export for ignored file', () => { ruleTester.run('no-unused-modules', rule, { valid: [ - test({ + tValid({ + code: 'export const test = true', + filename: testFilePath('./no-unused-modules/file-ignored-a.js'), options: [ { src: [testFilePath('./no-unused-modules/**/*.js')], @@ -1091,8 +1052,6 @@ describe('do not report missing export for ignored file', () => { missingExports: true, }, ], - code: 'export const test = true', - filename: testFilePath('./no-unused-modules/file-ignored-a.js'), }), ], invalid: [], @@ -1102,10 +1061,10 @@ describe('do not report missing export for ignored file', () => { // lint file not available in `src` ruleTester.run('no-unused-modules', rule, { valid: [ - test({ - options: unusedExportsOptions, + tValid({ code: `export const jsxFoo = 'foo'; export const jsxBar = 'bar'`, filename: testFilePath('../jsx/named.jsx'), + options: unusedExportsOptions, }), ], invalid: [], @@ -1114,42 +1073,38 @@ ruleTester.run('no-unused-modules', rule, { describe('do not report unused export for files mentioned in package.json', () => { ruleTester.run('no-unused-modules', rule, { valid: [ - test({ - options: unusedExportsOptions, + tValid({ code: 'export const bin = "bin"', filename: testFilePath('./no-unused-modules/bin.js'), - }), - test({ options: unusedExportsOptions, + }), + tValid({ code: 'export const binObject = "binObject"', filename: testFilePath('./no-unused-modules/binObject/index.js'), - }), - test({ options: unusedExportsOptions, + }), + tValid({ code: 'export const browser = "browser"', filename: testFilePath('./no-unused-modules/browser.js'), - }), - test({ options: unusedExportsOptions, + }), + tValid({ code: 'export const browserObject = "browserObject"', filename: testFilePath('./no-unused-modules/browserObject/index.js'), - }), - test({ options: unusedExportsOptions, + }), + tValid({ code: 'export const main = "main"', filename: testFilePath('./no-unused-modules/main/index.js'), + options: unusedExportsOptions, }), ], invalid: [ - test({ - options: unusedExportsOptions, + tInvalid({ code: 'export const privatePkg = "privatePkg"', filename: testFilePath('./no-unused-modules/privatePkg/index.js'), - errors: [ - error( - `exported declaration 'privatePkg' not used within other modules`, - ), - ], + options: unusedExportsOptions, + errors: [createUnusedError('privatePkg')], }), ], }) @@ -1158,10 +1113,10 @@ describe('do not report unused export for files mentioned in package.json', () = describe('Avoid errors if re-export all from umd compiled library', () => { ruleTester.run('no-unused-modules', rule, { valid: [ - test({ - options: unusedExportsOptions, + tValid({ code: `export * from '${testFilePath('./no-unused-modules/bin.js')}'`, filename: testFilePath('./no-unused-modules/main/index.js'), + options: unusedExportsOptions, }), ], invalid: [], @@ -1171,8 +1126,7 @@ describe('Avoid errors if re-export all from umd compiled library', () => { describe('TypeScript', () => { typescriptRuleTester.run('no-unused-modules', rule, { valid: [ - test({ - options: unusedExportsTypescriptOptions, + tValid({ code: ` import {b} from './file-ts-b'; import {c} from './file-ts-c'; @@ -1183,35 +1137,30 @@ describe('TypeScript', () => { const a2: c = {}; const a3: d = {}; `, - filename: testFilePath('./no-unused-modules/typescript/file-ts-a.ts'), - }), - test({ options: unusedExportsTypescriptOptions, + }), + tValid({ code: `export const b = 2;`, - filename: testFilePath('./no-unused-modules/typescript/file-ts-b.ts'), - }), - test({ options: unusedExportsTypescriptOptions, + }), + tValid({ code: `export interface c {};`, - filename: testFilePath('./no-unused-modules/typescript/file-ts-c.ts'), - }), - test({ options: unusedExportsTypescriptOptions, + }), + tValid({ code: `export type d = {};`, - filename: testFilePath('./no-unused-modules/typescript/file-ts-d.ts'), - }), - test({ options: unusedExportsTypescriptOptions, + }), + tValid({ code: `export enum e { f };`, - + options: unusedExportsTypescriptOptions, filename: testFilePath('./no-unused-modules/typescript/file-ts-e.ts'), }), - test({ - options: unusedExportsTypescriptOptions, + tValid({ code: ` import type {b} from './file-ts-b-used-as-type'; import type {c} from './file-ts-c-used-as-type'; @@ -1223,117 +1172,97 @@ describe('TypeScript', () => { const a3: d = {}; const a4: typeof e = undefined; `, - filename: testFilePath( './no-unused-modules/typescript/file-ts-a-import-type.ts', ), - }), - test({ options: unusedExportsTypescriptOptions, + }), + tValid({ code: `export const b = 2;`, - filename: testFilePath( './no-unused-modules/typescript/file-ts-b-used-as-type.ts', ), - }), - test({ options: unusedExportsTypescriptOptions, + }), + tValid({ code: `export interface c {};`, - filename: testFilePath( './no-unused-modules/typescript/file-ts-c-used-as-type.ts', ), - }), - test({ options: unusedExportsTypescriptOptions, + }), + tValid({ code: `export type d = {};`, - filename: testFilePath( './no-unused-modules/typescript/file-ts-d-used-as-type.ts', ), - }), - test({ options: unusedExportsTypescriptOptions, + }), + tValid({ code: `export enum e { f };`, - filename: testFilePath( './no-unused-modules/typescript/file-ts-e-used-as-type.ts', ), + options: unusedExportsTypescriptOptions, }), // Should also be valid when the exporting files are linted before the importing ones - test({ - options: unusedExportsTypescriptOptions, + tValid({ code: `export interface g {}`, - filename: testFilePath('./no-unused-modules/typescript/file-ts-g.ts'), - }), - test({ options: unusedExportsTypescriptOptions, + }), + tValid({ code: `import {g} from './file-ts-g';`, - filename: testFilePath('./no-unused-modules/typescript/file-ts-f.ts'), - }), - test({ options: unusedExportsTypescriptOptions, + }), + tValid({ code: `export interface g {}; /* used-as-type */`, - filename: testFilePath( './no-unused-modules/typescript/file-ts-g-used-as-type.ts', ), - }), - test({ options: unusedExportsTypescriptOptions, + }), + tValid({ code: `import type {g} from './file-ts-g';`, - filename: testFilePath( './no-unused-modules/typescript/file-ts-f-import-type.ts', ), + options: unusedExportsTypescriptOptions, }), ], invalid: [ - test({ - options: unusedExportsTypescriptOptions, + tInvalid({ code: `export const b = 2;`, - filename: testFilePath( './no-unused-modules/typescript/file-ts-b-unused.ts', ), - errors: [ - error(`exported declaration 'b' not used within other modules`), - ], - }), - test({ options: unusedExportsTypescriptOptions, + errors: [createUnusedError('b')], + }), + tInvalid({ code: `export interface c {};`, - filename: testFilePath( './no-unused-modules/typescript/file-ts-c-unused.ts', ), - errors: [ - error(`exported declaration 'c' not used within other modules`), - ], - }), - test({ options: unusedExportsTypescriptOptions, + errors: [createUnusedError('c')], + }), + tInvalid({ code: `export type d = {};`, - filename: testFilePath( './no-unused-modules/typescript/file-ts-d-unused.ts', ), - errors: [ - error(`exported declaration 'd' not used within other modules`), - ], - }), - test({ options: unusedExportsTypescriptOptions, + errors: [createUnusedError('d')], + }), + tInvalid({ code: `export enum e { f };`, - filename: testFilePath( './no-unused-modules/typescript/file-ts-e-unused.ts', ), - errors: [ - error(`exported declaration 'e' not used within other modules`), - ], + options: unusedExportsTypescriptOptions, + errors: [createUnusedError('e')], }), ], }) @@ -1343,54 +1272,48 @@ describe('ignoreUnusedTypeExports', () => { typescriptRuleTester.run('no-unused-modules', rule, { valid: [ // unused vars should not report - test({ - options: unusedExportsTypescriptIgnoreUnusedTypesOptions, + tValid({ code: `export interface c {};`, - filename: testFilePath( './no-unused-modules/typescript/file-ts-c-unused.ts', ), - }), - test({ options: unusedExportsTypescriptIgnoreUnusedTypesOptions, + }), + tValid({ code: `export type d = {};`, - filename: testFilePath( './no-unused-modules/typescript/file-ts-d-unused.ts', ), - }), - test({ options: unusedExportsTypescriptIgnoreUnusedTypesOptions, + }), + tValid({ code: `export enum e { f };`, - filename: testFilePath( './no-unused-modules/typescript/file-ts-e-unused.ts', ), + options: unusedExportsTypescriptIgnoreUnusedTypesOptions, }), // used vars should not report - test({ - options: unusedExportsTypescriptIgnoreUnusedTypesOptions, + tValid({ code: `export interface c {};`, - filename: testFilePath( './no-unused-modules/typescript/file-ts-c-used-as-type.ts', ), - }), - test({ options: unusedExportsTypescriptIgnoreUnusedTypesOptions, + }), + tValid({ code: `export type d = {};`, - filename: testFilePath( './no-unused-modules/typescript/file-ts-d-used-as-type.ts', ), - }), - test({ options: unusedExportsTypescriptIgnoreUnusedTypesOptions, + }), + tValid({ code: `export enum e { f };`, - filename: testFilePath( './no-unused-modules/typescript/file-ts-e-used-as-type.ts', ), + options: unusedExportsTypescriptIgnoreUnusedTypesOptions, }), ], invalid: [], @@ -1400,22 +1323,20 @@ describe('ignoreUnusedTypeExports', () => { describe('correctly work with JSX only files', () => { jsxRuleTester.run('no-unused-modules', rule, { valid: [ - test({ - options: unusedExportsJsxOptions, + tValid({ code: 'import a from "file-jsx-a";', - languageOptions: { parser: require(parsers.BABEL) }, filename: testFilePath('./no-unused-modules/jsx/file-jsx-a.jsx'), + languageOptions: { parser: require(parsers.BABEL) }, + options: unusedExportsJsxOptions, }), ], invalid: [ - test({ - options: unusedExportsJsxOptions, + tInvalid({ code: `export const b = 2;`, - languageOptions: { parser: require(parsers.BABEL) }, filename: testFilePath('./no-unused-modules/jsx/file-jsx-b.jsx'), - errors: [ - error(`exported declaration 'b' not used within other modules`), - ], + languageOptions: { parser: require(parsers.BABEL) }, + options: unusedExportsJsxOptions, + errors: [createUnusedError('b')], }), ], }) @@ -1424,44 +1345,44 @@ describe('correctly work with JSX only files', () => { describe('ignore flow types', () => { ruleTester.run('no-unused-modules', rule, { valid: [ - test({ - options: unusedExportsOptions, + tValid({ code: 'import { type FooType, type FooInterface } from "./flow-2";', - languageOptions: { parser: require(parsers.BABEL) }, filename: testFilePath('./no-unused-modules/flow/flow-0.js'), - }), - test({ + languageOptions: { parser: require(parsers.BABEL) }, options: unusedExportsOptions, + }), + tValid({ code: `// @flow strict - export type FooType = string; - export interface FooInterface {}; - `, - languageOptions: { parser: require(parsers.BABEL) }, + export type FooType = string; + export interface FooInterface {}; + `, filename: testFilePath('./no-unused-modules/flow/flow-2.js'), - }), - test({ + languageOptions: { parser: require(parsers.BABEL) }, options: unusedExportsOptions, + }), + tValid({ code: 'import type { FooType, FooInterface } from "./flow-4";', - languageOptions: { parser: require(parsers.BABEL) }, filename: testFilePath('./no-unused-modules/flow/flow-3.js'), - }), - test({ + languageOptions: { parser: require(parsers.BABEL) }, options: unusedExportsOptions, + }), + tValid({ code: `// @flow strict - export type FooType = string; - export interface FooInterface {}; - `, - languageOptions: { parser: require(parsers.BABEL) }, + export type FooType = string; + export interface FooInterface {}; + `, filename: testFilePath('./no-unused-modules/flow/flow-4.js'), - }), - test({ + languageOptions: { parser: require(parsers.BABEL) }, options: unusedExportsOptions, + }), + tValid({ code: `// @flow strict - export type Bar = number; - export interface BarInterface {}; - `, - languageOptions: { parser: require(parsers.BABEL) }, + export type Bar = number; + export interface BarInterface {}; + `, filename: testFilePath('./no-unused-modules/flow/flow-1.js'), + languageOptions: { parser: require(parsers.BABEL) }, + options: unusedExportsOptions, }), ], invalid: [], @@ -1471,17 +1392,17 @@ describe('ignore flow types', () => { describe('support (nested) destructuring assignment', () => { ruleTester.run('no-unused-modules', rule, { valid: [ - test({ - options: unusedExportsOptions, + tValid({ code: 'import {a, b} from "./destructuring-b";', - languageOptions: { parser: require(parsers.BABEL) }, filename: testFilePath('./no-unused-modules/destructuring-a.js'), - }), - test({ + languageOptions: { parser: require(parsers.BABEL) }, options: unusedExportsOptions, + }), + tValid({ code: 'const obj = {a: 1, dummy: {b: 2}}; export const {a, dummy: {b}} = obj;', - languageOptions: { parser: require(parsers.BABEL) }, filename: testFilePath('./no-unused-modules/destructuring-b.js'), + languageOptions: { parser: require(parsers.BABEL) }, + options: unusedExportsOptions, }), ], invalid: [], @@ -1491,43 +1412,41 @@ describe('support (nested) destructuring assignment', () => { describe('support ES2022 Arbitrary module namespace identifier names', () => { ruleTester.run('no-unused-module', rule, { valid: [ - test({ - options: unusedExportsOptions, + tValid({ code: `import { "foo" as foo } from "./arbitrary-module-namespace-identifier-name-a"`, - languageOptions: { - parser: require(parsers.ESPREE), - parserOptions: { ecmaVersion: 2022 }, - }, filename: testFilePath( './no-unused-modules/arbitrary-module-namespace-identifier-name-b.js', ), - }), - test({ - options: unusedExportsOptions, - code: 'const foo = 333;\nexport { foo as "foo" }', languageOptions: { parser: require(parsers.ESPREE), parserOptions: { ecmaVersion: 2022 }, }, + options: unusedExportsOptions, + }), + tValid({ + code: 'const foo = 333;\nexport { foo as "foo" }', filename: testFilePath( './no-unused-modules/arbitrary-module-namespace-identifier-name-a.js', ), + languageOptions: { + parser: require(parsers.ESPREE), + parserOptions: { ecmaVersion: 2022 }, + }, + options: unusedExportsOptions, }), ], invalid: [ - test({ - options: unusedExportsOptions, + tInvalid({ code: 'const foo = 333\nexport { foo as "foo" }', + filename: testFilePath( + './no-unused-modules/arbitrary-module-namespace-identifier-name-c.js', + ), languageOptions: { parser: require(parsers.BABEL), parserOptions: { ecmaVersion: 2022 }, }, - filename: testFilePath( - './no-unused-modules/arbitrary-module-namespace-identifier-name-c.js', - ), - errors: [ - error(`exported declaration 'foo' not used within other modules`), - ], + options: unusedExportsOptions, + errors: [createUnusedError('foo')], }), ], }) @@ -1537,15 +1456,15 @@ describe('parser ignores prefixes like BOM and hashbang', () => { // bom, hashbang ruleTester.run('no-unused-modules', rule, { valid: [ - test({ - options: unusedExportsOptions, + tValid({ code: 'export const foo = 1;\n', filename: testFilePath('./no-unused-modules/prefix-child.js'), - }), - test({ options: unusedExportsOptions, + }), + tValid({ code: `\uFEFF#!/usr/bin/env node\nimport {foo} from './prefix-child.js';\n`, filename: testFilePath('./no-unused-modules/prefix-parent-bom.js'), + options: unusedExportsOptions, }), ], invalid: [], @@ -1553,15 +1472,15 @@ describe('parser ignores prefixes like BOM and hashbang', () => { // no bom, hashbang ruleTester.run('no-unused-modules', rule, { valid: [ - test({ - options: unusedExportsOptions, + tValid({ code: 'export const foo = 1;\n', filename: testFilePath('./no-unused-modules/prefix-child.js'), - }), - test({ options: unusedExportsOptions, + }), + tValid({ code: `#!/usr/bin/env node\nimport {foo} from './prefix-child.js';\n`, filename: testFilePath('./no-unused-modules/prefix-parent-hashbang.js'), + options: unusedExportsOptions, }), ], invalid: [], @@ -1569,17 +1488,17 @@ describe('parser ignores prefixes like BOM and hashbang', () => { // bom, no hashbang ruleTester.run('no-unused-modules', rule, { valid: [ - test({ - options: unusedExportsOptions, + tValid({ code: 'export const foo = 1;\n', filename: testFilePath('./no-unused-modules/prefix-child.js'), - }), - test({ options: unusedExportsOptions, + }), + tValid({ code: `\uFEFF#!/usr/bin/env node\nimport {foo} from './prefix-child.js';\n`, filename: testFilePath( './no-unused-modules/prefix-parent-bomhashbang.js', ), + options: unusedExportsOptions, }), ], invalid: [], @@ -1587,15 +1506,15 @@ describe('parser ignores prefixes like BOM and hashbang', () => { // no bom, no hashbang ruleTester.run('no-unused-modules', rule, { valid: [ - test({ - options: unusedExportsOptions, + tValid({ code: 'export const foo = 1;\n', filename: testFilePath('./no-unused-modules/prefix-child.js'), - }), - test({ options: unusedExportsOptions, + }), + tValid({ code: `import {foo} from './prefix-child.js';\n`, filename: testFilePath('./no-unused-modules/prefix-parent.js'), + options: unusedExportsOptions, }), ], invalid: [], From dc6276c670ea56bba989893b7f3f786f7089fae9 Mon Sep 17 00:00:00 2001 From: Marco Pasqualetti Date: Thu, 21 Nov 2024 00:02:04 +0100 Subject: [PATCH 43/49] test(order): migrate to `createRuleTestCaseFunctions` --- test/rules/order.spec.ts | 1032 +++++++++++++------------------- test/rules/unambiguous.spec.ts | 6 +- 2 files changed, 417 insertions(+), 621 deletions(-) diff --git a/test/rules/order.spec.ts b/test/rules/order.spec.ts index 3777f54e7..6495c2018 100644 --- a/test/rules/order.spec.ts +++ b/test/rules/order.spec.ts @@ -1,8 +1,13 @@ import { RuleTester as TSESLintRuleTester } from '@typescript-eslint/rule-tester' -import eslintPkg from 'eslint/package.json' -import semver from 'semver' +import type { TestCaseError as TSESLintTestCaseError } from '@typescript-eslint/rule-tester' -import { test, parsers, getNonDefaultParsers, testFilePath } from '../utils' +import { + createRuleTestCaseFunctions, + parsers, + getNonDefaultParsers, + testFilePath, +} from '../utils' +import type { GetRuleModuleMessageIds } from '../utils' import rule from 'eslint-plugin-import-x/rules/order' @@ -22,10 +27,25 @@ const flowRuleTester = new TSESLintRuleTester({ }, }) +const { tValid, tInvalid } = createRuleTestCaseFunctions() + +function createOrderError( + data: [string, 'before' | 'after', string], +): TSESLintTestCaseError> { + const [secondImport, order, firstImport] = data + return { messageId: 'order', data: { secondImport, order, firstImport } } +} + +function createGenericError( + error: string, +): TSESLintTestCaseError> { + return { messageId: 'error', data: { error } } +} + ruleTester.run('order', rule, { valid: [ // Default order using require - test({ + tValid({ code: ` var fs = require('fs'); var async = require('async'); @@ -37,7 +57,7 @@ ruleTester.run('order', rule, { var index = require('./');`, }), // Default order using import - test({ + tValid({ code: ` import fs from 'fs'; import async, {foo1} from 'async'; @@ -48,7 +68,7 @@ ruleTester.run('order', rule, { import index from './';`, }), // Multiple module of the same rank next to each other - test({ + tValid({ code: ` var fs = require('fs'); var fs = require('fs'); @@ -57,7 +77,7 @@ ruleTester.run('order', rule, { var async = require('async');`, }), // Overriding order to be the reverse of the default order - test({ + tValid({ code: ` var index = require('./'); var sibling = require('./foo'); @@ -72,7 +92,7 @@ ruleTester.run('order', rule, { ], }), // Ignore dynamic requires - test({ + tValid({ code: ` var path = require('path'); var _ = require('lodash'); @@ -80,14 +100,14 @@ ruleTester.run('order', rule, { var fs = require('f' + 's');`, }), // Ignore non-require call expressions - test({ + tValid({ code: ` var path = require('path'); var result = add(1, 2); var _ = require('lodash');`, }), // Ignore requires that are not at the top-level #1 - test({ + tValid({ code: ` var index = require('./'); function foo() { @@ -99,7 +119,7 @@ ruleTester.run('order', rule, { }`, }), // Ignore requires that are not at the top-level #2 - test({ + tValid({ code: ` const foo = [ require('./foo'), @@ -107,11 +127,11 @@ ruleTester.run('order', rule, { ]`, }), // Ignore requires in template literal (#1936) - test({ + tValid({ code: "const foo = `${require('./a')} ${require('fs')}`", }), // Ignore unknown/invalid cases - test({ + tValid({ code: ` var unknown1 = require('/unknown1'); var fs = require('fs'); @@ -131,7 +151,7 @@ ruleTester.run('order', rule, { `, }), // Ignoring unassigned values by default (require) - test({ + tValid({ code: ` require('./foo'); require('fs'); @@ -139,7 +159,7 @@ ruleTester.run('order', rule, { `, }), // Ignoring unassigned values by default (import) - test({ + tValid({ code: ` import './foo'; import 'fs'; @@ -147,7 +167,7 @@ ruleTester.run('order', rule, { `, }), // No imports - test({ + tValid({ code: ` function add(a, b) { return a + b; @@ -156,7 +176,7 @@ ruleTester.run('order', rule, { `, }), // Grouping import types - test({ + tValid({ code: ` var fs = require('fs'); var index = require('./'); @@ -177,7 +197,7 @@ ruleTester.run('order', rule, { ], }), // Omitted types should implicitly be considered as the last type - test({ + tValid({ code: ` var index = require('./'); var path = require('path'); @@ -193,7 +213,7 @@ ruleTester.run('order', rule, { ], }), // Mixing require and import should have import up top - test({ + tValid({ code: ` import async, {foo1} from 'async'; import relParent2, {foo2} from '../foo/bar'; @@ -205,7 +225,7 @@ ruleTester.run('order', rule, { `, }), // Export equals expressions should be on top alongside with ordinary import-statements. - test({ + tValid({ code: ` import async, {foo1} from 'async'; import relParent2, {foo2} from '../foo/bar'; @@ -218,13 +238,13 @@ ruleTester.run('order', rule, { `, }), - test({ + tValid({ code: ` export import CreateSomething = _CreateSomething; `, }), // Adding unknown import types (e.g. using a resolver alias via babel) to the groups. - test({ + tValid({ code: ` import fs from 'fs'; import { Input } from '-/components/Input'; @@ -245,7 +265,7 @@ ruleTester.run('order', rule, { }), // Using unknown import types (e.g. using a resolver alias via babel) with // an alternative custom group list. - test({ + tValid({ code: ` import { Input } from '-/components/Input'; import { Button } from '-/components/Button'; @@ -266,7 +286,7 @@ ruleTester.run('order', rule, { }), // Using unknown import types (e.g. using a resolver alias via babel) // Option: newlines-between: 'always' - test({ + tValid({ code: ` import fs from 'fs'; @@ -296,7 +316,7 @@ ruleTester.run('order', rule, { }), // Using pathGroups to customize ordering, position 'after' - test({ + tValid({ code: ` import fs from 'fs'; import _ from 'lodash'; @@ -313,7 +333,7 @@ ruleTester.run('order', rule, { ], }), // pathGroup without position means "equal" with group - test({ + tValid({ code: ` import fs from 'fs'; import { Input } from '~/components/Input'; @@ -331,7 +351,7 @@ ruleTester.run('order', rule, { ], }), // Using pathGroups to customize ordering, position 'before' - test({ + tValid({ code: ` import fs from 'fs'; @@ -353,7 +373,7 @@ ruleTester.run('order', rule, { ], }), // Using pathGroups to customize ordering, with patternOptions - test({ + tValid({ code: ` import fs from 'fs'; @@ -381,7 +401,7 @@ ruleTester.run('order', rule, { }), // Using pathGroups to customize ordering for imports that are recognized as 'external' // by setting pathGroupsExcludedImportTypes without 'external' - test({ + tValid({ code: ` import fs from 'fs'; @@ -404,7 +424,7 @@ ruleTester.run('order', rule, { ], }), // Using pathGroups (a test case for https://github.com/import-js/eslint-plugin-import-x/pull/1724) - test({ + tValid({ code: ` import fs from 'fs'; import external from 'external'; @@ -427,7 +447,7 @@ ruleTester.run('order', rule, { ], }), // Monorepo setup, using Webpack resolver, workspace folder name in external-module-folders - test({ + tValid({ code: ` import _ from 'lodash'; import m from '@test-scope/some-module'; @@ -448,7 +468,7 @@ ruleTester.run('order', rule, { }, }), // Monorepo setup, using Node resolver (doesn't resolve symlinks) - test({ + tValid({ code: ` import _ from 'lodash'; import m from '@test-scope/some-module'; @@ -469,7 +489,7 @@ ruleTester.run('order', rule, { }, }), // Option: newlines-between: 'always' - test({ + tValid({ code: ` var fs = require('fs'); var index = require('./'); @@ -492,7 +512,7 @@ ruleTester.run('order', rule, { ], }), // Option: newlines-between: 'never' - test({ + tValid({ code: ` var fs = require('fs'); var index = require('./'); @@ -510,7 +530,7 @@ ruleTester.run('order', rule, { ], }), // Option: newlines-between: 'ignore' - test({ + tValid({ code: ` var fs = require('fs'); @@ -532,7 +552,7 @@ ruleTester.run('order', rule, { ], }), // 'ignore' should be the default value for `newlines-between` - test({ + tValid({ code: ` var fs = require('fs'); @@ -554,7 +574,7 @@ ruleTester.run('order', rule, { ], }), // Option newlines-between: 'always' with multiline imports #1 - test({ + tValid({ code: ` import path from 'path'; @@ -570,7 +590,7 @@ ruleTester.run('order', rule, { options: [{ 'newlines-between': 'always' }], }), // Option newlines-between: 'always' with multiline imports #2 - test({ + tValid({ code: ` import path from 'path'; import net @@ -581,7 +601,7 @@ ruleTester.run('order', rule, { options: [{ 'newlines-between': 'always' }], }), // Option newlines-between: 'always' with multiline imports #3 - test({ + tValid({ code: ` import foo from '../../../../this/will/be/very/long/path/and/therefore/this/import-x/has/to/be/in/two/lines'; @@ -592,7 +612,7 @@ ruleTester.run('order', rule, { options: [{ 'newlines-between': 'always' }], }), // Option newlines-between: 'always' with not assigned import #1 - test({ + tValid({ code: ` import path from 'path'; @@ -604,7 +624,7 @@ ruleTester.run('order', rule, { options: [{ 'newlines-between': 'always' }], }), // Option newlines-between: 'never' with not assigned import #2 - test({ + tValid({ code: ` import path from 'path'; import 'loud-rejection'; @@ -614,7 +634,7 @@ ruleTester.run('order', rule, { options: [{ 'newlines-between': 'never' }], }), // Option newlines-between: 'always' with not assigned require #1 - test({ + tValid({ code: ` var path = require('path'); @@ -626,7 +646,7 @@ ruleTester.run('order', rule, { options: [{ 'newlines-between': 'always' }], }), // Option newlines-between: 'never' with not assigned require #2 - test({ + tValid({ code: ` var path = require('path'); require('loud-rejection'); @@ -636,7 +656,7 @@ ruleTester.run('order', rule, { options: [{ 'newlines-between': 'never' }], }), // Option newlines-between: 'never' should ignore nested require statement's #1 - test({ + tValid({ code: ` var some = require('asdas'); var config = { @@ -653,7 +673,7 @@ ruleTester.run('order', rule, { options: [{ 'newlines-between': 'never' }], }), // Option newlines-between: 'always' should ignore nested require statement's #2 - test({ + tValid({ code: ` var some = require('asdas'); var config = { @@ -669,7 +689,7 @@ ruleTester.run('order', rule, { options: [{ 'newlines-between': 'always' }], }), // Option: newlines-between: 'always-and-inside-groups' - test({ + tValid({ code: ` var fs = require('fs'); var path = require('path'); @@ -695,7 +715,7 @@ ruleTester.run('order', rule, { ], }), // Option alphabetize: {order: 'ignore'} - test({ + tValid({ code: ` import a from 'foo'; import b from 'bar'; @@ -710,7 +730,7 @@ ruleTester.run('order', rule, { ], }), // Option alphabetize: {order: 'asc'} - test({ + tValid({ code: ` import c from 'Bar'; import b from 'bar'; @@ -726,7 +746,7 @@ ruleTester.run('order', rule, { ], }), // Option alphabetize: {order: 'desc'} - test({ + tValid({ code: ` import a from 'foo'; import b from 'bar'; @@ -742,7 +762,7 @@ ruleTester.run('order', rule, { ], }), // Option alphabetize: {order: 'asc'} and move nested import entries closer to the main import entry - test({ + tValid({ code: ` import a from "foo"; import c from "foo/bar"; @@ -752,7 +772,7 @@ ruleTester.run('order', rule, { options: [{ alphabetize: { order: 'asc' } }], }), // Option alphabetize: {order: 'asc'} and move nested import entries closer to the main import entry - test({ + tValid({ code: ` import a from "foo"; import c from "foo/foobar/bar"; @@ -762,7 +782,7 @@ ruleTester.run('order', rule, { options: [{ alphabetize: { order: 'asc' } }], }), // Option alphabetize: {order: 'desc'} and move nested import entries closer to the main import entry - test({ + tValid({ code: ` import b from "foo-bar"; import d from "foo/barfoo"; @@ -772,7 +792,7 @@ ruleTester.run('order', rule, { options: [{ alphabetize: { order: 'desc' } }], }), // Option alphabetize: {order: 'desc'} and move nested import entries closer to the main import entry with file names having non-alphanumeric characters. - test({ + tValid({ code: ` import b from "foo-bar"; import c from "foo,bar"; @@ -785,7 +805,7 @@ ruleTester.run('order', rule, { ], }), // Option alphabetize with newlines-between: {order: 'asc', newlines-between: 'always'} - test({ + tValid({ code: ` import b from 'Bar'; import c from 'bar'; @@ -802,7 +822,7 @@ ruleTester.run('order', rule, { ], }), // Alphabetize with require - test({ + tValid({ code: ` import { hello } from './hello'; import { int } from './int'; @@ -818,7 +838,7 @@ ruleTester.run('order', rule, { ], }), // Order of imports with similar names - test({ + tValid({ code: ` import React from 'react'; import { BrowserRouter } from 'react-router-dom'; @@ -831,7 +851,7 @@ ruleTester.run('order', rule, { }, ], }), - test({ + tValid({ code: ` import { UserInputError } from 'apollo-server-express'; @@ -856,7 +876,7 @@ ruleTester.run('order', rule, { }, ], }), - test({ + tValid({ code: ` import { ReactElement, ReactNode } from 'react'; @@ -887,7 +907,7 @@ ruleTester.run('order', rule, { ], }), // Order of the `import ... = require(...)` syntax - test({ + tValid({ code: ` import blah = require('./blah'); import { hello } from './hello';`, @@ -901,7 +921,7 @@ ruleTester.run('order', rule, { ], }), // Order of object-imports - test({ + tValid({ code: ` import blah = require('./blah'); import log = console.log;`, @@ -915,7 +935,7 @@ ruleTester.run('order', rule, { ], }), // Object-imports should not be forced to be alphabetized - test({ + tValid({ code: ` import debug = console.debug; import log = console.log;`, @@ -928,7 +948,7 @@ ruleTester.run('order', rule, { }, ], }), - test({ + tValid({ code: ` import log = console.log; import debug = console.debug;`, @@ -941,7 +961,7 @@ ruleTester.run('order', rule, { }, ], }), - test({ + tValid({ code: ` import { a } from "./a"; export namespace SomeNamespace { @@ -957,7 +977,7 @@ ruleTester.run('order', rule, { ], }), // Using `@/*` to alias internal modules - test({ + tValid({ code: ` import fs from 'fs'; @@ -1002,7 +1022,7 @@ ruleTester.run('order', rule, { }, }), // Option pathGroup[].distinctGroup: 'true' does not prevent 'position' properties from affecting the visible grouping - test({ + tValid({ code: ` import A from 'a'; @@ -1031,7 +1051,7 @@ ruleTester.run('order', rule, { ], }), // Option pathGroup[].distinctGroup: 'false' should prevent 'position' properties from affecting the visible grouping - test({ + tValid({ code: ` import A from 'a'; import C from 'c'; @@ -1058,7 +1078,7 @@ ruleTester.run('order', rule, { ], }), // Option pathGroup[].distinctGroup: 'false' should prevent 'position' properties from affecting the visible grouping 2 - test({ + tValid({ code: ` import A from 'a'; @@ -1085,7 +1105,7 @@ ruleTester.run('order', rule, { ], }), // Option pathGroup[].distinctGroup: 'false' should prevent 'position' properties from affecting the visible grouping 3 - test({ + tValid({ code: ` import A from "baz"; import B from "Bar"; @@ -1157,7 +1177,7 @@ ruleTester.run('order', rule, { ], }), // orderImportKind option that is not used - test({ + tValid({ code: ` import B from './B'; import b from './b'; @@ -1175,7 +1195,7 @@ ruleTester.run('order', rule, { ], invalid: [ // builtin before external module (require) - test({ + tInvalid({ code: ` var async = require('async'); var fs = require('fs'); @@ -1185,14 +1205,12 @@ ruleTester.run('order', rule, { var async = require('async'); `, errors: [ - { - message: '`fs` import should occur before import of `async`', - }, + createOrderError(['`fs` import', 'before', 'import of `async`']), ], languageOptions: { parser: require(parsers.ESPREE) }, }), // fix order with spaces on the end of line - test({ + tInvalid({ code: ` var async = require('async'); var fs = require('fs');${' '} @@ -1202,14 +1220,12 @@ ruleTester.run('order', rule, { var async = require('async'); `, errors: [ - { - message: '`fs` import should occur before import of `async`', - }, + createOrderError(['`fs` import', 'before', 'import of `async`']), ], languageOptions: { parser: require(parsers.ESPREE) }, }), // fix order with comment on the end of line - test({ + tInvalid({ code: ` var async = require('async'); var fs = require('fs'); /* comment */ @@ -1219,14 +1235,12 @@ ruleTester.run('order', rule, { var async = require('async'); `, errors: [ - { - message: '`fs` import should occur before import of `async`', - }, + createOrderError(['`fs` import', 'before', 'import of `async`']), ], languageOptions: { parser: require(parsers.ESPREE) }, }), // fix order with comments at the end and start of line - test({ + tInvalid({ code: ` /* comment1 */ var async = require('async'); /* comment2 */ /* comment3 */ var fs = require('fs'); /* comment4 */ @@ -1236,13 +1250,11 @@ ruleTester.run('order', rule, { /* comment1 */ var async = require('async'); /* comment2 */ `, errors: [ - { - message: '`fs` import should occur before import of `async`', - }, + createOrderError(['`fs` import', 'before', 'import of `async`']), ], }), // fix order with few comments at the end and start of line - test({ + tInvalid({ code: ` /* comment0 */ /* comment1 */ var async = require('async'); /* comment2 */ /* comment3 */ var fs = require('fs'); /* comment4 */ @@ -1252,13 +1264,11 @@ ruleTester.run('order', rule, { /* comment0 */ /* comment1 */ var async = require('async'); /* comment2 */ `, errors: [ - { - message: '`fs` import should occur before import of `async`', - }, + createOrderError(['`fs` import', 'before', 'import of `async`']), ], }), // fix order with windows end of lines - test({ + tInvalid({ code: `/* comment0 */ /* comment1 */ var async = require('async'); /* comment2 */` + `\r\n` + @@ -1270,13 +1280,11 @@ ruleTester.run('order', rule, { `/* comment0 */ /* comment1 */ var async = require('async'); /* comment2 */` + `\r\n`, errors: [ - { - message: '`fs` import should occur before import of `async`', - }, + createOrderError(['`fs` import', 'before', 'import of `async`']), ], }), // fix order with multilines comments at the end and start of line - test({ + tInvalid({ code: ` /* multiline1 comment1 */ var async = require('async'); /* multiline2 @@ -1294,13 +1302,11 @@ ruleTester.run('order', rule, { comment3 */ `, errors: [ - { - message: '`fs` import should occur before import of `async`', - }, + createOrderError(['`fs` import', 'before', 'import of `async`']), ], }), // fix destructured commonjs import - test({ + tInvalid({ code: ` var {b} = require('async'); var {a} = require('fs'); @@ -1310,13 +1316,11 @@ ruleTester.run('order', rule, { var {b} = require('async'); `, errors: [ - { - message: '`fs` import should occur before import of `async`', - }, + createOrderError(['`fs` import', 'before', 'import of `async`']), ], }), // fix order of multiline import - test({ + tInvalid({ code: ` var async = require('async'); var fs = @@ -1328,13 +1332,11 @@ ruleTester.run('order', rule, { var async = require('async'); `, errors: [ - { - message: '`fs` import should occur before import of `async`', - }, + createOrderError(['`fs` import', 'before', 'import of `async`']), ], }), // fix order at the end of file - test({ + tInvalid({ code: ` var async = require('async'); var fs = require('fs');`, @@ -1343,14 +1345,12 @@ ruleTester.run('order', rule, { var fs = require('fs'); var async = require('async');` + '\n', errors: [ - { - message: '`fs` import should occur before import of `async`', - }, + createOrderError(['`fs` import', 'before', 'import of `async`']), ], languageOptions: { parser: require(parsers.ESPREE) }, }), // builtin before external module (import) - test({ + tInvalid({ code: ` import async from 'async'; import fs from 'fs'; @@ -1360,13 +1360,11 @@ ruleTester.run('order', rule, { import async from 'async'; `, errors: [ - { - message: '`fs` import should occur before import of `async`', - }, + createOrderError(['`fs` import', 'before', 'import of `async`']), ], }), // builtin before external module (mixed import and require) - test({ + tInvalid({ code: ` var async = require('async'); import fs from 'fs'; @@ -1376,13 +1374,11 @@ ruleTester.run('order', rule, { var async = require('async'); `, errors: [ - { - message: '`fs` import should occur before import of `async`', - }, + createOrderError(['`fs` import', 'before', 'import of `async`']), ], }), // external before parent - test({ + tInvalid({ code: ` var parent = require('../parent'); var async = require('async'); @@ -1392,13 +1388,11 @@ ruleTester.run('order', rule, { var parent = require('../parent'); `, errors: [ - { - message: '`async` import should occur before import of `../parent`', - }, + createOrderError(['`async` import', 'before', 'import of `../parent`']), ], }), // parent before sibling - test({ + tInvalid({ code: ` var sibling = require('./sibling'); var parent = require('../parent'); @@ -1408,14 +1402,15 @@ ruleTester.run('order', rule, { var sibling = require('./sibling'); `, errors: [ - { - message: - '`../parent` import should occur before import of `./sibling`', - }, + createOrderError([ + '`../parent` import', + 'before', + 'import of `./sibling`', + ]), ], }), // sibling before index - test({ + tInvalid({ code: ` var index = require('./'); var sibling = require('./sibling'); @@ -1425,13 +1420,11 @@ ruleTester.run('order', rule, { var index = require('./'); `, errors: [ - { - message: '`./sibling` import should occur before import of `./`', - }, + createOrderError(['`./sibling` import', 'before', 'import of `./`']), ], }), // Multiple errors - test({ + tInvalid({ code: ` var sibling = require('./sibling'); var async = require('async'); @@ -1450,16 +1443,12 @@ ruleTester.run('order', rule, { `, ], errors: [ - { - message: '`async` import should occur before import of `./sibling`', - }, - { - message: '`fs` import should occur before import of `./sibling`', - }, + createOrderError(['`async` import', 'before', 'import of `./sibling`']), + createOrderError(['`fs` import', 'before', 'import of `./sibling`']), ], }), // Uses 'after' wording if it creates less errors - test({ + tInvalid({ code: ` var index = require('./'); var fs = require('fs'); @@ -1476,14 +1465,10 @@ ruleTester.run('order', rule, { var bar = require('bar'); var index = require('./'); `, - errors: [ - { - message: '`./` import should occur after import of `bar`', - }, - ], + errors: [createOrderError(['`./` import', 'after', 'import of `bar`'])], }), // Overriding order to be the reverse of the default order - test({ + tInvalid({ code: ` var fs = require('fs'); var index = require('./'); @@ -1495,38 +1480,30 @@ ruleTester.run('order', rule, { options: [ { groups: ['index', 'sibling', 'parent', 'external', 'builtin'] }, ], - errors: [ - { - message: '`./` import should occur before import of `fs`', - }, - ], + errors: [createOrderError(['`./` import', 'before', 'import of `fs`'])], }), // member expression of require - test({ + tInvalid({ code: ` var foo = require('./foo').bar; var fs = require('fs'); `, errors: [ - { - message: '`fs` import should occur before import of `./foo`', - }, + createOrderError(['`fs` import', 'before', 'import of `./foo`']), ], }), // nested member expression of require - test({ + tInvalid({ code: ` var foo = require('./foo').bar.bar.bar; var fs = require('fs'); `, errors: [ - { - message: '`fs` import should occur before import of `./foo`', - }, + createOrderError(['`fs` import', 'before', 'import of `./foo`']), ], }), // fix near nested member expression of require with newlines - test({ + tInvalid({ code: ` var foo = require('./foo').bar .bar @@ -1534,13 +1511,11 @@ ruleTester.run('order', rule, { var fs = require('fs'); `, errors: [ - { - message: '`fs` import should occur before import of `./foo`', - }, + createOrderError(['`fs` import', 'before', 'import of `./foo`']), ], }), // fix nested member expression of require with newlines - test({ + tInvalid({ code: ` var foo = require('./foo'); var fs = require('fs').bar @@ -1548,13 +1523,11 @@ ruleTester.run('order', rule, { .bar; `, errors: [ - { - message: '`fs` import should occur before import of `./foo`', - }, + createOrderError(['`fs` import', 'before', 'import of `./foo`']), ], }), // Grouping import types - test({ + tInvalid({ code: ` var fs = require('fs'); var index = require('./'); @@ -1576,13 +1549,11 @@ ruleTester.run('order', rule, { }, ], errors: [ - { - message: '`path` import should occur before import of `./foo`', - }, + createOrderError(['`path` import', 'before', 'import of `./foo`']), ], }), // Omitted types should implicitly be considered as the last type - test({ + tInvalid({ code: ` var path = require('path'); var async = require('async'); @@ -1601,59 +1572,58 @@ ruleTester.run('order', rule, { }, ], errors: [ - { - message: '`async` import should occur before import of `path`', - }, + createOrderError(['`async` import', 'before', 'import of `path`']), ], }), // Setting the order for an unknown type // should make the rule trigger an error and do nothing else - test({ + tInvalid({ code: ` var async = require('async'); var index = require('./'); `, options: [ + // @ts-expect-error testing incorrect options { groups: ['index', ['sibling', 'parent', 'UNKNOWN', 'internal']] }, ], errors: [ - { - message: - 'Incorrect configuration of the rule: Unknown type `"UNKNOWN"`', - }, + createGenericError( + 'Incorrect configuration of the rule: Unknown type `"UNKNOWN"`', + ), ], }), // Type in an array can't be another array, too much nesting - test({ + tInvalid({ code: ` var async = require('async'); var index = require('./'); `, options: [ + // @ts-expect-error testing incorrect options { groups: ['index', ['sibling', 'parent', ['builtin'], 'internal']] }, ], errors: [ - { - message: - 'Incorrect configuration of the rule: Unknown type `["builtin"]`', - }, + createGenericError( + 'Incorrect configuration of the rule: Unknown type `["builtin"]`', + ), ], }), // No numbers - test({ + tInvalid({ code: ` var async = require('async'); var index = require('./'); `, + // @ts-expect-error testing incorrect options options: [{ groups: ['index', ['sibling', 'parent', 2, 'internal']] }], errors: [ - { - message: 'Incorrect configuration of the rule: Unknown type `2`', - }, + createGenericError( + 'Incorrect configuration of the rule: Unknown type `2`', + ), ], }), // Duplicate - test({ + tInvalid({ code: ` var async = require('async'); var index = require('./'); @@ -1662,14 +1632,13 @@ ruleTester.run('order', rule, { { groups: ['index', ['sibling', 'parent', 'parent', 'internal']] }, ], errors: [ - { - message: - 'Incorrect configuration of the rule: `parent` is duplicated', - }, + createGenericError( + 'Incorrect configuration of the rule: `parent` is duplicated', + ), ], }), // Mixing require and import should have import up top - test({ + tInvalid({ code: ` import async, {foo1} from 'async'; import relParent2, {foo2} from '../foo/bar'; @@ -1689,12 +1658,10 @@ ruleTester.run('order', rule, { var index = require('./'); `, errors: [ - { - message: '`./foo` import should occur before import of `fs`', - }, + createOrderError(['`./foo` import', 'before', 'import of `fs`']), ], }), - test({ + tInvalid({ code: ` var fs = require('fs'); import async, {foo1} from 'async'; @@ -1706,13 +1673,11 @@ ruleTester.run('order', rule, { var fs = require('fs'); `, errors: [ - { - message: '`fs` import should occur after import of `../foo/bar`', - }, + createOrderError(['`fs` import', 'after', 'import of `../foo/bar`']), ], }), // Order of the `import ... = require(...)` syntax - test({ + tInvalid({ code: ` var fs = require('fs'); import async, {foo1} from 'async'; @@ -1723,14 +1688,11 @@ ruleTester.run('order', rule, { import bar = require("../foo/bar"); var fs = require('fs'); `, - errors: [ - { - message: '`fs` import should occur after import of `../foo/bar`', - }, + createOrderError(['`fs` import', 'after', 'import of `../foo/bar`']), ], }), - test({ + tInvalid({ code: ` var async = require('async'); var fs = require('fs'); @@ -1739,15 +1701,12 @@ ruleTester.run('order', rule, { var fs = require('fs'); var async = require('async'); `, - + languageOptions: { parser: require(parsers.ESPREE) }, errors: [ - { - message: '`fs` import should occur before import of `async`', - }, + createOrderError(['`fs` import', 'before', 'import of `async`']), ], - languageOptions: { parser: require(parsers.ESPREE) }, }), - test({ + tInvalid({ code: ` import sync = require('sync'); import async, {foo1} from 'async'; @@ -1766,28 +1725,25 @@ ruleTester.run('order', rule, { alphabetize: { order: 'asc' }, }, ], - errors: [ - { - message: '`async` import should occur before import of `sync`', - }, + createOrderError(['`async` import', 'before', 'import of `sync`']), ], }), // Order of object-imports - test({ + tInvalid({ code: ` import log = console.log; import blah = require('./blah');`, - errors: [ - { - message: - '`./blah` import should occur before import of `console.log`', - }, + createOrderError([ + '`./blah` import', + 'before', + 'import of `console.log`', + ]), ], }), // Default order using import with custom import alias - test({ + tInvalid({ code: ` import { Button } from '-/components/Button'; import { add } from './helper'; @@ -1812,14 +1768,17 @@ ruleTester.run('order', rule, { ], errors: [ { + ...createOrderError([ + '`fs` import', + 'before', + 'import of `-/components/Button`', + ]), line: 4, - message: - '`fs` import should occur before import of `-/components/Button`', }, ], }), // Default order using import with custom import alias - test({ + tInvalid({ code: ` import fs from 'fs'; import { Button } from '-/components/Button'; @@ -1848,20 +1807,12 @@ ruleTester.run('order', rule, { }, ], errors: [ - { - line: 2, - message: - 'There should be at least one empty line between import groups', - }, - { - line: 4, - message: - 'There should be at least one empty line between import groups', - }, + { messageId: 'oneLineBetweenGroups', line: 2 }, + { messageId: 'oneLineBetweenGroups', line: 4 }, ], }), // Option newlines-between: 'never' - should report unnecessary line between groups - test({ + tInvalid({ code: ` var fs = require('fs'); var index = require('./'); @@ -1889,18 +1840,12 @@ ruleTester.run('order', rule, { }, ], errors: [ - { - line: 4, - message: 'There should be no empty line between import groups', - }, - { - line: 6, - message: 'There should be no empty line between import groups', - }, + { messageId: 'noLineBetweenGroups', line: 4 }, + { messageId: 'noLineBetweenGroups', line: 6 }, ], }), // Fix newlines-between with comments after - test({ + tInvalid({ code: ` var fs = require('fs'); /* comment */ @@ -1916,15 +1861,10 @@ ruleTester.run('order', rule, { 'newlines-between': 'never', }, ], - errors: [ - { - line: 2, - message: 'There should be no empty line between import groups', - }, - ], + errors: [{ messageId: 'noLineBetweenGroups', line: 2 }], }), // Cannot fix newlines-between with multiline comment after - test({ + tInvalid({ code: ` var fs = require('fs'); /* multiline comment */ @@ -1937,15 +1877,10 @@ ruleTester.run('order', rule, { 'newlines-between': 'never', }, ], - errors: [ - { - line: 2, - message: 'There should be no empty line between import groups', - }, - ], + errors: [{ messageId: 'noLineBetweenGroups', line: 2 }], }), // Option newlines-between: 'always' - should report lack of newline between groups - test({ + tInvalid({ code: ` var fs = require('fs'); var index = require('./'); @@ -1973,20 +1908,12 @@ ruleTester.run('order', rule, { }, ], errors: [ - { - line: 4, - message: - 'There should be at least one empty line between import groups', - }, - { - line: 5, - message: - 'There should be at least one empty line between import groups', - }, + { messageId: 'oneLineBetweenGroups', line: 4 }, + { messageId: 'oneLineBetweenGroups', line: 5 }, ], }), // Option newlines-between: 'always' should report unnecessary empty lines space between import groups - test({ + tInvalid({ code: ` var fs = require('fs'); @@ -2015,19 +1942,13 @@ ruleTester.run('order', rule, { }, ], errors: [ - { - line: 2, - message: 'There should be no empty line within import group', - }, - { - line: 7, - message: 'There should be no empty line within import group', - }, + { messageId: 'noLineWithinGroup', line: 2 }, + { messageId: 'noLineWithinGroup', line: 7 }, ], }), // Option newlines-between: 'never' with unassigned imports and warnOnUnassignedImports disabled // newline is preserved to match existing behavior - test({ + tInvalid({ code: ` import path from 'path'; import 'loud-rejection'; @@ -2038,15 +1959,10 @@ ruleTester.run('order', rule, { options: [ { 'newlines-between': 'never', warnOnUnassignedImports: false }, ], - errors: [ - { - line: 2, - message: 'There should be no empty line between import groups', - }, - ], + errors: [{ messageId: 'noLineBetweenGroups', line: 2 }], }), // Option newlines-between: 'never' with unassigned imports and warnOnUnassignedImports enabled - test({ + tInvalid({ code: ` import path from 'path'; import 'loud-rejection'; @@ -2061,15 +1977,10 @@ ruleTester.run('order', rule, { import _ from 'lodash'; `, options: [{ 'newlines-between': 'never', warnOnUnassignedImports: true }], - errors: [ - { - line: 3, - message: 'There should be no empty line between import groups', - }, - ], + errors: [{ messageId: 'noLineBetweenGroups', line: 3 }], }), // Option newlines-between: 'never' cannot fix if there are other statements between imports - test({ + tInvalid({ code: ` import path from 'path'; export const abc = 123; @@ -2078,15 +1989,10 @@ ruleTester.run('order', rule, { import _ from 'lodash'; `, options: [{ 'newlines-between': 'never' }], - errors: [ - { - line: 2, - message: 'There should be no empty line between import groups', - }, - ], + errors: [{ messageId: 'noLineBetweenGroups', line: 2 }], }), // Option newlines-between: 'always' should report missing empty lines when using not assigned imports - test({ + tInvalid({ code: ` import path from 'path'; import 'loud-rejection'; @@ -2101,16 +2007,10 @@ ruleTester.run('order', rule, { import _ from 'lodash'; `, options: [{ 'newlines-between': 'always' }], - errors: [ - { - line: 2, - message: - 'There should be at least one empty line between import groups', - }, - ], + errors: [{ messageId: 'oneLineBetweenGroups', line: 2 }], }), // fix missing empty lines with single line comment after - test({ + tInvalid({ code: ` import path from 'path'; // comment import _ from 'lodash'; @@ -2121,16 +2021,10 @@ ruleTester.run('order', rule, { import _ from 'lodash'; `, options: [{ 'newlines-between': 'always' }], - errors: [ - { - line: 2, - message: - 'There should be at least one empty line between import groups', - }, - ], + errors: [{ messageId: 'oneLineBetweenGroups', line: 2 }], }), // fix missing empty lines with few line block comment after - test({ + tInvalid({ code: ` import path from 'path'; /* comment */ /* comment */ import _ from 'lodash'; @@ -2141,16 +2035,10 @@ ruleTester.run('order', rule, { import _ from 'lodash'; `, options: [{ 'newlines-between': 'always' }], - errors: [ - { - line: 2, - message: - 'There should be at least one empty line between import groups', - }, - ], + errors: [{ messageId: 'oneLineBetweenGroups', line: 2 }], }), // fix missing empty lines with single line block comment after - test({ + tInvalid({ code: ` import path from 'path'; /* 1 2 */ @@ -2172,16 +2060,10 @@ ruleTester.run('order', rule, { `, ], options: [{ 'newlines-between': 'always' }], - errors: [ - { - line: 2, - message: - 'There should be at least one empty line between import groups', - }, - ], + errors: [{ messageId: 'oneLineBetweenGroups', line: 2 }], }), // reorder fix cannot cross function call on moving below #1 - test({ + tInvalid({ code: ` const local = require('./local'); @@ -2193,14 +2075,12 @@ ruleTester.run('order', rule, { fn_call(); `, errors: [ - { - message: '`./local` import should occur after import of `global2`', - }, + createOrderError(['`./local` import', 'after', 'import of `global2`']), ], languageOptions: { parser: require(parsers.ESPREE) }, }), // reorder fix cannot cross function call on moving below #2 - test({ + tInvalid({ code: ` const local = require('./local'); fn_call(); @@ -2210,14 +2090,12 @@ ruleTester.run('order', rule, { fn_call(); `, errors: [ - { - message: '`./local` import should occur after import of `global2`', - }, + createOrderError(['`./local` import', 'after', 'import of `global2`']), ], languageOptions: { parser: require(parsers.ESPREE) }, }), // reorder fix cannot cross function call on moving below #3 - test({ + tInvalid({ code: ` const local1 = require('./local1'); const local2 = require('./local2'); @@ -2232,14 +2110,14 @@ ruleTester.run('order', rule, { fn_call(); `, errors: [ - '`./local1` import should occur after import of `global5`', - '`./local2` import should occur after import of `global5`', - '`./local3` import should occur after import of `global5`', - '`./local4` import should occur after import of `global5`', + createOrderError(['`./local1` import', 'after', 'import of `global5`']), + createOrderError(['`./local2` import', 'after', 'import of `global5`']), + createOrderError(['`./local3` import', 'after', 'import of `global5`']), + createOrderError(['`./local4` import', 'after', 'import of `global5`']), ], }), // reorder fix cannot cross function call on moving below - test({ + tInvalid({ code: ` const local = require('./local'); const global1 = require('global1'); @@ -2250,15 +2128,13 @@ ruleTester.run('order', rule, { fn_call(); `, errors: [ - { - message: '`./local` import should occur after import of `global3`', - }, + createOrderError(['`./local` import', 'after', 'import of `global3`']), ], languageOptions: { parser: require(parsers.ESPREE) }, }), // reorder fix cannot cross function call on moving below // fix imports that not crosses function call only - test({ + tInvalid({ code: ` const local1 = require('./local1'); const global1 = require('global1'); @@ -2282,12 +2158,12 @@ ruleTester.run('order', rule, { fn_call(); `, errors: [ - '`./local1` import should occur after import of `global4`', - '`./local2` import should occur after import of `global4`', + createOrderError(['`./local1` import', 'after', 'import of `global4`']), + createOrderError(['`./local2` import', 'after', 'import of `global4`']), ], }), // pathGroup with position 'after' - test({ + tInvalid({ code: ` import fs from 'fs'; import _ from 'lodash'; @@ -2308,14 +2184,15 @@ ruleTester.run('order', rule, { }, ], errors: [ - { - message: - '`~/components/Input` import should occur before import of `./helper`', - }, + createOrderError([ + '`~/components/Input` import', + 'before', + 'import of `./helper`', + ]), ], }), // pathGroup without position - test({ + tInvalid({ code: ` import fs from 'fs'; import _ from 'lodash'; @@ -2336,13 +2213,11 @@ ruleTester.run('order', rule, { }, ], errors: [ - { - message: '`./helper` import should occur after import of `async`', - }, + createOrderError(['`./helper` import', 'after', 'import of `async`']), ], }), // pathGroup with position 'before' - test({ + tInvalid({ code: ` import fs from 'fs'; import _ from 'lodash'; @@ -2363,14 +2238,15 @@ ruleTester.run('order', rule, { }, ], errors: [ - { - message: - '`~/components/Input` import should occur before import of `lodash`', - }, + createOrderError([ + '`~/components/Input` import', + 'before', + 'import of `lodash`', + ]), ], }), // multiple pathGroup with different positions for same group, fix for 'after' - test({ + tInvalid({ code: ` import fs from 'fs'; import { Import } from '$/components/Import'; @@ -2400,15 +2276,16 @@ ruleTester.run('order', rule, { }, ], errors: [ - { - message: - '`-/components/Export` import should occur before import of `$/components/Import`', - }, + createOrderError([ + '`-/components/Export` import', + 'before', + 'import of `$/components/Import`', + ]), ], }), // multiple pathGroup with different positions for same group, fix for 'before' - test({ + tInvalid({ code: ` import fs from 'fs'; import { Export } from '-/components/Export'; @@ -2438,15 +2315,16 @@ ruleTester.run('order', rule, { }, ], errors: [ - { - message: - '`~/components/Output` import should occur before import of `#/components/Input`', - }, + createOrderError([ + '`~/components/Output` import', + 'before', + 'import of `#/components/Input`', + ]), ], }), // pathGroups overflowing to previous/next groups - test({ + tInvalid({ code: ` import path from 'path'; import { namespace } from '@namespace'; @@ -2508,14 +2386,13 @@ ruleTester.run('order', rule, { settings: { 'import-x/internal-regex': '^(a|b|c|d|e|f|g|h|i|j|k)(\\/|$)', }, - errors: Array.from( - { length: 11 }, - () => 'There should be at least one empty line between import groups', - ), + errors: Array.from({ length: 11 }, () => ({ + messageId: 'oneLineBetweenGroups', + })), }), // rankings that overflow to double-digit ranks - test({ + tInvalid({ code: ` import external from 'external'; import a from '@namespace/a'; @@ -2554,31 +2431,30 @@ ruleTester.run('order', rule, { { pattern: '@namespace', group: 'external', position: 'after' }, { pattern: '@namespace/**', group: 'external', position: 'after' }, ], + // @ts-expect-error testing options pathGroupsExcludedImportTypes: ['@namespace'], }, ], errors: [ - 'There should be at least one empty line between import groups', - 'There should be at least one empty line between import groups', - 'There should be at least one empty line between import groups', + { messageId: 'oneLineBetweenGroups' }, + { messageId: 'oneLineBetweenGroups' }, + { messageId: 'oneLineBetweenGroups' }, ], }), // reorder fix cannot cross non import or require - test({ + tInvalid({ code: ` var async = require('async'); fn_call(); var fs = require('fs'); `, errors: [ - { - message: '`fs` import should occur before import of `async`', - }, + createOrderError(['`fs` import', 'before', 'import of `async`']), ], }), // reorder fix cannot cross function call on moving below (from #1252) - test({ + tInvalid({ code: ` const env = require('./config'); @@ -2590,116 +2466,98 @@ ruleTester.run('order', rule, { http.createServer(express()); `, errors: [ - { - message: '`./config` import should occur after import of `express`', - }, + createOrderError(['`./config` import', 'after', 'import of `express`']), ], }), // reorder cannot cross non plain requires - test({ + tInvalid({ code: ` var async = require('async'); var a = require('./value.js')(a); var fs = require('fs'); `, errors: [ - { - message: '`fs` import should occur before import of `async`', - }, + createOrderError(['`fs` import', 'before', 'import of `async`']), ], }), // reorder fixes cannot be applied to non plain requires #1 - test({ + tInvalid({ code: ` var async = require('async'); var fs = require('fs')(a); `, errors: [ - { - message: '`fs` import should occur before import of `async`', - }, + createOrderError(['`fs` import', 'before', 'import of `async`']), ], }), // reorder fixes cannot be applied to non plain requires #2 - test({ + tInvalid({ code: ` var async = require('async')(a); var fs = require('fs'); `, errors: [ - { - message: '`fs` import should occur before import of `async`', - }, + createOrderError(['`fs` import', 'before', 'import of `async`']), ], }), // cannot require in case of not assignment require - test({ + tInvalid({ code: ` var async = require('async'); require('./aa'); var fs = require('fs'); `, errors: [ - { - message: '`fs` import should occur before import of `async`', - }, + createOrderError(['`fs` import', 'before', 'import of `async`']), ], languageOptions: { parser: require(parsers.ESPREE) }, }), // reorder cannot cross function call (import statement) - test({ + tInvalid({ code: ` import async from 'async'; fn_call(); import fs from 'fs'; `, errors: [ - { - message: '`fs` import should occur before import of `async`', - }, + createOrderError(['`fs` import', 'before', 'import of `async`']), ], }), // reorder cannot cross variable assignment (import statement) - test({ + tInvalid({ code: ` import async from 'async'; var a = 1; import fs from 'fs'; `, errors: [ - { - message: '`fs` import should occur before import of `async`', - }, + createOrderError(['`fs` import', 'before', 'import of `async`']), ], }), // reorder cannot cross non plain requires (import statement) - test({ + tInvalid({ code: ` import async from 'async'; var a = require('./value.js')(a); import fs from 'fs'; `, errors: [ - { - message: '`fs` import should occur before import of `async`', - }, + createOrderError(['`fs` import', 'before', 'import of `async`']), ], }), // cannot reorder in case of not assignment import - test({ + tInvalid({ code: ` import async from 'async'; import './aa'; import fs from 'fs'; `, errors: [ - { - message: '`fs` import should occur before import of `async`', - }, + createOrderError(['`fs` import', 'before', 'import of `async`']), ], }), // Option alphabetize: {order: 'asc'} - test({ + tInvalid({ code: ` import b from 'bar'; import c from 'Bar'; @@ -2720,14 +2578,10 @@ ruleTester.run('order', rule, { alphabetize: { order: 'asc' }, }, ], - errors: [ - { - message: '`Bar` import should occur before import of `bar`', - }, - ], + errors: [createOrderError(['`Bar` import', 'before', 'import of `bar`'])], }), // Option alphabetize: {order: 'desc'} - test({ + tInvalid({ code: ` import a from 'foo'; import c from 'Bar'; @@ -2748,14 +2602,10 @@ ruleTester.run('order', rule, { alphabetize: { order: 'desc' }, }, ], - errors: [ - { - message: '`bar` import should occur before import of `Bar`', - }, - ], + errors: [createOrderError(['`bar` import', 'before', 'import of `Bar`'])], }), // Option alphabetize: {order: 'asc'} and move nested import entries closer to the main import entry - test({ + tInvalid({ code: ` import a from "foo"; import b from "foo-bar"; @@ -2774,13 +2624,15 @@ ruleTester.run('order', rule, { import b from "foo-bar"; `, errors: [ - { - message: '`foo-bar` import should occur after import of `foo/barfoo`', - }, + createOrderError([ + '`foo-bar` import', + 'after', + 'import of `foo/barfoo`', + ]), ], }), // Option alphabetize {order: 'asc': caseInsensitive: true} - test({ + tInvalid({ code: ` import b from 'foo'; import a from 'Bar'; @@ -2799,14 +2651,10 @@ ruleTester.run('order', rule, { alphabetize: { order: 'asc', caseInsensitive: true }, }, ], - errors: [ - { - message: '`Bar` import should occur before import of `foo`', - }, - ], + errors: [createOrderError(['`Bar` import', 'before', 'import of `foo`'])], }), // Option alphabetize {order: 'desc': caseInsensitive: true} - test({ + tInvalid({ code: ` import a from 'Bar'; import b from 'foo'; @@ -2825,14 +2673,10 @@ ruleTester.run('order', rule, { alphabetize: { order: 'desc', caseInsensitive: true }, }, ], - errors: [ - { - message: '`foo` import should occur before import of `Bar`', - }, - ], + errors: [createOrderError(['`foo` import', 'before', 'import of `Bar`'])], }), // Option alphabetize {order: 'asc'} and require with member expression - test({ + tInvalid({ code: ` const b = require('./b').get(); const a = require('./a'); @@ -2846,14 +2690,10 @@ ruleTester.run('order', rule, { alphabetize: { order: 'asc' }, }, ], - errors: [ - { - message: '`./a` import should occur before import of `./b`', - }, - ], + errors: [createOrderError(['`./a` import', 'before', 'import of `./b`'])], }), // Alphabetize with parent paths - test({ + tInvalid({ code: ` import a from '../a'; import p from '..'; @@ -2868,14 +2708,10 @@ ruleTester.run('order', rule, { alphabetize: { order: 'asc' }, }, ], - errors: [ - { - message: '`..` import should occur before import of `../a`', - }, - ], + errors: [createOrderError(['`..` import', 'before', 'import of `../a`'])], }), // Option pathGroup[].distinctGroup: 'false' should error when newlines are incorrect 2 - test({ + tInvalid({ code: ` import A from 'a'; import C from './c'; @@ -2892,15 +2728,10 @@ ruleTester.run('order', rule, { pathGroupsExcludedImportTypes: [], }, ], - errors: [ - { - message: - 'There should be at least one empty line between import groups', - }, - ], + errors: [{ messageId: 'oneLineBetweenGroups' }], }), // Option pathGroup[].distinctGroup: 'false' should error when newlines are incorrect 2 - test({ + tInvalid({ code: ` import A from 'a'; @@ -2929,14 +2760,10 @@ ruleTester.run('order', rule, { ], }, ], - errors: [ - { - message: 'There should be no empty line within import group', - }, - ], + errors: [{ messageId: 'noLineWithinGroup' }], }), // Alphabetize with require - test({ + tInvalid({ code: ` const { cello } = require('./cello'); import { int } from './int'; @@ -2958,12 +2785,8 @@ ruleTester.run('order', rule, { `, ], errors: [ - { - message: '`./int` import should occur before import of `./cello`', - }, - { - message: '`./hello` import should occur before import of `./cello`', - }, + createOrderError(['`./int` import', 'before', 'import of `./cello`']), + createOrderError(['`./hello` import', 'before', 'import of `./cello`']), ], }), ], @@ -2986,7 +2809,7 @@ describe('TypeScript', () => { // #1667: typescript type import support // Option alphabetize: {order: 'asc'} - test({ + tValid({ code: ` import c from 'Bar'; import type { C } from 'Bar'; @@ -3005,7 +2828,7 @@ describe('TypeScript', () => { ], }), // Option alphabetize: {order: 'desc'} - test({ + tValid({ code: ` import a from 'foo'; import type { A } from 'foo'; @@ -3024,7 +2847,7 @@ describe('TypeScript', () => { ], }), // Option alphabetize: {order: 'asc'} with type group - test({ + tValid({ code: ` import c from 'Bar'; import b from 'bar'; @@ -3044,7 +2867,7 @@ describe('TypeScript', () => { ], }), // Option alphabetize: {order: 'asc'} with type group & path group - test({ + tValid({ // only: true, code: ` import c from 'Bar'; @@ -3074,7 +2897,7 @@ describe('TypeScript', () => { ], }), // Option alphabetize: {order: 'asc'} with path group - test({ + tValid({ // only: true, code: ` import c from 'Bar'; @@ -3103,7 +2926,7 @@ describe('TypeScript', () => { ], }), // Option alphabetize: {order: 'desc'} with type group - test({ + tValid({ code: ` import a from 'foo'; import b from 'bar'; @@ -3122,7 +2945,7 @@ describe('TypeScript', () => { }, ], }), - test({ + tValid({ code: ` import { Partner } from '@models/partner/partner'; import { PartnerId } from '@models/partner/partner-id'; @@ -3134,7 +2957,7 @@ describe('TypeScript', () => { }, ], }), - test({ + tValid({ code: ` import { serialize, parse, mapFieldErrors } from '@vtaits/form-schema'; import type { GetFieldSchema } from '@vtaits/form-schema'; @@ -3151,7 +2974,7 @@ describe('TypeScript', () => { ], }), // Imports inside module declaration - test({ + tValid({ code: ` import type { CopyOptions } from 'fs'; import type { ParsedPath } from 'path'; @@ -3168,7 +2991,7 @@ describe('TypeScript', () => { }, ], }), - test({ + tValid({ code: ` import { useLazyQuery, useQuery } from "@apollo/client"; import { useEffect } from "react"; @@ -3200,7 +3023,7 @@ describe('TypeScript', () => { }, ], }), - test({ + tValid({ code: ` import express from 'express'; import log4js from 'log4js'; @@ -3227,7 +3050,7 @@ describe('TypeScript', () => { ], invalid: [ // Option alphabetize: {order: 'asc'} - test({ + tInvalid({ code: ` import b from 'bar'; import c from 'Bar'; @@ -3254,15 +3077,11 @@ describe('TypeScript', () => { }, ], errors: [ - { - message: semver.satisfies(eslintPkg.version, '< 3') - ? '`bar` import should occur after type import of `Bar`' - : /(`bar` import should occur after type import of `Bar`)|(`Bar` type import should occur before import of `bar`)/, - }, + createOrderError(['`bar` import', 'after', 'type import of `Bar`']), ], }), // Option alphabetize: {order: 'desc'} - test({ + tInvalid({ code: ` import a from 'foo'; import type { A } from 'foo'; @@ -3289,15 +3108,11 @@ describe('TypeScript', () => { }, ], errors: [ - { - message: semver.satisfies(eslintPkg.version, '< 3') - ? '`bar` import should occur before import of `Bar`' - : /(`bar` import should occur before import of `Bar`)|(`Bar` import should occur after import of `bar`)/, - }, + createOrderError(['`bar` import', 'before', 'import of `Bar`']), ], }), // Option alphabetize: {order: 'asc'} with type group - test({ + tInvalid({ code: ` import b from 'bar'; import c from 'Bar'; @@ -3325,29 +3140,17 @@ describe('TypeScript', () => { alphabetize: { order: 'asc' }, }, ], - errors: semver.satisfies(eslintPkg.version, '< 3') - ? [ - { - message: '`Bar` import should occur before import of `bar`', - }, - { - message: - '`Bar` type import should occur before type import of `foo`', - }, - ] - : [ - { - message: - /(`Bar` import should occur before import of `bar`)|(`bar` import should occur after import of `Bar`)/, - }, - { - message: - /(`Bar` type import should occur before type import of `foo`)|(`foo` type import should occur after type import of `Bar`)/, - }, - ], + errors: [ + createOrderError(['`Bar` import', 'before', 'import of `bar`']), + createOrderError([ + '`Bar` type import', + 'before', + 'type import of `foo`', + ]), + ], }), // Option alphabetize: {order: 'desc'} with type group - test({ + tInvalid({ code: ` import a from 'foo'; import c from 'Bar'; @@ -3375,29 +3178,17 @@ describe('TypeScript', () => { alphabetize: { order: 'desc' }, }, ], - errors: semver.satisfies(eslintPkg.version, '< 3') - ? [ - { - message: '`bar` import should occur before import of `Bar`', - }, - { - message: - '`foo` type import should occur before type import of `Bar`', - }, - ] - : [ - { - message: - /(`bar` import should occur before import of `Bar`)|(`Bar` import should occur after import of `bar`)/, - }, - { - message: - /(`foo` type import should occur before type import of `Bar`)|(`Bar` type import should occur after import of type `foo`)/, - }, - ], + errors: [ + createOrderError(['`bar` import', 'before', 'import of `Bar`']), + createOrderError([ + '`foo` type import', + 'before', + 'type import of `Bar`', + ]), + ], }), // warns for out of order unassigned imports (warnOnUnassignedImports enabled) - test({ + tInvalid({ code: ` import './local1'; import global from 'global1'; @@ -3405,19 +3196,21 @@ describe('TypeScript', () => { import 'global2'; `, errors: [ - { - message: - '`global1` import should occur before import of `./local1`', - }, - { - message: - '`global2` import should occur before import of `./local1`', - }, + createOrderError([ + '`global1` import', + 'before', + 'import of `./local1`', + ]), + createOrderError([ + '`global2` import', + 'before', + 'import of `./local1`', + ]), ], options: [{ warnOnUnassignedImports: true }], }), // fix cannot move below unassigned import (warnOnUnassignedImports enabled) - test({ + tInvalid({ code: ` import local from './local'; @@ -3427,15 +3220,16 @@ describe('TypeScript', () => { import global3 from 'global3'; `, errors: [ - { - message: - '`./local` import should occur after import of `global3`', - }, + createOrderError([ + '`./local` import', + 'after', + 'import of `global3`', + ]), ], options: [{ warnOnUnassignedImports: true }], }), // Imports inside module declaration - test({ + tInvalid({ code: ` import type { ParsedPath } from 'path'; import type { CopyOptions } from 'fs'; @@ -3455,14 +3249,16 @@ describe('TypeScript', () => { } `, errors: [ - { - message: - '`fs` type import should occur before type import of `path`', - }, - { - message: - '`fs` type import should occur before type import of `path`', - }, + createOrderError([ + '`fs` type import', + 'before', + 'type import of `path`', + ]), + createOrderError([ + '`fs` type import', + 'before', + 'type import of `path`', + ]), ], ...parserConfig, options: [ @@ -3472,7 +3268,7 @@ describe('TypeScript', () => { ], }), - test({ + tInvalid({ code: ` import express from 'express'; import log4js from 'log4js'; @@ -3500,10 +3296,11 @@ describe('TypeScript', () => { }, ], errors: [ - { - message: - '`node:child_process` import should occur before import of `express`', - }, + createOrderError([ + '`node:child_process` import', + 'before', + 'import of `express`', + ]), // { message: '`node:fs/promises` import should occur before import of `express`' }, ], }), @@ -3514,7 +3311,7 @@ describe('TypeScript', () => { flowRuleTester.run('order', rule, { valid: [ - test({ + tValid({ options: [ { alphabetize: { order: 'asc', orderImportKind: 'asc' }, @@ -3528,7 +3325,7 @@ flowRuleTester.run('order', rule, { }), ], invalid: [ - test({ + tInvalid({ options: [ { alphabetize: { order: 'asc', orderImportKind: 'asc' }, @@ -3545,13 +3342,14 @@ flowRuleTester.run('order', rule, { import {bar} from 'common'; `, errors: [ - { - message: - '`common` typeof import should occur before import of `common`', - }, + createOrderError([ + '`common` typeof import', + 'before', + 'import of `common`', + ]), ], }), - test({ + tInvalid({ options: [ { alphabetize: { order: 'asc', orderImportKind: 'desc' }, @@ -3568,13 +3366,14 @@ flowRuleTester.run('order', rule, { import type {Bar} from 'common'; `, errors: [ - { - message: - '`common` type import should occur after typeof import of `common`', - }, + createOrderError([ + '`common` type import', + 'after', + 'typeof import of `common`', + ]), ], }), - test({ + tInvalid({ options: [ { alphabetize: { order: 'asc', orderImportKind: 'asc' }, @@ -3593,13 +3392,14 @@ flowRuleTester.run('order', rule, { import {baz} from './local-sub'; `, errors: [ - { - message: - '`./local/sub` typeof import should occur before import of `./local/sub`', - }, + createOrderError([ + '`./local/sub` typeof import', + 'before', + 'import of `./local/sub`', + ]), ], }), - test({ + tInvalid({ code: ` import { cfg } from 'path/path/path/src/Cfg'; import { l10n } from 'path/src/l10n'; @@ -3609,18 +3409,8 @@ flowRuleTester.run('order', rule, { import { controller } from '../../../../path/path/path/controller'; import { component } from '../../../../path/path/path/component'; `, - output: semver.satisfies(eslintPkg.version, '< 3') - ? ` - import { cfg } from 'path/path/path/src/Cfg'; - import { tip } from 'path/path/tip'; - import { l10n } from 'path/src/l10n'; - import { helpers } from 'path/path/path/helpers'; - - import { component } from '../../../../path/path/path/component'; - import { controller } from '../../../../path/path/path/controller'; - ` - : [ - ` + output: [ + ` import { helpers } from 'path/path/path/helpers'; import { cfg } from 'path/path/path/src/Cfg'; import { l10n } from 'path/src/l10n'; @@ -3629,7 +3419,7 @@ flowRuleTester.run('order', rule, { import { component } from '../../../../path/path/path/component'; import { controller } from '../../../../path/path/path/controller'; `, - ` + ` import { helpers } from 'path/path/path/helpers'; import { cfg } from 'path/path/path/src/Cfg'; import { tip } from 'path/path/tip'; @@ -3638,7 +3428,8 @@ flowRuleTester.run('order', rule, { import { component } from '../../../../path/path/path/component'; import { controller } from '../../../../path/path/path/controller'; `, - ], + ], + languageOptions: { parser: require(parsers.ESPREE) }, options: [ { groups: [ @@ -3666,6 +3457,7 @@ flowRuleTester.run('order', rule, { }, }, ], + // @ts-expect-error testing options pathGroupsExcludedImportTypes: ['react'], alphabetize: { order: 'asc', @@ -3675,25 +3467,33 @@ flowRuleTester.run('order', rule, { ], errors: [ { - message: - '`path/path/path/helpers` import should occur before import of `path/path/path/src/Cfg`', + ...createOrderError([ + '`path/path/path/helpers` import', + 'before', + 'import of `path/path/path/src/Cfg`', + ]), line: 4, column: 9, }, { - message: - '`path/path/tip` import should occur before import of `path/src/l10n`', + ...createOrderError([ + '`path/path/tip` import', + 'before', + 'import of `path/src/l10n`', + ]), line: 5, column: 9, }, { - message: - '`../../../../path/path/path/component` import should occur before import of `../../../../path/path/path/controller`', + ...createOrderError([ + '`../../../../path/path/path/component` import', + 'before', + 'import of `../../../../path/path/path/controller`', + ]), line: 8, column: 9, }, ], - languageOptions: { parser: require(parsers.ESPREE) }, }), ], }) diff --git a/test/rules/unambiguous.spec.ts b/test/rules/unambiguous.spec.ts index c38932e52..1d19826eb 100644 --- a/test/rules/unambiguous.spec.ts +++ b/test/rules/unambiguous.spec.ts @@ -83,11 +83,7 @@ ruleTester.run('unambiguous', rule, { parserOptions: { ecmaVersion: 2015, sourceType: 'module' }, }, output: null, - errors: [ - { - messageId: 'module', - }, - ], + errors: [{ messageId: 'module' }], }, ], }) From 8dcd3cdfc4cf5a8fc25418e2af6b27167fe4a66f Mon Sep 17 00:00:00 2001 From: Marco Pasqualetti Date: Thu, 21 Nov 2024 00:14:30 +0100 Subject: [PATCH 44/49] test(prefer-default-export): migrate to `createRuleTestCaseFunctions` --- test/rules/prefer-default-export.spec.ts | 351 +++++++---------------- 1 file changed, 110 insertions(+), 241 deletions(-) diff --git a/test/rules/prefer-default-export.spec.ts b/test/rules/prefer-default-export.spec.ts index 9920359f8..e1474da1d 100644 --- a/test/rules/prefer-default-export.spec.ts +++ b/test/rules/prefer-default-export.spec.ts @@ -1,102 +1,112 @@ import { RuleTester as TSESLintRuleTester } from '@typescript-eslint/rule-tester' +import type { TestCaseError as TSESLintTestCaseError } from '@typescript-eslint/rule-tester' +import type { AST_NODE_TYPES } from '@typescript-eslint/utils' -import { test, getNonDefaultParsers, parsers } from '../utils' +import { + createRuleTestCaseFunctions, + getNonDefaultParsers, + parsers, +} from '../utils' +import type { GetRuleModuleMessageIds } from '../utils' import rule from 'eslint-plugin-import-x/rules/prefer-default-export' const ruleTester = new TSESLintRuleTester() +const { tValid, tInvalid } = createRuleTestCaseFunctions() + +function createSingleError( + type: `${AST_NODE_TYPES}`, +): TSESLintTestCaseError> { + return { messageId: 'single', type: type as AST_NODE_TYPES } +} + // test cases for default option { target: 'single' } ruleTester.run('prefer-default-export', rule, { valid: [ - test({ + tValid({ code: ` export const foo = 'foo'; export const bar = 'bar';`, }), - test({ + tValid({ code: ` export default function bar() {};`, }), - test({ + tValid({ code: ` export const foo = 'foo'; export function bar() {};`, }), - test({ + tValid({ code: ` export const foo = 'foo'; export default bar;`, }), - test({ + tValid({ code: ` let foo, bar; export { foo, bar }`, }), - test({ - code: ` - export const { foo, bar } = item;`, + tValid({ + code: `export const { foo, bar } = item;`, }), - test({ - code: ` - export const { foo, bar: baz } = item;`, + tValid({ + code: `export const { foo, bar: baz } = item;`, }), - test({ - code: ` - export const { foo: { bar, baz } } = item;`, + tValid({ + code: `export const { foo: { bar, baz } } = item;`, }), - test({ - code: ` - export const [a, b] = item;`, + tValid({ + code: `export const [a, b] = item;`, }), - test({ + tValid({ code: ` let item; export const foo = item; export { item };`, }), - test({ + tValid({ code: ` let foo; export { foo as default }`, }), - test({ + tValid({ code: ` export * from './foo';`, }), - test({ + tValid({ code: `export Memory, { MemoryValue } from './Memory'`, languageOptions: { parser: require(parsers.BABEL) }, }), // no exports at all - test({ - code: ` - import * as foo from './foo';`, + tValid({ + code: `import * as foo from './foo';`, }), - test({ + tValid({ code: `export type UserId = number;`, languageOptions: { parser: require(parsers.BABEL) }, }), // issue #653 - test({ + tValid({ code: 'export default from "foo.js"', languageOptions: { parser: require(parsers.BABEL) }, }), - test({ + tValid({ code: 'export { a, b } from "foo.js"', languageOptions: { parser: require(parsers.BABEL) }, }), // ...SYNTAX_CASES, - test({ + tValid({ code: ` export const [CounterProvider,, withCounter] = func();; `, languageOptions: { parser: require(parsers.BABEL) }, }), - test({ + tValid({ code: 'let foo; export { foo as "default" };', languageOptions: { parser: require(parsers.ESPREE), @@ -105,66 +115,31 @@ ruleTester.run('prefer-default-export', rule, { }), ], invalid: [ - test({ - code: ` - export function bar() {};`, - errors: [ - { - type: 'ExportNamedDeclaration', - messageId: 'single', - }, - ], + tInvalid({ + code: `export function bar() {};`, + errors: [createSingleError('ExportNamedDeclaration')], }), - test({ - code: ` - export const foo = 'foo';`, - errors: [ - { - type: 'ExportNamedDeclaration', - messageId: 'single', - }, - ], + tInvalid({ + code: `export const foo = 'foo';`, + errors: [createSingleError('ExportNamedDeclaration')], }), - test({ + tInvalid({ code: ` const foo = 'foo'; export { foo };`, - errors: [ - { - type: 'ExportSpecifier', - messageId: 'single', - }, - ], + errors: [createSingleError('ExportSpecifier')], }), - test({ - code: ` - export const { foo } = { foo: "bar" };`, - errors: [ - { - type: 'ExportNamedDeclaration', - messageId: 'single', - }, - ], + tInvalid({ + code: `export const { foo } = { foo: "bar" };`, + errors: [createSingleError('ExportNamedDeclaration')], }), - test({ - code: ` - export const { foo: { bar } } = { foo: { bar: "baz" } };`, - errors: [ - { - type: 'ExportNamedDeclaration', - messageId: 'single', - }, - ], + tInvalid({ + code: `export const { foo: { bar } } = { foo: { bar: "baz" } };`, + errors: [createSingleError('ExportNamedDeclaration')], }), - test({ - code: ` - export const [a] = ["foo"]`, - errors: [ - { - type: 'ExportNamedDeclaration', - messageId: 'single', - }, - ], + tInvalid({ + code: `export const [a] = ["foo"]`, + errors: [createSingleError('ExportNamedDeclaration')], }), ], }) @@ -173,94 +148,54 @@ ruleTester.run('prefer-default-export', rule, { ruleTester.run('prefer-default-export', rule, { // Any exporting file must contain default export valid: [ - test({ - code: ` - export default function bar() {};`, - options: [ - { - target: 'any', - }, - ], + tValid({ + code: `export default function bar() {};`, + options: [{ target: 'any' }], }), - test({ + tValid({ code: ` export const foo = 'foo'; export const bar = 'bar'; export default 42;`, - options: [ - { - target: 'any', - }, - ], + options: [{ target: 'any' }], }), - test({ - code: ` - export default a = 2;`, - options: [ - { - target: 'any', - }, - ], + tValid({ + code: `export default a = 2;`, + options: [{ target: 'any' }], }), - test({ + tValid({ code: ` export const a = 2; export default function foo() {};`, - options: [ - { - target: 'any', - }, - ], + options: [{ target: 'any' }], }), - test({ + tValid({ code: ` export const a = 5; export function bar(){}; let foo; export { foo as default }`, - options: [ - { - target: 'any', - }, - ], + options: [{ target: 'any' }], }), - test({ - code: ` - export * from './foo';`, - options: [ - { - target: 'any', - }, - ], + tValid({ + code: `export * from './foo';`, + options: [{ target: 'any' }], }), - test({ + tValid({ code: `export Memory, { MemoryValue } from './Memory'`, languageOptions: { parser: require(parsers.BABEL) }, - options: [ - { - target: 'any', - }, - ], + options: [{ target: 'any' }], }), // no exports at all - test({ - code: ` - import * as foo from './foo';`, - options: [ - { - target: 'any', - }, - ], + tValid({ + code: `import * as foo from './foo';`, + options: [{ target: 'any' }], }), - test({ + tValid({ code: `const a = 5;`, - options: [ - { - target: 'any', - }, - ], + options: [{ target: 'any' }], }), - test({ + tValid({ code: 'export const a = 4; let foo; export { foo as "default" };', options: [{ target: 'any' }], languageOptions: { @@ -271,123 +206,57 @@ ruleTester.run('prefer-default-export', rule, { ], // { target: 'any' } invalid cases when any exporting file must contain default export but does not invalid: [ - test({ + tInvalid({ code: ` export const foo = 'foo'; export const bar = 'bar';`, - options: [ - { - target: 'any', - }, - ], - errors: [ - { - messageId: 'any', - }, - ], + options: [{ target: 'any' }], + errors: [{ messageId: 'any' }], }), - test({ + tInvalid({ code: ` export const foo = 'foo'; export function bar() {};`, - options: [ - { - target: 'any', - }, - ], - errors: [ - { - messageId: 'any', - }, - ], + options: [{ target: 'any' }], + errors: [{ messageId: 'any' }], }), - test({ + tInvalid({ code: ` let foo, bar; export { foo, bar }`, - options: [ - { - target: 'any', - }, - ], - errors: [ - { - messageId: 'any', - }, - ], + options: [{ target: 'any' }], + errors: [{ messageId: 'any' }], }), - test({ + tInvalid({ code: ` let item; export const foo = item; export { item };`, - options: [ - { - target: 'any', - }, - ], - errors: [ - { - messageId: 'any', - }, - ], + options: [{ target: 'any' }], + errors: [{ messageId: 'any' }], }), - test({ + tInvalid({ code: 'export { a, b } from "foo.js"', languageOptions: { parser: require(parsers.BABEL) }, - options: [ - { - target: 'any', - }, - ], - errors: [ - { - messageId: 'any', - }, - ], + options: [{ target: 'any' }], + errors: [{ messageId: 'any' }], }), - test({ + tInvalid({ code: ` const foo = 'foo'; export { foo };`, - options: [ - { - target: 'any', - }, - ], - errors: [ - { - messageId: 'any', - }, - ], + options: [{ target: 'any' }], + errors: [{ messageId: 'any' }], }), - test({ - code: ` - export const { foo } = { foo: "bar" };`, - options: [ - { - target: 'any', - }, - ], - errors: [ - { - messageId: 'any', - }, - ], + tInvalid({ + code: `export const { foo } = { foo: "bar" };`, + options: [{ target: 'any' }], + errors: [{ messageId: 'any' }], }), - test({ - code: ` - export const { foo: { bar } } = { foo: { bar: "baz" } };`, - options: [ - { - target: 'any', - }, - ], - errors: [ - { - messageId: 'any', - }, - ], + tInvalid({ + code: `export const { foo: { bar } } = { foo: { bar: "baz" } };`, + options: [{ target: 'any' }], + errors: [{ messageId: 'any' }], }), ], }) @@ -407,7 +276,7 @@ describe('TypeScript', () => { ruleTester.run('prefer-default-export', rule, { valid: [ // Exporting types - test({ + tValid({ code: ` export type foo = string; export type bar = number; @@ -415,15 +284,15 @@ describe('TypeScript', () => { `, ...parserConfig, }), - test({ + tValid({ code: `export type foo = string /* ${parser.replace(process.cwd(), '$$PWD')}*/`, ...parserConfig, }), - test({ + tValid({ code: `export interface foo { bar: string; } /* ${parser.replace(process.cwd(), '$$PWD')}*/`, ...parserConfig, }), - test({ + tValid({ code: `export interface foo { bar: string; }; export function goo() {} /* ${parser.replace(process.cwd(), '$$PWD')}*/`, ...parserConfig, }), From 511bcfe53a19f1fbd1026b2dffa7bc9e24b8e4a3 Mon Sep 17 00:00:00 2001 From: Marco Pasqualetti Date: Thu, 21 Nov 2024 00:30:01 +0100 Subject: [PATCH 45/49] test: use `AST_NODE_TYPES` directlyinstead of reading from `TSESTree` --- .../consistent-type-specifier-style.spec.ts | 113 +++-------- test/rules/dynamic-import-chunkname.spec.ts | 178 ++++-------------- test/rules/exports-last.spec.ts | 9 +- test/rules/named.spec.ts | 10 +- test/rules/namespace.spec.ts | 8 +- test/rules/no-default-export.spec.ts | 6 +- test/rules/no-deprecated.spec.ts | 6 +- test/rules/no-named-as-default-member.spec.ts | 6 +- test/rules/no-named-as-default.spec.ts | 6 +- test/rules/no-rename-default.spec.ts | 10 +- test/rules/no-unresolved.spec.ts | 6 +- 11 files changed, 91 insertions(+), 267 deletions(-) diff --git a/test/rules/consistent-type-specifier-style.spec.ts b/test/rules/consistent-type-specifier-style.spec.ts index b9e5d7628..a6d2e12f7 100644 --- a/test/rules/consistent-type-specifier-style.spec.ts +++ b/test/rules/consistent-type-specifier-style.spec.ts @@ -1,5 +1,5 @@ import { RuleTester as TSESLintRuleTester } from '@typescript-eslint/rule-tester' -import { TSESTree } from '@typescript-eslint/utils' +import { AST_NODE_TYPES } from '@typescript-eslint/utils' import { parsers, createRuleTestCaseFunctions } from '../utils' import type { RuleRunTests } from '../utils' @@ -120,7 +120,7 @@ const COMMON_TESTS: RuleRunTests = { data: { kind: 'type', }, - type: TSESTree.AST_NODE_TYPES.ImportDeclaration, + type: AST_NODE_TYPES.ImportDeclaration, }, ], }, @@ -129,10 +129,7 @@ const COMMON_TESTS: RuleRunTests = { output: "import type {Foo as Bar} from 'Foo';", options: ['prefer-top-level'], errors: [ - { - messageId: 'topLevel', - type: TSESTree.AST_NODE_TYPES.ImportDeclaration, - }, + { messageId: 'topLevel', type: AST_NODE_TYPES.ImportDeclaration }, ], }, { @@ -140,55 +137,32 @@ const COMMON_TESTS: RuleRunTests = { output: "import type {Foo, Bar} from 'Foo';", options: ['prefer-top-level'], errors: [ - { - messageId: 'topLevel', - type: TSESTree.AST_NODE_TYPES.ImportDeclaration, - }, + { messageId: 'topLevel', type: AST_NODE_TYPES.ImportDeclaration }, ], }, { code: "import { Foo, type Bar } from 'Foo';", output: "import { Foo } from 'Foo';\nimport type {Bar} from 'Foo';", options: ['prefer-top-level'], - errors: [ - { - messageId: 'topLevel', - type: TSESTree.AST_NODE_TYPES.ImportSpecifier, - }, - ], + errors: [{ messageId: 'topLevel', type: AST_NODE_TYPES.ImportSpecifier }], }, { code: "import { type Foo, Bar } from 'Foo';", output: "import { Bar } from 'Foo';\nimport type {Foo} from 'Foo';", options: ['prefer-top-level'], - errors: [ - { - messageId: 'topLevel', - type: TSESTree.AST_NODE_TYPES.ImportSpecifier, - }, - ], + errors: [{ messageId: 'topLevel', type: AST_NODE_TYPES.ImportSpecifier }], }, { code: "import Foo, { type Bar } from 'Foo';", output: "import Foo from 'Foo';\nimport type {Bar} from 'Foo';", options: ['prefer-top-level'], - errors: [ - { - messageId: 'topLevel', - type: TSESTree.AST_NODE_TYPES.ImportSpecifier, - }, - ], + errors: [{ messageId: 'topLevel', type: AST_NODE_TYPES.ImportSpecifier }], }, { code: "import Foo, { type Bar, Baz } from 'Foo';", output: "import Foo, { Baz } from 'Foo';\nimport type {Bar} from 'Foo';", options: ['prefer-top-level'], - errors: [ - { - messageId: 'topLevel', - type: TSESTree.AST_NODE_TYPES.ImportSpecifier, - }, - ], + errors: [{ messageId: 'topLevel', type: AST_NODE_TYPES.ImportSpecifier }], }, // https://github.com/import-js/eslint-plugin-import-x/issues/2753 { @@ -212,12 +186,7 @@ import { Component5, } from "package-2";`, options: ['prefer-top-level'], - errors: [ - { - messageId: 'topLevel', - type: TSESTree.AST_NODE_TYPES.ImportSpecifier, - }, - ], + errors: [{ messageId: 'topLevel', type: AST_NODE_TYPES.ImportSpecifier }], }, // @@ -233,7 +202,7 @@ import { data: { kind: 'type', }, - type: TSESTree.AST_NODE_TYPES.ImportDeclaration, + type: AST_NODE_TYPES.ImportDeclaration, }, ], }, @@ -241,12 +210,7 @@ import { code: "import type { Foo, Bar, Baz } from 'Foo';", output: "import { type Foo, type Bar, type Baz } from 'Foo';", options: ['prefer-inline'], - errors: [ - { - messageId: 'inline', - type: TSESTree.AST_NODE_TYPES.ImportDeclaration, - }, - ], + errors: [{ messageId: 'inline', type: AST_NODE_TYPES.ImportDeclaration }], }, ], } as const @@ -309,7 +273,7 @@ const FLOW_ONLY: RuleRunTests = { data: { kind: 'typeof', }, - type: TSESTree.AST_NODE_TYPES.ImportDeclaration, + type: AST_NODE_TYPES.ImportDeclaration, }, ], }, @@ -318,10 +282,7 @@ const FLOW_ONLY: RuleRunTests = { output: "import typeof {Foo as Bar} from 'Foo';", options: ['prefer-top-level'], errors: [ - { - messageId: 'topLevel', - type: TSESTree.AST_NODE_TYPES.ImportDeclaration, - }, + { messageId: 'topLevel', type: AST_NODE_TYPES.ImportDeclaration }, ], }, { @@ -334,7 +295,7 @@ const FLOW_ONLY: RuleRunTests = { data: { kind: 'type/typeof', }, - type: TSESTree.AST_NODE_TYPES.ImportDeclaration, + type: AST_NODE_TYPES.ImportDeclaration, }, ], }, @@ -343,33 +304,20 @@ const FLOW_ONLY: RuleRunTests = { output: "import typeof {Foo, Bar} from 'Foo';", options: ['prefer-top-level'], errors: [ - { - messageId: 'topLevel', - type: TSESTree.AST_NODE_TYPES.ImportDeclaration, - }, + { messageId: 'topLevel', type: AST_NODE_TYPES.ImportDeclaration }, ], }, { code: "import { Foo, typeof Bar } from 'Foo';", output: "import { Foo } from 'Foo';\nimport typeof {Bar} from 'Foo';", options: ['prefer-top-level'], - errors: [ - { - messageId: 'topLevel', - type: TSESTree.AST_NODE_TYPES.ImportSpecifier, - }, - ], + errors: [{ messageId: 'topLevel', type: AST_NODE_TYPES.ImportSpecifier }], }, { code: "import { typeof Foo, Bar } from 'Foo';", output: "import { Bar } from 'Foo';\nimport typeof {Foo} from 'Foo';", options: ['prefer-top-level'], - errors: [ - { - messageId: 'topLevel', - type: TSESTree.AST_NODE_TYPES.ImportSpecifier, - }, - ], + errors: [{ messageId: 'topLevel', type: AST_NODE_TYPES.ImportSpecifier }], }, { code: "import { Foo, type Bar, typeof Baz } from 'Foo';", @@ -382,14 +330,14 @@ const FLOW_ONLY: RuleRunTests = { data: { kind: 'type', }, - type: TSESTree.AST_NODE_TYPES.ImportSpecifier, + type: AST_NODE_TYPES.ImportSpecifier, }, { messageId: 'topLevel', data: { kind: 'typeof', }, - type: TSESTree.AST_NODE_TYPES.ImportSpecifier, + type: AST_NODE_TYPES.ImportSpecifier, }, ], }, @@ -397,24 +345,14 @@ const FLOW_ONLY: RuleRunTests = { code: "import Foo, { typeof Bar } from 'Foo';", output: "import Foo from 'Foo';\nimport typeof {Bar} from 'Foo';", options: ['prefer-top-level'], - errors: [ - { - messageId: 'topLevel', - type: TSESTree.AST_NODE_TYPES.ImportSpecifier, - }, - ], + errors: [{ messageId: 'topLevel', type: AST_NODE_TYPES.ImportSpecifier }], }, { code: "import Foo, { typeof Bar, Baz } from 'Foo';", output: "import Foo, { Baz } from 'Foo';\nimport typeof {Bar} from 'Foo';", options: ['prefer-top-level'], - errors: [ - { - messageId: 'topLevel', - type: TSESTree.AST_NODE_TYPES.ImportSpecifier, - }, - ], + errors: [{ messageId: 'topLevel', type: AST_NODE_TYPES.ImportSpecifier }], }, // @@ -430,7 +368,7 @@ const FLOW_ONLY: RuleRunTests = { data: { kind: 'typeof', }, - type: TSESTree.AST_NODE_TYPES.ImportDeclaration, + type: AST_NODE_TYPES.ImportDeclaration, }, ], }, @@ -438,12 +376,7 @@ const FLOW_ONLY: RuleRunTests = { code: "import typeof { Foo, Bar, Baz } from 'Foo';", output: "import { typeof Foo, typeof Bar, typeof Baz } from 'Foo';", options: ['prefer-inline'], - errors: [ - { - messageId: 'inline', - type: TSESTree.AST_NODE_TYPES.ImportDeclaration, - }, - ], + errors: [{ messageId: 'inline', type: AST_NODE_TYPES.ImportDeclaration }], }, ], } as const diff --git a/test/rules/dynamic-import-chunkname.spec.ts b/test/rules/dynamic-import-chunkname.spec.ts index bb333d49c..0ed3ddc0c 100644 --- a/test/rules/dynamic-import-chunkname.spec.ts +++ b/test/rules/dynamic-import-chunkname.spec.ts @@ -1,5 +1,5 @@ import { RuleTester as TSESLintRuleTester } from '@typescript-eslint/rule-tester' -import { TSESTree } from '@typescript-eslint/utils' +import { AST_NODE_TYPES } from '@typescript-eslint/utils' import { SYNTAX_VALID_CASES, parsers } from '../utils' import type { GetRuleModuleOptions } from '../utils' @@ -441,10 +441,7 @@ ruleTester.run('dynamic-import-chunkname', rule, { languageOptions: { parser: babelParser }, output: null, errors: [ - { - messageId: 'blockComment', - type: TSESTree.AST_NODE_TYPES.ImportExpression, - }, + { messageId: 'blockComment', type: AST_NODE_TYPES.ImportExpression }, ], }, { @@ -453,10 +450,7 @@ ruleTester.run('dynamic-import-chunkname', rule, { languageOptions: { parser: babelParser }, output: null, errors: [ - { - messageId: 'leadingComment', - type: TSESTree.AST_NODE_TYPES.ImportExpression, - }, + { messageId: 'leadingComment', type: AST_NODE_TYPES.ImportExpression }, ], }, { @@ -468,10 +462,7 @@ ruleTester.run('dynamic-import-chunkname', rule, { languageOptions: { parser: babelParser }, output: null, errors: [ - { - messageId: 'webpackComment', - type: TSESTree.AST_NODE_TYPES.ImportExpression, - }, + { messageId: 'webpackComment', type: AST_NODE_TYPES.ImportExpression }, ], }, { @@ -483,10 +474,7 @@ ruleTester.run('dynamic-import-chunkname', rule, { languageOptions: { parser: babelParser }, output: null, errors: [ - { - messageId: 'webpackComment', - type: TSESTree.AST_NODE_TYPES.ImportExpression, - }, + { messageId: 'webpackComment', type: AST_NODE_TYPES.ImportExpression }, ], }, { @@ -498,10 +486,7 @@ ruleTester.run('dynamic-import-chunkname', rule, { languageOptions: { parser: babelParser }, output: null, errors: [ - { - messageId: 'webpackComment', - type: TSESTree.AST_NODE_TYPES.ImportExpression, - }, + { messageId: 'webpackComment', type: AST_NODE_TYPES.ImportExpression }, ], }, { @@ -513,10 +498,7 @@ ruleTester.run('dynamic-import-chunkname', rule, { languageOptions: { parser: babelParser }, output: null, errors: [ - { - messageId: 'webpackComment', - type: TSESTree.AST_NODE_TYPES.ImportExpression, - }, + { messageId: 'webpackComment', type: AST_NODE_TYPES.ImportExpression }, ], }, { @@ -528,10 +510,7 @@ ruleTester.run('dynamic-import-chunkname', rule, { languageOptions: { parser: babelParser }, output: null, errors: [ - { - messageId: 'webpackComment', - type: TSESTree.AST_NODE_TYPES.ImportExpression, - }, + { messageId: 'webpackComment', type: AST_NODE_TYPES.ImportExpression }, ], }, { @@ -543,10 +522,7 @@ ruleTester.run('dynamic-import-chunkname', rule, { languageOptions: { parser: babelParser }, output: null, errors: [ - { - messageId: 'chunknameFormat', - type: TSESTree.AST_NODE_TYPES.ImportExpression, - }, + { messageId: 'chunknameFormat', type: AST_NODE_TYPES.ImportExpression }, ], }, { @@ -558,10 +534,7 @@ ruleTester.run('dynamic-import-chunkname', rule, { languageOptions: { parser: babelParser }, output: null, errors: [ - { - messageId: 'chunknameFormat', - type: TSESTree.AST_NODE_TYPES.ImportExpression, - }, + { messageId: 'chunknameFormat', type: AST_NODE_TYPES.ImportExpression }, ], }, { @@ -573,10 +546,7 @@ ruleTester.run('dynamic-import-chunkname', rule, { languageOptions: { parser: babelParser }, output: null, errors: [ - { - messageId: 'chunknameFormat', - type: TSESTree.AST_NODE_TYPES.ImportExpression, - }, + { messageId: 'chunknameFormat', type: AST_NODE_TYPES.ImportExpression }, ], }, { @@ -588,10 +558,7 @@ ruleTester.run('dynamic-import-chunkname', rule, { languageOptions: { parser: babelParser }, output: null, errors: [ - { - messageId: 'paddedSpaces', - type: TSESTree.AST_NODE_TYPES.ImportExpression, - }, + { messageId: 'paddedSpaces', type: AST_NODE_TYPES.ImportExpression }, ], }, { @@ -603,10 +570,7 @@ ruleTester.run('dynamic-import-chunkname', rule, { languageOptions: { parser: babelParser }, output: null, errors: [ - { - messageId: 'webpackComment', - type: TSESTree.AST_NODE_TYPES.ImportExpression, - }, + { messageId: 'webpackComment', type: AST_NODE_TYPES.ImportExpression }, ], }, { @@ -618,10 +582,7 @@ ruleTester.run('dynamic-import-chunkname', rule, { languageOptions: { parser: babelParser }, output: null, errors: [ - { - messageId: 'webpackComment', - type: TSESTree.AST_NODE_TYPES.ImportExpression, - }, + { messageId: 'webpackComment', type: AST_NODE_TYPES.ImportExpression }, ], }, { @@ -633,10 +594,7 @@ ruleTester.run('dynamic-import-chunkname', rule, { languageOptions: { parser: babelParser }, output: null, errors: [ - { - messageId: 'webpackComment', - type: TSESTree.AST_NODE_TYPES.ImportExpression, - }, + { messageId: 'webpackComment', type: AST_NODE_TYPES.ImportExpression }, ], }, { @@ -649,10 +607,7 @@ ruleTester.run('dynamic-import-chunkname', rule, { languageOptions: { parser: babelParser }, output: null, errors: [ - { - messageId: 'webpackComment', - type: TSESTree.AST_NODE_TYPES.ImportExpression, - }, + { messageId: 'webpackComment', type: AST_NODE_TYPES.ImportExpression }, ], }, { @@ -664,10 +619,7 @@ ruleTester.run('dynamic-import-chunkname', rule, { languageOptions: { parser: babelParser }, output: null, errors: [ - { - messageId: 'webpackComment', - type: TSESTree.AST_NODE_TYPES.ImportExpression, - }, + { messageId: 'webpackComment', type: AST_NODE_TYPES.ImportExpression }, ], }, { @@ -681,7 +633,7 @@ ruleTester.run('dynamic-import-chunkname', rule, { errors: [ { ...pickyChunkNameFormatError, - type: TSESTree.AST_NODE_TYPES.ImportExpression, + type: AST_NODE_TYPES.ImportExpression, }, ], }, @@ -694,10 +646,7 @@ ruleTester.run('dynamic-import-chunkname', rule, { languageOptions: { parser: babelParser }, output: null, errors: [ - { - messageId: 'webpackComment', - type: TSESTree.AST_NODE_TYPES.ImportExpression, - }, + { messageId: 'webpackComment', type: AST_NODE_TYPES.ImportExpression }, ], }, { @@ -709,10 +658,7 @@ ruleTester.run('dynamic-import-chunkname', rule, { languageOptions: { parser: babelParser }, output: null, errors: [ - { - messageId: 'webpackComment', - type: TSESTree.AST_NODE_TYPES.ImportExpression, - }, + { messageId: 'webpackComment', type: AST_NODE_TYPES.ImportExpression }, ], }, { @@ -724,10 +670,7 @@ ruleTester.run('dynamic-import-chunkname', rule, { languageOptions: { parser: babelParser }, output: null, errors: [ - { - messageId: 'webpackComment', - type: TSESTree.AST_NODE_TYPES.ImportExpression, - }, + { messageId: 'webpackComment', type: AST_NODE_TYPES.ImportExpression }, ], }, { @@ -739,10 +682,7 @@ ruleTester.run('dynamic-import-chunkname', rule, { languageOptions: { parser: babelParser }, output: null, errors: [ - { - messageId: 'webpackComment', - type: TSESTree.AST_NODE_TYPES.ImportExpression, - }, + { messageId: 'webpackComment', type: AST_NODE_TYPES.ImportExpression }, ], }, { @@ -754,10 +694,7 @@ ruleTester.run('dynamic-import-chunkname', rule, { languageOptions: { parser: babelParser }, output: null, errors: [ - { - messageId: 'webpackComment', - type: TSESTree.AST_NODE_TYPES.ImportExpression, - }, + { messageId: 'webpackComment', type: AST_NODE_TYPES.ImportExpression }, ], }, { @@ -769,10 +706,7 @@ ruleTester.run('dynamic-import-chunkname', rule, { languageOptions: { parser: babelParser }, output: null, errors: [ - { - messageId: 'webpackComment', - type: TSESTree.AST_NODE_TYPES.ImportExpression, - }, + { messageId: 'webpackComment', type: AST_NODE_TYPES.ImportExpression }, ], }, { @@ -784,10 +718,7 @@ ruleTester.run('dynamic-import-chunkname', rule, { languageOptions: { parser: babelParser }, output: null, errors: [ - { - messageId: 'webpackComment', - type: TSESTree.AST_NODE_TYPES.ImportExpression, - }, + { messageId: 'webpackComment', type: AST_NODE_TYPES.ImportExpression }, ], }, { @@ -799,10 +730,7 @@ ruleTester.run('dynamic-import-chunkname', rule, { languageOptions: { parser: babelParser }, output: null, errors: [ - { - messageId: 'webpackComment', - type: TSESTree.AST_NODE_TYPES.ImportExpression, - }, + { messageId: 'webpackComment', type: AST_NODE_TYPES.ImportExpression }, ], }, { @@ -814,10 +742,7 @@ ruleTester.run('dynamic-import-chunkname', rule, { languageOptions: { parser: babelParser }, output: null, errors: [ - { - messageId: 'webpackComment', - type: TSESTree.AST_NODE_TYPES.ImportExpression, - }, + { messageId: 'webpackComment', type: AST_NODE_TYPES.ImportExpression }, ], }, { @@ -829,10 +754,7 @@ ruleTester.run('dynamic-import-chunkname', rule, { languageOptions: { parser: babelParser }, output: null, errors: [ - { - messageId: 'webpackComment', - type: TSESTree.AST_NODE_TYPES.ImportExpression, - }, + { messageId: 'webpackComment', type: AST_NODE_TYPES.ImportExpression }, ], }, { @@ -844,10 +766,7 @@ ruleTester.run('dynamic-import-chunkname', rule, { languageOptions: { parser: babelParser }, output: null, errors: [ - { - messageId: 'webpackComment', - type: TSESTree.AST_NODE_TYPES.ImportExpression, - }, + { messageId: 'webpackComment', type: AST_NODE_TYPES.ImportExpression }, ], }, { @@ -858,10 +777,7 @@ ruleTester.run('dynamic-import-chunkname', rule, { options: multipleImportFunctionOptions, output: null, errors: [ - { - messageId: 'webpackComment', - type: TSESTree.AST_NODE_TYPES.CallExpression, - }, + { messageId: 'webpackComment', type: AST_NODE_TYPES.CallExpression }, ], }, { @@ -872,10 +788,7 @@ ruleTester.run('dynamic-import-chunkname', rule, { options: multipleImportFunctionOptions, output: null, errors: [ - { - messageId: 'webpackComment', - type: TSESTree.AST_NODE_TYPES.CallExpression, - }, + { messageId: 'webpackComment', type: AST_NODE_TYPES.CallExpression }, ], }, { @@ -886,10 +799,7 @@ ruleTester.run('dynamic-import-chunkname', rule, { options, output: null, errors: [ - { - messageId: 'blockComment', - type: TSESTree.AST_NODE_TYPES.CallExpression, - }, + { messageId: 'blockComment', type: AST_NODE_TYPES.CallExpression }, ], }, { @@ -897,10 +807,7 @@ ruleTester.run('dynamic-import-chunkname', rule, { options, output: null, errors: [ - { - messageId: 'leadingComment', - type: TSESTree.AST_NODE_TYPES.CallExpression, - }, + { messageId: 'leadingComment', type: AST_NODE_TYPES.CallExpression }, ], }, { @@ -911,10 +818,7 @@ ruleTester.run('dynamic-import-chunkname', rule, { options, output: null, errors: [ - { - messageId: 'webpackComment', - type: TSESTree.AST_NODE_TYPES.CallExpression, - }, + { messageId: 'webpackComment', type: AST_NODE_TYPES.CallExpression }, ], }, { @@ -925,10 +829,7 @@ ruleTester.run('dynamic-import-chunkname', rule, { options, output: null, errors: [ - { - messageId: 'webpackComment', - type: TSESTree.AST_NODE_TYPES.CallExpression, - }, + { messageId: 'webpackComment', type: AST_NODE_TYPES.CallExpression }, ], }, { @@ -939,10 +840,7 @@ ruleTester.run('dynamic-import-chunkname', rule, { options, output: null, errors: [ - { - messageId: 'webpackComment', - type: TSESTree.AST_NODE_TYPES.CallExpression, - }, + { messageId: 'webpackComment', type: AST_NODE_TYPES.CallExpression }, ], }, { @@ -955,7 +853,7 @@ ruleTester.run('dynamic-import-chunkname', rule, { errors: [ { ...pickyChunkNameFormatError, - type: TSESTree.AST_NODE_TYPES.CallExpression, + type: AST_NODE_TYPES.CallExpression, }, ], }, @@ -963,7 +861,7 @@ ruleTester.run('dynamic-import-chunkname', rule, { }) describe('TypeScript', () => { - const nodeType = TSESTree.AST_NODE_TYPES.ImportExpression + const nodeType = AST_NODE_TYPES.ImportExpression ruleTester.run('dynamic-import-chunkname', rule, { valid: [ diff --git a/test/rules/exports-last.spec.ts b/test/rules/exports-last.spec.ts index 89f8e9066..83bbccabc 100644 --- a/test/rules/exports-last.spec.ts +++ b/test/rules/exports-last.spec.ts @@ -1,6 +1,6 @@ import { RuleTester as TSESLintRuleTester } from '@typescript-eslint/rule-tester' import type { TestCaseError as TSESLintTestCaseError } from '@typescript-eslint/rule-tester' -import type { TSESTree } from '@typescript-eslint/utils' +import type { AST_NODE_TYPES } from '@typescript-eslint/utils' import { createRuleTestCaseFunctions } from '../utils' import type { GetRuleModuleMessageIds } from '../utils' @@ -12,12 +12,9 @@ const ruleTester = new TSESLintRuleTester() const { tValid, tInvalid } = createRuleTestCaseFunctions() function createInvalidCaseError( - type: `${TSESTree.AST_NODE_TYPES}`, + type: `${AST_NODE_TYPES}`, ): TSESLintTestCaseError> { - return { - messageId: 'end', - type: type as TSESTree.AST_NODE_TYPES, - } + return { messageId: 'end', type: type as AST_NODE_TYPES } } ruleTester.run('exports-last', rule, { diff --git a/test/rules/named.spec.ts b/test/rules/named.spec.ts index 798e2687f..0138c3bc3 100644 --- a/test/rules/named.spec.ts +++ b/test/rules/named.spec.ts @@ -2,7 +2,7 @@ import path from 'node:path' import { RuleTester as TSESLintRuleTester } from '@typescript-eslint/rule-tester' import type { TestCaseError as TSESLintTestCaseError } from '@typescript-eslint/rule-tester' -import type { TSESTree } from '@typescript-eslint/utils' +import type { AST_NODE_TYPES } from '@typescript-eslint/utils' import { createRuleTestCaseFunctions, @@ -22,24 +22,24 @@ const { tValid, tInvalid } = createRuleTestCaseFunctions() function createNotFoundError( name: string, module: string, - type: `${TSESTree.AST_NODE_TYPES}` = 'Identifier', + type: `${AST_NODE_TYPES}` = 'Identifier', ): TSESLintTestCaseError> { return { messageId: 'notFound', data: { name, path: module }, - type: type as TSESTree.AST_NODE_TYPES, + type: type as AST_NODE_TYPES, } } function createNotFoundDeepError( name: string, deepPath: string, - type: `${TSESTree.AST_NODE_TYPES}` = 'Identifier', + type: `${AST_NODE_TYPES}` = 'Identifier', ): TSESLintTestCaseError> { return { messageId: 'notFoundDeep', data: { name, deepPath }, - type: type as TSESTree.AST_NODE_TYPES, + type: type as AST_NODE_TYPES, } } diff --git a/test/rules/namespace.spec.ts b/test/rules/namespace.spec.ts index eab032d56..b12ab244f 100644 --- a/test/rules/namespace.spec.ts +++ b/test/rules/namespace.spec.ts @@ -1,6 +1,6 @@ import { RuleTester as TSESLintRuleTester } from '@typescript-eslint/rule-tester' import type { TestCaseError as TSESLintTestCaseError } from '@typescript-eslint/rule-tester' -import type { TSESTree } from '@typescript-eslint/utils' +import { AST_NODE_TYPES } from '@typescript-eslint/utils' import { createRuleTestCaseFunctions, @@ -23,12 +23,12 @@ const { tValid, tInvalid } = createRuleTestCaseFunctions() function createNotFoundInNamespaceError( name: string, namespace: string, - type?: `${TSESTree.AST_NODE_TYPES}`, + type?: `${AST_NODE_TYPES}`, ): TSESLintTestCaseError> { return { messageId: 'notFoundInNamespace', data: { name, namepath: namespace }, - type: type as TSESTree.AST_NODE_TYPES, + type: type as AST_NODE_TYPES, } } @@ -383,7 +383,7 @@ let invalid: RuleRunTests['invalid'] = [ // @ts-expect-error testing parsing error message: "Parse errors in imported module './malformed.js': 'return' outside of function (1:1)", - type: 'Literal' as TSESTree.AST_NODE_TYPES, + type: AST_NODE_TYPES.Literal, }, ], }), diff --git a/test/rules/no-default-export.spec.ts b/test/rules/no-default-export.spec.ts index e15feb719..16c878c24 100644 --- a/test/rules/no-default-export.spec.ts +++ b/test/rules/no-default-export.spec.ts @@ -1,6 +1,6 @@ import { RuleTester as TSESLintRuleTester } from '@typescript-eslint/rule-tester' import type { TestCaseError as TSESLintTestCaseError } from '@typescript-eslint/rule-tester' -import type { TSESTree, AST_NODE_TYPES } from '@typescript-eslint/utils' +import type { AST_NODE_TYPES } from '@typescript-eslint/utils' import { parsers, createRuleTestCaseFunctions } from '../utils' import type { GetRuleModuleMessageIds } from '../utils' @@ -13,12 +13,12 @@ const { tValid, tInvalid } = createRuleTestCaseFunctions() function createNoAliasDefaultError( local: string, - type?: `${TSESTree.AST_NODE_TYPES}`, + type?: `${AST_NODE_TYPES}`, ): TSESLintTestCaseError> { return { messageId: 'noAliasDefault', data: { local }, - type: type as TSESTree.AST_NODE_TYPES, + type: type as AST_NODE_TYPES, } } diff --git a/test/rules/no-deprecated.spec.ts b/test/rules/no-deprecated.spec.ts index f102f8adc..e2029da3a 100644 --- a/test/rules/no-deprecated.spec.ts +++ b/test/rules/no-deprecated.spec.ts @@ -1,6 +1,6 @@ import { RuleTester as TSESLintRuleTester } from '@typescript-eslint/rule-tester' import type { TestCaseError as TSESLintTestCaseError } from '@typescript-eslint/rule-tester' -import type { TSESTree } from '@typescript-eslint/utils' +import type { AST_NODE_TYPES } from '@typescript-eslint/utils' import { createRuleTestCaseFunctions, @@ -17,12 +17,12 @@ const { tValid, tInvalid } = createRuleTestCaseFunctions() function createDeprecatedDescError( description: string, - type?: `${TSESTree.AST_NODE_TYPES}`, + type?: `${AST_NODE_TYPES}`, ): TSESLintTestCaseError> { return { messageId: 'deprecatedDesc', data: { description }, - type: type as TSESTree.AST_NODE_TYPES, + type: type as AST_NODE_TYPES, } } diff --git a/test/rules/no-named-as-default-member.spec.ts b/test/rules/no-named-as-default-member.spec.ts index 08c696015..ce7a47936 100644 --- a/test/rules/no-named-as-default-member.spec.ts +++ b/test/rules/no-named-as-default-member.spec.ts @@ -1,6 +1,6 @@ import { RuleTester as TSESLintRuleTester } from '@typescript-eslint/rule-tester' import type { TestCaseError as TSESLintTestCaseError } from '@typescript-eslint/rule-tester' -import type { TSESTree } from '@typescript-eslint/utils' +import type { AST_NODE_TYPES } from '@typescript-eslint/utils' import { createRuleTestCaseFunctions, @@ -17,9 +17,9 @@ const { tValid, tInvalid } = createRuleTestCaseFunctions() function createMemberError( data: { objectName: string; propName: string; sourcePath: string }, - type: `${TSESTree.AST_NODE_TYPES}`, + type: `${AST_NODE_TYPES}`, ): TSESLintTestCaseError> { - return { messageId: 'member', data, type: type as TSESTree.AST_NODE_TYPES } + return { messageId: 'member', data, type: type as AST_NODE_TYPES } } ruleTester.run('no-named-as-default-member', rule, { diff --git a/test/rules/no-named-as-default.spec.ts b/test/rules/no-named-as-default.spec.ts index 51ee8c829..dd983ff7d 100644 --- a/test/rules/no-named-as-default.spec.ts +++ b/test/rules/no-named-as-default.spec.ts @@ -1,6 +1,6 @@ import { RuleTester as TSESLintRuleTester } from '@typescript-eslint/rule-tester' import type { TestCaseError as TSESLintTestCaseError } from '@typescript-eslint/rule-tester' -import type { AST_NODE_TYPES, TSESTree } from '@typescript-eslint/utils' +import type { AST_NODE_TYPES } from '@typescript-eslint/utils' import { createRuleTestCaseFunctions, @@ -17,12 +17,12 @@ const { tValid, tInvalid } = createRuleTestCaseFunctions() function createDefaultError( name: string, - type: `${TSESTree.AST_NODE_TYPES}`, + type: `${AST_NODE_TYPES}`, ): TSESLintTestCaseError> { return { messageId: 'default', data: { name }, - type: type as TSESTree.AST_NODE_TYPES, + type: type as AST_NODE_TYPES, } } diff --git a/test/rules/no-rename-default.spec.ts b/test/rules/no-rename-default.spec.ts index be4a61761..33b24dee8 100644 --- a/test/rules/no-rename-default.spec.ts +++ b/test/rules/no-rename-default.spec.ts @@ -1,6 +1,6 @@ import { RuleTester } from '@typescript-eslint/rule-tester' import type { TestCaseError as TSESLintTestCaseError } from '@typescript-eslint/rule-tester' -import type { TSESTree } from '@typescript-eslint/utils' +import type { AST_NODE_TYPES } from '@typescript-eslint/utils' import { parsers, createRuleTestCaseFunctions } from '../utils' import type { GetRuleModuleMessageIds } from '../utils' @@ -19,13 +19,9 @@ function createRenameDefaultError( importName: string suggestion: string }, - type: `${TSESTree.AST_NODE_TYPES}`, + type: `${AST_NODE_TYPES}`, ): TSESLintTestCaseError> { - return { - messageId: 'renameDefault', - data, - type: type as TSESTree.AST_NODE_TYPES, - } + return { messageId: 'renameDefault', data, type: type as AST_NODE_TYPES } } // IMPORT diff --git a/test/rules/no-unresolved.spec.ts b/test/rules/no-unresolved.spec.ts index 34e310847..752cdfea3 100644 --- a/test/rules/no-unresolved.spec.ts +++ b/test/rules/no-unresolved.spec.ts @@ -2,7 +2,7 @@ import path from 'node:path' import { RuleTester as TSESLintRuleTester } from '@typescript-eslint/rule-tester' import type { TestCaseError as TSESLintTestCaseError } from '@typescript-eslint/rule-tester' -import type { TSESTree } from '@typescript-eslint/utils' +import type { AST_NODE_TYPES } from '@typescript-eslint/utils' import { createRuleTestCaseFunctions, @@ -20,12 +20,12 @@ const ruleTester = new TSESLintRuleTester() function createError( messageId: GetRuleModuleMessageIds, module: string, - type?: `${TSESTree.AST_NODE_TYPES}`, + type?: `${AST_NODE_TYPES}`, ): TSESLintTestCaseError> { return { messageId, data: { module }, - type: type as TSESTree.AST_NODE_TYPES, + type: type as AST_NODE_TYPES, } } From 71e410b88ebe0796ed91351ce23fbac557b9c1f3 Mon Sep 17 00:00:00 2001 From: Marco Pasqualetti Date: Thu, 21 Nov 2024 00:35:01 +0100 Subject: [PATCH 46/49] test: remove old utils --- test/utils.ts | 35 ++--------------------------------- 1 file changed, 2 insertions(+), 33 deletions(-) diff --git a/test/utils.ts b/test/utils.ts index 601383e23..a60d210a2 100644 --- a/test/utils.ts +++ b/test/utils.ts @@ -5,9 +5,7 @@ import type { InvalidTestCase as TSESLintInvalidTestCase, RunTests as TSESLintRunTests, } from '@typescript-eslint/rule-tester' -import type { TSESTree } from '@typescript-eslint/utils' import type { RuleModule } from '@typescript-eslint/utils/ts-eslint' -import type { RuleTester } from 'eslint' import semver from 'semver' import typescriptPkg from 'typescript/package.json' @@ -46,17 +44,7 @@ export function getNonDefaultParsers() { export const TEST_FILENAME = testFilePath() -// eslint-disable-next-line @typescript-eslint/no-explicit-any -- simplify testing -export type ValidTestCase = TSESLintValidTestCase & { - errors?: readonly InvalidTestCaseError[] | number - parser?: never - parserOptions?: never -} - -type InvalidTestCase = // eslint-disable-next-line @typescript-eslint/no-explicit-any -- simplify testing - TSESLintInvalidTestCase - -/** @warning DO NOT EXPORT THIS. use {@link createRuleTestCaseFunction} or {@link test} instead */ +/** @warning DO NOT EXPORT THIS. Use {@link createRuleTestCaseFunctions} instead */ function createRuleTestCase>( t: TTestCase, ): TTestCase { @@ -74,23 +62,6 @@ function createRuleTestCase>( } } -export type InvalidTestCaseError = - | string - | InvalidTestCase['errors'][number] - | (RuleTester.TestCaseError & { - type?: `${TSESTree.AST_NODE_TYPES}` - }) - -/** @deprecated use {@link createRuleTestCaseFunctions} */ -export function test( - t: T, -): T extends { errors: InvalidTestCaseError[] | number } - ? InvalidTestCase - : ValidTestCase { - // @ts-expect-error simplify testing - return createRuleTestCase(t) -} - type GetRuleModuleTypes = TRule extends RuleModule ? { @@ -258,6 +229,4 @@ export const SYNTAX_VALID_CASES: TSESLintRunTests['valid'] = }), ] -const testCompiled = process.env.TEST_COMPILED === '1' - -export const srcDir = testCompiled ? 'lib' : 'src' +export const srcDir = process.env.TEST_COMPILED === '1' ? 'lib' : 'src' From 9a4ee87b1c2682ef61f041922c4836d9fee346ac Mon Sep 17 00:00:00 2001 From: Marco Pasqualetti Date: Thu, 21 Nov 2024 00:46:41 +0100 Subject: [PATCH 47/49] test: add `SYNTAX_VALID_CASES` casting --- test/rules/dynamic-import-chunkname.spec.ts | 4 ++-- test/rules/export.spec.ts | 3 ++- test/rules/named.spec.ts | 2 +- test/rules/no-deprecated.spec.ts | 4 ++-- test/rules/no-named-as-default-member.spec.ts | 4 ++-- test/rules/no-named-as-default.spec.ts | 4 ++-- test/rules/no-named-default.spec.ts | 3 ++- test/rules/no-unresolved.spec.ts | 4 ++-- 8 files changed, 15 insertions(+), 13 deletions(-) diff --git a/test/rules/dynamic-import-chunkname.spec.ts b/test/rules/dynamic-import-chunkname.spec.ts index 0ed3ddc0c..6b6c2632a 100644 --- a/test/rules/dynamic-import-chunkname.spec.ts +++ b/test/rules/dynamic-import-chunkname.spec.ts @@ -2,7 +2,7 @@ import { RuleTester as TSESLintRuleTester } from '@typescript-eslint/rule-tester import { AST_NODE_TYPES } from '@typescript-eslint/utils' import { SYNTAX_VALID_CASES, parsers } from '../utils' -import type { GetRuleModuleOptions } from '../utils' +import type { GetRuleModuleOptions, RuleRunTests } from '../utils' import rule from 'eslint-plugin-import-x/rules/dynamic-import-chunkname' @@ -428,7 +428,7 @@ ruleTester.run('dynamic-import-chunkname', rule, { options, languageOptions: { parser: babelParser }, }, - ...SYNTAX_VALID_CASES, + ...(SYNTAX_VALID_CASES as RuleRunTests['valid']), ], invalid: [ diff --git a/test/rules/export.spec.ts b/test/rules/export.spec.ts index 770128035..b9d7e5a5e 100644 --- a/test/rules/export.spec.ts +++ b/test/rules/export.spec.ts @@ -6,6 +6,7 @@ import { parsers, createRuleTestCaseFunctions, } from '../utils' +import type { RuleRunTests } from '../utils' import rule from 'eslint-plugin-import-x/rules/export' @@ -34,7 +35,7 @@ ruleTester.run('export', rule, { // #328: "export * from" does not export a default tValid({ code: 'export default foo; export * from "./bar"' }), - ...SYNTAX_VALID_CASES, + ...(SYNTAX_VALID_CASES as RuleRunTests['valid']), tValid({ code: ` diff --git a/test/rules/named.spec.ts b/test/rules/named.spec.ts index 0138c3bc3..c213e1e18 100644 --- a/test/rules/named.spec.ts +++ b/test/rules/named.spec.ts @@ -218,7 +218,7 @@ ruleTester.run('named', rule, { options: [{ commonjs: true }], }), - ...SYNTAX_VALID_CASES, + ...(SYNTAX_VALID_CASES as RuleRunTests['valid']), tValid({ code: `import { ExtfieldModel, Extfield2Model } from './models';`, diff --git a/test/rules/no-deprecated.spec.ts b/test/rules/no-deprecated.spec.ts index e2029da3a..789eb35b0 100644 --- a/test/rules/no-deprecated.spec.ts +++ b/test/rules/no-deprecated.spec.ts @@ -7,7 +7,7 @@ import { SYNTAX_VALID_CASES, parsers, } from '../utils' -import type { GetRuleModuleMessageIds } from '../utils' +import type { GetRuleModuleMessageIds, RuleRunTests } from '../utils' import rule from 'eslint-plugin-import-x/rules/no-deprecated' @@ -63,7 +63,7 @@ ruleTester.run('no-deprecated', rule, { code: "import { deepDep } from './deep-deprecated'; function x(deepDep) { console.log(deepDep.MY_TERRIBLE_ACTION) }", }), - ...SYNTAX_VALID_CASES, + ...(SYNTAX_VALID_CASES as RuleRunTests['valid']), ], invalid: [ // reports on parse errors even without specifiers diff --git a/test/rules/no-named-as-default-member.spec.ts b/test/rules/no-named-as-default-member.spec.ts index ce7a47936..25275a5a7 100644 --- a/test/rules/no-named-as-default-member.spec.ts +++ b/test/rules/no-named-as-default-member.spec.ts @@ -7,7 +7,7 @@ import { SYNTAX_VALID_CASES, parsers, } from '../utils' -import type { GetRuleModuleMessageIds } from '../utils' +import type { GetRuleModuleMessageIds, RuleRunTests } from '../utils' import rule from 'eslint-plugin-import-x/rules/no-named-as-default-member' @@ -41,7 +41,7 @@ ruleTester.run('no-named-as-default-member', rule, { }, }), - ...SYNTAX_VALID_CASES, + ...(SYNTAX_VALID_CASES as RuleRunTests['valid']), ], invalid: [ diff --git a/test/rules/no-named-as-default.spec.ts b/test/rules/no-named-as-default.spec.ts index dd983ff7d..bd5d02d82 100644 --- a/test/rules/no-named-as-default.spec.ts +++ b/test/rules/no-named-as-default.spec.ts @@ -7,7 +7,7 @@ import { SYNTAX_VALID_CASES, parsers, } from '../utils' -import type { GetRuleModuleMessageIds } from '../utils' +import type { GetRuleModuleMessageIds, RuleRunTests } from '../utils' import rule from 'eslint-plugin-import-x/rules/no-named-as-default' @@ -99,7 +99,7 @@ ruleTester.run('no-named-as-default', rule, { }, }), - ...SYNTAX_VALID_CASES, + ...(SYNTAX_VALID_CASES as RuleRunTests['valid']), ], invalid: [ diff --git a/test/rules/no-named-default.spec.ts b/test/rules/no-named-default.spec.ts index fbee75d59..311045579 100644 --- a/test/rules/no-named-default.spec.ts +++ b/test/rules/no-named-default.spec.ts @@ -6,6 +6,7 @@ import { SYNTAX_VALID_CASES, parsers, } from '../utils' +import type { RuleRunTests } from '../utils' import rule from 'eslint-plugin-import-x/rules/no-named-default' @@ -28,7 +29,7 @@ ruleTester.run('no-named-default', rule, { languageOptions: { parser: require(parsers.BABEL) }, }), - ...SYNTAX_VALID_CASES, + ...(SYNTAX_VALID_CASES as RuleRunTests['valid']), ], invalid: [ diff --git a/test/rules/no-unresolved.spec.ts b/test/rules/no-unresolved.spec.ts index 752cdfea3..fd6e793db 100644 --- a/test/rules/no-unresolved.spec.ts +++ b/test/rules/no-unresolved.spec.ts @@ -10,7 +10,7 @@ import { parsers, testFilePath, } from '../utils' -import type { GetRuleModuleMessageIds } from '../utils' +import type { GetRuleModuleMessageIds, RuleRunTests } from '../utils' import rule from 'eslint-plugin-import-x/rules/no-unresolved' import { CASE_SENSITIVE_FS } from 'eslint-plugin-import-x/utils' @@ -470,7 +470,7 @@ ruleTester.run('no-unresolved electron', rule, { }) ruleTester.run('no-unresolved syntax verification', rule, { - valid: SYNTAX_VALID_CASES, + valid: SYNTAX_VALID_CASES as RuleRunTests['valid'], invalid: [], }) From 8b6c09fabe153a530f250bd69926756cce10f243 Mon Sep 17 00:00:00 2001 From: Marco Pasqualetti Date: Thu, 21 Nov 2024 00:55:55 +0100 Subject: [PATCH 48/49] chore: apply fix from last version of `eslint-plugin-unicorn` --- .eslintrc.js | 2 +- package.json | 7 ++++--- src/rules/dynamic-import-chunkname.ts | 2 +- src/rules/no-default-export.ts | 3 ++- src/rules/no-duplicates.ts | 2 +- src/rules/no-import-module-exports.ts | 20 +++++++++----------- src/rules/no-named-export.ts | 3 +-- src/rules/prefer-default-export.ts | 4 +--- src/utils/declared-scope.ts | 2 +- src/utils/export-map.ts | 5 ++++- test/rules/no-unresolved.spec.ts | 2 +- test/rules/order.spec.ts | 2 +- test/utils/import-type.spec.ts | 10 ++++++---- yarn.lock | 2 +- 14 files changed, 34 insertions(+), 32 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index 93dfb07dc..e0371599c 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -34,7 +34,7 @@ module.exports = { 'eslint-plugin/report-message-format': 'error', 'eslint-plugin/require-meta-docs-description': [ 'error', - { pattern: '^(Enforce|Ensure|Prefer|Forbid).+\\.$' }, + { pattern: String.raw`^(Enforce|Ensure|Prefer|Forbid).+\.$` }, ], 'eslint-plugin/require-meta-schema': 'error', 'eslint-plugin/require-meta-type': 'error', diff --git a/package.json b/package.json index 046bd0527..7635a7568 100644 --- a/package.json +++ b/package.json @@ -48,6 +48,7 @@ "eslint": "^8.57.0 || ^9.0.0" }, "dependencies": { + "@typescript-eslint/scope-manager": "^8.1.0", "@typescript-eslint/utils": "^8.1.0", "debug": "^4.3.4", "doctrine": "^3.0.0", @@ -82,8 +83,8 @@ "@total-typescript/ts-reset": "^0.5.1", "@types/debug": "^4.1.12", "@types/doctrine": "^0.0.9", - "@types/eslint8.56": "npm:@types/eslint@^8.56.11", "@types/eslint": "^9.6.1", + "@types/eslint8.56": "npm:@types/eslint@^8.56.11", "@types/eslint9": "npm:@types/eslint@^9.6.1", "@types/is-glob": "^4.0.4", "@types/jest": "^29.5.12", @@ -97,9 +98,7 @@ "cross-env": "^7.0.3", "enhanced-resolve": "^5.16.0", "escope": "^4.0.0", - "eslint8.56": "npm:eslint@^8.56.0", "eslint": "^9.15.0", - "eslint9": "npm:eslint@^9.15.0", "eslint-config-prettier": "^9.1.0", "eslint-doc-generator": "^1.7.1", "eslint-import-resolver-typescript": "^3.6.1", @@ -112,6 +111,8 @@ "eslint-plugin-n": "^16.6.2", "eslint-plugin-prettier": "^5.1.3", "eslint-plugin-unicorn": "^56.0.1", + "eslint8.56": "npm:eslint@^8.56.0", + "eslint9": "npm:eslint@^9.15.0", "hermes-eslint": "^0.23.1", "jest": "^29.7.0", "klaw-sync": "^6.0.0", diff --git a/src/rules/dynamic-import-chunkname.ts b/src/rules/dynamic-import-chunkname.ts index fdde52e39..2b27c4131 100644 --- a/src/rules/dynamic-import-chunkname.ts +++ b/src/rules/dynamic-import-chunkname.ts @@ -73,7 +73,7 @@ export = createRule<[Options?], MessageId>({ const { importFunctions = [], allowEmpty = false, - webpackChunknameFormat = '([0-9a-zA-Z-_/.]|\\[(request|index)\\])+', + webpackChunknameFormat = String.raw`([0-9a-zA-Z-_/.]|\[(request|index)\])+`, } = context.options[0] || {} const paddedCommentRegex = /^ (\S[\S\s]+\S) $/ diff --git a/src/rules/no-default-export.ts b/src/rules/no-default-export.ts index d41b33b41..565c5e91e 100644 --- a/src/rules/no-default-export.ts +++ b/src/rules/no-default-export.ts @@ -37,7 +37,8 @@ export = createRule({ ExportNamedDeclaration(node) { for (const specifier of node.specifiers.filter( - specifier => getValue(specifier.exported) === 'default')) { + specifier => getValue(specifier.exported) === 'default', + )) { const { loc } = sourceCode.getFirstTokens(node)[1] || {} // @ts-expect-error - experimental parser type if (specifier.type === 'ExportDefaultSpecifier') { diff --git a/src/rules/no-duplicates.ts b/src/rules/no-duplicates.ts index 2ed7cb503..93813609c 100644 --- a/src/rules/no-duplicates.ts +++ b/src/rules/no-duplicates.ts @@ -381,7 +381,7 @@ function hasCommentInsideNonSpecifiers( // `node` (only inside). If there's a `{...}` part, look for comments before // the `{`, but not before the `}` (hence the `+1`s). const someTokens = - openBraceIndex >= 0 && closeBraceIndex >= 0 + openBraceIndex !== -1 && closeBraceIndex !== -1 ? [ ...tokens.slice(1, openBraceIndex + 1), ...tokens.slice(closeBraceIndex + 1), diff --git a/src/rules/no-import-module-exports.ts b/src/rules/no-import-module-exports.ts index 29c54002f..d2cdf0981 100644 --- a/src/rules/no-import-module-exports.ts +++ b/src/rules/no-import-module-exports.ts @@ -21,17 +21,15 @@ function getEntryPoint(context: RuleContext) { function findScope(context: RuleContext, identifier: string) { const { scopeManager } = context.sourceCode - return ( - scopeManager?.scopes - // eslint-disable-next-line unicorn/prefer-spread - .slice() - .reverse() - .find(scope => - scope.variables.some(variable => - variable.identifiers.some(node => node.name === identifier), - ), - ) - ) + return scopeManager?.scopes + + .slice() + .reverse() + .find(scope => + scope.variables.some(variable => + variable.identifiers.some(node => node.name === identifier), + ), + ) } function findDefinition(objectScope: TSESLint.Scope.Scope, identifier: string) { diff --git a/src/rules/no-named-export.ts b/src/rules/no-named-export.ts index bf4d8f968..b7a6b9d2d 100644 --- a/src/rules/no-named-export.ts +++ b/src/rules/no-named-export.ts @@ -32,8 +32,7 @@ export = createRule({ } const someNamed = node.specifiers.some( - specifier => getValue(specifier.exported) !== - 'default', + specifier => getValue(specifier.exported) !== 'default', ) if (someNamed) { context.report({ node, messageId: 'noAllowed' }) diff --git a/src/rules/prefer-default-export.ts b/src/rules/prefer-default-export.ts index aace88227..ea9f25d5e 100644 --- a/src/rules/prefer-default-export.ts +++ b/src/rules/prefer-default-export.ts @@ -69,9 +69,7 @@ export = createRule<[Options?], MessageId>({ }, ExportSpecifier(node) { - if ( - getValue(node.exported) === 'default' - ) { + if (getValue(node.exported) === 'default') { hasDefaultExport = true } else { specifierExportCount++ diff --git a/src/utils/declared-scope.ts b/src/utils/declared-scope.ts index 8a25d811f..c6c5a0c64 100644 --- a/src/utils/declared-scope.ts +++ b/src/utils/declared-scope.ts @@ -1,5 +1,5 @@ +import type { ScopeType } from '@typescript-eslint/scope-manager' import type { TSESTree } from '@typescript-eslint/utils' -import type { ScopeType } from '@typescript-eslint/scope-manager'; import type { RuleContext } from '../types' diff --git a/src/utils/export-map.ts b/src/utils/export-map.ts index d01bbe615..442357f0e 100644 --- a/src/utils/export-map.ts +++ b/src/utils/export-map.ts @@ -241,7 +241,10 @@ export class ExportMap { } } - function addNamespace(object: object, identifier: TSESTree.Identifier | TSESTree.StringLiteral) { + function addNamespace( + object: object, + identifier: TSESTree.Identifier | TSESTree.StringLiteral, + ) { const nsfn = getNamespace(getValue(identifier)) if (nsfn) { Object.defineProperty(object, 'namespace', { get: nsfn }) diff --git a/test/rules/no-unresolved.spec.ts b/test/rules/no-unresolved.spec.ts index fd6e793db..fea7598fc 100644 --- a/test/rules/no-unresolved.spec.ts +++ b/test/rules/no-unresolved.spec.ts @@ -149,7 +149,7 @@ function runResolverTests(resolver: 'node' | 'webpack') { invalid: [ tInvalid({ code: 'import reallyfake from "./reallyfake/module"', - settings: { 'import-x/ignore': ['^\\./fake/'] }, + settings: { 'import-x/ignore': [String.raw`^\./fake/`] }, errors: [createError('unresolved', './reallyfake/module')], }), diff --git a/test/rules/order.spec.ts b/test/rules/order.spec.ts index 6495c2018..37e72c3c4 100644 --- a/test/rules/order.spec.ts +++ b/test/rules/order.spec.ts @@ -2384,7 +2384,7 @@ ruleTester.run('order', rule, { }, ], settings: { - 'import-x/internal-regex': '^(a|b|c|d|e|f|g|h|i|j|k)(\\/|$)', + 'import-x/internal-regex': String.raw`^(a|b|c|d|e|f|g|h|i|j|k)(\/|$)`, }, errors: Array.from({ length: 11 }, () => ({ messageId: 'oneLineBetweenGroups', diff --git a/test/utils/import-type.spec.ts b/test/utils/import-type.spec.ts index 2fe50d7a0..2762e6c87 100644 --- a/test/utils/import-type.spec.ts +++ b/test/utils/import-type.spec.ts @@ -313,21 +313,23 @@ describe('importType(name)', () => { it('`isExternalModule` works with windows directory separator', () => { const context = testContext() expect( - isExternalModule('foo', 'E:\\path\\to\\node_modules\\foo', context), + isExternalModule('foo', String.raw`E:\path\to\node_modules\foo`, context), ).toBe(true) expect( isExternalModule( '@foo/bar', - 'E:\\path\\to\\node_modules\\@foo\\bar', + String.raw`E:\path\to\node_modules\@foo\bar`, context, ), ).toBe(true) expect( isExternalModule( 'foo', - 'E:\\path\\to\\node_modules\\foo', + String.raw`E:\path\to\node_modules\foo`, testContext({ - 'import-x/external-module-folders': ['E:\\path\\to\\node_modules'], + 'import-x/external-module-folders': [ + String.raw`E:\path\to\node_modules`, + ], }), ), ).toBe(true) diff --git a/yarn.lock b/yarn.lock index 9d14efbdd..dfe2a1bda 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2420,7 +2420,7 @@ "@typescript-eslint/types" "5.62.0" "@typescript-eslint/visitor-keys" "5.62.0" -"@typescript-eslint/scope-manager@8.15.0": +"@typescript-eslint/scope-manager@8.15.0", "@typescript-eslint/scope-manager@^8.1.0": version "8.15.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-8.15.0.tgz#28a1a0f13038f382424f45a988961acaca38f7c6" integrity sha512-QRGy8ADi4J7ii95xz4UoiymmmMd/zuy9azCaamnZ3FM8T5fZcex8UfJcjkiEZjJSztKfEBe3dZ5T/5RHAmw2mA== From 5247ee5ce6dbdfaca2668a1a696d7ad0a9c58d91 Mon Sep 17 00:00:00 2001 From: Marco Pasqualetti Date: Thu, 21 Nov 2024 01:01:11 +0100 Subject: [PATCH 49/49] ci: restore version `9` on `eslint` matrix --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e2ccad36f..11bd9b54f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -24,7 +24,7 @@ jobs: eslint: - '8.56' - '8' - - '9.14' + - '9' include: - executeLint: true