extract-stream.js 2.4 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889
  1. 'use strict'
  2. const Minipass = require('minipass')
  3. const path = require('path')
  4. const tar = require('tar')
  5. module.exports = extractStream
  6. module.exports._computeMode = computeMode
  7. class Transformer extends Minipass {
  8. constructor (spec, opts) {
  9. super()
  10. this.spec = spec
  11. this.opts = opts
  12. this.str = ''
  13. }
  14. write (data) {
  15. this.str += data
  16. return true
  17. }
  18. end () {
  19. const replaced = this.str.replace(
  20. /}\s*$/,
  21. `\n,"_resolved": ${
  22. JSON.stringify(this.opts.resolved || '')
  23. }\n,"_integrity": ${
  24. JSON.stringify(this.opts.integrity || '')
  25. }\n,"_from": ${
  26. JSON.stringify(this.spec.toString())
  27. }\n}`
  28. )
  29. super.write(replaced)
  30. return super.end()
  31. }
  32. }
  33. function computeMode (fileMode, optMode, umask) {
  34. return (fileMode | optMode) & ~(umask || 0)
  35. }
  36. function pkgJsonTransform (spec, opts) {
  37. return entry => {
  38. if (entry.path === 'package.json') {
  39. const transformed = new Transformer(spec, opts)
  40. return transformed
  41. }
  42. }
  43. }
  44. function extractStream (spec, dest, opts) {
  45. opts = opts || {}
  46. const sawIgnores = new Set()
  47. return tar.x({
  48. cwd: dest,
  49. filter: (name, entry) => !entry.header.type.match(/^.*link$/i),
  50. strip: 1,
  51. onwarn: msg => opts.log && opts.log.warn('tar', msg),
  52. uid: opts.uid,
  53. gid: opts.gid,
  54. umask: opts.umask,
  55. transform: opts.resolved && pkgJsonTransform(spec, opts),
  56. onentry (entry) {
  57. if (entry.type.toLowerCase() === 'file') {
  58. entry.mode = computeMode(entry.mode, opts.fmode, opts.umask)
  59. } else if (entry.type.toLowerCase() === 'directory') {
  60. entry.mode = computeMode(entry.mode, opts.dmode, opts.umask)
  61. } else {
  62. entry.mode = computeMode(entry.mode, 0, opts.umask)
  63. }
  64. // Note: This mirrors logic in the fs read operations that are
  65. // employed during tarball creation, in the fstream-npm module.
  66. // It is duplicated here to handle tarballs that are created
  67. // using other means, such as system tar or git archive.
  68. if (entry.type.toLowerCase() === 'file') {
  69. const base = path.basename(entry.path)
  70. if (base === '.npmignore') {
  71. sawIgnores.add(entry.path)
  72. } else if (base === '.gitignore') {
  73. const npmignore = entry.path.replace(/\.gitignore$/, '.npmignore')
  74. if (!sawIgnores.has(npmignore)) {
  75. // Rename, may be clobbered later.
  76. entry.path = npmignore
  77. }
  78. }
  79. }
  80. }
  81. })
  82. }