Public/Build-PSBuildModule.ps1
# spell-checker:ignore modulename function Build-PSBuildModule { <# .SYNOPSIS Builds a PowerShell module based on source directory .DESCRIPTION Builds a PowerShell module based on source directory and optionally concatenates public/private functions from separate files into monolithic .PSM1 file. .PARAMETER Path The source module path. .PARAMETER DestinationPath Destination path to write "built" module to. .PARAMETER ModuleName The name of the module. .PARAMETER Compile Switch to indicate if separate function files should be concatenated into monolithic .PSM1 file. .PARAMETER CompileHeader String that will be at the top of your PSM1 file. .PARAMETER CompileFooter String that will be added to the bottom of your PSM1 file. .PARAMETER CompileScriptHeader String that will be added to your PSM1 file before each script file. .PARAMETER CompileScriptFooter String that will be added to your PSM1 file after each script file. .PARAMETER ReadMePath Path to project README. If present, this will become the "about_<ModuleName>.help.txt" file in the build module. .PARAMETER CompileDirectories List of directories containing .ps1 files that will also be compiled into the PSM1. .PARAMETER CopyDirectories List of directories that will copying "as-is" into the build module. .PARAMETER Exclude Array of files (regular expressions) to exclude from copying into built module. .PARAMETER Culture UI Culture. This is used to determine what culture directory to store "about_<ModuleName>.help.txt" in. .EXAMPLE $buildParams = @{ Path = ./MyModule DestinationPath = ./Output/MoModule/0.1.0 ModuleName = MyModule Exclude = @() Compile = $false Culture = (Get-UICulture).Name } Build-PSBuildModule @buildParams Build module from source directory './MyModule' and save to '/Output/MoModule/0.1.0' #> [CmdletBinding()] param( [parameter(Mandatory)] [string]$Path, [parameter(Mandatory)] [string]$DestinationPath, [parameter(Mandatory)] [string]$ModuleName, [switch]$Compile, [string]$CompileHeader, [string]$CompileFooter, [string]$CompileScriptHeader, [string]$CompileScriptFooter, [string]$ReadMePath, [string[]]$CompileDirectories = @(), [string[]]$CopyDirectories = @(), [string[]]$Exclude = @(), [string]$Culture = (Get-UICulture).Name ) if (-not (Test-Path -LiteralPath $DestinationPath)) { $newItemSplat = @{ Path = $DestinationPath ItemType = 'Directory' Verbose = $VerbosePreference } New-Item @newItemSplat > $null } # Copy "non-processed files" $getChildItemSplat = @{ Path = $Path Include = '*.psm1', '*.psd1', '*.ps1xml' Depth = 1 } Get-ChildItem @getChildItemSplat | Copy-Item -Destination $DestinationPath -Force foreach ($dir in $CopyDirectories) { $copyPath = [IO.Path]::Combine($Path, $dir) Copy-Item -Path $copyPath -Destination $DestinationPath -Recurse -Force } # Copy README as about_<modulename>.help.txt if (-not [string]::IsNullOrEmpty($ReadMePath)) { $culturePath = [IO.Path]::Combine($DestinationPath, $Culture) $aboutModulePath = [IO.Path]::Combine( $culturePath, "about_$($ModuleName).help.txt" ) if (-not (Test-Path $culturePath -PathType Container)) { New-Item $culturePath -Type Directory -Force > $null $copyItemSplat = @{ LiteralPath = $ReadMePath Destination = $aboutModulePath Force = $true } Copy-Item @copyItemSplat } } # Copy source files to destination and optionally combine *.ps1 files # into the PSM1 if ($Compile.IsPresent) { $rootModule = [IO.Path]::Combine($DestinationPath, "$ModuleName.psm1") # Grab the contents of the copied over PSM1 # This will be appended to the end of the finished PSM1 $psm1Contents = Get-Content -Path $rootModule -Raw '' | Out-File -FilePath $rootModule -Encoding 'utf8' if ($CompileHeader) { $CompileHeader | Add-Content -Path $rootModule -Encoding 'utf8' } $resolvedCompileDirectories = $CompileDirectories | ForEach-Object { [IO.Path]::Combine($Path, $_) } $getChildItemSplat = @{ Path = $resolvedCompileDirectories Filter = '*.ps1' File = $true Recurse = $true ErrorAction = 'SilentlyContinue' } $allScripts = Get-ChildItem @getChildItemSplat $allScripts = $allScripts | Remove-ExcludedItem -Exclude $Exclude $addContentSplat = @{ Path = $rootModule Encoding = 'utf8' } $allScripts | ForEach-Object { $srcFile = Resolve-Path $_.FullName -Relative Write-Verbose ($LocalizedData.AddingFileToPsm1 -f $srcFile) if ($CompileScriptHeader) { Write-Output $CompileScriptHeader } Get-Content $srcFile if ($CompileScriptFooter) { Write-Output $CompileScriptFooter } } | Add-Content @addContentSplat $psm1Contents | Add-Content @addContentSplat if ($CompileFooter) { $CompileFooter | Add-Content @addContentSplat } } else { # Copy everything over, then remove stuff that should have been excluded # It's just easier this way $copyParams = @{ Path = [IO.Path]::Combine($Path, '*') Destination = $DestinationPath Recurse = $true Force = $true Verbose = $VerbosePreference } Copy-Item @copyParams $allItems = Get-ChildItem -Path $DestinationPath -Recurse $toRemove = foreach ($item in $allItems) { foreach ($regex in $Exclude) { if ($item -match $regex) { $item } } } $toRemove | Remove-Item -Recurse -Force -ErrorAction 'Ignore' } # Export public functions in manifest if there are any public functions $getChildItemSplat = @{ Recurse = $true ErrorAction = 'SilentlyContinue' Path = "$Path/Public/*.ps1" } $publicFunctions = Get-ChildItem @getChildItemSplat if ($publicFunctions) { $outputManifest = [IO.Path]::Combine( $DestinationPath, "$ModuleName.psd1" ) $updateMetadataSplat = @{ Path = $OutputManifest PropertyName = 'FunctionsToExport' Value = $publicFunctions.BaseName } Update-Metadata @updateMetadataSplat } } |