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
padis the definition; the importedpadis 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
functiondeclaration is there and the code appears to work; aconst, aclass, or anything assigned during evaluation is still in its temporal dead zone, so the same shape throwsReferenceError— or yieldsundefinedthrough 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.