Self Import

ID

javascript.self_import

Severity

high

Remediation Complexity

trivial

Remediation Risk

low

Remediation Effort

low

Resource

Reliability

Language

JavaScript

Tags

dead-code, imports, reliability

Description

Reports an import whose specifier resolves to the file that contains it.

Only a relative specifier is resolved: ./x, ../x, . and ... A bare specifier is a package name, and a path alias is resolved by configuration this rule does not read, so neither is compared. A directory specifier is resolved through the index convention, which is how index.js manages to import itself with import { x } from '.'.

Resolution is lexical, on canonical paths with the source extension stripped, so ./format, ./format.js and ../src/format.ts all compare equal to src/format.ts.

Rationale

// in src/format.js
// Bad
import { pad } from './format.js';

export function pad(value) {
  return String(value).padStart(2, '0');
}
  • A module importing itself is never intentional. It is what a copy-pasted import line, a rename, or a half-done file split leaves behind.

  • It creates two names for the same thing, and they drift. The local pad is the definition; the imported pad is a binding into this module’s own namespace, and a later edit to one of them silently misses the other.

  • It is quietly dependent on evaluation order. A self-import is a cycle of length one: the module is already being evaluated when its own import is resolved, so the imported binding is whatever the module has published so far. A hoisted function declaration is there and the code appears to work; a const, a class, or anything assigned during evaluation is still in its temporal dead zone, so the same shape throws ReferenceError — or yields undefined through a bundler’s CommonJS interop — depending on nothing a reader can see.

javascript.cyclic_package_dependencies does not catch this. It reports strongly connected components of more than one module, so a self-loop — a component of exactly one — falls through it by construction.

Remediation

Delete the import and use the local declaration; or, if the name really came from somewhere else, point the import at the module that defines it.

// Good
export function pad(value) {
  return String(value).padStart(2, '0');
}

When the file has grown to the point where importing "its own" helpers felt natural, that is the signal to split it: move the helpers into their own module and import them from there, so the two names belong to two files.

© 2026 Xygeni. All rights reserved