UNPKG

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