Last active
July 24, 2025 17:21
-
-
Save koad/e5deed3441f83be97aecc96115edbd39 to your computer and use it in GitHub Desktop.
This script scans Meteor applications for deprecated functions and patterns that need to be updated for Meteor 3.0 compatibility. It provides detailed reports with file locations, line numbers, and migration advice.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
# | |
# =================================================================== | |
# Meteor 3.0 Migration Analysis Script | |
# =================================================================== | |
# | |
# Purpose: | |
# This script scans Meteor applications for deprecated functions and patterns | |
# that need to be updated for Meteor 3.0 compatibility. It provides detailed | |
# reports with file locations, line numbers, and migration advice. | |
# | |
# Requirements: | |
# - ripgrep (rg command) - much faster and more powerful than grep | |
# | |
# Usage: | |
# ./meteor3-migration-checker.sh [directory] | |
# | |
# Examples: | |
# ./meteor3-migration-checker.sh # Scans current directory recursively | |
# ./meteor3-migration-checker.sh ./my-app # Scans specific directory recursively | |
# | |
# Notes: | |
# - Searches recursively through all subdirectories by default | |
# - Automatically excludes node_modules, .meteor, .local, .npm, dist, and build directories | |
# - Focuses on JavaScript/TypeScript files (*.js, *.jsx, *.ts, *.tsx) | |
# | |
# Author: koad via claude | |
# Version: 1.0 | |
# Date: July 24 2025 | |
# | |
# =================================================================== | |
# ================================================================= | |
# SCRIPT CONFIGURATION AND SETUP | |
# ================================================================= | |
# Don't exit on errors - we want to complete the full scan even if some parts fail | |
# This ensures we get a complete report of all issues | |
set +e | |
# Store the start time to calculate total execution time at the end | |
START_TIME=$(date +%s) | |
# Directory to scan (default is current directory) | |
# The ${1:-.} syntax means "use the first argument if provided, otherwise use '.'" | |
SCAN_DIR="${1:-.}" | |
# ================================================================= | |
# COLOR DEFINITIONS - Making Terminal Output Pretty | |
# ================================================================= | |
# ANSI escape codes tell the terminal to change text colors and styles | |
# Format: \033[COLORm where COLOR is a number code | |
# These variables make the rest of the script more readable | |
# Text colors | |
RED='\033[0;31m' # Critical issues (must fix) | |
BRIGHT_RED='\033[1;31m' # Section headings | |
YELLOW='\033[0;33m' # Warnings (should fix) | |
GREEN='\033[0;32m' # Success messages | |
BLUE='\033[0;34m' # Information | |
CYAN='\033[0;36m' # File paths and technical details | |
MAGENTA='\033[0;35m' # Progress indicators | |
GRAY='\033[0;90m' # Less important information | |
WHITE='\033[1;37m' # Highlighted content | |
NC='\033[0m' # No Color - resets to default terminal color | |
# Text styles | |
BOLD='\033[1m' | |
UNDERLINE='\033[4m' | |
ITALIC='\033[3m' | |
# ================================================================= | |
# UNICODE SYMBOLS FOR BETTER READABILITY | |
# ================================================================= | |
# These symbols make the output more visual and easier to scan | |
CHECK="✓" # Success/completed | |
CROSS="✗" # Error/issue found | |
WARN="⚠" # Warning | |
INFO="ℹ" # Information | |
ARROW="➤" # Pointing to specific line | |
ROCKET="🚀" # Progress/action | |
MAGNIFY="🔍" # Searching | |
LIGHTBULB="💡" # Tip/suggestion | |
TOOLS="🔧" # Fix required | |
HOURGLASS="⏳" # Processing/waiting | |
# ================================================================= | |
# CHECK FOR REQUIRED TOOLS | |
# ================================================================= | |
# Check if ripgrep is installed - exit with instructions if not found | |
check_requirements() { | |
echo -e "${BLUE}${HOURGLASS} Checking requirements...${NC}" | |
if ! command -v rg &> /dev/null; then | |
echo -e "${RED}${CROSS} Error: ripgrep (rg) is not installed${NC}" | |
echo | |
echo -e "${YELLOW}Ripgrep is required for this script. Please install it:${NC}" | |
echo | |
echo -e "${CYAN}On Ubuntu/Debian:${NC}" | |
echo " sudo apt-get update" | |
echo " sudo apt-get install ripgrep" | |
echo | |
echo -e "${CYAN}On MacOS:${NC}" | |
echo " brew install ripgrep" | |
echo | |
echo -e "${CYAN}On CentOS/RHEL:${NC}" | |
echo " sudo yum install ripgrep" | |
echo | |
echo -e "${CYAN}On Windows (with Chocolatey):${NC}" | |
echo " choco install ripgrep" | |
echo | |
echo -e "${CYAN}Or download from: ${UNDERLINE}https://github.com/BurntSushi/ripgrep/releases${NC}" | |
echo | |
exit 1 | |
fi | |
echo -e "${GREEN}${CHECK} All requirements satisfied${NC}" | |
echo | |
} | |
# ================================================================= | |
# SEARCH PATTERNS | |
# ================================================================= | |
# These arrays define what we're looking for in the codebase | |
# Each array holds patterns for a specific category of migration issues | |
# Critical Issues (must fix) - Breaking changes that will stop the app from running | |
declare -a critical_patterns=( | |
# Fiber-related patterns | |
"Fiber\.yield" | |
"Fiber\.current" | |
"Promise\.await" | |
"Meteor\.wrapAsync" | |
"fibers/future" | |
"Npm\.require\(['\"]fibers['\"]" | |
# Synchronous MongoDB operations | |
"\.find\(\)\.fetch\(\)" | |
"\.findOne\(" | |
"\.insert\(" | |
"\.update\(" | |
"\.remove\(" | |
"\.upsert\(" | |
) | |
# Critical pattern descriptions - matches order with the array above | |
declare -a critical_descriptions=( | |
"Fiber.yield() - Fibers have been removed in Meteor 3" | |
"Fiber.current - Fibers have been removed in Meteor 3" | |
"Promise.await() - No longer needed, use native await" | |
"Meteor.wrapAsync() - No longer needed, use async/await" | |
"fibers/future - Future package import (removed in Meteor 3)" | |
"Npm.require('fibers') - Fibers dependency import" | |
"Collection.find().fetch() - Synchronous fetch() operation" | |
"Collection.findOne() - Synchronous findOne() operation" | |
"Collection.insert() - Synchronous insert() operation" | |
"Collection.update() - Synchronous update() operation" | |
"Collection.remove() - Synchronous remove() operation" | |
"Collection.upsert() - Synchronous upsert() operation" | |
) | |
# Critical fix suggestions - matches order with the arrays above | |
declare -a critical_fixes=( | |
"Replace with 'await' and make containing function async" | |
"Replace with modern async/await pattern" | |
"Replace with native 'await' keyword" | |
"Replace with direct use of async/await" | |
"Remove Future imports and use async/await pattern" | |
"Remove Fibers dependency import" | |
"Replace with 'await Collection.find().fetchAsync()'" | |
"Replace with 'await Collection.findOneAsync()'" | |
"Replace with 'await Collection.insertAsync()'" | |
"Replace with 'await Collection.updateAsync()'" | |
"Replace with 'await Collection.removeAsync()'" | |
"Replace with 'await Collection.upsertAsync()'" | |
) | |
# High Priority Issues (should fix) - Features that may work but are deprecated | |
declare -a high_patterns=( | |
# Method calls (recommended to update) | |
"Meteor\.call\([^,]+,[^,]+,\s*function" | |
# API renames | |
"Accounts\.setPassword" | |
"Accounts\.addEmail" | |
"Assets\.getText" | |
"Assets\.getBinary" | |
"Meteor\.user\(\)" | |
) | |
# High pattern descriptions | |
declare -a high_descriptions=( | |
"Meteor.call() with callback - Better to use callAsync" | |
"Accounts.setPassword - Renamed in Meteor 3" | |
"Accounts.addEmail - Renamed in Meteor 3" | |
"Assets.getText - Renamed in Meteor 3" | |
"Assets.getBinary - Renamed in Meteor 3" | |
"Meteor.user() - Server-side should use userAsync" | |
) | |
# High fix suggestions | |
declare -a high_fixes=( | |
"Replace with 'const result = await Meteor.callAsync(...)'" | |
"Replace with 'await Accounts.setPasswordAsync()'" | |
"Replace with 'await Accounts.addEmailAsync()'" | |
"Replace with 'await Assets.getTextAsync()'" | |
"Replace with 'await Assets.getBinaryAsync()'" | |
"Replace with 'await Meteor.userAsync()' on server-side" | |
) | |
# Medium Priority Issues (consider fixing) - API changes that have alternatives | |
declare -a medium_patterns=( | |
# WebApp API changes | |
"WebApp\.connectHandlers" | |
"WebApp\.rawConnectHandlers" | |
"WebApp\.connectApp" | |
) | |
# Medium pattern descriptions | |
declare -a medium_descriptions=( | |
"WebApp.connectHandlers - Connect API replaced with Express" | |
"WebApp.rawConnectHandlers - Connect API replaced with Express" | |
"WebApp.connectApp - Connect API replaced with Express" | |
) | |
# Medium fix suggestions | |
declare -a medium_fixes=( | |
"Replace with 'WebApp.handlers'" | |
"Replace with 'WebApp.rawHandlers'" | |
"Replace with 'WebApp.expressApp'" | |
) | |
# ================================================================= | |
# HELPER FUNCTIONS | |
# ================================================================= | |
# Print a separator line to make output more readable | |
# No parameters needed - just prints a line of a fixed length | |
print_separator() { | |
echo -e "${GRAY}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}" | |
} | |
# Print a section heading to organize output | |
# $1: Text to display as a heading | |
print_heading() { | |
echo | |
print_separator | |
echo -e "${BRIGHT_RED}${BOLD}$1${NC}" | |
print_separator | |
} | |
# Print information about what we're searching for | |
# $1: Search pattern description | |
# $2: Current pattern number | |
# $3: Total number of patterns | |
print_searching() { | |
echo | |
echo -e "${MAGENTA}${MAGNIFY} [$2/$3] Searching for: ${CYAN}$1${NC}" | |
print_separator | |
} | |
# Format file paths to be more readable | |
# $1: File path to format | |
format_file_path() { | |
echo -e "${CYAN}${BOLD}$1${NC}" | |
} | |
# Count files in the directory to be scanned | |
# Takes no parameters but uses the global SCAN_DIR variable | |
count_files() { | |
# Find all JS/TS files that might contain Meteor code | |
# Using -not -path to exclude node_modules, .local, .meteor and other non-source directories | |
# This ensures we're counting the same files that ripgrep will search | |
local file_count=$(find "$SCAN_DIR" -type f \( -name "*.js" -o -name "*.jsx" -o -name "*.ts" -o -name "*.tsx" \) \ | |
-not -path "*/node_modules/*" \ | |
-not -path "*/.meteor/*" \ | |
-not -path "*/.local/*" \ | |
-not -path "*/.npm/*" \ | |
-not -path "*/dist/*" \ | |
-not -path "*/build/*" \ | |
| wc -l) | |
echo "$file_count" | |
} | |
# ================================================================= | |
# MAIN SEARCH FUNCTIONS | |
# ================================================================= | |
# Search for a specific pattern with ripgrep and display results | |
# $1: Search pattern (regex) | |
# $2: Pattern description | |
# $3: Fix suggestion | |
# $4: Severity color (RED, YELLOW, etc.) | |
# $5: Severity label (CRITICAL, HIGH, etc.) | |
# Sets global PATTERN_MATCH_COUNT with number of matches found | |
search_pattern() { | |
local pattern="$1" | |
local description="$2" | |
local fix="$3" | |
local color="$4" | |
local severity="$5" | |
PATTERN_MATCH_COUNT=0 | |
# This command runs ripgrep with: | |
# -i: Case insensitive matching | |
# --context=2: Show 2 lines before and after the match | |
# -g: Glob patterns to include only JavaScript/TypeScript files | |
# --iglob: Glob patterns to exclude (node_modules, .local, .npm, etc.) | |
# --color=always: Force color output | |
# -n: Show line numbers | |
# Note: ripgrep searches recursively by default | |
local results=$(rg -i --context=2 -g "*.{js,jsx,ts,tsx}" \ | |
--iglob "!node_modules" \ | |
--iglob "!.meteor" \ | |
--iglob "!.local" \ | |
--iglob "!.npm" \ | |
--iglob "!dist" \ | |
--iglob "!build" \ | |
--color=always \ | |
-n "$pattern" "$SCAN_DIR" 2>/dev/null || true) | |
# If we found matches, display them with context | |
if [[ -n "$results" ]]; then | |
# Count lines that have the pattern (ignoring context lines) | |
# Get a clean count by counting lines with our pattern | |
count=$(rg -i --no-heading --no-line-number \ | |
-g "*.{js,jsx,ts,tsx}" \ | |
--iglob "!node_modules" \ | |
--iglob "!.meteor" \ | |
--iglob "!.local" \ | |
--iglob "!.npm" \ | |
--iglob "!dist" \ | |
--iglob "!build" \ | |
"$pattern" "$SCAN_DIR" 2>/dev/null | wc -l) | |
# Make sure we have valid count | |
if [[ -z "$count" ]] || [[ "$count" -lt 1 ]]; then | |
count=0 | |
fi | |
echo -e "${color}${severity}: Found ${count} instances${NC}" | |
echo | |
# Much simpler approach - just echo the results directly | |
# This avoids complex processing that might hang | |
# Add some basic formatting to make it more readable | |
echo "$results" | sed "s/^--$//" | sed "s/^$//" | \ | |
sed "s/^\([^:]*\):\([0-9]*\):\(.*\)/${CYAN}📁 \1${NC}\n ${GRAY}\2 │${NC} ${color}${ARROW} \3${NC}/" | \ | |
sed "s/^\([0-9]*\)\([-:]\)\(.*\)/ ${GRAY}\1 │${NC} \3/" | |
echo "" | |
# Print the fix suggestion | |
echo -e "${GREEN}${LIGHTBULB} Migration: ${fix}${NC}" | |
echo | |
# Store count in global variable | |
PATTERN_MATCH_COUNT=$count | |
else | |
PATTERN_MATCH_COUNT=0 | |
fi | |
} | |
# Process an entire array of patterns | |
# $1: Array of patterns | |
# $2: Array of descriptions | |
# $3: Array of fixes | |
# $4: Color for display | |
# $5: Severity label | |
# Returns: Total number of issues found | |
process_patterns() { | |
local -n patterns=$1 | |
local -n descriptions=$2 | |
local -n fixes=$3 | |
local color=$4 | |
local severity=$5 | |
local total=0 | |
local current=0 | |
# Loop through all patterns | |
for i in "${!patterns[@]}"; do | |
current=$((current + 1)) | |
# Calculate global pattern number and total for progress display | |
local global_current=$((global_pattern_current + current)) | |
local global_total=$((global_pattern_total)) | |
# Show what we're searching for | |
print_searching "${descriptions[$i]}" "$global_current" "$global_total" | |
# Search for the pattern and get count | |
PATTERN_MATCH_COUNT=0 | |
search_pattern "${patterns[$i]}" "${descriptions[$i]}" "${fixes[$i]}" "$color" "$severity" | |
local count=$PATTERN_MATCH_COUNT | |
# Add to the total | |
total=$((total + count)) | |
# Update statistics by severity | |
if [[ "$severity" == "CRITICAL" ]]; then | |
critical_total=$((critical_total + count)) | |
elif [[ "$severity" == "HIGH" ]]; then | |
high_total=$((high_total + count)) | |
elif [[ "$severity" == "MEDIUM" ]]; then | |
medium_total=$((medium_total + count)) | |
fi | |
done | |
# Update the global pattern counter | |
global_pattern_current=$((global_pattern_current + ${#patterns[@]})) | |
# Make sure we return a valid total even if something goes wrong | |
if [[ $total -lt 0 ]]; then | |
total=0 | |
fi | |
return $total | |
} | |
# ================================================================= | |
# MAIN EXECUTION | |
# ================================================================= | |
# Initialize global variables and counters for statistics | |
PATTERN_MATCH_COUNT=0 | |
critical_total=0 | |
high_total=0 | |
medium_total=0 | |
global_pattern_current=0 | |
global_pattern_total=$((${#critical_patterns[@]} + ${#high_patterns[@]} + ${#medium_patterns[@]})) | |
# Print banner | |
echo -e "${BRIGHT_RED}${BOLD}" | |
echo " __ __ _ _____ __ __ _ _ _ " | |
echo " | \/ | ___| |_ ___ ___ _ __ |___ / | \/ (_) __ _ _ __ __ _| |_(_) ___ _ __ " | |
echo " | |\/| |/ _ \ __/ _ \/ _ \| '__| |_ \ | |\/| | |/ _\` | '__/ _\` | __| |/ _ \| '_ \ " | |
echo " | | | | __/ || __/ (_) | | ___) | | | | | | (_| | | | (_| | |_| | (_) | | | |" | |
echo " |_| |_|\___|\__\___|\___/|_| |____/___|_| |_|_|\__, |_| \__,_|\__|_|\___/|_| |_|" | |
echo " |_____| |___/ " | |
echo -e "${NC}" | |
echo -e "${BLUE}${BOLD}A comprehensive Meteor 3.0 migration analysis tool${NC}" | |
echo -e "${GRAY}Analyzes your Meteor codebase for deprecated functions and patterns${NC}" | |
echo | |
# Check for required tools | |
check_requirements | |
# Show scan information | |
echo -e "${BLUE}${INFO} Scan Information:${NC}" | |
echo -e " ${GRAY}Directory:${NC} ${CYAN}$SCAN_DIR${NC}" | |
# Count files to scan | |
file_count=$(count_files) | |
echo -e " ${GRAY}Files to scan:${NC} ${CYAN}$file_count${NC} JavaScript/TypeScript files" | |
echo -e " ${GRAY}Patterns to check:${NC} ${CYAN}$global_pattern_total${NC} deprecated functions and patterns" | |
echo | |
# Begin scan | |
print_heading "BEGINNING SCAN" | |
echo -e "${BLUE}${ROCKET} Starting comprehensive scan for Meteor 3.0 migration issues...${NC}" | |
echo | |
# Process each category of patterns | |
print_heading "CRITICAL ISSUES (Must Fix)" | |
echo -e "${RED}These issues will break your application in Meteor 3.0${NC}" | |
process_patterns critical_patterns critical_descriptions critical_fixes "$RED" "CRITICAL" | |
critical_count=$? | |
print_heading "HIGH PRIORITY ISSUES (Should Fix)" | |
echo -e "${YELLOW}These issues are important to address for Meteor 3.0${NC}" | |
process_patterns high_patterns high_descriptions high_fixes "$YELLOW" "HIGH" | |
high_count=$? | |
print_heading "MEDIUM PRIORITY ISSUES (Consider Fixing)" | |
echo -e "${BLUE}These issues are recommended to address but may not break functionality${NC}" | |
process_patterns medium_patterns medium_descriptions medium_fixes "$BLUE" "MEDIUM" | |
medium_count=$? | |
# Make sure totals are valid | |
if [[ $critical_total -lt 0 ]]; then critical_total=0; fi | |
if [[ $high_total -lt 0 ]]; then high_total=0; fi | |
if [[ $medium_total -lt 0 ]]; then medium_total=0; fi | |
# Calculate total issues | |
total_issues=$((critical_total + high_total + medium_total)) | |
# Calculate execution time | |
END_TIME=$(date +%s) | |
EXECUTION_TIME=$((END_TIME - START_TIME)) | |
# Print summary | |
print_heading "SCAN COMPLETE" | |
echo -e "${GREEN}${CHECK} Scan completed in ${CYAN}${EXECUTION_TIME}${NC} seconds" | |
echo | |
echo -e "${BLUE}${INFO} Summary:${NC}" | |
echo -e " ${GRAY}Files scanned:${NC} ${CYAN}$file_count${NC}" | |
echo -e " ${GRAY}Patterns checked:${NC} ${CYAN}$global_pattern_total${NC}" | |
echo -e " ${GRAY}Total issues found:${NC} ${CYAN}$total_issues${NC}" | |
echo | |
echo -e " ${RED}Critical issues:${NC} ${CYAN}$critical_total${NC} ${GRAY}(Must fix for Meteor 3.0)${NC}" | |
echo -e " ${YELLOW}High priority issues:${NC} ${CYAN}$high_total${NC} ${GRAY}(Should fix for best practices)${NC}" | |
echo -e " ${BLUE}Medium priority issues:${NC} ${CYAN}$medium_total${NC} ${GRAY}(Consider fixing)${NC}" | |
echo | |
# Add migration advice based on findings | |
if [ $total_issues -eq 0 ]; then | |
echo -e "${GREEN}${CHECK} Great news! No migration issues found. Your app appears ready for Meteor 3.0.${NC}" | |
elif [ $critical_total -eq 0 ]; then | |
echo -e "${YELLOW}${WARN} Your app has some non-critical issues but may work with Meteor 3.0.${NC}" | |
echo -e "${YELLOW}${LIGHTBULB} Recommendation: Address the high priority issues for better compatibility.${NC}" | |
else | |
echo -e "${RED}${CROSS} Your app needs updates before it will work with Meteor 3.0.${NC}" | |
echo -e "${RED}${LIGHTBULB} Focus first on fixing the ${CYAN}$critical_total${NC} ${RED}critical issues.${NC}" | |
fi | |
echo | |
echo -e "${BLUE}${INFO} Next Steps:${NC}" | |
echo -e " ${GRAY}1. Fix critical issues first (red items)${NC}" | |
echo -e " ${GRAY}2. Address high priority issues (yellow items)${NC}" | |
echo -e " ${GRAY}3. Consider fixing medium priority issues (blue items)${NC}" | |
echo -e " ${GRAY}4. Run Meteor with ${CYAN}--release 3.3.0${NC} ${GRAY}to test${NC}" | |
echo | |
echo -e "${BLUE}${INFO} For more information, see:${NC}" | |
echo -e " ${CYAN}https://docs.meteor.com/v3-migration-guide${NC}" | |
echo | |
# Exit with status code based on findings (helpful for CI/CD pipelines) | |
if [ $critical_total -gt 0 ]; then | |
exit 1 # Critical issues found | |
elif [ $total_issues -gt 0 ]; then | |
exit 0 # Issues found but not critical | |
else | |
exit 0 # No issues found | |
fi |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment