Modules/businessdev.ALbuild.Apps/Private/Resolve-BcAnalyzer.ps1
|
function Resolve-BcAnalyzer { <# .SYNOPSIS Resolves analyzer references (tokens, short names, or DLL paths) to compiler analyzer DLLs. .DESCRIPTION Internal helper. The AL toolchain names its built-in analyzers with tokens ('${CodeCop}', '${UICop}', '${AppSourceCop}', '${PerTenantExtensionCop}') that the VS Code AL extension resolves to DLLs; a CLI compile must resolve them itself. This maps each token/short name (with or without the '${...}' wrapper, case-insensitive) to its 'Microsoft.Dynamics.Nav.<Cop>.dll' file and locates it under -SearchRoot (searched recursively - the AL Tool ships them in an 'Analyzers' folder; the container's AL Language extension in 'bin/Analyzers'). An entry that is already a path to an existing .dll is returned as-is; a custom '<name>.dll' is located by that file name. Unresolved entries are dropped and reported via -UnresolvedVariable so the caller can warn. Returns the resolved DLL paths. .PARAMETER Analyzer The analyzer references to resolve. .PARAMETER SearchRoot The folder tree to search for the analyzer DLLs (the compiler's install directory). .PARAMETER UnresolvedVariable Name of a variable in the caller's scope to receive the references that could not be resolved. .OUTPUTS System.String[]: resolved analyzer DLL paths. #> [CmdletBinding()] [OutputType([string[]])] param( [string[]] $Analyzer = @(), [string] $SearchRoot, [string] $UnresolvedVariable ) $map = @{ 'codecop' = 'Microsoft.Dynamics.Nav.CodeCop.dll' 'uicop' = 'Microsoft.Dynamics.Nav.UICop.dll' 'appsourcecop' = 'Microsoft.Dynamics.Nav.AppSourceCop.dll' 'pertenantextensioncop' = 'Microsoft.Dynamics.Nav.PerTenantExtensionCop.dll' } $resolved = [System.Collections.Generic.List[string]]::new() $unresolved = [System.Collections.Generic.List[string]]::new() $dllCache = $null # lazily filled once, only if a token needs a search foreach ($a in $Analyzer) { if ([string]::IsNullOrWhiteSpace($a)) { continue } $token = $a.Trim() # Already a concrete, existing .dll path. if ($token -match '\.dll$' -and (Test-Path -LiteralPath $token)) { $resolved.Add((Resolve-Path -LiteralPath $token).Path) continue } # '${CodeCop}' / 'CodeCop' / 'codecop' -> the mapped DLL file name; a bare '<name>.dll' -> that name. $key = $token.Trim('$', '{', '}').ToLowerInvariant() $dllName = $map[$key] if (-not $dllName -and $token -match '\.dll$') { $dllName = Split-Path -Path $token -Leaf } if (-not $dllName) { $unresolved.Add($token); continue } if (-not $SearchRoot -or -not (Test-Path -LiteralPath $SearchRoot)) { $unresolved.Add($token); continue } if ($null -eq $dllCache) { $dllCache = @(Get-ChildItem -LiteralPath $SearchRoot -Recurse -Filter '*.dll' -File -ErrorAction SilentlyContinue) } $hit = $dllCache | Where-Object { $_.Name -ieq $dllName } | Select-Object -First 1 if ($hit) { $resolved.Add($hit.FullName) } else { $unresolved.Add($token) } } if ($UnresolvedVariable) { Set-Variable -Name $UnresolvedVariable -Scope 1 -Value @($unresolved) } return @($resolved | Select-Object -Unique) } |