|
| 1 | +using namespace System.Management.Automation.Language |
| 2 | +using namespace System.Collections.Generic |
| 3 | + |
| 4 | +function ConvertTo-Script { |
| 5 | + <# |
| 6 | + .SYNOPSIS |
| 7 | + A Script Generator which converts a module to a script |
| 8 | + .DESCRIPTION |
| 9 | + ConvertTo-Script takes a script module (which may include dotnet assemblies), |
| 10 | + and generates a script file with the module and libraries embedded and ready to run. |
| 11 | +
|
| 12 | + You should provide the name of a function from the module that will be invoked when the script is run. |
| 13 | +
|
| 14 | + In this way you can package any module into a script which invokes a specific command in that module. |
| 15 | +
|
| 16 | + This function never outputs any TextReplacements. |
| 17 | + #> |
| 18 | + [CmdletBinding()] |
| 19 | + [OutputType([TextReplacement])] |
| 20 | + param( |
| 21 | + # The AST of the script module to convert to a script |
| 22 | + [Parameter(Mandatory, ValueFromPipelineByPropertyName)] |
| 23 | + [Alias("Ast")] |
| 24 | + [Ast]$ScriptModule, |
| 25 | + |
| 26 | + # The name of a function in the module to invoke when the script is run |
| 27 | + [Parameter(Mandatory)] |
| 28 | + [string]$FunctionName, |
| 29 | + |
| 30 | + # The path to the script module to convert |
| 31 | + # This is used to find the module manifest, |
| 32 | + # But the the script will be saved in the same location |
| 33 | + [Parameter(Mandatory, ValueFromPipelineByPropertyName)] |
| 34 | + [string]$Path |
| 35 | + ) |
| 36 | + begin { |
| 37 | + Write-Debug " ENTER: ConvertTo-Script BEGIN $Path $FunctionName" |
| 38 | + class ParamBlockExtractor : AstVisitor { |
| 39 | + [string]$ParamBlock |
| 40 | + [string]$FunctionName |
| 41 | + [CommentHelpInfo]$HelpInfo |
| 42 | + |
| 43 | + [AstVisitAction] VisitParamBlock([ParamBlockAst]$ast) { |
| 44 | + if ($ast.Parameters) { |
| 45 | + $this.ParamBlock = @(@($ast.Attributes.Extent.Text) + $ast.Extent.Text) -join "`n" |
| 46 | + } |
| 47 | + return [AstVisitAction]::StopVisit |
| 48 | + } |
| 49 | + |
| 50 | + [AstVisitAction] VisitFunctionDefinition([FunctionDefinitionAst]$ast) { |
| 51 | + if ($ast.Name -ne $this.FunctionName) { |
| 52 | + return [AstVisitAction]::SkipChildren |
| 53 | + } |
| 54 | + $this.HelpInfo = $ast.GetHelpContent() |
| 55 | + return [AstVisitAction]::Continue |
| 56 | + } |
| 57 | + } |
| 58 | + Write-Debug " EXIT: ConvertTo-Script BEGIN" |
| 59 | + } |
| 60 | + process { |
| 61 | + Write-Debug " ENTER: ConvertTo-Script PROCESS $Path $FunctionName" |
| 62 | + $Visitor = [ParamBlockExtractor]@{ |
| 63 | + FunctionName = $FunctionName |
| 64 | + } |
| 65 | + $ScriptModule.Visit($Visitor) |
| 66 | + |
| 67 | + Write-Debug " Parse Module Manifest: $Path" |
| 68 | + $ModuleManifest = [IO.Path]::ChangeExtension($Path, '.psd1') |
| 69 | + Write-Debug " Parse Module Manifest: $ModuleManifest" |
| 70 | + $Manifest = ConvertFrom-Metadata $ModuleManifest |
| 71 | + |
| 72 | + if (!$Visitor.ParamBlock) { |
| 73 | + Write-Error "ConvertTo-Script: Could not find function $FunctionName in $Path" |
| 74 | + } |
| 75 | + |
| 76 | + Push-Location (Split-Path $ModuleManifest -Parent) |
| 77 | + if (Test-Path "$FunctionName.ps1") { |
| 78 | + Write-Warning "Overwriting existing script $FunctionName.ps1" |
| 79 | + } |
| 80 | + @( |
| 81 | + @" |
| 82 | +<#PSScriptInfo |
| 83 | +.VERSION 0.0.0 |
| 84 | +.GUID $([guid]::newguid()) |
| 85 | +.AUTHOR anonymous |
| 86 | +.COMPANYNAME |
| 87 | +.COPYRIGHT |
| 88 | +.TAGS |
| 89 | +.LICENSEURI |
| 90 | +.PROJECTURI |
| 91 | +.ICONURI |
| 92 | +.EXTERNALMODULEDEPENDENCIES |
| 93 | +.REQUIREDSCRIPTS |
| 94 | +.EXTERNALSCRIPTDEPENDENCIES |
| 95 | +.RELEASENOTES |
| 96 | +.PRIVATEDATA |
| 97 | +#> |
| 98 | +
|
| 99 | +"@ |
| 100 | + $Visitor.HelpInfo.GetCommentBlock() |
| 101 | + $Visitor.ParamBlock |
| 102 | + "`n" |
| 103 | + @( |
| 104 | + if ($Manifest.RequiredAssemblies) { |
| 105 | + Get-ChildItem $Manifest.RequiredAssemblies |
| 106 | + } |
| 107 | + Get-Item $Path |
| 108 | + ) | CompressToBase64 -ExpandScriptName ImportBase64Module |
| 109 | + "$FunctionName @PSBoundParameters" |
| 110 | + ) | Set-Content "$FunctionName.ps1" |
| 111 | + |
| 112 | + Update-ScriptFileInfo "$FunctionName.ps1" -Version $Manifest.ModuleVersion -Author $Manifest.Author -CompanyName $Manifest.CompanyName -Copyright $Manifest.Copyright -Tags $Manifest.PrivateData.PSData.Tags -ProjectUri $Manifest.PrivateData.PSData.ProjectUri -LicenseUri $Manifest.PrivateData.PSData.LicenseUri -IconUri $Manifest.PrivateData.PSData.IconUri -ReleaseNotes $Manifest.PrivateData.PSData.ReleaseNotes |
| 113 | + |
| 114 | + Pop-Location |
| 115 | + Write-Debug " EXIT: ConvertTo-Script PROCESS" |
| 116 | + } |
| 117 | +} |
| 118 | + |
0 commit comments