Back to Biomejs

noImportCycles

src/content/docs/linter/rules/no-import-cycles.mdx

latest10.0 KB
Original Source

import { Tabs, TabItem } from '@astrojs/starlight/components';

<Tabs> <TabItem label="JavaScript (and super languages)" icon="seti:javascript"> :::note This rule belongs to the project domain. This means that its activation will activate the Biome Scanner to scan the files of your project. Read more about it in the [documentation page](/linter/domains#project) ::: ## Summary - Rule available since: `v2.0.0` - Diagnostic Category: [`lint/suspicious/noImportCycles`](/reference/diagnostics#diagnostic-category) - This rule isn't recommended, so you need to enable it. - This rule doesn't have a fix. - The default severity of this rule is [**warning**](/reference/diagnostics#warning). - This rule belongs to the following domains: - [`project`](/linter/domains#project) - Sources: - Same as [`import/no-cycle`](https://github.com/import-js/eslint-plugin-import/blob/main/docs/rules/no-cycle.md)

How to configure

json
{
	"linter": {
		"rules": {
			"suspicious": {
				"noImportCycles": "error"
			}
		}
	}
}

Description

Prevent import cycles.

This rule warns when a file imports another file that, either directly or indirectly, imports the original file again.

Cycles can lead to symbols that are unexpectedly undefined and are generally considered poor code hygiene.

If a cycle is detected, it is advised to move code such that imports only go in a single direction, i.e. they don't point "back" to the importing file.

However, files that import themselves are allowed, and the rule won't trigger for these use cases. This allows for encapsulation of functions/variables into a namespace instead of using a static class (triggers noStaticOnlyClass).

:::note This rule is computationally expensive. If you are particularly pressed for lint time, or don't think you have an issue with dependency cycles, you may not want this rule enabled. :::

Examples

Invalid

js
import { baz } from "./baz.js";

export function foo() {
    baz();
}

export function bar() {
    console.log("foobar");
}
<pre class="language-text"><code class="language-text"><a href="file:///foobar.js">/foobar.js</a>:1:21 <a href="https://biomejs.dev/linter/rules/no-import-cycles">lint/suspicious/noImportCycles</a> ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ <strong><span style="color: Orange;">⚠</span></strong> <span style="color: Orange;">This import is part of a cycle.</span> <strong><span style="color: Tomato;">&gt;</span></strong> <strong>1 │ </strong>import &#123; baz &#125; from &quot;./baz.js&quot;; <strong> │ </strong> <strong><span style="color: Tomato;">^</span></strong><strong><span style="color: Tomato;">^</span></strong><strong><span style="color: Tomato;">^</span></strong><strong><span style="color: Tomato;">^</span></strong><strong><span style="color: Tomato;">^</span></strong><strong><span style="color: Tomato;">^</span></strong><strong><span style="color: Tomato;">^</span></strong><strong><span style="color: Tomato;">^</span></strong><strong><span style="color: Tomato;">^</span></strong><strong><span style="color: Tomato;">^</span></strong> <strong>2 │ </strong> <strong>3 │ </strong>export function foo() &#123; <strong><span style="color: lightgreen;">ℹ</span></strong> <span style="color: lightgreen;">This import resolves to </span><span style="color: lightgreen;"><span style="color: lightgreen;">/baz.js</span></span><span style="color: lightgreen;"> </span> <span style="color: lightgreen;"> ... which imports </span><span style="color: lightgreen;"><span style="color: lightgreen;">/foobar.js</span></span><span style="color: lightgreen;"> </span> <span style="color: lightgreen;"> ... which is the file we're importing from.</span> </code></pre>
js
import { bar } from "./foobar.js";

export function baz() {
    bar();
}
<pre class="language-text"><code class="language-text"><a href="file:///baz.js">/baz.js</a>:1:21 <a href="https://biomejs.dev/linter/rules/no-import-cycles">lint/suspicious/noImportCycles</a> ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ <strong><span style="color: Orange;">⚠</span></strong> <span style="color: Orange;">This import is part of a cycle.</span> <strong><span style="color: Tomato;">&gt;</span></strong> <strong>1 │ </strong>import &#123; bar &#125; from &quot;./foobar.js&quot;; <strong> │ </strong> <strong><span style="color: Tomato;">^</span></strong><strong><span style="color: Tomato;">^</span></strong><strong><span style="color: Tomato;">^</span></strong><strong><span style="color: Tomato;">^</span></strong><strong><span style="color: Tomato;">^</span></strong><strong><span style="color: Tomato;">^</span></strong><strong><span style="color: Tomato;">^</span></strong><strong><span style="color: Tomato;">^</span></strong><strong><span style="color: Tomato;">^</span></strong><strong><span style="color: Tomato;">^</span></strong><strong><span style="color: Tomato;">^</span></strong><strong><span style="color: Tomato;">^</span></strong><strong><span style="color: Tomato;">^</span></strong> <strong>2 │ </strong> <strong>3 │ </strong>export function baz() &#123; <strong><span style="color: lightgreen;">ℹ</span></strong> <span style="color: lightgreen;">This import resolves to </span><span style="color: lightgreen;"><span style="color: lightgreen;">/foobar.js</span></span><span style="color: lightgreen;"> </span> <span style="color: lightgreen;"> ... which imports </span><span style="color: lightgreen;"><span style="color: lightgreen;">/baz.js</span></span><span style="color: lightgreen;"> </span> <span style="color: lightgreen;"> ... which is the file we're importing from.</span> </code></pre>

Valid

js
import { baz } from "./baz.js";

export function foo() {
    baz();
}
js
export function bar() {
    console.log("foobar");
}
js
import { bar } from "./bar.js";

export function baz() {
    bar();
}
js
export function foo() {
    console.log("foobaz");
}

export * as baz from './foobaz.js';

import { baz } from './foobaz.js';
ts
import type { bar } from "./qux.ts";

export type Foo = {
  bar: typeof bar;
};
ts
import type { Foo } from "./types.ts";

export function bar(foo: Foo) {
    console.log(foo);
}

Options

The rule provides the options described below.

ignoreTypes

Ignores type-only imports when finding an import cycle. A type-only import (import type) will be removed by the compiler, so it cuts an import cycle at runtime. Note that named type imports (import { type Foo }) aren't considered as type-only because it's not removed by the compiler if the verbatimModuleSyntax option is enabled. Enabled by default.

json
{
	"linter": {
		"rules": {
			"suspicious": {
				"noImportCycles": {
					"options": {
						"ignoreTypes": false
					}
				}
			}
		}
	}
}

Invalid

ts
import type { bar } from "./qux.ts";

export type Foo = {
  bar: typeof bar;
};
ts
import type { Foo } from "./types.ts";

export function bar(foo: Foo) {
    console.log(foo);
}
<pre class="language-text"><code class="language-text"><a href="file:///qux.ts">/qux.ts</a>:1:26 <a href="https://biomejs.dev/linter/rules/no-import-cycles">lint/suspicious/noImportCycles</a> ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ <strong><span style="color: Orange;">⚠</span></strong> <span style="color: Orange;">This import is part of a cycle.</span> <strong><span style="color: Tomato;">&gt;</span></strong> <strong>1 │ </strong>import type &#123; Foo &#125; from &quot;./types.ts&quot;; <strong> │ </strong> <strong><span style="color: Tomato;">^</span></strong><strong><span style="color: Tomato;">^</span></strong><strong><span style="color: Tomato;">^</span></strong><strong><span style="color: Tomato;">^</span></strong><strong><span style="color: Tomato;">^</span></strong><strong><span style="color: Tomato;">^</span></strong><strong><span style="color: Tomato;">^</span></strong><strong><span style="color: Tomato;">^</span></strong><strong><span style="color: Tomato;">^</span></strong><strong><span style="color: Tomato;">^</span></strong><strong><span style="color: Tomato;">^</span></strong><strong><span style="color: Tomato;">^</span></strong> <strong>2 │ </strong> <strong>3 │ </strong>export function bar(foo: Foo) &#123; <strong><span style="color: lightgreen;">ℹ</span></strong> <span style="color: lightgreen;">This import resolves to </span><span style="color: lightgreen;"><span style="color: lightgreen;">/types.ts</span></span><span style="color: lightgreen;"> </span> <span style="color: lightgreen;"> ... which imports </span><span style="color: lightgreen;"><span style="color: lightgreen;">/qux.ts</span></span><span style="color: lightgreen;"> </span> <span style="color: lightgreen;"> ... which is the file we're importing from.</span> </code></pre> </TabItem> </Tabs>