dir.js 3.0 KB

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