Public/New-PstModule.ps1
|
function New-PstModule { <# .SYNOPSIS Creates a new PowerShell module from a template or using intelligent code generation. .DESCRIPTION This function creates a new PowerShell module with proper structure. It supports two modes: 1. Template Mode (default): Unpacks a zip template and renames files 2. Generator Mode (-UseGenerator): Uses the intelligent code generation system to create the module root file (.psm1) with proper structure based on complexity level Complexity Levels (for Generator Mode): - Basic: Simple module with minimal structure - Standard: Module with proper loading, error handling, and exports - Advanced: Full module features including version checking, dependency management Module templates always include proper folder structure and manifest files. .PARAMETER Path (Optional) The destination path where the new module will be created. Defaults to current directory. .PARAMETER Complexity (Optional) The complexity level for code generation. Valid values: Basic, Standard, Advanced. Defaults to Standard. Only used with -UseGenerator switch. .PARAMETER UseGenerator (Optional) Switch to use the intelligent code generation system for the .psm1 file. .PARAMETER IncludeProjectLayout (Optional) Creates a complete project structure with src/, tests/, docs/ directories. .EXAMPLE New-PstModule -Path "C:\Modules\MyNewModule" Creates a new module using the template (legacy mode). .EXAMPLE New-PstModule -Path "C:\Modules\MyModule" -UseGenerator -Complexity Standard Creates a module with a generated .psm1 file using Standard complexity. .EXAMPLE New-PstModule -Path "C:\Modules\MyAwesomeModule" -IncludeProjectLayout Creates a complete project structure with the module in src/. .NOTES Template mode uses Module.zip from Resources/Samples. Generator mode creates the .psm1 content dynamically. .LINK https://docs.microsoft.com/en-us/powershell/scripting/developer/module/writing-a-windows-powershell-module #> [CmdletBinding(SupportsShouldProcess)] param ( [Parameter(Mandatory = $false, Position = 0, HelpMessage = "The destination path where the new module will be created.")] [string]$Path = (Get-Location), [Parameter(Mandatory = $false, HelpMessage = "Complexity level for code generation (Basic, Standard, Advanced).")] [ValidateSet("Basic", "Standard", "Advanced")] [string]$Complexity = "Standard", [Parameter(Mandatory = $false, HelpMessage = "Use the intelligent code generation system for the .psm1 file.")] [switch]$UseGenerator, [Parameter(Mandatory = $false)] [switch]$IncludeProjectLayout ) begin { Write-Debug -Message "Begin '$($MyInvocation.MyCommand.Name)' at '$(Get-Date)'" Write-Verbose -Message "Path: '$($Path)'" # Check if Path is empty if (-not $Path) { throw "Path cannot be empty." } # Determine actual module destination based on project layout option if ($IncludeProjectLayout) { $ModuleName = Split-Path -Path $Path -Leaf $ModuleDestination = Join-Path $Path "src" $ModuleName } else { $ModuleDestination = $Path $ModuleName = Split-Path -Path $Path -Leaf } # Check if the module destination is not empty (contains any files or folders) if (Test-Path -Path $ModuleDestination) { $existingItems = @(Get-ChildItem -Path $ModuleDestination -Force) if ($existingItems.Count -gt 0) { throw "The path '$ModuleDestination' is not empty. Please choose an empty directory." } } Write-Verbose -Message "ModuleName: '$($ModuleName)'" Write-Verbose -Message "ModuleDestination: '$($ModuleDestination)'" # Use the predefined file path from the $Samples hashtable if (-not $Samples.ContainsKey("Module.zip")) { throw "The file 'Module.zip' was not found in the Samples hashtable." } $ZipFilePath = $Samples["Module.zip"] Write-Verbose -Message "ZipFilePath: '$($ZipFilePath)'" $result = @{} } process { try { # Create project structure if IncludeProjectLayout is specified if ($IncludeProjectLayout) { # Create directory structure $directories = @( $Path, (Join-Path $Path "src"), (Join-Path $Path "tests"), (Join-Path $Path "tests" "Unit"), (Join-Path $Path "tests" "Integration"), (Join-Path $Path "docs") ) foreach ($dir in $directories) { if (-not (Test-Path $dir)) { if ($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 = @" # $ModuleName PowerShell module for [brief description]. ## Installation ``````powershell Import-Module .\src\$ModuleName `````` ## Usage ``````powershell Import-Module .\src\$ModuleName Get-Command -Module $ModuleName `````` ## Testing ``````powershell Invoke-Pester -Path .\tests\ `````` ## License [Specify license] "@ $readmePath = Join-Path $Path "README.md" if ($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 $Path ".gitignore" if ($PSCmdlet.ShouldProcess($gitignorePath, "Create .gitignore")) { $gitignoreContent | Set-Content -Path $gitignorePath Write-Verbose "Created: $gitignorePath" } # Create docs README.md $docsReadmeContent = @" # $ModuleName Documentation ## Overview [Module overview and purpose] ## Cmdlets [List of exported cmdlets and their usage] ## Examples [Usage examples] ## Notes [Additional notes] "@ $docsReadmePath = Join-Path $Path "docs" "README.md" if ($PSCmdlet.ShouldProcess($docsReadmePath, "Create docs README.md")) { $docsReadmeContent | Set-Content -Path $docsReadmePath Write-Verbose "Created: $docsReadmePath" } } Write-Information -Message "Unpacking zip file to '$($ModuleDestination)'" # Ensure the destination path exists if (-not (Test-Path -Path $ModuleDestination)) { if ($PSCmdlet.ShouldProcess($ModuleDestination, "Create module directory")) { New-Item -Path $ModuleDestination -ItemType Directory -Force | Out-Null } } # Extract the contents of the zip file to the destination path if ($PSCmdlet.ShouldProcess($ModuleDestination, "Extract module template")) { Expand-Archive -Path $ZipFilePath -DestinationPath $ModuleDestination -Force # Get the .psm1 file from the extracted contents $Psm1File = Get-ChildItem -Path $ModuleDestination -Filter "*.psm1" | Select-Object -First 1 if ($Psm1File) { # Rename the .psm1 file to match the module name $NewPsm1Name = Join-Path -Path $ModuleDestination -ChildPath "$ModuleName.psm1" if ($PSCmdlet.ShouldProcess($NewPsm1Name, "Rename .psm1 file")) { Rename-Item -Path $Psm1File.FullName -NewName $NewPsm1Name -Force Write-Information -Message "Renamed .psm1 file to '$NewPsm1Name'" } # If using generator, replace the .psm1 content with generated content if ($UseGenerator) { $generatedPsm1Content = New-PstModuleRootContent -ModuleName $ModuleName -Complexity $Complexity if ($PSCmdlet.ShouldProcess($NewPsm1Name, "Generate .psm1 content ($Complexity complexity)")) { Set-Content -Path $NewPsm1Name -Value $generatedPsm1Content -Force Write-Information -Message "Generated .psm1 content with $Complexity complexity" } } } else { throw "No .psm1 file found in the extracted template." } # Get the .psd1 file from the extracted contents $Psd1File = Get-ChildItem -Path $ModuleDestination -Filter "*.psd1" | Select-Object -First 1 if ($Psd1File) { # Rename the .psd1 file to match the module name $NewPsd1Name = Join-Path -Path $ModuleDestination -ChildPath "$ModuleName.psd1" if ($PSCmdlet.ShouldProcess($NewPsd1Name, "Rename .psd1 file")) { Rename-Item -Path $Psd1File.FullName -NewName $NewPsd1Name -Force Write-Information -Message "Renamed .psd1 file to '$NewPsd1Name'" } } else { Write-Warning -Message "No .psd1 file found in the extracted template." } } if ($IncludeProjectLayout) { Write-Output "New module project created: $Path" Write-Output " - Module: $ModuleDestination" if ($UseGenerator) { Write-Output " - .psm1: Generated ($Complexity complexity)" } Write-Output " - Tests: $(Join-Path $Path 'tests')" Write-Output " - Documentation: $(Join-Path $Path 'docs' 'README.md')" } else { if ($UseGenerator) { Write-Output "Module '$ModuleName' created at '$ModuleDestination' (Generated: $Complexity complexity)" } else { Write-Output $result } } } catch { Write-Error -Message "An error occurred: $($_.Exception.Message)" } } end { if ($?) { Write-Debug -Message "End '$($MyInvocation.MyCommand.Name)' at '$(Get-Date)'" if ($IncludeProjectLayout) { Write-Information -Message "Module project '$ModuleName' created successfully at '$Path'." } else { Write-Information -Message "Module '$ModuleName' created successfully at '$Path'." } } } } |