Public/New-PstScript.ps1
|
function New-PstScript { <# .SYNOPSIS Creates a new script file from a template with configurable complexity levels. .DESCRIPTION This function takes a name parameter, copies a script template based on the specified complexity level, and saves it with the specified name. Complexity Levels: - Basic: Simple script with linear execution and minimal structure - Moderate: Script with parameter validation, basic error handling, and logging - Complex: Advanced script with begin/process/end blocks, pipeline support, and comprehensive features .PARAMETER Name (Required) The name for the new script file. .PARAMETER ComplexityLevel (Optional) The complexity level of the script template. Valid values: Basic, Moderate, Complex. Defaults to Moderate for typical script scenarios. .PARAMETER Force (Optional) Forces creation even if file exists, useful in automation scenarios. .PARAMETER IncludeProjectLayout (Optional) Creates a complete project structure including src/, tests/, docs/ directories and supporting files. .EXAMPLE New-PstScript -Name MyScript Creates a new moderate complexity script file named MyScript.ps1. .EXAMPLE New-PstScript -Name SimpleTask -ComplexityLevel Basic Creates a simple script with minimal structure for basic automation. .EXAMPLE New-PstScript -Name AdvancedProcessor -ComplexityLevel Complex Creates an advanced script with full begin/process/end structure and pipeline support. .EXAMPLE New-PstScript -Name ProcessData -IncludeProjectLayout Creates a complete project structure with ProcessData.ps1 in src/, test files, documentation, and .gitignore. .NOTES Ensure that the appropriate template files exist in the Resources/Samples directory. Template files: Script-Basic.ps1, Script-Moderate.ps1, Script-Complex.ps1 .LINK For more information on PowerShell scripting, visit: https://docs.microsoft.com/en-us/powershell/scripting/overview #> [CmdletBinding(SupportsShouldProcess)] param ( [Parameter(Mandatory = $true, Position = 0, HelpMessage = "The name for the new script file.")] [ValidatePattern('^[A-Za-z][A-Za-z0-9\-_]*$')] [string]$Name, [Parameter(Mandatory = $false)] [ValidateSet("Basic", "Moderate", "Complex")] [string]$ComplexityLevel = "Moderate", [Parameter(Mandatory = $false)] [switch]$Force, [Parameter(Mandatory = $false)] [switch]$IncludeProjectLayout ) begin { Write-Debug -Message "Begin '$($MyInvocation.MyCommand.Name)' at '$(Get-Date)'" # Select template based on complexity level $templateFileName = switch ($ComplexityLevel) { "Basic" { "Script-Basic.ps1" } "Moderate" { "Script-Moderate.ps1" } "Complex" { "Script-Complex.ps1" } default { "Script-Moderate.ps1" } # Fallback to Moderate } $templatePath = $Samples[$templateFileName] # Determine file path based on project layout option if ($IncludeProjectLayout) { $projectRoot = $Name $newFilePath = Join-Path $projectRoot "src" "$Name.ps1" } else { $newFilePath = "$Name.ps1" } Write-Verbose "Using complexity level: $ComplexityLevel" Write-Verbose "Selected template: $templateFileName" if (-not $templatePath) { throw "Template '$templateFileName' not found in Samples hashtable. Available templates: $($Samples.Keys -join ', ')" } if (-not (Test-Path $templatePath)) { throw "Template file not found: $templatePath" } else { Write-Verbose "Template file found: $templatePath" } } process { try { # Check if we're in automation mode - inline check to avoid scope issues $isAutomationMode = $Force -or ($Global:PstAutomationMode -eq $true) -or ($env:CI -eq 'true') -or ($env:GITHUB_ACTIONS -eq 'true') -or ($Host.Name -eq 'ServerRemoteHost') -or (-not [Environment]::UserInteractive) -or ($global:ConfirmPreference -eq 'None') # Create project structure if IncludeProjectLayout is specified if ($IncludeProjectLayout) { $projectRoot = $Name # Create directory structure $directories = @( $projectRoot, (Join-Path $projectRoot "src"), (Join-Path $projectRoot "tests"), (Join-Path $projectRoot "tests" "Unit"), (Join-Path $projectRoot "tests" "Integration"), (Join-Path $projectRoot "docs") ) foreach ($dir in $directories) { if (-not (Test-Path $dir)) { if ($isAutomationMode -or $PSCmdlet.ShouldProcess($dir, "Create directory")) { New-Item -Path $dir -ItemType Directory -Force | Out-Null Write-Verbose "Created directory: $dir" } } } # Create README.md in root $readmeContent = @" # $Name PowerShell script for [brief description]. ## Installation ``````powershell .\src\$Name.ps1 `````` ## Usage ``````powershell .\src\$Name.ps1 -Parameter Value `````` ## Testing ``````powershell Invoke-Pester -Path .\tests\ `````` ## License [Specify license] "@ $readmePath = Join-Path $projectRoot "README.md" if ($isAutomationMode -or $PSCmdlet.ShouldProcess($readmePath, "Create README.md")) { $readmeContent | Set-Content -Path $readmePath Write-Verbose "Created: $readmePath" } # Create .gitignore $gitignoreContent = @" # Build artifacts build/ *.nupkg # Test results TestResults*.xml coverage*.xml # Temporary files temp/ *.tmp *.log "@ $gitignorePath = Join-Path $projectRoot ".gitignore" if ($isAutomationMode -or $PSCmdlet.ShouldProcess($gitignorePath, "Create .gitignore")) { $gitignoreContent | Set-Content -Path $gitignorePath Write-Verbose "Created: $gitignorePath" } # Create docs README.md $docsReadmeContent = @" # $Name Documentation ## Overview [Script overview and purpose] ## Parameters [Parameter documentation] ## Examples [Usage examples] ## Notes [Additional notes] "@ $docsReadmePath = Join-Path $projectRoot "docs" "README.md" if ($isAutomationMode -or $PSCmdlet.ShouldProcess($docsReadmePath, "Create docs README.md")) { $docsReadmeContent | Set-Content -Path $docsReadmePath Write-Verbose "Created: $docsReadmePath" } # Create test file $testContent = @" BeforeAll { `$scriptPath = "`$PSScriptRoot\..\..\src\$Name.ps1" } Describe "$Name" { Context "When executed with valid parameters" { It "Should execute without errors" { # Arrange # Act # Assert `$true | Should -Be `$true } } Context "When executed with invalid parameters" { It "Should handle errors gracefully" { # Arrange & Act & Assert `$true | Should -Be `$true } } } "@ $testFilePath = Join-Path $projectRoot "tests" "Unit" "$Name.Tests.ps1" if ($isAutomationMode -or $PSCmdlet.ShouldProcess($testFilePath, "Create test file")) { $testContent | Set-Content -Path $testFilePath Write-Verbose "Created: $testFilePath" } } # Check if file already exists if ((Test-Path $newFilePath) -and -not $isAutomationMode) { if (-not $PSCmdlet.ShouldProcess($newFilePath, "Overwrite existing script file")) { Write-Warning "Operation cancelled by user." return } } elseif ((Test-Path $newFilePath) -and $isAutomationMode -and -not $Force) { Write-Warning "File '$newFilePath' already exists. Use -Force to overwrite in automation mode." return } # Proceed with file creation - skip ShouldProcess in automation mode if ($isAutomationMode -or $PSCmdlet.ShouldProcess($newFilePath, "Create new script file")) { # Create parent directory if it doesn't exist (for project layout) $parentDir = Split-Path -Path $newFilePath -Parent if ($parentDir -and -not (Test-Path $parentDir)) { New-Item -Path $parentDir -ItemType Directory -Force | Out-Null } Write-Information -Message "Copy-Item -Path '$($templatePath)' -Destination '$(Join-Path (Get-Location) $newFilePath)'" Copy-Item -Path $templatePath -Destination $newFilePath if ($IncludeProjectLayout) { Write-Output "New script project created: $projectRoot" Write-Output " - Script file: $newFilePath" Write-Output " - Test file: tests\Unit\$Name.Tests.ps1" Write-Output " - Documentation: docs\README.md" } else { Write-Output "New script file created: $newFilePath" } } else { Write-Verbose "Operation cancelled or skipped." } } catch { if ($_.Exception -and $_.Exception.Message) { Write-Error "An error occurred: $($_.Exception.Message)" } else { Write-Error "An error occurred, but no additional information is available." } } } end { if ($?) { Write-Debug -Message "End '$($MyInvocation.MyCommand.Name)' at '$(Get-Date)'" } } } |