1 | 'use strict'
|
2 | const fs = require('fs')
|
3 | const path = require('path')
|
4 | let tmp = null
|
5 | const UINT64 = require('cuint').UINT64
|
6 |
|
7 | class 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 |
|
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 |
|
146 | if (info.link && followLinks) {
|
147 | return this.getFile(info.link)
|
148 | } else {
|
149 | return info
|
150 | }
|
151 | }
|
152 | }
|
153 |
|
154 | module.exports = Filesystem
|