filesystem.js 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152
  1. 'use strict'
  2. const fs = process.versions.electron ? require('original-fs') : require('fs')
  3. const path = require('path')
  4. const tmp = require('tmp')
  5. const UINT64 = require('cuint').UINT64
  6. class Filesystem {
  7. constructor (src) {
  8. this.src = path.resolve(src)
  9. this.header = {files: {}}
  10. this.offset = UINT64(0)
  11. }
  12. searchNodeFromDirectory (p) {
  13. let json = this.header
  14. const dirs = p.split(path.sep)
  15. for (const dir of dirs) {
  16. if (dir !== '.') {
  17. json = json.files[dir]
  18. }
  19. }
  20. return json
  21. }
  22. searchNodeFromPath (p) {
  23. p = path.relative(this.src, p)
  24. if (!p) { return this.header }
  25. const name = path.basename(p)
  26. const node = this.searchNodeFromDirectory(path.dirname(p))
  27. if (node.files == null) {
  28. node.files = {}
  29. }
  30. if (node.files[name] == null) {
  31. node.files[name] = {}
  32. }
  33. return node.files[name]
  34. }
  35. insertDirectory (p, shouldUnpack) {
  36. const node = this.searchNodeFromPath(p)
  37. if (shouldUnpack) {
  38. node.unpacked = shouldUnpack
  39. }
  40. node.files = {}
  41. return node.files
  42. }
  43. insertFile (p, shouldUnpack, file, options, callback) {
  44. const dirNode = this.searchNodeFromPath(path.dirname(p))
  45. const node = this.searchNodeFromPath(p)
  46. if (shouldUnpack || dirNode.unpacked) {
  47. node.size = file.stat.size
  48. node.unpacked = true
  49. process.nextTick(callback)
  50. return
  51. }
  52. const handler = () => {
  53. const size = file.transformed ? file.transformed.stat.size : file.stat.size
  54. // JavaScript can not precisely present integers >= UINT32_MAX.
  55. if (size > 4294967295) {
  56. throw new Error(`${p}: file size can not be larger than 4.2GB`)
  57. }
  58. node.size = size
  59. node.offset = this.offset.toString()
  60. if (process.platform !== 'win32' && (file.stat.mode & 0o100)) {
  61. node.executable = true
  62. }
  63. this.offset.add(UINT64(size))
  64. return callback()
  65. }
  66. const tr = options.transform && options.transform(p)
  67. if (tr) {
  68. return tmp.file(function (err, path) {
  69. if (err) { return handler() }
  70. const out = fs.createWriteStream(path)
  71. const stream = fs.createReadStream(p)
  72. stream.pipe(tr).pipe(out)
  73. return out.on('close', function () {
  74. file.transformed = {
  75. path,
  76. stat: fs.lstatSync(path)
  77. }
  78. return handler()
  79. })
  80. })
  81. } else {
  82. return process.nextTick(handler)
  83. }
  84. }
  85. insertLink (p, stat) {
  86. const link = path.relative(fs.realpathSync(this.src), fs.realpathSync(p))
  87. if (link.substr(0, 2) === '..') {
  88. throw new Error(`${p}: file links out of the package`)
  89. }
  90. const node = this.searchNodeFromPath(p)
  91. node.link = link
  92. return link
  93. }
  94. listFiles () {
  95. const files = []
  96. const fillFilesFromHeader = function (p, json) {
  97. if (!json.files) {
  98. return
  99. }
  100. return (() => {
  101. const result = []
  102. for (const f in json.files) {
  103. const fullPath = path.join(p, f)
  104. files.push(fullPath)
  105. result.push(fillFilesFromHeader(fullPath, json.files[f]))
  106. }
  107. return result
  108. })()
  109. }
  110. fillFilesFromHeader('/', this.header)
  111. return files
  112. }
  113. getNode (p) {
  114. const node = this.searchNodeFromDirectory(path.dirname(p))
  115. const name = path.basename(p)
  116. if (name) {
  117. return node.files[name]
  118. } else {
  119. return node
  120. }
  121. }
  122. getFile (p, followLinks) {
  123. followLinks = typeof followLinks === 'undefined' ? true : followLinks
  124. const info = this.getNode(p)
  125. // if followLinks is false we don't resolve symlinks
  126. if (info.link && followLinks) {
  127. return this.getFile(info.link)
  128. } else {
  129. return info
  130. }
  131. }
  132. }
  133. module.exports = Filesystem