codekingpro/portable-devtools
114k
1const { resolve } = require('node:path')
2const packlist = require('npm-packlist')
3const runScript = require('@npmcli/run-script')
4const tar = require('tar')
5const { Minipass } = require('minipass')
6const Fetcher = require('./fetcher.js')
7const FileFetcher = require('./file.js')
8const _ = require('./util/protected.js')
9const tarCreateOptions = require('./util/tar-create-options.js')
10
11class DirFetcher extends Fetcher {
12 constructor (spec, opts) {
13 super(spec, opts)
14 // just the fully resolved filename
15 this.resolved = this.spec.fetchSpec
16
17 this.tree = opts.tree || null
18 this.Arborist = opts.Arborist || null
19 }
20
21 // exposes tarCreateOptions as public API
22 static tarCreateOptions (manifest) {
23 return tarCreateOptions(manifest)
24 }
25
26 get types () {
27 return ['directory']
28 }
29
30 #prepareDir () {
31 return this.manifest().then(mani => {
32 if (!mani.scripts || !mani.scripts.prepare) {
33 return
34 }
35 if (this.opts.ignoreScripts) {
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 return runScript({
45 // this || undefined is because runScript will be unhappy with the default null value
46 scriptShell: this.opts.scriptShell || undefined,
47 pkg: mani,
48 event: 'prepare',
49 path: this.resolved,
50 stdio,
51 env: {
52 npm_package_resolved: this.resolved,
53 npm_package_integrity: this.integrity,
54 npm_package_json: resolve(this.resolved, 'package.json'),
55 },
56 })
57 })
58 }
59
60 [_.tarballFromResolved] () {
61 if (!this.tree && !this.Arborist) {
62 throw new Error('DirFetcher requires either a tree or an Arborist constructor to pack')
63 }
64
65 const stream = new Minipass()
66 stream.resolved = this.resolved
67 stream.integrity = this.integrity
68
69 const { prefix, workspaces } = this.opts
70
71 // run the prepare script, get the list of files, and tar it up
72 // pipe to the stream, and proxy errors the chain.
73 this.#prepareDir()
74 .then(async () => {
75 if (!this.tree) {
76 const arb = new this.Arborist({ path: this.resolved })
77 this.tree = await arb.loadActual()
78 }
79 return packlist(this.tree, { path: this.resolved, prefix, workspaces })
80 })
81 .then(files => tar.c(tarCreateOptions(this.package), files)
82 .on('error', er => stream.emit('error', er)).pipe(stream))
83 .catch(er => stream.emit('error', er))
84 return stream
85 }
86
87 manifest () {
88 if (this.package) {
89 return Promise.resolve(this.package)
90 }
91
92 return this[_.readPackageJson](this.resolved)
93 .then(mani => this.package = {
94 ...mani,
95 _integrity: this.integrity && String(this.integrity),
96 _resolved: this.resolved,
97 _from: this.from,
98 })
99 }
100
101 packument () {
102 return FileFetcher.prototype.packument.apply(this)
103 }
104}
105module.exports = DirFetcher
106 