file.js 2.5 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394
  1. const { resolve } = require('node:path')
  2. const { stat, chmod } = require('node:fs/promises')
  3. const cacache = require('cacache')
  4. const fsm = require('fs-minipass')
  5. const Fetcher = require('./fetcher.js')
  6. const _ = require('./util/protected.js')
  7. class FileFetcher extends Fetcher {
  8. constructor (spec, opts) {
  9. super(spec, opts)
  10. // just the fully resolved filename
  11. this.resolved = this.spec.fetchSpec
  12. }
  13. get types () {
  14. return ['file']
  15. }
  16. manifest () {
  17. if (this.package) {
  18. return Promise.resolve(this.package)
  19. }
  20. // have to unpack the tarball for this.
  21. return cacache.tmp.withTmp(this.cache, this.opts, dir =>
  22. this.extract(dir)
  23. .then(() => this[_.readPackageJson](dir))
  24. .then(mani => this.package = {
  25. ...mani,
  26. _integrity: this.integrity && String(this.integrity),
  27. _resolved: this.resolved,
  28. _from: this.from,
  29. }))
  30. }
  31. #exeBins (pkg, dest) {
  32. if (!pkg.bin) {
  33. return Promise.resolve()
  34. }
  35. return Promise.all(Object.keys(pkg.bin).map(async k => {
  36. const script = resolve(dest, pkg.bin[k])
  37. // Best effort. Ignore errors here, the only result is that
  38. // a bin script is not executable. But if it's missing or
  39. // something, we just leave it for a later stage to trip over
  40. // when we can provide a more useful contextual error.
  41. try {
  42. const st = await stat(script)
  43. const mode = st.mode | 0o111
  44. if (mode === st.mode) {
  45. return
  46. }
  47. await chmod(script, mode)
  48. } catch {
  49. // Ignore errors here
  50. }
  51. }))
  52. }
  53. extract (dest) {
  54. // if we've already loaded the manifest, then the super got it.
  55. // but if not, read the unpacked manifest and chmod properly.
  56. return super.extract(dest)
  57. .then(result => this.package ? result
  58. : this[_.readPackageJson](dest).then(pkg =>
  59. this.#exeBins(pkg, dest)).then(() => result))
  60. }
  61. [_.tarballFromResolved] () {
  62. // create a read stream and return it
  63. return new fsm.ReadStream(this.resolved)
  64. }
  65. packument () {
  66. // simulate based on manifest
  67. return this.manifest().then(mani => ({
  68. name: mani.name,
  69. 'dist-tags': {
  70. [this.defaultTag]: mani.version,
  71. },
  72. versions: {
  73. [mani.version]: {
  74. ...mani,
  75. dist: {
  76. tarball: `file:${this.resolved}`,
  77. integrity: this.integrity && String(this.integrity),
  78. },
  79. },
  80. },
  81. }))
  82. }
  83. }
  84. module.exports = FileFetcher