deslop/lazy-import-at-top-level

Flag a dynamic import() at module top level that is awaited or .then/.catch/.finally-ed during load (no laziness benefit); prefer a static import.

  • Category: Correctness
  • Severity: warn
  • Source: deslop-js
  • Framework: global
  • Enabled when: react-doctor deadCode analysis enabled (default true); whole-project scan only — skipped in --diff/--staged modes
  • Documentation: https://github.com/millionco/deslop-js

Validation prompt

Use this to decide whether a fired diagnostic is real or a false positive.

Fires when deslop walks the top-level program body and finds a dynamic import settled during module evaluation: kind top-level-await-import (high confidence; a top-level await import("x") in a VariableDeclaration init or ExpressionStatement, reason 'top-level await import("x") runs synchronously before the module finishes loading anyway — there is no laziness benefit, prefer a static import') or kind top-level-then-import (medium confidence; a top-level import("x").then/.catch/.finally(...) statement — the detector matches the member property then, catch, or finally — reason 'top-level import("x").then(...) runs at module evaluation — prefer a static import and a regular function call unless the dynamic-import contract is intentional'). It never fires on import() inside a function, handler, lazy(), or route loader, because the detector only walks programNode.body. False positive to SUPPRESS: the top-level await is deliberate sequencing, not failed code-splitting — e.g. it conditionally selects an implementation at load (await import(isProd ? "./prod" : "./dev")), gates init on an env/feature check, or loads an optional/native peer dep inside try/catch so the module can be skipped on platforms where it is absent.

Fix prompt

Use this once validation confirms the diagnostic is real.

When the import is unconditional, replace the top-level dynamic import with a static one: turn const m = await import("./mod") into import * as m from "./mod" (or the named/default form), and turn import("./mod").then(fn) into import mod from "./mod" plus a direct fn(mod) call — a top-level await/then already blocks module evaluation, so the static form has identical timing but is statically analyzable and tree-shakeable. Keep dynamic import() only when you actually want the chunk split off and loaded later, in which case move the import() inside the function, event handler, route loader, or React.lazy(() => import(...)) that should trigger it. See https://react.dev/reference/react/lazy