extract.js 2.9 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798
  1. 'use strict'
  2. const BB = require('bluebird')
  3. const extractStream = require('./lib/extract-stream.js')
  4. const fs = require('fs')
  5. const mkdirp = BB.promisify(require('mkdirp'))
  6. const npa = require('npm-package-arg')
  7. const optCheck = require('./lib/util/opt-check.js')
  8. const path = require('path')
  9. const rimraf = BB.promisify(require('rimraf'))
  10. const withTarballStream = require('./lib/with-tarball-stream.js')
  11. const inferOwner = require('infer-owner')
  12. const chown = BB.promisify(fs.chown)
  13. const truncateAsync = BB.promisify(fs.truncate)
  14. const readFileAsync = BB.promisify(fs.readFile)
  15. const appendFileAsync = BB.promisify(fs.appendFile)
  16. // you used to call me on my...
  17. const selfOwner = process.getuid ? {
  18. uid: process.getuid(),
  19. gid: process.getgid()
  20. } : {
  21. uid: undefined,
  22. gid: undefined
  23. }
  24. module.exports = extract
  25. function extract (spec, dest, opts) {
  26. opts = optCheck(opts)
  27. spec = npa(spec, opts.where)
  28. if (spec.type === 'git' && !opts.cache) {
  29. throw new TypeError('Extracting git packages requires a cache folder')
  30. }
  31. if (typeof dest !== 'string') {
  32. throw new TypeError('Extract requires a destination')
  33. }
  34. const startTime = Date.now()
  35. return inferOwner(dest).then(({ uid, gid }) => {
  36. opts = opts.concat({ uid, gid })
  37. return withTarballStream(spec, opts, stream => {
  38. return tryExtract(spec, stream, dest, opts)
  39. })
  40. .then(() => {
  41. if (!opts.resolved) {
  42. const pjson = path.join(dest, 'package.json')
  43. return readFileAsync(pjson, 'utf8')
  44. .then(str => truncateAsync(pjson)
  45. .then(() => appendFileAsync(pjson, str.replace(
  46. /}\s*$/,
  47. `\n,"_resolved": ${
  48. JSON.stringify(opts.resolved || '')
  49. }\n,"_integrity": ${
  50. JSON.stringify(opts.integrity || '')
  51. }\n,"_from": ${
  52. JSON.stringify(spec.toString())
  53. }\n}`
  54. ))))
  55. }
  56. })
  57. .then(() => opts.log.silly(
  58. 'extract',
  59. `${spec} extracted to ${dest} (${Date.now() - startTime}ms)`
  60. ))
  61. })
  62. }
  63. function tryExtract (spec, tarStream, dest, opts) {
  64. return new BB((resolve, reject) => {
  65. tarStream.on('error', reject)
  66. rimraf(dest)
  67. .then(() => mkdirp(dest))
  68. .then(() => {
  69. // respect the current ownership of unpack targets
  70. if (typeof selfOwner.uid === 'number' &&
  71. typeof selfOwner.gid === 'number' &&
  72. selfOwner.uid !== opts.uid && selfOwner.gid !== opts.gid) {
  73. return chown(dest, opts.uid, opts.gid)
  74. }
  75. })
  76. .then(() => {
  77. const xtractor = extractStream(spec, dest, opts)
  78. xtractor.on('error', reject)
  79. xtractor.on('close', resolve)
  80. tarStream.pipe(xtractor)
  81. })
  82. .catch(reject)
  83. })
  84. .catch(err => {
  85. if (err.code === 'EINTEGRITY') {
  86. err.message = `Verification failed while extracting ${spec}:\n${err.message}`
  87. }
  88. throw err
  89. })
  90. }