NpmProvider.ps1
|
function Get-NpmProviderPackage { [CmdletBinding()] param([switch]$Outdated) $nodeModule = Get-Module -Name Shmuelie.Node -ErrorAction Stop if (-not $nodeModule) { throw "The required module 'Shmuelie.Node' is no longer loaded." } $previousExitCode = $global:LASTEXITCODE try { $global:LASTEXITCODE = $null # npm outdated uses exit 1 for available updates. Set the preference in # the owning module's invocation scope, without changing its session state. $packages = @(& $nodeModule { param([bool]$Outdated) $PSNativeCommandUseErrorActionPreference = $false Shmuelie.Node\Get-NpmPackage -Global -Outdated:$Outdated -ErrorAction Stop } ([bool]$Outdated)) $exitCode = $global:LASTEXITCODE if ($null -ne $exitCode -and $exitCode -ne 0 -and -not ($Outdated -and $exitCode -eq 1 -and $packages.Count)) { throw "npm global package discovery failed with exit code $exitCode." } foreach ($package in $packages) { if ($package.PSTypeNames -notcontains 'NpmPackage' -or $package.Global -isnot [bool] -or -not $package.Global) { throw 'npm discovery returned an invalid or non-global package.' } if ($package.Name -isnot [string] -or ($null -ne $package.Version -and $package.Version -isnot [string])) { throw 'npm discovery returned invalid package identity or version data.' } } $packages } finally { $global:LASTEXITCODE = $previousExitCode } } function Assert-NpmProviderPackageName { [CmdletBinding()] param([Parameter(Mandatory)][AllowEmptyString()][string]$Name) # Only registry package identifiers may reach npm.cmd, never package specs, # options, URLs, paths, or cmd.exe metacharacters. if ($Name.Length -gt 214 -or $Name -cnotmatch '\A(?:@[A-Za-z0-9_-][A-Za-z0-9._-]*/)?[A-Za-z0-9_-][A-Za-z0-9._-]*\z' -or $Name.StartsWith('-')) { throw "Invalid npm registry package name '$Name'." } } function Get-NpmPackageProvider { <# .SYNOPSIS Describe the global npm package update integration without probing tools. .DESCRIPTION Lazily uses Shmuelie.Node and npm to discover outdated global packages. No provider options or local dependency updates are supported. The core owns confirmation and failure continuation. Successful updates are verified against the installed global package list, not the proposed latest version; missing observations are failures with unknown versions. #> [CmdletBinding()] param() [pscustomobject]@{ Name = 'Npm' Platforms = @('Windows', 'Linux', 'MacOS') RequiredModules = @('Shmuelie.Node') RequiredCommands = @('npm', 'Shmuelie.Node\Get-NpmPackage', 'Shmuelie.Node\Update-NpmPackage') OptionNames = @() TestAvailable = $null GetTargets = { param([hashtable]$Options) foreach ($package in Get-NpmProviderPackage -Outdated) { Assert-NpmProviderPackageName -Name $package.Name if ([string]::IsNullOrWhiteSpace($package.Version) -or $package.Latest -isnot [string] -or [string]::IsNullOrWhiteSpace($package.Latest)) { throw "npm did not report installed and latest versions for '$($package.Name)'; outdated global discovery is incomplete." } if ($package.Version -eq $package.Latest) { continue } New-PackageUpdateTarget -Target $package.Name -PreviousVersion $package.Version -ProposedVersion $package.Latest } } Update = { param($Target, [hashtable]$Options) Assert-NpmProviderPackageName -Name $Target.Target $updates = @(Shmuelie.Node\Update-NpmPackage -Name $Target.Target -Global -Confirm:$false -ErrorAction Stop) if ($updates.Count -ne 1 -or $updates[0].PSTypeNames -notcontains 'NpmUpdateResult' -or $updates[0].Name -cne $Target.Target -or $updates[0].Global -isnot [bool] -or -not $updates[0].Global -or $updates[0].Success -isnot [bool]) { throw "npm returned an invalid update result for '$($Target.Target)'; the outcome is unknown." } if (-not $updates[0].Success) { throw "npm failed to update global package '$($Target.Target)'." } $installed = @(Get-NpmProviderPackage | Where-Object Name -CEQ $Target.Target) if ($installed.Count -ne 1 -or [string]::IsNullOrWhiteSpace($installed[0].Version)) { throw "Cannot observe the installed global version of '$($Target.Target)' after its npm update." } if (-not $Target.PreviousVersion) { throw "Cannot establish a version change for '$($Target.Target)' because its previous version is unknown." } $status = if ($installed[0].Version -eq $Target.PreviousVersion) { 'Unchanged' } else { 'Updated' } New-PackageUpdateResult -Provider Npm -Target $Target.Target -PreviousVersion $Target.PreviousVersion -ResultingVersion $installed[0].Version -Status $status } } } |