Skip to content

Instantly share code, notes, and snippets.

@koad
Last active July 24, 2025 17:21
Show Gist options
  • Select an option

  • Save koad/e5deed3441f83be97aecc96115edbd39 to your computer and use it in GitHub Desktop.

Select an option

Save koad/e5deed3441f83be97aecc96115edbd39 to your computer and use it in GitHub Desktop.

Revisions

  1. koad revised this gist Jul 24, 2025. 1 changed file with 2 additions and 2 deletions.
    4 changes: 2 additions & 2 deletions meteor-3-migration-checker.sh
    Original file line number Diff line number Diff line change
    @@ -24,9 +24,9 @@
    # - Automatically excludes node_modules, .meteor, .local, .npm, dist, and build directories
    # - Focuses on JavaScript/TypeScript files (*.js, *.jsx, *.ts, *.tsx)
    #
    # Author: [Your Name]
    # Author: koad via claude
    # Version: 1.0
    # Date: July 2025
    # Date: July 24 2025
    #
    # ===================================================================

  2. koad created this gist Jul 24, 2025.
    539 changes: 539 additions & 0 deletions meteor-3-migration-checker.sh
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,539 @@
    #!/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: [Your Name]
    # Version: 1.0
    # Date: July 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