feat: add ESM-compatible circular import resolution #250
+43,687
−8,282
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
ESM-Compatible Circular Import Resolution
Fixes unlight/prisma-nestjs-graphql#245
Problem
When using ES Modules (ESM) with the generated GraphQL types, Node.js throws
ReferenceError: Cannot access 'X' before initializationerrors due to circular dependencies between generated input types. This is particularly common with:*WhereInput↔*ListRelationFilterrelationshipsAND,OR,NOTfields)CommonJS handles these circular dependencies differently due to its synchronous module loading, but ESM's hoisted imports cause the initialization order issues.
Solution
This PR introduces a lazy type registry pattern that breaks circular dependencies at runtime while maintaining full TypeScript type safety.
New Configuration Option
How It Works
Generated Code Changes
Before (causes circular import errors in ESM):
After (ESM compatible):
New Generated Files
When
esmCompatible = true:type-registry.ts- ProvidesregisterType()andgetType()functions for lazy type resolutionregister-all-types.ts- Imports all generated files to ensure type registrationUsage
Add this import at the top of your application entry point (e.g.,
app.module.tsormain.ts):Files Changed
New Files
src/handlers/type-registry.ts- Generates the type registry utilitysrc/handlers/register-all-types.ts- Generates the central registration filesrc/helpers/detect-circular-deps.ts- Circular dependency detection utilitiessrc/test/esm-circular-deps.spec.ts- Unit tests for circular dependency detectionModified Files
src/generate.ts- Integrated circular dependency detection and new generatorssrc/handlers/input-type.ts- Added ESM-compatible type generation for inputssrc/handlers/model-output-type.ts- Added ESM-compatible type generation for modelssrc/handlers/output-type.ts- Fixed Decimal import pathsrc/helpers/import-declaration-map.ts- AddedaddType()method for type-only importssrc/helpers/create-config.ts- AddedesmCompatibleconfiguration optionsrc/types.ts- AddedcircularDependenciesto event arguments, updated DMMF imports for Prisma v7Dependency Updates
@prisma/generator-helperto^7.0.0for Prisma v7 compatibility@prisma/clientpeer dependency to support v7ts-morphrange to11 - 24.npmrcwithlegacy-peer-deps=truefor peer dependency resolutionTechnical Details
Circular Dependency Detection
The generator builds a dependency graph from the Prisma schema and uses DFS to detect cycles. Only types involved in circular dependencies use the lazy loading pattern, minimizing the performance impact.
Type Registry
Lazy Field Decorator
The
getType()function returns a thunk that's evaluated lazily by NestJS when building the GraphQL schema:The double function call
getType('TypeName')()is intentional:getType('TypeName')returns a function() => registry.get('TypeName')() => ...ensures NestJS evaluates it lazily()call retrieves the actual type from the registryTesting
Breaking Changes
None. The
esmCompatibleoption is opt-in and defaults tofalse, maintaining backward compatibility with existing CommonJS setups.Migration Guide
Update your generator configuration:
Regenerate your types:
Add the registration import to your app entry point:
Remove any manually defined enums that duplicate generated ones to avoid "Schema must contain uniquely named types" errors.