diff --git a/resources/NpmDsc/NpmDsc.psm1 b/resources/NpmDsc/NpmDsc.psm1 index 64f7b4bc..4a96b135 100644 --- a/resources/NpmDsc/NpmDsc.psm1 +++ b/resources/NpmDsc/NpmDsc.psm1 @@ -343,6 +343,14 @@ class NpmPackage { $inDesiredState = $this.Test() if ($this.Ensure -eq [Ensure]::Present) { if (-not $inDesiredState) { + # TODO: Handling owner/repo package references pointing to GH repositories requires accounting + # for git errors (missing git, failed authentication, etc.). + # + # See: https://docs.npmjs.com/cli/v11/commands/npm-install#description + if (($this.Name -notmatch '^git(?:\+(?:ssh|https?|file))?://') -and $this.Name.Contains('/') -and -not $this.Name.StartsWith('@')) { + throw "The Set operation currently only supports packages specified as [<@scope>/]. The given package looks like a GitHub repository: $($this.Name)." + } + Install-NpmPackage -PackageName $this.Name -Arguments $this.Arguments -Global $this.Global } } else { diff --git a/tests/NpmDsc/NpmDsc.tests.ps1 b/tests/NpmDsc/NpmDsc.tests.ps1 index 3162aeee..d6c88c98 100644 --- a/tests/NpmDsc/NpmDsc.tests.ps1 +++ b/tests/NpmDsc/NpmDsc.tests.ps1 @@ -85,6 +85,47 @@ Describe 'NpmPackage' { $finalState.InDesiredState | Should -Be $true } + Context 'Package names' { + BeforeEach { + npm uninstall babel/helpers 2>$null | Out-Null || $(throw 'npm uninstall failed') + npm uninstall --global babel/helpers 2>$null | Out-Null || $(throw 'npm uninstall failed') + } + + Context 'Present' { + It 'Unsupported if package name points to VCS repository (scope/name)' -Skip:(!$IsWindows) -ForEach @( + @{ isGlobal = $false } + @{ isGlobal = $true } + ) { + $desiredState = @{ + Name = 'babel/helpers' + Global = $isGlobal + Ensure = 'Present' + } + + $message = 'The Set operation currently only supports packages specified as `[<@scope>/`]. The given package looks like a GitHub repository: babel/helpers.' + + { Invoke-DscResource -Name NpmPackage -ModuleName NpmDsc -Method Set -Property $desiredState } | Should -Throw $message + } + } + + Context 'Absent' { + It 'Supports any package name' -Skip:(!$IsWindows) -ForEach @( + @{ isGlobal = $false } + @{ isGlobal = $true } + ) { + $desiredState = @{ + Name = 'babel/helpers' + Global = $isGlobal + Ensure = 'Absent' + } + + Invoke-DscResource -Name NpmPackage -ModuleName NpmDsc -Method Set -Property $desiredState + $desiredState = Invoke-DscResource -Name NpmPackage -ModuleName NpmDsc -Method Test -Property $desiredState + $desiredState.InDesiredState | Should -Be $true + } + } + } + It 'Performs whatif operation successfully' -Skip:(!$IsWindows) { $whatIfState = @{ Name = 'react'