From 8f6185d077971100a22abbc1c3b0631df9bc1f4c Mon Sep 17 00:00:00 2001 From: Stephen Jason Wang Date: Thu, 10 Apr 2025 01:45:45 +0800 Subject: [PATCH 1/2] feat(extensions): add autofix support for consistent file extensions in import paths --- README.md | 2 +- docs/rules/extensions.md | 2 ++ src/rules/extensions.js | 27 ++++++++++++++++++++++++++- 3 files changed, 29 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 885f34873c..735cf3b689 100644 --- a/README.md +++ b/README.md @@ -76,7 +76,7 @@ This plugin intends to support linting of ES2015+ (ES6+) import/export syntax, a | [consistent-type-specifier-style](docs/rules/consistent-type-specifier-style.md) | Enforce or ban the use of inline type-only markers for named imports. | | | | 🔧 | | | | [dynamic-import-chunkname](docs/rules/dynamic-import-chunkname.md) | Enforce a leading comment with the webpackChunkName for dynamic imports. | | | | | 💡 | | | [exports-last](docs/rules/exports-last.md) | Ensure all exports appear after other statements. | | | | | | | -| [extensions](docs/rules/extensions.md) | Ensure consistent use of file extension within the import path. | | | | | | | +| [extensions](docs/rules/extensions.md) | Ensure consistent use of file extension within the import path. | | | | 🔧 | | | | [first](docs/rules/first.md) | Ensure all imports appear before other statements. | | | | 🔧 | | | | [group-exports](docs/rules/group-exports.md) | Prefer named exports to be grouped together in a single export declaration | | | | | | | | [imports-first](docs/rules/imports-first.md) | Replaced by `import/first`. | | | | 🔧 | | ❌ | diff --git a/docs/rules/extensions.md b/docs/rules/extensions.md index bd9f3f3584..901d36c0cf 100644 --- a/docs/rules/extensions.md +++ b/docs/rules/extensions.md @@ -1,5 +1,7 @@ # import/extensions +🔧 This rule is automatically fixable by the [`--fix` CLI option](https://eslint.org/docs/latest/user-guide/command-line-interface#--fix). + Some file resolve algorithms allow you to omit the file extension within the import source path. For example the `node` resolver (which does not yet support ESM/`import`) can resolve `./foo/bar` to the absolute path `/User/someone/foo/bar.js` because the `.js` extension is resolved automatically by default in CJS. Depending on the resolver you can configure more extensions to get resolved automatically. diff --git a/src/rules/extensions.js b/src/rules/extensions.js index 2aeef64758..9c03d229bb 100644 --- a/src/rules/extensions.js +++ b/src/rules/extensions.js @@ -46,6 +46,7 @@ function buildProperties(context) { defaultConfig: 'never', pattern: {}, ignorePackages: false, + fix: false, }; context.options.forEach((obj) => { @@ -72,6 +73,11 @@ function buildProperties(context) { result.ignorePackages = obj.ignorePackages; } + // If fix is provided, transfer it to result + if (obj.fix !== undefined) { + result.fix = obj.fix; + } + if (obj.checkTypeImports !== undefined) { result.checkTypeImports = obj.checkTypeImports; } @@ -97,7 +103,7 @@ module.exports = { description: 'Ensure consistent use of file extension within the import path.', url: docsUrl('extensions'), }, - + fixable: 'code', schema: { anyOf: [ { @@ -225,6 +231,14 @@ module.exports = { node: source, message: `Missing file extension ${extension ? `"${extension}" ` : ''}for "${importPathWithQueryString}"`, + ...props.fix && extension ? { + fix(fixer) { + return fixer.replaceText( + source, + JSON.stringify(`${importPathWithQueryString}.${extension}`), + ); + }, + } : {}, }); } } else if (extension) { @@ -232,6 +246,17 @@ module.exports = { context.report({ node: source, message: `Unexpected use of file extension "${extension}" for "${importPathWithQueryString}"`, + ...props.fix + ? { + fix(fixer) { + return fixer.replaceText( + source, + JSON.stringify( + importPath.slice(0, -(extension.length + 1)), + ), + ); + }, + } : {}, }); } } From b8e73709316cd6caf8f40444abc537606c5b2e17 Mon Sep 17 00:00:00 2001 From: Stephen Jason Wang Date: Thu, 10 Apr 2025 23:42:04 +0800 Subject: [PATCH 2/2] test: add test cases for extensions rule with fix option --- tests/src/rules/extensions.js | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/tests/src/rules/extensions.js b/tests/src/rules/extensions.js index 8843713e34..0ac1a59688 100644 --- a/tests/src/rules/extensions.js +++ b/tests/src/rules/extensions.js @@ -155,6 +155,16 @@ ruleTester.run('extensions', rule, { ].join('\n'), options: ['always'], }), + + test({ + code: "import foo from './foo';", + options: [{ fix: true }], + }), + + test({ + code: "import foo from './foo.js';", + options: [{ fix: true, pattern: { js: 'always' } }], + }), ], invalid: [ @@ -652,6 +662,13 @@ ruleTester.run('extensions', rule, { }, ], }), + + test({ + code: 'import foo from "./foo.js";', + options: ['always', { pattern: { js: 'never' }, fix: true }], + errors: [{ message: 'Unexpected use of file extension "js" for "./foo.js"' }], + output: 'import foo from "./foo";', + }), ], });