From 991f0ca6c1386a68ff086c7bcfb750a575ae9b06 Mon Sep 17 00:00:00 2001 From: Lenz Weber-Tronic Date: Wed, 5 Jul 2023 12:20:03 +0200 Subject: [PATCH 1/3] `extensions`: add the `checkTypeImports` option --- docs/rules/extensions.md | 10 ++++++++++ src/rules/extensions.js | 9 +++++++-- tests/src/rules/extensions.js | 18 ++++++++++++++++++ 3 files changed, 35 insertions(+), 2 deletions(-) diff --git a/docs/rules/extensions.md b/docs/rules/extensions.md index df4f34128..c218dbd64 100644 --- a/docs/rules/extensions.md +++ b/docs/rules/extensions.md @@ -56,6 +56,8 @@ For example, `["error", "never", { "svg": "always" }]` would require that all ex In that case, if you still want to specify extensions, you can do so inside the **pattern** property. Default value of `ignorePackages` is `false`. +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`. + ### Exception 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. @@ -167,6 +169,14 @@ import express from 'express'; import foo from '@/foo'; ``` +The following patterns are considered problems when the option "checkTypeImports" is set to `true`: + +```js +import type { Foo } from './foo'; + +export type { Foo } from './foo'; +``` + ## When Not To Use It If you are not concerned about a consistent usage of file extension. diff --git a/src/rules/extensions.js b/src/rules/extensions.js index 50debc6c8..72028e6b0 100644 --- a/src/rules/extensions.js +++ b/src/rules/extensions.js @@ -14,6 +14,7 @@ const properties = { type: 'object', properties: { pattern: patternProperties, + checkTypeImports: { type: 'boolean' }, ignorePackages: { type: 'boolean' }, }, }; @@ -35,7 +36,7 @@ function buildProperties(context) { } // If this is not the new structure, transfer all props to result.pattern - if (obj.pattern === undefined && obj.ignorePackages === undefined) { + if (obj.pattern === undefined && obj.ignorePackages === undefined && obj.checkTypeImports === undefined) { Object.assign(result.pattern, obj); return; } @@ -49,6 +50,10 @@ function buildProperties(context) { if (obj.ignorePackages !== undefined) { result.ignorePackages = obj.ignorePackages; } + + if (obj.checkTypeImports !== undefined) { + result.checkTypeImports = obj.checkTypeImports; + } }); if (result.defaultConfig === 'ignorePackages') { @@ -167,7 +172,7 @@ module.exports = { if (!extension || !importPath.endsWith(`.${extension}`)) { // ignore type-only imports and exports - if (node.importKind === 'type' || node.exportKind === 'type') { return; } + if (props.checkTypeImports !== true && (node.importKind === 'type' || node.exportKind === 'type')) { return; } const extensionRequired = isUseOfExtensionRequired(extension, isPackage); const extensionForbidden = isUseOfExtensionForbidden(extension); if (extensionRequired && !extensionForbidden) { diff --git a/tests/src/rules/extensions.js b/tests/src/rules/extensions.js index ede1a8d88..4f0b49e96 100644 --- a/tests/src/rules/extensions.js +++ b/tests/src/rules/extensions.js @@ -640,6 +640,24 @@ describe('TypeScript', () => { ], parser, }), + test({ + code: 'import type T from "./typescript-declare";', + errors: ['Missing file extension for "./typescript-declare"'], + options: [ + 'always', + { ts: 'never', tsx: 'never', js: 'never', jsx: 'never', checkTypeImports: true }, + ], + parser, + }), + test({ + code: 'export type { MyType } from "./typescript-declare";', + errors: ['Missing file extension for "./typescript-declare"'], + options: [ + 'always', + { ts: 'never', tsx: 'never', js: 'never', jsx: 'never', checkTypeImports: true }, + ], + parser, + }), ], }); }); From 14e3560677864b980ba53cd2d9526e2412c93ec0 Mon Sep 17 00:00:00 2001 From: Lenz Weber-Tronic Date: Tue, 14 Nov 2023 12:18:42 +0100 Subject: [PATCH 2/3] Update src/rules/extensions.js Co-authored-by: Jordan Harband --- src/rules/extensions.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rules/extensions.js b/src/rules/extensions.js index 72028e6b0..90f8c8620 100644 --- a/src/rules/extensions.js +++ b/src/rules/extensions.js @@ -172,7 +172,7 @@ module.exports = { if (!extension || !importPath.endsWith(`.${extension}`)) { // ignore type-only imports and exports - if (props.checkTypeImports !== true && (node.importKind === 'type' || node.exportKind === 'type')) { return; } + if (!props.checkTypeImports && (node.importKind === 'type' || node.exportKind === 'type')) { return; } const extensionRequired = isUseOfExtensionRequired(extension, isPackage); const extensionForbidden = isUseOfExtensionForbidden(extension); if (extensionRequired && !extensionForbidden) { From 6ac3d03217b80c068fc7846f77410ce5976f943f Mon Sep 17 00:00:00 2001 From: Lenz Weber-Tronic Date: Tue, 14 Nov 2023 12:54:08 +0100 Subject: [PATCH 3/3] add some tests, add doc comment --- docs/rules/extensions.md | 10 ++++++- tests/src/rules/extensions.js | 49 +++++++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+), 1 deletion(-) diff --git a/docs/rules/extensions.md b/docs/rules/extensions.md index c218dbd64..c0318b4f7 100644 --- a/docs/rules/extensions.md +++ b/docs/rules/extensions.md @@ -106,6 +106,14 @@ import express from 'express/index'; import * as path from 'path'; ``` +The following patterns are considered problems when the configuration is set to "never" and the option "checkTypeImports" is set to `true`: + +```js +import type { Foo } from './foo.ts'; + +export type { Foo } from './foo.ts'; +``` + The following patterns are considered problems when configuration set to "always": ```js @@ -169,7 +177,7 @@ import express from 'express'; import foo from '@/foo'; ``` -The following patterns are considered problems when the option "checkTypeImports" is set to `true`: +The following patterns are considered problems when the configuration is set to "always" and the option "checkTypeImports" is set to `true`: ```js import type { Foo } from './foo'; diff --git a/tests/src/rules/extensions.js b/tests/src/rules/extensions.js index 4f0b49e96..5b274a39b 100644 --- a/tests/src/rules/extensions.js +++ b/tests/src/rules/extensions.js @@ -3,6 +3,15 @@ import rule from 'rules/extensions'; import { getTSParsers, test, testFilePath, parsers } from '../utils'; const ruleTester = new RuleTester(); +const ruleTesterWithTypeScriptImports = new RuleTester({ + settings: { + 'import/resolver': { + typescript: { + alwaysTryTypes: true, + }, + }, + }, +}); ruleTester.run('extensions', rule, { valid: [ @@ -660,5 +669,45 @@ describe('TypeScript', () => { }), ], }); + ruleTesterWithTypeScriptImports.run(`${parser}: (with TS resolver) extensions are enforced for type imports/export when checkTypeImports is set`, rule, { + valid: [ + test({ + code: 'import type { MyType } from "./typescript-declare.ts";', + options: [ + 'always', + { checkTypeImports: true }, + ], + parser, + }), + test({ + code: 'export type { MyType } from "./typescript-declare.ts";', + options: [ + 'always', + { checkTypeImports: true }, + ], + parser, + }), + ], + invalid: [ + test({ + code: 'import type { MyType } from "./typescript-declare";', + errors: ['Missing file extension "ts" for "./typescript-declare"'], + options: [ + 'always', + { checkTypeImports: true }, + ], + parser, + }), + test({ + code: 'export type { MyType } from "./typescript-declare";', + errors: ['Missing file extension "ts" for "./typescript-declare"'], + options: [ + 'always', + { checkTypeImports: true }, + ], + parser, + }), + ], + }); }); });