From b96b499aa25743dbec5a9293001fb0e567433f1d Mon Sep 17 00:00:00 2001 From: Adrian Pascu Date: Tue, 13 Dec 2022 14:50:30 +0100 Subject: [PATCH] [New] `no-absolute-path`: add fixer --- CHANGELOG.md | 2 ++ README.md | 2 +- docs/rules/no-absolute-path.md | 2 ++ src/rules/no-absolute-path.js | 16 +++++++++++++++- tests/src/rules/no-absolute-path.js | 26 ++++++++++++++++++++++++++ 5 files changed, 46 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 58bfe0ab3..5907c521c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ This change log adheres to standards from [Keep a CHANGELOG](https://keepachange - [`consistent-type-specifier-style`]: add rule ([#2473], thanks [@bradzacher]) - Add [`no-empty-named-blocks`] rule ([#2568], thanks [@guilhermelimak]) - [`prefer-default-export`]: add "target" option ([#2602], thanks [@azyzz228]) +- [`no-absolute-path`]: add fixer ([#2613], thanks [@adipascu]) ### Fixed - [`order`]: move nested imports closer to main import entry ([#2396], thanks [@pri1311]) @@ -1026,6 +1027,7 @@ for info on changes for earlier releases. [`memo-parser`]: ./memo-parser/README.md +[#2613]: https://github.com/import-js/eslint-plugin-import/pull/2613 [#2608]: https://github.com/import-js/eslint-plugin-import/pull/2608 [#2605]: https://github.com/import-js/eslint-plugin-import/pull/2605 [#2602]: https://github.com/import-js/eslint-plugin-import/pull/2602 diff --git a/README.md b/README.md index 640929c06..5c6f1a321 100644 --- a/README.md +++ b/README.md @@ -56,7 +56,7 @@ This plugin intends to support linting of ES2015+ (ES6+) import/export syntax, a | [default](docs/rules/default.md) | Ensure a default export is present, given a default import. | ❗ ☑️ | | | | | | | [named](docs/rules/named.md) | Ensure named imports correspond to a named export in the remote file. | ❗ ☑️ | | ⌨️ | | | | | [namespace](docs/rules/namespace.md) | Ensure imported namespaces contain dereferenced properties as they are dereferenced. | ❗ ☑️ | | | | | | -| [no-absolute-path](docs/rules/no-absolute-path.md) | Forbid import of modules using absolute paths. | | | | | | | +| [no-absolute-path](docs/rules/no-absolute-path.md) | Forbid import of modules using absolute paths. | | | | 🔧 | | | | [no-cycle](docs/rules/no-cycle.md) | Forbid a module from importing a module with a dependency path back to itself. | | | | | | | | [no-dynamic-require](docs/rules/no-dynamic-require.md) | Forbid `require()` calls with expressions. | | | | | | | | [no-internal-modules](docs/rules/no-internal-modules.md) | Forbid importing the submodules of other modules. | | | | | | | diff --git a/docs/rules/no-absolute-path.md b/docs/rules/no-absolute-path.md index 3d85b9eba..a796f9d57 100644 --- a/docs/rules/no-absolute-path.md +++ b/docs/rules/no-absolute-path.md @@ -1,5 +1,7 @@ # import/no-absolute-path +🔧 This rule is automatically fixable by the [`--fix` CLI option](https://eslint.org/docs/latest/user-guide/command-line-interface#--fix). + Node.js allows the import of modules using an absolute path such as `/home/xyz/file.js`. That is a bad practice as it ties the code using it to your computer, and therefore makes it unusable in packages distributed on `npm` for instance. diff --git a/src/rules/no-absolute-path.js b/src/rules/no-absolute-path.js index 171419d84..19dae6b6f 100644 --- a/src/rules/no-absolute-path.js +++ b/src/rules/no-absolute-path.js @@ -1,3 +1,4 @@ +import path from 'path'; import moduleVisitor, { makeOptionsSchema } from 'eslint-module-utils/moduleVisitor'; import { isAbsolute } from '../core/importType'; import docsUrl from '../docsUrl'; @@ -10,13 +11,26 @@ module.exports = { description: 'Forbid import of modules using absolute paths.', url: docsUrl('no-absolute-path'), }, + fixable: 'code', schema: [ makeOptionsSchema() ], }, create(context) { function reportIfAbsolute(source) { if (isAbsolute(source.value)) { - context.report(source, 'Do not import modules using an absolute path'); + context.report({ + node: source, + message: 'Do not import modules using an absolute path', + fix: fixer => { + const resolvedContext = context.getPhysicalFilename ? context.getPhysicalFilename() : context.getFilename(); + // node.js and web imports work with posix style paths ("/") + let relativePath = path.posix.relative(path.dirname(resolvedContext), source.value); + if (!relativePath.startsWith('.')) { + relativePath = './' + relativePath; + } + return fixer.replaceText(source, JSON.stringify(relativePath)); + }, + }); } } diff --git a/tests/src/rules/no-absolute-path.js b/tests/src/rules/no-absolute-path.js index 63fb8c0b6..bfa08465c 100644 --- a/tests/src/rules/no-absolute-path.js +++ b/tests/src/rules/no-absolute-path.js @@ -53,48 +53,74 @@ ruleTester.run('no-absolute-path', rule, { invalid: [ test({ code: 'import f from "/foo"', + filename: '/foo/bar/index.js', errors: [error], + output: 'import f from ".."', + }), + test({ + code: 'import f from "/foo/bar/baz.js"', + filename: '/foo/bar/index.js', + errors: [error], + output: 'import f from "./baz.js"', }), test({ code: 'import f from "/foo/path"', + filename: '/foo/bar/index.js', errors: [error], + output: 'import f from "../path"', }), test({ code: 'import f from "/some/path"', + filename: '/foo/bar/index.js', errors: [error], + output: 'import f from "../../some/path"', }), test({ code: 'import f from "/some/path"', + filename: '/foo/bar/index.js', options: [{ amd: true }], errors: [error], + output: 'import f from "../../some/path"', }), test({ code: 'var f = require("/foo")', + filename: '/foo/bar/index.js', errors: [error], + output: 'var f = require("..")', }), test({ code: 'var f = require("/foo/path")', + filename: '/foo/bar/index.js', errors: [error], + output: 'var f = require("../path")', }), test({ code: 'var f = require("/some/path")', + filename: '/foo/bar/index.js', errors: [error], + output: 'var f = require("../../some/path")', }), test({ code: 'var f = require("/some/path")', + filename: '/foo/bar/index.js', options: [{ amd: true }], errors: [error], + output: 'var f = require("../../some/path")', }), // validate amd test({ code: 'require(["/some/path"], function (f) { /* ... */ })', + filename: '/foo/bar/index.js', options: [{ amd: true }], errors: [error], + output: 'require(["../../some/path"], function (f) { /* ... */ })', }), test({ code: 'define(["/some/path"], function (f) { /* ... */ })', + filename: '/foo/bar/index.js', options: [{ amd: true }], errors: [error], + output: 'define(["../../some/path"], function (f) { /* ... */ })', }), ], });