npm-windows-upgrade
Version:
Upgrade npm on Windows, easily and automatically
245 lines (208 loc) • 7.02 kB
JavaScript
const { Spinner } = require('cli-spinner')
const chalk = require('chalk')
const inquirer = require('inquirer')
const powershell = require('./powershell')
const utils = require('./utils')
const strings = require('./strings')
const versions = require('./versions')
const findNpm = require('./find-npm')
const debug = require('./debug')
class Upgrader {
constructor (program) {
this.options = program
if (this.options.prompt === false) {
this.options.spinner = false
}
}
/**
* Executes the upgrader's "let's check the user's internet" logic,
* eventually quietly resolving or quitting the process with an
* error if the connection is not sufficient
*/
async ensureInternet () {
if (this.options.dnsCheck !== false) {
const isOnline = await utils.checkInternetConnection()
if (!isOnline) {
utils.exit(1, strings.noInternet)
}
}
}
/**
* Executes the upgrader's "let's check the user's powershell execution
* policy" logic, eventually quietly resolving or quitting the process
* with an error if the policy is not sufficient
*/
async ensureExecutionPolicy () {
if (this.options.executionPolicyCheck !== false) {
try {
const isExecutable = await utils.checkExecutionPolicy()
if (!isExecutable) {
utils.exit(1, strings.noExecutionPolicy)
}
} catch (err) {
utils.exit(1, strings.executionPolicyCheckError, err)
}
}
}
/**
* Checks if the upgrade was successful
*
* @return {boolean} - was the upgrade successful?
*/
async wasUpgradeSuccessful () {
this.installedVersion = await versions.getInstalledNPMVersion()
return (this.installedVersion === this.options.npmVersion)
}
/**
* Executes the upgrader's "let's have the user choose a version" logic
*/
async chooseVersion () {
if (!this.options.npmVersion) {
const availableVersions = await versions.getAvailableNPMVersions()
const versionList = [{
type: 'list',
name: 'version',
message: 'Which version do you want to install?',
choices: availableVersions.reverse()
}]
this.options.npmVersion = await inquirer.prompt(versionList)
.then(answer => answer.version)
}
if (this.options.npmVersion === 'latest') {
this.options.npmVersion = await versions.getLatestNPMVersion()
}
}
/**
* Executes the upgrader's "let's find npm" logic
*/
async choosePath () {
try {
const npmPaths = await findNpm(this.options.npmPath)
this.log(npmPaths.message)
this.options.npmPath = npmPaths.path
debug(`Upgrader: Chosen npm path: ${this.options.npmPath}`)
} catch (err) {
utils.exit(1, err)
}
}
/**
* Attempts a simple upgrade, eventually calling npm install -g npm
*
* @param {string} version - Version that should be installed
* @private
*/
async upgradeSimple () {
this.spinner = new Spinner(`${strings.startingUpgradeSimple} %s`)
if (this.options.spinner === false) {
console.log(strings.startingUpgradeSimple)
} else {
this.spinner.start()
}
const output = await powershell.runSimpleUpgrade(this.options.npmVersion)
this.spinner.stop(false)
console.log('\n')
if (output.error) {
throw output.error
}
}
/**
* Upgrades npm in the correct directory, securing and reapplying
* existing configuration
*
* @param {string} version - Version that should be installed
* @param {string} npmPath - Path where npm should be installed
* @private
*/
async upgradeComplex () {
this.spinner = new Spinner(`${strings.startingUpgradeComplex} %s`)
if (this.options.spinner === false) {
console.log(strings.startingUpgradeComplex)
} else {
this.spinner.start()
}
const output = await powershell.runUpgrade(this.options.npmVersion, this.options.npmPath)
this.spinner.stop(false)
console.log('\n')
// If we failed to elevate to administrative rights, we have to abort.
if (output.stdout[0] && output.stdout[0].includes('NOTADMIN')) {
utils.exit(1, strings.noAdmin)
}
}
/**
* Executes the full upgrade flow
*/
upgrade () {
debug('Starting upgrade')
return this.upgradeComplex()
.then(() => this.wasUpgradeSuccessful())
.then((isDone) => {
if (isDone) {
// Awesome, the upgrade worked!
utils.exit(0, strings.upgradeFinished(this.installedVersion))
} else {
return this.upgradeSimple()
}
})
.then(() => this.wasUpgradeSuccessful())
.then((isDone) => {
if (isDone) {
// Awesome, the upgrade worked!
utils.exit(0, strings.upgradeFinished(this.installedVersion))
} else {
this.logUpgradeFailure()
}
})
.catch((err) => console.log(err))
}
/**
* Logs a message to console, unless the user specified quiet mode
*
* @param {string} message - message to log
* @private
*/
log (message) {
if (!this.options.quiet) {
console.log(message)
}
}
/**
* If the whole upgrade failed, we use this method to log a
* detailed trace with versions - all to make it easier for
* users to create meaningful issues.
*
* @param errors {array} - AS many errors as found
*/
logUpgradeFailure (...errors) {
// Uh-oh, something didn't work as it should have.
versions.getVersions().then((debugVersions) => {
let info
if (this.options.npmVersion && this.installedVersion) {
info = `You wanted to install npm ${this.options.npmVersion}, but the installed version is ${this.installedVersion}.\n\n`
info += 'A common reason is an attempted "npm install npm" or "npm upgrade npm". '
info += 'As of today, the only solution is to completely uninstall and then reinstall Node.js. '
info += 'For a small tutorial, please see https://github.com/felixrieseberg/npm-windows-upgrade#usage.\n'
} else if (this.options.npmVersion) {
info = `You wanted to install npm ${this.options.npmVersion}, but we could not confirm that the installation succeeded.`
} else {
info = 'We encountered an error during installation.\n'
}
info += '\nPlease consider reporting your trouble to https://aka.ms/npm-issues.'
console.log(chalk.red(info))
console.log(chalk.bold('\nDebug Information:\n'))
console.log(debugVersions)
if (errors && errors.length && errors.length > 0) console.log('Here is the error:')
// If we just got an error string (we shouldn't handle that)
if (typeof errors !== 'string') {
console.log('\n' + errors + '\n')
return process.exit(1)
}
for (let i = 0; i < errors.length; i++) {
console.log('\n' + errors[i] + '\n')
}
setTimeout(() => {
process.exit(1)
}, 1000)
})
}
}
module.exports = Upgrader