MetaNull.LaravelUtils.psm1
# Module Constants and Initialization # Icon configuration based on PowerShell version $script:ModuleIcons = @{} $script:UseEmojis = $false # Detect PowerShell version and set icon preference if ($PSVersionTable.PSVersion.Major -ge 7) { # PowerShell 7+ supports Unicode emojis better $script:UseEmojis = $true $script:ModuleIcons = @{ # Status Icons Rocket = "`u1F680" # 🚀 CheckMark = "`u2705" # ✅ Warning = "`u26A0" # ⚠️ Info = "`u2139" # ℹ️ Error = "`u274C" # ❌ # Application Icons Celebration = "`u1F389" # 🎉 MobilePhone = "`u1F4F1" # 📱 Satellite = "`u1F4E1" # 📡 Lightning = "`u26A1" # ⚡ # Tool Icons Wrench = "`u1F527" # 🔧 Books = "`u1F4DA" # 📚 GreenHeart = "`u1F49A" # 💚 Key = "`u1F511" # 🔑 FloppyDisk = "`u1F4BE" # 💾 # Fallback plain text versions PlainText = @{ Rocket = "[START]" CheckMark = "[OK]" Warning = "[WARN]" Info = "[INFO]" Error = "[ERROR]" Celebration = "[SUCCESS]" MobilePhone = "[APP]" Satellite = "[API]" Lightning = "[HMR]" Wrench = "[TOOLS]" Books = "[DOCS]" GreenHeart = "[HEALTH]" Key = "[DASH]" FloppyDisk = "[DEV]" } } } else { # PowerShell 5.1 and earlier - use plain text icons $script:UseEmojis = $false $script:ModuleIcons = @{ PlainText = @{ Rocket = "[START]" CheckMark = "[OK]" Warning = "[WARN]" Info = "[INFO]" Error = "[ERROR]" Celebration = "[SUCCESS]" MobilePhone = "[APP]" Satellite = "[API]" Lightning = "[HMR]" Wrench = "[TOOLS]" Books = "[DOCS]" GreenHeart = "[HEALTH]" Key = "[DASH]" FloppyDisk = "[DEV]" } } } # Color constants for consistent output Set-Variable -Name "ModuleColorSuccess" -Value "Green" -Option Constant Set-Variable -Name "ModuleColorWarning" -Value "Yellow" -Option Constant Set-Variable -Name "ModuleColorError" -Value "Red" -Option Constant Set-Variable -Name "ModuleColorInfo" -Value "Cyan" -Option Constant Set-Variable -Name "ModuleColorStep" -Value "Magenta" -Option Constant Set-Variable -Name "ModuleColorHeader" -Value "White" -Option Constant Function Get-DevIcon { <# .SYNOPSIS Gets an icon for display based on PowerShell version capabilities .DESCRIPTION Returns either an emoji icon (PowerShell 7+) or plain text fallback (PowerShell 5.1). This is a public wrapper around the private Get-ModuleIcon function. .PARAMETER IconName The name of the icon to retrieve. Valid values include: Rocket, CheckMark, Warning, Info, Error, Celebration, MobilePhone, Satellite, Lightning, Wrench, Books, GreenHeart, Key, FloppyDisk .OUTPUTS String - The icon character or text representation .EXAMPLE Get-DevIcon "Rocket" Returns "🚀" on PowerShell 7+ or "[START]" on PowerShell 5.1 .EXAMPLE $icon = Get-DevIcon "CheckMark" Write-Host "$icon Operation completed successfully!" #> [CmdletBinding()] [OutputType([string])] param( [Parameter(Mandatory = $true, Position = 0)] [ValidateSet("Rocket", "CheckMark", "Warning", "Info", "Error", "Celebration", "MobilePhone", "Satellite", "Lightning", "Wrench", "Books", "GreenHeart", "Key", "FloppyDisk")] [string]$IconName ) End { return Get-ModuleIcon -IconName $IconName } } Function Get-ModuleIcon { <# .SYNOPSIS Gets an icon for display based on PowerShell version capabilities .DESCRIPTION Returns either an emoji icon (PowerShell 7+) or plain text fallback (PowerShell 5.1) .PARAMETER IconName The name of the icon to retrieve .EXAMPLE Get-ModuleIcon "Rocket" Returns "`u{1F680}" on PowerShell 7+ or "[START]" on PowerShell 5.1 #> [CmdletBinding()] [OutputType([string])] param( [Parameter(Mandatory = $true, Position = 0)] [string]$IconName, [Parameter()] [ValidateSet("plaintext", "unicode", "auto")] [string]$Mode = "auto" ) End { if ($Mode -eq "plaintext" -or ($Mode -eq "auto" -and -not $script:UseEmojis)) { $PlainText = $true } elseif ($Mode -eq "unicode") { $PlainText = $false } else { $PlainText = $script:UseEmojis -eq $false } if ($PlainText) { $IconSet = $script:ModuleIcons.PlainText } else { $IconSet = $script:ModuleIcons } if ($IconSet.ContainsKey($IconName)) { return $IconSet[$IconName] } else { Write-Warning "Icon '$IconName' not found." } return "?" } } Function Stop-DevProcessOnPort { <# .SYNOPSIS Stops processes running on a specific port .DESCRIPTION Finds and forcefully stops all processes that are listening on the specified port. Useful for freeing up ports before starting development servers. .PARAMETER Port The port number to free up .EXAMPLE Stop-DevProcessOnPort -Port 8000 Stops all processes using port 8000 #> [CmdletBinding()] param( [Parameter(Mandatory = $true)] [int]$Port ) End { try { $processes = Get-NetTCPConnection -LocalPort $Port -ErrorAction SilentlyContinue | Select-Object -ExpandProperty OwningProcess -Unique foreach ($processId in $processes) { if ($processId -and $processId -ne 0) { $process = Get-Process -Id $processId -ErrorAction SilentlyContinue if ($process) { Write-Development -Message "Stopping process $($process.Name) (PID: $processId) on port $Port" -Type Warning Stop-Process -Id $processId -Force Start-Sleep -Seconds 1 } } } } catch { # Ignore errors when stopping processes Write-Development -Message "Could not stop processes on port $Port - they may have already been stopped" -Type Warning } } } Function Test-DevCommand { <# .SYNOPSIS Tests if a command is available in the current session .DESCRIPTION Checks if a command exists and can be executed in the current PowerShell session .PARAMETER Command The command name to test .OUTPUTS Boolean - True if command exists, False otherwise .EXAMPLE Test-DevCommand "php" Returns $true if PHP is available in PATH #> [CmdletBinding()] [OutputType([bool])] param( [Parameter(Mandatory = $true)] [string]$Command ) End { try { $null = Get-Command $Command -ErrorAction Stop return $true } catch { return $false } } } Function Test-DevPort { <# .SYNOPSIS Tests if a TCP port is open and accepting connections .DESCRIPTION Uses a TCP client to test if a port is available on localhost (127.0.0.1). This approach avoids IPv6 warnings that can occur with Test-NetConnection. .PARAMETER Port The port number to test .OUTPUTS Boolean - True if port is in use, False if available .EXAMPLE Test-DevPort 8000 Returns $true if port 8000 is in use, $false if available #> [CmdletBinding()] [OutputType([bool])] param( [Parameter(Mandatory = $true)] [int]$Port ) End { try { # Use a TCP client approach which is more reliable and doesn't show IPv6 warnings $tcpClient = New-Object System.Net.Sockets.TcpClient $tcpClient.ReceiveTimeout = 1000 $tcpClient.SendTimeout = 1000 $result = $tcpClient.BeginConnect("127.0.0.1", $Port, $null, $null) $success = $result.AsyncWaitHandle.WaitOne(1000, $false) if ($success) { try { $tcpClient.EndConnect($result) $tcpClient.Close() return $true } catch { $tcpClient.Close() return $false } } else { $tcpClient.Close() return $false } } catch { return $false } } } Function Test-LaravelPath { <# .SYNOPSIS Tests if the directory is the root of a Laravel application .DESCRIPTION Checks if the specified path contains a Laravel application by looking for key files and directories .PARAMETER Path The root directory of the Laravel application .EXAMPLE Test-LaravelPath -Path "C:\path\to\laravel" Tests if the specified path is a Laravel application root #> [CmdletBinding()] param( [Parameter(Mandatory = $true, Position = 0)] [string]$Path = '.' ) Begin { if(-Not (Test-Path -Path $Path -PathType Container)) { Write-Development -Message "The specified path '$Path' does not exist or is not a directory." -Type Error return $false } if(-Not (Test-Path -Path (Join-Path -Path $Path -ChildPath 'artisan'))) { Write-Development -Message "The specified path '$Path' does not contain a Laravel application (missing 'artisan' file)." -Type Error return $false } if(-Not (Test-Path -Path (Join-Path -Path $Path -ChildPath 'composer.json'))) { Write-Development -Message "The specified path '$Path' does not contain a Laravel application (missing 'composer.json' file)." -Type Error return $false } if(-Not (Test-Path -Path (Join-Path -Path $Path -ChildPath 'vendor'))) { Write-Development -Message "The specified path '$Path' does not contain a Laravel application (missing 'vendor' directory). Please run 'composer install' first." -Type Error return $false } if(-Not (Test-Path -Path (Join-Path -Path $Path -ChildPath 'package.json'))) { Write-Development -Message "The specified path '$Path' does not contain a Laravel application (missing 'package.json' file)." -Type Error return $false } if(-Not (Test-Path -Path (Join-Path -Path $Path -ChildPath 'vite.config.js'))) { Write-Development -Message "The specified path '$Path' does not contain a Laravel application (missing 'vite.config.js' file)." -Type Error return $false } if(-Not (Test-Path -Path (Join-Path -Path $Path -ChildPath 'node_modules'))) { Write-Development -Message "The specified path '$Path' does not contain a Laravel application (missing 'node_modules' directory). Please run 'npm install' first." -Type Error return $false } return $true } } Function Wait-ForDevPort { <# .SYNOPSIS Waits for a port to become available (in use) with timeout .DESCRIPTION Continuously tests a port until it becomes available or timeout is reached. Useful for waiting for servers to start up. .PARAMETER Port The port number to wait for .PARAMETER TimeoutSeconds Maximum time to wait in seconds (default: 10) .PARAMETER IntervalSeconds Time between checks in seconds (default: 1) .OUTPUTS Boolean - True if port became available within timeout, False otherwise .EXAMPLE Wait-ForDevPort -Port 8000 -TimeoutSeconds 30 Waits up to 30 seconds for port 8000 to become available #> [CmdletBinding()] [OutputType([bool])] param( [Parameter(Mandatory = $true)] [int]$Port, [Parameter(Mandatory = $false)] [int]$TimeoutSeconds = 10, [Parameter(Mandatory = $false)] [int]$IntervalSeconds = 1 ) End { $elapsed = 0 while ($elapsed -lt $TimeoutSeconds) { if (Test-DevPort -Port $Port) { return $true } Start-Sleep -Seconds $IntervalSeconds $elapsed += $IntervalSeconds } return $false } } Function Write-Development { <# .SYNOPSIS Writes a development message with appropriate icon and color .DESCRIPTION Displays a message for development utilities with icon and color based on type .PARAMETER Message The message to display .PARAMETER Type The type of message (Info, Error, Success, Warning, Step, Header). Default is Info. .EXAMPLE Write-Development -Message "Server started successfully!" -Type Success .EXAMPLE Write-Development -Message "An error occurred while starting the server." -Type Error .EXAMPLE Write-Development -Message "This is an information message." #> [CmdletBinding()] [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingWriteHost', '', Justification = 'Write-Host is appropriate for colored user output in development utilities')] param( [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true)] [AllowNull()] [AllowEmptyString()] [string]$Message, [ValidateSet('Info','Error','Success','Warning','Step','Header')] [string]$Type = 'Info' ) Process { switch ($Type) { 'Error' { $icon = Get-ModuleIcon 'Error'; $color = $script:ModuleColorError } 'Success' { $icon = Get-ModuleIcon 'CheckMark'; $color = $script:ModuleColorSuccess } 'Warning' { $icon = Get-ModuleIcon 'Warning'; $color = $script:ModuleColorWarning } 'Step' { $icon = Get-ModuleIcon 'Rocket'; $color = $script:ModuleColorStep } 'Header' { $icon = Get-ModuleIcon 'Celebration'; $color = $script:ModuleColorHeader } default { $icon = Get-ModuleIcon 'Info'; $color = $script:ModuleColorInfo } } if($Type -in @('Header')) { Write-Host "" Write-Host "$icon $Message" -ForegroundColor $color Write-Host "" } else { Write-Host "$icon $Message" -ForegroundColor $color } } } Function Start-Laravel { <# .SYNOPSIS Starts the complete Laravel development environment .DESCRIPTION Starts all Laravel development components: Web server, Vite, and Queue workers .PARAMETER Path The root directory of the Laravel application .PARAMETER WebPort Port for the Laravel web server (default: 8000) .PARAMETER VitePort Port for the Vite development server (default: 5173) .PARAMETER Queue Queue name for Laravel queue workers (default: "default") .PARAMETER TimeoutSeconds Timeout in seconds for server startup checks (default: 30) .PARAMETER Force Force stop any existing processes on the specified ports .EXAMPLE Start-Laravel -Path "C:\path\to\laravel" Starts Laravel with default settings (web on 8000, vite on 5173, default queue) .EXAMPLE Start-Laravel -Path "C:\path\to\laravel" -WebPort 8080 -VitePort 3000 -Queue "emails" -Force Starts Laravel with custom ports and queue, forcing stop of existing processes #> [CmdletBinding()] param( [Parameter()] [ValidateScript({ Test-LaravelPath -Path $_ })] [string]$Path = '.', [Parameter()] [int]$WebPort = 8000, [Parameter()] [int]$VitePort = 5173, [Parameter()] [string]$Queue = "default", [Parameter()] [int]$TimeoutSeconds = 30, [Parameter()] [switch]$Force ) Begin { Write-Development -Message "Starting Laravel Development Environment" -Type Header $success = $true try { # Start Laravel Web Server Write-Development -Message "Starting Laravel web server..." -Type Info $webResult = Start-LaravelWeb -Path $Path -Port $WebPort -TimeoutSeconds $TimeoutSeconds -Force:$Force if (-not $webResult) { Write-Development -Message "Failed to start Laravel web server" -Type Error $success = $false } # Start Vite Development Server Write-Development -Message "Starting Vite development server..." -Type Info $viteResult = Start-LaravelVite -Path $Path -Port $VitePort -LaravelPort $WebPort -TimeoutSeconds $TimeoutSeconds -Force:$Force if (-not $viteResult) { Write-Development -Message "Failed to start Vite development server" -Type Error $success = $false } # Start Laravel Queue Worker Write-Development -Message "Starting Laravel queue worker..." -Type Info $queueResult = Start-LaravelQueue -Path $Path -Queue $Queue -Force:$Force if (-not $queueResult) { Write-Development -Message "Failed to start Laravel queue worker" -Type Error $success = $false } if ($success) { Write-Development -Message "Laravel development environment started successfully!" -Type Success Write-Development -Message "Services:" -Type Info Write-Development -Message " - Web Server: http://localhost:$WebPort" -Type Info Write-Development -Message " - Vite Server: http://localhost:$VitePort" -Type Info Write-Development -Message " - Queue Worker: $Queue queue" -Type Info } else { Write-Development -Message "Some Laravel services failed to start. Check the logs above." -Type Warning } return $success } catch { Write-Development -Message "Failed to start Laravel development environment: $($_.Exception.Message)" -Type Error return $false } } } Function Start-LaravelQueue { <# .SYNOPSIS Starts the Laravel queue worker .DESCRIPTION Starts Laravel's queue worker to process background jobs .PARAMETER Path The root directory of the Laravel application .PARAMETER Queue The queue name to process (default: default) .PARAMETER ConnectionName The queue connection to use (default: uses Laravel's default) .PARAMETER MaxJobs Maximum number of jobs to process before restarting (default: 1000) .PARAMETER MaxTime Maximum time in seconds the worker should run (default: 3600) .PARAMETER Sleep Number of seconds to sleep when no jobs are available (default: 3) .PARAMETER Timeout Number of seconds a child process can run (default: 60) .PARAMETER Force Force stop any existing queue workers .EXAMPLE Start-LaravelQueue -Path "C:\path\to\laravel" Starts Laravel queue worker with default settings .EXAMPLE Start-LaravelQueue -Path "C:\path\to\laravel" -Queue "emails" -MaxJobs 500 Starts Laravel queue worker for the "emails" queue with max 500 jobs #> [CmdletBinding()] [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSReviewUnusedParameter', 'Path', Justification = 'Used via $Using:Path in Start-Job ScriptBlock')] param( [Parameter()] [ValidateScript({ Test-LaravelPath -Path $_ })] [string]$Path = '.', [Parameter()] [string]$Queue = "default", [Parameter()] [string]$ConnectionName, [Parameter()] [int]$MaxJobs = 1000, [Parameter()] [int]$MaxTime = 3600, [Parameter()] [int]$Sleep = 3, [Parameter()] [int]$Timeout = 60, [Parameter()] [switch]$Force ) Begin { Write-Development -Message "Starting Laravel queue worker for queue '$Queue'..." -Type Step if ($Force) { Write-Development -Message "Stopping any existing queue workers..." -Type Info Stop-LaravelQueue -Queue $Queue -Force } # Build the artisan command $queueCommand = "queue:work" $queueArgs = @() if ($ConnectionName) { $queueArgs += $ConnectionName } $queueArgs += "--queue=$Queue" $queueArgs += "--max-jobs=$MaxJobs" $queueArgs += "--max-time=$MaxTime" $queueArgs += "--sleep=$Sleep" $queueArgs += "--timeout=$Timeout" $queueArgs += "--verbose" # Start queue worker $queueJob = Start-Job -ScriptBlock { Set-Location $Using:Path & php artisan $Using:queueCommand $Using:queueArgs } # Give it a moment to start Start-Sleep -Seconds 2 # Check if job is running if ($queueJob.State -eq "Running") { Write-Development -Message "Laravel queue worker started successfully for queue '$Queue'" -Type Success Write-Development -Message "Worker will process up to $MaxJobs jobs or run for $MaxTime seconds" -Type Info return $queueJob } else { Write-Development -Message "Failed to start Laravel queue worker" -Type Error # Get job output for debugging $jobOutput = Receive-Job $queueJob -ErrorAction SilentlyContinue if ($jobOutput) { Write-Development -Message "Queue worker output: $jobOutput" -Type Error } Remove-Job $queueJob -ErrorAction SilentlyContinue return $null } } } Function Start-LaravelVite { <# .SYNOPSIS Starts the Laravel Vite development server .DESCRIPTION Starts the Vite development server for Laravel frontend assets .PARAMETER Path The root directory of the Laravel application .PARAMETER Port The port number to start the Vite server on (default: 5173) .PARAMETER LaravelPort The Laravel web server port for proper integration (default: 8000) .PARAMETER TimeoutSeconds How long to wait for the server to start (default: 15) .PARAMETER Force Force stop any existing processes on the specified port .EXAMPLE Start-LaravelVite -Path "C:\path\to\laravel" -Port 5173 -LaravelPort 8000 Starts Vite server on port 5173 integrated with Laravel on port 8000 #> [CmdletBinding()] [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSReviewUnusedParameter', 'Path', Justification = 'Used via $Using:Path in Start-Job ScriptBlock')] param( [Parameter()] [ValidateScript({ Test-LaravelPath -Path $_})] [string]$Path = '.', [Parameter()] [int]$Port = 5173, [Parameter()] [int]$LaravelPort = 8000, [Parameter()] [int]$TimeoutSeconds = 15, [Parameter()] [switch]$Force ) Begin { Write-Development -Message "Starting Laravel Vite server on port $Port..." -Type Step # Check if port is available if (Test-DevPort -Port $Port) { if ($Force) { Write-Development -Message "Port $Port is already in use. Force stopping processes..." -Type Warning Stop-DevProcessOnPort -Port $Port Start-Sleep -Seconds 2 } if (Test-DevPort -Port $Port) { Write-Development -Message "Unable to free port $Port. Please check what's using it and try again." -Type Error return $null } } # Start Vite server with output redirection to prevent job termination $viteJob = Start-Job -ScriptBlock { Set-Location $Using:Path # Set environment variables for Vite to ensure IPv4 binding $env:VITE_PORT = $Using:Port $env:VITE_HOST = "127.0.0.1" $env:VITE_DEV_SERVER_URL = "http://127.0.0.1:$($Using:Port)" # Start Vite with port specification using npx directly for proper argument handling npx vite --host 127.0.0.1 --port $Using:Port --strictPort 2>&1 } # Wait longer for Vite to start (it takes more time than Laravel) Write-Development -Message "Waiting for Laravel Vite server to start (timeout: $TimeoutSeconds seconds)..." -Type Info if (Wait-ForDevPort -Port $Port -TimeoutSeconds $TimeoutSeconds) { Write-Development -Message "Laravel Vite server running at http://127.0.0.1:$Port" -Type Success Write-Development -Message "Note: Access your Vue app via Laravel at http://127.0.0.1:$LaravelPort/" -Type Info return $viteJob } else { Write-Development -Message "Failed to start Laravel Vite server within $TimeoutSeconds seconds" -Type Error if ($viteJob) { # Get job output for debugging Start-Sleep -Seconds 2 # Give job time to produce output $jobOutput = Receive-Job $viteJob -ErrorAction SilentlyContinue if ($jobOutput) { Write-Development -Message "Vite job output: $jobOutput" -Type Error } # Also check job state and errors if ($viteJob.State -eq "Failed") { $jobErrors = $viteJob.ChildJobs[0].Error if ($jobErrors) { Write-Development -Message "Vite job errors: $jobErrors" -Type Error } } Stop-Job $viteJob -ErrorAction SilentlyContinue Remove-Job $viteJob -ErrorAction SilentlyContinue } return $null } } } Function Start-LaravelWeb { <# .SYNOPSIS Starts the Laravel web development server .DESCRIPTION Starts the Laravel artisan serve command on the specified port with proper error handling .PARAMETER Path The root directory of the Laravel application .PARAMETER Port The port number to start the Laravel server on (default: 8000) .PARAMETER TimeoutSeconds How long to wait for the server to start (default: 10) .PARAMETER Force Force stop any existing processes on the specified port .EXAMPLE Start-LaravelWeb -Path "C:\path\to\laravel" -Port 8000 Starts Laravel web server on port 8000 .EXAMPLE Start-LaravelWeb -Path "C:\path\to\laravel" -Port 8001 -TimeoutSeconds 15 Starts Laravel web server on port 8001 with 15 second timeout #> [CmdletBinding()] [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSReviewUnusedParameter', 'Path', Justification = 'Used via $Using:Path in Start-Job ScriptBlock')] param( [Parameter()] [ValidateScript({ Test-LaravelPath -Path $_})] [string]$Path = '.', [Parameter()] [int]$Port = 8000, [Parameter()] [int]$TimeoutSeconds = 10, [Parameter()] [switch]$Force ) Begin { Write-Development -Message "Starting Laravel web server on port $Port..." -Type Step # Check if port is available if (Test-DevPort -Port $Port) { if ($Force) { Write-Development -Message "Port $Port is already in use. Force stopping processes..." -Type Warning Stop-DevProcessOnPort -Port $Port Start-Sleep -Seconds 2 } if (Test-DevPort -Port $Port) { Write-Development -Message "Unable to free port $Port. Please check what's using it and try again." -Type Error return $null } } # Start Laravel server $laravelJob = Start-Job -ScriptBlock { Set-Location $Using:Path php artisan serve --port=$Using:Port --host=127.0.0.1 } # Wait for server to start with configurable timeout Write-Development -Message "Waiting for Laravel web server to start (timeout: $TimeoutSeconds seconds)..." -Type Info if (Wait-ForDevPort -Port $Port -TimeoutSeconds $TimeoutSeconds) { Write-Development -Message "Laravel web server running at http://127.0.0.1:$Port" -Type Success return $laravelJob } else { Write-Development -Message "Failed to start Laravel web server within $TimeoutSeconds seconds" -Type Error # Get job output for debugging Start-Sleep -Seconds 1 $jobOutput = Receive-Job $laravelJob -ErrorAction SilentlyContinue if ($jobOutput) { Write-Development -Message "Laravel job output: $jobOutput" -Type Error } if ($laravelJob) { Stop-Job $laravelJob -ErrorAction SilentlyContinue Remove-Job $laravelJob -ErrorAction SilentlyContinue } return $null } } } Function Stop-Laravel { <# .SYNOPSIS Stops the complete Laravel development environment .DESCRIPTION Stops all Laravel development components: Web server, Vite, and Queue workers .PARAMETER Path The root directory of the Laravel application .PARAMETER WebPort Port for the Laravel web server (default: 8000) .PARAMETER VitePort Port for the Vite development server (default: 5173) .PARAMETER Queue Queue name for Laravel queue workers (optional - stops all if not specified) .PARAMETER Force Force stop processes without graceful shutdown .EXAMPLE Stop-Laravel Stops all Laravel services with default settings .EXAMPLE Stop-Laravel -WebPort 8080 -VitePort 3000 -Queue "emails" -Force Forcefully stops Laravel services with custom ports and specific queue #> [CmdletBinding()] param( [Parameter()] [int]$WebPort = 8000, [Parameter()] [int]$VitePort = 5173, [Parameter()] [string]$Queue, [Parameter()] [switch]$Force ) Begin { Write-Development -Message "Stopping Laravel Development Environment" -Type Header $success = $true try { # Stop Laravel Web Server Write-Development -Message "Stopping Laravel web server..." -Type Info $webResult = Stop-LaravelWeb -Port $WebPort -Force:$Force if (-not $webResult) { Write-Development -Message "Issues stopping Laravel web server" -Type Warning $success = $false } # Stop Vite Development Server Write-Development -Message "Stopping Vite development server..." -Type Info $viteResult = Stop-LaravelVite -Port $VitePort -Force:$Force if (-not $viteResult) { Write-Development -Message "Issues stopping Vite development server" -Type Warning $success = $false } # Stop Laravel Queue Worker Write-Development -Message "Stopping Laravel queue worker..." -Type Info if ($Queue) { $queueResult = Stop-LaravelQueue -Queue $Queue -Force:$Force } else { $queueResult = Stop-LaravelQueue -Force:$Force } if (-not $queueResult) { Write-Development -Message "Issues stopping Laravel queue worker" -Type Warning $success = $false } if ($success) { Write-Development -Message "Laravel development environment stopped successfully!" -Type Success } else { Write-Development -Message "Some Laravel services may still be running. Check the logs above." -Type Warning } return $success } catch { Write-Development -Message "Failed to stop Laravel development environment: $($_.Exception.Message)" -Type Error return $false } } } Function Stop-LaravelQueue { <# .SYNOPSIS Stops the Laravel queue worker .DESCRIPTION Stops Laravel queue worker processes gracefully .PARAMETER Path The root directory of the Laravel application .PARAMETER Force Force stop without confirmation .PARAMETER Queue Specific queue name to target (optional) .EXAMPLE Stop-LaravelQueue Stops all Laravel queue workers .EXAMPLE Stop-LaravelQueue -Queue "emails" -Force Force stops Laravel queue workers for the "emails" queue #> [CmdletBinding()] param( [Parameter()] [switch]$Force, [Parameter()] [string]$Queue ) Begin { Write-Development -Message "Stopping Laravel queue worker$(if($Queue) { " for queue '$Queue'" })..." -Type Step try { # Find queue worker processes $queueProcesses = Get-Process | Where-Object { $_.ProcessName -match "php" -and $_.CommandLine -match "artisan.*queue:work" } if ($Queue) { $queueProcesses = $queueProcesses | Where-Object { $_.CommandLine -match "queue.*$Queue" } } if (-not $queueProcesses) { Write-Development -Message "No Laravel queue worker processes found$(if($Queue) { " for queue '$Queue'" })" -Type Info return $true } $stoppedAny = $false foreach ($process in $queueProcesses) { if (-not $Force) { $confirmation = Read-Host "Stop Laravel queue worker process (PID: $($process.Id))? [Y/n]" if ($confirmation -eq "n" -or $confirmation -eq "N") { Write-Development -Message "Skipping process $($process.Id)" -Type Info continue } } Write-Development -Message "Stopping Laravel queue worker process (PID: $($process.Id))" -Type Step try { # Try graceful stop first $process.CloseMainWindow() Start-Sleep -Seconds 3 # Check if process is still running $runningProcess = Get-Process -Id $process.Id -ErrorAction SilentlyContinue if ($runningProcess) { # Force kill if still running Stop-Process -Id $process.Id -Force Start-Sleep -Seconds 1 } Write-Development -Message "Stopped Laravel queue worker (PID: $($process.Id))" -Type Success $stoppedAny = $true } catch { Write-Development -Message "Failed to stop process $($process.Id)`: $($_.Exception.Message)" -Type Error } } if ($stoppedAny) { Write-Development -Message "Laravel queue worker$(if($Queue) { "s for queue '$Queue'" }) stopped successfully" -Type Success return $true } else { Write-Development -Message "No Laravel queue workers were stopped" -Type Warning return $false } } catch { Write-Development -Message "Failed to stop Laravel queue worker: $($_.Exception.Message)" -Type Error return $false } } } Function Stop-LaravelVite { <# .SYNOPSIS Stops the Laravel Vite development server .DESCRIPTION Stops processes running on the Laravel Vite server port .PARAMETER Path The root directory of the Laravel application .PARAMETER Port The port number where Laravel Vite server is running (default: 5173) .PARAMETER Force Force stop without confirmation .EXAMPLE Stop-LaravelVite -Port 5173 Stops Laravel Vite server on port 5173 .EXAMPLE Stop-LaravelVite -Force Force stops Laravel Vite server with default port #> [CmdletBinding()] param( [Parameter()] [int]$Port = 5173, [Parameter()] [switch]$Force ) Begin { Write-Development -Message "Stopping Laravel Vite server on port $Port..." -Type Step try { if (-not (Test-DevPort -Port $Port)) { Write-Development -Message "Laravel Vite server is not running on port $Port" -Type Info return $true } # Get processes using the port $processes = Get-NetTCPConnection -LocalPort $Port -ErrorAction SilentlyContinue | Select-Object -ExpandProperty OwningProcess -Unique if (-not $processes) { Write-Development -Message "Port $Port appears to be in use but no processes found" -Type Warning return $false } $stoppedAny = $false foreach ($processId in $processes) { if ($processId -and $processId -ne 0) { $process = Get-Process -Id $processId -ErrorAction SilentlyContinue if ($process) { # Verify this is likely a Vite server process $isViteProcess = $process.ProcessName -match "(node|npm)" -or $process.CommandLine -match "(vite|npm.*dev)" if ($isViteProcess -or $Force) { if (-not $Force) { $confirmation = Read-Host "Stop Laravel Vite process '$($process.Name)' (PID: $processId)? [Y/n]" if ($confirmation -eq "n" -or $confirmation -eq "N") { Write-Development -Message "Skipping process $processId" -Type Info continue } } Write-Development -Message "Stopping Laravel Vite process '$($process.Name)' (PID: $processId)" -Type Step try { # Try graceful stop first $process.CloseMainWindow() Start-Sleep -Seconds 2 # Check if process is still running $runningProcess = Get-Process -Id $processId -ErrorAction SilentlyContinue if ($runningProcess) { # Force kill if still running Stop-Process -Id $processId -Force Start-Sleep -Seconds 1 } Write-Development -Message "Stopped Laravel Vite server (PID: $processId)" -Type Success $stoppedAny = $true } catch { Write-Development -Message "Failed to stop process $processId`: $($_.Exception.Message)" -Type Error } } else { Write-Development -Message "Process '$($process.Name)' (PID: $processId) on port $Port doesn't appear to be a Laravel Vite server" -Type Warning Write-Development -Message "Use -Force to stop it anyway" -Type Info } } } } # Verify port is now free Start-Sleep -Seconds 1 if (-not (Test-DevPort -Port $Port)) { if ($stoppedAny) { Write-Development -Message "Laravel Vite server stopped successfully" -Type Success } return $true } else { Write-Development -Message "Laravel Vite port $Port is still in use after attempting to stop processes" -Type Warning return $false } } catch { Write-Development -Message "Failed to stop Laravel Vite server on port $Port`: $($_.Exception.Message)" -Type Error return $false } } } Function Stop-LaravelWeb { <# .SYNOPSIS Stops the Laravel web development server .DESCRIPTION Stops processes running on the Laravel web server port .PARAMETER Path The root directory of the Laravel application .PARAMETER Port The port number where Laravel web server is running (default: 8000) .PARAMETER Force Force stop without confirmation .EXAMPLE Stop-LaravelWeb -Port 8000 Stops Laravel web server on port 8000 .EXAMPLE Stop-LaravelWeb -Port 8001 -Force Force stops Laravel web server on port 8001 #> [CmdletBinding()] param( [Parameter()] [int]$Port = 8000, [Parameter()] [switch]$Force ) Begin { Write-Development -Message "Stopping Laravel web server on port $Port..." -Type Step try { if (-not (Test-DevPort -Port $Port)) { Write-Development -Message "Laravel web server is not running on port $Port" -Type Info return $true } # Get processes using the port $processes = Get-NetTCPConnection -LocalPort $Port -ErrorAction SilentlyContinue | Select-Object -ExpandProperty OwningProcess -Unique if (-not $processes) { Write-Development -Message "Port $Port appears to be in use but no processes found" -Type Warning return $false } $stoppedAny = $false foreach ($processId in $processes) { if ($processId -and $processId -ne 0) { $process = Get-Process -Id $processId -ErrorAction SilentlyContinue if ($process) { # Verify this is likely a Laravel web server process $isLaravelProcess = $process.ProcessName -match "(php|artisan)" -or $process.CommandLine -match "artisan serve" if ($isLaravelProcess -or $Force) { if (-not $Force) { $confirmation = Read-Host "Stop Laravel web process '$($process.Name)' (PID: $processId)? [Y/n]" if ($confirmation -eq "n" -or $confirmation -eq "N") { Write-Development -Message "Skipping process $processId" -Type Info continue } } Write-Development -Message "Stopping Laravel web process '$($process.Name)' (PID: $processId)" -Type Step try { # Try graceful stop first $process.CloseMainWindow() Start-Sleep -Seconds 2 # Check if process is still running $runningProcess = Get-Process -Id $processId -ErrorAction SilentlyContinue if ($runningProcess) { # Force kill if still running Stop-Process -Id $processId -Force Start-Sleep -Seconds 1 } Write-Development -Message "Stopped Laravel web server (PID: $processId)" -Type Success $stoppedAny = $true } catch { Write-Development -Message "Failed to stop process $processId`: $($_.Exception.Message)" -Type Error } } else { Write-Development -Message "Process '$($process.Name)' (PID: $processId) on port $Port doesn't appear to be a Laravel web server" -Type Warning Write-Development -Message "Use -Force to stop it anyway" -Type Info } } } } # Verify port is now free Start-Sleep -Seconds 1 if (-not (Test-DevPort -Port $Port)) { if ($stoppedAny) { Write-Development -Message "Laravel web server stopped successfully" -Type Success } return $true } else { Write-Development -Message "Laravel web port $Port is still in use after attempting to stop processes" -Type Warning return $false } } catch { Write-Development -Message "Failed to stop Laravel web server on port $Port`: $($_.Exception.Message)" -Type Error return $false } } } Function Test-Laravel { <# .SYNOPSIS Tests if the complete Laravel development environment is running .DESCRIPTION Checks if all Laravel development components are active: Web server, Vite, and Queue workers .PARAMETER Path The root directory of the Laravel application .PARAMETER WebPort Port for the Laravel web server (default: 8000) .PARAMETER VitePort Port for the Vite development server (default: 5173) .PARAMETER Queue Queue name for Laravel queue workers (optional - checks all if not specified) .EXAMPLE Test-Laravel Tests all Laravel services with default settings .EXAMPLE Test-Laravel -WebPort 8080 -VitePort 3000 -Queue "emails" Tests Laravel services with custom ports and specific queue #> [CmdletBinding()] param( [Parameter()] [int]$WebPort = 8000, [Parameter()] [int]$VitePort = 5173, [Parameter()] [string]$Queue ) Begin { Write-Development -Message "Testing Laravel Development Environment" -Type Header $webStatus = $false $viteStatus = $false $queueStatus = $false try { # Test Laravel Web Server Write-Development -Message "Testing Laravel web server..." -Type Info $webStatus = Test-LaravelWeb -Port $WebPort # Test Vite Development Server Write-Development -Message "Testing Vite development server..." -Type Info $viteStatus = Test-LaravelVite -Port $VitePort # Test Laravel Queue Worker Write-Development -Message "Testing Laravel queue worker..." -Type Info if ($Queue) { $queueStatus = Test-LaravelQueue -Queue $Queue } else { $queueStatus = Test-LaravelQueue } # Summary Write-Development -Message "" -Type Info Write-Development -Message "Laravel Development Environment Status:" -Type Info Write-Development -Message " - Web Server (port $WebPort): $(if($webStatus) { 'Running' } else { 'Stopped' })" -Type Info Write-Development -Message " - Vite Server (port $VitePort): $(if($viteStatus) { 'Running' } else { 'Stopped' })" -Type Info Write-Development -Message " - Queue Worker$(if($Queue) { " ($Queue)" }): $(if($queueStatus) { 'Running' } else { 'Stopped' })" -Type Info $allRunning = $webStatus -and $viteStatus -and $queueStatus if ($allRunning) { Write-Development -Message "All Laravel services are running!" -Type Success } else { $runningCount = @($webStatus, $viteStatus, $queueStatus) | Where-Object { $_ } | Measure-Object | Select-Object -ExpandProperty Count Write-Development -Message "$runningCount of 3 Laravel services are running" -Type Warning } return @{ Web = $webStatus Vite = $viteStatus Queue = $queueStatus All = $allRunning } } catch { Write-Development -Message "Failed to test Laravel development environment: $($_.Exception.Message)" -Type Error return @{ Web = $false Vite = $false Queue = $false All = $false } } } } Function Test-LaravelQueue { <# .SYNOPSIS Tests if the Laravel queue worker is running .DESCRIPTION Checks if Laravel queue worker processes are active .PARAMETER Path The root directory of the Laravel application .PARAMETER Queue Specific queue name to check (optional) .EXAMPLE Test-LaravelQueue Tests if any Laravel queue workers are running .EXAMPLE Test-LaravelQueue -Queue "emails" Tests if Laravel queue workers are running for the "emails" queue #> [CmdletBinding()] [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingWMICmdlet', '', Justification = 'Test file uses WMI for process detection')] param( [Parameter()] [string]$Queue ) Begin { Write-Development -Message "Testing Laravel queue worker$(if($Queue) { " for queue '$Queue'" })..." -Type Info try { # Find queue worker processes using WMI for more reliable command line detection # Note: Using WMI for compatibility with existing tests and broad system support $queueProcesses = Get-WmiObject Win32_Process | Where-Object { $_.Name -match "php" -and $_.CommandLine -match "artisan.*queue:work" } if ($Queue) { $queueProcesses = $queueProcesses | Where-Object { $_.CommandLine -match "queue.*$Queue" } } if ($queueProcesses) { $processCount = $queueProcesses.Count Write-Development -Message "Found $processCount Laravel queue worker process$(if($processCount -gt 1) { 'es' })$(if($Queue) { " for queue '$Queue'" })" -Type Success foreach ($process in $queueProcesses) { Write-Development -Message " - PID: $($process.ProcessId), Started: $($process.CreationDate)" -Type Info } return $true } else { Write-Development -Message "No Laravel queue worker processes found$(if($Queue) { " for queue '$Queue'" })" -Type Info return $false } } catch { Write-Development -Message "Failed to test Laravel queue worker: $($_.Exception.Message)" -Type Error return $false } } } Function Test-LaravelVite { <# .SYNOPSIS Tests if the Laravel Vite development server is running .DESCRIPTION Checks if the Laravel Vite server is responding on the specified port .PARAMETER Port The port number to test (default: 5173) .EXAMPLE Test-LaravelVite -Port 5173 Tests if Laravel Vite server is running on port 5173 #> [CmdletBinding()] param( [Parameter()] [int]$Port = 5173 ) Begin { Write-Development -Message "Testing Laravel Vite server on port $Port..." -Type Info if (Test-DevPort -Port $Port) { try { # Try to make a simple HTTP request to verify it's actually Vite $response = Invoke-WebRequest -Uri "http://127.0.0.1:$Port" -Method HEAD -TimeoutSec 5 -ErrorAction SilentlyContinue if ($response) { Write-Development -Message "Laravel Vite server is running and responding on port $Port" -Type Success return $true } else { Write-Development -Message "Port $Port is in use but not responding to HTTP requests" -Type Warning return $false } } catch { Write-Development -Message "Port $Port is in use but HTTP test failed: $($_.Exception.Message)" -Type Warning return $false } } else { Write-Development -Message "Laravel Vite server is not running on port $Port" -Type Info return $false } } } Function Test-LaravelWeb { <# .SYNOPSIS Tests if the Laravel web development server is running .DESCRIPTION Checks if the Laravel web server is responding on the specified port .PARAMETER Path The root directory of the Laravel application .PARAMETER Port The port number to test (default: 8000) .EXAMPLE Test-LaravelWeb -Port 8000 Tests if Laravel web server is running on port 8000 #> [CmdletBinding()] param( [Parameter()] [int]$Port = 8000 ) Begin { Write-Development -Message "Testing Laravel web server on port $Port..." -Type Info if (Test-DevPort -Port $Port) { try { # Try to make a simple HTTP request to verify it's actually Laravel $response = Invoke-WebRequest -Uri "http://127.0.0.1:$Port" -Method HEAD -TimeoutSec 5 -ErrorAction SilentlyContinue if ($response) { Write-Development -Message "Laravel web server is running and responding on port $Port" -Type Success return $true } else { Write-Development -Message "Port $Port is in use but not responding to HTTP requests" -Type Warning return $false } } catch { Write-Development -Message "Port $Port is in use but HTTP test failed: $($_.Exception.Message)" -Type Warning return $false } } else { Write-Development -Message "Laravel web server is not running on port $Port" -Type Info return $false } } } |