Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
seleniumhq
GitHub Repository: seleniumhq/selenium
Path: blob/trunk/javascript/selenium-webdriver/io/zip.js
2884 views
1
// Licensed to the Software Freedom Conservancy (SFC) under one
2
// or more contributor license agreements. See the NOTICE file
3
// distributed with this work for additional information
4
// regarding copyright ownership. The SFC licenses this file
5
// to you under the Apache License, Version 2.0 (the
6
// "License"); you may not use this file except in compliance
7
// with the License. You may obtain a copy of the License at
8
//
9
// http://www.apache.org/licenses/LICENSE-2.0
10
//
11
// Unless required by applicable law or agreed to in writing,
12
// software distributed under the License is distributed on an
13
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14
// KIND, either express or implied. See the License for the
15
// specific language governing permissions and limitations
16
// under the License.
17
18
'use strict'
19
20
const jszip = require('jszip')
21
const path = require('node:path')
22
23
const io = require('./index')
24
const { InvalidArgumentError } = require('../lib/error')
25
26
/**
27
* Manages a zip archive.
28
*/
29
class Zip {
30
constructor() {
31
/** @private @const */
32
this.z_ = new jszip()
33
34
/** @private @const {!Set<!Promise<?>>} */
35
this.pendingAdds_ = new Set()
36
}
37
38
/**
39
* Adds a file to this zip.
40
*
41
* @param {string} filePath path to the file to add.
42
* @param {string=} zipPath path to the file in the zip archive, defaults
43
* to the basename of `filePath`.
44
* @return {!Promise<?>} a promise that will resolve when added.
45
*/
46
addFile(filePath, zipPath = path.basename(filePath)) {
47
let add = io
48
.read(filePath)
49
.then((buffer) => this.z_.file(/** @type {string} */ (zipPath.replace(/\\/g, '/')), buffer))
50
this.pendingAdds_.add(add)
51
return add.then(
52
() => this.pendingAdds_.delete(add),
53
(e) => {
54
this.pendingAdds_.delete(add)
55
throw e
56
},
57
)
58
}
59
60
/**
61
* Recursively adds a directory and all of its contents to this archive.
62
*
63
* @param {string} dirPath path to the directory to add.
64
* @param {string=} zipPath path to the folder in the archive to add the
65
* directory contents to. Defaults to the root folder.
66
* @return {!Promise<?>} returns a promise that will resolve when
67
* the operation is complete.
68
*/
69
addDir(dirPath, zipPath = '') {
70
return io.walkDir(dirPath).then((entries) => {
71
let archive = this.z_
72
if (zipPath) {
73
archive = archive.folder(zipPath)
74
}
75
76
let files = []
77
entries.forEach((spec) => {
78
if (spec.dir) {
79
archive.folder(spec.path)
80
} else {
81
files.push(this.addFile(path.join(dirPath, spec.path), path.join(zipPath, spec.path)))
82
}
83
})
84
85
return Promise.all(files)
86
})
87
}
88
89
/**
90
* @param {string} path File path to test for within the archive.
91
* @return {boolean} Whether this zip archive contains an entry with the given
92
* path.
93
*/
94
has(path) {
95
return this.z_.file(path) !== null
96
}
97
98
/**
99
* Returns the contents of the file in this zip archive with the given `path`.
100
* The returned promise will be rejected with an {@link InvalidArgumentError}
101
* if either `path` does not exist within the archive, or if `path` refers
102
* to a directory.
103
*
104
* @param {string} path the path to the file whose contents to return.
105
* @return {!Promise<!Buffer>} a promise that will be resolved with the file's
106
* contents as a buffer.
107
*/
108
getFile(path) {
109
let file = this.z_.file(path)
110
if (!file) {
111
return Promise.reject(new InvalidArgumentError(`No such file in zip archive: ${path}`))
112
}
113
114
if (file.dir) {
115
return Promise.reject(new InvalidArgumentError(`The requested file is a directory: ${path}`))
116
}
117
118
return Promise.resolve(file.async('nodebuffer'))
119
}
120
121
/**
122
* Returns the compressed data for this archive in a buffer. _This method will
123
* not wait for any outstanding {@link #addFile add}
124
* {@link #addDir operations} before encoding the archive._
125
*
126
* @param {string} compression The desired compression.
127
* Must be `STORE` (the default) or `DEFLATE`.
128
* @return {!Promise<!Buffer>} a promise that will resolve with this archive
129
* as a buffer.
130
*/
131
toBuffer(compression = 'STORE') {
132
if (compression !== 'STORE' && compression !== 'DEFLATE') {
133
return Promise.reject(new InvalidArgumentError(`compression must be one of {STORE, DEFLATE}, got ${compression}`))
134
}
135
return Promise.resolve(this.z_.generateAsync({ compression, type: 'nodebuffer' }))
136
}
137
}
138
139
/**
140
* Asynchronously opens a zip archive.
141
*
142
* @param {string} path to the zip archive to load.
143
* @return {!Promise<!Zip>} a promise that will resolve with the opened
144
* archive.
145
*/
146
function load(path) {
147
return io.read(path).then((data) => {
148
let zip = new Zip()
149
return zip.z_.loadAsync(data).then(() => zip)
150
})
151
}
152
153
/**
154
* Asynchronously unzips an archive file.
155
*
156
* @param {string} src path to the source file to unzip.
157
* @param {string} dst path to the destination directory.
158
* @return {!Promise<string>} a promise that will resolve with `dst` once the
159
* archive has been unzipped.
160
*/
161
function unzip(src, dst) {
162
return load(src).then((zip) => {
163
const promisedDirs = new Map()
164
const promises = []
165
166
zip.z_.forEach((relPath, file) => {
167
let p
168
if (file.dir) {
169
p = createDir(relPath)
170
} else {
171
let dirname = path.dirname(relPath)
172
if (dirname === '.') {
173
p = writeFile(relPath, file)
174
} else {
175
p = createDir(dirname).then(() => writeFile(relPath, file))
176
}
177
}
178
promises.push(p)
179
})
180
181
return Promise.all(promises).then(() => dst)
182
183
function createDir(dir) {
184
let p = promisedDirs.get(dir)
185
if (!p) {
186
p = io.mkdirp(path.join(dst, dir))
187
promisedDirs.set(dir, p)
188
}
189
return p
190
}
191
192
function writeFile(relPath, file) {
193
return file.async('nodebuffer').then((buffer) => io.write(path.join(dst, relPath), buffer))
194
}
195
})
196
}
197
198
// PUBLIC API
199
module.exports = { Zip, load, unzip }
200
201