@appium/support 2.57.4 → 2.59.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/build/lib/console.d.ts +110 -0
- package/build/lib/console.d.ts.map +1 -0
- package/build/lib/console.js +113 -0
- package/build/lib/env.d.ts.map +1 -1
- package/build/lib/env.js +1 -1
- package/build/lib/fs.d.ts.map +1 -1
- package/build/lib/fs.js +1 -1
- package/build/lib/image-util.d.ts.map +1 -1
- package/build/lib/image-util.js +1 -1
- package/build/lib/index.d.ts +3 -1
- package/build/lib/index.js +8 -3
- package/build/lib/log-internal.d.ts.map +1 -1
- package/build/lib/log-internal.js +1 -1
- package/build/lib/logger.d.ts +1 -1
- package/build/lib/logger.d.ts.map +1 -1
- package/build/lib/logger.js +1 -1
- package/build/lib/logging.d.ts +11 -4
- package/build/lib/logging.d.ts.map +1 -1
- package/build/lib/logging.js +7 -6
- package/build/lib/mjpeg.d.ts.map +1 -1
- package/build/lib/mjpeg.js +1 -1
- package/build/lib/mkdirp.js +1 -1
- package/build/lib/net.d.ts +5 -2
- package/build/lib/net.d.ts.map +1 -1
- package/build/lib/net.js +1 -1
- package/build/lib/node.d.ts +14 -0
- package/build/lib/node.d.ts.map +1 -1
- package/build/lib/node.js +22 -1
- package/build/lib/npm.d.ts.map +1 -1
- package/build/lib/npm.js +1 -1
- package/build/lib/plist.d.ts +1 -1
- package/build/lib/plist.d.ts.map +1 -1
- package/build/lib/plist.js +1 -1
- package/build/lib/process.d.ts.map +1 -1
- package/build/lib/process.js +1 -1
- package/build/lib/system.js +1 -1
- package/build/lib/tempdir.d.ts.map +1 -1
- package/build/lib/tempdir.js +1 -1
- package/build/lib/timing.d.ts.map +1 -1
- package/build/lib/timing.js +1 -1
- package/build/lib/util.d.ts +19 -2
- package/build/lib/util.d.ts.map +1 -1
- package/build/lib/util.js +3 -7
- package/build/lib/zip.d.ts +5 -5
- package/build/lib/zip.d.ts.map +1 -1
- package/build/lib/zip.js +2 -2
- package/build/tsconfig.tsbuildinfo +1 -1
- package/lib/console.js +173 -0
- package/lib/env.js +12 -14
- package/lib/fs.js +87 -62
- package/lib/image-util.js +50 -32
- package/lib/index.js +39 -8
- package/lib/log-internal.js +16 -11
- package/lib/logger.js +1 -1
- package/lib/logging.js +32 -30
- package/lib/mjpeg.js +32 -27
- package/lib/mkdirp.js +3 -3
- package/lib/net.js +60 -52
- package/lib/node.js +50 -16
- package/lib/npm.js +50 -47
- package/lib/plist.js +34 -18
- package/lib/process.js +8 -6
- package/lib/system.js +8 -8
- package/lib/tempdir.js +14 -9
- package/lib/timing.js +12 -15
- package/lib/util.js +121 -72
- package/lib/zip.js +88 -92
- package/package.json +11 -16
package/lib/net.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import _ from 'lodash';
|
|
2
2
|
import fs from './fs';
|
|
3
3
|
import B from 'bluebird';
|
|
4
|
-
import {
|
|
4
|
+
import {toReadableSizeString} from './util';
|
|
5
5
|
import log from './logger';
|
|
6
6
|
import Ftp from 'jsftp';
|
|
7
7
|
import Timer from './timing';
|
|
@@ -12,10 +12,10 @@ const DEFAULT_TIMEOUT_MS = 4 * 60 * 1000;
|
|
|
12
12
|
|
|
13
13
|
/**
|
|
14
14
|
* Converts {@linkcode AuthCredentials} to credentials understood by {@linkcode axios}.
|
|
15
|
-
* @param {AuthCredentials | import('axios').AxiosBasicCredentials} auth
|
|
15
|
+
* @param {AuthCredentials | import('axios').AxiosBasicCredentials} [auth]
|
|
16
16
|
* @returns {import('axios').AxiosBasicCredentials?}
|
|
17
17
|
*/
|
|
18
|
-
function toAxiosAuth
|
|
18
|
+
function toAxiosAuth(auth) {
|
|
19
19
|
if (!_.isPlainObject(auth)) {
|
|
20
20
|
return null;
|
|
21
21
|
}
|
|
@@ -24,7 +24,7 @@ function toAxiosAuth (auth) {
|
|
|
24
24
|
username: _.get(auth, 'username', _.get(auth, 'user')),
|
|
25
25
|
password: _.get(auth, 'password', _.get(auth, 'pass')),
|
|
26
26
|
};
|
|
27
|
-
return
|
|
27
|
+
return axiosAuth.username && axiosAuth.password ? axiosAuth : null;
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
/**
|
|
@@ -32,7 +32,11 @@ function toAxiosAuth (auth) {
|
|
|
32
32
|
* @param {URL} parsedUri
|
|
33
33
|
* @param {HttpUploadOptions & NetOptions} [uploadOptions]
|
|
34
34
|
*/
|
|
35
|
-
async function uploadFileToHttp
|
|
35
|
+
async function uploadFileToHttp(
|
|
36
|
+
localFileStream,
|
|
37
|
+
parsedUri,
|
|
38
|
+
uploadOptions = /** @type {HttpUploadOptions & NetOptions} */ ({})
|
|
39
|
+
) {
|
|
36
40
|
const {
|
|
37
41
|
method = 'POST',
|
|
38
42
|
timeout = DEFAULT_TIMEOUT_MS,
|
|
@@ -41,7 +45,7 @@ async function uploadFileToHttp (localFileStream, parsedUri, uploadOptions = /**
|
|
|
41
45
|
fileFieldName = 'file',
|
|
42
46
|
formFields,
|
|
43
47
|
} = uploadOptions;
|
|
44
|
-
const {
|
|
48
|
+
const {href} = parsedUri;
|
|
45
49
|
|
|
46
50
|
/** @type {import('axios').AxiosRequestConfig} */
|
|
47
51
|
const requestOpts = {
|
|
@@ -73,7 +77,7 @@ async function uploadFileToHttp (localFileStream, parsedUri, uploadOptions = /**
|
|
|
73
77
|
}
|
|
74
78
|
requestOpts.headers = {
|
|
75
79
|
...(_.isPlainObject(headers) ? headers : {}),
|
|
76
|
-
...form.getHeaders()
|
|
80
|
+
...form.getHeaders(),
|
|
77
81
|
};
|
|
78
82
|
requestOpts.data = form;
|
|
79
83
|
} else {
|
|
@@ -82,8 +86,10 @@ async function uploadFileToHttp (localFileStream, parsedUri, uploadOptions = /**
|
|
|
82
86
|
}
|
|
83
87
|
requestOpts.data = localFileStream;
|
|
84
88
|
}
|
|
85
|
-
log.debug(
|
|
86
|
-
|
|
89
|
+
log.debug(
|
|
90
|
+
`Performing ${method} to ${href} with options (excluding data): ` +
|
|
91
|
+
JSON.stringify(_.omit(requestOpts, ['data']))
|
|
92
|
+
);
|
|
87
93
|
|
|
88
94
|
const {status, statusText} = await axios(requestOpts);
|
|
89
95
|
log.info(`Server response: ${status} ${statusText}`);
|
|
@@ -94,16 +100,13 @@ async function uploadFileToHttp (localFileStream, parsedUri, uploadOptions = /**
|
|
|
94
100
|
* @param {URL} parsedUri
|
|
95
101
|
* @param {NotHttpUploadOptions & NetOptions} [uploadOptions]
|
|
96
102
|
*/
|
|
97
|
-
async function uploadFileToFtp
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
protocol,
|
|
105
|
-
pathname,
|
|
106
|
-
} = parsedUri;
|
|
103
|
+
async function uploadFileToFtp(
|
|
104
|
+
localFileStream,
|
|
105
|
+
parsedUri,
|
|
106
|
+
uploadOptions = /** @type {NotHttpUploadOptions & NetOptions} */ ({})
|
|
107
|
+
) {
|
|
108
|
+
const {auth} = uploadOptions;
|
|
109
|
+
const {hostname, port, protocol, pathname} = parsedUri;
|
|
107
110
|
|
|
108
111
|
const ftpOpts = {
|
|
109
112
|
host: hostname,
|
|
@@ -131,7 +134,7 @@ async function uploadFileToFtp (localFileStream, parsedUri, uploadOptions = /**
|
|
|
131
134
|
* @param {URL} url
|
|
132
135
|
* @returns {opts is HttpUploadOptions & NetOptions}
|
|
133
136
|
*/
|
|
134
|
-
function isHttpUploadOptions
|
|
137
|
+
function isHttpUploadOptions(opts, url) {
|
|
135
138
|
try {
|
|
136
139
|
const {protocol} = new URL(url);
|
|
137
140
|
return protocol === 'http:' || protocol === 'https:';
|
|
@@ -146,7 +149,7 @@ function isHttpUploadOptions (opts, url) {
|
|
|
146
149
|
* @param {URL} url
|
|
147
150
|
* @returns {opts is NotHttpUploadOptions & NetOptions}
|
|
148
151
|
*/
|
|
149
|
-
function isNotHttpUploadOptions
|
|
152
|
+
function isNotHttpUploadOptions(opts, url) {
|
|
150
153
|
try {
|
|
151
154
|
const {protocol} = new URL(url);
|
|
152
155
|
return protocol === 'ftp:';
|
|
@@ -163,14 +166,16 @@ function isNotHttpUploadOptions (opts, url) {
|
|
|
163
166
|
* @param {(HttpUploadOptions|NotHttpUploadOptions) & NetOptions} [uploadOptions]
|
|
164
167
|
* @returns {Promise<void>}
|
|
165
168
|
*/
|
|
166
|
-
async function uploadFile
|
|
167
|
-
|
|
168
|
-
|
|
169
|
+
async function uploadFile(
|
|
170
|
+
localPath,
|
|
171
|
+
remoteUri,
|
|
172
|
+
uploadOptions = /** @type {(HttpUploadOptions|NotHttpUploadOptions) & NetOptions} */ ({})
|
|
173
|
+
) {
|
|
174
|
+
if (!(await fs.exists(localPath))) {
|
|
175
|
+
throw new Error(`'${localPath}' does not exists or is not accessible`);
|
|
169
176
|
}
|
|
170
177
|
|
|
171
|
-
const {
|
|
172
|
-
isMetered = true,
|
|
173
|
-
} = uploadOptions;
|
|
178
|
+
const {isMetered = true} = uploadOptions;
|
|
174
179
|
const url = new URL(remoteUri);
|
|
175
180
|
const {size} = await fs.stat(localPath);
|
|
176
181
|
if (isMetered) {
|
|
@@ -181,20 +186,24 @@ async function uploadFile (localPath, remoteUri, uploadOptions = /** @type {(Htt
|
|
|
181
186
|
if (!uploadOptions.fileFieldName) {
|
|
182
187
|
uploadOptions.headers = {
|
|
183
188
|
...(_.isPlainObject(uploadOptions.headers) ? uploadOptions.headers : {}),
|
|
184
|
-
'Content-Length': size
|
|
189
|
+
'Content-Length': size,
|
|
185
190
|
};
|
|
186
191
|
}
|
|
187
192
|
await uploadFileToHttp(fs.createReadStream(localPath), url, uploadOptions);
|
|
188
193
|
} else if (isNotHttpUploadOptions(uploadOptions, url)) {
|
|
189
194
|
await uploadFileToFtp(fs.createReadStream(localPath), url, uploadOptions);
|
|
190
195
|
} else {
|
|
191
|
-
throw new Error(
|
|
192
|
-
`
|
|
193
|
-
|
|
196
|
+
throw new Error(
|
|
197
|
+
`Cannot upload the file at '${localPath}' to '${remoteUri}'. ` +
|
|
198
|
+
`Unsupported remote protocol '${url.protocol}'. ` +
|
|
199
|
+
`Only http/https and ftp/ftps protocols are supported.`
|
|
200
|
+
);
|
|
194
201
|
}
|
|
195
202
|
if (isMetered) {
|
|
196
|
-
log.info(
|
|
197
|
-
|
|
203
|
+
log.info(
|
|
204
|
+
`Uploaded '${localPath}' of ${toReadableSizeString(size)} size in ` +
|
|
205
|
+
`${timer.getDuration().asSeconds.toFixed(3)}s`
|
|
206
|
+
);
|
|
198
207
|
}
|
|
199
208
|
}
|
|
200
209
|
|
|
@@ -206,13 +215,12 @@ async function uploadFile (localPath, remoteUri, uploadOptions = /** @type {(Htt
|
|
|
206
215
|
* @param {DownloadOptions & NetOptions} [downloadOptions]
|
|
207
216
|
* @throws {Error} If download operation fails
|
|
208
217
|
*/
|
|
209
|
-
async function downloadFile
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
} = downloadOptions;
|
|
218
|
+
async function downloadFile(
|
|
219
|
+
remoteUrl,
|
|
220
|
+
dstPath,
|
|
221
|
+
downloadOptions = /** @type {DownloadOptions & NetOptions} */ ({})
|
|
222
|
+
) {
|
|
223
|
+
const {isMetered = true, auth, timeout = DEFAULT_TIMEOUT_MS, headers} = downloadOptions;
|
|
216
224
|
|
|
217
225
|
/**
|
|
218
226
|
* @type {import('axios').AxiosRequestConfig}
|
|
@@ -234,10 +242,7 @@ async function downloadFile (remoteUrl, dstPath, downloadOptions = /** @type {Do
|
|
|
234
242
|
let responseLength;
|
|
235
243
|
try {
|
|
236
244
|
const writer = fs.createWriteStream(dstPath);
|
|
237
|
-
const {
|
|
238
|
-
data: responseStream,
|
|
239
|
-
headers: responseHeaders,
|
|
240
|
-
} = await axios(requestOpts);
|
|
245
|
+
const {data: responseStream, headers: responseHeaders} = await axios(requestOpts);
|
|
241
246
|
responseLength = parseInt(responseHeaders['content-length'], 10);
|
|
242
247
|
responseStream.pipe(writer);
|
|
243
248
|
|
|
@@ -256,13 +261,17 @@ async function downloadFile (remoteUrl, dstPath, downloadOptions = /** @type {Do
|
|
|
256
261
|
const {size} = await fs.stat(dstPath);
|
|
257
262
|
if (responseLength && size !== responseLength) {
|
|
258
263
|
await fs.rimraf(dstPath);
|
|
259
|
-
throw new Error(
|
|
260
|
-
`
|
|
264
|
+
throw new Error(
|
|
265
|
+
`The size of the file downloaded from ${remoteUrl} (${size} bytes) ` +
|
|
266
|
+
`differs from the one in Content-Length response header (${responseLength} bytes)`
|
|
267
|
+
);
|
|
261
268
|
}
|
|
262
269
|
if (isMetered) {
|
|
263
270
|
const secondsElapsed = timer.getDuration().asSeconds;
|
|
264
|
-
log.debug(
|
|
265
|
-
|
|
271
|
+
log.debug(
|
|
272
|
+
`${remoteUrl} (${toReadableSizeString(size)}) ` +
|
|
273
|
+
`has been downloaded to '${dstPath}' in ${secondsElapsed.toFixed(3)}s`
|
|
274
|
+
);
|
|
266
275
|
if (secondsElapsed >= 2) {
|
|
267
276
|
const bytesPerSec = Math.floor(size / secondsElapsed);
|
|
268
277
|
log.debug(`Approximate download speed: ${toReadableSizeString(bytesPerSec)}/s`);
|
|
@@ -270,21 +279,21 @@ async function downloadFile (remoteUrl, dstPath, downloadOptions = /** @type {Do
|
|
|
270
279
|
}
|
|
271
280
|
}
|
|
272
281
|
|
|
273
|
-
export {
|
|
282
|
+
export {uploadFile, downloadFile};
|
|
274
283
|
|
|
275
284
|
/**
|
|
276
285
|
* Common options for {@linkcode uploadFile} and {@linkcode downloadFile}.
|
|
277
286
|
* @typedef NetOptions
|
|
278
287
|
* @property {boolean} [isMetered=true] - Whether to log the actual download performance
|
|
279
288
|
* (e.g. timings and speed)
|
|
280
|
-
* @property {AuthCredentials} auth
|
|
289
|
+
* @property {AuthCredentials} [auth] - Authentication credentials
|
|
281
290
|
*/
|
|
282
291
|
|
|
283
292
|
/**
|
|
284
293
|
* Specific options for {@linkcode downloadFile}.
|
|
285
294
|
* @typedef DownloadOptions
|
|
286
295
|
* @property {number} [timeout] - The actual request timeout in milliseconds; defaults to {@linkcode DEFAULT_TIMEOUT_MS}
|
|
287
|
-
* @property {Record<string,any>} headers - Request headers mapping
|
|
296
|
+
* @property {Record<string,any>} [headers] - Request headers mapping
|
|
288
297
|
*/
|
|
289
298
|
|
|
290
299
|
/**
|
|
@@ -316,4 +325,3 @@ export { uploadFile, downloadFile };
|
|
|
316
325
|
* to be included into the upload request. This property is only considered if
|
|
317
326
|
* `fileFieldName` is set
|
|
318
327
|
*/
|
|
319
|
-
|
package/lib/node.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {isWindows} from './system';
|
|
2
2
|
import log from './logger';
|
|
3
3
|
import _ from 'lodash';
|
|
4
|
-
import {
|
|
4
|
+
import {exec} from 'teen_process';
|
|
5
5
|
import path from 'path';
|
|
6
|
-
import {
|
|
6
|
+
import {v4 as uuidV4} from 'uuid';
|
|
7
7
|
|
|
8
8
|
const ECMA_SIZES = Object.freeze({
|
|
9
9
|
STRING: 2,
|
|
@@ -17,7 +17,7 @@ const ECMA_SIZES = Object.freeze({
|
|
|
17
17
|
* @param {string} packageName - name of the package to link
|
|
18
18
|
* @throws {Error} If the command fails
|
|
19
19
|
*/
|
|
20
|
-
async function linkGlobalPackage
|
|
20
|
+
async function linkGlobalPackage(packageName) {
|
|
21
21
|
try {
|
|
22
22
|
log.debug(`Linking package '${packageName}'`);
|
|
23
23
|
const cmd = isWindows() ? 'npm.cmd' : 'npm';
|
|
@@ -43,7 +43,7 @@ async function linkGlobalPackage (packageName) {
|
|
|
43
43
|
* @returns {Promise<unknown>} - the package object
|
|
44
44
|
* @throws {Error} If the package is not found locally or globally
|
|
45
45
|
*/
|
|
46
|
-
async function requirePackage
|
|
46
|
+
async function requirePackage(packageName) {
|
|
47
47
|
// first, get it in the normal way (see https://nodejs.org/api/modules.html#modules_all_together)
|
|
48
48
|
try {
|
|
49
49
|
log.debug(`Loading local package '${packageName}'`);
|
|
@@ -54,7 +54,12 @@ async function requirePackage (packageName) {
|
|
|
54
54
|
|
|
55
55
|
// second, get it from where it ought to be in the global node_modules
|
|
56
56
|
try {
|
|
57
|
-
const globalPackageName = path.resolve(
|
|
57
|
+
const globalPackageName = path.resolve(
|
|
58
|
+
process.env.npm_config_prefix ?? '',
|
|
59
|
+
'lib',
|
|
60
|
+
'node_modules',
|
|
61
|
+
packageName
|
|
62
|
+
);
|
|
58
63
|
log.debug(`Loading global package '${globalPackageName}'`);
|
|
59
64
|
return require(globalPackageName);
|
|
60
65
|
} catch (err) {
|
|
@@ -71,18 +76,18 @@ async function requirePackage (packageName) {
|
|
|
71
76
|
}
|
|
72
77
|
}
|
|
73
78
|
|
|
74
|
-
function extractAllProperties
|
|
79
|
+
function extractAllProperties(obj) {
|
|
75
80
|
const stringProperties = [];
|
|
76
81
|
for (const prop in obj) {
|
|
77
82
|
stringProperties.push(prop);
|
|
78
83
|
}
|
|
79
84
|
if (_.isFunction(Object.getOwnPropertySymbols)) {
|
|
80
|
-
stringProperties.push(...
|
|
85
|
+
stringProperties.push(...Object.getOwnPropertySymbols(obj));
|
|
81
86
|
}
|
|
82
87
|
return stringProperties;
|
|
83
88
|
}
|
|
84
89
|
|
|
85
|
-
function _getSizeOfObject
|
|
90
|
+
function _getSizeOfObject(seen, object) {
|
|
86
91
|
if (_.isNil(object)) {
|
|
87
92
|
return 0;
|
|
88
93
|
}
|
|
@@ -113,13 +118,13 @@ function _getSizeOfObject (seen, object) {
|
|
|
113
118
|
return bytes;
|
|
114
119
|
}
|
|
115
120
|
|
|
116
|
-
function getCalculator
|
|
117
|
-
return function calculator
|
|
121
|
+
function getCalculator(seen) {
|
|
122
|
+
return function calculator(obj) {
|
|
118
123
|
if (_.isBuffer(obj)) {
|
|
119
124
|
return obj.length;
|
|
120
125
|
}
|
|
121
126
|
|
|
122
|
-
switch (typeof
|
|
127
|
+
switch (typeof obj) {
|
|
123
128
|
case 'string':
|
|
124
129
|
return obj.length * ECMA_SIZES.STRING;
|
|
125
130
|
case 'boolean':
|
|
@@ -128,7 +133,7 @@ function getCalculator (seen) {
|
|
|
128
133
|
return ECMA_SIZES.NUMBER;
|
|
129
134
|
case 'symbol':
|
|
130
135
|
return _.isFunction(Symbol.keyFor) && Symbol.keyFor(obj)
|
|
131
|
-
? /** @type {string} */(Symbol.keyFor(obj)).length * ECMA_SIZES.STRING
|
|
136
|
+
? /** @type {string} */ (Symbol.keyFor(obj)).length * ECMA_SIZES.STRING
|
|
132
137
|
: (obj.toString().length - 8) * ECMA_SIZES.STRING;
|
|
133
138
|
case 'object':
|
|
134
139
|
return _.isArray(obj)
|
|
@@ -147,7 +152,7 @@ function getCalculator (seen) {
|
|
|
147
152
|
* @param {*} obj An object whose size should be calculated
|
|
148
153
|
* @returns {number} Object size in bytes.
|
|
149
154
|
*/
|
|
150
|
-
function getObjectSize
|
|
155
|
+
function getObjectSize(obj) {
|
|
151
156
|
return getCalculator(new WeakSet())(obj);
|
|
152
157
|
}
|
|
153
158
|
|
|
@@ -159,11 +164,40 @@ const OBJECTS_MAPPING = new WeakMap();
|
|
|
159
164
|
* @param {object} object Any valid ECMA object
|
|
160
165
|
* @returns {string} A uuidV4 string that uniquely identifies given object
|
|
161
166
|
*/
|
|
162
|
-
function getObjectId
|
|
167
|
+
function getObjectId(object) {
|
|
163
168
|
if (!OBJECTS_MAPPING.has(object)) {
|
|
164
169
|
OBJECTS_MAPPING.set(object, uuidV4());
|
|
165
170
|
}
|
|
166
171
|
return OBJECTS_MAPPING.get(object);
|
|
167
172
|
}
|
|
168
173
|
|
|
169
|
-
|
|
174
|
+
/**
|
|
175
|
+
* Perform deep freeze of the given object (e. g.
|
|
176
|
+
* all nested objects also become immutable).
|
|
177
|
+
* If the passed object is of a plain type
|
|
178
|
+
* then no change is done and the same object
|
|
179
|
+
* is returned.
|
|
180
|
+
* ! This function changes the given object,
|
|
181
|
+
* so it becomes immutable.
|
|
182
|
+
*
|
|
183
|
+
* @param {*} object Any valid ECMA object
|
|
184
|
+
* @returns {*} The same object that was passed to the
|
|
185
|
+
* function after it was made immutable.
|
|
186
|
+
*/
|
|
187
|
+
function deepFreeze(object) {
|
|
188
|
+
let propNames;
|
|
189
|
+
try {
|
|
190
|
+
propNames = Object.getOwnPropertyNames(object);
|
|
191
|
+
} catch (ign) {
|
|
192
|
+
return object;
|
|
193
|
+
}
|
|
194
|
+
for (const name of propNames) {
|
|
195
|
+
const value = object[name];
|
|
196
|
+
if (value && typeof value === 'object') {
|
|
197
|
+
deepFreeze(value);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
return Object.freeze(object);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
export {requirePackage, getObjectSize, getObjectId, deepFreeze};
|
package/lib/npm.js
CHANGED
|
@@ -2,31 +2,23 @@
|
|
|
2
2
|
|
|
3
3
|
import path from 'path';
|
|
4
4
|
import semver from 'semver';
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
5
|
+
import {hasAppiumDependency} from './env';
|
|
6
|
+
import {exec} from 'teen_process';
|
|
7
|
+
import {fs} from './fs';
|
|
8
8
|
import * as util from './util';
|
|
9
9
|
import * as system from './system';
|
|
10
10
|
import resolveFrom from 'resolve-from';
|
|
11
11
|
|
|
12
|
-
|
|
13
12
|
/**
|
|
14
13
|
* Relative path to directory containing any Appium internal files
|
|
15
14
|
* XXX: this is duplicated in `appium/lib/constants.js`.
|
|
16
15
|
*/
|
|
17
|
-
export const CACHE_DIR_RELATIVE_PATH = path.join(
|
|
18
|
-
'node_modules',
|
|
19
|
-
'.cache',
|
|
20
|
-
'appium',
|
|
21
|
-
);
|
|
16
|
+
export const CACHE_DIR_RELATIVE_PATH = path.join('node_modules', '.cache', 'appium');
|
|
22
17
|
|
|
23
18
|
/**
|
|
24
19
|
* Relative path to lockfile used when installing an extension via `appium`
|
|
25
20
|
*/
|
|
26
|
-
export const INSTALL_LOCKFILE_RELATIVE_PATH = path.join(
|
|
27
|
-
CACHE_DIR_RELATIVE_PATH,
|
|
28
|
-
'.install.lock',
|
|
29
|
-
);
|
|
21
|
+
export const INSTALL_LOCKFILE_RELATIVE_PATH = path.join(CACHE_DIR_RELATIVE_PATH, '.install.lock');
|
|
30
22
|
|
|
31
23
|
/**
|
|
32
24
|
* XXX: This should probably be a singleton, but it isn't. Maybe this module should just export functions?
|
|
@@ -37,7 +29,7 @@ export class NPM {
|
|
|
37
29
|
* @private
|
|
38
30
|
* @param {string} cwd
|
|
39
31
|
*/
|
|
40
|
-
_getInstallLockfilePath
|
|
32
|
+
_getInstallLockfilePath(cwd) {
|
|
41
33
|
return path.join(cwd, INSTALL_LOCKFILE_RELATIVE_PATH);
|
|
42
34
|
}
|
|
43
35
|
|
|
@@ -51,8 +43,8 @@ export class NPM {
|
|
|
51
43
|
* @param {ExecOpts} opts
|
|
52
44
|
* @param {ExecOpts} [execOpts]
|
|
53
45
|
*/
|
|
54
|
-
async exec
|
|
55
|
-
let {
|
|
46
|
+
async exec(cmd, args, opts, execOpts = /** @type {ExecOpts} */ ({})) {
|
|
47
|
+
let {cwd, json, lockFile} = opts;
|
|
56
48
|
|
|
57
49
|
// make sure we perform the current operation in cwd
|
|
58
50
|
execOpts = {...execOpts, cwd};
|
|
@@ -81,8 +73,12 @@ export class NPM {
|
|
|
81
73
|
ret.json = JSON.parse(stdout);
|
|
82
74
|
} catch (ign) {}
|
|
83
75
|
} catch (e) {
|
|
84
|
-
const {stdout = '', stderr = '', code = null} = /** @type {TeenProcessExecError} */(e);
|
|
85
|
-
const err = new Error(
|
|
76
|
+
const {stdout = '', stderr = '', code = null} = /** @type {TeenProcessExecError} */ (e);
|
|
77
|
+
const err = new Error(
|
|
78
|
+
`npm command '${args.join(
|
|
79
|
+
' '
|
|
80
|
+
)}' failed with code ${code}.\n\nSTDOUT:\n${stdout.trim()}\n\nSTDERR:\n${stderr.trim()}`
|
|
81
|
+
);
|
|
86
82
|
throw err;
|
|
87
83
|
}
|
|
88
84
|
return ret;
|
|
@@ -92,11 +88,13 @@ export class NPM {
|
|
|
92
88
|
* @param {string} cwd
|
|
93
89
|
* @param {string} pkg
|
|
94
90
|
*/
|
|
95
|
-
async getLatestVersion
|
|
96
|
-
return (
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
91
|
+
async getLatestVersion(cwd, pkg) {
|
|
92
|
+
return (
|
|
93
|
+
await this.exec('view', [pkg, 'dist-tags'], {
|
|
94
|
+
json: true,
|
|
95
|
+
cwd,
|
|
96
|
+
})
|
|
97
|
+
).json?.latest;
|
|
100
98
|
}
|
|
101
99
|
|
|
102
100
|
/**
|
|
@@ -104,11 +102,13 @@ export class NPM {
|
|
|
104
102
|
* @param {string} pkg
|
|
105
103
|
* @param {string} curVersion
|
|
106
104
|
*/
|
|
107
|
-
async getLatestSafeUpgradeVersion
|
|
108
|
-
const allVersions = (
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
105
|
+
async getLatestSafeUpgradeVersion(cwd, pkg, curVersion) {
|
|
106
|
+
const allVersions = (
|
|
107
|
+
await this.exec('view', [pkg, 'versions'], {
|
|
108
|
+
json: true,
|
|
109
|
+
cwd,
|
|
110
|
+
})
|
|
111
|
+
).json;
|
|
112
112
|
return this.getLatestSafeUpgradeFromVersions(curVersion, allVersions);
|
|
113
113
|
}
|
|
114
114
|
|
|
@@ -117,7 +117,7 @@ export class NPM {
|
|
|
117
117
|
* @param {string} cwd
|
|
118
118
|
* @param {string} [pkg]
|
|
119
119
|
*/
|
|
120
|
-
async list
|
|
120
|
+
async list(cwd, pkg) {
|
|
121
121
|
return (await this.exec('list', pkg ? [pkg] : [], {cwd, json: true})).json;
|
|
122
122
|
}
|
|
123
123
|
|
|
@@ -131,7 +131,7 @@ export class NPM {
|
|
|
131
131
|
*
|
|
132
132
|
* @return {string|null} - the highest safely-upgradable version, or null if there isn't one
|
|
133
133
|
*/
|
|
134
|
-
getLatestSafeUpgradeFromVersions
|
|
134
|
+
getLatestSafeUpgradeFromVersions(curVersion, allVersions) {
|
|
135
135
|
let safeUpgradeVer = null;
|
|
136
136
|
const curSemver = semver.parse(curVersion);
|
|
137
137
|
if (curSemver === null) {
|
|
@@ -173,7 +173,7 @@ export class NPM {
|
|
|
173
173
|
* @param {InstallPackageOpts} [opts]
|
|
174
174
|
* @returns {Promise<import('type-fest').PackageJson>}
|
|
175
175
|
*/
|
|
176
|
-
async installPackage
|
|
176
|
+
async installPackage(cwd, pkgName, {pkgVer} = {}) {
|
|
177
177
|
/** @type {any} */
|
|
178
178
|
let dummyPkgJson;
|
|
179
179
|
const dummyPkgPath = path.join(cwd, 'package.json');
|
|
@@ -199,18 +199,19 @@ export class NPM {
|
|
|
199
199
|
* "dummy" and is controlled by the user. So we'll just add it as a dev
|
|
200
200
|
* dep; whatever else it does is up to the user's npm config.
|
|
201
201
|
*/
|
|
202
|
-
const installOpts = await hasAppiumDependency(cwd)
|
|
203
|
-
['--save-dev']
|
|
204
|
-
['--save-dev', '--save-exact', '--global-style', '--no-package-lock'];
|
|
202
|
+
const installOpts = (await hasAppiumDependency(cwd))
|
|
203
|
+
? ['--save-dev']
|
|
204
|
+
: ['--save-dev', '--save-exact', '--global-style', '--no-package-lock'];
|
|
205
205
|
|
|
206
|
-
const res = await this.exec(
|
|
207
|
-
|
|
208
|
-
pkgVer ? `${pkgName}@${pkgVer}` : pkgName
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
206
|
+
const res = await this.exec(
|
|
207
|
+
'install',
|
|
208
|
+
[...installOpts, pkgVer ? `${pkgName}@${pkgVer}` : pkgName],
|
|
209
|
+
{
|
|
210
|
+
cwd,
|
|
211
|
+
json: true,
|
|
212
|
+
lockFile: this._getInstallLockfilePath(cwd),
|
|
213
|
+
}
|
|
214
|
+
);
|
|
214
215
|
|
|
215
216
|
if (res.json) {
|
|
216
217
|
// we parsed a valid json response, so if we got an error here, return that
|
|
@@ -228,9 +229,11 @@ export class NPM {
|
|
|
228
229
|
try {
|
|
229
230
|
return require(pkgJsonPath);
|
|
230
231
|
} catch {
|
|
231
|
-
throw new Error(
|
|
232
|
-
|
|
233
|
-
|
|
232
|
+
throw new Error(
|
|
233
|
+
'The package was not downloaded correctly; its package.json ' +
|
|
234
|
+
'did not exist or was unreadable. We looked for it at ' +
|
|
235
|
+
pkgJsonPath
|
|
236
|
+
);
|
|
234
237
|
}
|
|
235
238
|
}
|
|
236
239
|
|
|
@@ -238,10 +241,10 @@ export class NPM {
|
|
|
238
241
|
* @param {string} cwd
|
|
239
242
|
* @param {string} pkg
|
|
240
243
|
*/
|
|
241
|
-
async uninstallPackage
|
|
244
|
+
async uninstallPackage(cwd, pkg) {
|
|
242
245
|
await this.exec('uninstall', [pkg], {
|
|
243
246
|
cwd,
|
|
244
|
-
lockFile: this._getInstallLockfilePath(cwd)
|
|
247
|
+
lockFile: this._getInstallLockfilePath(cwd),
|
|
245
248
|
});
|
|
246
249
|
}
|
|
247
250
|
}
|