<# .SYNOPSIS Generates a manifest file listing all files recursively within the script's directory (or a specified source directory), using relative paths suitable for web deployment. # ... (rest of the help block) ... /> #> param( [Parameter(HelpMessage="[Optional] The root directory containing the files/folders to list. Defaults to this script's directory.")] [ValidateScript({Test-Path $_ -PathType Container})] [ValidateNotNullOrEmpty()] [string]$SourceDirectory = $PSScriptRoot, [Parameter(HelpMessage="[Optional] The full path where the generated manifest.txt file should be saved. Defaults to 'manifest.txt' in the SourceDirectory.")] [ValidateNotNullOrEmpty()] # Default to an empty string - we will explicitly check and calculate if empty [string]$ManifestFileOutputPath = "", [Parameter(HelpMessage="Optional list of file/directory names patterns to exclude (e.g., '*.log', '.git').")] [string[]]$Exclusions = @( 'manifest.txt', # Exclude the manifest itself! '.git', # Common version control folder '.vscode', # Common editor settings folder '*.tmp', # Temporary files '*.bak' # Backup files ) ) # --- Script Body --- # === Determine the final paths === # Validate Source Directory Explicitly (redundant with attribute but clearer) if ([string]::IsNullOrWhiteSpace($SourceDirectory)) { Write-Error "SourceDirectory parameter is empty. This usually means `$PSScriptRoot was not populated. Ensure you are running the saved .ps1 file directly (e.g., .\Generate-Manifest.ps1) and not pasting code into the console." return # Stop execution } # Determine the ManifestFileOutputPath if it wasn't provided or is empty $finalManifestPath = $ManifestFileOutputPath # Start with user input or default empty string if ([string]::IsNullOrWhiteSpace($finalManifestPath)) { Write-Host "INFO: ManifestFileOutputPath not provided or empty. Calculating default path..." try { $finalManifestPath = (Join-Path -Path $SourceDirectory -ChildPath 'manifest.txt' -ErrorAction Stop) # Check IMMEDIATELY if calculation resulted in empty/null if ([string]::IsNullOrWhiteSpace($finalManifestPath)) { throw "Calculated ManifestFileOutputPath is empty or whitespace. SourceDirectory might be invalid: '$SourceDirectory'" } Write-Host "INFO: Defaulting Output File To: $finalManifestPath" } catch { Write-Error "Failed to calculate default ManifestFileOutputPath based on SourceDirectory '$SourceDirectory'. Error: $($_.Exception.Message)" return } } else { # If user provided a path, resolve it to be safe try { $finalManifestPath = (Resolve-Path -Path $finalManifestPath -ErrorAction Stop).ProviderPath } catch { Write-Error "Failed to resolve the provided ManifestFileOutputPath '$ManifestFileOutputPath'. Error: $($_.Exception.Message)" return } } # --- At this point, $SourceDirectory and $finalManifestPath SHOULD be valid, non-empty paths --- Write-Host "Starting manifest generation..." Write-Host "Effective Source Directory: $SourceDirectory" Write-Host "Effective Output File: $finalManifestPath" # Use the calculated/resolved variable Write-Host "Excluding Patterns: $($Exclusions -join ', ')" # Get the fully resolved, absolute path for the source directory try { $resolvedSourceDir = (Resolve-Path -Path $SourceDirectory -ErrorAction Stop).ProviderPath if (-not $resolvedSourceDir.EndsWith([System.IO.Path]::DirectorySeparatorChar)) { $resolvedSourceDir += [System.IO.Path]::DirectorySeparatorChar } $sourceDirLength = $resolvedSourceDir.Length Write-Host "Resolved Source Path: $resolvedSourceDir" } catch { Write-Error "Failed to resolve source directory path '$SourceDirectory'. Error: $($_.Exception.Message)" return } # Find all *files* recursively Write-Host "Scanning for files..." try { if ((Get-Command Get-ChildItem).ParameterSets.Parameters.Name -contains 'File') { $fileItems = Get-ChildItem -Path $resolvedSourceDir -Recurse -File -Exclude $Exclusions -ErrorAction Stop } else { $fileItems = Get-ChildItem -Path $resolvedSourceDir -Recurse -Exclude $Exclusions -ErrorAction Stop | Where-Object { -not $_.PSIsContainer } } } catch { Write-Error "Error listing files in '$resolvedSourceDir'. Error: $($_.Exception.Message)" return } # Generate relative paths $relativePaths = $fileItems | ForEach-Object { $relativePath = $_.FullName.Substring($sourceDirLength) $relativePath = $relativePath.Replace([System.IO.Path]::DirectorySeparatorChar, '/') $relativePath } $fileCount = ($relativePaths | Measure-Object).Count Write-Host "Found $fileCount files matching criteria." # Ensure version.txt is included $versionFileName = "version.txt" $versionFileInList = $relativePaths | Where-Object { $_ -eq $versionFileName } $versionFileExists = Test-Path -Path (Join-Path -Path $resolvedSourceDir -ChildPath $versionFileName) -PathType Leaf if ($versionFileExists -and (-not $versionFileInList)) { Write-Host "INFO: Adding '$versionFileName' to the manifest." $relativePaths = @($versionFileName) + $relativePaths } elseif ($versionFileInList) { Write-Host "INFO: '$versionFileName' found during scan and is included." } elseif (-not $versionFileExists) { Write-Warning "Cannot find '$versionFileName' at the root of '$resolvedSourceDir'. It will NOT be included." } # Sort final content $finalManifestContent = $relativePaths | Where-Object { -not [string]::IsNullOrWhiteSpace($_) } | Sort-Object # --- Write Manifest File --- # FINAL CHECK before writing - using the guaranteed-to-be-processed variable if ([string]::IsNullOrWhiteSpace($finalManifestPath)) { # This check should ideally be redundant now, but keep as safeguard Write-Error "Internal Error: finalManifestPath is empty or whitespace just before writing the file. Cannot proceed." return } Write-Host "Generating manifest file: $finalManifestPath" try { # Ensure output directory exists $outputDir = Split-Path -Path $finalManifestPath -Parent -ErrorAction Stop if ($outputDir -and (-not (Test-Path -Path $outputDir -PathType Container))) { Write-Host "Creating output directory: $outputDir" New-Item -ItemType Directory -Path $outputDir -Force -ErrorAction Stop | Out-Null } # Write the file using ASCII encoding to avoid BOM issues with paths $finalManifestContent | Out-File -FilePath $finalManifestPath -Encoding ASCII -Force -ErrorAction Stop # Use finalManifestPath Write-Host "Manifest file generated successfully with $($finalManifestContent.Count) entries." Write-Host "-> $finalManifestPath" # Use finalManifestPath } catch { Write-Error "Failed to write manifest file '$finalManifestPath'. Error: $($_.Exception.Message)" # Use finalManifestPath } Write-Host "Manifest generation finished."