CircularDependencyRspackPlugin
Detects circular import dependencies between modules that will exist at runtime. Import cycles are often not indicative of a bug when used by functions called at runtime, but may cause bugs or errors when the imports are used during initialization. This plugin does not distinguish between the two, but can be used to help identify and resolve cycles after a bug has been encountered.
Comparison
This plugin is an adaptation of the original circular-dependency-plugin for Webpack and diverges in both behavior and features.
Performance
Because CircularDependencyRspackPlugin is implemented in Rust, it is able to integrate directly with the module graph and avoid expensive copying and serialization. On top of that, this plugin operates using a single traversal of the module graph for each entrypoint to identify all cycles rather than checking each module individually. Combined, that means CircularDependencyRspackPlugin is able to run fast enough to be part of a hot reload development cycle without any noticeable impact on reload times, even for extremely large projects with hundreds of thousands of modules and imports.
Features
CircularDependencyRspackPlugin aims to be compatible with the features of circular-dependency-plugin, with modifications to give finer control over cycle detection and behavior.
One notable difference between the two is the use of module identifiers for cycle entries in Rspack rather than relative paths. Identifiers represent the entire unique name for a bundled module, including the set of loaders that processed it, the absolute module path, and any request parameters that were provided when importing. While matching on just the path of the module is still possible, identifiers allow for matching against loaders and rulesets as well.
This plugin also provides a new option, ignoredConnections to allow for more granular control over whether a cycle is ignored. The exclude option from the original plugin is still implemented to match existing behavior, but causes any cycle containing the module to be ignored entirely. When only specific cycles are meant to be ignored, ignoredConnections allows for specifying both a from and a to pattern to match against, ignoring only cycles where an explicit dependency between two modules is present.
Examples
- Detect all cycles present in a compilation, ignoring any that include external packages from
node_modules, and emitting compilation errors for each cycle.
- Ignore a specific connection between two modules, as well as any connection
toany module matching a given pattern.
- Allow asynchronous cycles (such as
import('some/module')) and manually handle all detected cycles.
Options
failOnError
- Type:
boolean - Default:
false
When true, detected cycles will generate Error level diagnostics rather than Warnings. This will have no noticeable effect in watch mode, but will cause full builds to fail when the errors are emitted.
allowAsyncCycles
- Type:
boolean - Default:
false
Allow asynchronous imports and connections to cause a detected cycle to be ignored. Asynchronous imports include import() function calls, weak imports for lazy compilation, hot module connections, and more.
exclude
- Type:
RegExp - Default:
undefined
Similar to exclude from the original circular-dependency-plugin, detected cycles containing any module resource path that matches this pattern will be ignored.
ignoredConnections
- Type:
Array<[string | RegExp, string | RegExp]> - Default:
[]
A list of explicit connections that should cause a detected cycle to be ignored. Each entry in the list represents a connection as [from, to], matching any connection where from depends on to.
Each pattern can be represented as either a plain string or a RegExp. Plain strings will be matched as substrings against the module identifier for that part of a connection, and RegExps will match anywhere in the entire identifier. For example:
- The string
'some/module/'will match any module in thesome/moduledirectory, likesome/module/aandsome/module/b. - The RegExp
!file-loader!.*\.mdxwill match any.mdxmodule processed byfile-loader. - Empty strings effectively match any module, since an empty string is always a substring of any other string.
onDetected
- Type:
(entrypoint: string, modules: string[], compilation: Compilation) => void - Default:
undefined
Handler function called for every detected cycle. Providing this handler overrides the default behavior of adding diagnostics to the compilation, meaning the value of failOnError will be effectively unused.
This handler can be used to process detected cycles further before emitting warnings or errors to the compilation, or to handle them in any other way not directly implemented by the plugin.
entrypoint is the name of the entry where this cycle was detected. Because of how entrypoints are traversed for cycle detection, it's possible that the same cycle will be detected multiple times, once for each entrypoint.
modules is the list of identifiers of module contained in the cycle, where both the first and the last entry of the list will always be the same module, and the only module that is present more than once in the list.
compilation is the full Compilation object, allowing the handler to emit errors or inspect any other part of the bundle as needed.
onIgnored
- Type:
(entrypoint: string, modules: string[], compilation: Compilation) => void - Default:
undefined
Handler function called for every detected cycle that was intentionally ignored, whether by the exclude pattern, any match of an ignoredConnection, or any other possible reason.
onStart
- Type:
(compilation: Compilation) => void - Default:
undefined
Hook function called immediately before cycle detection begins, useful for setting up temporary state to use in the onDetected handler or logging progress.
onEnd
- Type:
(compilation: Compilation) => void - Default:
undefined
Hook function called immediately after cycle detection finishes, useful for cleaning up temporary state or logging progress.

