diff --git a/scripts/helpers/Build/Build-PSModuleRootModule.ps1 b/scripts/helpers/Build/Build-PSModuleRootModule.ps1 index be991d3..708dfcd 100644 --- a/scripts/helpers/Build/Build-PSModuleRootModule.ps1 +++ b/scripts/helpers/Build/Build-PSModuleRootModule.ps1 @@ -42,12 +42,55 @@ function Build-PSModuleRootModule { $rootModuleFile = New-Item -Path $ModuleOutputFolder -Name "$moduleName.psm1" -Force #region - Analyze source files - $exports = @{ - Function = Get-PSModuleFunctionsToExport -SourceFolderPath $ModuleOutputFolder - Cmdlet = Get-PSModuleCmdletsToExport -SourceFolderPath $ModuleOutputFolder - Variable = Get-PSModuleVariablesToExport -SourceFolderPath $ModuleOutputFolder - Alias = Get-PSModuleAliasesToExport -SourceFolderPath $ModuleOutputFolder + + #region - Export-Classes + $classesFolder = Join-Path -Path $ModuleOutputFolder -ChildPath 'classes' + $classExports = '' + if (Test-Path -Path $classesFolder) { + $classes = Get-PSModuleClassesToExport -SourceFolderPath $ModuleOutputFolder + if ($classes.count -gt 0) { + $classExports = @' +# Define the types to export with type accelerators. +$ExportableTypes = @( + +'@ + $classes | ForEach-Object { + $classExports += " [$_]`n" + } + + $classExports += @' +) +# Get the internal TypeAccelerators class to use its static methods. +$TypeAcceleratorsClass = [psobject].Assembly.GetType( + 'System.Management.Automation.TypeAccelerators' +) +# Ensure none of the types would clobber an existing type accelerator. +# If a type accelerator with the same name exists, throw an exception. +$ExistingTypeAccelerators = $TypeAcceleratorsClass::Get +foreach ($Type in $ExportableTypes) { + if ($Type.FullName -in $ExistingTypeAccelerators.Keys) { + Write-Debug "Accelerator already exists [$($Type.FullName)]" + } else { + $TypeAcceleratorsClass::Add($Type.FullName, $Type) + } +} + +# Remove type accelerators when the module is removed. +$MyInvocation.MyCommand.ScriptBlock.Module.OnRemove = { + foreach ($Type in $ExportableTypes) { + $TypeAcceleratorsClass::Remove($Type.FullName) + } +}.GetNewClosure() +'@ + } } + #endregion - Export-Classes + + $exports = [System.Collections.Specialized.OrderedDictionary]::new() + $exports.Add('Alias', (Get-PSModuleAliasesToExport -SourceFolderPath $ModuleOutputFolder)) + $exports.Add('Cmdlet', (Get-PSModuleCmdletsToExport -SourceFolderPath $ModuleOutputFolder)) + $exports.Add('Function', (Get-PSModuleFunctionsToExport -SourceFolderPath $ModuleOutputFolder)) + $exports.Add('Variable', (Get-PSModuleVariablesToExport -SourceFolderPath $ModuleOutputFolder)) Write-Verbose ($exports | Out-String) #endregion - Analyze source files @@ -133,6 +176,8 @@ Write-Verbose "[`$scriptName] - [$relativePath] - Done" #endregion - Add content from *.ps1 files on module root #region - Export-ModuleMember + Add-Content -Path $rootModuleFile -Force -Value $classExports + $exportsString = Convert-HashtableToString -Hashtable $exports Write-Verbose ($exportsString | Out-String) @@ -141,6 +186,9 @@ Write-Verbose "[`$scriptName] - [$relativePath] - Done" Path = $rootModuleFile Force = $true Value = @" +[Diagnostics.CodeAnalysis.SuppressMessageAttribute( + 'PSUseConsistentWhitespace', '', Justification = 'PSScriptAnalyzer does not handle spacing in hashtables it seems.' +)] `$exports = $exportsString Export-ModuleMember @exports "@ @@ -158,7 +206,7 @@ Export-ModuleMember @exports Start-LogGroup 'Build root module - Format' $AllContent = Get-Content -Path $rootModuleFile -Raw - $settings = (Join-Path -Path $PSScriptRoot 'PSScriptAnalyzer.Tests.psd1') + $settings = Join-Path -Path $PSScriptRoot 'PSScriptAnalyzer.Tests.psd1' Invoke-Formatter -ScriptDefinition $AllContent -Settings $settings | Out-File -FilePath $rootModuleFile -Encoding utf8BOM -Force Stop-LogGroup diff --git a/scripts/helpers/Build/Get-PSModuleClassesToExport.ps1 b/scripts/helpers/Build/Get-PSModuleClassesToExport.ps1 new file mode 100644 index 0000000..58bb64a --- /dev/null +++ b/scripts/helpers/Build/Get-PSModuleClassesToExport.ps1 @@ -0,0 +1,37 @@ +function Get-PSModuleClassesToExport { + <# + .SYNOPSIS + Gets the classes to export from the module source code. + + .DESCRIPTION + This function will get the classes to export from the module source code. + + .EXAMPLE + Get-PSModuleClassesToExport -SourceFolderPath 'C:\MyModule\src\MyModule' + + Book + BookList + + This will return the classes to export from the module source code. + + .NOTES + Inspired by [about_Classes | Exporting classes with type accelerators](https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_classes?view=powershell-7.4#exporting-classes-with-type-accelerators) + #> + [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidLongLines', '', Justification = 'Contains long links.')] + [CmdletBinding()] + param ( + # The path to the module root folder. + [Parameter(Mandatory)] + [string] $SourceFolderPath + ) + + $files = Get-ChildItem -Path $SourceFolderPath -Recurse -Include '*.ps1' + + foreach ($file in $files) { + $content = Get-Content -Path $file.FullName -Raw + $stringMatches = [Regex]::Matches($content, '(?i)^(?:class|enum)\s+([^\s{]+)', 'Multiline') + foreach ($match in $stringMatches) { + $match.Groups[1].Value + } + } +}