entry.js 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172
  1. // Separated out for easier unit testing
  2. module.exports = async (process, validateEngines) => {
  3. // set it here so that regardless of what happens later, we don't leak any private CLI configs to other programs
  4. process.title = 'npm'
  5. // Patch the global fs module here at the app level
  6. require('graceful-fs').gracefulify(require('node:fs'))
  7. const satisfies = require('semver/functions/satisfies')
  8. const ExitHandler = require('./exit-handler.js')
  9. const exitHandler = new ExitHandler({ process })
  10. const Npm = require('../npm.js')
  11. const npm = new Npm()
  12. exitHandler.setNpm(npm)
  13. // only log node and npm paths in argv initially since argv can contain sensitive info. a cleaned version will be logged later
  14. const { log, output } = require('proc-log')
  15. log.verbose('cli', process.argv.slice(0, 2).join(' '))
  16. log.info('using', 'npm@%s', npm.version)
  17. log.info('using', 'node@%s', process.version)
  18. // At this point we've required a few files and can be pretty sure we don't contain invalid syntax for this version of node.
  19. // It's possible a lazy require would, but that's unlikely enough that it's not worth catching anymore and we attach the more important exit handlers.
  20. validateEngines.off()
  21. exitHandler.registerUncaughtHandlers()
  22. // It is now safe to log a warning if they are using a version of node that is not going to fail on syntax errors but is still unsupported and untested and might not work reliably.
  23. // This is safe to use the logger now which we want since this will show up in the error log too.
  24. if (!satisfies(validateEngines.node, validateEngines.engines)) {
  25. log.warn('cli', validateEngines.unsupportedMessage)
  26. }
  27. // Now actually fire up npm and run the command.
  28. // This is how to use npm programmatically:
  29. try {
  30. const { exec, command, args } = await npm.load()
  31. if (!exec) {
  32. return exitHandler.exit()
  33. }
  34. if (!command) {
  35. output.standard(npm.usage)
  36. process.exitCode = 1
  37. return exitHandler.exit()
  38. }
  39. // Options are prefixed by a hyphen-minus (-, \u2d).
  40. // Other dash-type chars look similar but are invalid.
  41. const nonDashArgs = npm.argv.filter(a => /^[\u2010-\u2015\u2212\uFE58\uFE63\uFF0D]/.test(a))
  42. if (nonDashArgs.length) {
  43. log.error(
  44. 'arg',
  45. 'Argument starts with non-ascii dash, this is probably invalid:',
  46. require('@npmcli/redact').redactLog(nonDashArgs.join(', '))
  47. )
  48. }
  49. const execPromise = npm.exec(command, args)
  50. // this is async but we don't await it, since its ok if it doesnt finish before the command finishes running.
  51. // it uses command and argv so it must be initiated here, after the command name is set
  52. const updateNotifier = require('./update-notifier.js')
  53. // eslint-disable-next-line promise/catch-or-return
  54. updateNotifier(npm).then((msg) => (npm.updateNotification = msg))
  55. await execPromise
  56. return exitHandler.exit()
  57. } catch (err) {
  58. return exitHandler.exit(err)
  59. }
  60. }