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