Skip to Content
ErrorsNEXUS_MODULE_IMPORT_CYCLE

This page documents the 0.4 release candidates of @nexusdi/core. The newest on npm is 0.4.0-rc.1. The documentation for 0.3.2, the current release, is at nexus.js.org. Try the release candidate.

NEXUS_MODULE_IMPORT_CYCLE

NEXUS_MODULE_IMPORT_CYCLE means modules import each other in a loop. Nexus.create reports it inside NEXUS_BLUEPRINT_INVALID. The imports of a defineModule module exist before it does, so a loop only appears with module classes from @Module or declareModuleClass, which can name an import that is declared later.

Reproduce NEXUS_MODULE_IMPORT_CYCLE

The region declares Engineering importing Tactical and Tactical importing Engineering. Without a plugin, core prints a one-line message that starts with the code and ends with the address of this page. With errors() from @nexusdi/errors registered (npm install @nexusdi/errors@next), the same error carries the full text and its fix.

import { Nexus, declareModuleClass, defineModule } from '@nexusdi/core'; import { isNexusError } from '@nexusdi/core'; import { errors } from '@nexusdi/errors'; // Two module classes that import each other. @Module from // @nexusdi/decorators writes the same declaration. class Engineering {} class Tactical {} declareModuleClass(Engineering, { name: 'Engineering', imports: [Tactical] }); declareModuleClass(Tactical, { name: 'Tactical', imports: [Engineering] }); const Meridian = defineModule({ name: 'Meridian', imports: [Engineering] }); const thin = await Nexus.create(Meridian).catch((error: unknown) => error); if (!isNexusError(thin, 'NEXUS_BLUEPRINT_INVALID')) throw thin; const line = thin.errors[0]?.message; // -> '[NEXUS_MODULE_IMPORT_CYCLE] path=Engineering,Tactical,Engineering. https://nexus.js.org/errors/NEXUS_MODULE_IMPORT_CYCLE' console.log(line); const full = await Nexus.create(Meridian, { plugins: [errors()] }).catch( (error: unknown) => error, ); if (!isNexusError(full, 'NEXUS_BLUEPRINT_INVALID')) throw full; const text = full.errors[0]?.message.split('\n'); // -> ['[NEXUS_MODULE_IMPORT_CYCLE] Engineering → Tactical → Engineering is a module import cycle.', ' Fix: move the shared providers into a module that both import.'] console.log(text);

Try it: save the region as error.ts and run npx tsx error.ts. It prints the one-line message, then the full text as an array of two lines. The path field lists the modules around the loop. Then save the fix region below as fixed.ts and run npx tsx fixed.ts. It prints online.

Fix NEXUS_MODULE_IMPORT_CYCLE

NEXUS_MODULE_IMPORT_CYCLE is fixed by moving the providers both modules need into a third module that both import. Here Core provides COMPUTER, and Engineering and Tactical import Core.

import { Nexus, Token, defineModule, provide } from '@nexusdi/core'; interface IShipComputer { status(): string; } const COMPUTER = new Token<IShipComputer>('ShipComputer'); class QuantumComputer implements IShipComputer { status() { return 'online'; } } // The provider both modules needed moves to a module that both import. const Core = defineModule({ name: 'Core', providers: [provide(COMPUTER, { useClass: QuantumComputer })], exports: [COMPUTER], }); const Engineering = defineModule({ name: 'Engineering', imports: [Core] }); const Tactical = defineModule({ name: 'Tactical', imports: [Core] }); const Meridian = defineModule({ name: 'Meridian', imports: [Engineering, Tactical, Core], }); await using ship = await Nexus.create(Meridian); const status = ship.get(COMPUTER).status(); // -> 'online' console.log(status);

Try it: the fix region has no loop left to break by deleting a line. To bring the error back, run the reproduce region again: its two module classes import each other, and the program prints the one-line message with path=Engineering,Tactical,Engineering. Move the shared providers into Core as the fix region does and the program prints online.

Fields of ModuleImportCycleError

ModuleImportCycleError has one field:

  • path: the module names around the loop, ending with the first one again.