Skip to content

Instantly share code, notes, and snippets.

@loopmode
Created June 12, 2025 12:04
Show Gist options
  • Save loopmode/6c2e876b764888b72f8b7f6e93f6c7a9 to your computer and use it in GitHub Desktop.
Save loopmode/6c2e876b764888b72f8b7f6e93f6c7a9 to your computer and use it in GitHub Desktop.
codemod bash
# codemod function generated by codemod AI to allow using ast-grep with autocomplete for local rules
# opted for macos + zshell
# use some AI to make it work on bash/linux/windows??
codemod() {
local start_time=$(date +%s.%N)
local write_mode=false
local args=()
# Parse all arguments, extracting --write and keeping others
while [[ $# -gt 0 ]]; do
case $1 in
--write)
write_mode=true
shift
;;
*)
args+=("$1")
shift
;;
esac
done
# Need at least 2 arguments (1 rule + 1 target directory)
if [[ ${#args[@]} -lt 2 ]]; then
echo "Usage: codemod [--write] rule1 [rule2 ...] targetfolder [--write]"
echo "Examples:"
echo " codemod remove-prop-types-imports src/ # Single rule preview"
echo " codemod remove-prop-types-imports comment-out-proptypes src/ # Multiple rules preview"
echo " codemod --write remove-prop-types-imports comment-out-proptypes src/ # Apply all changes"
return 1
fi
# Last argument is target directory, everything else is rule names
local targetdir="${args[-1]}"
local rules=("${args[@]:0:${#args[@]}-1}")
if [ ! -d "$targetdir" ]; then
echo "Error: Target directory not found: $targetdir"
return 1
fi
# Get absolute path of target directory
local abs_targetdir=$(realpath "$targetdir" 2>/dev/null || echo $(cd "$targetdir" && pwd))
# Show what we're going to do
local mode_text="PREVIEW MODE"
if [[ "$write_mode" == true ]]; then
mode_text="APPLYING CHANGES"
fi
echo "Running ${#rules[@]} codemod(s) on $abs_targetdir ($mode_text)"
if [[ "$write_mode" == false ]]; then
echo "Add --write to apply changes"
fi
echo ""
# Execute each rule
for rulename in "${rules[@]}"; do
local rule_start_time=$(date +%s.%N)
local rulefile="$HOME/Projects/codemods/${rulename}"
# Try with .yml extension if not provided
if [[ ! "$rulename" == *.yml ]]; then
rulefile="${rulefile}.yml"
fi
if [ ! -f "$rulefile" ]; then
echo "Error: Rule file not found: $rulefile"
continue
fi
echo "→ Running rule: $rulename"
if [[ "$write_mode" == true ]]; then
ast-grep scan --rule "$rulefile" --update-all "$targetdir"
else
ast-grep scan --rule "$rulefile" "$targetdir"
fi
local rule_end_time=$(date +%s.%N)
local rule_duration=$(echo "$rule_end_time - $rule_start_time" | bc -l 2>/dev/null || echo "0")
printf " ✓ Completed in %.3f seconds\n" "$rule_duration"
echo ""
done
local end_time=$(date +%s.%N)
local total_duration=$(echo "$end_time - $start_time" | bc -l 2>/dev/null || echo "0")
if [[ "$write_mode" == true ]]; then
printf "All changes applied in %.3f seconds.\n" "$total_duration"
else
printf "Preview completed in %.3f seconds. Use --write to apply changes.\n" "$total_duration"
fi
}
# Zsh completion for codemod
_codemod() {
local -a rules
local rules_dir="$HOME/Projects/codemods"
# Get available rules
if [[ -d "$rules_dir" ]]; then
for file in "$rules_dir"/*.yml; do
[[ -f "$file" ]] && rules+=($(basename "$file" .yml))
done
fi
# Remove --write from consideration
local filtered_words=()
local write_flag_count=0
for word in "${words[@]:1}"; do
if [[ "$word" == "--write" ]]; then
((write_flag_count++))
else
filtered_words+=("$word")
fi
done
local effective_current=$((CURRENT - write_flag_count))
local word_count=${#filtered_words[@]}
# If we're on the last incomplete word and there are already 2+ words,
# and the current word looks like a directory, complete directories
if [[ $effective_current -gt 1 ]] && [[ -d "${words[CURRENT]}" || "${words[CURRENT]}" =~ ^[./] ]]; then
_directories
else
# Otherwise, complete rule names and directories
local -a all_completions
((${#rules[@]})) && all_completions+=("${rules[@]}")
# Add some common directory patterns
# all_completions+=(src/ js/ lib/ components/)
compadd -a all_completions
_directories
fi
}
# Initialize completion system
autoload -Uz compinit
compinit -i # The -i flag ignores insecure directories/files
compdef _codemod codemod
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment