anvm.psm1
$nvmPath = "$env:APPDATA\anvm" $sevenZipAvailable = $false if (Get-Command "7z" -ErrorAction SilentlyContinue) { $sevenZipAvailable = $true } $node64Path = "$env:ProgramFiles\nodejs" $node32Path = "${env:ProgramFiles(x86)}\nodejs" $npmPath = "$env:APPDATA\npm" $global:node_versions = @() function Install-NodeVersion { param( [Parameter(Mandatory=$true, Position=0)] [string]$version, [Parameter(Mandatory=$false, Position=1)] [ValidateSet('x64', 'x86')] [string]$arch ) if (-not $arch) { $arch = 'x64' } $version = getNodeVersion $version $v = parseVersion $version if ($v.Major -ge 1 -and $v.Major -lt 4) { $baseUrl = "https://iojs.org/dist/v$version" $productName = "iojs" } else { $baseUrl = "https://nodejs.org/dist/v$version" $productName = "node" } Write-Host "Installing Node v$version $arch..." -NoNewline $nodePath = getNodePath $version $arch if (Test-Path $nodePath) { Write-Host "already installed" } else { $origProg = $ProgressPreference $ProgressPreference = 'SilentlyContinue' $ext = if ($sevenZipAvailable) { "7z"} else { "zip" } $fileName = "$productName-v$version-win-$arch.$ext" try { $packageUrl = "$baseUrl/$fileName" Invoke-WebRequest -Uri $packageUrl -OutFile "$env:TEMP\$fileName" -UseBasicParsing } catch { $fileName = "$productName-v$version-$arch.msi" $packageUrl = "$baseUrl/$fileName" try { Invoke-WebRequest -Uri $packageUrl -OutFile "$env:TEMP\$fileName" -UseBasicParsing } catch { $packageUrl = "$baseUrl/x64/$fileName" try { Invoke-WebRequest -Uri $packageUrl -OutFile "$env:TEMP\$fileName" -UseBasicParsing } catch { throw "Package not found!" } } } finally { $ProgressPreference = $origProg } if ($fileName.EndsWith(".msi")) { installNodeMsi $version $arch "$env:TEMP\$fileName" } else { unzip $version $arch "$env:TEMP\$fileName" } Write-Host "OK" } } function Set-NodeVersion { param( [Parameter(Mandatory=$true, Position=0)] [string]$version, [Parameter(Mandatory=$false, Position=1)] [ValidateSet('x64', 'x86')] [string]$arch ) ensureElevatedModeOnWindows "Switching Node version requires elevated mode." if (-not $arch) { $arch = 'x64' } $version = getNodeVersion $version $nodePath = getNodePath $version $arch if (-not (Test-Path $nodePath)) { Install-NodeVersion $version $arch } Write-Host "Using Node v$version $arch" if (Test-Path $node64Path) { (Get-Item $node64Path).Delete() } if (Test-Path $node32Path) { (Get-Item $node32Path).Delete() } if ($arch -eq 'x64') { New-Item -ItemType SymbolicLink -Path $node64Path -Target $nodePath | Out-Null } else { New-Item -ItemType SymbolicLink -Path $node32Path -Target $nodePath | Out-Null } addPath $node64Path addPath $node32Path addPath $npmPath addSessionPath $node64Path addSessionPath $node32Path addSessionPath $npmPath } function getNodePath($version, $arch) { $p1 = Join-Path $nvmPath $version if ($arch) { $p1 = Join-Path $p1 $arch } return $p1 } function parseVersion([string]$str) { $versionDigits = $str.Split('.') $version = @{ major = -1 minor = -1 build = -1 number = 0 value = $null } $version.value = $str if($versionDigits -and $versionDigits.Length -gt 0) { $version.major = [int]$versionDigits[0] } if($versionDigits.Length -gt 1) { $version.minor = [int]$versionDigits[1] } if($versionDigits.Length -gt 2) { $version.build = [int]$versionDigits[2] } for($i = 0; $i -lt $versionDigits.Length; $i++) { $version.number += [long]$versionDigits[$i] -shl 16 * (3 - $i) } return $version } function getNodeVersion([string]$partialVersion) { if ($global:node_versions.Length -eq 0) { $global:node_versions = (Invoke-WebRequest -Uri 'https://nodejs.org/dist/index.json' -UseBasicParsing).Content | ConvertFrom-Json $global:node_versions += (Invoke-WebRequest -Uri 'https://iojs.org/dist/index.json' -UseBasicParsing).Content | ConvertFrom-Json } if ($partialVersion -eq 'lts' -or $partialVersion -eq 'stable') { foreach($ver in $global:node_versions) { if ($ver.lts) { return $ver.version.substring(1) } } } elseif ($partialVersion -eq 'current' -or $partialVersion -eq 'latest') { return $global:node_versions[0].version.substring(1) } else { $v = parseVersion $partialVersion if ($v.major -ne -1 -and $v.minor -ne -1 -and $v.build -ne -1) { return $partialVersion } else { $prefix = $partialVersion.Trim(".") + "." $latestVersion = $null foreach($ver in $global:node_versions) { $vj = $ver.version.substring(1) if ($vj.startswith($prefix)) { if ($null -eq $latestVersion -or (parseVersion $vj).number -gt (parseVersion $latestVersion).number) { $latestVersion = $vj } } } if ($null -eq $latestVersion) { throw "Node v$partialVersion not found" } return $latestVersion } } } function unzip([string]$version, [string]$arch, [string]$zipfile) { # unpack to a temp dir $tempPath = "$env:TEMP\anvm-$version-$arch" if ($sevenZipAvailable) { 7z x $zipfile -y -o"$tempPath" | Out-Null } else { Add-Type -AssemblyName System.IO.Compression.FileSystem [System.IO.Compression.ZipFile]::ExtractToDirectory($zipfile, $tempPath) } # move to a final destination $srcDir = (Get-ChildItem -Path $tempPath -Force -Directory | Select-Object -First 1).FullName $versionDir = getNodePath $version $destDir = getNodePath $version $arch New-Item -Path $versionDir -ItemType Directory -Force | Out-Null [IO.Directory]::Move($srcDir, $destDir) Remove-Item $tempPath -Recurse -Force } function installNodeMsi([string]$version, [string]$arch, [string]$msiFile) { $v = parseVersion $version if ($v.Major -eq 0 -or $v.Major -ge 4) { $features = 'NodeRuntime,npm' } else { $features = 'NodeRuntime,NodeAlias,npm' } $tempDir = "$env:TEMP\anvm-$version-$arch" ensureElevatedModeOnWindows "Installing Node from MSI requires elevated mode." $destDir = getNodePath $version $arch $msiArgs = @("/i", "`"$msiFile`"", "/q", "ADDLOCAL=$features", "INSTALLDIR=`"$tempDir`""<#, "/L*V", "C:\Projects\2\node-install.log"#>) $result = Start-Process -FilePath "msiexec.exe" -Wait -PassThru -ArgumentList $msiArgs if ($result.ExitCode -ne 0) { throw "msiexec installing node.msi exited with $($result.ExitCode)" } # copy installation New-Item -Path $destDir -ItemType Directory -Force | Out-Null Get-ChildItem -Path $tempDir | Copy-Item -Destination $destDir -Force -Recurse # uninstall node uninstallNode } function uninstallNode() { $uninstallCommand = getUninstallString "Node.js" if ($uninstallCommand) { Write-Verbose "Uninstalling existing installation of Node.js ..." ensureElevatedModeOnWindows "Uninstalling Node requires elevated mode." $uninstallCommand = $uninstallCommand.replace('MsiExec.exe /I{', '/x{').replace('MsiExec.exe /X{', '/x{') cmd /c start /wait msiexec.exe $uninstallCommand /quiet Write-Verbose "Uninstalled Node.js" } else { Write-Verbose "Node.js is not installed" } } function getUninstallString($productName) { $x64items = @(Get-ChildItem "HKLM:SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall") ($x64items + @(Get-ChildItem "HKLM:SOFTWARE\wow6432node\Microsoft\Windows\CurrentVersion\Uninstall") ` | ForEach-object { Get-ItemProperty Microsoft.PowerShell.Core\Registry::$_ } ` | Where-Object { $_.DisplayName -and $_.DisplayName.Contains($productName) } ` | Select-Object UninstallString).UninstallString } function ensureElevatedModeOnWindows([string]$msg) { if (-not $isLinux -and -not $isMacOS -and -not ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] 'Administrator')) { throw $msg } } function getSanitizedPath([string]$path) { return $path.Replace('/', '\').Trim('\').Trim(' ') } function addPath([string]$path) { $sanitizedPath = getSanitizedPath $path $machinePath = [Environment]::GetEnvironmentVariable("path", "machine") foreach($item in $machinePath.Split(";")) { if($sanitizedPath -eq (getSanitizedPath $item)) { return # already added } } [Environment]::SetEnvironmentVariable("path", "$sanitizedPath;$machinePath", "machine") } function addSessionPath([string]$path) { $sanitizedPath = getSanitizedPath $path foreach($item in $env:path.Split(";")) { if($sanitizedPath -eq (getSanitizedPath $item)) { return # already added } } $env:path = "$sanitizedPath;$env:path" } |