1const Fetcher = require('./fetcher.js') 2const FileFetcher = require('./file.js') 3const { Minipass } = require('minipass') 4const tarCreateOptions = require('./util/tar-create-options.js') 5const packlist = require('npm-packlist') 6const tar = require('tar') 7const _prepareDir = Symbol('_prepareDir') 8const { resolve } = require('path') 9const _readPackageJson = Symbol.for('package.Fetcher._readPackageJson') 10 11const runScript = require('@npmcli/run-script') 12 13const _tarballFromResolved = Symbol.for('pacote.Fetcher._tarballFromResolved') 14class DirFetcher extends Fetcher { 15 constructor (spec, opts) { 16 super(spec, opts) 17 // just the fully resolved filename 18 this.resolved = this.spec.fetchSpec 19 20 this.tree = opts.tree || null 21 this.Arborist = opts.Arborist || null 22 } 23 24 // exposes tarCreateOptions as public API 25 static tarCreateOptions (manifest) { 26 return tarCreateOptions(manifest) 27 } 28 29 get types () { 30 return ['directory'] 31 } 32 33 [_prepareDir] () { 34 return this.manifest().then(mani => { 35 if (!mani.scripts || !mani.scripts.prepare) { 36 return 37 } 38 39 // we *only* run prepare. 40 // pre/post-pack is run by the npm CLI for publish and pack, 41 // but this function is *also* run when installing git deps 42 const stdio = this.opts.foregroundScripts ? 'inherit' : 'pipe' 43 44 // hide the banner if silent opt is passed in, or if prepare running 45 // in the background. 46 const banner = this.opts.silent ? false : stdio === 'inherit' 47 48 return runScript({ 49 pkg: mani, 50 event: 'prepare', 51 path: this.resolved, 52 stdio, 53 banner, 54 env: { 55 npm_package_resolved: this.resolved, 56 npm_package_integrity: this.integrity, 57 npm_package_json: resolve(this.resolved, 'package.json'), 58 }, 59 }) 60 }) 61 } 62 63 [_tarballFromResolved] () { 64 if (!this.tree && !this.Arborist) { 65 throw new Error('DirFetcher requires either a tree or an Arborist constructor to pack') 66 } 67 68 const stream = new Minipass() 69 stream.resolved = this.resolved 70 stream.integrity = this.integrity 71 72 const { prefix, workspaces } = this.opts 73 74 // run the prepare script, get the list of files, and tar it up 75 // pipe to the stream, and proxy errors the chain. 76 this[_prepareDir]() 77 .then(async () => { 78 if (!this.tree) { 79 const arb = new this.Arborist({ path: this.resolved }) 80 this.tree = await arb.loadActual() 81 } 82 return packlist(this.tree, { path: this.resolved, prefix, workspaces }) 83 }) 84 .then(files => tar.c(tarCreateOptions(this.package), files) 85 .on('error', er => stream.emit('error', er)).pipe(stream)) 86 .catch(er => stream.emit('error', er)) 87 return stream 88 } 89 90 manifest () { 91 if (this.package) { 92 return Promise.resolve(this.package) 93 } 94 95 return this[_readPackageJson](this.resolved + '/package.json') 96 .then(mani => this.package = { 97 ...mani, 98 _integrity: this.integrity && String(this.integrity), 99 _resolved: this.resolved, 100 _from: this.from, 101 }) 102 } 103 104 packument () { 105 return FileFetcher.prototype.packument.apply(this) 106 } 107} 108module.exports = DirFetcher 109