1+ <#
2+ . SYNOPSIS
3+ requires one or more modules, variables, or types.
4+ . DESCRIPTION
5+ Requires will require on or more modules, variables, or types to exist.
6+ #>
7+
8+ using namespace System.Management.Automation.Language
9+
10+ [ValidateScript ({
11+ $validateVar = $_
12+ if ($validateVar -is [CommandAst ]) {
13+ $cmdAst = $validateVar
14+ if ($cmdAst.CommandElements [0 ].Value -in ' require' , ' requires' ) {
15+ return $true
16+ }
17+ }
18+ return $false
19+ })]
20+ [Reflection.AssemblyMetadata (" PipeScript.Keyword" , $true )]
21+ [Alias (' Require' )]
22+ param (
23+ # One or more required modules.
24+ [Parameter (ValueFromPipelineByPropertyName )]
25+ $Module ,
26+
27+ # If set, will require the latest version of a module.
28+ [Parameter (ValueFromPipelineByPropertyName )]
29+ [switch ]
30+ $Latest ,
31+
32+ # A ModuleLoader script can be used to dynamically load unresolved modules.
33+ # This script will be passed the unloaded module as an argument, and should return a module.
34+ [Parameter (ValueFromPipelineByPropertyName )]
35+ [Alias (' ModuleResolver' , ' Module Loader' , ' Module Resolver' )]
36+ [ScriptBlock ]
37+ $ModuleLoader ,
38+
39+ # One or more required types.
40+ [Parameter (ValueFromPipelineByPropertyName )]
41+ $Type ,
42+
43+ # A TypeLoader script can be used to dynamically load unresolved types.
44+ # This script will be passed the unloaded type as an argument.
45+ [Parameter (ValueFromPipelineByPropertyName )]
46+ [Alias (' TypeResolver' , ' Type Loader' , ' Type Resolver' )]
47+ [ScriptBlock ]
48+ $TypeLoader ,
49+
50+ # One or more required variables.
51+ [Parameter (ValueFromPipelineByPropertyName )]
52+ [Alias (' Variable' )]
53+ $Variables ,
54+
55+ # A VariableLoader script can be used to dynamically load unresolved variable.
56+ # This script will be passed the unloaded variable as an argument.
57+ [Parameter (ValueFromPipelineByPropertyName )]
58+ [Alias (' VariableResolver' , ' Variable Loader' , ' Variable Resolver' )]
59+ [ScriptBlock ]
60+ $VariableLoader ,
61+
62+ # The Command AST. This will be provided when using the transpiler as a keyword.
63+ [Parameter (Mandatory , ParameterSetName = ' CommandAST' , ValueFromPipeline )]
64+ [CommandAst ]
65+ $CommandAst ,
66+
67+ # The ScriptBlock. This will be provided when using the transpiler as an attribute.
68+ [Parameter (Mandatory , ParameterSetName = ' ScriptBlock' , ValueFromPipeline )]
69+ [ScriptBlock ]
70+ $ScriptBlock = {}
71+ )
72+
73+ process {
74+ # If we were called as a CommandAST
75+ if ($PSCmdlet.ParameterSetName -eq ' CommandAst' ) {
76+ # attempt to parse the command as a sentence.
77+ $mySentence = $commandAst.AsSentence ($MyInvocation.MyCommand )
78+
79+ # If the sentence had parameters
80+ if ($mySentence.Parameter.Count ) {
81+
82+ foreach ($clause in $mySentence.Clauses ) {
83+ if ($clause.ParameterName ) {
84+ $ExecutionContext.SessionState.PSVariable.Set ($clause.ParameterName , $mySentence.Parameter [$clause.Name ])
85+ }
86+ }
87+ }
88+
89+ # If the sentence only any remaining arguments, treat them as modules
90+ if ($mySentence.Argument ) {
91+ if ($Module ) {
92+ $module = @ ($module ) + $mySentence.Argument
93+ } else {
94+ $module = $mySentence.Argument
95+ }
96+ }
97+ }
98+
99+ # region Module Requirements
100+ $moduleRequirementScript = {}
101+ if ($Module ) {
102+ $moduleRequirementsList = @ (foreach ($mod in $module ) {
103+ if ($mod -is [string ]) {
104+ " '$ ( $mod -replace " '" , " ''" ) '"
105+ } else {
106+ " $mod "
107+ }
108+ }) -join ' ,'
109+ $moduleRequirementScript = [ScriptBlock ]::Create(
110+ (" foreach (`$ moduleRequirement in $moduleRequirementsList ) {
111+ `$ requireLatest = $ ( if ($Latest ) { ' $true' } else { ' $false' })
112+ `$ ModuleLoader = $ ( if ($moduleLoader ) { " {
113+ $moduleLoader
114+ }" } else { ' $null' })
115+ " + {
116+ # If the module requirement was a string
117+ if ($moduleRequirement -is [string ]) {
118+ # see if it's already loaded
119+ $foundModuleRequirement = Get-Module $moduleRequirement
120+ if (-not $foundModuleRequirement ) {
121+ # If it wasn't,
122+ $foundModuleRequirement = try { # try loading it
123+ Import-Module - Name $moduleRequirement - PassThru - Global - ErrorAction SilentlyContinue
124+ } catch {
125+ $null
126+ }
127+ }
128+
129+ # If we found a version but require the latest version,
130+ if ($foundModuleRequirement -and $requireLatest ) {
131+ # then find if there is a more recent version.
132+ Write-Verbose " Searching for a more recent version of $ ( $foundModuleRequirement.Name ) @$ ( $foundModuleRequirement.Version ) "
133+ $foundModuleInGallery = Find-Module - Name $foundModuleRequirement.Name
134+ if ($foundModuleInGallery -and
135+ ([Version ]$foundModuleInGallery.Version -gt [Version ]$foundModuleRequirement.Version )) {
136+ Write-Verbose " $ ( $foundModuleInGallery.Name ) @$ ( $foundModuleInGallery.Version ) "
137+ # If there was a more recent version, unload the one we already have
138+ $foundModuleRequirement | Remove-Module # Unload the existing module
139+ $foundModuleRequirement = $null
140+ } else {
141+ Write-Verbose " $ ( $foundModuleRequirement.Name ) @$ ( $foundModuleRequirement.Version ) is the latest"
142+ }
143+ }
144+
145+ # If we have no found the required module at this point
146+ if (-not $foundModuleRequirement ) {
147+ if ($moduleLoader ) { # load it using a -ModuleLoader (if provided)
148+ $foundModuleRequirement = . $moduleLoader $moduleRequirement
149+ } else {
150+ # or install it from the gallery.
151+ Install-Module - Name $moduleRequirement - Scope CurrentUser - Force - AllowClobber
152+ if ($? ) {
153+ # Provided the installation worked, try importing it
154+ $foundModuleRequirement =
155+ Import-Module - Name $moduleRequirement - PassThru - Global - ErrorAction SilentlyContinue
156+ }
157+ }
158+ } else {
159+ $foundModuleRequirement
160+ }
161+ }
162+ } + " }" )
163+ )
164+ }
165+ # endregion Module Requirements
166+
167+ # region Variable Requirements
168+ $variableRequirementScript = {}
169+ if ($Variables ) {
170+ # Translate variables into their string names.
171+ $variableRequirementsList = @ (foreach ($var in $Variables ) {
172+ if ($var -is [string ]) {
173+ " '$ ( $var -replace " '" , " ''" ) '"
174+ }
175+ elseif ($var -is [VariableExpressionAst ]) {
176+ " '$ ( $var.variablepath.ToString () -replace " '" , " ''" ) '"
177+ }
178+ }) -join ' ,'
179+ $variableRequirementScript =
180+ " foreach (`$ variableRequirement in $variableRequirementsList ) {
181+ `$ variableLoader = $ ( if ($VariableLoader ) { " {
182+ $variableLoader
183+ }" } else { ' $null' })
184+ " + {
185+ if (-not $ExecutionContext.SessionState.PSVariable.Get ($variableRequirement )) {
186+ if ($VariableLoader ) {
187+ . $VariableLoader $variableRequirement
188+ if (-not $ExecutionContext.SessionState.PSVariable.Get ($variableRequirement )) {
189+
190+ }
191+ } else {
192+ Write-Error " Missing required variable $variableRequirement "
193+ }
194+ }
195+ } +
196+ " }"
197+ $variableRequirementScript = [scriptblock ]::Create($variableRequirementScript )
198+ }
199+ # endregion Variable Requirements
200+
201+ # region Type Requirements
202+ $typeRequirementScript = {}
203+ if ($type ) {
204+ $typeRequirementsList = @ (foreach ($typeName in $Type ) {
205+ if ($typeName -is [string ]) {
206+ " '$ ( $typeName -replace " ^\[" -replace ' \]$' ) '"
207+ } elseif ($typeName.TypeName ) {
208+ " '$ ( $typeName.TypeName.ToString ()) '"
209+ }
210+ }) -join ' ,'
211+ $typeRequirementScript =
212+ " foreach (`$ typeRequirement in $typeRequirementsList ) {
213+ `$ typeLoader = $ ( if ($TypeLoader ) {
214+ " {$typeLoader }"
215+ } else { ' $null' })
216+ " + {
217+ if (-not ($typeRequirement -as [type ])) {
218+ if ($TypeLoader ) {
219+ . $TypeLoader $typeName
220+ if (-not ($typeRequirement -as [type ])) {
221+ Write-Error " Type [$typeRequirement ] could not be loaded"
222+ }
223+ } else {
224+ Write-Error " Type [$typeRequirement ] is not loaded"
225+ }
226+ }
227+ } + " }"
228+ $typeRequirementScript = [scriptblock ]::Create($typeRequirementScript )
229+ }
230+ # endregion Type Requirements
231+
232+ if ($PSCmdlet.ParameterSetName -eq ' CommandAst' ) {
233+ $moduleRequirementScript , $variableRequirementScript , $typeRequirementScript | Join-PipeScript
234+ } else {
235+ $declareInBlock = ' begin'
236+ if ($scriptBlock.Ast.dynamicParam ) {
237+ $declareInBlock = ' dynamicParam'
238+ }
239+
240+ $moduleRequirementScript = [ScriptBlock ]::Create(" $declareInBlock {
241+ $ModuleRequirementScript
242+ }" )
243+ $variableRequirementScript = [ScriptBlock ]::Create(" $declareInBlock {
244+ $variableRequirementScript
245+ }" )
246+ $typeRequirementScript = [ScriptBlock ]::Create(" $declareInBlock {
247+ $typeRequirementScript
248+ }" )
249+ $ScriptBlock , $moduleRequirementScript , $variableRequirementScript , $typeRequirementScript | Join-PipeScript
250+ }
251+ }
0 commit comments