Skip to content

Commit 9c58269

Browse files
teidesuSukkaW
andauthored
extensions: added checkTypeImports option (#169)
Co-authored-by: SukkaW <[email protected]>
1 parent 9715220 commit 9c58269

File tree

4 files changed

+99
-3
lines changed

4 files changed

+99
-3
lines changed

.changeset/tidy-crews-sit.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"eslint-plugin-import-x": minor
3+
---
4+
5+
Add new rule option `checkTypedImports` for `extensions`, backports https://github.com/import-js/eslint-plugin-import/pull/2817

docs/rules/extensions.md

+16
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,8 @@ For example, `["error", "never", { "svg": "always" }]` would require that all ex
5656
In that case, if you still want to specify extensions, you can do so inside the **pattern** property.
5757
Default value of `ignorePackages` is `false`.
5858

59+
By default, `import type` and `export type` style imports/exports are ignored. If you want to check them as well, you can set the `checkTypeImports` option to `true`.
60+
5961
### Exception
6062

6163
When disallowing the use of certain extensions this rule makes an exception and allows the use of extension when the file would not be resolvable without extension.
@@ -104,6 +106,13 @@ import express from 'express/index'
104106
import * as path from 'path'
105107
```
106108

109+
The following patterns are considered problems when the configuration is set to "never" and the option "checkTypeImports" is set to `true`:
110+
111+
```js
112+
import type { Foo } from './foo.ts';
113+
export type { Foo } from './foo.ts';
114+
```
115+
107116
The following patterns are considered problems when configuration set to "always":
108117

109118
```js
@@ -166,6 +175,13 @@ import express from 'express'
166175
import foo from '@/foo'
167176
```
168177

178+
The following patterns are considered problems when the configuration is set to "always" and the option "checkTypeImports" is set to `true`:
179+
180+
```js
181+
import type { Foo } from './foo';
182+
export type { Foo } from './foo';
183+
```
184+
169185
## When Not To Use It
170186

171187
If you are not concerned about a consistent usage of file extension.

src/rules/extensions.ts

+17-3
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@ const properties = {
2727
ignorePackages: {
2828
type: 'boolean' as const,
2929
},
30+
checkTypeImports: {
31+
type: 'boolean' as const,
32+
},
3033
},
3134
}
3235

@@ -38,6 +41,7 @@ type NormalizedOptions = {
3841
defaultConfig?: DefaultConfig
3942
pattern?: Record<FileExtension, DefaultConfig>
4043
ignorePackages?: boolean
44+
checkTypeImports?: boolean
4145
}
4246

4347
type Options = DefaultConfig | NormalizedOptions
@@ -47,6 +51,7 @@ function buildProperties(context: RuleContext<MessageId, Options[]>) {
4751
defaultConfig: 'never',
4852
pattern: {},
4953
ignorePackages: false,
54+
checkTypeImports: false,
5055
}
5156

5257
for (const obj of context.options) {
@@ -57,7 +62,11 @@ function buildProperties(context: RuleContext<MessageId, Options[]>) {
5762
}
5863

5964
// If this is not the new structure, transfer all props to result.pattern
60-
if (obj.pattern === undefined && obj.ignorePackages === undefined) {
65+
if (
66+
obj.pattern === undefined &&
67+
obj.ignorePackages === undefined &&
68+
obj.checkTypeImports === undefined
69+
) {
6170
Object.assign(result.pattern, obj)
6271
continue
6372
}
@@ -71,6 +80,10 @@ function buildProperties(context: RuleContext<MessageId, Options[]>) {
7180
if (obj.ignorePackages !== undefined) {
7281
result.ignorePackages = obj.ignorePackages
7382
}
83+
84+
if (obj.checkTypeImports !== undefined) {
85+
result.checkTypeImports = obj.checkTypeImports
86+
}
7487
}
7588

7689
if (result.defaultConfig === 'ignorePackages') {
@@ -213,8 +226,9 @@ export = createRule<Options[], MessageId>({
213226
if (!extension || !importPath.endsWith(`.${extension}`)) {
214227
// ignore type-only imports and exports
215228
if (
216-
('importKind' in node && node.importKind === 'type') ||
217-
('exportKind' in node && node.exportKind === 'type')
229+
!props.checkTypeImports &&
230+
(('importKind' in node && node.importKind === 'type') ||
231+
('exportKind' in node && node.exportKind === 'type'))
218232
) {
219233
return
220234
}

test/rules/extensions.spec.ts

+61
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,15 @@ import { test, testFilePath } from '../utils'
55
import rule from 'eslint-plugin-import-x/rules/extensions'
66

77
const ruleTester = new TSESLintRuleTester()
8+
const ruleTesterWithTypeScriptImports = new TSESLintRuleTester({
9+
settings: {
10+
'import/resolver': {
11+
typescript: {
12+
alwaysTryTypes: true,
13+
},
14+
},
15+
},
16+
})
817

918
ruleTester.run('extensions', rule, {
1019
valid: [
@@ -694,6 +703,58 @@ describe('TypeScript', () => {
694703
{ ts: 'never', tsx: 'never', js: 'never', jsx: 'never' },
695704
],
696705
}),
706+
test({
707+
code: 'import type T from "./typescript-declare";',
708+
errors: ['Missing file extension for "./typescript-declare"'],
709+
options: [
710+
'always',
711+
{
712+
ts: 'never',
713+
tsx: 'never',
714+
js: 'never',
715+
jsx: 'never',
716+
checkTypeImports: true,
717+
},
718+
],
719+
}),
720+
test({
721+
code: 'export type { MyType } from "./typescript-declare";',
722+
errors: ['Missing file extension for "./typescript-declare"'],
723+
options: [
724+
'always',
725+
{
726+
ts: 'never',
727+
tsx: 'never',
728+
js: 'never',
729+
jsx: 'never',
730+
checkTypeImports: true,
731+
},
732+
],
733+
}),
734+
],
735+
})
736+
ruleTesterWithTypeScriptImports.run('extensions', rule, {
737+
valid: [
738+
test({
739+
code: 'import type { MyType } from "./typescript-declare.ts";',
740+
options: ['always', { checkTypeImports: true }],
741+
}),
742+
test({
743+
code: 'export type { MyType } from "./typescript-declare.ts";',
744+
options: ['always', { checkTypeImports: true }],
745+
}),
746+
],
747+
invalid: [
748+
test({
749+
code: 'import type { MyType } from "./typescript-declare";',
750+
errors: ['Missing file extension for "./typescript-declare"'],
751+
options: ['always', { checkTypeImports: true }],
752+
}),
753+
test({
754+
code: 'export type { MyType } from "./typescript-declare";',
755+
errors: ['Missing file extension for "./typescript-declare"'],
756+
options: ['always', { checkTypeImports: true }],
757+
}),
697758
],
698759
})
699760
})

0 commit comments

Comments
 (0)