Skip to content

Instantly share code, notes, and snippets.

@javimosch
Created August 18, 2025 08:01
Show Gist options
  • Select an option

  • Save javimosch/01a457daa46bcb2c665a54728de5fd4f to your computer and use it in GitHub Desktop.

Select an option

Save javimosch/01a457daa46bcb2c665a54728de5fd4f to your computer and use it in GitHub Desktop.
Free Up Gigabytes of Disk Space by Pruning Your node_modules
#!/bin/bash
# Set default values
dry_run=false
force=false
days=30
ignore_folders=(".nvm" ".vscode" ".cursor" ".pyenv" ".npm" "/windsurf" ".config" ".local")
# Function to display help message
usage() {
echo "Usage: $0 [options]"
echo "Options:"
echo " --dry-run Print the node_modules folders that would be deleted without actually deleting them."
echo " --force Delete node_modules folders without asking for confirmation."
echo " --days <days> Set the number of days of inactivity to consider a node_modules folder for pruning (default: 30)."
echo " -h, --help Display this help message."
}
# Parse command-line options
while [[ "$#" -gt 0 ]]; do
case $1 in
--dry-run) dry_run=true; shift ;;
--force) force=true; shift ;;
--days) days="$2"; shift 2 ;;
-h|--help) usage; exit 0 ;;
*) echo "Unknown option: $1"; usage; exit 1 ;;
esac
done
# Function to calculate folder size
get_folder_size() {
du -sh "$1" | awk '{print $1}'
}
# Main script logic
all_node_modules_dirs=()
folders_to_delete=()
total_combined_size=0
ignored_paths=()
find_command="find . -name node_modules -type d -prune"
for folder in "${ignore_folders[@]}"; do
find_command+=" -o -path './$folder' -prune"
ignored_paths+=("./$folder")
done
# Ignore all other dotfiles
find_command+=" -o -path './.*' -prune"
while read -r node_modules_dir; do
all_node_modules_dirs+=("$node_modules_dir")
done < <(eval $find_command)
if [ ${#all_node_modules_dirs[@]} -eq 0 ]; then
echo "No node_modules folders found."
exit 0
fi
# Find the index of the most recently modified folder
latest_index=-1
latest_time=0
for i in "${!all_node_modules_dirs[@]}"; do
parent_dir=$(dirname "${all_node_modules_dirs[$i]}")
last_modified=$(stat -c %Y "$parent_dir")
if (( last_modified > latest_time )); then
latest_time=$last_modified
latest_index=$i
fi
done
for i in "${!all_node_modules_dirs[@]}"; do
if (( i == latest_index )); then
echo "Skipping most recently modified: ${all_node_modules_dirs[$i]}"
continue
fi
node_modules_dir=${all_node_modules_dirs[$i]}
parent_dir=$(dirname "$node_modules_dir")
last_modified=$(stat -c %Y "$parent_dir")
current_time=$(date +%s)
days_since_modified=$(( (current_time - last_modified) / 86400 ))
if (( days_since_modified >= days )); then
size=$(get_folder_size "$node_modules_dir")
echo "Found node_modules in $parent_dir with size: $size (last modified $days_since_modified days ago)"
folders_to_delete+=("$node_modules_dir")
total_size_bytes=$(du -sB1 "$node_modules_dir" | awk '{print $1}')
total_combined_size=$((total_combined_size + total_size_bytes))
fi
done
if [ ${#folders_to_delete[@]} -eq 0 ]; then
echo "No node_modules folders to prune."
exit 0
fi
echo "Total disk usage of node_modules folders to be pruned: $(numfmt --to=iec --format="%.1f" $total_combined_size)"
if [ "$dry_run" = true ]; then
echo "Dry run enabled. The following node_modules folders would be deleted:"
for folder in "${folders_to_delete[@]}"; do
echo "- $folder"
done
echo ""
echo "Ignored folders:"
for folder in "${ignored_paths[@]}"; do
echo "- $folder"
done
exit 0
fi
if [ "$force" = false ]; then
echo "The following node_modules folders will be deleted:"
for folder in "${folders_to_delete[@]}"; do
echo "- $folder"
done
read -p "Do you want to delete these folders? (y/N): " confirm
if [[ ! "$confirm" =~ ^[yY]$ ]]; then
echo "Deletion cancelled."
exit 0
fi
fi
for folder in "${folders_to_delete[@]}"; do
echo "Deleting $folder..."
rm -rf "$folder"
done
echo "Node_modules folders have been deleted."
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment