Dependency Impact Analyzer
Before upgrading or removing a dependency, understand exactly what will be affected. Traces all imports, finds every file that uses the package, checks test coverage of impacted code, and identifies potential breaking changes.
Use when: "what happens if I upgrade X", "impact of removing lodash", "can I safely update this", "dependency blast radius", "which files use package X", "upgrade risk assessment", or before major dependency changes.
Commands
1. trace — Trace All Usages of a Dependency
Find every file that imports or requires the package, and how it's used.
PACKAGE="${1:?Usage: trace <package-name>}"
echo "=== Tracing usage of: $PACKAGE ==="
# Direct imports (JS/TS)
echo "--- Direct Imports ---"
rg -n "from ['\"]$PACKAGE['\"/]|require\(['\"]$PACKAGE['\"/]\)|import ['\"]$PACKAGE['\"]" \
-g '*.{js,ts,jsx,tsx,mjs,cjs}' -g '!node_modules' -g '!dist' -g '!build' 2>/dev/null
# Subpath imports
echo "--- Subpath Imports ---"
rg -n "from ['\"]$PACKAGE/" \
-g '*.{js,ts,jsx,tsx}' -g '!node_modules' -g '!dist' 2>/dev/null
# Python imports
rg -n "^import $PACKAGE|^from $PACKAGE" \
-g '*.py' -g '!vendor' -g '!dist' 2>/dev/null
# Go imports
rg -n "\".*$PACKAGE" \
-g '*.go' -g '!vendor' 2>/dev/null
# Count total usage
USAGE_COUNT=$(rg -c "$PACKAGE" \
-g '!node_modules' -g '!vendor' -g '!dist' -g '!*.lock' -g '!package-lock.json' \
--type-not binary 2>/dev/null | awk -F: '{s+=$2} END {print s+0}')
FILE_COUNT=$(rg -l "$PACKAGE" \
-g '!node_modules' -g '!vendor' -g '!dist' -g '!*.lock' -g '!package-lock.json' \
--type-not binary 2>/dev/null | wc -l)
echo ""
echo "Total: $USAGE_COUNT references across $FILE_COUNT files"
Extract which specific exports/APIs are used:
# What functions/classes are imported from this package?
rg -o "import \{([^}]+)\} from ['\"]$PACKAGE['\"]" \
-g '*.{ts,js,tsx,jsx}' -g '!node_modules' 2>/dev/null | \
sed "s/.*{//;s/}.*//" | tr ',' '\n' | sed 's/^\s*//;s/\s*$//' | sort | uniq -c | sort -rn
rg -o "const \{([^}]+)\} = require\(['\"]$PACKAGE['\"]\)" \
-g '*.{js,ts}' -g '!node_modules' 2>/dev/null | \
sed "s/.*{//;s/}.*//" | tr ',' '\n' | sed 's/^\s*//;s/\s*$//' | sort | uniq -c | sort -rn
2. impact — Full Impact Assessment
Comprehensive analysis of what upgrading this dependency means.
Current Version and Target
PACKAGE="${1:?Usage: impact <package-name> [target-version]}"
TARGET_VERSION="${2:-latest}"
# Current version
if [ -f "package.json" ]; then
CURRENT=$(python3 -c "
import json
d = json.load(open('package.json'))
v = d.get('dependencies',{}).get('$PACKAGE') or d.get('devDependencies',{}).get('$PACKAGE') or 'not found'
print(v)
" 2>/dev/null)
echo "Current: $PACKAGE@$CURRENT"
fi
if [ -f "requirements.txt" ]; then
grep -i "^$PACKAGE" requirements.txt 2>/dev/null
fi
Breaking Changes Detection
# Check package changelog/releases for breaking changes
echo "--- Checking for breaking changes ---"
# For npm packages: check if major version changed
if [ -f "package.json" ]; then
npm info "$PACKAGE" versions --json 2>/dev/null | python3 -c "
import json, sys, re
try:
versions = json.load(sys.stdin)
current = '$CURRENT'.lstrip('^~>=')
current_major = current.split('.')[0] if current != 'not found' else '0'
latest = versions[-1] if isinstance(versions, list) else versions
latest_major = latest.split('.')[0]
if current_major != latest_major:
print(f'⚠️ MAJOR version change: {current} → {latest} (likely breaking changes)')
else:
print(f'✅ Same major version: {current} → {latest} (should be backward compatible)')
except Exception as e:
print(f'Could not check versions: {e}')
" 2>/dev/null
fi
# Check npm deprecation
npm info "$PACKAGE" deprecated 2>/dev/null | grep -v "^$" && echo "⚠️ Package is DEPRECATED"
Affected Test Coverage
# Find files that import the package
AFFECTED_FILES=$(rg -l "$PACKAGE" \
-g '!node_modules' -g '!vendor' -g '!dist' -g '!*.lock' -g '!*.test.*' -g '!*.spec.*' \
-g '*.{js,ts,jsx,tsx,py,go}' --type-not binary 2>/dev/null)
echo "--- Test Coverage of Affected Files ---"
for f in $AFFECTED_FILES; do
BASE=$(basename "$f" | sed 's/\.[^.]*$//')
DIR=$(dirname "$f")
# Look for corresponding test file
TEST=$(find "$DIR" -maxdepth 2 \( -name "${BASE}.test.*" -o -name "${BASE}.spec.*" -o -name "test_${BASE}.*" \) \
-not -path '*/node_modules/*' 2>/dev/null | head -1)
if [ -n "$TEST" ]; then
echo "✅ $f → $TEST"
else
echo "❌ $f → NO TEST (upgrade risk!)"
fi
done
Dependency Tree Impact
# What other packages depend on this one?
echo "--- Reverse Dependencies ---"
if [ -f "package-lock.json" ]; then
python3 -c "
import json
lock = json.load(open('package-lock.json'))
pkg = '$PACKAGE'
rdeps = []
packages = lock.get('packages', lock.get('dependencies', {}))
for name, info in packages.items():
deps = info.get('dependencies', {})
if pkg in deps:
clean_name = name.replace('node_modules/', '')
rdeps.append(f'{clean_name} (requires {pkg}@{deps[pkg]})')
if rdeps:
print(f'{len(rdeps)} packages also depend on {pkg}:')
for r in rdeps[:20]:
print(f' {r}')
else:
print(f'No other packages depend on {pkg} (leaf dependency)')
" 2>/dev/null
fi
3. remove — Safe Removal Analysis
What happens if you completely remove this dependency?
PACKAGE="${1:?Usage: remove <package-name>}"
echo "=== Removal Impact: $PACKAGE ==="
# All files that would break
BREAKING_FILES=$(rg -l "from ['\"]$PACKAGE|require\(['\"]$PACKAGE|import $PACKAGE|from $PACKAGE" \
-g '!node_modules' -g '!vendor' -g '!dist' -g '!*.lock' 2>/dev/null)
FILE_COUNT=$(echo "$BREAKING_FILES" | grep -c "." 2>/dev/null || echo "0")
echo "Files that would break: $FILE_COUNT"
echo "$BREAKING_FILES" | head -20
# Suggest alternatives
echo ""
echo "--- Alternative Packages ---"
echo "Use AI reasoning to suggest alternatives based on:"
echo "1. What specific features of $PACKAGE are used (from trace output)"
echo "2. Whether native/stdlib alternatives exist"
echo "3. What the community has migrated to"
4. replace — Migration Plan for Swapping Packages
Generate a migration plan for replacing one package with another.
OLD="${1:?Usage: replace <old-package> <new-package>}"
NEW="${2:?Usage: replace <old-package> <new-package>}"
echo "=== Migration Plan: $OLD ��� $NEW ==="
# Trace old package usage
echo "--- Current API Usage ($OLD) ---"
rg -o "import \{[^}]+\} from ['\"]$OLD['\"]" \
-g '*.{ts,js,tsx,jsx}' -g '!node_modules' 2>/dev/null | head -20
echo ""
echo "--- Files to Modify ---"
rg -l "$OLD" -g '!node_modules' -g '!vendor' -g '!dist' -g '!*.lock' 2>/dev/null
Generate AI-powered migration plan:
- Map old API → new API (function name equivalents)
- Identify APIs with no direct equivalent (need rewrite)
- Estimate effort per file
- Suggest migration order (tests first, then source)
5. audit — Dependency Portfolio Risk
Assess the overall risk profile of all dependencies.
echo "=== Dependency Risk Audit ==="
if [ -f "package.json" ]; then
python3 -c "
import json, subprocess, re
from datetime import datetime
d = json.load(open('package.json'))
all_deps = {**d.get('dependencies',{}), **d.get('devDependencies',{})}
print(f'Total dependencies: {len(all_deps)}')
print(f' Production: {len(d.get(\"dependencies\",{}))}')
print(f' Dev: {len(d.get(\"devDependencies\",{}))}')
# Known deprecated packages
deprecated = {
'request': 'Use fetch, axios, or got',
'node-uuid': 'Use uuid package',
'nomnom': 'Use commander or yargs',
'optimist': 'Use yargs',
'jade': 'Renamed to pug',
'istanbul': 'Use nyc or c8',
'coffee-script': 'Migrate to JavaScript/TypeScript',
'bower': 'Use npm/yarn/pnpm',
'grunt': 'Use npm scripts or modern bundlers',
'moment': 'Use date-fns, dayjs, or Temporal',
'left-pad': 'Use String.padStart()',
'underscore': 'Use lodash or native JS',
}
print()
found_deprecated = []
for pkg, alt in deprecated.items():
if pkg in all_deps:
found_deprecated.append(f' ⚠️ {pkg}@{all_deps[pkg]} → {alt}')
if found_deprecated:
print(f'Deprecated packages ({len(found_deprecated)}):')
for f in found_deprecated:
print(f)
else:
print('✅ No known deprecated packages')
# Pinning analysis
print()
unpinned = [(k,v) for k,v in all_deps.items() if v.startswith('^') or v.startswith('~')]
exact = [(k,v) for k,v in all_deps.items() if re.match(r'^\d', v)]
wildcard = [(k,v) for k,v in all_deps.items() if '*' in v]
print(f'Version pinning: {len(exact)} exact, {len(unpinned)} range (^/~), {len(wildcard)} wildcard')
if wildcard:
print(' ❌ Wildcard versions (dangerous):')
for k,v in wildcard:
print(f' {k}: {v}')
" 2>/dev/null
fi
# Check for security advisories
echo ""
echo "--- Security Advisories ---"
npm audit --json 2>/dev/null | python3 -c "
import json, sys
try:
d = json.load(sys.stdin)
v = d.get('metadata',{}).get('vulnerabilities',{})
total = sum(v.values())
if total > 0:
print(f'Vulnerabilities: {v.get(\"critical\",0)} critical, {v.get(\"high\",0)} high, {v.get(\"moderate\",0)} moderate, {v.get(\"low\",0)} low')
else:
print('✅ No known vulnerabilities')
except: print('Could not parse audit output')
" 2>/dev/null
# Dependency size impact
echo ""
echo "--- Size Impact (top 10 heaviest) ---"
if command -v npx &>/dev/null; then
echo "Run 'npx package-size <package>' for individual sizes"
fi
Output Formats
- text (default): Human-readable with status indicators
- json:
{package, current_version, usage: {files, imports, apis}, tests: {covered, uncovered}, breaking_changes: [], alternatives: []}
- markdown: Report suitable for PR descriptions or ADRs
CI Integration
Exit codes:
- 0: Low impact (< 5 files, all tested)
- 1: High impact (> 20 files or untested code paths)
- 2: Critical (deprecated, vulnerable, or major breaking changes)
Notes
- Supports JS/TS (npm), Python (pip), Go (go mod), Rust (cargo) projects
- Does not execute tests — reports coverage gaps for manual verification
- Breaking change detection relies on semver conventions and npm registry metadata
- For accurate reverse dependency analysis, a lock file must be present
- Alternative package suggestions use AI reasoning based on actual usage patterns, not generic recommendations