1 | 'use strict'
|
2 | const fs = require('fs')
|
3 | const path = require('path')
|
4 | const minimatch = require('minimatch')
|
5 | const mkdirp = require('mkdirp')
|
6 |
|
7 | const Filesystem = require('./filesystem')
|
8 | const disk = require('./disk')
|
9 | const crawlFilesystem = require('./crawlfs')
|
10 | const createSnapshot = require('./snapshot')
|
11 |
|
12 |
|
13 |
|
14 |
|
15 |
|
16 |
|
17 |
|
18 |
|
19 | const isUnpackDir = function (path, pattern, unpackDirs) {
|
20 | if (path.indexOf(pattern) === 0 || minimatch(path, pattern)) {
|
21 | if (unpackDirs.indexOf(path) === -1) {
|
22 | unpackDirs.push(path)
|
23 | }
|
24 | return true
|
25 | } else {
|
26 | for (let i = 0; i < unpackDirs.length; i++) {
|
27 | if (path.indexOf(unpackDirs[i]) === 0) {
|
28 | return true
|
29 | }
|
30 | }
|
31 | return false
|
32 | }
|
33 | }
|
34 |
|
35 | module.exports.createPackage = function (src, dest, callback) {
|
36 | return module.exports.createPackageWithOptions(src, dest, {}, callback)
|
37 | }
|
38 |
|
39 | module.exports.createPackageWithOptions = function (src, dest, options, callback) {
|
40 | const dot = typeof options.dot === 'undefined' ? true : options.dot
|
41 |
|
42 | return crawlFilesystem(src, { dot: dot }, function (error, filenames, metadata) {
|
43 | if (error) { return callback(error) }
|
44 | module.exports.createPackageFromFiles(src, dest, filenames, metadata, options, callback)
|
45 | })
|
46 | }
|
47 |
|
48 |
|
49 |
|
50 |
|
51 |
|
52 |
|
53 |
|
54 |
|
55 |
|
56 |
|
57 | module.exports.createPackageFromFiles = function (src, dest, filenames, metadata, options, callback) {
|
58 | if (typeof metadata === 'undefined' || metadata === null) { metadata = {} }
|
59 | const filesystem = new Filesystem(src)
|
60 | const files = []
|
61 | const unpackDirs = []
|
62 |
|
63 | let filenamesSorted = []
|
64 | if (options.ordering) {
|
65 | const orderingFiles = fs.readFileSync(options.ordering).toString().split('\n').map(function (line) {
|
66 | if (line.includes(':')) { line = line.split(':').pop() }
|
67 | line = line.trim()
|
68 | if (line.startsWith('/')) { line = line.slice(1) }
|
69 | return line
|
70 | })
|
71 |
|
72 | const ordering = []
|
73 | for (const file of orderingFiles) {
|
74 | const pathComponents = file.split(path.sep)
|
75 | let str = src
|
76 | for (const pathComponent of pathComponents) {
|
77 | str = path.join(str, pathComponent)
|
78 | ordering.push(str)
|
79 | }
|
80 | }
|
81 |
|
82 | let missing = 0
|
83 | const total = filenames.length
|
84 |
|
85 | for (const file of ordering) {
|
86 | if (!filenamesSorted.includes(file) && filenames.includes(file)) {
|
87 | filenamesSorted.push(file)
|
88 | }
|
89 | }
|
90 |
|
91 | for (const file of filenames) {
|
92 | if (!filenamesSorted.includes(file)) {
|
93 | filenamesSorted.push(file)
|
94 | missing += 1
|
95 | }
|
96 | }
|
97 |
|
98 | console.log(`Ordering file has ${((total - missing) / total) * 100}% coverage.`)
|
99 | } else {
|
100 | filenamesSorted = filenames
|
101 | }
|
102 |
|
103 | const handleFile = function (filename, done) {
|
104 | let file = metadata[filename]
|
105 | let type
|
106 | if (!file) {
|
107 | const stat = fs.lstatSync(filename)
|
108 | if (stat.isDirectory()) { type = 'directory' }
|
109 | if (stat.isFile()) { type = 'file' }
|
110 | if (stat.isSymbolicLink()) { type = 'link' }
|
111 | file = {stat, type}
|
112 | }
|
113 |
|
114 | let shouldUnpack
|
115 | switch (file.type) {
|
116 | case 'directory':
|
117 | shouldUnpack = options.unpackDir
|
118 | ? isUnpackDir(path.relative(src, filename), options.unpackDir, unpackDirs)
|
119 | : false
|
120 | filesystem.insertDirectory(filename, shouldUnpack)
|
121 | break
|
122 | case 'file':
|
123 | shouldUnpack = false
|
124 | if (options.unpack) {
|
125 | shouldUnpack = minimatch(filename, options.unpack, {matchBase: true})
|
126 | }
|
127 | if (!shouldUnpack && options.unpackDir) {
|
128 | const dirName = path.relative(src, path.dirname(filename))
|
129 | shouldUnpack = isUnpackDir(dirName, options.unpackDir, unpackDirs)
|
130 | }
|
131 | files.push({filename: filename, unpack: shouldUnpack})
|
132 | filesystem.insertFile(filename, shouldUnpack, file, options, done)
|
133 | return
|
134 | case 'link':
|
135 | filesystem.insertLink(filename, file.stat)
|
136 | break
|
137 | }
|
138 | return process.nextTick(done)
|
139 | }
|
140 |
|
141 | const insertsDone = function () {
|
142 | return mkdirp(path.dirname(dest), function (error) {
|
143 | if (error) { return callback(error) }
|
144 | return disk.writeFilesystem(dest, filesystem, files, metadata, function (error) {
|
145 | if (error) { return callback(error) }
|
146 | if (options.snapshot) {
|
147 | return createSnapshot(src, dest, filenames, metadata, options, callback)
|
148 | } else {
|
149 | return callback(null)
|
150 | }
|
151 | })
|
152 | })
|
153 | }
|
154 |
|
155 | const names = filenamesSorted.slice()
|
156 |
|
157 | const next = function (name) {
|
158 | if (!name) { return insertsDone() }
|
159 |
|
160 | return handleFile(name, function () {
|
161 | return next(names.shift())
|
162 | })
|
163 | }
|
164 |
|
165 | return next(names.shift())
|
166 | }
|
167 |
|
168 | module.exports.statFile = function (archive, filename, followLinks) {
|
169 | const filesystem = disk.readFilesystemSync(archive)
|
170 | return filesystem.getFile(filename, followLinks)
|
171 | }
|
172 |
|
173 | module.exports.listPackage = function (archive) {
|
174 | return disk.readFilesystemSync(archive).listFiles()
|
175 | }
|
176 |
|
177 | module.exports.extractFile = function (archive, filename) {
|
178 | const filesystem = disk.readFilesystemSync(archive)
|
179 | return disk.readFileSync(filesystem, filename, filesystem.getFile(filename))
|
180 | }
|
181 |
|
182 | module.exports.extractAll = function (archive, dest) {
|
183 | const filesystem = disk.readFilesystemSync(archive)
|
184 | const filenames = filesystem.listFiles()
|
185 |
|
186 |
|
187 | const followLinks = process.platform === 'win32'
|
188 |
|
189 |
|
190 | mkdirp.sync(dest)
|
191 |
|
192 | return filenames.map((filename) => {
|
193 | filename = filename.substr(1)
|
194 | const destFilename = path.join(dest, filename)
|
195 | const file = filesystem.getFile(filename, followLinks)
|
196 | if (file.files) {
|
197 |
|
198 | mkdirp.sync(destFilename)
|
199 | } else if (file.link) {
|
200 |
|
201 | const linkSrcPath = path.dirname(path.join(dest, file.link))
|
202 | const linkDestPath = path.dirname(destFilename)
|
203 | const relativePath = path.relative(linkDestPath, linkSrcPath);
|
204 |
|
205 | (() => {
|
206 | try {
|
207 | fs.unlinkSync(destFilename)
|
208 | } catch (error) {}
|
209 | })()
|
210 | const linkTo = path.join(relativePath, path.basename(file.link))
|
211 | fs.symlinkSync(linkTo, destFilename)
|
212 | } else {
|
213 |
|
214 | const content = disk.readFileSync(filesystem, filename, file)
|
215 | fs.writeFileSync(destFilename, content)
|
216 | }
|
217 | })
|
218 | }
|