switch-profile 1.1.0 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +197 -94
- package/dist/cli.js +7921 -0
- package/index.js +12 -409
- package/package.json +31 -25
- package/.eslintrc.json +0 -39
- package/CHANGELOG.md +0 -120
- package/docs/architecture.md +0 -199
- package/docs/aws-profile-management.md +0 -325
- package/docs/cli-interface.md +0 -304
- package/docs/configuration-files.md +0 -240
- package/docs/development-guide.md +0 -168
- package/docs/gotchas.md +0 -82
- package/skills-lock.json +0 -73
- package/src/aws/index.js +0 -574
- package/src/aws/regions.js +0 -103
- package/src/core.js +0 -64
- package/src/fileHelper.js +0 -433
package/src/core.js
DELETED
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
require('colors')
|
|
2
|
-
const { error: { catchErrors } } = require('puffy')
|
|
3
|
-
const { exec } = require('child_process')
|
|
4
|
-
|
|
5
|
-
const IS_WINDOWS = process.platform == 'win32'
|
|
6
|
-
|
|
7
|
-
const _exec = cmd => new Promise((next,fail) => {
|
|
8
|
-
exec(cmd, (error, stdout, stderr) => {
|
|
9
|
-
if (error || stderr)
|
|
10
|
-
fail(error || stderr)
|
|
11
|
-
else
|
|
12
|
-
next(stdout)
|
|
13
|
-
})
|
|
14
|
-
})
|
|
15
|
-
|
|
16
|
-
const _commandExistsResult = {}
|
|
17
|
-
const isCommandExist = (cmd, errorMsg) => () => catchErrors((async () => {
|
|
18
|
-
if (!cmd)
|
|
19
|
-
return false
|
|
20
|
-
|
|
21
|
-
if (_commandExistsResult[cmd] !== undefined)
|
|
22
|
-
return _commandExistsResult[cmd]
|
|
23
|
-
|
|
24
|
-
const testCmd = IS_WINDOWS ? 'where' : 'which'
|
|
25
|
-
const data = await _exec(`${testCmd} ${cmd}`).catch(() => false)
|
|
26
|
-
|
|
27
|
-
if (!data)
|
|
28
|
-
throw new Error(`Command ${cmd} not found${errorMsg ? `. ${errorMsg}` : ''}`)
|
|
29
|
-
|
|
30
|
-
_commandExistsResult[cmd] = true
|
|
31
|
-
return true
|
|
32
|
-
})())
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
*
|
|
36
|
-
* @param {[Error]} errors
|
|
37
|
-
* @param {Boolean} options.noStack
|
|
38
|
-
* @return {String}
|
|
39
|
-
*/
|
|
40
|
-
const formatErrorMsg = (errors, options) => {
|
|
41
|
-
if (!errors || !errors.length)
|
|
42
|
-
return ''
|
|
43
|
-
|
|
44
|
-
const noStack = options && options.noStack
|
|
45
|
-
const msg = errors.map(e => noStack ? e.message||'' : e.stack||e.message||'').join('\n')
|
|
46
|
-
const prefix = /^error/.test(msg.toLowerCase().trim()) ? '' : 'ERROR - '
|
|
47
|
-
return `${prefix}${msg}`
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
const printErrors = (errors, options) => console.log(formatErrorMsg(errors, options).red)
|
|
51
|
-
const printAWSerrors = (errors, options) => {
|
|
52
|
-
let msg = formatErrorMsg(errors, options)
|
|
53
|
-
if (msg.indexOf('ommand aws not found') >= 0)
|
|
54
|
-
msg += `\n\nTo fix this issue, try installing the ${'aws CLI'.bold}`
|
|
55
|
-
|
|
56
|
-
console.log(msg.red)
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
module.exports = {
|
|
60
|
-
exec: _exec,
|
|
61
|
-
isCommandExist,
|
|
62
|
-
printErrors,
|
|
63
|
-
printAWSerrors
|
|
64
|
-
}
|
package/src/fileHelper.js
DELETED
|
@@ -1,433 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
/**
|
|
3
|
-
* Copyright (c) 2017-2020, Neap Pty Ltd.
|
|
4
|
-
* All rights reserved.
|
|
5
|
-
*
|
|
6
|
-
* This source code is licensed under the BSD-style license.
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
// npm i rimraf fast-glob core-async mime-types archiver tar-stream convert-stream
|
|
10
|
-
const { co } = require('core-async')
|
|
11
|
-
const fs = require('fs')
|
|
12
|
-
const { join, resolve } = require('path')
|
|
13
|
-
const rimraf = require('rimraf')
|
|
14
|
-
const fg = require('fast-glob')
|
|
15
|
-
const mime = require('mime-types')
|
|
16
|
-
const archiver = require('archiver')
|
|
17
|
-
const tar = require('tar-stream')
|
|
18
|
-
const { toBuffer } = require('convert-stream')
|
|
19
|
-
const { Writable } = require('stream')
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* Checks if a file or folder exists
|
|
23
|
-
*
|
|
24
|
-
* @param {String} filePath Absolute path to file or folder on the local machine
|
|
25
|
-
* @return {Boolean}
|
|
26
|
-
*/
|
|
27
|
-
const fileExists = filePath => new Promise(onSuccess => fs.exists(filePath, yes => onSuccess(yes ? true : false)))
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* Creates a folder.
|
|
31
|
-
*
|
|
32
|
-
* @param {String} folderPath Absolute folder path on the local machine
|
|
33
|
-
* @return {Object}
|
|
34
|
-
*/
|
|
35
|
-
const createFolder = folderPath => new Promise((onSuccess, onFailure) => fs.mkdir(folderPath, err => {
|
|
36
|
-
if (!err || (err.message || '').indexOf('file already exists') >= 0)
|
|
37
|
-
onSuccess(folderPath)
|
|
38
|
-
else
|
|
39
|
-
onFailure(err)
|
|
40
|
-
}))
|
|
41
|
-
|
|
42
|
-
/**
|
|
43
|
-
* Deletes a folder.
|
|
44
|
-
*
|
|
45
|
-
* @param {String} folderPath Absolute folder path on the local machine
|
|
46
|
-
* @return {Void}
|
|
47
|
-
*/
|
|
48
|
-
const deleteFolder = folderPath => new Promise(onSuccess => rimraf(folderPath, () => onSuccess()))
|
|
49
|
-
|
|
50
|
-
/**
|
|
51
|
-
* Deletes a file.
|
|
52
|
-
*
|
|
53
|
-
* @param {String} filePath Absolute file path on the local machine
|
|
54
|
-
* @return {Void}
|
|
55
|
-
*/
|
|
56
|
-
const deleteFile = filePath => new Promise((onSuccess, onFailure) => fs.unlink(filePath, err => err ? onFailure(err) : onSuccess()))
|
|
57
|
-
|
|
58
|
-
/**
|
|
59
|
-
* Creates file or update file located under 'filePath'.
|
|
60
|
-
*
|
|
61
|
-
* @param {String} filePath Absolute file path on the local machine
|
|
62
|
-
* @param {Object} content File content
|
|
63
|
-
* @param {Boolean} options.append Default false. If true, this function appends rather than overrides.
|
|
64
|
-
* @param {String} options.appendSep Default '\n'. That the string used to separate appended content. This option is only
|
|
65
|
-
* active when 'options.append' is set to true.
|
|
66
|
-
* @return {Void}
|
|
67
|
-
*/
|
|
68
|
-
const writeToFile = (filePath, content, options) => new Promise((onSuccess, onFailure) => {
|
|
69
|
-
content = content || ''
|
|
70
|
-
const { append, appendSep='\n' } = options || {}
|
|
71
|
-
const stringContent = (typeof(content) == 'string' || content instanceof Buffer) ? content : JSON.stringify(content, null, ' ')
|
|
72
|
-
const fn = append ? fs.appendFile : fs.writeFile
|
|
73
|
-
fn(filePath, append ? `${stringContent}${appendSep}` : stringContent, err => err ? onFailure(err) : onSuccess())
|
|
74
|
-
})
|
|
75
|
-
|
|
76
|
-
/**
|
|
77
|
-
* Creates folders under a rootFolder
|
|
78
|
-
*
|
|
79
|
-
* @param {String} rootFolder Root folder. This folder must exist prior to calling this function.
|
|
80
|
-
* @param {Array} folders Array of folders so that the path of the last item in that array will be:
|
|
81
|
-
* rootFolder/folders[0]/folders[1]/.../folders[n]
|
|
82
|
-
* @param {Object} options
|
|
83
|
-
* @param {Boolean} options.deletePreviousContent If set to true, this will delete the content of the existing folder
|
|
84
|
-
* @return {String} Path of the latest folder:
|
|
85
|
-
* rootFolder/folders[0]/folders[1]/.../folders[n]
|
|
86
|
-
*/
|
|
87
|
-
const createFolders = (rootFolder, folders=[], options={}) => co(function *() {
|
|
88
|
-
const { deletePreviousContent } = options
|
|
89
|
-
if (!rootFolder)
|
|
90
|
-
throw new Error('\'rootFolder\' is required.')
|
|
91
|
-
const rootExists = yield fileExists(rootFolder)
|
|
92
|
-
if (!rootExists)
|
|
93
|
-
throw new Error(`Root folder ${rootFolder} does not exist.`)
|
|
94
|
-
|
|
95
|
-
yield folders.reduce((processPrevious, f) => co(function *(){
|
|
96
|
-
const rootPath = yield processPrevious
|
|
97
|
-
const folderPath = join(rootPath, f)
|
|
98
|
-
const folderExists = yield fileExists(folderPath)
|
|
99
|
-
if (folderExists && deletePreviousContent)
|
|
100
|
-
yield deleteFolder(folderPath)
|
|
101
|
-
else if (!folderExists)
|
|
102
|
-
yield createFolder(folderPath)
|
|
103
|
-
return folderPath
|
|
104
|
-
}), Promise.resolve(rootFolder))
|
|
105
|
-
})
|
|
106
|
-
|
|
107
|
-
/**
|
|
108
|
-
* Gets a file under a Google Cloud Storage's 'filePath'.
|
|
109
|
-
*
|
|
110
|
-
* @param {String} filePath Absolute file path on the local machine
|
|
111
|
-
* @return {Buffer}
|
|
112
|
-
*/
|
|
113
|
-
const readFile = filePath => new Promise((onSuccess, onFailure) => fs.readFile(filePath, (err, data) => err ? onFailure(err) : onSuccess(data)))
|
|
114
|
-
|
|
115
|
-
//
|
|
116
|
-
// Gets an array of absolute file paths located under the 'folderPath', or a Channel that streams those files.
|
|
117
|
-
//
|
|
118
|
-
// @param {String} folderPath Absolute path to folder
|
|
119
|
-
// @param {String|[String]} options.pattern Default is '*.*' which means all immediate files. To get all the files
|
|
120
|
-
// use '**/*.*'
|
|
121
|
-
// @param {String|[String]} options.ignore e.g., '**/node_modules/**'
|
|
122
|
-
// @param {Channel} options.channel When a channel is passed, all files are streamed to that channel instead of
|
|
123
|
-
// being returned as an array. The last file found add a specific string on
|
|
124
|
-
// the channel to indicates that the scan is over. That string value is: 'end'.
|
|
125
|
-
// @return {[String]} If a channel is passed via 'options.channel', than the output is null and
|
|
126
|
-
// the files are streamed to that channel.
|
|
127
|
-
//
|
|
128
|
-
const listFiles = (folderPath='', options={}) => co(function *(){
|
|
129
|
-
const pattern = options.pattern || '*.*'
|
|
130
|
-
const ignore = options.ignore
|
|
131
|
-
const channel = options.channel
|
|
132
|
-
const patterns = (typeof(pattern) == 'string' ? [pattern] : pattern).map(p => join(folderPath, p))
|
|
133
|
-
const opts = ignore ? { ignore:(typeof(ignore) == 'string' ? [ignore] : ignore).map(p => join(folderPath, p)) } : {}
|
|
134
|
-
|
|
135
|
-
if (!channel)
|
|
136
|
-
return yield fg(patterns,opts)
|
|
137
|
-
else {
|
|
138
|
-
const stream = fg.stream(patterns,opts)
|
|
139
|
-
stream.on('data', data => {
|
|
140
|
-
channel.put(data)
|
|
141
|
-
})
|
|
142
|
-
stream.on('end', () => {
|
|
143
|
-
channel.put('end')
|
|
144
|
-
stream.destroy()
|
|
145
|
-
})
|
|
146
|
-
stream.on('error', err => {
|
|
147
|
-
console.log(`An error happened while streaming files from ${folderPath}: ${err}`)
|
|
148
|
-
stream.destroy()
|
|
149
|
-
})
|
|
150
|
-
|
|
151
|
-
return null
|
|
152
|
-
}
|
|
153
|
-
})
|
|
154
|
-
|
|
155
|
-
/**
|
|
156
|
-
* Gets the absolute path. If not input is passed, it returns the current working directory. Supports both Windows and Unix OSes.
|
|
157
|
-
*
|
|
158
|
-
* @param {String} somePath Some absolute or relative file or folder path.
|
|
159
|
-
* @return {String} Absolute path
|
|
160
|
-
*/
|
|
161
|
-
const getAbsolutePath = somePath => {
|
|
162
|
-
if (!somePath)
|
|
163
|
-
return process.cwd()
|
|
164
|
-
else if (somePath.match(/^\./))
|
|
165
|
-
return resolve(somePath)
|
|
166
|
-
else if (somePath.match(/^(\\|\/|~)/))
|
|
167
|
-
return somePath
|
|
168
|
-
else if (typeof(somePath) == 'string')
|
|
169
|
-
return resolve(somePath)
|
|
170
|
-
else
|
|
171
|
-
throw new Error(`Invalid path ${somePath}`)
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
/**
|
|
175
|
-
* Gets a JSON object loacted under 'filePath'. This method is an alternative to 'require(filePath)' which caches results and prevents
|
|
176
|
-
* to get access to a refreshed version of the JSON file.
|
|
177
|
-
*
|
|
178
|
-
* @param {String} filePath Absolute path to the JSON file.
|
|
179
|
-
* @param {String} defaultValue Default is {}
|
|
180
|
-
*
|
|
181
|
-
* @return {Object} JSON Object
|
|
182
|
-
*/
|
|
183
|
-
const getJSON = (filePath, defaultValue={}) => readFile(filePath).then(text => {
|
|
184
|
-
if (!text || !text.length)
|
|
185
|
-
return defaultValue
|
|
186
|
-
|
|
187
|
-
try {
|
|
188
|
-
return JSON.parse(text.toString()) || defaultValue
|
|
189
|
-
} catch(e) {
|
|
190
|
-
return (() => ({}))(e)
|
|
191
|
-
}
|
|
192
|
-
})
|
|
193
|
-
|
|
194
|
-
const _isFolder = folderPath => new Promise(resolve => fs.stat(folderPath, (err,data) => resolve(err || !data || !data.isDirectory ? false : data.isDirectory())))
|
|
195
|
-
const _readdir = folderPath => new Promise(resolve => fs.readdir(folderPath, (err,data) => resolve(err || !data ? [] : data)))
|
|
196
|
-
const _rmdir = folderPath => new Promise(resolve => fs.rmdir(folderPath, resolve))
|
|
197
|
-
|
|
198
|
-
/**
|
|
199
|
-
* Deletes all the empty folders(incl. 'rootFolder') under 'rootFolder'.
|
|
200
|
-
*
|
|
201
|
-
* @param {String} rootFolder Absolute path to folder.
|
|
202
|
-
* @yield {Void}
|
|
203
|
-
*/
|
|
204
|
-
const deleteEmptyFolders = rootFolder => co(function *(){
|
|
205
|
-
const isDir = yield _isFolder(rootFolder)
|
|
206
|
-
if (!isDir)
|
|
207
|
-
return
|
|
208
|
-
|
|
209
|
-
let files = yield _readdir(rootFolder)
|
|
210
|
-
yield files.map(file => co(function *(){
|
|
211
|
-
const fullPath = join(rootFolder, file)
|
|
212
|
-
yield deleteEmptyFolders(fullPath)
|
|
213
|
-
}))
|
|
214
|
-
|
|
215
|
-
// re-evaluate files; after deleting subfolder
|
|
216
|
-
// we may have parent folder empty now
|
|
217
|
-
if (files.length > 0)
|
|
218
|
-
files = yield _readdir(rootFolder)
|
|
219
|
-
|
|
220
|
-
if (files.length == 0)
|
|
221
|
-
yield _rmdir(rootFolder)
|
|
222
|
-
})
|
|
223
|
-
|
|
224
|
-
/**
|
|
225
|
-
* Gets the mime type associated with a file extension.
|
|
226
|
-
*
|
|
227
|
-
* @param {String} fileOrExt e.g., 'json', '.md', 'file.html', 'folder/file.js', 'data:image/png;base64,....'
|
|
228
|
-
* @return {String} e.g., 'application/json', 'text/markdown', 'text/html', 'application/javascript'
|
|
229
|
-
*/
|
|
230
|
-
const getMimeType = fileOrExt => {
|
|
231
|
-
if (!fileOrExt)
|
|
232
|
-
return ''
|
|
233
|
-
|
|
234
|
-
// Test if 'fileOrExt' is a data URI
|
|
235
|
-
if (/^data:(.*?),/.test(fileOrExt))
|
|
236
|
-
return (fileOrExt.match(/^data:(.*?);/, '') || [])[1] || ''
|
|
237
|
-
|
|
238
|
-
return mime.lookup(fileOrExt) || ''
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
/**
|
|
242
|
-
* Gets the content type associated with a file extension.
|
|
243
|
-
*
|
|
244
|
-
* @param {String} fileOrExt e.g., 'json', '.md', 'file.html', 'folder/file.js'
|
|
245
|
-
* @return {String} e.g., 'application/json; charset=utf-8', 'text/x-markdown; charset=utf-8', 'text/html; charset=utf-8'
|
|
246
|
-
*/
|
|
247
|
-
const getContentType = fileOrExt => !fileOrExt ? '' : (mime.contentType(fileOrExt) || '')
|
|
248
|
-
|
|
249
|
-
/**
|
|
250
|
-
* Gets the a file's extension or the file extension associated with a mime type.
|
|
251
|
-
*
|
|
252
|
-
* @param {String} mimeType e.g., 'application/json', 'text/x-markdown', 'hello.pdf'
|
|
253
|
-
* @return {String} e.g., 'json', 'md', 'pdf'
|
|
254
|
-
*/
|
|
255
|
-
const getExt = fileOrMimeType => {
|
|
256
|
-
if (!fileOrMimeType)
|
|
257
|
-
return ''
|
|
258
|
-
|
|
259
|
-
const t = fileOrMimeType.split('.')
|
|
260
|
-
const [ext] = t[1] ? t.slice(-1) : [null]
|
|
261
|
-
let x
|
|
262
|
-
if (!ext && fileOrMimeType.indexOf('/') >= 0)
|
|
263
|
-
x = mime.extension(fileOrMimeType) || ''
|
|
264
|
-
else
|
|
265
|
-
x = ext
|
|
266
|
-
|
|
267
|
-
return x == 'document' ? 'doc' : x
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
/**
|
|
271
|
-
* Get
|
|
272
|
-
*
|
|
273
|
-
* @param {String} mimeType e.g., 'image/png'
|
|
274
|
-
* @return {String} output.type e.g., 'image'
|
|
275
|
-
* @return {String} output.type e.g., 'png'
|
|
276
|
-
*/
|
|
277
|
-
const getFileType = mimeType => {
|
|
278
|
-
if (!mimeType)
|
|
279
|
-
throw new Error('Missing required argument \'mimeType\'.')
|
|
280
|
-
|
|
281
|
-
const [type,subType] = mimeType.split('/')
|
|
282
|
-
|
|
283
|
-
return {
|
|
284
|
-
type,
|
|
285
|
-
subType
|
|
286
|
-
}
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
/**
|
|
290
|
-
* Formats file name to make sure it contains a name and an extension.
|
|
291
|
-
*
|
|
292
|
-
* @param {String} mimeType e.g., 'application/json'
|
|
293
|
-
* @param {String} fileName e.g., 'blob' or null
|
|
294
|
-
* @param {String} defaultName e.g., '12345' (if the value contains an extension, that extension overides the 'mimeType').
|
|
295
|
-
*
|
|
296
|
-
* @return {String} output.fileName e.g., 'blob.json' or '12345.json'
|
|
297
|
-
* @return {String} output.mimeType e.g., 'application/json'
|
|
298
|
-
*/
|
|
299
|
-
const getCanonicalFile = ({ mimeType, fileName, defaultName }) => {
|
|
300
|
-
if (!fileName && !defaultName)
|
|
301
|
-
throw new Error('Missing required argument. When \'fileName\' is not specified, \'defaultName\' is required.')
|
|
302
|
-
|
|
303
|
-
const _fileName = fileName || defaultName
|
|
304
|
-
const fileExt = getExt(_fileName)
|
|
305
|
-
|
|
306
|
-
if (fileExt) {
|
|
307
|
-
const _mimeType = getMimeType(_fileName) || mimeType
|
|
308
|
-
if (!_mimeType)
|
|
309
|
-
throw new Error(`Failed to determine the mime type for file ${_fileName}.`)
|
|
310
|
-
|
|
311
|
-
return { ...getFileType(_mimeType), fileName: _fileName, mimeType:_mimeType }
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
if (!mimeType)
|
|
315
|
-
throw new Error('Missing required argument. When \'fileName\' and \'defaultName\' don\'t contain an extension, \'mimeType\' is required.')
|
|
316
|
-
|
|
317
|
-
const mimeExt = getExt(mimeType)
|
|
318
|
-
if (!mimeExt)
|
|
319
|
-
throw new Error(`Failed to determine the extension of mime type ${mimeType}.`)
|
|
320
|
-
|
|
321
|
-
return { ...getFileType(mimeType), fileName:`${_fileName}.${mimeExt}`, mimeType }
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
/**
|
|
325
|
-
* Changes the file extension based on the mime type.
|
|
326
|
-
*
|
|
327
|
-
* @param {String} fileName e.g., 'hello.jpeg'
|
|
328
|
-
* @param {String} mimeType e.g., 'image/png'
|
|
329
|
-
* @return {String} e.g., 'hello.png'
|
|
330
|
-
*/
|
|
331
|
-
const changeFileExt = (fileName, mimeType) => {
|
|
332
|
-
if (!fileName || !mimeType)
|
|
333
|
-
return fileName
|
|
334
|
-
|
|
335
|
-
let mimeExt = getExt(mimeType)
|
|
336
|
-
if (!mimeExt)
|
|
337
|
-
throw new Error(`Failed to determine the extension of mime type ${mimeType}.`)
|
|
338
|
-
mimeExt = mimeExt == 'document' ? 'doc' : mimeExt
|
|
339
|
-
|
|
340
|
-
return fileName.indexOf('.') < 0 ? `${fileName}.${mimeExt}` : fileName.replace(/\.(.*?)$/, `.${mimeExt}`)
|
|
341
|
-
}
|
|
342
|
-
|
|
343
|
-
/**
|
|
344
|
-
* Zips a folder into a buffer using zip (default) or tar.
|
|
345
|
-
*
|
|
346
|
-
* @param {String} src Folder's absolute path on local machine.
|
|
347
|
-
* @param {String} options.type Default 'zip'. Valid values are: 'zip', 'tar'
|
|
348
|
-
* @return {Buffer}
|
|
349
|
-
*/
|
|
350
|
-
const zipToBuffer = (src, options) => co(function *(){
|
|
351
|
-
const result = yield fileExists(src)
|
|
352
|
-
|
|
353
|
-
if (!result)
|
|
354
|
-
throw new Error(`Failed to zip folder ${src}. This folder does not exist.`)
|
|
355
|
-
|
|
356
|
-
const { type='zip' } = options || {}
|
|
357
|
-
|
|
358
|
-
if (type == 'zip') {
|
|
359
|
-
const archive = archiver('zip', { zlib: { level: 6 } }) // this goes from 1 (minimum compression) to 9 (default 6). The higher the compression, the longer it takes to zip and unzip.
|
|
360
|
-
const getBuffer = toBuffer(archive)
|
|
361
|
-
|
|
362
|
-
archive.on('warning', err => {
|
|
363
|
-
console.log('Warning while creating zip file', err)
|
|
364
|
-
})
|
|
365
|
-
|
|
366
|
-
archive.on('error', err => {
|
|
367
|
-
throw err
|
|
368
|
-
})
|
|
369
|
-
|
|
370
|
-
archive.directory(src, '/')
|
|
371
|
-
archive.finalize()
|
|
372
|
-
|
|
373
|
-
const buffer = yield getBuffer
|
|
374
|
-
|
|
375
|
-
return buffer
|
|
376
|
-
} else if (type == 'tar') {
|
|
377
|
-
const files = yield listFiles(src, { pattern:'**/*.*' })
|
|
378
|
-
const data = yield files.map(file => readFile(file, { encoding:null }).then(content => ({
|
|
379
|
-
file,
|
|
380
|
-
relFile: file.replace(src,''),
|
|
381
|
-
content
|
|
382
|
-
})))
|
|
383
|
-
|
|
384
|
-
const pack = tar.pack()
|
|
385
|
-
data.forEach(({ relFile, content }) => pack.entry({ name:relFile }, content))
|
|
386
|
-
const chunks = []
|
|
387
|
-
const writeStream = new Writable({
|
|
388
|
-
write(chunk, encoding, callback) {
|
|
389
|
-
chunks.push(chunk)
|
|
390
|
-
callback()
|
|
391
|
-
}
|
|
392
|
-
})
|
|
393
|
-
|
|
394
|
-
pack.pipe(writeStream)
|
|
395
|
-
pack.finalize()
|
|
396
|
-
|
|
397
|
-
yield new Promise((success) => pack.on('end', success))
|
|
398
|
-
|
|
399
|
-
const buffer = Buffer.concat(chunks)
|
|
400
|
-
|
|
401
|
-
return buffer
|
|
402
|
-
} else
|
|
403
|
-
throw new Error(`Unsupported zip type ${type}. Valid zip types: 'zip', 'tar'`)
|
|
404
|
-
}).catch(e => {
|
|
405
|
-
console.log(`Failed to zip folder ${src}`)
|
|
406
|
-
throw e
|
|
407
|
-
})
|
|
408
|
-
|
|
409
|
-
module.exports = {
|
|
410
|
-
list: listFiles,
|
|
411
|
-
json: {
|
|
412
|
-
'get': getJSON,
|
|
413
|
-
write: (filePath, obj) => writeToFile(filePath, obj)
|
|
414
|
-
},
|
|
415
|
-
read: readFile,
|
|
416
|
-
write: writeToFile,
|
|
417
|
-
exists: fileExists,
|
|
418
|
-
delete: deleteFile,
|
|
419
|
-
getAbsolutePath,
|
|
420
|
-
folder: {
|
|
421
|
-
create: createFolder,
|
|
422
|
-
createMany: createFolders,
|
|
423
|
-
delete: deleteFolder,
|
|
424
|
-
deleteEmpties: deleteEmptyFolders
|
|
425
|
-
},
|
|
426
|
-
getMimeType,
|
|
427
|
-
getContentType,
|
|
428
|
-
getExt,
|
|
429
|
-
getCanonicalFile,
|
|
430
|
-
changeFileExt,
|
|
431
|
-
getFileType,
|
|
432
|
-
zipToBuffer
|
|
433
|
-
}
|