Functions/Install-WhiskeyNodeJs.ps1
function Install-WhiskeyNodeJs { <# .SYNOPSIS Installs a specific version of Node.js and returns its path. .DESCRIPTION The `Install-WhiskeyNodeJs` function installs a specific version of Node.js and returns the path to its `node.exe` program. It uses NVM to to the installation. If NVM isn't installed/available, it will download it and install it to `%APPDATA%\nvm`. If the requested version of Node.js is installed, nothing happens, but the path to that version's `node.exe` is still returned. After installation, both Node *and* NPM will be installed together in the same directory. IF NVM is downloaded, the `NVM_HOME` environment variable for the current user is created to point to where NVM is installed. .EXAMPLE Install-WhiskeyNodeJs -Version '4.4.7' Installs version `4.4.7` of Node.js and returns the path to its `node.exe` file. #> [CmdletBinding()] param( [Parameter(Mandatory=$true)] [uri] # The URI to the registry from which NPM packages should be downloaded. $RegistryUri, [Parameter(Mandatory=$true)] [string] # The root directory of the target Node.js application. This directory will contain the application's `package.json` config file. $ApplicationRoot, [string] # The directory where NVM should be installed to. Only used if NVM isn't already installed. NVM is installed to `$NvmInstallDirectory\nvm`. $NvmInstallDirectory, [Switch] # Install Node on a developer computer. $ForDeveloper ) Set-StrictMode -Version 'Latest' Use-CallerPreference -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState if( -not $NvmInstallDirectory ) { $NvmInstallDirectory = $env:APPDATA } if( (Test-Path -Path 'env:NVM_HOME') ) { $nvmRoot = (Get-Item -Path 'env:NVM_HOME').Value } else { # On developer computers, NVM should be installed by default. If not, make the dev install it. if( $ForDeveloper ) { Write-Error -Message (@" NVM for Windows is not installed. To install it: 1. Uninstall any existing versions of Node.js using the "Programs and Features" Control Panel. 2. Reboot 3. Delete these folders, if they still exist: * C:\Program Files (x86)\Nodejs * C:\Program Files\Nodejs * C:\Users\$($env:USERNAME)\AppData\Roaming\npm (i.e. ``%APPDATA%\Roaming\npm``) * C:\Users\$($env:USERNAME)\AppData\Roaming\npm-cache (i.e. ``%APPDATA%\Roaming\npm-cache``) * C:\Users\$($env:USERNAME)\.npmrc * C:\Users\$($env:USERNAME)\npmrc 4. Remove any nodejs or npm paths from your %PATH% environment variable. 5. Download the latest version of NVM for Windows from Github: https://github.com/coreybutler/nvm-windows/releases 6. Right-click the .zip file, choose Properties, and click the "Unblock" button. 7. Unzip the installer 8. Run nvm-setup.exe. Leave all installation options to their defaults. 6. Restart PowerShell "@) return } $nvmRoot = Join-Path -Path $NvmInstallDirectory -ChildPath 'nvm' Set-Item -Path 'env:NVM_HOME' -Value $nvmRoot } $nvmPath = Join-Path -Path $nvmRoot -ChildPath 'nvm.exe' if( -not (Test-Path -Path $nvmPath -PathType Leaf) ) { $tempZipFile = 'Whiskey+Install-WhiskeyNodeJs+nvm-setup.zip+{0}' -f [IO.Path]::GetRandomFileName() $tempZipFile = Join-Path -Path $env:TEMP -ChildPath $tempZipFile $nvmUri = 'https://github.com/coreybutler/nvm-windows/releases/download/1.1.1/nvm-noinstall.zip' Invoke-WebRequest -Uri $nvmUri -OutFile $tempZipFile if( -not (Test-Path -Path $tempZipFile -PathType Leaf) ) { Write-Error -Message ('Failed to download NVM from {0}' -f $nvmUri) return } $nvmSymlink = Join-Path -Path $env:ProgramFiles -ChildPath 'nodejs' Add-Type -AssemblyName 'System.IO.Compression.FileSystem' [IO.Compression.ZipFile]::ExtractToDirectory($tempZipFile,$nvmRoot) @" root: $($nvmRoot) path: $($nvmSymlink) "@ | Set-Content -Path (Join-Path -Path $nvmRoot -ChildPath 'settings.txt') } if( -not (Test-Path -Path $nvmPath -PathType Leaf) ) { Write-Error -Message ('Failed to install NVM to {0}.' -f $nvmRoot) return } $packageJsonPath = Resolve-Path -Path (Join-Path -Path $ApplicationRoot -ChildPath 'package.json') | Select-Object -ExpandProperty 'ProviderPath' if( -not $packageJsonPath ) { Write-Error -Message ('Package.json file ''{0}'' does not exist. This file is mandatory when using the Node build task.' -f (Join-Path -Path (Get-Location).ProviderPath -ChildPath 'package.json')) return } $packageJson = Get-Content -Raw -Path $packageJsonPath | ConvertFrom-Json if( -not $packageJson ) { Write-Error -Message ('Package.json file ''{0}'' contains invalid JSON. Please see previous errors for more information.' -f $packageJsonPath) return } if( -not ($packageJson | Get-Member -Name 'name') -or -not $packageJson.name ) { Write-Error -Message ('Package name is missing or doesn''t have a value. Please ensure ''{0}'' contains a ''name'' field., e.g. `"name": "fubarsnafu"`. A package name is required by NSP, the Node Security Platform, when scanning for security vulnerabilities.' -f $packageJsonPath) return } if( -not ($packageJson | Get-Member -Name 'engines') -or -not ($packageJson.engines | Get-Member -Name 'node') ) { Write-Error -Message ('Node version is not defined or is missing from the package.json file ''{0}''. Please ensure the Node version to use is defined using the package.json''s engines field, e.g. `"engines": {{ node: "VERSION" }}`. See https://docs.npmjs.com/files/package.json#engines for more information.' -f $packageJsonPath) return } if( $packageJson.engines.node -notmatch '(\d+\.\d+\.\d+)' ) { Write-Error -Message ('Node version ''{0}'' is invalid. The Node version must be a valid semantic version. Package.json file ''{1}'', engines field:{2}{3}' -f $packageJson.engines.node,$packageJsonPath,[Environment]::NewLine,($packageJson.engines | ConvertTo-Json -Depth 50)) return } $version = $Matches[1] $activity = 'Installing Node.js {0}' -f $version Write-Progress -Activity $activity $output = & $nvmPath install $version 64 | Where-Object { $_ } | ForEach-Object { Write-Progress -Activity $activity -Status $_; $_ } Write-Progress -Activity $activity -Completed $versionRoot = Join-Path -Path $nvmRoot -ChildPath ('v{0}' -f $version) $node64Path = Join-Path -Path $versionRoot -ChildPath 'node64.exe' $nodePath = Join-Path -Path $versionRoot -ChildPath 'node.exe' if( (Test-Path -Path $node64Path -PathType Leaf) ) { Move-Item -Path $node64Path -Destination $nodePath -Force } if( (Test-Path -Path $nodePath -PathType Leaf) ) { $npmPath = Join-Path -Path $versionRoot -ChildPath 'node_modules\npm\bin\npm-cli.js' -Resolve [version]$version = & $nodePath $npmPath '--version' if( $version -lt [version]'3.0' ) { $activity = 'Upgrading NPM to version 3.' & $nodePath $npmPath 'install' 'npm@3' '-g' | Where-Object { $_ } | ForEach-Object { Write-Progress -Activity $activity -Status $_ ; } Write-Progress -Activity $activity -Completed } $npmRegistry = & $nodePath $npmPath config --global get registry if( $npmRegistry -ne $RegistryUri.ToString() ) { Write-Verbose ('NPM registry {0} -> {1}' -f $npmRegistry,$RegistryUri) Invoke-Command -ScriptBlock { & $nodePath $npmPath config --global set registry $RegistryUri } } $myAlwaysAuth = 'false' $alwaysAuth = & $nodePath $npmPath config --global get 'always-auth' if( $alwaysAuth -ne $myAlwaysAuth ) { Write-Verbose -Message ('NPM config always-auth {0} -> {1}' -f $alwaysAuth,$myAlwaysAuth) & $nodePath $npmPath config --global set 'always-auth' $myAlwaysAuth } return $nodePath } Write-Error -Message ('Failed to install Node.js version {0}.{1}{2}' -f $version,[Environment]::NewLine,($output -join [Environment]::NewLine)) } |