When building a React/TypeScript application, maintaining clean and modular code is important. As the codebase grows, circular dependencies can easily slip in, causing unexpected bugs, performance issues, and making the app harder to maintain.
Circular dependencies can be a real headache for developers. I’ve faced this myself while working on a legacy project riddled with them. In this article, I’ll share my experience for tracking circular dependencies in CI environment.
Why Are Circular Dependencies a Problem?
- Unpredictable Behavior: JavaScript may load incomplete or undefined modules due to the circular reference.
- Hard to Debug: Circular dependencies can lead to subtle bugs that are difficult to trace.
Introducing Madge
Fortunately, there's a tool called Madge that can detect circular dependencies.
Setting Up Madge
To start using Madge, install it via npm
or yarn
:
npm install --global madge
or
yarn global add madge
In the root of your project directory, run the following command:
madge --circular src/
This command analyzes the src/
directory and outputs any circular dependencies it finds.
Sample Output:
✖ Found 2 circular dependencies!
1) moduleA.ts -> moduleB.ts -> moduleA.ts
2) moduleC.ts -> moduleD.ts -> moduleE.ts -> moduleC.ts
Configuring
To ensure Madge works properly with TypeScript and path mappings specify file extensions and point Madge to your tsconfig.json
.
madge --circular --ts-config ./tsconfig.json --extensions ts,tsx src/
Command Breakdown:
-
--circular
: Detects circular dependencies. -
--ts-config ./tsconfig.json
: Directs Madge to use your TypeScript configuration. -
--extensions ts,tsx
: Ensures Madge scans.ts
and.tsx
files. -
src/
: The directory to scan.
Also you might Madge to ignore TypeScript type imports and dynamic (async) imports. To achieve that you can add the following configuration to package.json
.
"madge": {
"detectiveOptions": {
"ts": {
"skipAsyncImports": true,
"skipTypeImports": true
},
"tsx": {
"skipAsyncImports": true,
"skipTypeImports": true
}
}
}
Automating Circular Dependency Detection
The goal is to catch circular dependencies before they are merged into the main branch.
Step 1: Creating a Bash Script for Circular Dependency Checks
First, let's create a script that will check for circular dependencies and set a limit on how many are allowed. This script can be particularly useful if you’re dealing with an existing project with legacy circular dependencies, allowing you to monitor that no new ones are added:
Create a Bash script (check_circular_deps.sh
) to run Madge with an error threshold:
#!/bin/bash
if ! command -v madge &> /dev/null
then
echo "Madge is not installed. Please install it first."
exit 1
fi
if [ -z "$1" ]; then
echo "Usage: $0 <error_limit>"
exit 1
fi
error_limit=$1
stderr_output=$(madge --circular --no-spinner --no-color --ts-config ./tsconfig.json --extensions ts,tsx ./src/index.tsx 2>&1 >/dev/null)
if echo "$stderr_output" | grep -q 'Found [0-9]\+ circular dependencies'; then
circular_count=$(echo "$stderr_output" | grep -o 'Found [0-9]\+ circular dependencies' | grep -o '[0-9]\+')
else
circular_count=0
fi
echo "Number of circular dependencies: $circular_count"
if [ "$circular_count" -gt "$error_limit" ]; then
echo "Error: Circular dependency count ($circular_count) exceeds the limit ($error_limit)."
exit 1
fi
echo "Circular dependency count is within the limit."
Step 2: Adding a Script to package.json
To make it easier to run the check within the CI pipeline, add a script to your package.json:
{
"scripts": {
"check-circular-deps": "bash ./scripts/check_circular_deps.sh 0"
}
}
In this example, the number 0 is the threshold for allowed circular dependencies. Adjust this number based on your project’s current state.
Step 3: Setting Up CI Pipeline
Next, modify your pipelines configuration to include a job that runs this circular dependency check.
Here’s an example configuration for GitLab:
stages:
- test
circular-dependency-check:
stage: test
image: node:18
script:
- npm install madge -g # Install Madge globally
- npm install # Install project dependencies
- npm run check-circular-deps # Run the circular dependency check
allow_failure: false # Fail the pipeline if circular dependencies exceed the limit
Conclusion
Circular dependencies can sneak into your code and cause all sorts of problems. They can lead to weird bugs, slow down your app, and make it a pain to work with. Luckily, you can automate the process of finding and fixing these pesky dependencies, making your app more stable and easier to maintain.
Top comments (0)