File Explorer

/var/lang/lib/node_modules/npm/lib/cli

This explorer reads the filesystem of the server it runs on, so /workspace/user isn't present here. Browsing and the terminal still work against this server's own disk from /.

0 dirs
4 files
exit-handler.js5.2 KB · 175 lines
const { log, output, META } = require('proc-log')const { errorMessage, getExitCodeFromError } = require('../utils/error-message.js') class ExitHandler {  #npm = null  #process = null  #exited = false  #exitErrorMessage = false   #noNpmError = false   get #hasNpm () {    return !!this.#npm  }   get #loaded () {    return !!this.#npm?.loaded  }   get #showExitErrorMessage () {    if (!this.#loaded) {      return false    }    if (!this.#exited) {      return true    }    return this.#exitErrorMessage  }   get #notLoadedOrExited () {    return !this.#loaded && !this.#exited  }   setNpm (npm) {    this.#npm = npm  }   constructor ({ process }) {    this.#process = process    this.#process.on('exit', this.#handleProcessExitAndReset)  }   registerUncaughtHandlers () {    this.#process.on('uncaughtException', this.#handleExit)    this.#process.on('unhandledRejection', this.#handleExit)  }   exit (err) {    this.#handleExit(err)  }   #handleProcessExitAndReset = (code) => {    this.#handleProcessExit(code)     // Reset all the state. This is only relevant for tests since    // in reality the process fully exits here.    this.#process.off('exit', this.#handleProcessExitAndReset)    this.#process.off('uncaughtException', this.#handleExit)    this.#process.off('unhandledRejection', this.#handleExit)    if (this.#loaded) {      this.#npm.unload()    }    this.#npm = null    this.#exited = false    this.#exitErrorMessage = false  }   #handleProcessExit (code) {    const numCode = Number(code) || 0    // Always exit w/ a non-zero code if exit handler was not called    const exitCode = this.#exited ? numCode : (numCode || 1)    this.#process.exitCode = exitCode     if (this.#notLoadedOrExited) {      // Exit handler was not called and npm was not loaded so we have to log something      this.#logConsoleError(new Error(`Process exited unexpectedly with code: ${exitCode}`))      return    }     if (this.#logNoNpmError()) {      return    }     const os = require('node:os')    log.verbose('cwd', this.#process.cwd())    log.verbose('os', `${os.type()} ${os.release()}`)    log.verbose('node', this.#process.version)    log.verbose('npm ', `v${this.#npm.version}`)     // only show the notification if it finished    if (typeof this.#npm.updateNotification === 'string') {      log.notice('', this.#npm.updateNotification, { [META]: true, force: true })    }     if (!this.#exited) {      log.error('', 'Exit handler never called!')      log.error('', 'This is an error with npm itself. Please report this error at:')      log.error('', '  <https://github.com/npm/cli/issues>')      if (this.#npm.silent) {        output.error('')      }    }     log.verbose('exit', exitCode)     if (exitCode) {      log.verbose('code', exitCode)    } else {      log.info('ok')    }     if (this.#showExitErrorMessage) {      log.error('', this.#npm.exitErrorMessage())    }  }   #logConsoleError (err) {    // Run our error message formatters on all errors even if we    // have no npm or an unloaded npm. This will clean the error    // and possible return a formatted message about EACCESS or something.    const { summary, detail } = errorMessage(err, this.#npm)    const formatted = [...new Set([...summary, ...detail].flat().filter(Boolean))].join('\n')    // If we didn't get anything from the formatted message then just display the full stack    // eslint-disable-next-line no-console    console.error(formatted === err.message ? err.stack : formatted)  }   #logNoNpmError (err) {    if (this.#hasNpm) {      return false    }    // Make sure we only log this error once    if (!this.#noNpmError) {      this.#noNpmError = true      this.#logConsoleError(        new Error(`Exit prior to setting npm in exit handler`, err ? { cause: err } : {})      )    }    return true  }   #handleExit = (err) => {    this.#exited = true     // No npm at all    if (this.#logNoNpmError(err)) {      return this.#process.exit(this.#process.exitCode || getExitCodeFromError(err) || 1)    }     // npm was never loaded but we still might have a config loading error or    // something similar that we can run through the error message formatter    // to give the user a clue as to what happened.s    if (!this.#loaded) {      this.#logConsoleError(new Error('Exit prior to config file resolving', { cause: err }))      return this.#process.exit(this.#process.exitCode || getExitCodeFromError(err) || 1)    }     this.#exitErrorMessage = err?.suppressError === true ? false : !!err     // Prefer the exit code of the error, then the current process exit code,    // then set it to 1 if we still have an error. Otherwise, we call process.exit    // with undefined so that it can determine the final exit code    const exitCode = err?.exitCode ?? this.#process.exitCode ?? (err ? 1 : undefined)     // explicitly call process.exit now so we don't hang on things like the    // update notifier, also flush stdout/err beforehand because process.exit doesn't    // wait for that to happen.    this.#process.stderr.write('', () => this.#process.stdout.write('', () => {      this.#process.exit(exitCode)    }))  }} module.exports = ExitHandler