diff --git a/CHANGELOG.md b/CHANGELOG.md index bd96d8abe..7b8b19b5a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -54,6 +54,7 @@ Yanked due to critical issue in eslint-module-utils with cache key resulting fro - [`no-anonymous-default-export`] rule: report anonymous default exports ([#712], thanks [@duncanbeevers]). - Add new value to [`order`]'s `newlines-between` option to allow newlines inside import groups ([#627], [#628], thanks [@giodamelio]) - Add `count` option to the [`newline-after-import`] rule to allow configuration of number of newlines expected ([#742], thanks [@ntdb]) +- Add [`no-self-import`] rule: forbids a module from importing itself. ([#727], [#449], [#447], thanks [@giodamelio]). ### Changed - [`no-extraneous-dependencies`]: use `read-pkg-up` to simplify finding + loading `package.json` ([#680], thanks [@wtgtybhertgeghgtwtg]) @@ -433,6 +434,7 @@ for info on changes for earlier releases. [`no-anonymous-default-export`]: ./docs/rules/no-anonymous-default-export.md [`exports-last`]: ./docs/rules/exports-last.md [`group-exports`]: ./docs/rules/group-exports.md +[`no-self-import`]: ./docs/rules/no-self-import.md [`memo-parser`]: ./memo-parser/README.md @@ -444,6 +446,7 @@ for info on changes for earlier releases. [#744]: https://github.com/benmosher/eslint-plugin-import/pull/744 [#742]: https://github.com/benmosher/eslint-plugin-import/pull/742 [#737]: https://github.com/benmosher/eslint-plugin-import/pull/737 +[#727]: https://github.com/benmosher/eslint-plugin-import/pull/727 [#721]: https://github.com/benmosher/eslint-plugin-import/pull/721 [#712]: https://github.com/benmosher/eslint-plugin-import/pull/712 [#696]: https://github.com/benmosher/eslint-plugin-import/pull/696 @@ -468,6 +471,7 @@ for info on changes for earlier releases. [#489]: https://github.com/benmosher/eslint-plugin-import/pull/489 [#485]: https://github.com/benmosher/eslint-plugin-import/pull/485 [#461]: https://github.com/benmosher/eslint-plugin-import/pull/461 +[#449]: https://github.com/benmosher/eslint-plugin-import/pull/449 [#444]: https://github.com/benmosher/eslint-plugin-import/pull/444 [#428]: https://github.com/benmosher/eslint-plugin-import/pull/428 [#395]: https://github.com/benmosher/eslint-plugin-import/pull/395 @@ -533,6 +537,7 @@ for info on changes for earlier releases. [#456]: https://github.com/benmosher/eslint-plugin-import/issues/456 [#453]: https://github.com/benmosher/eslint-plugin-import/issues/453 [#452]: https://github.com/benmosher/eslint-plugin-import/issues/452 +[#447]: https://github.com/benmosher/eslint-plugin-import/issues/447 [#441]: https://github.com/benmosher/eslint-plugin-import/issues/441 [#423]: https://github.com/benmosher/eslint-plugin-import/issues/423 [#416]: https://github.com/benmosher/eslint-plugin-import/issues/416 diff --git a/README.md b/README.md index cfe360e4f..7280a57cf 100644 --- a/README.md +++ b/README.md @@ -23,6 +23,7 @@ This plugin intends to support linting of ES2015+ (ES6+) import/export syntax, a * Forbid `require()` calls with expressions ([`no-dynamic-require`]) * Prevent importing the submodules of other modules ([`no-internal-modules`]) * Forbid Webpack loader syntax in imports ([`no-webpack-loader-syntax`]) +* Forbid a module from importing itself ([`no-self-import`]) [`no-unresolved`]: ./docs/rules/no-unresolved.md [`named`]: ./docs/rules/named.md @@ -33,6 +34,7 @@ This plugin intends to support linting of ES2015+ (ES6+) import/export syntax, a [`no-dynamic-require`]: ./docs/rules/no-dynamic-require.md [`no-internal-modules`]: ./docs/rules/no-internal-modules.md [`no-webpack-loader-syntax`]: ./docs/rules/no-webpack-loader-syntax.md +[`no-self-import`]: ./docs/rules/no-self-import.md **Helpful warnings:** diff --git a/docs/rules/no-self-import.md b/docs/rules/no-self-import.md new file mode 100644 index 000000000..089f5e029 --- /dev/null +++ b/docs/rules/no-self-import.md @@ -0,0 +1,30 @@ +# Forbid a module from importing itself + +Forbid a module from importing itself. This can sometimes happen during refactoring. + +## Rule Details + +### Fail + +```js +// foo.js +import foo from './foo'; + +const foo = require('./foo'); +``` + +```js +// index.js +import index from '.'; + +const index = require('.'); +``` + +### Pass + +```js +// foo.js +import bar from './bar'; + +const bar = require('./bar'); +``` diff --git a/src/index.js b/src/index.js index 1589b724f..57a40ac10 100644 --- a/src/index.js +++ b/src/index.js @@ -11,6 +11,7 @@ export const rules = { 'no-internal-modules': require('./rules/no-internal-modules'), 'group-exports': require('./rules/group-exports'), + 'no-self-import': require('./rules/no-self-import'), 'no-named-default': require('./rules/no-named-default'), 'no-named-as-default': require('./rules/no-named-as-default'), 'no-named-as-default-member': require('./rules/no-named-as-default-member'), diff --git a/src/rules/no-self-import.js b/src/rules/no-self-import.js new file mode 100644 index 000000000..ae3d4453b --- /dev/null +++ b/src/rules/no-self-import.js @@ -0,0 +1,41 @@ +/** + * @fileOverview Forbids a module from importing itself + * @author Gio d'Amelio + */ + +import resolve from 'eslint-module-utils/resolve' +import isStaticRequire from '../core/staticRequire' + +function isImportingSelf(context, node, requireName) { + const filePath = context.getFilename() + + // If the input is from stdin, this test can't fail + if (filePath !== '' && filePath === resolve(requireName, context)) { + context.report({ + node, + message: 'Module imports itself.', + }) + } +} + +module.exports = { + meta: { + doc: { + description: 'Forbid a module from importing itself', + recommended: true, + }, + schema: [], + }, + create: function (context) { + return { + ImportDeclaration(node) { + isImportingSelf(context, node, node.source.value) + }, + CallExpression(node) { + if (isStaticRequire(node)) { + isImportingSelf(context, node, node.arguments[0].value) + } + }, + } + }, +} diff --git a/tests/files/index.js b/tests/files/index.js index e69de29bb..8eb61aca3 100644 --- a/tests/files/index.js +++ b/tests/files/index.js @@ -0,0 +1 @@ +// Used in `no-self-import` tests diff --git a/tests/files/no-self-import-folder/index.js b/tests/files/no-self-import-folder/index.js new file mode 100644 index 000000000..8eb61aca3 --- /dev/null +++ b/tests/files/no-self-import-folder/index.js @@ -0,0 +1 @@ +// Used in `no-self-import` tests diff --git a/tests/files/no-self-import.js b/tests/files/no-self-import.js new file mode 100644 index 000000000..8eb61aca3 --- /dev/null +++ b/tests/files/no-self-import.js @@ -0,0 +1 @@ +// Used in `no-self-import` tests diff --git a/tests/src/rules/no-self-import.js b/tests/src/rules/no-self-import.js new file mode 100644 index 000000000..f8549b49e --- /dev/null +++ b/tests/src/rules/no-self-import.js @@ -0,0 +1,121 @@ +import { test, testFilePath } from '../utils' + +import { RuleTester } from 'eslint' + +const ruleTester = new RuleTester() + , rule = require('rules/no-self-import') + +const error = { + ruleId: 'no-self-import', + message: 'Module imports itself.', +} + +ruleTester.run('no-self-import', rule, { + valid: [ + test({ + code: 'import _ from "lodash"', + filename: testFilePath('./no-self-import.js'), + }), + test({ + code: 'import find from "lodash.find"', + filename: testFilePath('./no-self-import.js'), + }), + test({ + code: 'import foo from "./foo"', + filename: testFilePath('./no-self-import.js'), + }), + test({ + code: 'import foo from "../foo"', + filename: testFilePath('./no-self-import.js'), + }), + test({ + code: 'import foo from "foo"', + filename: testFilePath('./no-self-import.js'), + }), + test({ + code: 'import foo from "./"', + filename: testFilePath('./no-self-import.js'), + }), + test({ + code: 'import foo from "@scope/foo"', + filename: testFilePath('./no-self-import.js'), + }), + test({ + code: 'var _ = require("lodash")', + filename: testFilePath('./no-self-import.js'), + }), + test({ + code: 'var find = require("lodash.find")', + filename: testFilePath('./no-self-import.js'), + }), + test({ + code: 'var foo = require("./foo")', + filename: testFilePath('./no-self-import.js'), + }), + test({ + code: 'var foo = require("../foo")', + filename: testFilePath('./no-self-import.js'), + }), + test({ + code: 'var foo = require("foo")', + filename: testFilePath('./no-self-import.js'), + }), + test({ + code: 'var foo = require("./")', + filename: testFilePath('./no-self-import.js'), + }), + test({ + code: 'var foo = require("@scope/foo")', + filename: testFilePath('./no-self-import.js'), + }), + test({ + code: 'var bar = require("./bar/index")', + filename: testFilePath('./no-self-import.js'), + }), + test({ + code: 'var bar = require("./bar")', + filename: testFilePath('./bar/index.js'), + }), + test({ + code: 'var bar = require("./bar")', + filename: '', + }), + ], + invalid: [ + test({ + code: 'import bar from "./no-self-import"', + errors: [error], + filename: testFilePath('./no-self-import.js'), + }), + test({ + code: 'var bar = require("./no-self-import")', + errors: [error], + filename: testFilePath('./no-self-import.js'), + }), + test({ + code: 'var bar = require("./no-self-import.js")', + errors: [error], + filename: testFilePath('./no-self-import.js'), + }), + test({ + code: 'var bar = require(".")', + errors: [error], + filename: testFilePath('./index.js'), + }), + test({ + code: 'var bar = require("./")', + errors: [error], + filename: testFilePath('./index.js'), + }), + test({ + code: 'var bar = require("././././")', + errors: [error], + filename: testFilePath('./index.js'), + }), + test({ + code: 'var bar = require("../no-self-import-folder")', + errors: [error], + filename: testFilePath('./no-self-import-folder/index.js'), + }), + ], +})